DESIGN: derived interaction-state tests assume a per-product CSS var scheme the color.json files explicitly reject #72
Labels
No labels
priority
P1
priority
P2
priority
P3
priority
P4
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Toilville/toilville-design#72
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
While working design issue #51,
python -m pytest generator/tests/surfaced 2 pre-existing failures unrelated to that work:generator/tests/unit/test_derived_states.py::test_bespoke_canonical_palettes_are_coveredgenerator/tests/unit/test_derived_states.py::test_missing_primary_role_skips_primary_statesBoth assert that spelwork/tctd/fux-capacity's derived interaction-state CSS vars (the color-mix() hover/active/focus-glow states added by
extract.py:_derived_state_lines) use product-prefixed names — e.g.--spelwork-color-primary-hover,--tctd-accent-primary-hover,--fux-capacity-accent-secondary-hover.That doesn't match the actual, current
Brand/UDG/products/{spelwork,tctd,fux-capacity}/dtcg/color.jsonfiles:$descriptionfields explicitly say the opposite of what the tests expect: "self-defined on canonical--udg-color-*identities... keeps the standard contract so agnostic surfaces and clients can read/inherit it." Renaming to per-product-prefixed vars would break that documented contract (this repo'scomponents//templates/arevar(--udg-color-*)-only consumers per CLAUDE.md).test_bespoke_canonical_palettes_are_coveredexpects tctd to use anaccent.*color grouping — tctd has noaccent.*path at all, onlybrand.*/bg.*/text.*/border.*/safety.*.test_missing_primary_role_skips_primary_statesasserts "fux-capacity has no primary token" — fux-capacity has a realbrand.primary(#E63535,udg.dart-key: brandPrimary).The code comment directly above
_derived_state_linesinudg/generators/extract.py(around line 99) says: "PROVISIONAL (2026-07-25): the mix ratios, the base-role candidate lists, and the derived-name convention... await ville's design review." So this looks like the tests were written against a planned retokenization (probably the per-product CSS-var namespacing that would matter once multiple WP theme.json style presets — spelwork/tctd/fux-capacity/peterswimm all load undertoilville-fse— coexist in one site and need non-colliding var names) that either never landed or was superseded by the "keep the standard contract" decision recorded in the color.json files themselves.Ask: @ville — which is current intent?
--udg-color-*shared contract is correct as documented in the color.json files → the two tests are stale and should be rewritten to assert the actual (shared-var) behavior, or deleted if the scenario they're pinning no longer applies._common_var_prefix's focus-glow naming only works if every emitted color var for that product shares the prefix, so this would mean renaming every token in these 3 files, which is a bigger change than these two tests alone suggest).Not fixing this in design issue #51 / its PR — that work is a documented space/radius/border override, unrelated to this color-derived-states gap. Left
_derived_state_lines/the color.json files untouched pending this decision.Option 1 — the
--udg-color-*shared contract is correct as documented. The two tests are stale and should be rewritten against actual behaviour.Verified your diagnosis by running the suite and reading the data. All of it holds:
python -m pytest generator/tests/unit/test_derived_states.py→ 2 failed, 6 passed, exactly the two you named.$descriptionfields say "self-defined on canonical--udg-color-*identities … keeps the standard contract so agnostic surfaces and clients can read/inherit it."accent.*grouping at all — onlybrand.*/bg.*/text.*/border.*/safety.*.brand.primary=#E63535,udg.dart-key: brandPrimary. The test's "no primary token" premise is simply false.Two things worth adding, because they close the question rather than leaving it a judgement call.
The WP collision scenario that would motivate prefixing does not exist. I checked
tools/wp_theme_ingestor/build_surfaces.pyand the presets it reads.udg/products/wp-themes/toilville-fse/styles/{fux-capacity,pardner,spelwork,tctd,peterswimm}.jsonare theme.json style variations carrying asettings.color.paletteof slug/colour pairs — they emit--wp--preset--color--<slug>, never--udg-color-*, and WordPress applies exactly one variation at a time. So the five brands never coexist as CSS custom properties in one document, and there is nothing for a product prefix to disambiguate. The hypothesis in option 2 was reasonable but the WP path doesn't work that way._common_var_prefixcannot produce a per-product prefix today, by construction. All four canonical products emit byte-identical derived-state names:spelwork, tctd, fux-capacity and rituals all produce that exact block. The longest common prefix of a set of
--udg-color-*vars is always--udg-color-, whatever the product — so the function can only ever yield a product namespace if the base vars were already product-prefixed. That is your point about scope, arrived at from the other direction: prefixing the derived states is not a small change that happens to touch three roles, it is unreachable without renaming every token in these files first. Which the$descriptionfields rule out.Note this also means the derived states inherit precisely the collision profile the base contract already has — if two canonical products' skins ever did load into one document they would already collide on
--udg-color-brand-primaryitself. That is a scoping problem (:root[data-theme=…], or a wrapper selector), not a naming problem, and it is orthogonal to this issue.Suggested follow-ups when the tests get rewritten:
test_bespoke_canonical_palettes_are_covered— assert the real behaviour: a product withcolor_inherits_baseline: falsestill resolves all three roles from its ownbrand.*/safety.*tokens and emits the canonical names. Worth noting that as written this would then duplicatetest_canonical_grammar_derives_from_canonical_base, so the thing actually worth pinning is the no-baseline-inheritance path, not the naming.test_missing_primary_role_skips_primary_states— the scenario no longer has a fixture. No shipping product lacks a primary role.test_absent_base_token_is_skippedalready covers the skip logic with a synthetic palette and does it better. I'd delete this one rather than re-point it at a product that will acquire a primary the moment someone edits it._common_var_prefixis dead logic for every current product. Either drop it and hardcode the canonical prefix, or leave it with a comment saying it is a hook for a namespacing scheme that was considered and rejected here — otherwise the next reader will assume it namespaces when it cannot.The PROVISIONAL marker stays on the mix ratios. That half of the comment is a separate question from the naming one, and it is not settled — see #75. Short version: one global derivation rule does not carry across brands, so 88/78/90/85/22-toward-
#000is accepted as a placeholder default, not as a house rule. Nothing blocks on it today; the tests in this issue can be rewritten against current behaviour regardless.Wish completed — all requirements satisfied.