My Own CSS Engine
I’ve stated before how I vital I see it is for new browser engines to be developed, in order to fight the collapse of browser divirsity and push the web to simplify enough to continue surviving. For my small part of this I implemented a CSS engine from scratch.
I hope my efforts will aid the development of browsers like Vado, or perhaps even something more novel like one with a voice UI. While I don’t believe in the hype of “virtual assistants” like Siri and Cortana, I am particularly interested in the later due to my experience using “smart” TVs. Because I know the inconvenience I experienced there must be blockers to other differently-abled people.
So if you’re keen please contact me via email or the fediverse so I can best help you!
Using Stylish Haskell
I implemented my CSS engine “Stylish Haskell” in Haskell due to it’s pattern matching syntax. This makes it trivial for you to declarative define the CSS properties and how it maps to your datastructures. Though I find Haskell very will suited for the internal code as well.
Put simply, Stylish Haskell has two main entry points each of which outputs a generic type constructed from a dedicated typeclass. The
parse function outputs a type which knows how to parse at-rules and add style rules, for which I define a basic implementation you can extend.
cascade function takes that basic implementation in order to output a type which knows how to parse a given CSS properties and translate them into setters. Furthermore this base stylesheet type wraps this typeclass in order to check the syntax of all CSS properties (and expand CSS shorthand properties) in order to ensure invalid properties don’t override good ones in CSS cascade. Web developers often rely on that for graceful degradation!
Implementation of Stylish Haskell
The parser is implemented around haskell-css-syntax with a couple of it’s own parser combinator utils, and some utilities for skipping over tokens until we reach a known good state again. It was trivial to convert from a parser combinator into a token skipping function using the builtin
Though all the real work is done by the CSS cascade function, which goes through the following steps when storing and retrieving style rules in memory:
- Split off
!importantproperties into higher priority style rules.
- Expand CSS shorthands and verify property syntax.
- Sort style rules by selector specificity/line number/stylesheet priority.
- Evaluate CSS selectors. Haskell had some great syntax for this, but it’s interesting to consider the performance benefits a JIT interpretor for Haskell would have.
- Look up selectors that at least partially match the element in a hashmap index.
After that all properties are loaded into a hashmap for CSS cascade to be read back out into the output trait for CSS inheritance.