UDG: code styling tokens (syntax highlighting, diff, mono) #217

Open
opened 2026-10-20 15:07:31 +00:00 by peterswimm · 2 comments
Owner

Split out of #89 (VS Code and Xcode theme templates), which is closed as deferred.

ville's 2026-09-29 assessment found no consumer for branded editor themes. The greater need is code styling inside UDG: how code, diffs and terminal output look in Rituals, the docs and the dashboards.

Scope

  • Syntax-highlighting tokens in DTCG: comment, keyword, string, number, function, type, punctuation, and the added/removed diff tints. Each needs light and dark values that pass WCAG AA on that theme's code surface.
  • A mono type token pairing, reusing the existing fontMono resolution.
  • Consumers: the Dart code and diff block renderers (packages/udg/lib/src/blocks/block_renderers/) and the macOS equivalents.

Not in scope

Exporting VS Code or Xcode theme files. If a consumer appears later, those can be generated from these tokens.

Acceptance

  • The tokens exist in DTCG with contrast-pair tests in both themes.
  • The code and diff renderers read them, with no hardcoded colours.
Split out of #89 (VS Code and Xcode theme templates), which is closed as deferred. ville's 2026-09-29 assessment found no consumer for branded editor themes. The greater need is code styling *inside* UDG: how code, diffs and terminal output look in Rituals, the docs and the dashboards. ## Scope - Syntax-highlighting tokens in DTCG: comment, keyword, string, number, function, type, punctuation, and the added/removed diff tints. Each needs light and dark values that pass WCAG AA on that theme's code surface. - A mono type token pairing, reusing the existing `fontMono` resolution. - Consumers: the Dart `code` and `diff` block renderers (`packages/udg/lib/src/blocks/block_renderers/`) and the macOS equivalents. ## Not in scope Exporting VS Code or Xcode theme files. If a consumer appears later, those can be generated from these tokens. ## Acceptance - The tokens exist in DTCG with contrast-pair tests in both themes. - The `code` and `diff` renderers read them, with no hardcoded colours.
Member

Approval needed: code tokens for #217, one new colour

Add color.syntax.* (comment, keyword, string, number, function, type, punctuation) and color.diff.added / color.diff.removed to the baseline and Rituals palettes. The code and diff renderers will read these tokens. The diff renderer now takes its colours from color.safety.*, which is the governance palette.

One new colour: #4E6552, the Rituals light syntax.string. Every other value is an existing UDG value, copied into the role token. A drift test ties each copy to its source.

Why #4E6552 is needed. In the Rituals light themes, plum (pilgrimage) and slate (federation) look alike under protanopia (ΔE00 2.3), whichever roles they take. Sage separates from both. The existing light sage (accent.workshop) is 4.49:1 on the code surface, 0.01 short of AA. #4E6552 is the same family, slightly darker, at 4.7:1. Dark and vanillanight use the existing sage #99B78F.

Contrast. Every syntax role is at least 4.5:1 on the code surface (bg.surface-variant) in every theme, with the lowest at 4.50:1. The vanillanight roles reuse the existing dark values, because its own accent values are 3.5:1.

Diff uses Sanctum blue for added and accent.error for removed, not green and red. Green and red tints collapse under deuteranopia (ΔE00 1.2 to 1.7). Blue and red stay apart (5.6 to 8.2, except Rituals light at 3.5, see below). In Rituals, accent.success is rose, so it does not read as green anyway. This is a value copy only: accent.sanctum keeps its mode meaning.

Remaining weak spots (analysis thresholds from the #173 check, not an accessibility standard):

  • Rituals light, diff added/removed: ΔE00 3.5 under protanopia.
  • Baseline: comment/type 3.9 under deuteranopia (light), and string/number 3.9 to 4.0 under tritanopia, which is very rare.

Colour is never the only cue. Diff lines carry a + or - mark and their content. Syntax colours sit on top of the code text itself.

Condition. Baseline number uses accent.warning. It passes AA only with the revised light value in #230.

Scope of the PR: DTCG tokens, WCAG pairs, drift test and spec notes in this repo. The Dart and macOS renderer changes are a separate PR in toilville_rituals.

**Approval needed: code tokens for #217, one new colour** Add `color.syntax.*` (comment, keyword, string, number, function, type, punctuation) and `color.diff.added` / `color.diff.removed` to the baseline and Rituals palettes. The `code` and `diff` renderers will read these tokens. The diff renderer now takes its colours from `color.safety.*`, which is the governance palette. **One new colour:** `#4E6552`, the Rituals light `syntax.string`. Every other value is an existing UDG value, copied into the role token. A drift test ties each copy to its source. **Why `#4E6552` is needed.** In the Rituals light themes, plum (`pilgrimage`) and slate (`federation`) look alike under protanopia (ΔE00 2.3), whichever roles they take. Sage separates from both. The existing light sage (`accent.workshop`) is 4.49:1 on the code surface, 0.01 short of AA. `#4E6552` is the same family, slightly darker, at 4.7:1. Dark and `vanillanight` use the existing sage `#99B78F`. **Contrast.** Every syntax role is at least 4.5:1 on the code surface (`bg.surface-variant`) in every theme, with the lowest at 4.50:1. The `vanillanight` roles reuse the existing dark values, because its own accent values are 3.5:1. **Diff uses Sanctum blue for added and `accent.error` for removed, not green and red.** Green and red tints collapse under deuteranopia (ΔE00 1.2 to 1.7). Blue and red stay apart (5.6 to 8.2, except Rituals light at 3.5, see below). In Rituals, `accent.success` is rose, so it does not read as green anyway. This is a value copy only: `accent.sanctum` keeps its mode meaning. **Remaining weak spots** (analysis thresholds from the #173 check, not an accessibility standard): - Rituals light, diff added/removed: ΔE00 3.5 under protanopia. - Baseline: comment/type 3.9 under deuteranopia (light), and string/number 3.9 to 4.0 under tritanopia, which is very rare. Colour is never the only cue. Diff lines carry a `+` or `-` mark and their content. Syntax colours sit on top of the code text itself. **Condition.** Baseline `number` uses `accent.warning`. It passes AA only with the revised light value in #230. Scope of the PR: DTCG tokens, WCAG pairs, drift test and spec notes in this repo. The Dart and macOS renderer changes are a separate PR in `toilville_rituals`.
Author
Owner

Approved (Peter, 2026-10-27). Go ahead with the PR.

Approving the whole set: color.syntax.* (comment, keyword, string, number, function, type, punctuation) and color.diff.added / color.diff.removed in the baseline and Rituals palettes, plus the one new colour.

The new colour is justified and I'm approving it as the exception it is. #4E6552 for Rituals light syntax.string — the existing light sage at 4.49:1 is 0.01 short of AA, and shipping a role token that misses the floor by a rounding error is worse than adding one value. The protanopia case for sage over plum/slate (ΔE00 2.3 between pilgrimage and federation) is the right reason to pick the family. Dark and vanillanight reusing the existing #99B78F keeps it to one.

Your condition is met — I checked rather than assuming. Baseline number depends on the revised accent.warning, and #230 is merged: Brand/UDG/dtcg/color.json now carries accent.warning light #B43A01, accent.error #AA0110, accent.critical #84014C. None of the three superseded values (#CD4D0B, #DC2626, #960157) survive anywhere in the file. So number passes AA on the code surface as of now, not conditionally.

Blue-and-red for diff instead of green-and-red: yes. Green/red collapsing at ΔE00 1.2–1.7 under deuteranopia is disqualifying for the one UI element people scan fastest, and in Rituals accent.success is rose, so green was never available anyway. Taking the value from accent.sanctum as a copy while the mode meaning stays put is the right mechanism. Moving diff off color.safety.* is overdue on its own merits — the governance palette should not be doing double duty as a syntax palette, and a reader has no way to tell which meaning a colour is carrying.

The drift test is the part that makes this safe, so treat it as load-bearing, not as test scaffolding. Nearly every value here is a copy, and copies decay silently; the test tying each role token to its source is what keeps "reuses the existing value" true a year from now.

The remaining weak spots are accepted as noted. Rituals light diff at ΔE00 3.5 under protanopia, baseline comment/type at 3.9 under deuteranopia, string/number 3.9–4.0 under tritanopia. These are below the #173 analysis thresholds, not below an accessibility floor — every role still clears 4.5:1 on bg.surface-variant, and colour is never the only cue, since diff lines carry +/- and syntax colour sits on text that is already legible. Worth a line in the spec notes so the next person reads them as known and accepted rather than as an oversight.

Scope as you proposed: DTCG tokens, WCAG pairs, drift test and spec notes here; the Dart and macOS renderer changes as a separate PR in toilville_rituals. Acceptance stays as the issue body sets it — tokens exist with contrast-pair tests in both themes, and the code and diff renderers read them with no hardcoded colours.

**Approved (Peter, 2026-10-27).** Go ahead with the PR. Approving the whole set: `color.syntax.*` (comment, keyword, string, number, function, type, punctuation) and `color.diff.added` / `color.diff.removed` in the baseline and Rituals palettes, plus the one new colour. **The new colour is justified and I'm approving it as the exception it is.** `#4E6552` for Rituals light `syntax.string` — the existing light sage at 4.49:1 is 0.01 short of AA, and shipping a role token that misses the floor by a rounding error is worse than adding one value. The protanopia case for sage over plum/slate (ΔE00 2.3 between `pilgrimage` and `federation`) is the right reason to pick the family. Dark and `vanillanight` reusing the existing `#99B78F` keeps it to one. **Your condition is met — I checked rather than assuming.** Baseline `number` depends on the revised `accent.warning`, and #230 is merged: `Brand/UDG/dtcg/color.json` now carries `accent.warning` light `#B43A01`, `accent.error` `#AA0110`, `accent.critical` `#84014C`. None of the three superseded values (`#CD4D0B`, `#DC2626`, `#960157`) survive anywhere in the file. So `number` passes AA on the code surface as of now, not conditionally. **Blue-and-red for diff instead of green-and-red: yes.** Green/red collapsing at ΔE00 1.2–1.7 under deuteranopia is disqualifying for the one UI element people scan fastest, and in Rituals `accent.success` is rose, so green was never available anyway. Taking the value from `accent.sanctum` as a copy while the mode meaning stays put is the right mechanism. Moving diff off `color.safety.*` is overdue on its own merits — the governance palette should not be doing double duty as a syntax palette, and a reader has no way to tell which meaning a colour is carrying. **The drift test is the part that makes this safe, so treat it as load-bearing**, not as test scaffolding. Nearly every value here is a copy, and copies decay silently; the test tying each role token to its source is what keeps "reuses the existing value" true a year from now. **The remaining weak spots are accepted as noted.** Rituals light diff at ΔE00 3.5 under protanopia, baseline comment/type at 3.9 under deuteranopia, string/number 3.9–4.0 under tritanopia. These are below the #173 analysis thresholds, not below an accessibility floor — every role still clears 4.5:1 on `bg.surface-variant`, and colour is never the only cue, since diff lines carry `+`/`-` and syntax colour sits on text that is already legible. Worth a line in the spec notes so the next person reads them as known and accepted rather than as an oversight. Scope as you proposed: DTCG tokens, WCAG pairs, drift test and spec notes here; the Dart and macOS renderer changes as a separate PR in `toilville_rituals`. Acceptance stays as the issue body sets it — tokens exist with contrast-pair tests in both themes, and the `code` and `diff` renderers read them with no hardcoded colours.
Sign in to join this conversation.
No description provided.