Skip to content

Instantly share code, notes, and snippets.

@twolfson
Last active November 26, 2019 18:27
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save twolfson/8117853 to your computer and use it in GitHub Desktop.
Save twolfson/8117853 to your computer and use it in GitHub Desktop.
CSS selector minifier concept

CSS selector minification is a missed opportunity of saved bytes. Currently, Google uses it but not much beyond that.

The concept is change .box to .b and <div class="box"> to <div class="b">.

There is room for issues with JavaScript so that should be treated as a nice-to-have and be conservatively avoided.

Approach

To convert HTML and CSS, it would be a 2 step process:

Minify CSS selectors

  1. Parse CSS into its AST
/* DEV: This is bad OOCSS but it is for example purposes */
.hello.world .arrow {
  background-color: blue;
}

to

// This is more AST pseudocode
{
  type: 'CSSRule',
  selector: '.hello.world .arrow',
  properties: [{
    name: 'background-color',
    value: 'blue'
  }]
}
  1. Break up and selectors (e.g. ids, classes)

Be sure this focuses exclusively on and not optimizing the selectors themself (e.g. * #id -> #id). That is the responsibility of CSS optimizers (e.g. http://bem.info/tools/optimizers/csso/). Do one thing and do it well.

// DEV: This step might be overkill with the format
{
  type: 'CSSRule',
  selector: '.hello.world .arrow',
  // Selectors: Array of selectors groups
  // Selector group: Array of selector properties
  selectors: [
    [{type: 'class', value: 'hello'}, {type: 'class', value: 'world'}],
    [{type: 'class', value: 'arrow'}]
  ],
  properties: [{
    name: 'background-color',
    value: 'blue'
  }]
}
  1. Transform and save selectors
var defn = {
  type: 'CSSRule',
  selector: '.h.w .a',
  // Selectors: Array of selectors groups
  // Selector group: Array of selector properties
  selectors: [
    [{type: 'class', value: 'h'}, {type: 'class', value: 'w'}],
    [{type: 'class', value: 'a'}]
  ],
  properties: [{
    name: 'background-color',
    value: 'blue'
  }]
};

// DEV: These are selectors as previously used
var map = [
  {type: 'class', original: 'hello', value: 'h'},
  {type: 'class', original: 'world', value: 'w'},
  {type: 'class', original: 'arrow', value: 'a'}
];
  1. Recompile CSS
.h.w .a {
  background-color: blue;
}

Library fragmentation (each of these should be its own node module)

  • CSS to AST parser, probably can use rework or similar
  • CSS selector parser (step 2)
  • CSS selector minifier (step 3) -- meant to work with AST pieces only
  • CSS AST lexer, probably use rework again
  • Module that combines all of them (as a vanilla node module)

Minify HTML selectors

  1. Convert HTML to AST or DOM
<div class="hello world">
  <div class="arrow">&gt;</div>
</div>

to

{
  type: HTMLDivElement,
  attributes: {
    className: 'hello world'
  },
  childNodes: [{
    type: HTMLDivElement,
    attributes: {
      className: 'arrow'
    },
    // childNodes: TextNode: value: '&gt;'
  }]
}
  1. Break down classes
{
  type: HTMLDivElement,
  classes: ['hello', 'world'],
  attributes: {
    className: 'hello world'
  },
  childNodes: [{
    type: HTMLDivElement,
    classes: ['arrow'],
    attributes: {
      className: 'arrow'
    },
    // childNodes: TextNode: value: '&gt;'
  }]
}
  1. Convert against a given map
var map = [
  {type: 'class', original: 'hello', value: 'h'},
  {type: 'class', original: 'world', value: 'w'},
  {type: 'class', original: 'arrow', value: 'a'}
];

generates

{
  type: HTMLDivElement,
  classes: ['h', 'w']
  attributes: {
    className: 'h w'
  },
  childNodes: [{
    type: HTMLDivElement,
    classes: ['a'],
    attributes: {
      className: 'a'
    },
    // childNodes: TextNode: value: '&gt;'
  }]
}
  1. Compile AST/DOM into HTML
<div class="h w">
  <div class="a">&gt;</div>
</div>

Library fragmentation (each of these should be its own node module)

  • HTML to AST parser
  • HTML class parser (step 2)
  • HTML class transformer (step 3) -- meant to work with AST pieces only
  • HTML AST lexer
  • Module that combines all of them (as a vanilla node module)

Minify JS selectors

If you haven't guessed, this would in theory be more of the same. However, this step can be potentially dangerous so an opt-in approach is best suited (e.g. whitelist).

We can provide an identity function that signfies as a special marker in the AST to switch over its selectors.

function k(selector) {
  return selector;
}

// We will write
var $arrow = $('.arrow');
// as (signifying an opt-in)
var $arrow = $(k('.arrow'));

// In development, runs as
var $arrow = $(k('.arrow'));

// In production, this *compiles* to
var $arrow = $('.a');

The selector would be broken up with the same module as the CSS selector breakup.

This can feed back into step 1 by providing a list of opted-in classes. However, this can be potentially frustrating if you forget to opt-in an already opted-in selector on new code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment