Rule handbook metadata

Rule
DET.DATA.COLOR_ONLY · lane deterministic
Page status
stale
page_version
6867492406d3ec909a0742d87b388c8f118929ae94e16f369025c8fa1c41f736
generated_at
2026-05-19T21:45:00.000Z
registry_fingerprint
6773fda516344e110b5a7b1435e655e1264e773825ca8bbe62194189891c42ba

How this rule is fixed

Pilot deterministic fixer: handbook_after · harness mode standalone · verify expect_rule_clean.

  • Harness: apply-harness-fixture-remediation.py copies the After example HTML from this handbook page onto the defect fixture, then invoke-det-ruleset-remediation-verify.sh re-audits and expects zero findings for this rule.
  • Production sites: run-website-ux-remediation-loop.sh runs handbook_html_patch (HTML patches in lib/ux-deterministic-fixers/fixers/patches/) before invoking the Cursor agent when the quality gate still fails.

Detection module: docs/design/ux-audit/deterministic-design-rules.md#det-data-color_only. Scroll down for Before / After examples and Evidence and remediation steps.

Purpose

Kitchen Sink data surfacesks-chart-mount / [data-ks-chart] charts (forge-data-charts.js), token swatches on Tkn / For Agents pages (.ks-swatch, .ks-swatch-box, .ks-swatch-label), and tables beside charts—often use color to encode series, status, or magnitude. Visitors who cannot distinguish cyan from amber, or who print in grayscale, still need the same meaning from redundant text or shape.

This deterministic rule (metrics phase, colorOnlyReport) flags detectable color-only encoding inside data contexts: legend rows with a colored mark but no label (minimum 2 characters via MIN_COLOR_LABEL_CHARS), .ks-swatch blocks missing .ks-swatch-label, and table cells whose background carries meaning while the cell body is empty. Decorative ambient layers and aria-hidden subtrees are ignored.

Plan: Inventory chart mounts, token grids, and heatmap-style tables on the page. Do: Pair every swatch or color-filled cell with a visible name, value, or icon in the same row. Check: Re-run auditor metrics or inspect colorOnlyReport.violations[]. Adjust: Wire labels in generators (tokens.py, forge-data-charts.js) or page templates; for narrative quality of the insight, see AI.DATA.INSIGHT_LEGIBILITY.

Passing signals

  • Token swatches follow the showcase pattern: .ks-swatch > .ks-swatch-box + .ks-swatch-label with at least two characters (for example amber, surface-2).
  • Donut / bar legends from forge-data-charts.js use .lenses-overview-donut-swatch in a .d-flex row with a <span> series name and optional .text-muted percentage—hue is not the only cue.
  • Chart swatch rows include sibling text, .ks-swatch-label, or aria-label / title on the swatch or row container (at least 2 characters).
  • Heatmap or status tables put numeric or categorical text (or aria-label) in the same <td> as a background tint, or use icons/patterns in addition to fill.
  • SVG slices expose <title> with series name and value when a separate legend is omitted (supplements, not replaces, visible legend text for sighted users).
  • Swatches and encoded cells sit inside a data context ([data-ks-chart], .ks-chart-mount, figure, table, etc.) so the check applies; unrelated decorative dots outside those regions are not scored.

Failing signals

  • .ks-swatch with .ks-swatch-box but no .ks-swatch-label (or label shorter than two characters)—kind ks-swatch-missing-label, major.
  • .lenses-overview-donut-legend row containing only .lenses-overview-donut-swatch with inline background: and no adjacent text—kind legend-swatch-without-label, warn.
  • Generic legend marks ([class*="legend-mark"], [class*="color-dot"], [class*="status-dot"]) inside a chart or table context with no row label and no aria-label.
  • Table body cells with meaningful background-color but empty text and no aria-label, while the table has a header row with real <th> labels—kind table-cell-color-only, warn.
  • Status dashboards that rely on green/red cell fills alone for pass/fail with no column text or icon.
  • Heatmap grids where intensity is only style="background:…" on <td> elements with no legend key or cell values.

Before example

Before (failing example)

Accent palette

Repository mix

CheckStatus
Unit tests
Lint

Failing KS markup: token swatch and chart legend rely on color alone; the data table encodes severity only via background fill.

After example

After (passing example)

Accent palette

amber
cyan

Repository mix

Donut of tracked files by top-level language; Python leads at 41.2%, Markdown 33.8%.

Python: 41.2% Markdown: 33.8%
Python 41.2%
Markdown 33.8%
CheckStatus
Unit tests Pass
Lint Fail

Passing KS markup: every swatch and legend row includes text labels; status cells repeat meaning in characters, not only fill.

Evidence and remediation

Evidence: Auditor metrics colorOnlyReport with violations[] entries: kind (ks-swatch-missing-label, legend-swatch-without-label, table-cell-color-only), region, selectorHint, and className. Findings use evidence token color_only_encoding with severity major for missing .ks-swatch-label and warn for legend or table-cell cases. Capture the swatch row outer HTML, sibling text nodes, and any aria-label / title attributes.

Remediate (in order):

  1. Token pages: emit .ks-swatch-label beside every .ks-swatch-box (see generator/pages/tokens.py and components/layouts.py showcase styles).
  2. Chart legends: match forge-data-charts.js—each .lenses-overview-donut-swatch in a .d-flex row with series name and optional percentage text.
  3. Custom legends: add .ks-swatch-label, visible <span> text, or aria-label on the swatch or row (at least 2 characters).
  4. Tables: put status words, counts, or icons in the cell; use aria-label only when visible text is intentionally abbreviated.
  5. Re-run deterministic color-only checks; pair with DET.CHART.ALT_SUMMARY for mount-level summaries and DET.DATA.TABLE_HEADERS for header scope.