From 90597bc40f5bb369f4d4754ce2e39906e066d7b6 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Wed, 7 Oct 2026 09:11:01 +0200 Subject: [PATCH] feat(zuiddrecht): the website's columns, type, buttons, grey and photo ground; the menu mark restored Website tokens for Zuiddrecht from the Kop, Home and Contentpagina boards: a 1328px page with a 24px gutter, a 1280px header row and band, 17px text, a 21px lead, 600 buttons through --nldesign-website-button-font-weight (700 for every other set), a website grey #4A4A4A and a #D9E3EF photo ground. Restore --nldesign-website-nav-current-in-line and -color, which #1105 removed as unread although the portal reads them. Every --nldesign-* name portaliq's site-theme.css reads now has a role in public-bridge.css, held by a measured fixture and tests/vitest/portalReaders.spec.js. Dark variant and token reference regenerated for zuiddrecht only. --- CHANGELOG.md | 13 ++ css/public-bridge.css | 60 ++++++++- css/tokens/dark/zuiddrecht.css | 8 +- css/tokens/zuiddrecht.css | 26 +++- docs/reference/token-sets/zuiddrecht.md | 15 ++- .../portal-site-nldesign-readers.json | 38 ++++++ tests/vitest/portalReaders.spec.js | 115 ++++++++++++++++++ tests/vitest/publicBridgeRoleLayer.spec.js | 64 +++++++++- 8 files changed, 328 insertions(+), 11 deletions(-) create mode 100644 tests/vitest/fixtures/portal-site-nldesign-readers.json create mode 100644 tests/vitest/portalReaders.spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index b3d506b29..9148fc31f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file. ## Unreleased ### Added +- **Zuiddrecht's website measures and type.** The site's columns (1328px page with a 24px gutter, + 1280px header row and opening band), 17px running text, a 21px lead (Contentpagina; it was 18), + semibold buttons (`--nldesign-website-button-font-weight`, read by the public bridge's four + button weights, 700 for every other set), the site's own grey `#4A4A4A` (8.86:1 on white) beside + the workplace's `#5E6168`, and a light blue ground for a missing picture (`#D9E3EF`). + +### Fixed +- **The blue mark under the menu item on screen is back on the Zuiddrecht site.** #1105 removed + `--nldesign-website-nav-current-in-line` and `--nldesign-website-nav-current-color` as unread, + because only the portal read them. Every `--nldesign-*` name the portal reads now has a role in + `css/public-bridge.css`, and `tests/vitest/portalReaders.spec.js` checks a measured list of them, + so the vocabulary test no longer calls such a name unread. + - **The workplace top bar, as an option.** A seventh layout option, `header_style` (`default` or `workplace`), draws the DqKop board's bar with the light layout: no logo and no app name, the search beside the app grid button, an outline bell with a red dot, a divider, and your initials, diff --git a/css/public-bridge.css b/css/public-bridge.css index d7885a959..8c7e0221e 100644 --- a/css/public-bridge.css +++ b/css/public-bridge.css @@ -889,7 +889,7 @@ --utrecht-alert-border-width: var(--nldesign-website-notice-border-width, 2px); --utrecht-button-border-width: var(--nldesign-website-control-border-width, 1px); --utrecht-button-font-size: 16px; - --utrecht-button-font-weight: 700; + --utrecht-button-font-weight: var(--nldesign-website-button-font-weight, 700); --utrecht-button-icon-size: 24px; --utrecht-button-line-height: 150%; --utrecht-button-min-block-size: 48px; @@ -898,9 +898,18 @@ --utrecht-button-padding-block-start: 12px; --utrecht-button-padding-inline-end: 16px; --utrecht-button-padding-inline-start: 16px; - --utrecht-button-primary-action-font-weight: 700; - --utrecht-button-secondary-action-font-weight: 700; - --utrecht-button-subtle-font-weight: 700; + --utrecht-button-primary-action-font-weight: var( + --nldesign-website-button-font-weight, + 700 + ); + --utrecht-button-secondary-action-font-weight: var( + --nldesign-website-button-font-weight, + 700 + ); + --utrecht-button-subtle-font-weight: var( + --nldesign-website-button-font-weight, + 700 + ); --utrecht-data-list-item-key-font-weight: 700; --utrecht-document-font-size: 16px; --utrecht-document-line-height: 150%; @@ -1181,6 +1190,49 @@ --thematiq-hero-action-color: var(--nldesign-hero-action-color); --thematiq-hero-decoration-image: var(--nldesign-hero-decoration-image); + /* + * THE NAMES THE PORTAL READS BY ITSELF (portaliq css/site-theme.css; the + * full list is tests/vitest/fixtures/portal-site-nldesign-readers.json). + * A set may only declare a `--nldesign-*` name something reads + * (TokenSetVocabularyTest), and that test looks inside this repository. + * The portal read these names and nothing here did, so #1105 took two of + * them out of Zuiddrecht as unread, and the blue mark under the menu item + * on screen went with them. Each gets a role here, so the vocabulary + * knows the portal reads it. No fallbacks: a set that names none of them + * leaves the portal's own fallback in force. + */ + --thematiq-website-page-max-width: var(--nldesign-website-page-max-width); + --thematiq-website-page-gutter: var(--nldesign-website-page-gutter); + --thematiq-website-band-max-width: var(--nldesign-website-band-max-width); + --thematiq-website-header-max-width: var(--nldesign-website-header-max-width); + --thematiq-website-header-gutter: var(--nldesign-website-header-gutter); + --thematiq-website-content-font-size: var(--nldesign-website-content-font-size); + --thematiq-website-nav-current-in-line: var( + --nldesign-website-nav-current-in-line + ); + --thematiq-website-nav-current-color: var(--nldesign-website-nav-current-color); + --thematiq-website-hero-title-size: var(--nldesign-website-hero-title-size); + --thematiq-website-hero-decoration-opacity: var( + --nldesign-website-hero-decoration-opacity + ); + --thematiq-color-border-strong: var(--nldesign-color-border-strong); + --thematiq-emblem-url: var(--nldesign-emblem-url); + + /* + * The site's own grey text and the ground of a missing picture. The site + * reads --nldesign-color-text-muted, which is the workplace's grey too, + * and this file loads before the set, so it cannot change that name; it + * offers the website's grey under its own role, falling back to the + * shared one. The placeholder ground has no fallback. + */ + --thematiq-website-text-muted: var( + --nldesign-website-color-text-muted, + var(--nldesign-color-text-muted) + ); + --thematiq-placeholder-background-color: var( + --nldesign-website-placeholder-background-color + ); + /* * THE BRAND STRIPE ON THE PORTAL (openspec/changes/brand-motif-on-portals). * diff --git a/css/tokens/dark/zuiddrecht.css b/css/tokens/dark/zuiddrecht.css index a45de5362..9f6621e88 100644 --- a/css/tokens/dark/zuiddrecht.css +++ b/css/tokens/dark/zuiddrecht.css @@ -1,4 +1,4 @@ -/* GENERATED by nldesign DarkPaletteService v9 from tokens/zuiddrecht.css (sha256:df7f10553b8424644dd2fda2aaeda41cdd5e0eb46b2c76cfa830cdbb056e01d8) — do not edit */ +/* GENERATED by nldesign DarkPaletteService v9 from tokens/zuiddrecht.css (sha256:a41d81eeb158b6cf63f06a21f1a1308921984d2bff7812362434411f8762c738) — do not edit */ @media (prefers-color-scheme: dark) { body:not([data-theme-light]):not([data-theme-dark]):not([data-theme-light-highcontrast]):not([data-theme-dark-highcontrast]) { --nldesign-brand-stripe-color-1: #f43535; @@ -121,9 +121,12 @@ --nldesign-website-attention-background-color: #412d03; --nldesign-website-attention-border-color: #8a691f; --nldesign-website-attention-color: #e5e5e5; + --nldesign-website-color-text-muted: #b5b5b5; + --nldesign-website-nav-current-color: #6d9acf; --nldesign-website-notice-background-color: #13202e; --nldesign-website-notice-border-color: #273b55; --nldesign-website-notice-color: #e5e5e5; + --nldesign-website-placeholder-background-color: #1b2a3b; --nldesign-website-step-current-background-color: #141414; --nldesign-website-step-current-color: #ff3d3d; --nldesign-website-step-done-color: #6d9acf; @@ -264,9 +267,12 @@ body[data-themes*=dark] { --nldesign-website-attention-background-color: #412d03; --nldesign-website-attention-border-color: #8a691f; --nldesign-website-attention-color: #e5e5e5; + --nldesign-website-color-text-muted: #b5b5b5; + --nldesign-website-nav-current-color: #6d9acf; --nldesign-website-notice-background-color: #13202e; --nldesign-website-notice-border-color: #273b55; --nldesign-website-notice-color: #e5e5e5; + --nldesign-website-placeholder-background-color: #1b2a3b; --nldesign-website-step-current-background-color: #141414; --nldesign-website-step-current-color: #ff3d3d; --nldesign-website-step-done-color: #6d9acf; diff --git a/css/tokens/zuiddrecht.css b/css/tokens/zuiddrecht.css index b3bd3cbad..e769e40c0 100644 --- a/css/tokens/zuiddrecht.css +++ b/css/tokens/zuiddrecht.css @@ -231,10 +231,32 @@ * css/public-bridge.css and by the portal's own site stylesheet only. * A content page's title: 44px (2.75rem) on a 1.15 line, said for the h1 * the site draws above the page's own heading scale. The lead paragraph - * under it is 18px. */ + * under it is 21px (Contentpagina), the running text 17px. */ --nldesign-website-page-title-size: 2.75rem; --nldesign-website-page-title-line-height: 1.15; - --nldesign-website-lead-font-size: 18px; + --nldesign-website-lead-font-size: 21px; + --nldesign-website-content-font-size: 17px; + /* The site's columns (Kop, Home, Contentpagina): the page 1328px wide + * with a 24px gutter, so the content is 1280px; the header's row is the + * same 1280px with no gutter of its own, and a band that opens a page is + * 1280px too. */ + --nldesign-website-page-max-width: 1328px; + --nldesign-website-page-gutter: 24px; + --nldesign-website-band-max-width: 1280px; + --nldesign-website-header-max-width: 1280px; + --nldesign-website-header-gutter: 0px; + /* The menu item on screen: a blue bar in the red line under the menu + * (Kop), not above it. */ + --nldesign-website-nav-current-in-line: 1; + --nldesign-website-nav-current-color: #3669A5; + /* Buttons on the site are semibold (Kop "Mijn Zuiddrecht", Home). */ + --nldesign-website-button-font-weight: 600; + /* The site's grey text is darker than the workplace's #5E6168: #4A4A4A, + * 8.86:1 on white, 8.19:1 on the site's surface #F4F6F9 and 6.83:1 on + * the photo ground below. A picture that has not loaded, or a card + * without one, sits on a light blue (#D9E3EF; ink on it 13.41:1). */ + --nldesign-website-color-text-muted: #4A4A4A; + --nldesign-website-placeholder-background-color: #D9E3EF; /* The text on a plain notice: the site's ink, 15.17:1 on the light blue * notice ground above and 15.95:1 on the soft yellow attention strip the * Home board draws (#FFF4DE). */ diff --git a/docs/reference/token-sets/zuiddrecht.md b/docs/reference/token-sets/zuiddrecht.md index e16ddf236..dd0599a24 100644 --- a/docs/reference/token-sets/zuiddrecht.md +++ b/docs/reference/token-sets/zuiddrecht.md @@ -2,7 +2,7 @@ # Gemeente Zuiddrecht -Token set `zuiddrecht`: 111 declared by this set, 129 from the defaults layer. +Token set `zuiddrecht`: 122 declared by this set, 129 from the defaults layer. Contrast: primary text on primary 5.64:1, primary on background 5.22:1 (pass). @@ -299,22 +299,33 @@ Contrast: primary text on primary 5.64:1, primary on background 5.22:1 (pass). | `--nldesign-website-attention-border-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23E8C77D%22%2F%3E%3C%2Fsvg%3E) `#E8C77D` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%238a691f%22%2F%3E%3C%2Fsvg%3E) `#8a691f` | this set | | | `--nldesign-website-attention-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%231A1A1A%22%2F%3E%3C%2Fsvg%3E) `#1A1A1A` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23e5e5e5%22%2F%3E%3C%2Fsvg%3E) `#e5e5e5` | this set | | | `--nldesign-website-badge-border-radius` | `14px` | | this set | | +| `--nldesign-website-band-max-width` | `1280px` | | this set | | | `--nldesign-website-border-radius` | `4px` | | this set | | | `--nldesign-website-border-radius-large` | `6px` | | this set | | +| `--nldesign-website-button-font-weight` | `600` | | this set | | +| `--nldesign-website-color-text-muted` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%234A4A4A%22%2F%3E%3C%2Fsvg%3E) `#4A4A4A` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23b5b5b5%22%2F%3E%3C%2Fsvg%3E) `#b5b5b5` | this set | | +| `--nldesign-website-content-font-size` | `17px` | | this set | | | `--nldesign-website-control-border-width` | `2px` | | this set | | +| `--nldesign-website-header-gutter` | `0px` | | this set | | +| `--nldesign-website-header-max-width` | `1280px` | | this set | | | `--nldesign-website-heading-1-font-size` | `44px` | | this set | | | `--nldesign-website-heading-2-font-size` | `40px` | | this set | | | `--nldesign-website-heading-3-font-size` | `26px` | | this set | | -| `--nldesign-website-lead-font-size` | `18px` | | this set | | +| `--nldesign-website-lead-font-size` | `21px` | | this set | | | `--nldesign-website-logo-height` | `50px` | | this set | | | `--nldesign-website-logo-text-size` | `0` | | this set | | | `--nldesign-website-logo-width` | `234px` | | this set | | +| `--nldesign-website-nav-current-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%233669A5%22%2F%3E%3C%2Fsvg%3E) `#3669A5` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%236d9acf%22%2F%3E%3C%2Fsvg%3E) `#6d9acf` | this set | | +| `--nldesign-website-nav-current-in-line` | `1` | | this set | | | `--nldesign-website-notice-background-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23EAF0F7%22%2F%3E%3C%2Fsvg%3E) `#EAF0F7` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%2313202e%22%2F%3E%3C%2Fsvg%3E) `#13202e` | this set | | | `--nldesign-website-notice-border-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23B9CBE2%22%2F%3E%3C%2Fsvg%3E) `#B9CBE2` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23273b55%22%2F%3E%3C%2Fsvg%3E) `#273b55` | this set | | | `--nldesign-website-notice-border-width` | `1px` | | this set | | | `--nldesign-website-notice-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%231A1A1A%22%2F%3E%3C%2Fsvg%3E) `#1A1A1A` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23e5e5e5%22%2F%3E%3C%2Fsvg%3E) `#e5e5e5` | this set | | +| `--nldesign-website-page-gutter` | `24px` | | this set | | +| `--nldesign-website-page-max-width` | `1328px` | | this set | | | `--nldesign-website-page-title-line-height` | `1.15` | | this set | | | `--nldesign-website-page-title-size` | `2.75rem` | | this set | | +| `--nldesign-website-placeholder-background-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23D9E3EF%22%2F%3E%3C%2Fsvg%3E) `#D9E3EF` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%231b2a3b%22%2F%3E%3C%2Fsvg%3E) `#1b2a3b` | this set | | | `--nldesign-website-step-current-background-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fsvg%3E) `#ffffff` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23141414%22%2F%3E%3C%2Fsvg%3E) `#141414` | this set | | | `--nldesign-website-step-current-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23CC0000%22%2F%3E%3C%2Fsvg%3E) `#CC0000` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%23ff3d3d%22%2F%3E%3C%2Fsvg%3E) `#ff3d3d` | this set | | | `--nldesign-website-step-done-color` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%233669A5%22%2F%3E%3C%2Fsvg%3E) `#3669A5` | ![](data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2214%22%20height%3D%2214%22%3E%3Crect%20width%3D%2214%22%20height%3D%2214%22%20fill%3D%22%236d9acf%22%2F%3E%3C%2Fsvg%3E) `#6d9acf` | this set | | diff --git a/tests/vitest/fixtures/portal-site-nldesign-readers.json b/tests/vitest/fixtures/portal-site-nldesign-readers.json new file mode 100644 index 000000000..e1ee01b11 --- /dev/null +++ b/tests/vitest/fixtures/portal-site-nldesign-readers.json @@ -0,0 +1,38 @@ +{ + "source": "Every --nldesign-* name portaliq's css/site-theme.css reads (var() outside comments), measured on portaliq fb15658a (branch feat/zuiddrecht-portal-round3), 7 October 2026. A set may declare any of these; tests/vitest/portalReaders.spec.js holds that this repository reads each one, so TokenSetVocabularyTest does not call it unread.", + "names": [ + "--nldesign-body-font-family", + "--nldesign-color-background", + "--nldesign-color-background-hover", + "--nldesign-color-border", + "--nldesign-color-border-dark", + "--nldesign-color-border-strong", + "--nldesign-color-footer-background", + "--nldesign-color-footer-text", + "--nldesign-color-link", + "--nldesign-color-primary", + "--nldesign-color-primary-hover", + "--nldesign-color-primary-light", + "--nldesign-color-primary-light-hover", + "--nldesign-color-primary-text", + "--nldesign-color-text", + "--nldesign-color-text-muted", + "--nldesign-emblem-url", + "--nldesign-hero-body-color", + "--nldesign-hero-title-color", + "--nldesign-website-band-max-width", + "--nldesign-website-border-radius", + "--nldesign-website-border-radius-large", + "--nldesign-website-content-font-size", + "--nldesign-website-header-gutter", + "--nldesign-website-header-max-width", + "--nldesign-website-hero-decoration-opacity", + "--nldesign-website-hero-title-size", + "--nldesign-website-nav-current-color", + "--nldesign-website-nav-current-in-line", + "--nldesign-website-page-gutter", + "--nldesign-website-page-max-width", + "--nldesign-website-page-title-line-height", + "--nldesign-website-page-title-size" + ] +} diff --git a/tests/vitest/portalReaders.spec.js b/tests/vitest/portalReaders.spec.js new file mode 100644 index 000000000..6cd4557d8 --- /dev/null +++ b/tests/vitest/portalReaders.spec.js @@ -0,0 +1,115 @@ +/** + * SPDX-FileCopyrightText: 2026 Conduction B.V. + * SPDX-License-Identifier: EUPL-1.2 + * + * The portal reads `--nldesign-*` names of its own (portaliq + * css/site-theme.css). TokenSetVocabularyTest only lets a set declare a + * name that something reads, and it looks inside this repository. A name + * only the portal read looked unread: #1105 took Zuiddrecht's two menu-mark + * tokens out for that reason, and the blue bar under the menu item on screen + * disappeared from the live site. This file holds that every name the portal + * reads is read here too (css/public-bridge.css carries a role for it), so a + * set may declare it. + * + * The fixture is measured from the portal; when the portal reads a new name, + * add it there and give it a role in the bridge. + */ + +import fs from 'fs' +import path from 'path' +import { describe, it, expect } from 'vitest' + +const ROOT = path.resolve(__dirname, '../..') +const FIXTURE = JSON.parse( + fs.readFileSync( + path.join(ROOT, 'tests/vitest/fixtures/portal-site-nldesign-readers.json'), + 'utf8', + ), +) + +/** + * Every CSS file under css/ that is not a token set: the layers whose reads + * make up the vocabulary. + * + * @return {string[]} The file contents, comments stripped. + */ +function readerLayers() { + const out = [] + const walk = (dir) => { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name) + if (entry.isDirectory()) { + if (full !== path.join(ROOT, 'css/tokens')) { + walk(full) + } + } else if (entry.name.endsWith('.css')) { + out.push( + fs.readFileSync(full, 'utf8').replace(/\/\*[\s\S]*?\*\//g, ''), + ) + } + } + } + walk(path.join(ROOT, 'css')) + return out +} + +describe('the names the portal reads', () => { + const css = readerLayers().join('\n') + + it('is a real list, measured from the portal', () => { + expect(FIXTURE.names.length).toBeGreaterThan(20) + expect(FIXTURE.names).toContain('--nldesign-website-nav-current-in-line') + expect(FIXTURE.names).toContain('--nldesign-website-nav-current-color') + }) + + it.each(FIXTURE.names)('%s is known to this repository too', (name) => { + // The vocabulary counts a name a layer reads or gives a default. + const read = new RegExp(`${name.replace(/-/g, '\\-')}(?![a-z0-9-])`) + expect(read.test(css)).toBe(true) + }) +}) + +describe("the website tokens are Zuiddrecht's alone (the control)", () => { + const NEW = [ + '--nldesign-website-page-max-width', + '--nldesign-website-band-max-width', + '--nldesign-website-page-gutter', + '--nldesign-website-header-max-width', + '--nldesign-website-header-gutter', + '--nldesign-website-content-font-size', + '--nldesign-website-nav-current-in-line', + '--nldesign-website-nav-current-color', + '--nldesign-website-button-font-weight', + '--nldesign-website-color-text-muted', + '--nldesign-website-placeholder-background-color', + ] + const sets = fs + .readdirSync(path.join(ROOT, 'css/tokens')) + .filter((f) => f.endsWith('.css') && f !== 'zuiddrecht.css') + + it('walks the shipped sets', () => { + expect(sets.length).toBeGreaterThan(50) + }) + + it.each(sets)('%s declares none of them', (file) => { + const text = fs.readFileSync(path.join(ROOT, 'css/tokens', file), 'utf8') + expect(NEW.filter((name) => text.includes(name + ':'))).toEqual([]) + }) + + it('the site grey reaches AA on white, the site surface and the photo ground', () => { + const lum = (hex) => { + const c = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255) + const l = c.map((v) => + v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4, + ) + return 0.2126 * l[0] + 0.7152 * l[1] + 0.0722 * l[2] + } + const ratio = (a, b) => { + const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p) + return (x + 0.05) / (y + 0.05) + } + for (const ground of ['#FFFFFF', '#F4F6F9', '#D9E3EF']) { + expect(ratio('#4A4A4A', ground), ground).toBeGreaterThanOrEqual(4.5) + } + }) +}) diff --git a/tests/vitest/publicBridgeRoleLayer.spec.js b/tests/vitest/publicBridgeRoleLayer.spec.js index 69bbc208d..f0cd2724e 100644 --- a/tests/vitest/publicBridgeRoleLayer.spec.js +++ b/tests/vitest/publicBridgeRoleLayer.spec.js @@ -275,13 +275,13 @@ describe('the website type scale, controls and marks are vocabulary', () => { /** * The public site's own pages (openspec/changes/zuiddrecht-site-page-title-notice-surface). */ - it('zuiddrecht draws its site: a 44px content title on a 1.15 line, an 18px lead, ink on a notice, a cool grey surface', () => { + it('zuiddrecht draws its site: a 44px content title on a 1.15 line, a 21px lead, ink on a notice, a cool grey surface', () => { expect(token('zuiddrecht', '--thematiq-page-title-font-size')).toBe( '2.75rem', ) expect(token('zuiddrecht', '--thematiq-page-title-line-height')).toBe('1.15') expect(token('zuiddrecht', '--utrecht-paragraph-lead-font-size')).toBe( - '18px', + '21px', ) expect(token('zuiddrecht', '--utrecht-alert-color')).toBe('#1A1A1A') expect(token('zuiddrecht', '--thematiq-surface-color')).toBe('#F4F6F9') @@ -309,6 +309,43 @@ describe('the website type scale, controls and marks are vocabulary', () => { ) }) + it('zuiddrecht: semibold buttons, the menu mark in the red line, its columns, its own grey and the photo ground', () => { + for (const role of [ + '--utrecht-button-font-weight', + '--utrecht-button-primary-action-font-weight', + '--utrecht-button-secondary-action-font-weight', + '--utrecht-button-subtle-font-weight', + ]) { + expect(token('zuiddrecht', role), role).toBe('600') + } + expect(token('zuiddrecht', '--thematiq-website-nav-current-in-line')).toBe( + '1', + ) + expect(token('zuiddrecht', '--thematiq-website-nav-current-color')).toBe( + '#3669A5', + ) + expect(token('zuiddrecht', '--thematiq-website-page-max-width')).toBe( + '1328px', + ) + expect(token('zuiddrecht', '--thematiq-website-page-gutter')).toBe('24px') + expect(token('zuiddrecht', '--thematiq-website-band-max-width')).toBe( + '1280px', + ) + expect(token('zuiddrecht', '--thematiq-website-header-max-width')).toBe( + '1280px', + ) + expect(token('zuiddrecht', '--thematiq-website-header-gutter')).toBe('0px') + expect(token('zuiddrecht', '--thematiq-website-content-font-size')).toBe( + '17px', + ) + expect(token('zuiddrecht', '--thematiq-website-text-muted')).toBe('#4A4A4A') + expect(token('zuiddrecht', '--thematiq-placeholder-background-color')).toBe( + '#D9E3EF', + ) + // The workplace keeps its own grey. + expect(token('zuiddrecht', '--nldesign-color-text-muted')).toBe('#5E6168') + }) + it.each([...SCHOOLS, 'vng'])( '%s names none of it and keeps every value it had (the control)', (set) => { @@ -341,6 +378,29 @@ describe('the website type scale, controls and marks are vocabulary', () => { expect(token(set, '--thematiq-attention-background-color')).toBeNull() expect(token(set, '--thematiq-attention-border-color')).toBeNull() expect(token(set, '--thematiq-attention-color')).toBeNull() + // Buttons keep their 700, the portal's own names resolve to nothing, + // and the site's grey is the set's one grey. + for (const role of [ + '--utrecht-button-font-weight', + '--utrecht-button-primary-action-font-weight', + '--utrecht-button-secondary-action-font-weight', + '--utrecht-button-subtle-font-weight', + ]) { + expect(token(set, role), role).toBe('700') + } + for (const role of [ + '--thematiq-website-nav-current-in-line', + '--thematiq-website-nav-current-color', + '--thematiq-website-page-max-width', + '--thematiq-website-band-max-width', + '--thematiq-website-content-font-size', + '--thematiq-placeholder-background-color', + ]) { + expect(token(set, role), role).toBeNull() + } + expect(token(set, '--thematiq-website-text-muted')).toBe( + token(set, '--nldesign-color-text-muted'), + ) }, ) })