Skip to content

Prevent semantic token collisions and stabilize duplicate contrast rows#13

Merged
Artsen merged 1 commit into
mainfrom
fix/semantic-token-collisions
Jul 26, 2026
Merged

Prevent semantic token collisions and stabilize duplicate contrast rows#13
Artsen merged 1 commit into
mainfrom
fix/semantic-token-collisions

Conversation

@Artsen

@Artsen Artsen commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • Reserve every canonical Tailwind role-* semantic token before allocating base palette tokens.
  • Use one deterministic allocator for CSS and Tailwind base tokens, including reserved names, existing numeric suffixes, ordinary duplicates, Unicode normalization, and unnamed fallbacks.
  • Add array positions to immutable contrast-result React keys so duplicate HEX pairs and issues render independently without warnings.
  • Document the reserved Tailwind semantic namespace and the separate CSS base/semantic namespaces.

Root cause

Base palette tokens were deduplicated only against earlier palette names. They were not checked against generated Tailwind semantic keys, and the count-based allocator could also produce a token already requested by another palette name. Separately, contrast rows used only displayed HEX pairs as React keys even though deterministic backend snapshots can contain distinct entries with identical displayed values.

User impact

A color name such as Role primary action now receives a deterministic collision-safe base token such as role-primary-action-2, while the generated semantic key remains role-primary-action. CSS aliases reference the same final base token used by Tailwind. Duplicate contrast pairs and issues all render, with no synthetic key exposed in the interface.

Contract scope

  • RoleAssignments remain ID-based.
  • Saved-palette schema remains version 3.
  • Portable JSON remains version 3 with unchanged legacy migration behavior.
  • Backend endpoints, accessibility payloads, contrast calculations, thresholds, dependencies, and ports are unchanged.

Validation

  • python check.py: passed
    • frontend: 21 files, 123 tests passed
    • frontend coverage: 92.15% statements/lines, 85.36% branches, 74.53% functions
    • backend: 78 tests passed with 86% coverage
    • Playwright: 6 workflows passed
    • axe: no serious or critical violations in covered representative states
    • Prettier, ESLint with zero warnings, TypeScript, production build, Ruff, and mypy passed
  • Focused exporter, ReviewWorkspace, and AnalysisResults suites: 23 tests passed
  • pnpm review:screenshots: passed; 16 deterministic screenshots generated
  • git diff --check: passed

Remaining limitations

Token suffixes are intentionally order-dependent because palette order defines deterministic allocation. The role-* Tailwind namespace is reserved for ColorCraft-generated semantics and cannot be claimed verbatim by a base palette token.

@Artsen
Artsen marked this pull request as ready for review July 26, 2026 00:11
@Artsen
Artsen merged commit 2be073c into main Jul 26, 2026
1 check passed
@Artsen
Artsen deleted the fix/semantic-token-collisions branch July 26, 2026 02:29
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