Skip to main content

max / goingson

Adopt CSS cascade layers so ordering stops being specificity's job @layer base, components, responsive. A layer beats specificity outright, so a responsive rule now wins over a component rule wherever it sits in the file, and inserting a component rule can no longer silently kill one. That hazard was the reason for this. `.ui-mode-mobile .foo` was (0,2,0) and `.foo` was (0,1,0), so which won was decided by a prefix; removing the prefix made file position start mattering, two rules had to be re-homed on the spot and two more were found already dead. Sections 25 and 59 exist only to manage cascade order and can now be collapsed, which is the next commit. Wrappers are inserted without re-indenting the 9800 lines they enclose, so the diff is the seven inserted constructs rather than the whole file. VERIFIED WITH A BEFORE/AFTER CASCADE DIFF, which the task said did not exist and which turned out to be the only reason this was safe. It parses every rule, models each rule's own selector as an element, resolves every property against every other rule that could match it, and reports where the winner changes between (importance, specificity, order) and (importance, layer, specificity, order). Control: demoting `responsive` below `components` produces 96 changes, so it detects what it claims to. Four winners changed. One was a real regression and is fixed here: `.event-row-virtual:last-child { border-bottom: none }` in §16 was outranked by §59's `.event-row-virtual`, so the last row of a compact event list would have grown a border the wide shell does not give it. §59 now restates it. The other three stand: - `.month-heatmap-cell` hover: §60 (touch) now beats the component rule. That is a FIX. §60 exists to disable hover on touch and currently fails to. - `.main-content` max-width in print: `100%` gives way to §24's `none`. Same intent, since both exist to not constrain it. - `.task-row:hover` background: print against a max-width 599px query. Print renders around 816px, so the two never co-occur; the diff over-reports across mutually exclusive queries deliberately. PRINT STAYS IN `components`, at its current position, which preserves today's print output exactly. Giving it a layer above `responsive` is arguably right and changes what prints in 8 places, because print currently loses to the component rules in §49-70 that follow it. Nobody has looked at that output, so it is filed rather than changed. UTILITIES stay in `components` for the same reason: they do not beat later component rules today either, so a lower layer would have changed behaviour. `.hidden` is `!important` and is unaffected.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-02 19:00 UTC
Signed with PGP, not checked
Commit: 6ae3621e8aba73ab60c02fc54ad3b2da70115c50
Parent: 15dac71
1 file changed, +67 insertions, -0 deletions
@@ -252,6 +252,45 @@
252 252
253 253 */
254 254
255 + /* ============================================================================
256 + CASCADE LAYERS
257 + ============================================================================
258 + @layer base, components, responsive;
259 +
260 + Earlier in the list = lower priority. A layer beats specificity outright, so
261 + a responsive rule now wins over a component rule wherever it sits in this
262 + file, and inserting a component rule can no longer silently kill one.
263 +
264 + That is the whole point. Before this, `.ui-mode-mobile .foo` (0,2,0) beat
265 + `.foo` (0,1,0) and which rule won was decided by a prefix; removing the
266 + prefix made file position start mattering, and four rules had to be re-homed
267 + or were found already dead. Sections 25 and 59 exist only because of it.
268 +
269 + base the reset, the token block, typography (1-3)
270 + components everything a component owns at rest, plus the
271 + utilities and the print block (4-23, 26-58, 61-70)
272 + responsive every width and capability block (24-25, 59-60)
273 +
274 + `makeover` is declared ahead of these in index.html: the generated
275 + stylesheets are the design system and this file overrides them.
276 +
277 + WHERE PRINT SITS, and why it is not its own layer. Section 48 holds the only
278 + @media print block. It stays in `components`, at its current position, which
279 + preserves today's print behaviour exactly. Giving print a layer above
280 + `responsive` is arguably right and would change what prints in 8 places,
281 + because print rules currently lose to the component rules in sections 49-70
282 + that follow them. Nobody has looked at that output, so it is filed rather
283 + than changed here.
284 +
285 + UTILITIES are in `components` deliberately. They do not currently beat later
286 + component rules either -- `.mb-section` at the top of the file loses to any
287 + component rule setting margin-bottom below it -- so putting them in a lower
288 + layer would have changed behaviour, where leaving them here does not.
289 + `.hidden` is `!important` and is unaffected by any of this.
290 + ============================================================================ */
291 + @layer base, components, responsive;
292 +
293 + @layer base {
255 294 /* 1. Font Face */
256 295 @font-face {
257 296 font-family: 'Reglo';
@@ -405,6 +444,9 @@
405 444 font-size: 16px;
406 445 }
407 446
447 + }
448 +
449 + @layer components {
408 450 /* 4. Utility Classes (Shadow, Hover, Border) */
409 451 /* --- LAYOUT UTILITIES */
410 452 /* Compose: .row-flex + .row-flex-{bound,peer,group,section} for
@@ -3396,6 +3438,10 @@
3396 3438 }
3397 3439 .form-alt-path .button--link { padding: 0; font-size: inherit; }
3398 3440
3441 + }
3442 +
3443 + /* Sections 24-25: the wide-shell tuning and the compact base pass. */
3444 + @layer responsive {
3399 3445 /* 24. Responsive - Wide Shell Tuning (600-1024px, and 1400px up) WIDTH
3400 3446
3401 3447 Both blocks below used to carry a `.ui-mode-desktop` prefix, which was
@@ -3497,6 +3543,9 @@
3497 3543 }
3498 3544
3499 3545
3546 + }
3547 +
3548 + @layer components {
3500 3549 /* 26. Screen Reader & Accessibility */
3501 3550 .sr-only {
3502 3551 position: absolute;
@@ -7466,6 +7515,10 @@
7466 7515 display: none;
7467 7516 }
7468 7517
7518 + }
7519 +
7520 + /* Section 59-60: the compact pass and the capability pass. */
7521 + @layer responsive {
7469 7522 /* 59. Compact Responsive (late pass) WIDTH */
7470 7523
7471 7524 /* --- Safe area padding + room for mobile tab bar --- */
@@ -7870,6 +7923,16 @@
7870 7923 border-bottom: var(--border-width) solid var(--surface-overlay);
7871 7924 }
7872 7925
7926 + /* Restates §16's `.event-row-virtual:last-child { border-bottom: none }`,
7927 + which this pass now outranks. Before layers it won on specificity and
7928 + source order together; a layer beats both, so the last row in a compact
7929 + list would have grown a border the wide shell does not give it. The one
7930 + real regression the layer adoption introduced, and the reason the
7931 + before/after cascade diff was worth writing. */
7932 + .event-row-virtual:last-child {
7933 + border-bottom: none;
7934 + }
7935 +
7873 7936 .event-cell-date {
7874 7937 font-weight: 700;
7875 7938 font-size: var(--font-size-sm);
@@ -8189,6 +8252,9 @@
8189 8252 display: none;
8190 8253 }
8191 8254
8255 + }
8256 +
8257 + @layer components {
8192 8258 /* 61. Kanban Board */
8193 8259
8194 8260 /* View Toggle */
@@ -9881,3 +9947,4 @@
9881 9947 justify-content: flex-start;
9882 9948 }
9883 9949 }
9950 + }