Colour-blind safe palette and a no-colour-alone rule (#56) #86

Merged
robert merged 1 commit from area/colour-palette into main 2026-09-04 10:01:00 +02:00
Owner

Implements #56 ("Colour-blind safe palette and a no-colour-alone rule").

This gates #59 (the HERO1/HERO2/QUAD/GRID6 template renderer) — #83/#85 both explicitly deferred
real visual template work to #59, which needs this palette to exist first.

What's here

  • watchapp/src/c/palette.h — named GColor constants (neutrals, staleness grey tying into D44,
    and five semantic accent colours), aliased from the SDK's own gcolor_definitions.h, not invented
    hex values.
  • docs/DESIGN.md — new section 7 (Colour): palette source, the no-colour-alone rule restated with
    a per-feature intent table, CVD reasoning, contrast, and how to use gcolor_legible_over().
  • docs/DECISIONS.md — a small correction under D24: gabbro is 64 colours, not just "colour"
    (verified by diffing its gcolor_definitions.h against emery's — byte-for-byte identical).

No visual change today

Grepped the tree: nothing under watchapp/src/c/ sets a GColor anywhere. Every current TextLayer
draws with the system default (black on white). This PR is pure infrastructure — zero effect until
#59 (and later #54, off-route state, HR zones, GPS quality, speed source) start consuming it.

Verified vs. reasoned (the project's own convention, see docs/TEAM.md)

Verified:

  • The 64-colour palette itself — read directly from the installed SDK, not recalled:
    ~/.local/share/pebble-sdk/SDKs/4.33.1/sdk-core/pebble/{emery,gabbro}/include/gcolor_definitions.h,
    byte-for-byte identical between the two platforms.
  • basalt is also 64-colour, confirmed against current docs at
    developer.rebble.io.
  • gcolor_legible_over(GColor8) exists — confirmed both in current
    developer.repebble.com docs and by
    grepping the installed SDK's pebble.h (GColor8 gcolor_legible_over(GColor8 background_color);).
  • WCAG 2.1 SC 1.4.11's 3:1 floor for graphical objects/UI components — confirmed against the current
    spec text at w3c.github.io.
  • Contrast ratios in palette.h's comments — computed (not guessed) via the standard WCAG
    relative-luminance formula against each colour's exact SDK RGB values.
  • pebble build — actually run, clean, for gabbro/emery/basalt, with palette.h temporarily
    #included into main.c to force real compilation under arm-none-eabi-gcc (then reverted; the
    build is unaffected either way — identical heap-free figures).

Reasoned, explicitly not simulator-verified (no CVD simulator available in this sandbox):
colour-vision-deficiency confusion-axis reasoning — protanopia/deuteranopia collapse red-green,
tritanopia collapses blue-yellow. Response is structural: green is not used for any semantic/stateful
colour at all, so the red/green pairing the issue calls out can't arise by accident. Section 7.3 is
explicit about where this is honestly weakest (the blue→violet HR-zone step), which is exactly why
the no-colour-alone rule is a requirement, not a nicety.

Explicitly NOT verified, flagged as needing Robert's physical devices (both emery and gabbro are
in hand per this session's memory): direct-sunlight legibility of these swatches on the actual
e-paper. Searched for official Pebble/Rebble contrast-in-sunlight documentation and found none —
only general, non-Pebble-specific e-paper reflectivity claims, which section 7.4 is explicit about
not treating as if they were a measured figure. The WCAG contrast numbers are a real computation, but
they assume an emissive sRGB display; e-paper reflectance under direct sun is a physical property no
formula run on RGB channel values can establish.

Acceptance criteria (from #56)

  • Palette chosen and documented in docs/DESIGN.md, checked against deuteranopia/protanopia/
    tritanopia (reasoned, not simulator-verified — see above)
  • No state signalled by colour alone — documented as a rule (section 7.2) with a per-feature
    pairing table
  • Red/green never the sole distinction — satisfied structurally: green isn't used for any
    state at all
  • Verified specifically for GPS quality, speed source, off-route state, HR zones, sensor alerts
    — documented intent per feature (all future UI, none implemented yet, per the issue's own
    scope note)
  • Contrast checked on e-paper in direct sunlight — cannot be done in this sandbox; computed
    WCAG contrast instead, flagged explicitly as not a substitute, needs Robert's physical watches
  • Palette defined as named constants in one header — watchapp/src/c/palette.h

https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt

Implements #56 ("Colour-blind safe palette and a no-colour-alone rule"). This gates #59 (the HERO1/HERO2/QUAD/GRID6 template renderer) — #83/#85 both explicitly deferred real visual template work to #59, which needs this palette to exist first. ## What's here - `watchapp/src/c/palette.h` — named `GColor` constants (neutrals, staleness grey tying into D44, and five semantic accent colours), aliased from the SDK's own `gcolor_definitions.h`, not invented hex values. - `docs/DESIGN.md` — new section 7 (Colour): palette source, the no-colour-alone rule restated with a per-feature intent table, CVD reasoning, contrast, and how to use `gcolor_legible_over()`. - `docs/DECISIONS.md` — a small correction under D24: gabbro is 64 colours, not just "colour" (verified by diffing its `gcolor_definitions.h` against emery's — byte-for-byte identical). ## No visual change today Grepped the tree: nothing under `watchapp/src/c/` sets a `GColor` anywhere. Every current `TextLayer` draws with the system default (black on white). This PR is pure infrastructure — zero effect until #59 (and later #54, off-route state, HR zones, GPS quality, speed source) start consuming it. ## Verified vs. reasoned (the project's own convention, see docs/TEAM.md) **Verified:** - The 64-colour palette itself — read directly from the installed SDK, not recalled: `~/.local/share/pebble-sdk/SDKs/4.33.1/sdk-core/pebble/{emery,gabbro}/include/gcolor_definitions.h`, byte-for-byte identical between the two platforms. - `basalt` is also 64-colour, confirmed against current docs at [developer.rebble.io](https://developer.rebble.io/guides/best-practices/building-for-every-pebble/). - `gcolor_legible_over(GColor8)` exists — confirmed both in current [developer.repebble.com](https://developer.repebble.com/docs/c/Graphics/Graphics_Types/) docs and by grepping the installed SDK's `pebble.h` (`GColor8 gcolor_legible_over(GColor8 background_color);`). - WCAG 2.1 SC 1.4.11's 3:1 floor for graphical objects/UI components — confirmed against the current spec text at w3c.github.io. - Contrast ratios in `palette.h`'s comments — computed (not guessed) via the standard WCAG relative-luminance formula against each colour's exact SDK RGB values. - `pebble build` — actually run, clean, for gabbro/emery/basalt, with `palette.h` temporarily `#include`d into `main.c` to force real compilation under `arm-none-eabi-gcc` (then reverted; the build is unaffected either way — identical heap-free figures). **Reasoned, explicitly not simulator-verified** (no CVD simulator available in this sandbox): colour-vision-deficiency confusion-axis reasoning — protanopia/deuteranopia collapse red-green, tritanopia collapses blue-yellow. Response is structural: green is not used for any semantic/stateful colour at all, so the red/green pairing the issue calls out can't arise by accident. Section 7.3 is explicit about where this is honestly weakest (the blue→violet HR-zone step), which is exactly why the no-colour-alone rule is a requirement, not a nicety. **Explicitly NOT verified, flagged as needing Robert's physical devices** (both emery and gabbro are in hand per this session's memory): direct-sunlight legibility of these swatches on the actual e-paper. Searched for official Pebble/Rebble contrast-in-sunlight documentation and found none — only general, non-Pebble-specific e-paper reflectivity claims, which section 7.4 is explicit about not treating as if they were a measured figure. The WCAG contrast numbers are a real computation, but they assume an emissive sRGB display; e-paper reflectance under direct sun is a physical property no formula run on RGB channel values can establish. ## Acceptance criteria (from #56) - [x] Palette chosen and documented in docs/DESIGN.md, checked against deuteranopia/protanopia/ tritanopia (reasoned, not simulator-verified — see above) - [x] No state signalled by colour alone — documented as a rule (section 7.2) with a per-feature pairing table - [x] Red/green never the sole distinction — satisfied structurally: green isn't used for any state at all - [x] Verified specifically for GPS quality, speed source, off-route state, HR zones, sensor alerts — documented intent per feature (all future UI, none implemented yet, per the issue's own scope note) - [x] Contrast checked on e-paper in direct sunlight — **cannot be done in this sandbox**; computed WCAG contrast instead, flagged explicitly as not a substitute, needs Robert's physical watches - [x] Palette defined as named constants in one header — `watchapp/src/c/palette.h` https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt
Colour-blind safe palette and a no-colour-alone rule (#56)
Some checks failed
dev-artifact / build-pbw (push) Has been cancelled
dev-artifact / build-apk (push) Has been cancelled
fast-lane / host-c-tests (push) Has been cancelled
fast-lane / jvm-tests (push) Has been cancelled
fast-lane / pebble-build (push) Has been cancelled
fast-lane / lint-and-secrets (push) Has been cancelled
fast-lane / meta-declares-required-jobs (push) Has been cancelled
fast-lane / host-c-tests (pull_request) Has been cancelled
fast-lane / jvm-tests (pull_request) Has been cancelled
fast-lane / pebble-build (pull_request) Has been cancelled
fast-lane / lint-and-secrets (pull_request) Has been cancelled
fast-lane / meta-declares-required-jobs (pull_request) Has been cancelled
dev-artifact / publish (push) Has been cancelled
1574c9fd83
Adds watchapp/src/c/palette.h: named GColor constants for emery/gabbro/
basalt, taken from the installed SDK's own gcolor_definitions.h (verified
identical between emery and gabbro; basalt confirmed 64-colour by current
developer.rebble.io docs) rather than invented hex values.

No file under watchapp/src/c/ sets a GColor today, so this changes nothing
on screen. It's infrastructure for #59 (the HERO1/HERO2/QUAD/GRID6
renderer) and later issues (#54 sensor alerts, off-route state, HR zones,
GPS quality, speed source), none of which have any UI yet.

docs/DESIGN.md gets a new section 7 (Colour):
- the palette source and why it applies uniformly across all three build
  platforms
- the no-colour-alone rule, restated with a per-feature intent table for
  the five things this issue was asked to verify against
- colour-vision-deficiency reasoning (protanopia/deuteranopia/tritanopia),
  tagged reasoned, not simulator-verified — no CVD simulator is available
  in this environment
- contrast, tagged computed (WCAG relative-luminance formula against the
  exact SDK RGB values) but explicitly NOT sunlight-verified — that needs
  Robert's physical emery and gabbro units, both in hand
- gcolor_legible_over() as the recommended mechanism for text-on-fill,
  verified present in both current docs and the installed SDK's pebble.h

docs/DECISIONS.md gets a small correction under D24: gabbro is 64 colours,
not just "colour" (verified by diffing its gcolor_definitions.h against
emery's — byte-for-byte identical), and DESIGN.md's platform table is
corrected to match.

`pebble build` verified clean for gabbro/emery/basalt with palette.h
actually #include'd (temporarily, then reverted) to force real compilation
under arm-none-eabi-gcc, not just left unreferenced.

Claude-Session: https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt
robert merged commit 955bfe5d1f into main 2026-09-04 10:01:00 +02:00
Sign in to join this conversation.
No description provided.