feat(design-systems): central prose — guidance on the system, rationale on tokens
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 19s
CI & Build / TypeScript typecheck (push) Failing after 20s
CI & Build / Python tests (push) Successful in 44s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 19s
CI & Build / TypeScript typecheck (push) Failing after 20s
CI & Build / Python tests (push) Successful in 44s
CI & Build / Build & push image (push) Skipped
Last piece of the architecture in #2296. The operator: "the prose doesn't have to live as one offs, there's a central system for managing it." Two fields, both free-form: design_systems.guidance the narrative a token table cannot hold — aesthetic, voice and tone, what is deliberately out of scope. design_tokens.rationale WHY a token is this value, which is a different question from `purpose` (what it is FOR). "Success equals Moss, aligned by design" is a rationale; "page bg, deepest surface" is a purpose. Rules carry the first routinely and a token row had nowhere to put it. Free-form rather than a column per category, deliberately. A schema with `voice`, `aesthetic` and `scope` columns would bake one rulebook's table of contents into every install (rule #115), leaving the next install three empty columns and nowhere for what it actually cares about. Both nullable: a design system with no prose at all is complete, not a draft. `rationale` cascades like `purpose` — deepest non-empty wins — so an app overriding a colour keeps the family's reasoning rather than blanking it. Same argument as `supersedes`: the override was about the value, not the meaning. In the generated sheet the inline comment prefers `purpose` and falls back to `rationale`, so a token carrying only the why still says something instead of rendering bare.
This commit is contained in:
@@ -319,3 +319,24 @@ def test_clean_code_reports_nothing_to_act_on():
|
||||
assert report["superseded_literals"] == []
|
||||
assert report["local_definitions"] == []
|
||||
assert report["used"] == ["--fs-obsidian", "--fs-parchment"]
|
||||
|
||||
|
||||
def test_the_inline_comment_falls_back_to_rationale_when_there_is_no_purpose():
|
||||
"""A token carrying only the why still says something in the sheet, rather
|
||||
than rendering bare because the other field happened to be empty."""
|
||||
token = SimpleNamespace(
|
||||
name="--fs-success", value_by_mode={"base": "#4a5d3f"},
|
||||
group_name=None, purpose=None, rationale="equals Moss, by design",
|
||||
)
|
||||
assert "equals Moss, by design" in render_stylesheet([token])
|
||||
|
||||
|
||||
def test_purpose_wins_over_rationale_in_the_comment():
|
||||
"""What a token is FOR is what a reader of the stylesheet needs first."""
|
||||
token = SimpleNamespace(
|
||||
name="--fs-x", value_by_mode={"base": "1px"},
|
||||
group_name=None, purpose="hairline borders", rationale="because thin",
|
||||
)
|
||||
css = render_stylesheet([token])
|
||||
assert "hairline borders" in css
|
||||
assert "because thin" not in css
|
||||
|
||||
Reference in New Issue
Block a user