CSS-expressions
1 Overview
2 Installation
3 Usage
css-expr
css-expr->css
4 Language
4.1 Stylesheet
4.2 Rule
4.3 Nesting
4.4 At-Rule Expression
4.5 Declaration
4.6 Value
4.7 Selector
5 Grammar
6 Acknowledgments
7 Changelog
7.1 0.0.2 · 2017-03-08
7.1.1 Changed
7.2 0.0.1 · 2017-02-01
7.2.1 Added
8 References
7.7

CSS-expressions

Leandro Facchinetti <me@leafac.com>

 (require css-expr) package: css-expr

S-expression-based CSS.

Version

 

0.0.2

Documentation

 

Racket Documentation

License

 

GNU General Public License Version 3

Code of Conduct

 

Contributor Covenant v1.4.0

Distribution

 

Racket Package

Source

 

GitHub

Bug Reports

 

GitHub Issues

Contributions

 

GitHub Pull Requests

1 Overview

S-expressions are a convenient way of representing hierarchical data. Racket’s syntax is based on S-expressions and it includes particular dialects to support embedded domain-specific languages. For example, one can use X-expressions to represent XML in general or HTML in specific. Embedding documents in Racket programs as X-expressions has several benefits: parts of the document can be generated programmatically, the text editor can syntax-highlight and indent the code without extra effort and transformations on the document are data structure manipulations which do not require custom tools.

CSS-expressions go great with Pollen and Pollen Component.

CSS is a domain-specific language for styling documents. Documents can be generated with X-expressions, so it is natural to want a S-expression-based representation for CSS as well. We introduce CSS-expressions, a domain-specific language embedded in Racket which is based on S-expressions and outputs CSS. In addition to the benefits of embedded domain-specific languages already mentioned, CSS-expressions also supports extended features generally found in CSS preprocessors including Sass, Less and Stylus. For example, nested rules and declarations. Finally, because CSS-expressions are embedded in Racket, many features from the CSS preprocessors are free: variables, mixins, operations, and so on.

Examples:
> (require racket/format css-expr)
> (~a
   (substring
    (css-expr->css
     (css-expr
      ; Styles from http://bettermotherfuckingwebsite.com/
      [body
       #:margin (40px auto)
       #:max-width 650px
       #:line-height 1.6
       #:font-size 18px
       #:color |#444|
       #:padding (0 10px)]
      [h1 h2 h3
       #:line-height 1.2]))
    0 69) "...")

"body{margin:40px auto;max-width:650px;line-height:1.6;font-size:18px;..."

2 Installation

CSS-expressions are a Racket package. Install it in DrRacket or with the following command line:

$ raco pkg install css-expr

3 Usage

See CSS-expressions in action on my personal website (source). It also uses Pollen Component.

First, require the library. In Racket, add the following line:

(require css-expr)

Or, in Typed Racket, add the following line:

(require css-expr/typed)

Then, build CSS-expressions with css-expr. Finally, use css-expr->css to transform CSS-expressions into CSS.

syntax

(css-expr expr ...)

Creates an CSS-expression from the given expressions. Escape to Racket code with unquoting (,expr). css-expr works like quasiquoting and only exists to clarify the intent of creating CSS-expressions.

Examples:
> (require css-expr)
> (define main-width (css-expr 700px))
> (css-expr
   [body #:width ,@main-width]
   [.menu #:width ,@main-width])

'((body #:width 700px) (.menu #:width 700px))

procedure

(css-expr->css css-expr)  String

  css-expr : Sexp
Compiles a CSS-expression stylesheet to CSS.

4 Language

The following subsections describe CSS-expressions with English and examples. A formal grammar is also available in the next section.

4.1 Stylesheet

A complete CSS-expression is, at the top level, a stylesheet, which is a list of rules.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:margin (40px auto)]
    [h1 h2 h3
     #:line-height 1.2]))

"body{margin:40px auto;}h1,h2,h3{line-height:1.2;}"

In the example above, ([body ...] [h1 h2 h3 ...]) is a stylesheet and each of [body ...] and [h1 h2 h3 ...] are rules.

4.2 Rule

There are two kinds of rules: qualified rules and at-rules. Qualified rules contain declarations for the styles in the document.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:margin (40px auto)
     #:font-family "Fira Sans"]))

"body{margin:40px auto;font-family:\"Fira Sans\";}"

In the example above, [body ...] is a qualified rule and each of #:margin (40px auto) and #:font-family "Fira Sans" are declarations.

At-rules contain elements that control the stylesheet itself.

Identifiers starting with @ can be troublesome to generate programmatically (for example, (string->symbol (~a "@" some-computation))). So [@ name ...] is an at-rule equivalent to [@name ...].

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [@import "other-stylesheet.css"]))

"@import \"other-stylesheet.css\";"

> (css-expr->css
   (css-expr
    [@font-face #:font-family "Fira Sans" #:src "..."]))

"@font-face{font-family:\"Fira Sans\";src:\"...\";}"

> (css-expr->css
   (css-expr
    [@media (and screen (#:min-width 700px))
     [body #:font-size 20px]]))

"@media screen and (min-width:700px){body{font-size:20px;}}"

In the example above, [@import ...], [@font-face ...] and [@media ...] are at-rules. They differentiate from qualified rules by the @ prefix. The rule [@import ...] is requesting the browser to load another stylesheet; it shows how at-rules handle expressions (in the example, the expression is "other-stylesheet.css"). The rule [@font-face ...] is defining a new font and showing how at-rules can include declarations (in the example, #:font-family "Fira Sans" and #:src "..." are declarations). Finally, the rule [@media ...] is declaring qualified rules that only apply under certain conditions; showing that at-rules can include qualified rules (in the example, [body ...]) in addition to expressions (in the example, (and ...)).

4.3 Nesting

CSS-expressions allow for arbitrary nesting of rules within one another, which is an extension to plain CSS generally found in preprocessors including Sass, Less and Stylus. The nested rules are unnested during the process of translating CSS-expressions into CSS.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [.menu #:width 700px
     [.item #:text-decoration none]]))

".menu{width:700px;}.menu .item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [.menu #:width 700px
     [@media (#:min-width 500px)
      #:color green]]))

".menu{width:700px;}@media (min-width:500px){.menu{color:green;}}"

> (css-expr->css
   (css-expr
    [@media screen
     [@media (#:min-width 700px)
      [body #:font-size 20px]]]))

"@media screen and (min-width:700px){body{font-size:20px;}}"

In the first example, nested qualified rules illustrate how to compactly define components. In the second example, a media query is nested within a qualified rule, which is convenient for responsive design. The third example shows how nested at-rules compose.

When nesting qualified rules, the default combinator for selectors is the descendant combinator, which in CSS is pronounced with a space—see example above. To combine selectors differently, it is necessary to explicitly refer to the selector of the parent qualified rule in the selector of the nested qualified rule. Accomplish that using &.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [.menu #:width 700px
     [(> & .item) #:text-decoration none]]))

".menu{width:700px;}.menu>.item{text-decoration:none;}"

In the example above, the declaration #:text-decoration none is only effective on .items that are immediate children of .menus.

The following kinds of parent selectors allow for an added suffix with &-: identifiers, namespaced selectors, prefixed selectors or combinations in which the last selector is one of the previous kinds.

Under special conditions one can declare nested rules that add a suffix to the parent selector. Accomplish that using &-.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [.menu #:width 700px
     [(&- item) #:text-decoration none]]))

".menu{width:700px;}.menu-item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [|#main| #:color blue
     [(&- sidebar) #:color pink]]))

"#main{color:blue;}#main-sidebar{color:pink;}"

4.4 At-Rule Expression

At-rule expressions occur in at-rules, after the @name and before any declarations or inner rules. In its simplest form, an at-rule expression is a value.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [@import "other-stylesheet.css"]))

"@import \"other-stylesheet.css\";"

In the example above, "other-stylesheet.css" is a value standing for an at-rule expression.

Multiple at-rule expressions in a list translate as a composite expression in CSS.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [@import ("other-stylesheet.css" screen)]))

"@import \"other-stylesheet.css\" screen;"

In the example above, the list ("other-stylesheet.css" screen) translated to the composite expression "other-stylesheet.css" screen in CSS.

Multiple at-rule expressions in a row translate to alternative expressions in CSS.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [@media screen print
     [body #:line-height 1.2]]))

"@media screen,print{body{line-height:1.2;}}"

In the example above, each of screen and print are at-rule expressions on their own. They translate to alternative expressions in CSS—separated by the comma.

Declarations can be at-rule expressions, and they have to be surrounded by parenthesis to be distinguished from declarations in the body of the at-rule.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [@media (#:min-width 700px)
     [body #:line-height 1.2]]))

"@media (min-width:700px){body{line-height:1.2;}}"

> (css-expr->css
   (css-expr
    [@font-face #:font-family "Fira Sans" #:src "..."]))

"@font-face{font-family:\"Fira Sans\";src:\"...\";}"

In the first example, (#:min-width 700px) is a declaration working as an at-rule expression. Note the surrounding parenthesis; they are necessary to differentiate from declarations in the at-rule body, the case which the second example illustrates.

The last type of at-rule expressions is operations involving other at-rule expressions.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [@media (and screen (#:min-width 700px))
     [body #:line-height 1.2]]))

"@media screen and (min-width:700px){body{line-height:1.2;}}"

> (css-expr->css
   (css-expr
    [@media (or screen print)
     [body #:line-height 1.2]]))

"@media screen or print{body{line-height:1.2;}}"

> (css-expr->css
   (css-expr
    [@media (not (and screen (#:min-width 700px)))
     [body #:line-height 1.2]]))

"@media not screen and (min-width:700px){body{line-height:1.2;}}"

> (css-expr->css
   (css-expr
    [@media (only screen)
     [body #:line-height 1.2]]))

"@media only screen{body{line-height:1.2;}}"

4.5 Declaration

Declarations set CSS properties. In CSS-expressions, declarations are the property names as keywords followed by values and an optional !important flag.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:line-height 1.2
     #:background-color black]))

"body{line-height:1.2;background-color:black;}"

> (css-expr->css
   (css-expr
    [body
     #:line-height 1.2 !important]))

"body{line-height:1.2 !important;}"

In the first example above, #:line-height 1.2 and #:background-color black are declarations. The properties names are #:line-height and #:background-color, while 1.2 and black are values. The second example above illustrates the use of the !important flag.

CSS-expressions also support an extension generally found in CSS preprocessors including Sass, Less and Stylus: nested declarations.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:font
     (#:size 18px
      #:family Helvetica)]))

"body{font-size:18px;font-family:Helvetica;}"

In the example above, the #:font common prefix has been factored out from the declarations of #:font-size and #:font-family.

Note that values can occur before the nested declarations.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:font italic
     (#:size 18px
      #:family Helvetica)]))

"body{font:italic;font-size:18px;font-family:Helvetica;}"

In the example above, italic is a value before the nested declarations (#:size ...).

4.6 Value

This simplest kinds of value are symbols, numbers and strings.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:font-style italic
     #:line-height 3.5
     #:font-family "Fira Sans"]))

"body{font-style:italic;line-height:3.5;font-family:\"Fira Sans\";}"

In the example above, italic is a symbol value, 3.5 is a number value and "Fira Sans" is a string value. Note how strings are quoted in the CSS output while symbols are not.

Similar to what happens in at-rule expressions, composite values must be enclosed in parenthesis.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:font (italic 18px "Fira Sans")]))

"body{font:italic 18px \"Fira Sans\";}"

In the example above, (italic 18px "Fira Sans") is a composite value.

A list of values is just laid out after the property name, without enclosing parenthesis.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:font-family "Fira Sans" sans-serif]))

"body{font-family:\"Fira Sans\",sans-serif;}"

In the example above, "Fira Sans" and sans-serif compose a list of values.

Writing colors as "#ff00dd" does not work, because the CSS would include quotes around the value.

Hexadecimal colors in CSS-expressions are just symbols, but it is necessary to escape the hash because of Racket’s parsing rules.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:color |#ff00dd|]))

"body{color:#ff00dd;}"

In the example above, one can write the color as either |#ff00dd| or \#ff00dd.

Measurement symbols can be troublesome to generate programmatically (for example, (string->symbol (~a some-computation "px"))). So (px 12) is a value equivalent to 12px.

Measurements are also just symbols.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:margin-left 12px]))

"body{margin-left:12px;}"

In the example above, 12px is a measurement. Valid measurement units are: %, em, ex, ch, rem, vw, vh, vmin, vmax, cm, mm, q, in, pt, pc, px, deg, grad, rad, turn, s, ms, hz, khz, dpi, dpcm and dppx.

Note that apply is a form in CSS-expressions. It results in CSS that looks like function application, which is not equivalent to unquoting from the CSS-expression and writing a Racket expression using Racket’s apply. For example, (apply calc 2px) translates to calc(2px), while (px ,(apply + '(1 1))) translates to 2px. Both are valid forms, useful in different contexts.

Use apply to write values that look like function application.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:color (apply rgb 20 30 40)]))

"body{color:rgb(20,30,40);}"

In the example above, (apply rgb 20 30 40) is a value that looks like a function application in CSS.

Note that operations on values are forms in CSS-expressions. They result in operations in CSS, which are not equivalent to unquoting from the CSS-expression and writing Racket expressions. For example, (+ 2px 3px) translates to 2px + 3px, while (px ,(+ 2 3)) translates to 5px. Both are valid forms, useful in different contexts.

Operations on values are also valid values.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [body
     #:width (apply calc (- 12px 2px))]))

"body{width:calc(12px - 2px);}"

In the example above, (- 12px 2px) is an operation. Valid operands are +, -, * and /.

4.7 Selector

The simplest kind of selector is an identifier.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [li #:width 700px]))

"li{width:700px;}"

In the example above, li is a selector that matches all occurrences of the <li> HTML tag.

Multiple selectors in a row turn into a list.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [li a #:width 700px]))

"li,a{width:700px;}"

In the example above, the rule matches any <li> and any <a> HTML tag.

Enclose selectors in parenthesis to combine them with the descendant combinator—which in CSS is pronounced with a space.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [(li a) #:width 700px]))

"li a{width:700px;}"

In the example above, the rule matches any link (<a>) which occurs anywhere within a list item (<li>).

Namespaced symbols can be troublesome to generate programmatically (for example, (string->symbol (~a some-computation "|" other-computation))). So (\| ns a) is a selector equivalent to ns\|a. Again, note how it is necessary to escape the pipe (|).

Selectors can occur under a namespace.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [ns\|a #:width 700px]))

"ns|a{width:700px;}"

In the example above, the rule only matches <a> tags under the ns namespace. Note that the pipe (|) needs escaping because of Racket’s parsing rules for identifiers.

Prefixed symbols can be troublesome to generate programmatically (for example, (string->symbol (~a some-computation "#" other-computation))). So (|#| main) is a selector equivalent to |#main|, and (|#| div main) is a selector equivalent to div#main. Again, note how it is necessary to escape the hash (#).

This applies to all prefixed selectors. For example, (|.| a menu) is equivalent to a.menu. Note that the dot alone (.) has special meaning in Racket, so it also requires escaping.

Selectors may require prefixes.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [|#menu| #:width 700px]))

"#menu{width:700px;}"

> (css-expr->css
   (css-expr
    [.menu-item #:width 700px]))

".menu-item{width:700px;}"

> (css-expr->css
   (css-expr
    [a:hover #:width 700px]))

"a:hover{width:700px;}"

> (css-expr->css
   (css-expr
    [a::before #:width 700px]))

"a::before{width:700px;}"

In the first example above, note that the hash—the prefix for selecting by id—needs escaping because of Racket’s parsing rules for identifiers. The alternative spelling \#menu works the same.

Note that apply is a form in CSS-expressions. It results in CSS that looks like function application, which is not equivalent to unquoting from the CSS-expression and writing a Racket expression using Racket’s apply. For example, (apply nth-child 2) translates to nth-child(2), while (|.| ,(apply string-downcase '(ODD))) translates to .odd. Both are valid forms, useful in different contexts.

Pseudo-classes that look like function applications use the apply form.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [(: a (apply nth-child 2)) #:width 700px]))

"a:nth-child(2){width:700px;}"

In the example above, (apply nth-child 2) translates to a pseudo-class that looks like function application: nth-child(2).

The arguments to those pseudo-classes that look like function applications can be selectors, other nested pseudo-classes that look like function application and An+B forms.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [(: a (apply not .classy)) #:width 700px]))

"a:not(.classy){width:700px;}"

> (css-expr->css
   (css-expr
    [(: a (apply nth-child odd)) #:width 700px]))

"a:nth-child(odd){width:700px;}"

> (css-expr->css
   (css-expr
    [(: a (apply nth-child even)) #:width 700px]))

"a:nth-child(even){width:700px;}"

> (css-expr->css
   (css-expr
    [(: a (apply nth-child 3)) #:width 700px]))

"a:nth-child(3){width:700px;}"

> (css-expr->css
   (css-expr
    [(: a (apply nth-child n)) #:width 700px]))

"a:nth-child(n){width:700px;}"

> (css-expr->css
   (css-expr
    [(: a (apply nth-child (n 2))) #:width 700px]))

"a:nth-child(2n){width:700px;}"

> (css-expr->css
   (css-expr
    [(: a (apply nth-child (n 2 1))) #:width 700px]))

"a:nth-child(2n+1){width:700px;}"

> (css-expr->css
   (css-expr
    [(: a (apply nth-child (n+ 2))) #:width 700px]))

"a:nth-child(n+2){width:700px;}"

In the first example above, (apply not .classy) is a pseudo-class that looks like function application whose argument another selector (.classy). The rest of the examples illustrate the An+B form.

See Nesting for the specific cases in which it is valid to add a suffix to the parent selector with &-.

In nested qualified rules, the selector & stands for the parent selector. The selector &- is a reference to the parent selector that allows for suffixes in limited cases.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [.menu #:width 700px
     [(> & .item) #:text-decoration none]]))

".menu{width:700px;}.menu>.item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [.menu #:width 700px
     [(&- item) #:text-decoration none]]))

".menu{width:700px;}.menu-item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [|#main| #:color blue
     [(&- sidebar) #:color pink]]))

"#main{color:blue;}#main-sidebar{color:pink;}"

Note that the || combinator requires escaping because of Racket’s parsing rules.

One can combine selectors in various ways.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [(+ .menu .item) #:text-decoration none]))

".menu+.item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [(> .menu .item) #:text-decoration none]))

".menu>.item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [(~ .menu .item) #:text-decoration none]))

".menu~.item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [((// for) .menu .item) #:text-decoration none]))

".menu /for/ .item{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [(\|\| .menu .item) #:text-decoration none]))

".menu||.item{text-decoration:none;}"

Attribute-based selectors use the attribute form.

Examples:
> (require css-expr)
> (css-expr->css
   (css-expr
    [(attribute .menu selected) #:text-decoration none]))

".menu[selected]{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [(attribute .menu (= href "...")) #:text-decoration none]))

".menu[href=\"...\"]{text-decoration:none;}"

> (css-expr->css
   (css-expr
    [(attribute .menu (~= href (case-insensitive "...")))
     #:text-decoration none]))

".menu[href~=\"...\" i]{text-decoration:none;}"

Valid operands for attributes are: =, ~=, ^=, $=, *= and \|=.

5 Grammar

  stylesheet = (rule ...)
     
  rule = [selector ...+ block-element ...+]
  | [@ name at-rule/expression ... block-element ...]
     
  block-element = declaration
  | rule
     
  at-rule/expression = value
  | declaration
  | (at-rule/expression ...+)
  | (at-rule/expression/operation/operator/unary at-rule/expression)
  | (at-rule/expression/operation/operator/n-ary at-rule/expression ...+)
     
  at-rule/expression/operation/operator/unary = not
  | only
     
  at-rule/expression/operation/operator/n-ary = and
  | or
     
  declaration = #:name value ... !important? (declaration ...+)?
     
  value = identifier
  | number
  | string
  | (value/measurement/unit number)
  | (value/operation/operator value ...+)
  | (apply name value ...+)
  | (value ...+)
     
  value/measurement/unit = %
  | em
  | ex
  | ch
  | rem
  | vw
  | vh
  | vmin
  | vmax
  | cm
  | mm
  | q
  | in
  | pt
  | pc
  | px
  | deg
  | grad
  | rad
  | turn
  | s
  | ms
  | hz
  | khz
  | dpi
  | dpcm
  | dppx
     
  value/operation/operator = +
  | -
  | *
  | /
     
  selector = name
  | (selector ...+)
  | selector/namespaced
  | (selector/prefixed/prefix selector? selector/prefixed/subject)
  | (attribute selector? selector/attribute/subject)
  | (selector/combination/combinator combinand:selector ...+)
  | &
  | (&- suffix)
     
  selector/namespaced = (\| name? name)
     
  selector/prefixed/prefix = |.|
  | |#|
  | :
  | ::
     
  selector/prefixed/subject = name
  | selector/function/application
     
  selector/function/application = (apply name selector/function/argument ...+)
     
  selector/function/argument = selector
  | selector/An+B
  | selector/function/application
     
  selector/An+B = odd
  | even
  | integer
  | n
  | (n integer integer?)
  | (n+ integer)
     
  selector/attribute/subject = name
  | selector/namespaced
  | 
(selector/attribute/operation/operator
 selector/attribute/operation/operand
 selector/attribute/operation/operand)
     
  selector/attribute/operation/operator = =
  | ~=
  | ^=
  | $=
  | *=
  | \|=
     
  selector/attribute/operation/operand = name
  | string
  | (case-insensitive string)
     
  selector/combination/combinator = +
  | >
  | ~
  | (// name)
  | \|\|

6 Acknowledgments

Thank you Matthew Butterick for Pollen—which motivated the creation of CSS-expressions—and for the feedback given in private email conversations. Thank you Greg Trzeciak for the early feedback. Thank you all Racket developers. Thank you all users of this library.

7 Changelog

This section documents all notable changes to CSS-expressions. It follows recommendations from Keep a CHANGELOG and uses Semantic Versioning. Each released version is a Git tag.

7.1 0.0.2 · 2017-03-08

7.1.1 Changed

7.2 0.0.1 · 2017-02-01

7.2.1 Added

8 References

  1. Understanding the CSS Specifications.

  2. CSS Snapshot 2015.

  3. CSS spec­i­fi­ca­tions.

  4. CSS Syntax Module Level 3.

  5. CSS Syntax Module Level 3 Editor’s Draft.

  6. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification.

  7. Cascading Style Sheets Level 2 Revision 2 (CSS 2.2) Specification.

  8. Selectors Level 3.

  9. Selectors Level 4.

  10. CSS Values and Units Module Level 3.

  11. Media Queries.

  12. Media Queries Level 4.

  13. CSS Color Module Level 3.

  14. CSS Color Module Level 4.

  15. CSS Fonts Module Level 3.

  16. Sass (Syntactically Awesome StyleSheets).

  17. Less.

  18. Stylus.