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:
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:
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:
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!