css.js

css.js is a tiny piece of JavaScript that reinterprets CSS and SCSS as an object. It could be used to build a SCSS parser, which is why I am sharing it.

Structure

[CSS Object] {
  "parent": "object" /* circular reference */
  "block": "array", /* children */
}

[CSSRule Object] {
  "selector": "string, object", /* like "h1", or CSSSelector */
  "block": "array" /* children */
}

[CSSSelector Object] {
  "identifier": "string", /* like "media" */
  "parameters": "string" /* like "(max-width:40em)" */
}

[CSSDeclaration Object] {
  "property": "string", /* like "font-weight" */
  "value": "string" /* like "bold" */
  "block": "array" /* children, like font: { weight: bold; } */
}

Example: SCSS

$defaultColor: #222;
$defaultMargin: 1em;
$blue: #00F;

* {
  box-sizing: border-box;
  font-size: 100%;
  margin: 0;
}

html {
  color: $defaultColor;
  font-family: sans-serif;
}

article, aside, p, pre, section {
  margin-bottom: 1em;

  &:last-child {
    margin-bottom: 0;
  }
}

.content-navigation {
  border-color: $blue;
  color: darken($blue, 10%);
}

.callout {
  border: {
    color: $defaultColor;
    style: solid;
    width: 1px;
  }
  padding: $defaultMargin / 2;
  margin: $defaultMargin / 2;
}

table {
  margin-bottom: 2em;

  &:last-child {
    margin-bottom: 0;
  }

  td {
    text-align: left;

    &.r {
      text-align: right;
    }
  }
}

li {
  font: {
    size: 150%;
    weight: bold;
  }
  @media (max-width: 40em) {
    font-size: 100%;
  }
}

Example: Parsed SCSS

Circular references has been ommitted.

{
  "block": [
    {
      "property": "$defaultColor",
      "value": "#222"
    },
    {
      "property": "$defaultMargin",
      "value": "1em"
    },
    {
      "property": "$blue",
      "value": "#00F"
    },
    {
      "selector": "*",
      "block": [
        {
          "property": "box-sizing",
          "value": "border-box"
        },
        {
          "property": "font-size",
          "value": "100%"
        },
        {
          "property": "margin",
          "value": "0"
        }
      ]
    },
    {
      "selector": "html",
      "block": [
        {
          "property": "color",
          "value": "$defaultColor"
        },
        {
          "property": "font-family",
          "value": "sans-serif"
        }
      ]
    },
    {
      "selector": "article, aside, p, pre, section",
      "block": [
        {
          "property": "margin-bottom",
          "value": "1em"
        },
        {
          "selector": "&:last-child",
          "block": [
            {
              "property": "margin-bottom",
              "value": "0"
            }
          ]
        }
      ]
    },
    {
      "selector": ".content-navigation",
      "block": [
        {
          "property": "border-color",
          "value": "$blue"
        },
        {
          "property": "color",
          "value": "darken($blue, 10%)"
        }
      ]
    },
    {
      "selector": ".callout",
      "block": [
        {
          "property": "border",
          "block": [
            {
              "property": "color",
              "value": "$defaultColor"
            },
            {
              "property": "style",
              "value": "solid"
            },
            {
              "property": "width",
              "value": "1px"
            }
          ]
        },
        {
          "property": "padding",
          "value": "$defaultMargin / 2"
        },
        {
          "property": "margin",
          "value": "$defaultMargin / 2"
        }
      ]
    },
    {
      "selector": "table",
      "block": [
        {
          "property": "margin-bottom",
          "value": "2em"
        },
        {
          "selector": "&:last-child",
          "block": [
            {
              "property": "margin-bottom",
              "value": "0"
            }
          ]
        },
        {
          "selector": "td",
          "block": [
            {
              "property": "text-align",
              "value": "left"
            },
            {
              "selector": "&.r",
              "block": [
                {
                  "property": "text-align",
                  "value": "right"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "selector": "li",
      "block": [
        {
          "property": "font",
          "block": [
            {
              "property": "size",
              "value": "150%"
            },
            {
              "property": "weight",
              "value": "bold"
            }
          ]
        },
        {
          "selector": {
            "identifier": "media",
            "parameters": "(max-width:40em)"
          },
          "block": [
            {
              "selector": "li",
              "block": [
                {
                  "property": "font-size",
                  "value": "100%"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Example: Rendered CSS

* {
  box-sizing: border-box;
  font-size: 100%;
  margin: 0;
}

html {
  color: #222;
  font-family: sans-serif;
}

article, aside, p, pre, section {
  margin-bottom: 1em;
}

article:last-child, aside:last-child, p:last-child, pre:last-child, section:last-child {
  margin-bottom: 0;
}

.content-navigation {
  border-color: #00F;
  color: #00C;
}

.callout {
  border-color: #222;
  border-style: solid;
  border-width: 1px;
  padding: 0.5em;
  margin: 0.5em;
}

table {
  margin-bottom: 2em;
}

table:last-child {
  margin-bottom: 0;
}

table td {
  text-align: left;
}

table td.r {
  text-align: right;
}

li {
  font-size: 150%;
  font-weight: bold;
}

@media (max-width: 40em) {
  li {
    font-size: 100%;
  }
}

Thank you