Skip to content

Keep the reader's colour scheme and motion preference, and refuse a page that drops them #87

Description

@iderex

Depends on #63 and on #66.

The pages served today answer two questions about the reader's own machine, and
nothing in this plan says the generated ones have to:

for p in / /design-system.html; do
  printf "%-40s " "https://flowfin.dev$p"
  curl -sS "https://flowfin.dev$p" \
    | grep -o -i -E 'prefers-color-scheme: *[a-z]+|prefers-reduced-motion: *[a-z-]+|color-scheme: *[a-z ]+' \
    | sort -u | tr '\n' ' '
  echo ""
done
https://flowfin.dev/                     color-scheme:light dark prefers-color-scheme:dark
https://flowfin.dev/design-system.html   prefers-color-scheme: light prefers-reduced-motion: reduce prefers-reduced-motion:no-preference prefers-reduced-motion:reduce

Run 2026-08-08. The page that demonstrates the design system also carries the
motion it demonstrates:

curl -sS https://flowfin.dev/design-system.html \
  | grep -o -i -E '(transition|animation)[a-z-]*:' | sort | uniq -c
      1 animation:
      4 transition:

Run 2026-08-08. Both behaviours exist in what is published now, and the
generator in #4 is the thing that would drop them. Every check in this plan
stays green if it does. The byte budget in #34 counts bytes and would get a
smaller number. The audit in #36 runs at whatever the harness opens with. Its
contrast leg covers the colour vision presets rather than the scheme. #71 loads
a narrow viewport and enlarged text and says nothing about either of these. The
only party who finds out is a reader on a machine set to the other scheme, or
one who asked their system to reduce motion.

Two rules, both decidable by a machine, and neither of them a preference this
issue invents. They are what is published today, kept.

Every produced page follows the reader's colour scheme, and the document says
which schemes it supports so that a browser picks the right default background
and the right form control rendering before any stylesheet has been read.

Every produced page follows a request to reduce motion. Anything that moves sits
inside a query that switches it off. This is not a rule about whether the site
animates. It is a rule that the answer belongs to the reader.

Both legs run in the headless browser #63 pins, over every produced page, under
that browser's emulation of each preference, with the honesty rule the rest of
the plan uses: a browser that cannot start reds the run and never passes it.

The static half is cheaper and catches the ordinary case. A produced page
carrying a motion-bearing declaration that no reduce query switches off reds the
invariant gate in #14, by the same walk over the output that #85 uses for image
dimensions.

Where a value behind either behaviour is a token it comes from the pinned copy
#66 keeps, and nothing here defines one. That is #65's rule rather than a new
one.

Done when

Every produced page declares the schemes it supports and renders under each,
shown by the headless run loading every page under both emulated schemes; every
produced page renders with motion switched off under an emulated request to
reduce it, shown by the same run; a produced page carrying a motion-bearing
declaration outside a reduce query reds the invariant gate and the failure names
the page and the declaration; a browser that is present and cannot start reds
the run; and no colour value behind either behaviour is defined in this tree.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions