Alfian Yusuf Abdullah
All work

2025 · Abandoned

A single-file CSS engine that renders to canvas and falls over on deeply nested selectors

A toy CSS parser and layout engine with no dependencies. It worked until it met a selector four levels deep, and I stopped there.

Role
Build
Stack
TypeScript, Canvas, Parsing
Source
Repository

The goal was a CSS engine in one file with no dependencies. Not a browser engine, just enough to take a stylesheet and a flat list of boxes and paint a rectangle. I got about 80% of the way to something I would call correct and then hit a wall I did not want to climb.

The part that worked

Specificity resolution and the cascade were straightforward once I stopped trying to be clever. Sort by origin, then specificity, then document order. That is the whole rule.

function compare(a: Rule, b: Rule): number {
	if (a.origin !== b.origin) return a.origin - b.origin;
	if (a.specificity !== b.specificity) return a.specificity - b.specificity;
	return a.order - b.order;
}

Layout for the subset I supported was also fine: block, inline, and a fixed grid. No floats, no flex, no min-content. Choosing to exclude those kept the whole thing under 900 lines and still useful for the one thing I wanted it for.

The wall

Descendant combinators with resugaring. .a .b .c .d requires walking the ancestor chain per node, which is fine, but matching it against a live tree while styles are still being resolved means every pass can invalidate the previous one. I needed a proper fixpoint computation or I needed to give up.

I gave up. The honest reason is that I had already learned the thing I wanted to learn about specificity, and the remaining work was engineering rather than discovery.

What is left

The repository works for flat stylesheets and fails loudly on nested ones. That is a reasonable place to leave it, and I would rather document the limit than pretend the tool is more general than it is.