Skip to content

Design system v4.1, licht: de kleine letter en de prijsregel halen WCAG AA, geen donkere modus - #380

Merged
Apolloccrypt merged 2 commits into
mainfrom
feat/droom-systeem
Sep 3, 2026
Merged

Design system v4.1, licht: de kleine letter en de prijsregel halen WCAG AA, geen donkere modus#380
Apolloccrypt merged 2 commits into
mainfrom
feat/droom-systeem

Conversation

@Apolloccrypt

@Apolloccrypt Apolloccrypt commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Herstel na review. De donkere laag is er in zijn geheel uit. Wat overblijft is de gemeten lichte winst, en verder niets: geen nieuwe tokenset, geen componentlaag, geen letterlijke-kleur-sweep over de gedeelde stylesheets. Eén token in frontend/design-system.css verandert van waarde.

Waarom de donkere laag eruit ging

De directie las de vorige stand en trok drie dingen aan:

  • de donkere modus stond achter [data-theme] zonder schakelaar in de nav, dus geen bezoeker kon hem bereiken
  • in die stand was hij onleesbaar op /security, /pricing en het dashboard: wit op lime, 1.07:1
  • hij kostte 31 KB op alle 58 pagina's, en geen enkele nginx-conf in deploy/ zet gzip aan

frontend/design-system.css ging in de vorige stand van 85374 naar 116364 bytes. Nu staat hij op 86270, en die 896 bytes zijn vrijwel volledig het commentaarblok met de gemeten getallen. nav.css, parapear.css en relay-pulse.css staan weer letterlijk op main.

De winst

--ink-dim stond op rgba(11,58,106,.65):

tegen ratio AA vraagt
het papier (#F8FAFC) 4.09:1 4.5
de voet (#EEF2F6) 3.96:1 4.5
een witte kaart 4.18:1 4.5

Drie keer net eronder, en drie keer onder de tekst die de prijs, de limiet en de kleine letter draagt. --ink-dim, --lime-dim en --black-dim wijzen nu naar --ink-2, #40505F, 7.95:1 op het papier.

Gemeten met de WCAG 2.1 relatieve-luminantieformule, elke tekstkleur afgevlakt tegen de eerste ondoorzichtige achtergrond erboven, op 390 en 1440, in licht:

main deze tak
hele site, uniek onder AA 82 5
/ 4 1
/pricing 8 0
/parasign 7 0
/security 8 1
/dashboard 0 0

Gemeten tegen main op 39179f50. Het dashboard staat er als nul tegen nul: #381 heeft die pagina zelf al schoongemaakt terwijl dit liep.

De vijf die overblijven staan met naam en reden in KNOWN_LIGHT in tests/theme-contrast.test.mjs. Geen van de vijf komt uit het ontwerpsysteem: alle vijf staan als letterlijke kleur in het style-blok van een pagina, en alle vijf staan al op main. De statusregel van /parashare stond er ook op en is door #381 opgelost, dus die is van de lijst af: korter mag, langer niet. De twee op de pagina's hierboven:

Pixeldiff van de homepage

index.html tegen main, dezelfde chromium, animaties en transities uit:

390x844    0 pixels verschil
1440x900   0 pixels verschil
hele pagina, 390    3705 pixels op 46 regels, y 8908..9004
hele pagina, 1440   3705 pixels op 46 regels, y 6084..6180

Die 46 regels zijn de twee voetregels die op --ink-dim staan. Boven de vouw verandert er geen pixel, ook niet in de nav.

De drie testbestanden

tests/theme-contrast.test.mjs meet elk tekstpaar op zestien pagina's op 390 en 1440: 4448 paren, vijf bekende gevallen, nul nieuwe. De lijst mag korter worden en nooit langer.

tests/motion-safety.test.mjs faalt zodra een element verdwijnt, vervaagt of inklapt onder prefers-reduced-motion in plaats van alleen stil te staan. De review meldde dat /parashare er een element bij verloor, 272 tegen 271. Het meetpunt deugde niet.

Hij laadde elke pagina twee keer, een keer met en een keer zonder reduce, en legde de twee lijsten naast elkaar. Dat vergelijkt twee paginaladingen en niet twee instellingen. /parashare start 400ms na het laden een WebGL-globe waarvan het aantal knopen op een vast meetmoment in de ene lading wel en in de andere nog niet staat: gemeten acht keer op rij 272 tegen 267, en op de CI-runner kwam dat binnen twintig seconden nooit tot stilstand.

Elke pagina wordt nu een keer geladen. Na de binnenkomst opname A, dan page.emulateMedia({ reducedMotion: 'reduce' }) en opname B, dan terug en opname C. Dezelfde DOM, dezelfde scripts, dezelfde toestand, en het enige dat tussen A en B verandert is de media query. C is de controle: wat de pagina zelf tussen twee metingen weghaalt is in C ook weg en telt dus niet als schade van reduce. Vergeleken wordt op wat een layoutbox heeft, en op een DOM-pad in plaats van op een volgnummer in een platte lijst.

Dat is een scherpere claim en geen zwakkere: een element dat in A en C een box heeft en in B niet, is nu rood, en was dat eerder alleen als het totaalaantal toevallig meeveranderde. Gesaboteerd met display:none, opacity:0 en visibility:hidden onder reduce, alle drie rood:

DIV section(5)>div(1)>div(2)>div(1)>div(1) stopped being rendered (235x16 without reduce, no layout box with it)
DIV section(5)>div(1)>div(2)>div(1)>div(3) faded from opacity 1 to 0
DIV section(5)>div(1)>div(2)>div(1)>div(1) became visibility:hidden

tests/apply-nav-idempotent.test.mjs is nieuw en dicht de tweede reviewbevinding. frontend/apply-nav.py stempelt de nav, de mobiele la, de voet en de vier cache-bust-verwijzingen in 58 pagina's, en zijn DS_LINK liep achter op wat die pagina's dragen: pagina's op ?v=24, generator op ?v=23. Een onschuldige python3 frontend/apply-nav.py zou 58 pagina's stil hebben teruggezet naar een stylesheet-url die niet meer bestaat, en de nginx-conf serveert CSS als immutable, max-age=1y. De test draait de generator op een kopie van frontend/ en eist een lege diff.

Gesaboteerd om te zien dat hij bijt: DS_LINK terug op ?v=23 geeft rood en noemt bestand, regelnummer en beide waarden.

frontend/apply-nav.py is not idempotent. Running it on a clean checkout rewrites:
  404.html:28
      committed: <link rel="stylesheet" href="/design-system.css?v=24">
      generated: <link rel="stylesheet" href="/design-system.css?v=23">

Alles in dat bestand staat in functiescope. tests/*.mjs is één gedeelde top-level naamruimte waar elke openstaande pull request in schrijft; scripts/check-test-declarations.sh bewaakt dat en dit bestand voegt er geen naam aan toe.

node_modules

De vorige stand droeg een node_modules-symlink als 120000-blob. .gitignore had node_modules/ en **/node_modules/, en een regel met slash matcht alleen een map: een symlink is voor git een bestand en glipte er langs. De blob is weg en .gitignore vangt hem nu ook zonder slash.

Cache-bust

design-system.css naar ?v=24 over 58 pagina's, plus frontend/apply-nav.py en de verwijzing in tests/access-log-visitors.test.mjs. nav.css blijft op ?v=19, want dat bestand is nu byte voor byte gelijk aan main en een bump die niets verandert is 20 KB die elke bezoeker opnieuw ophaalt.

Poorten

Alles op 39179f50, de main van dit moment.

first-screen                9/9     theme-contrast              1/1
pricing-fold                4/4     motion-safety               8/8
frontend-loading-contract   7/7     apply-nav-idempotent        1/1
seo-contract              14/14     access-log-visitors       16/16
site-claims               36/36     navigation-shell    26 checks
ui-truthfulness             1/1     relay pricing-page          1/1
links                 2 + 2 skip
static-sanity   PASS (alle harde checks)   check-cache-bust   340 links OK
check-csp-inline           OK      check-test-declarations  121 suites
apply_seo_head --check   0 pages   eslint                    schoon

Gerebased

Op main van vandaag, drie keer, want er landden tijdens deze ronde vier andere PR's. De botsingen zaten allemaal in de stylesheet-lijst van de <head>, en zijn allemaal op dezelfde regel opgelost: de lijst van main blijft staan, alleen de cache-bust van deze tak gaat mee. De enige regel die deze tak in die 58 pagina's aanraakt is daarmee de cache-bust van design-system.css. Te controleren met:

git diff origin/main..HEAD -- '*.html' | grep '^[+-]' | grep -v '^[+-][+-]' | grep -vc 'design-system.css?v='

Dat geeft 0.

@Apolloccrypt
Apolloccrypt force-pushed the feat/droom-systeem branch 2 times, most recently from 8611016 to 5f4ab4a Compare September 3, 2026 01:11
@Apolloccrypt Apolloccrypt changed the title Design system v5: gemeten kleur, een echte donkere modus en de 2026-componentlaag Design system v4.1, licht: de kleine letter en de prijsregel halen WCAG AA, geen donkere modus Sep 3, 2026
@Apolloccrypt
Apolloccrypt force-pushed the feat/droom-systeem branch 3 times, most recently from f6078a8 to dbc5657 Compare September 3, 2026 02:12
Een contrastmeting over 4448 tekstparen op zestien pagina's, op 390 en 1440,
wees een token aan. --ink-dim stond op rgba(11,58,106,.65) en landt daarmee op
4.09:1 tegen het papier, 3.96:1 tegen de voet en 4.18:1 op een witte kaart.
Drie keer net onder de 4.5 die AA vraagt, en drie keer onder de tekst die de
prijs, de limiet en de kleine letter draagt. Niet te zien met het blote oog,
wel het verschil tussen voldoen en niet voldoen.

--ink-dim, --lime-dim en --black-dim wijzen nu naar --ink-2 (#40505F, 7.95:1
op het papier). Dat is de enige wijziging aan het ontwerpsysteem; elke andere
waarde staat zoals hij stond, en design-system.css groeit van 85374 naar 86270
bytes, waarvan het grootste deel commentaar met de gemeten getallen.

Gemeten, licht, uniek onder AA, tegen main op 39179f5:

  hele site         82 -> 5
  /                  4 -> 1     (de rest zit in het style-blok van #382)
  /pricing           8 -> 0
  /parasign          7 -> 0
  /security          8 -> 1     (het lime vinkje, 1.16:1, bullet en geen tekst)
  /dashboard         0 -> 0     (#381 heeft die pagina al schoongemaakt)

De vijf die overblijven staan met naam en reden in KNOWN_LIGHT in
tests/theme-contrast.test.mjs. Geen van de vijf komt uit het ontwerpsysteem:
alle vijf staan als letterlijke kleur in het style-blok van een pagina en alle
vijf staan al op main. De test faalt op elke nieuwe. De statusregel van
/parashare stond er ook op en is door #381 opgelost, dus hij is van de lijst
af: korter mag, langer niet.

Geen donkere modus. Een eerdere stand van deze tak droeg een volledige donkere
tokenset achter [data-theme], zonder schakelaar in de nav, onleesbaar op
/security, /pricing en het dashboard, en 31 KB zwaarder op alle 58 pagina's.
Die laag is er in zijn geheel uit; wat blijft is de gemeten lichte winst.

tests/theme-contrast.test.mjs meet elk tekstpaar op elke pagina op 390 en 1440,
vlakt de tekstkleur af tegen de eerste ondoorzichtige achtergrond erboven en
rekent WCAG 2.1. 4448 paren, vijf bekende gevallen, nul nieuwe.

tests/motion-safety.test.mjs vergelijkt elke pagina met en zonder
prefers-reduced-motion en faalt zodra een element verdwijnt, vervaagt of
inklapt in plaats van alleen stil te staan. De review meldde dat /parashare
een element verloor, 272 tegen 271. Het meetpunt deugde niet.

Hij laadde elke pagina twee keer, een keer met en een keer zonder reduce, en
legde de twee lijsten naast elkaar. Dat vergelijkt twee paginaladingen en niet
twee instellingen. /parashare start 400ms na het laden een WebGL-globe waarvan
het aantal knopen op een vast meetmoment in de ene lading wel en in de andere
nog niet staat: acht keer op rij 272 tegen 267, en op de CI-runner kwam dat
binnen twintig seconden nooit tot stilstand.

Elke pagina wordt nu een keer geladen. Na de binnenkomst: opname A, dan
page.emulateMedia reduce en opname B, dan terug en opname C. Dezelfde DOM,
dezelfde scripts, een variabele. C is de controle: wat de pagina zelf tussen
twee metingen weghaalt is in C ook weg en telt niet als schade van reduce.
Vergeleken wordt op wat een layoutbox heeft, op een DOM-pad in plaats van op
een volgnummer in een platte lijst.

Dat is een scherpere claim en geen zwakkere. Gesaboteerd om te zien dat hij
bijt: display:none, opacity 0 en visibility:hidden onder reduce geven alle
drie rood, met pad en maat.

tests/apply-nav-idempotent.test.mjs is nieuw. frontend/apply-nav.py stempelt de
nav, de voet en de vier cache-bust-verwijzingen in 58 pagina's, en zijn DS_LINK
liep achter op wat die pagina's dragen. Een onschuldige draai zou 58 pagina's
hebben teruggezet naar een stylesheet-url die niet meer bestaat. De test draait
de generator op een kopie van frontend/ en eist een lege diff. Alles in
functiescope, zodat tests/*.mjs er geen naam bij krijgt.

.gitignore vangt node_modules nu ook zonder slash. Een regel met slash matcht
alleen een map, en een symlink is voor git een bestand: zo belandde er in een
eerdere stand van deze tak een 120000-blob in de commit.

Pixeldiff van index.html tegen main, dezelfde chromium, animaties en transities
uit: op 390x844 en 1440x900 nul verschillende pixels. Over de hele pagina 3705
pixels op 46 regels, en die 46 regels zijn de twee voetregels die op --ink-dim
staan. Dat is precies de wijziging en niets anders.
@Apolloccrypt
Apolloccrypt merged commit b492ffb into main Sep 3, 2026
13 checks passed
Apolloccrypt pushed a commit that referenced this pull request Sep 3, 2026
…jurybesluit

Het oude document beschreef een richting die op drie punten is teruggedraaid
en op een vierde nooit is uitgevoerd. Het wees naar een tokenbestand dat door
niets geladen wordt en naar elf screenshots van prototypepagina's die niet
bestaan. Elke regel is nu getraceerd naar de PR die hem heeft besloten.

De richting leeft in vier lagen en niet in een. design-system.css v4.1 op alle
58 paginas, licht only; nav.css; app-2026.css op elf app-schermen, met donker
als keuze op /account onder paramant.theme.v1; en een eigen --hp-palet in de
kritieke CSS van de homepage. Het document zei dat er een globale tokenlaag
zou komen. Die is er niet en het bestand dat hem zou dragen is hier weg.

Drie dingen zijn verworpen en staan nu met hun reden in het document. De
STIL-balk in mono hoofdletters (#392: de stem van een terminal boven een
pagina in papier, inkt en zinnen, en Help stond daardoor op 9px). Lime als
primaire knop (#392: lime is de leestekenkleur van dit merk en las naast de
kobalt-heroknop als een tweede aanbod). En donker op marketing (#380:
onbereikbaar achter [data-theme] zonder schakelaar, onleesbaar wit op lime op
1.07:1, en 31 KB op elke pagina zonder gzip).

frontend/design-tokens-2026.css is weg. Geen pagina, build, test of nginx-conf
laadde hem, en hij was in tegenspraak met wat wel draait: hij schreef donker
als :root:not([data-theme="light"]), precies de sprong van creme naar zwart
die #381 heeft weggehaald en die docs/site-claims.md rij 28 en
tests/app-theme.test.mjs bewaken. Zijn aliaslaag verschoof --space-8/9/10 en
liet --space-10 en --space-11 ongedefinieerd, en --navy: var(--ink) zou elke
navy op 58 paginas in een keer verzetten. De levende bron is app-2026.css.

De elf prototypebeelden zijn vervangen door twee van de echte homepage, met
scripts/brand-shots-direction.mjs. Dat script volgt de regel uit #393: een
tempmap standaard, docs/brand/assets alleen met PARAMANT_WRITE_BRAND_SHOTS=1,
en het hergebruikt de vlag en de docs-check uit scripts/brand-shots-dir.mjs in
plaats van er een tweede kopie van te houden.

Twee merkbesluiten staan open in sectie 8. Een gevuld accent per scherm, want
/pricing draagt sinds de balkwissel zeven btn-primary plus de knop in de balk,
en op het eerste scherm staan er twee naast elkaar. En de koele naad: de balk
op rgb(248,250,252) tegen homepagepapier #FAF8F3, nu verborgen achter een
gradient over de eerste 160px, met de balkkleur gepind door
tests/navigation-shell.test.mjs.

Poorten groen: tests/static-sanity.sh (twaalf checks),
scripts/check-test-declarations.sh (134 suites), node --test
tests/links.test.mjs, npx eslint@9 . (exit 0). Extra:
tests/brand-shots-optin.test.mjs 10/10, scripts/check-cache-bust.sh 363 links,
tests/frontend-loading-contract.test.mjs 7/7.
@Apolloccrypt
Apolloccrypt deleted the feat/droom-systeem branch September 5, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants