Repository navigation
feat(token-sets): denhaag declares its bridge tokens from its own vendored source - #1089
Merged
rubenvdlinde merged 3 commits intoOct 5, 2026
Merged
Conversation
…source The coverage audit read denhaag at 0 of the 87 --utrecht-* names the bridge reads. It is the one bridge-zero set whose upstream is already vendored here: scripts/sources/denhaag/ holds the pinned @Gemeente-DenHaag token CSS under EUPL-1.2, so the values can be sourced instead of chosen. scripts/brands/denhaag.json plus the captured scripts/brands/denhaag.components.json regenerate css/tokens/denhaag.css through scripts/generate-brand-set.mjs. The set now declares 81 of the 87 bridge names: 52 are Den Haag's own, 29 come from the shared role layer with the ramp re-pointed at Den Haag colours. Nothing is interpolated. The pink ramp, extra-limegreen and extra-purple have no Den Haag family and are left out, which the generator reports as 6 unsubstituted literals. Six bridge names stay undeclared because neither the source nor the role layer states them. The palette is converted from hsl() to 8-bit sRGB hex, with the whole table in the brand file. ContrastService::parseColor returns null for hsl(), so leaving it would turn every measured Den Haag pair into an unmeasurable one. primary moves from the invented #1a7a3e to Den Haag's own green-3 #238541, and the header moves from green to white, which is what --denhaag-page-header-background-color states. token-sets.json records the font licence position: Den Haag names TheSans, a LucasFonts retail face, so the set declares it and renders the self-hosted Fira Sans until an administrator uploads the licensed one. REUSE.toml names both copyright holders on the generated set and the brand files, because their values are half Den Haag's and half ours.
The dark variant, the contrast report, the token reference page, the coverage table and the two coverage claims all follow from css/tokens/denhaag.css, and their staleness checks compare byte for byte. `generate-dark-variants.php --force` rewrote all 58 variants and only css/tokens/dark/denhaag.css differs from development. Verified after both generator runs. Contrast moves and the verdict holds: textRatio and uiRatio both 5.39 -> 4.66, thresholds 4.5 and 3.0, verdict pass. The 15 measured Den Haag pairs stay 15 pass, 0 fail, 0 unevaluated, which is what converting the palette to hex buys. --nldesign-color-primary-light-hover is green-1 rather than green-2. Den Haag states no hover wash and uses green-2 only as a border colour. Deriving the dark variant with it pushed --nldesign-color-primary to near-black #111111, because the repair loop could not reach 4.5:1 for primary on the lightened green-2 wash. With green-1 the dark primary is #55d27b. tests/vitest/denhaagBridge.spec.js used denhaag as its example of a semantic-only set, which it no longer is. The subject moves to tilburg, which the test now asserts declares no --denhaag-* property, and a second test covers the mixed case: denhaag keeps its own case card title and subtitle and still takes the card border through the bridge. The bridge header and the portals doc claimed no set declares a case card property. cunningham already declared one before this change, so the count is two.
…nance The generator repeats the provenance string inside section A's own comment, so a parenthesised licence note read as a bracket inside a bracket. Same facts, commas instead. Only the two generated files and the brand file move; the contrast report and the coverage table do not.
Contributor
Quality Report — ConductionNL/thematiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| test-fonts | ✅ | ||||
| test-token-set-coverage | ✅ | ||||
| composer | ✅ | ✅ 107/107 | |||
| npm | ✅ | ✅ 2/2 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ✅ |
Quality workflow — 2026-10-05 09:38 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
The coverage audit read
denhaagat 0 of the 87--utrecht-*namescss/systems/nldesign/utrecht-bridge.cssreads. It is now at 81 of 87.It is the one bridge-zero set that could be completed without choosing a
municipality's house style.
scripts/sources/denhaag/already holds the pinned@gemeente-denhaagtoken CSS, vendored forscripts/generate-denhaag-bridge.mjsand annotated EUPL-1.2 in
REUSE.toml. So every value here is read off thatsource. The seven other bridge-zero sets have no such source and stay undone on
purpose.
Bridge-zero never meant the set rendered wrong. Of the bridge's 84 declarations,
42 fall back to an
--nldesign-*token the set declares and 38 to a non-colourliteral. This completes a set from a real source. It repairs no defect.
Where the 81 come from
design-tokens-common@4.0.0.cssanddesign-tokens-components@5.1.0.css: the button, badge, link, heading,paragraph, table, textbox, select and form-label families.
Haag's palette: the heading colours and weights,
--utrecht-heading-6-*,--utrecht-paragraph-color, the list colours and the rest of the buttonfamily.
them:
--utrecht-form-input-border-color,-border-radius,-font-size,--utrecht-separator-border-color,-border-widthand--utrecht-table-header-cell-background-color. They keep the bridge fallback.What is left out for want of a source
extra-limegreenandextra-purple. Den Haagpublishes no magenta, lime or purple family. The generator reports them as 6
unsubstituted literals, 12 occurrences, rather than hiding them. Two of the
three places they reach are repointed by name instead: the focus ring takes
Den Haag's own ocher-5 and the status badge takes its grey-1.
No release of it may be redistributed in an app package, so
token-sets.jsonrecords the licence position the way
duodoes for RijksoverheidSans, and theset renders the self-hosted Fira Sans behind it until an administrator uploads
the licensed one. The coverage table's
fontcell moves fromself-hostedtodeclared, which still passes.--nldesign-border-radius-pill. Den Haag ships one radius token, 3px, soevery radius role is 3px. The pill value is the role layer's own lozenge
geometry and the brand file says so.
Nothing is interpolated
The grey ramp is the one place the source does not map one for one: the role
layer wants eleven greys and Den Haag publishes six neutrals. The brand file
states the rule it follows, takes the declared neutral whose own hsl lightness
is nearest the slot's, and never computes a step in between. Five Den Haag greys
land on ten slots, two slots each, with warmgrey-1 on gray-50.
gray-400is theone close call and the brand file names it: 82% against the slot's 66%, with
grey-3 at 48% one point further. It is read once, for
--utrecht-feedback-neutral-fill-border-color.The palette is converted from
hsl()to 8-bit sRGB hex, with the wholeconversion table in the brand file. This is not cosmetic:
ContrastService::parseColorreturns null forhsl(), so leaving the sourcenotation would have turned all 15 measured Den Haag contrast pairs into
unevaluated ones while the verdict still read
pass.What a reader will see change
--nldesign-color-primarymoves from#1a7a3e, which no Den Haag token states,to green-3
#238541, which is--utrecht-button-primary-action-background-color. The header moves from greento white, because
--denhaag-page-header-background-coloris white and Den Haagpaints its own side navigation grey-4 on white.
Contrast, before and after
textRatio(--nldesign-color-primary-texton--nldesign-color-primary)uiRatio(--nldesign-color-primaryon--nldesign-color-background)Both ratios fall because Den Haag's own green is lighter than the invented one.
Both stay above their thresholds, 4.5 and 3.0.
One measurement that changed a decision
--nldesign-color-primary-light-hoverwas green-2 first. Den Haag states nohover wash, and green-2 is the only other green it publishes. Deriving the dark
variant with it pushed
--nldesign-color-primaryto near-black#111111,because the repair loop could not reach 4.5:1 for primary sitting on the
lightened green-2 wash. Den Haag's one use of green-2 is a border, on
--denhaag-floating-contact-switch-border-color, so a wash made from it was acolour Den Haag does not paint anyway. It is green-1 again, the hover state
matches the rest state, and the dark primary is a green
#55d27b.Regenerated, and nothing else moved
php scripts/generate-dark-variants.php --forcerewrote all 58 dark variantsand only
css/tokens/dark/denhaag.cssdiffers fromorigin/development.Verified twice, once per generator run.
One test changed, and why
tests/vitest/denhaagBridge.spec.jsuseddenhaagas its example of a set withonly the semantic layer. That is no longer true of it, so the case card title
came back as Den Haag's blue-5 rather than the set's text colour. The subject
moves to
tilburg, which the test now asserts declares no--denhaag-*property at all, and a second test covers the mixed case:
denhaagkeeps itsown title and subtitle colours and still takes the card border through the
bridge, which is what linking the bridge first is for.
Licensing
REUSE.tomlnames both copyright holders on the generated set, its dark variantand the two brand files. Their values are half Den Haag's and half ours, and
both halves are EUPL-1.2, so the licence identifier was already right and only
the copyright line was not.
Inherited, reported not fixed
css/public-bridge.cssanddocs/features/public-portals-as-consumers.mdclaimed no set declares a case card property.
cunninghamalready declared onebefore this change, so the claim was wrong by one on
development; it is nowtwo and the line is corrected, because this change had to touch it anyway.
css/tokens/rotterdam.csshas the same composite-copyright shape as the DenHaag set and no REUSE entry of its own. That predates this change and is noted
in REUSE.toml rather than fixed here.
Verified
Run once in
~/memcap-work/portal-design/dh/thematiq, each with its ownTMPDIRso no analyser cache is shared with another lane.bash scripts/token-set-gate.sh(331 PHPUnit, 5691 assertions; 299 vitest)npm run audit:token-sets:checknpm run test:token-set-coverageCOMPOSER_PROCESS_TIMEOUT=0 composer check:strictnpm run lintnpm run formatnpm run stylelintnpm run test:denhaag-bridgenpm run test:l10nnpm run check:manifestTwo things that green does not cover, stated rather than implied.
composer check:strictreportstest:allSKIPPED, because this clone sitsoutside a Nextcloud server tree and the suite type-hints OCP interfaces. The
token-set subset it would have covered ran anyway, as
phpunit.token-sets.xmlthrough the gate, includingDenhaagContrastPairsTest,TokenSetContrastAuditTest,TokenSetFontAuditTest,TokenSetVocabularyTestandClaimAccuracyTest. NoPHP source changed in this branch. CI's PHPUnit job is the authority for the
rest.