Fix the CSS defects the NLDD migration left behind #207

Merged
robbertbos merged 1 commit from fix-silent-css-breakage into main 2026-07-26 21:17:57 +00:00
Owner

Eight CSS defects, found two ways: a sweep of main for the shapes the NLDD migration kept producing, and then looking at the result in a running app.

The common thread is that none of them raised a signal. vue-tsc does not read CSS values, ESLint does not parse <style module> values, no test asserts on computed style, and a var() on a property that does not exist simply drops its declaration. Every one of these was found by measuring or by eye.

Found by the sweep

CardShell was a missed site of the #206 pattern. It suppresses the outline and sets --components-card-box-shadow, a custom property the card reads inside its shadow DOM, but never box-shadow itself. The global rule's opaque 6px band of page background still painted on the host, wiping out the drop shadow of the card above it - tabbing down the list made it blink away row by row.

My check for this in #206 gave a false negative: it grepped for the substring box-shadow, which --components-card-box-shadow contains. Re-run anchored on the property, src is clean.

The Welkom heading was never centred. nldd-title resets slotted children with text-align: start !important and says so outright in its source: "a host cannot centre or justify our text". The two lines written to centre it did nothing. The host is shrunk and centred instead - align-self would not work either, the parent is a plain block.

The dev user-switcher lost its dropdown arrow. background: is the shorthand and resets background-image, which is where the chevron comes from in the global select skin.

.wg-prose-inline left a gap where list markers used to be. It collapses lists to inline and zeroes their margin, but left the 24px padding-left from .wg-prose, so a one-line message summary containing a list showed a blank gap mid-sentence.

Found by looking

The global :focus-visible rule mutated the element's radius. An outline follows the element's own border-radius anyway, so the declaration only ever added a side effect. It showed on the breadcrumbs, which are inline links: a 4px radius on a 17px box drew a pinched pill.

In the card modal the crumb ring was clipped. .title truncates with overflow: hidden, which clips at the padding box, and a ring sits 2px outside the line box. On a card row, where nothing clips, the same ring was already correct - which is what located the cause. Padding on every side with a matching negative margin gives it room and leaves the text exactly where it was.

overflow-clip-margin expresses this directly and was verified in a browser to keep text-overflow working. Rejected on support: it is absent in Safari and would silently degrade to a clipped ring, which is the wrong trade for a focus indicator.

The empty states broke their line at 35 characters against a readable measure of 45-75. nldd-inline-dialog caps its body at var(--primitives-area-480) and exposes only icon-colour tokens, so that primitive is widened locally. Both sentences now fit on one line.

Its action button spanned the whole dialog, because the component wraps its actions slot in a vertical nldd-button-group and a block-level button fills a column flex. No attribute exists for a content-sized button, so the host is sized and centred: 160px instead of 640px.

Verified

Every fix measured in a running preview, with real keyboard focus for the rings. vue-tsc clean, 570 tests green on a cold vite cache, build green, eslint 0 errors.

Related: #204 tracks the missing guard. Most of these would have been caught by a rule that resolves every var() against the declared token set and flags partial overrides of shared rules.

Eight CSS defects, found two ways: a sweep of `main` for the shapes the NLDD migration kept producing, and then looking at the result in a running app. The common thread is that none of them raised a signal. `vue-tsc` does not read CSS values, ESLint does not parse `<style module>` values, no test asserts on computed style, and a `var()` on a property that does not exist simply drops its declaration. Every one of these was found by measuring or by eye. ## Found by the sweep **`CardShell` was a missed site of the #206 pattern.** It suppresses the outline and sets `--components-card-box-shadow`, a custom property the card reads *inside* its shadow DOM, but never `box-shadow` itself. The global rule's opaque 6px band of page background still painted on the host, wiping out the drop shadow of the card above it - tabbing down the list made it blink away row by row. My check for this in #206 gave a false negative: it grepped for the substring `box-shadow`, which `--components-card-box-shadow` contains. Re-run anchored on the property, `src` is clean. **The Welkom heading was never centred.** `nldd-title` resets slotted children with `text-align: start !important` and says so outright in its source: *"a host cannot centre or justify our text"*. The two lines written to centre it did nothing. The host is shrunk and centred instead - `align-self` would not work either, the parent is a plain block. **The dev user-switcher lost its dropdown arrow.** `background:` is the shorthand and resets `background-image`, which is where the chevron comes from in the global `select` skin. **`.wg-prose-inline` left a gap where list markers used to be.** It collapses lists to `inline` and zeroes their margin, but left the 24px `padding-left` from `.wg-prose`, so a one-line message summary containing a list showed a blank gap mid-sentence. ## Found by looking **The global `:focus-visible` rule mutated the element's radius.** An outline follows the element's own `border-radius` anyway, so the declaration only ever added a side effect. It showed on the breadcrumbs, which are inline links: a 4px radius on a 17px box drew a pinched pill. **In the card modal the crumb ring was clipped.** `.title` truncates with `overflow: hidden`, which clips at the padding box, and a ring sits 2px outside the line box. On a card row, where nothing clips, the same ring was already correct - which is what located the cause. Padding on every side with a matching negative margin gives it room and leaves the text exactly where it was. `overflow-clip-margin` expresses this directly and was verified in a browser to keep `text-overflow` working. Rejected on support: it is absent in Safari and would silently degrade to a clipped ring, which is the wrong trade for a focus indicator. **The empty states broke their line at 35 characters** against a readable measure of 45-75. `nldd-inline-dialog` caps its body at `var(--primitives-area-480)` and exposes only icon-colour tokens, so that primitive is widened locally. Both sentences now fit on one line. **Its action button spanned the whole dialog,** because the component wraps its actions slot in a vertical `nldd-button-group` and a block-level button fills a column flex. No attribute exists for a content-sized button, so the host is sized and centred: 160px instead of 640px. ## Verified Every fix measured in a running preview, with real keyboard focus for the rings. `vue-tsc` clean, 570 tests green on a cold vite cache, build green, eslint 0 errors. Related: #204 tracks the missing guard. Most of these would have been caught by a rule that resolves every `var()` against the declared token set and flags partial overrides of shared rules.
robbertbos force-pushed fix-silent-css-breakage from 82b181cda3
Some checks failed
CI / release-scripts (pull_request) Successful in 5s
security-scan / SBOM (trivy) (pull_request) Successful in 10s
security-scan / Filesystem scan (trivy fs) (pull_request) Successful in 13s
security-scan / JS SCA (npm audit) (pull_request) Successful in 19s
security-scan / Python SAST (bandit) (pull_request) Successful in 22s
CI / pre-commit (pull_request) Successful in 27s
security-scan / Python SCA (pip-audit) (pull_request) Successful in 41s
CI / backend-test (pull_request) Successful in 59s
test-build / build (backend) (pull_request) Successful in 1m0s
test-build / build (frontend) (pull_request) Successful in 1m5s
test-build / build (pull_request) Successful in 0s
CI / frontend-test (pull_request) Successful in 1m8s
CI / e2e (pull_request) Has been cancelled
CI / backend-test-postgres (pull_request) Has been cancelled
to 7867f2b261
Some checks failed
CI / release-scripts (pull_request) Successful in 7s
security-scan / SBOM (trivy) (pull_request) Successful in 11s
security-scan / Filesystem scan (trivy fs) (pull_request) Successful in 14s
security-scan / JS SCA (npm audit) (pull_request) Successful in 19s
security-scan / Python SAST (bandit) (pull_request) Successful in 20s
security-scan / Python SCA (pip-audit) (pull_request) Successful in 39s
CI / pre-commit (pull_request) Successful in 56s
test-build / build (frontend) (pull_request) Successful in 56s
test-build / build (backend) (pull_request) Successful in 59s
test-build / build (pull_request) Successful in 0s
CI / e2e (pull_request) Successful in 2m55s
CI / backend-test-postgres (pull_request) Has been cancelled
CI / backend-test (pull_request) Has been cancelled
CI / frontend-test (pull_request) Has been cancelled
2026-07-26 21:10:04 +00:00
Compare
robbertbos force-pushed fix-silent-css-breakage from 7867f2b261
Some checks failed
CI / release-scripts (pull_request) Successful in 7s
security-scan / SBOM (trivy) (pull_request) Successful in 11s
security-scan / Filesystem scan (trivy fs) (pull_request) Successful in 14s
security-scan / JS SCA (npm audit) (pull_request) Successful in 19s
security-scan / Python SAST (bandit) (pull_request) Successful in 20s
security-scan / Python SCA (pip-audit) (pull_request) Successful in 39s
CI / pre-commit (pull_request) Successful in 56s
test-build / build (frontend) (pull_request) Successful in 56s
test-build / build (backend) (pull_request) Successful in 59s
test-build / build (pull_request) Successful in 0s
CI / e2e (pull_request) Successful in 2m55s
CI / backend-test-postgres (pull_request) Has been cancelled
CI / backend-test (pull_request) Has been cancelled
CI / frontend-test (pull_request) Has been cancelled
to 1c7e76c773
All checks were successful
CI / release-scripts (pull_request) Successful in 5s
security-scan / SBOM (trivy) (pull_request) Successful in 10s
security-scan / Filesystem scan (trivy fs) (pull_request) Successful in 13s
security-scan / JS SCA (npm audit) (pull_request) Successful in 20s
security-scan / Python SAST (bandit) (pull_request) Successful in 21s
CI / pre-commit (pull_request) Successful in 28s
security-scan / Python SCA (pip-audit) (pull_request) Successful in 42s
CI / backend-test (pull_request) Successful in 56s
test-build / build (backend) (pull_request) Successful in 58s
test-build / build (frontend) (pull_request) Successful in 1m6s
test-build / build (pull_request) Successful in 0s
CI / frontend-test (pull_request) Successful in 1m12s
CI / e2e (pull_request) Successful in 3m2s
CI / backend-test-postgres (pull_request) Successful in 4m15s
CI / release-scripts (push) Successful in 7s
security-scan / SBOM (trivy) (push) Successful in 11s
security-scan / Filesystem scan (trivy fs) (push) Successful in 12s
security-scan / JS SCA (npm audit) (push) Successful in 20s
security-scan / Python SAST (bandit) (push) Successful in 21s
CI / pre-commit (push) Successful in 31s
security-scan / Python SCA (pip-audit) (push) Successful in 39s
CI / backend-test (push) Successful in 57s
publish-main / build (backend) (push) Successful in 1m4s
publish-main / build (frontend) (push) Successful in 1m10s
publish-main / build (push) Successful in 0s
CI / frontend-test (push) Successful in 1m12s
CI / e2e (push) Successful in 3m4s
CI / backend-test-postgres (push) Successful in 4m18s
2026-07-26 21:13:33 +00:00
Compare
robbertbos changed title from Fix four CSS defects that fail without any signal to Fix the CSS defects the NLDD migration left behind 2026-07-26 21:15:00 +00:00
robbertbos deleted branch fix-silent-css-breakage 2026-07-26 21:17:57 +00:00
Sign in to join this conversation.
No reviewers
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!207
No description provided.