Menu

D32. The theme's rules are defaults under the components; overrides are said (13 Oct 2026)

The session that built « Nietzsche et la grande santé » (13 Oct) lost time to a theme rule, .t-figure img { width: 100%; height: auto }, that silently beat its « atelier » layout’s own rule for its small plate: from 6 October the theme came after the component stylesheets, so it could restyle them without fighting specificity. The layout’s value had to move into theme.css, a component’s style living in two files and lost once the component is published to a library. The theme winning over components was a design flaw.

  • Order (render/cascade.ts): elabora.base < elabora.theme < elabora.components < elabora.overrides. The theme stylesheet’s rules are the course’s defaults; a component’s stylesheet wins over them and is complete on its own. What the theme overrides in components on purpose goes in its top-level @layer overrides { … } blocks, the layer elabora.overrides (a package keeps them in elabora/overrides.css; the theme file keeps its line numbers). This is the order the cascade-layers literature recommends (reset and defaults, then components, then overrides) and the one CSS authors and agents already know.
  • Weighed and set aside. Tokens alone (components read custom properties, the theme only sets them): kept as the advice for what a component wants tuned (var(--<name>, <default>), never set by the component), but a theme is free CSS on the player’s hooks, and tokens do not stop a general rule from reaching inside a component. Splitting the theme by selector (a rule that names a component overrides it, the others are defaults): no syntax to learn, but magic that devtools do not show, a selector analyser to maintain, and rules that change layer with their selector. A second file for overrides: as explicit, but one more file to know for a rare need. Keeping the theme last with a diagnostic only: the trap stays, and the diagnostic would fire on every component that uses a player hook.
  • Courses keep their look. A course whose course.json has no theme.cascade keeps the order of 6–13 October (theme-over-components: elabora.base < elabora.components < elabora.theme < elabora.overrides) and is told (I_THEME_CASCADE_OLD, printed by elabora validate); elabora init writes components-over-theme. No format version: the field is optional, its absence the old order. elabora theme upgrade (MCP elabora_upgrade_theme, @elabora/node edit upgradeThemeCascade) moves a course to the new order in one undoable change without changing its look: the theme’s declarations of properties that any component stylesheet of the course also sets go into an @layer overrides block, in order, inside their rules and at-rules, before the theme’s own overrides blocks; @keyframes and @property a component defines too go along; a component or a theme that sets all moves the whole theme. Properties that act on one another (a shorthand and its longhands, logical and physical sides, aliases) share a family and move together (propertyFamily), so every declaration of a family stays in one layer and the theme’s rules keep their outcome among themselves; against the components and the player, moved declarations stay above as before and the others meet no component. Measured on the « Nietzsche » module (22 screens, initial and answered at 1600×900, and 375×812): no pixel changed beyond capture noise; 135 declarations of 63 rules moved. The block is coarse (every theme rule on a property some component sets), so the upgrade says to move out the course-wide defaults afterwards. The examples got theme.cascade directly: their captures did not change, except the gloss notes of medieval-charts, whose emphasis is roman as the gloss component always asked (.mc-gloss-body em { font-style: normal }, overridden until now by the theme’s .t-text em).
  • Measured in the page (@elabora/node style-cascade.ts, run by elabora look, MCP elabora_look and elabora fit, the canvas and packages alike): for each rule of a component stylesheet and each theme rule that match one element and set one property, a declaration is taken out of its rule for a moment and the computed value read again, so media and container queries, specificity, layers and !important are the browser’s own. W_STYLE_COMPONENT_OVERRIDDEN: a component’s declaration overridden by a theme rule that does not name the component (the old order, or a broad rule in @layer overrides). W_STYLE_THEME_UNDER_COMPONENT: a theme rule that names a component (its .t-block--<id>, .t-layout--<id> or the classes of its own stylesheet) and gives way to it, outside @layer overrides. On that module it found two more cases of the same trap than the one reported: .t-text { line-height } overriding the « carte » and « frise » components. Revisit if: authors need the theme to win in general again (a per-course setting exists: theme-over-components), the upgrade’s block proves too coarse to edit (it could be narrowed with the page’s measure, at the cost of exactness across states nobody captured), or browsers give stylesheets a native way to scope a theme under components (@scope with lower proximity).