NLDD alignment: the time field, the popup slot and the popover frame #235
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "nldd-alignment"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #182, closes #132, closes #170.
Four patterns we had built by hand go back to the design system, plus a test for the one hazard that made the work necessary in the first place.
What made this possible
NLDD 0.8.78 (released 2026-08-05) ships
nldd-time-fieldandnldd-time-picker. That was the single thing #182 said the schedule popover could not do without: "cannot be fully NLDD-native because NLDD has no time-field, so the time input stays native/custom". It has one now, so the nativeinput type="time"and the eighteen lines of chrome that copied its neighbour by hand are gone - border, corner radius, 16px tier, 44px control box, each with a comment naming the value it was matching.What else went back
popupslot (#170). That removes the sharedopenMenu()helper, theanchorElement/showPopoverbookkeeping, two toggle listeners, the manualaria-haspopup/aria-expandedpair, and the.heading/.moreBtnrules that reimplemented a 1.75rem transparent button - down to a comment saying "matches NLDD's button convention". The explicithidePopover()calls go too:nldd-menucollapses its own chain onselect.nldd-buttons. The primary one was painted with--semantics-links-colorand abrightness(0.95)hover.nldd-menuframes itself with radius and shadow on the base surface and nothing else, so ours read as a different system than the menus beside them.nldd-text-field's border token and the global focus ring instead of a divider-coloured border and a hand-mixedcolor-mix()glow. It stays a native input: it is wired to a raw Tiptap reference.paragraph-sign,heading-1..heading-6, verified against the icon registry first, sincenldd-iconfails silently on an unknown name.The guard, and why the reset stays as it is
#205 said our
* { margin: 0; padding: 0 }was silently zeroing the padding ofnldd-menu,nldd-bannerandnldd-switch. Measured on 0.8.78: that is no longer true. NLDD has moved every affected box into a shadow wrapper (.menu__main,.banner,.switch__track) or shielded it with!important, and says why in its own source - "shields the row padding from consumer universal resets, which beat normal:hostdeclarations per CSS Scoping".The hazard itself has not moved. A probe element with
:host { padding: 12px; margin: 7px }still computes to0px/0pxin the running app, while the same declarations inside its shadow root survive. So narrowing the reset would touch every page to fix nothing measurable, and instead there is nownlddHostBox.test.ts: it reads the tags our own templates use, 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, including an assertion that proves the detector fires on input that should fail it. That one earned its place immediately - the first version split declarations per line and so missed the padding in:host { display: block; padding: 12px }.Verified in the browser, not only on green tests
2026-08-07T07:00:00Z, which is the 09:00 that was picked.aria-haspopup="menu"andaria-expandedfrom first render - an 0.8.76 fix we now get for free. The menus anchor under their trigger, close on select, and a format applied through the slotted menu reaches the editor.Gates
vitest694,vue-tsc,npm run build,pytest1669 on Postgres, pinnedpre-commit,eslint0 errors, Playwright 67 passed / 1 failed. That one failure isencrypted-export-roundtrip, which is permanently red on this machine and unrelated.just lintfails onbackend/tests/integration/test_migrations.pyimport order. That is local ruff 0.15 disagreeing with the 0.8.6 the hooks pin; the pinned run passes, and the file came in with the Postgres-only merge.Not in this PR
hyphenshalf of #132 does not apply: we usenldd-rich-textnowhere, deliberately.reset.css sets `* { margin: 0; padding: 0 }`, and `*` matches the host of a custom element. Per CSS Scoping a declaration from the outer document beats one from a shadow root regardless of specificity, so a component that puts its box on :host loses it here - silently, while the component still lays out as if the padding were there. This cost us a visibly broken banner once (#205). Measured in the running app on 0.8.78: a probe element with `:host { padding: 12px; margin: 7px }` computes to 0px/0px, while the same declarations on an element inside its shadow root survive at 12px. So the hazard is unchanged. What changed is NLDD, which has since moved every affected box into a shadow wrapper (.menu__main, .banner, .switch__track) or shielded it with !important, and says why in its own source: "shields the row padding from consumer universal resets, which beat normal :host declarations per CSS Scoping". Nothing is broken today, so this narrows nothing in the reset. It watches instead: scan the tags our own templates use, resolve each to its component directory, and fail on a :host-only rule declaring padding or margin without !important. Three assertions, in the shape nlddTokens.test.ts already uses. The middle one proves the detector fires on input that should fail it, and earned its place immediately: the first version split declarations per line and so missed the padding in `:host { display: block; padding: 12px }`.CI note, for the record.
CI / e2efailed on6adff5dand is green ondd2d00a, which is the same frontend plus one CI step. What I can back up:encrypted-export-roundtrip, which is permanently red on this machine. That holds withCI=1too, so the two retries CI uses are included.What I cannot back up is which test failed.
/actions/runs/<id>/jobs/<n>/logsanswers 404 even with a token, so there was nothing to read. Calling it a probable flake rather than a proven one.Hence the third commit: the e2e job now keeps
playwright-report/andtest-results/as an artifact when it fails (7 days). That was a real gap - a red e2e left nothing behind unless you opened the job in a browser. Next timenpx playwright show-traceis enough.Two conflicts, both in files main had also been working in. CHANGELOG.md: both sides had prepended a bullet to "Onder de motorkap". Kept both. ComposeOverlay.test.ts: took main's version whole. The conflicting hunks were mine, and they only disambiguated a selector - `wrapper.get('nldd-button')` picked the discard button on the assumption that it was the only nldd-button in the modal, which stopped being true once the toolbar's block-level dropdown became one. Main has since dropped the discard button from that composer entirely (31cddbb), so the tests those hunks touched no longer exist. Checked afterwards that no unqualified `nldd-button` selector is left in the file. Also moved the time field and the heading icons out of "Onder de motorkap" and into their own sections: both are things a user sees, and that heading is for things they do not.