Skip to content

Instantly share code, notes, and snippets.

@zspecza
Last active December 17, 2022 21:01
Show Gist options
  • Save zspecza/1301b6d9c4f182bdd6e1 to your computer and use it in GitHub Desktop.
Save zspecza/1301b6d9c4f182bdd6e1 to your computer and use it in GitHub Desktop.
The Coolest CSS Pre-Processor MicroSpec You'll Ever See

CSS Preprocessor Micro-Specification

Introduction

This is a conceptual specification for what could/would/should be the preprocessor to end all preprocessors. Unfortunately, it is just that - a specification. I had made quite a few attempts to create a CSS preprocessor, but I am far too inexperienced with advanced programming to succeed at this task. I got as far as the scanner and half of a half-working tokenizer before proceeding to pull my hair out. So I gave up, and decided just to list the proposed features here for reference. Perhaps these will make their way into existing pre-processors, or even inspire the birth of a new one.

First off, the basic features:

Pretty much everything the 3 leading pre-processors offer in terms of:

  1. Variables
  2. Comments
  3. Mixins
  4. Functions
  5. Conditionals
  6. Logic Operators
  7. Arithmetic Operators
  8. Built-in functions
  9. Color operations
  10. Property lookup (like in Stylus)
  11. Nesting
  12. Embedded Media Queries
  13. @extend
  14. Placeholder selectors
  15. Block Mixins
  16. @import'ing

Basically, the general rule is that if it exists in at least one of the 3 major CSS preprocessors, it should exist here. Also, every feature should work everywhere, no matter where it is in the context of the code. Variables should be easily usable inside media queries, and dynamic @importing based on the truthfulness of a variable should work.

Property lookup improvements

Stylus offers a feature called property lookup - it is used inside a selector and "bubbles" up all the way to the root until it finds that it exists - which allows you to get the value of properties from a parent context, for example - if I want to change text colour on a button between black/white depending on the color of the background:

.parent
  background-color: black
.button
  color: light(@background-color) ? black : white

An improvement would be the ability to specify just how far to bubble up using a 0-based index and an optional number of selectors to pick from (imagine this second value as bubbling down rather than up):

@property // bubble to root
@property(0) // only the current selector
@property(1) // only the current selector or it's parent
@property(1, 1) // only the parent selector

Handy shortcuts in selectors

What is a pre-processor if not DRY?

  1. Optional child selector:

    .a > .b? > .c {}
    

    compiles to:

    .a > .c, .a > .b > .c {}
    
  2. Multiple Children:

    #long-selector > .child > (.grandchild, .other-grandchild) {
      // code
    }
    

    compiles to:

    #long-selector > .child > .grandchild,
    #long-selector > .child > .other-grandchild {
      // code
    }
    

Mixin Improvements

  1. Mixins should be able to detect the use of other mixins within the same selector:

    if (selector.mixins.clearfix() is true) {}
    
  2. a @has directive that allows mixins and functions to inspect any selector at the current point of execution:

    if (body @has color: blue) {} or if (.user-avatar @has border-radius) {}
    

Extras

  1. ability to define a property on an element from within an entirely different element:

    .selector {
      if (condition) {
        define .other-selector { property: value }
      }
    }
    

    compiles to:

    .selector {
    
    }
    .other-selector {
    
    }
    
  2. ability to overwrite parent properties using the same syntax as property lookup

    .selector {
      @color(1, 1): red
    }
    
  3. BIF to remove all declarations associated with a selector for use in mixins e.g.

    .selector {
      remove-declarations();
    }
    

    or a specific declaration:

    .selector {
      remove-declarations(@color(1), @font-size(1));
    }
    
  4. An events system to monitor variables for changes/redefinition and execute code when it happens:

    variable = 'red'
    
    .selector
      color: variable
      mixin()
    
    mixin()
      @change variable
        define .selector
          color: variable
    
    variable = 'blue'
    

    You may be thinking "Why would this be useful?" - I'll tell you. Let's say you have a mixin that relies on a settings variable being true. Now, let's assume that mixin overrides something depending on if the setting is true. There's no way the user interacts with this mixin, it's simply there to override something else depending on the truthfulness of a value encountered before it's execution.

What if the user decides to change the setting outside of a conventional settings file? Your override mixin is broken, that's what happens. But with a @change event - the mixin can listen for changes and re-call itself after the change. Nifty, huh?

@mauro-balades
Copy link

Hi! I'm currently doing my own css preprocessor (https://github.com/mauro-balades/aurora.css). I love some of the ideas you mentioned and I was wondering if you would we cool with me taking inspirations from these.

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