Skip to content

Instantly share code, notes, and snippets.

@tomhodgins
Last active December 17, 2018 21:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomhodgins/b803e0cf47aba8f69fdf2a52c16a6563 to your computer and use it in GitHub Desktop.
Save tomhodgins/b803e0cf47aba8f69fdf2a52c16a6563 to your computer and use it in GitHub Desktop.

CSS is a declarative stylesheet language, this means you describe the result you want to see rather than the steps the browser would need to take to arrive at that result. You can write CSS selectors that target tags matching a certain description in a certain state, and then you declare the values that different styling properties should have at that time.

There are a few aspects of structured programming living within CSS too. Animations that define sequences of keyframes, media queries, and feature queries are all examples of structured programming inside the declarative language of CSS.

Custom properties (commonly called “CSS variables”) expand CSS to include reactive programming as well. Any time a CSS variable is changed, the new values are updated live in your styles everywhere they have been used. So it's possible for CSS to declare a value that represents a stream of data.

Have you ever felt like CSS was too low-level to provide the abstractions for you to describe what you wanted the result to be, or felt like you were describing the steps to achieve the result you wanted instead?

Though you can't extend CSS with CSS, you can extend CSS in CSS. Even though CSS doesn't include the power to define new functionality for itself, its syntax is flexible enough that we can describe the results we want to see using our own custom properties, custom selectors, and custom at-rules and define the functionality to support those features using JavaScript.

Most people writing CSS think that the only valid CSS they can write is CSS that uses its 'standard library' of supported features, but the syntax of CSS allows us to express valid styles that don't have features defined for them, so we can write styles in valid CSS syntax that describe the result we want to see at a higher level than the CSS features the browser understands, and create abstractions out of JavaScript that output the lower-level CSS by writing and using Higher Order Functions that use a string combinator to output a CSS stylesheet, or a function to update the value of a CSS variable to whatever it should be at that given moment. And you're able to leverage the different declarative, imperative, and reactive programming models CSS uses in the styling abstractions you write. We can call this high-level CSS.

The biggest revelation that happens when extending CSS with JavaScript, that totally flips how we usually think about styling upside down, is that CSS is event-driven in nature.

Most of us usually think that styling begins with an HTML element. Whether it's writing a CSS selector to target an element in a certain state, or using a reference to an HTML element in JavaScript and applying styles to it — in reality every application of a style to an HTML element follows some kind of event happening in the browser. Even if it's simply the page itself loading.

For example when you are defining hover styles or other styles that respond to different states caused by user interaction or other things happening on the page, no matter how you have written your CSS, the styles are not split by elements but they are split by the events that drive the changes that make those styles either apply or not apply to the elements.

When you want to think about extending your styles to include higher level abstractions you don't want to begin with the elements themselves, you want to start by listening to events that happen in the browser, and only then do you try to figure out which elements to apply the styles to. Everything is more straightforward when you start with the events, rather than with the elements.

This is the approach I call "Event-Driven Virtual Stylesheets", and it's the most powerful and natural way I've found for extending CSS because imitates how CSS already works.

When you extend CSS using the event-driven programming model it's easy to set up and use your extended styles, as well as integrate your styles into any JS framework or frontend code you might be using. Since JavaScript can listen to (and create) custom events, this way of controlling when the styles update is also easy to integrate with other features JavaScript is aware of like Mutation Observer, Resize Observer, and Intersection Observer.

Since you can work with the reactive programming model in CSS by using CSS variables, you can easily leverage any streams of data JavaScript is aware of for styling purposes. And with JavaScript logic you can choose to write abstractions for your styles using any programming style possible with JavaScript. The best thing is whether you're extending a custom property, custom selector, or custom at-rule, you don't have to wait for a standards organization to come to an agreement on a new name for each feature and hope it's one you like using — when you define your own custom CSS extensions you can work with whatever names you want to invent.

We can use some JavaScript functions to extend CSS to make it a higher-level styling language, and we can design the styling language we want to use for ourselves, all while writing valid CSS.

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