Skip to content

The app screens get the 2026 design direction, with a dark mode that is a first citizen - #381

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

The app screens get the 2026 design direction, with a dark mode that is a first citizen#381
Apolloccrypt merged 3 commits into
mainfrom
feat/droom-app

Conversation

@Apolloccrypt

@Apolloccrypt Apolloccrypt commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Group app of the "Paramant moet een droom zijn" build. The claims are pinned and finished, so this is the experience: the ten screens of the product itself, on the token system from docs/brand/design-direction.md (PR #373), in light and in dark, plus the states nobody drew yet.

Not stacked. feat/droom-systeem was not on origin when this started, so everything needed is in this branch and nothing outside group app is touched.

Repair round, after the review

Four findings, all four addressed on this branch.

1 and 2. /signup/verified was half dark. The page took the dark critical CSS, the two theme-color metas and app-2026.css, but its own <style> block still carried fifteen light hexes. In dark that gave h1 #0B3A6A on #0A0F16 at 1.67:1, .vlead #475569 at 2.53:1, the FAQ h2 #64748B at 4.03:1, and a .next-box that stayed a light-blue panel with the help link on it at 2.19:1. Every colour in that block is a token now: --ink-1 for headings, --ink-2 for running text, --ink-3 for meta, --accent-soft on --line for the panel, --surface for the subject box and the FAQ cards.

3. The gate had a hole exactly where the page broke. tests/app-contrast.test.mjs walked ten screens and this branch touches eleven. /signup/verified is in SCREENS now, and it is walked signed out, because you land there straight from the mail link and have never signed in. That is what caught a defect nobody had reported: .nav-signin falls back to a hard #64748b in design-system.css v4, 4.03:1 on the dark paper, on every app page in the signed-out state. app-2026.css points it at --ink-2.

4. The parashare beacons. Eight .dot elements in the card heads were section markers, not measurements, and section 3 of the direction says the beacon only beats where something is really measured. design-system.css v4 puts status-pulse and a navy ring on every .dot; both are reset in the page, and the one dot that really tracks something, #waiting-dot, keeps the pulse. Infinite animations on parashare go from 14 to 7, and the only one inside main is that waiting beacon. The remaining six are named and left: hcPulse on the four frame corners, globeSpin on the loading ring and blink on the session dot, all inside the globe overlay outside main, v4 chrome on a surface this branch does not redesign. prefers-reduced-motion still zeroes all seven.

One thing beyond the findings, on the same page. The FAQ rows drew Chromium's list-item triangle next to the page's own plus sign. The marker is off and the row is a flex line, so each summary is one 44px target with one indicator.

Measured after the repair.

  • tests/app-contrast.test.mjs: 876 colour pairs over 11 screens x 2 widths x 2 themes, none under AA, and the 44px tap check green on all eleven.
  • axe-core 4.13.0 on /signup/verified and /parashare, 390x844 and 1440x900, light and dark, no-preference and reduce: 0 violations in all 16 runs, down from 6 on this page.
  • Re-run on the branch head after the theme switch landed on top, with the appearance choice stored: app-contrast still green at 880 pairs, and axe still 0 violations in all 16 runs. On /signup/verified in dark the body computes to rgb(10,15,22), the h1 to rgb(237,241,246) and the panel to rgba(127,166,255,.12), so the clean run is a dark screen and not a light one measured twice.
  • Lighthouse 13.4.1 on /signup/verified, mobile profile, same local server: accessibility 89 to 100, performance 99 to 98, best practices 100, CLS 0.003 to 0.007. The single extra stylesheet is the whole difference on the two that moved.
  • Four new screenshots in docs/brand/assets/app-2026/: verified-390x844-{light,dark}.png and verified-1440x900-{light,dark}.png, shot signed out. The four parashare shots are regenerated.

Rebased on main at 05bbd1b.

Second repair round: dark is a choice, never a jump

One finding, and it was the right one to stop on.

What was measured. With prefers-color-scheme: dark and no data-theme anywhere, /index and /pricing stayed light, while /dashboard, /sign, /parashare, /account and /auth/login came up on rgb(10,15,22). An advocate on a dark operating system went from creme to black by signing in, in one click, with no way to say no. The dark mode itself is good: dashboard dark had one AA fault at the time, light had none, and both are at zero now. What was wrong was how you arrived in it.

Light is the default, everywhere. The dark token block in app-2026.css and the critical <style> of all twelve app pages are scoped to [data-theme="auto"], and the explicit :root[data-theme="dark"] block is unchanged. With no choice made there is no attribute, nothing below matches, and the app is light. That holds with JavaScript switched off as well: the light default does not depend on a script running. The dark palette itself did not move a single value.

A three-way switch on /account. Follow my system, Light, Dark. Picking one is the change, no save button, and the page you are standing on repaints as its own preview. The radio input is 0x0 and the label carries the 44px target, so a 13px radio is never the thing you have to hit. The text next to it says what the choice covers, that it is kept in this browser only, and that the public pages stay light.

frontend/js/theme.js. A plain blocking <script> at the end of the <head> of every app page. It runs while the head is parsed, before the body exists and before the first paint, so a reader who chose dark sees no light flash. It cannot be inline: the site runs under script-src 'self'. It writes nothing when there is no choice, so the light default and the no-JavaScript case are the same path. It also moves the theme-color meta, which was on its own media query and would have tinted the address bar dark above a light page; the two media-scoped metas are one plain meta now.

Storage. localStorage['paramant.theme.v1'], one of auto, light, dark. Anything else, and a browser that refuses storage, reads as no choice. The key goes on /privacy, in the local-storage list, which is where a reader can act on it, and row 28 of tests/site-claims.test.mjs (from #372) already fails on a key that list does not name. docs/site-claims.md records what the key is for. There is deliberately no second inventory: tests/ui-truthfulness.test.mjs pins the name, the three values, that theme.js contains no fetch, XMLHttpRequest or sendBeacon, and the three sentences beside the switch.

tests/app-theme.test.mjs, new. Eight checks in Chromium over the six screens a reader meets after signing up (/dashboard, /sign, /parashare, /account, /auth/login, /signup/verified):

what it sets what it must measure
dark system, no choice body rgb(251, 250, 247), no data-theme, theme-color #FBFAF7
dark system, choice light light
light system, choice dark body rgb(10, 15, 22), theme-color #0A0F16
choice auto dark on a dark system, light on a light one
/ and /pricing, dark system, every choice light, which is what the switch text promises
the switch on /account three values in order, the page repaints, the choice survives to /dashboard
every page loading app-2026.css loads /js/theme.js

The sabotage. The last test serves the pages back with [data-theme="auto"] rewritten to the :not([data-theme="light"]) it replaced, in the stylesheet and in every page's critical <style>, and fails unless all six screens go dark again. Without it a suite like this one passes just as happily against a site that has no dark mode at all. It is green, so the seven tests above are measuring the gate and not the absence of one.

Two suites were lying by omission until this landed. tests/app-contrast.test.mjs and tests/app-shots.mjs reached dark by setting colorScheme alone. From this commit on that no longer darkens anything, so both store the choice. Without that change the dark half of the contrast run would have measured the light screens twice and reported clean, and sixteen of the screenshots would have been more pictures of the light theme.

Measured, before and after. This suite run against the parent commit fails exactly as reported: all six screens on rgb(10, 15, 22) with a dark system and nothing chosen. It is 8/8 green here, sabotage included.

app-contrast: 876 distinct colour pairs over 11 screens x 2 widths x 2 themes, none under AA, up from 872 on the parent by the four the switch adds. The 36 screenshots are taken again with the choice stored, so the dark half is the dark theme; dashboard-1440x900-light-reduced-motion.png is still byte-identical to dashboard-1440x900-light.png, and the same in dark.

Rebased on origin/main at #385. The nine-line cache-bust collision with #380 is untouched and still nine files: this branch leaves design-system.css?v=23 and nav.css?v=19 exactly where they are, so whichever merges second takes #380's v=24 and v=20 and nothing else has to be decided.

What changed

frontend/app-2026.css is the app layer. It defines the 2026 palette from section 2 of the direction, written three times so an explicit [data-theme] choice and the system preference both win and no colour exists only inside a media query. Then it points the v4 token names at those values, so every rule already written against --bone or --ink-hair follows the theme without being rewritten. That alias layer is what made a dark mode on ten screens affordable at all. It is the last stylesheet in the head, so the hard-coded hexes the pages still carry can be corrected from one place. Marketing pages do not load it and do not change.

Dashboard. The loading state now has the shape of the dashboard instead of a spinner on an empty page: hero, the three ways in, and the first register rows are all reserved. Register rows get a meter with the number next to it, never a bar alone, and a status chip in the four words dashboard.js actually renders (waiting, in_progress, completed, cancelled). Filters became a segmented control with the count inside the button, because the count is the reason you click it. Under 760px the three columns stack in reading order. The document dialog is excluded from the arrival animation: it is position: fixed, and an animating ancestor was quietly dragging the whole overlay 6px down the screen.

parashare. A real 44px copy button next to the link instead of a click-anywhere box, and the confirmation lands on the button rather than overwriting the link you just asked to see. Upload progress carries its percentage in text and on the track, so a screen reader hears the number the eye reads. The burn-on-read warning moved to --sig-wait: it is not a fault. Three fields got real <label for> elements.

signup. One column, no marketing rhythm leaving holes between the five steps. When you are already signed in, the form used to be dimmed to opacity: 0.4, which looked disabled but was not: the fields stayed in the tab order and every label underneath dropped to 1.78:1. It is now inert, and readable.

account. One filled accent on the screen, not two. Dangerous work is a hairline that reddens. Two inputs that lived outside .form-card had never been given a size and measured 22px on a phone.

auth. The error moved above the form with role="alert". Passkey and code sit next to each other as two ways in, instead of one with an "or" underneath making the other second-rate.

sign. The document layer is pinned to fixed colours on purpose, and it is the one place in the app where a token is deliberately not used. A PDF page is white paper in every theme, and the seal is a printed artifact: under a dark theme --cobalt resolves to a light blue, and a white-on-light-blue seal would have previewed a stamp no printer will ever produce.

The new gate

tests/app-contrast.test.mjs walks all eleven app screens in Chromium at 390x844 and 1440x900, in light and dark, reads the computed colour of every visible text node, flattens it against the first opaque background behind it, and fails under AA. Source inspection cannot do this: the whole point of the alias layer is that a rule written years ago now resolves differently per theme, and only the browser knows the answer. It also checks that a coarse pointer gets a 44px target, with one deliberate exception, a text link inside a running paragraph, which lives under the reading rule with its underline.

876 distinct colour pairs measured. None under AA. Eleven screens, two widths, and the dark half is the dark theme: both app-contrast and app-shots store the appearance choice now that a dark operating system alone no longer darkens anything.

Measured, not claimed

Lighthouse 13.4.1 against a local server with the API stubbed, mobile profile.

screen perf a11y best practices CLS
/dashboard 95 100 100 0.005
/sign 86 100 100 0.005
/parashare 75 100 100 0.003
/signup 97 100 100 0.06
/account 94 100 100 0.003
/auth/login 98 100 100 0.005
/signup/verified 98 100 100 0.007

Baseline on main, same server, same run: dashboard perf 83, a11y 96, CLS 0.236. parashare perf 68, a11y 84. The dashboard skeleton is what took CLS from 0.236 to 0.005.

SEO scores 66 on every app screen, before and after. That is the robots: noindex these pages carry in production, and it is correct.

Reduced motion. dashboard-1440x900-light-reduced-motion.png is byte-identical to dashboard-1440x900-light.png, and the same holds in dark. Everything that moves stops, and no state disappears.

Screenshots

36 images in docs/brand/assets/app-2026/, deviceScaleFactor 2, generated by tests/app-shots.mjs.

Eight screens at 390x844 and 1440x900, light and dark: dashboard, sign, parashare, signup, account, login, setup, verified (that last one shot signed out). Plus the dashboard loading silhouette and the reduced-motion pair.

dashboard-1440x900-light.png dashboard-1440x900-dark.png
dashboard-390x844-light.png dashboard-390x844-dark.png
dashboard-1440x900-light-loading.png dashboard-1440x900-dark-loading.png
dashboard-1440x900-light-reduced-motion.png dashboard-1440x900-dark-reduced-motion.png
sign-*, parashare-*, signup-*, account-*, login-*, setup-* four each

Gates

tests/static-sanity.sh eleven checks, first-screen 9, pricing-fold 4, navigation-shell 1, user-dashboard-documents 25 checks, ui-truthfulness, site-claims 22, seo-contract 14, links, frontend-loading-contract 7, frontend-module-scripts, product-heartbeat 9, usage-purpose 8, code-manifest, version-consistency, app-contrast 2, app-theme 8. scripts/check-csp-inline.sh and scripts/check-cache-bust.sh clean, 351 links on one consistent ?v=. tests/heartbeat-lib.test.mjs fails on this machine for a missing @noble/post-quantum, identically on unmodified main.

Not in this PR

  • The three-way theme switcher. It is in this PR now, on /account, because leaving prefers-color-scheme in charge until group systeem landed meant shipping the jump from creme to black. The app pages still answer to [data-theme="dark"] and [data-theme="light"] exactly as before, so a switcher in systeem that writes the same attribute keeps working; if that group lands its own control, one of the two goes and the CSS does not move.
  • design-tokens-2026.css from PR docs: de designrichting 2026 beschrijft de gemergede stand, niet het jurybesluit #373 is still not loaded by anything. app-2026.css uses the same token names and the same values, so when that file goes global the two agree rather than fight. Folding them together belongs to step 6 of the rollout in the direction, once no page names the old tokens.
  • The marketing pages. Untouched on purpose.
  • parashare performance is 75 because of the 1MB globe bundle it loads. That is outside this scope, and it is still up from 68.

@Apolloccrypt
Apolloccrypt force-pushed the feat/droom-app branch 2 times, most recently from 95f2be1 to 4d10c5c Compare September 3, 2026 01:48
…is a first citizen

The tekst is pinned and finished. This is the experience: dashboard, sign,
parashare, signup, account and the four auth screens on the token system from
docs/brand/design-direction.md, in light and in dark, with the loading, empty,
error and confirmation states that were never drawn.

frontend/app-2026.css is the app layer. It does two things. It defines the 2026
palette from section 2 of the direction, written three times so an explicit
theme choice and the system preference both win and no colour exists only
inside a media query. And it points the v4 token names at those values, so
every rule already written against --bone or --ink-hair follows the theme
without being rewritten. That alias layer is what made a dark mode on ten
screens affordable at all. Marketing pages do not load the file and do not
change.

What is new on the screens, beyond colour:

The dashboard gets a loading state with the shape of the dashboard instead of a
spinner on an empty page: the hero, the three ways in and the first register
rows are all reserved. Lighthouse measured CLS 0.236 before and 0.005 after,
and the performance score went from 83 to 95. The register rows get a meter
with the number next to it, never a bar alone, and a status chip in the four
words dashboard.js actually renders. The filters become a segmented control
with the count inside the button, because the count is the reason you click it.
Under 760px the three columns stack in reading order.

parashare gets a real 44px copy button next to the link instead of a
click-anywhere box, and the confirmation lands on the button rather than
overwriting the link you just asked to see. Upload progress carries its
percentage in text and on the track, so a screen reader hears the number the
eye reads. The burn-on-read warning moves to the wait colour: it is not a fault.

signup loses the marketing rhythm that left holes between its five steps and
becomes one column. When you are already signed in the form was dimmed to 0.4
opacity, which looked disabled but was not: the fields stayed in the tab order
and every label underneath dropped to 1.78:1. It is now inert, and readable.

The auth screens put the error above the form with role="alert", and passkey
and code sit next to each other as two ways in instead of one with an "or"
underneath it.

On sign.html the document layer is pinned to fixed colours on purpose. A PDF
page is white paper in every theme, and the seal is a printed artifact: under a
dark theme it would have previewed a stamp that no printer will ever produce.

tests/app-contrast.test.mjs is the new gate. It walks all ten app screens in
Chromium at 390 and 1440, in light and dark, reads the computed colour of every
visible text node, flattens it against the first opaque background behind it,
and fails under AA. Source inspection cannot do this: the whole point of the
alias layer is that an old rule now resolves differently per theme, and only
the browser knows the answer. It also checks that a coarse pointer gets a 44px
target, with the deliberate exception of a text link inside a running
paragraph. 792 distinct colour pairs, none under AA.

Measured, not claimed. Lighthouse against a local server, accessibility first:
dashboard 100, sign 100, parashare 100, signup 100, account 100, login 100.
On main those were 96 for the dashboard and 84 for parashare. Best practices
100 on all six. Performance 95, 86, 75, 97, 94, 98. The reduced-motion
screenshot of the dashboard is byte-identical to the normal one, in both
themes: everything stops and no state disappears.

Repo gates green: static-sanity eleven checks, first-screen, pricing-fold,
navigation-shell, user-dashboard-documents (25 checks), ui-truthfulness,
site-claims, seo-contract, links, frontend-loading-contract, csp-inline,
cache-bust.
The review on the first round was right on all four counts.

1. The page loaded the dark critical CSS, the two theme-color metas and
   app-2026.css, but its own <style> block still carried fifteen light hexes.
   In dark that gave h1 #0B3A6A on #0A0F16 at 1.67:1, .vlead #475569 at 2.53:1
   and the FAQ h2 #64748B at 4.03:1. Every colour in that block is now a token:
   --ink-1 for headings, --ink-2 for running text, --ink-3 for meta.

2. .next-box stayed a light-blue panel in a dark page, so the link to
   /help/authenticator-apps sat at 2.19:1. It is --accent-soft on --line now,
   which is the same panel in both themes.

3. The gate had a hole exactly where the page broke: tests/app-contrast.test.mjs
   walked ten screens and the branch touched eleven. /signup/verified is in
   SCREENS now, and it is walked signed OUT, because you land there straight
   from the mail link and never signed in. That is what caught the last one:
   .nav-signin falls back to a hard #64748b in design-system.css, 4.03:1 on the
   dark paper, on every app page in the signed-out state. app-2026.css points
   it at --ink-2.

   876 colour pairs over 11 screens x 2 widths x 2 themes, none under AA.
   axe-core 4.13.0 on /signup/verified and /parashare, both widths, both
   themes, both motion settings: 0 violations, down from 6 on this page.

4. The eight beacons in the parashare card heads were section markers, not
   measurements. design-system.css v4 puts status-pulse and a navy ring on
   every .dot; both are reset, and the one dot that really tracks something,
   #waiting-dot, keeps the pulse. Infinite animations on parashare go from 14
   to 7, and the 7th in main is that one waiting beacon. The other six are the
   globe overlay outside main (hcPulse, globeSpin, blink), v4 chrome on a
   surface this branch does not touch. prefers-reduced-motion still zeroes all
   of them.

One thing beyond the findings, on the same page: the FAQ rows drew Chromium's
list-item triangle next to the page's own plus. The marker is off and the row
is a flex line, so the summary is one 44px target with one indicator.
…switch on /account

Measured on this branch with prefers-color-scheme: dark and nothing chosen:
/index and /pricing stayed light, while /dashboard, /sign, /parashare, /account
and /auth/login came up on rgb(10,15,22). Signing in threw a reader on a dark
operating system from creme to black in one click, and there was no way to say
no. The dark mode itself is good. How you arrived in it was not.

So the system preference is honoured under exactly one condition now. The media
block in app-2026.css and the critical <style> of all twelve app pages are
scoped to [data-theme="auto"], and the explicit [data-theme="dark"] block stays
as it was. Without a choice there is no attribute, nothing matches, and the app
is light. That holds with JavaScript switched off too: the light default does
not depend on any script running.

frontend/js/theme.js applies a choice that was made. It is a plain blocking
<script> at the end of the head of every app page, so it runs before the body
exists and before the first paint and a reader who picked dark sees no flash.
It cannot be inline under script-src 'self'. It also moves the theme-color
meta, which was on its own media query and would have tinted the address bar
dark above a light page.

The switch is on /account: Follow my system, Light, Dark, stored in
localStorage under paramant.theme.v1, no save button, and the page you are
standing on repaints as its own preview. The radio is 0x0 and the label carries
the 44px target. The text says what the choice covers, that it never leaves the
browser, and that the public pages stay light.

Gates:
- tests/app-theme.test.mjs, new, 8 checks in Chromium over the six screens a
  reader meets after signing up. No choice on a dark system is light; "light"
  holds against a dark system; "dark" darkens on a light system; "auto" hands
  the decision back; the marketing pages stay light in every case; the switch
  works and carries to the next screen; every page loading app-2026.css loads
  theme.js. The last test serves the pages back with the [data-theme="auto"]
  gate removed and fails unless they go dark again, so the suite cannot pass
  against a site with no dark mode at all.
- tests/app-contrast.test.mjs and tests/app-shots.mjs now store the choice
  instead of only setting colorScheme. Without that the dark half of both would
  have quietly measured and photographed the light theme twice.
- /privacy gains the key in its local-storage list, which is where a reader can
  act on it, and site-claims row 28 already fails on a key that list does not
  name. docs/site-claims.md records what the key is for. There is no second
  inventory: tests/ui-truthfulness.test.mjs pins the name, the three values,
  that theme.js contains no fetch, XMLHttpRequest or sendBeacon, and the three
  sentences beside the switch.

Measured: this suite against the parent commit fails exactly as reported, with
all six screens on rgb(10,15,22) under a dark system and nothing chosen, and it
is green here. app-contrast 876 distinct colour pairs over eleven screens, two
widths, both themes, none under AA, up from 872 by the four the switch adds.
The 36 screenshots are taken again under a stored choice, so the dark half is
the dark theme and not sixteen more pictures of the light one, and the
reduced-motion pair is still byte-identical to its normal pair in both themes.
@Apolloccrypt
Apolloccrypt merged commit 39179f5 into main Sep 3, 2026
13 checks passed
Apolloccrypt pushed a commit that referenced this pull request Sep 3, 2026
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 added a commit that referenced this pull request Sep 3, 2026
…very page (#380)

One token: --ink-dim was rgba(11,58,106,.65) and is now var(--ink-2), #40505F, with the two legacy dim aliases pointing at the same. Measured on index, pricing, parasign, security and dashboard at 390 and 1440: from 82 unique text pairs under AA to 5 known cases that may only shrink, pricing and parasign at zero, the homepage pixel-identical outside its animated hero. The dark layer of the original design-system v5 is out: it sat behind a gate no switch reached and was unreadable on /security and /pricing; the app screens carry their own measured dark mode as a choice since #381. Two new gates: apply-nav.py must reproduce frontend/ byte for byte (the cache-bust versions in the generator and the pages had drifted, which would have pinned the site to a year-old stylesheet), and theme-contrast keeps the known list from growing. motion-safety measures one page load in three captures so the WebGL globe on /parashare no longer makes it flaky. The node_modules symlink is out of the history and .gitignore now catches a symlink as well as a directory.
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-app 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.

1 participant