Execution — bi-07-css-engine
Steps extracted from CONCEPTS.md. Read the concepts first; this file is the doing.
Record results in observation.md; tick checkpoints in verification.md.
6. Lab — mini-browser M4–M6
Build a CSS engine supporting div {}, .foo {}, #header {}, .parent .child {}.
- Tokenizer + parser producing a rule list.
- Selector representation and right-to-left matching.
- Specificity, cascade, inheritance, computed style.
- Deliberately naive first: on any DOM change, recompute every element's style. Measure on a 10,000-node tree.
- Then build an index. Bucket rules by key selector. Measure again.
- Then build invalidation sets. On
classList.add(x), consult a precomputed map from class → affected-descendant descriptors. Measure again. - Add a selector your analysis cannot handle precisely (a sibling combinator). Implement the subtree fallback. Measure the cliff.
Deliverable: a table of recalculated-element counts and wall time for stages 4, 5, 6 and 7, plus a written explanation of where each speedup came from. Stage 7 is the point of the lab — you must be able to state exactly which selector shapes cost you precision.
7. Failure Lab
- Invalidate too little. Deliberately omit sibling invalidation. Construct markup that renders incorrectly. This is the bug class the complexity exists to prevent.
- Invalidate too much. Fall back to whole-document invalidation always. Show it is correct and unusable.
- Style-sharing defeat. Add a unique inline style to every row of a 10,000-row list. Measure memory and recalc time against the shared version.
- The container-query cycle. Construct a case where an element's style depends on a container's size, which depends on that element's size. Predict the outcome, then check what real browsers do and find the rule that breaks the cycle.
8. Debugging Exercise
- DevTools → Performance: capture a
classList.addthat triggers recalc. Find the number of elements affected. Find where DevTools reports it. - Construct two changes with identical visual effect but 100× different recalc scope. Explain the difference by selector shape alone.
- Trace with the
blinkcategory and locate style-recalc trace events. Correlate one back to source by grepping its literal event name (bi-01, rung 3 → rung 1). - Find, in the local checkout, the code path that decides to fall back to subtree invalidation. Write down the exact condition.