Universele CSS-reset haalt de padding van NLDD-web-componenten weg #205

Closed
opened 2026-07-26 20:05:22 +00:00 by robbertbos · 1 comment
Owner

Onze universele CSS-reset zet de padding van NLDD-web-componenten op nul, zonder dat er iets misgaat waar je het aan ziet. De component denkt dat hij zijn padding heeft, de browser rekent met 0.

Wat er aan de hand is

frontend/src/styles/reset.css:3:

* { margin: 0; padding: 0; }

Die * matcht ook de host van een custom element. En volgens CSS Scoping wint een regel uit het buitenste document van een :host-regel in de shadow root, ongeacht specificiteit. Een component die zichzelf padding geeft, kan zich daar niet tegen verdedigen.

NLDD zet bij drie componenten de hele doos op de host - achtergrond, rand, grid en padding samen:

:host { display: grid; border-radius: ...; box-shadow: inset 0 0 0 ...;
        background-color: ...; width: 100%; padding: var(--_padding); }

Dat geldt voor nldd-menu, nldd-switch en nldd-banner (grep -rl "padding: var(--_padding)" in dist/components).

Aangetoond, niet vermoed

In de draaiende app, via getComputedStyle:

  • nldd-menu (in de topnav, dus op elke pagina): computed padding: 0px.
  • nldd-banner: computed padding: 0px, terwijl --components-banner-padding op datzelfde element wel gewoon 12px opleverde. De inset-box-shadow die de rand tekent liep daardoor achter de knop langs in plaats van eromheen.

nldd-switch gebruiken we op dit moment niet.

Hoe het is gevonden

Via de bewaartermijn-PR: de actieknop in RetentionIntroBanner stak zichtbaar onder de rand van het infovak uit. Bij het opmeten bleek er niets over te lopen - de padding was er simpelweg niet.

Die PR bevat nu een reparatie per component: de banner herstelt zijn eigen padding vanuit onze scope via het gepubliceerde token --components-banner-padding, zonder privé-variabelen of magische getallen. Dat werkt, maar het is een pleister op een gat dat in de reset zit. nldd-menu heeft die pleister niet en mist zijn padding dus nog steeds.

Voorgestelde richting

Vervang de blanket-regel door een gerichte reset: de elementen die hem echt nodig hebben (koppen, alinea's, lijsten, figure, blockquote, dl/dd) in plaats van *. Dat is de gangbare praktijk zodra je een web-componentbibliotheek adopteert, want vrijwel alle van die bibliotheken stylen hun host.

Let op bij de uitvoering: dit raakt de hele app. Overal waar de blanket-regel stilzwijgend werk deed kan spatiëring verschuiven, en drie componenten krijgen hun bedoelde padding terug. Het verdient dus een eigen blik in de browser, niet alleen groene tests. Als de reset klopt, kan de reparatie in RetentionIntroBanner.vue er weer uit.

Los hiervan

NLDD zou dit voor consumenten kunnen verharden door de doos naar een wrapper ín de shadow root te verplaatsen en de host kaal te laten. Dan kan geen enkele buitenste reset er nog bij. Dat is een suggestie richting de bibliotheek, geen defect - hun huidige gedrag volgt de spec.

Onze universele CSS-reset zet de padding van NLDD-web-componenten op nul, zonder dat er iets misgaat waar je het aan ziet. De component denkt dat hij zijn padding heeft, de browser rekent met 0. ## Wat er aan de hand is `frontend/src/styles/reset.css:3`: ```css * { margin: 0; padding: 0; } ``` Die `*` matcht ook de host van een custom element. En volgens CSS Scoping wint een regel uit het buitenste document van een `:host`-regel in de shadow root, ongeacht specificiteit. Een component die zichzelf padding geeft, kan zich daar niet tegen verdedigen. NLDD zet bij drie componenten de hele doos op de host - achtergrond, rand, grid en padding samen: ``` :host { display: grid; border-radius: ...; box-shadow: inset 0 0 0 ...; background-color: ...; width: 100%; padding: var(--_padding); } ``` Dat geldt voor `nldd-menu`, `nldd-switch` en `nldd-banner` (`grep -rl "padding: var(--_padding)"` in `dist/components`). ## Aangetoond, niet vermoed In de draaiende app, via `getComputedStyle`: - `nldd-menu` (in de topnav, dus op elke pagina): computed `padding: 0px`. - `nldd-banner`: computed `padding: 0px`, terwijl `--components-banner-padding` op datzelfde element wel gewoon 12px opleverde. De inset-box-shadow die de rand tekent liep daardoor achter de knop langs in plaats van eromheen. `nldd-switch` gebruiken we op dit moment niet. ## Hoe het is gevonden Via de bewaartermijn-PR: de actieknop in `RetentionIntroBanner` stak zichtbaar onder de rand van het infovak uit. Bij het opmeten bleek er niets over te lopen - de padding was er simpelweg niet. Die PR bevat nu een reparatie per component: de banner herstelt zijn eigen padding vanuit onze scope via het gepubliceerde token `--components-banner-padding`, zonder privé-variabelen of magische getallen. Dat werkt, maar het is een pleister op een gat dat in de reset zit. `nldd-menu` heeft die pleister niet en mist zijn padding dus nog steeds. ## Voorgestelde richting Vervang de blanket-regel door een gerichte reset: de elementen die hem echt nodig hebben (koppen, alinea's, lijsten, `figure`, `blockquote`, `dl`/`dd`) in plaats van `*`. Dat is de gangbare praktijk zodra je een web-componentbibliotheek adopteert, want vrijwel alle van die bibliotheken stylen hun host. Let op bij de uitvoering: dit raakt de hele app. Overal waar de blanket-regel stilzwijgend werk deed kan spatiëring verschuiven, en drie componenten krijgen hun bedoelde padding terug. Het verdient dus een eigen blik in de browser, niet alleen groene tests. Als de reset klopt, kan de reparatie in `RetentionIntroBanner.vue` er weer uit. ## Los hiervan NLDD zou dit voor consumenten kunnen verharden door de doos naar een wrapper ín de shadow root te verplaatsen en de host kaal te laten. Dan kan geen enkele buitenste reset er nog bij. Dat is een suggestie richting de bibliotheek, geen defect - hun huidige gedrag volgt de spec.
Author
Owner

Measured on 0.8.78 in the running app: the damage is gone, because NLDD fixed it from their side. Closing, with a guard in place instead of a change to the reset.

What is true now

All three components named here have moved their box off the host into a shadow wrapper: banner.styles.js draws it on .banner, menu.styles.js on .menu__main, switch.styles.js on .switch__track. Measured on nldd-menu in the topnav:

host padding:          0px      (our reset still wins here)
.menu__main padding:   6px      (where the box lives now, untouched)

Where a box still has to sit on the host, NLDD shields it with !important, and says why in their own source: "!important: shields the row padding from consumer universal resets, which beat normal :host declarations per CSS Scoping" (drag-handle-cell, icon-cell, timeline-track-cell, list-item). That is exactly the hardening the last section of this issue asked them for.

The hazard itself is unchanged

Verified with a probe element in the same page, so this is not a guess about the cascade:

probe :host { padding: 12px; margin: 7px }  -> computed 0px / 0px
the same padding on an element in its shadow root -> computed 12px

So * { padding: 0 } still beats :host and always will. Nothing protects us except NLDD's current structure, which can change in a patch release - and did, twice, in both directions.

What landed instead of narrowing the reset

frontend/src/styles/nlddHostBox.test.ts. It reads the tags we actually use out of our own templates, resolves each to its component directory, and fails on a :host-only rule declaring padding or margin without !important. Same shape as nlddTokens.test.ts: three assertions, one of which proves the detector fires on input that should fail it (that probe caught a real bug in the first version, which split declarations per line and so missed padding on a one-line rule).

Narrowing the reset to a targeted element list stays on the table, but it would touch every page to fix nothing measurable today. If a future bump trips this test, that is the moment to reconsider.

Measured on 0.8.78 in the running app: the damage is gone, because NLDD fixed it from their side. Closing, with a guard in place instead of a change to the reset. ## What is true now All three components named here have moved their box off the host into a shadow wrapper: banner.styles.js draws it on .banner, menu.styles.js on .menu__main, switch.styles.js on .switch__track. Measured on nldd-menu in the topnav: host padding: 0px (our reset still wins here) .menu__main padding: 6px (where the box lives now, untouched) Where a box still has to sit on the host, NLDD shields it with !important, and says why in their own source: "!important: shields the row padding from consumer universal resets, which beat normal :host declarations per CSS Scoping" (drag-handle-cell, icon-cell, timeline-track-cell, list-item). That is exactly the hardening the last section of this issue asked them for. ## The hazard itself is unchanged Verified with a probe element in the same page, so this is not a guess about the cascade: probe :host { padding: 12px; margin: 7px } -> computed 0px / 0px the same padding on an element in its shadow root -> computed 12px So * { padding: 0 } still beats :host and always will. Nothing protects us except NLDD's current structure, which can change in a patch release - and did, twice, in both directions. ## What landed instead of narrowing the reset frontend/src/styles/nlddHostBox.test.ts. It reads the tags we actually use out of our own templates, resolves each to its component directory, and fails on a :host-only rule declaring padding or margin without !important. Same shape as nlddTokens.test.ts: three assertions, one of which proves the detector fires on input that should fail it (that probe caught a real bug in the first version, which split declarations per line and so missed padding on a one-line rule). Narrowing the reset to a targeted element list stays on the table, but it would touch every page to fix nothing measurable today. If a future bump trips this test, that is the moment to reconsider.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
robbertbos/waggle#205
No description provided.