About

Clay is a CSS preprocessor like LESS and Sass, but implemented as an embedded domain specific language (EDSL) in Haskell. This means that all CSS selectors and style rules are first class Haskell functions, which makes reuse and composability easy.

Clay doesn't really use any clever Haskell tricks which makes it easy to work with, even without a lot of Haskell experience.

In action

{-# LANGUAGE
    OverloadedStrings
  #-}
import Clay

menu :: Css
menu = header |> nav ?
  do background    white
     color         "#04a"
     fontSize      (px 24)
     padding       20 0 20 0
     textTransform uppercase
     position      absolute
     left          0
     right         0
     bottom        (px (-72))

Features

  • First class style properties.
  • First class selectors.
  • Typed values.
  • Nested style rules.
  • Access to outer scopes in nested rules.
  • Modules, functions, mixins, etc.
  • Support for media queries.
  • Size and color arithmetics.
  • Recognizable syntax, inspired by CSS.
  • Easily extensible with new functionality.
  • Near-total coverage of CSS.
  • Easy fall-back for uncovered parts.
  • Automatic addition of browser prefixes.
  • Both pretty and compact output printers.

Futures

Despite the amazing amount of features, Clay is far from finished and needs a lot more work. Missing features, bugs, and plans for the future are on the issue tracker.

» project issue tracker


Installation

Clay is written in Haskell and requires the Glasgow Haskell Compiler (GHC) to get installed. After installing GHC, and the accompanying package manager Cabal, you can directly install Clay from the Hackage package repository:

$ cabal update
$ cabal install clay

When you don't have GHC installed yet, it's probably best to install the Haskell Platform. The platform includes GHC and a basic set of Haskell tools and libraries, including Cabal. After installing the platform you can install Clay using cabal install.

» The Haskell Platform

Usage

Clay is written as an embedded language and requires you to write a Haskell program that models the stylesheet. You only need to import the Clay module to get started. As a first demonstration we create the `Hello world' of the stylesheets, a style definition that sets the background color of the HTML body to red.

import Clay

main :: IO ()
main = putCss myStylesheet

myStylesheet :: Css
myStylesheet = body ? background red

Running the putCss function directly outputs the generated CSS to the standard output resulting in:

body
{
  background : rgb(255,0,0);
}

API documentation

The Clay API documentation can be found on Hackage. For most uses only the Clay top level module is of any relevance. It re-exports all the code that is needed to write your stylesheet.

» API documentation

Tutorial

Clay is an EDSL that uses a monad to collect style rules. Rules are combinations of CSS selectors with associated style properties. Multiple style rules can be composed and nested to form a stylesheet. Stylesheets can be rendered to plain old CSS and served to your browser.

There are several methods of assigning style properties to selectors. The most important one is the ? operator:

import Clay

main = putCss $
  body ?
    do background  black
       color       green
       border      dashed (px 2) yellow
body
{
  background : rgb(0,0,0);
  color      : rgb(0,128,0);
  border     : dashed 2px rgb(255,255,0);
}

Style rules can easily be composed and nested by just adding more rules using the monad syntax:

import Clay

main = putCss $
  do p ? color red
     b ? color yellow
     article ?
       do strong ? background black
          abbr  <? fontVariant smallCaps
p
{
  color : rgb(255,0,0);
}
b
{
  color : rgb(255,255,0);
}
article strong
{
  background : rgb(0,0,0);
}
article > abbr
{
  font-variant : small-caps;
}

Basic selectors can be composed with several combinators to construct more complicated selectors:

import Data.Monoid
import Prelude hiding ((**))
import Clay

main = putCss $
  (body <> abbr) ** p |> (a <> b) # lastOfType ?
      color red
body p > a:last-of-type,
body p > b:last-of-type,
abbr p > a:last-of-type,
abbr p > b:last-of-type
{
  color : rgb(255,0,0);
}

Using OverloadedStrings allows us to create selectors for classes, ids, pseudo classes, attributes etc:

{-# LANGUAGE OverloadedStrings #-}
import Clay

main = putCss $
  section # "#open" |> ".link" ?
    do textDecoration none
       "@href" &
         do textDecoration underline
            ":hover" & fontWeight bold
section#open > .link
{
  text-decoration : none;
}
section#open > .link[href]
{
  text-decoration : underline;
}
section#open > .link[href]:hover
{
  font-weight : bold;
}

Or we can use some build in functions to avoid too much string literals:

import Data.Monoid
import Clay

main = putCss $
  a # href # hover ? textDecoration none
a[href]:hover
{
  text-decoration : none;
}

Sometimes browsers implement exotic style properties or values that Clay doesn't yet know of. For most value type it is easy to supply a custom value using the Other type class. We can even escape from the typed world entirely by using the fallback operator -: and use strings:

{-# LANGUAGE OverloadedStrings #-}
import Clay

main = putCss $
  star ?
    do color (other "ultraviolet")
       "-ms-lens-flare-style" -: "really-shiny"
*
{
  color                : ultraviolet;
  -ms-lens-flare-style : really-shiny;
}

Not every browser implements the full CSS standard and some browsers introduce experimental features that are not yet in the standard. In Clay, most experimental properties are automatically prefixed with vendor prefixes:

import Clay

main = putCss $
  body ?
    do let rot = deg 30
       transform (rotateX rot)
       background $
         linearGradient (angular rot)
           [ ( red    ,   0 )
           , ( yellow ,  40 )
           , ( blue   , 100 )
           ]
body
{
  -webkit-transform : rotateX(30deg);
  -moz-transform    : rotateX(30deg);
  -ms-transform     : rotateX(30deg);
  -o-transform      : rotateX(30deg);
  transform         : rotateX(30deg);
  background        : -webkit-linear-gradient(30deg,rgb(255,0,0) 0%,rgb(255,255,0) 40%,rgb(0,0,255) 100%);
  background        : -moz-linear-gradient(30deg,rgb(255,0,0) 0%,rgb(255,255,0) 40%,rgb(0,0,255) 100%);
  background        : -ms-linear-gradient(30deg,rgb(255,0,0) 0%,rgb(255,255,0) 40%,rgb(0,0,255) 100%);
  background        : -o-linear-gradient(30deg,rgb(255,0,0) 0%,rgb(255,255,0) 40%,rgb(0,0,255) 100%);
  background        : linear-gradient(30deg,rgb(255,0,0) 0%,rgb(255,255,0) 40%,rgb(0,0,255) 100%);
}

Because Clay is just Haskell we can do some really nice tricks that might take a lot of effort in other CSS preprocessors. Functions, mixins, control structures, colors and size calculation, all is easy. The following (somewhat contrived) example shows how to use the expressiveness of Haskell to produce more advanced stylesheets:

{-# LANGUAGE OverloadedStrings #-}
import Clay
import Control.Monad
import Data.String

main = putCss $
  article ? forM_ [0..4] mySection

myShade :: Color
myShade = "#f80"

mySection :: Integer -> Css
mySection n =
  let idx = fromString (show n)
   in section # nthChild idx ?
      do backgroundColor (setA (n * 40) myShade)
         let w = 20 * fromIntegral n
         backgroundSize (px 20 `by` pct w)
         p ? if n > 2
               then textDecoration underline
               else color yellow
article section:nth-child(0)
{
  background-color : rgba(255,136,0,0.0000);
  background-size  : 20px 0%;
}
article section:nth-child(0) p
{
  color : rgb(255,255,0);
}
article section:nth-child(1)
{
  background-color : rgba(255,136,0,0.1562);
  background-size  : 20px 20%;
}
article section:nth-child(1) p
{
  color : rgb(255,255,0);
}
article section:nth-child(2)
{
  background-color : rgba(255,136,0,0.3125);
  background-size  : 20px 40%;
}
article section:nth-child(2) p
{
  color : rgb(255,255,0);
}
article section:nth-child(3)
{
  background-color : rgba(255,136,0,0.4688);
  background-size  : 20px 60%;
}
article section:nth-child(3) p
{
  text-decoration : underline;
}
article section:nth-child(4)
{
  background-color : rgba(255,136,0,0.6250);
  background-size  : 20px 80%;
}
article section:nth-child(4) p
{
  text-decoration : underline;
}

More examples coming soon!

Project source

Clay is open source and hosted on GitHub. Every form of feedback, pull requests or other participation is more than welcome. CSS is a big standard containing lots of official and probably even more experimental style properties. Clay's support for all those properties is most likely incomplete. Adding new properties is easy so let me know if things should be added.

» project source repository

» project issue tracker

Build status

Build Status

Website source

Of course, the style sheet of this website is also implemented in Clay. You can find the sources in the project source repository on GitHub. Feel free to use this page as template for your own projects.

» stylesheet source in Clay

» website source repository

Generated CSS

You can take a look at the generated stylesheet for this website. We have both a pretty printed and compacted version, of which the compacted version is directly used by this site.

» pretty printed CSS

» compacted CSS