Files
FabledScribe/tests/test_design_stylesheet.py
T
bvandeusen 0f80b790c7
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
feat(design-systems): central prose — guidance on the system, rationale on tokens
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.
2026-07-30 21:52:16 -04:00

343 lines
14 KiB
Python

"""Rendering a design system as its master CSS sheet.
The generator is pure, so a whole sheet is one literal of tokens in and a string
out. Two things here are load-bearing rather than cosmetic: what the sheet
deliberately does NOT contain, and the fact that a value cannot escape its
declaration.
"""
from types import SimpleNamespace
from scribe.services.design_stylesheet import (
check_code_against_tokens,
duplicate_values,
is_valid_token_name,
render_stylesheet,
safe_comment,
safe_value,
selector_for_mode,
)
def _token(name, value_by_mode, group_name=None, purpose=None):
return SimpleNamespace(
name=name, value_by_mode=value_by_mode,
group_name=group_name, purpose=purpose,
)
# --- safety -----------------------------------------------------------------
#
# Design systems are shareable records. A value that can close its declaration
# can inject arbitrary CSS into the page of anyone the system was shared with,
# which makes this a real boundary rather than tidiness.
def test_a_value_that_would_escape_its_declaration_is_refused():
"""`red; } body { display: none` is the whole attack: end the declaration,
close the block, open your own."""
assert safe_value("red; } body { display: none") is None
assert safe_value("#fff}") is None
assert safe_value("#fff;") is None
def test_at_rules_and_tags_are_refused():
assert safe_value("@import url(evil.css)") is None
assert safe_value("</style><script>") is None
def test_comment_delimiters_in_a_value_are_refused():
"""A value is not rendered inside a comment, but `/*` would comment out
every declaration after it — silently blanking the rest of the sheet."""
assert safe_value("red /* ") is None
assert safe_value("*/ red") is None
def test_a_newline_in_a_value_is_refused():
assert safe_value("red\n color: blue") is None
def test_ordinary_values_survive_untouched():
for value in ("#14171a", "8px", "cubic-bezier(0.2, 0.6, 0.2, 1)",
"0 4px 12px rgba(0,0,0,0.35)", "color-mix(in srgb, red 15%, transparent)"):
assert safe_value(value) == value
def test_a_rejected_value_is_dropped_not_cleaned_up():
"""Rejecting beats stripping. A partially-sanitised value is one the operator
never wrote, and the sheet's entire claim is that it IS the record — quietly
rendering a different colour would break that claim invisibly."""
css = render_stylesheet([_token("--fs-x", {"base": "red; } body { color: blue"})])
assert "body" not in css
assert "value rejected" in css
def test_comment_text_cannot_close_its_comment():
"""`purpose` is operator prose rendered into a comment — `*/` in it would
end the comment and spill the rest into the stylesheet as code."""
assert "*/" not in safe_comment("ends the comment */ then color: red")
def test_a_malformed_token_name_is_dropped():
"""A name is an identifier. A 'cleaned up' identifier is a different token
than the one recorded, so it is dropped rather than repaired."""
assert is_valid_token_name("--fs-obsidian")
assert not is_valid_token_name("--fs obsidian")
assert not is_valid_token_name("color: red")
assert not is_valid_token_name("fs-obsidian") # no leading --
css = render_stylesheet([_token("--bad name", {"base": "red"})])
assert "bad name" not in css
def test_a_mode_name_cannot_break_out_of_its_selector():
assert selector_for_mode('dark"] body {') == '[data-theme="darkbody"]'
# --- what the sheet is ------------------------------------------------------
def test_the_sheet_declares_properties_and_styles_no_elements():
"""THE shape decision. A sheet that styled elements would restate the same
handful of values once per element and grow with the UI. Purpose tokens are
stated once and reused; components are snippets that reference them."""
css = render_stylesheet([
_token("--fs-obsidian", {"base": "#14171a"}, group_name="surface"),
_token("--fs-moss", {"base": "#4a5d3f"}, group_name="action"),
])
assert "--fs-obsidian: #14171a;" in css
# No element or class rules — the sheet has exactly one block here, and
# every declaration in it is a custom property.
assert css.count("{") == 1
declarations = [
line.strip() for line in css.splitlines()
if ":" in line and line.strip().endswith(";")
]
assert declarations and all(d.startswith("--") for d in declarations)
def test_the_header_says_what_the_sheet_is_for():
"""A generated file with no explanation gets hand-edited, and then it has
diverged from the record it claims to be."""
css = render_stylesheet([_token("--fs-x", {"base": "1px"})], title="FabledSword")
assert "FabledSword" in css
assert "Generated" in css
assert "snippets" in css
# --- modes ------------------------------------------------------------------
def test_base_goes_on_the_root_selector_and_other_modes_layer_over_it():
"""Matches the convention already in the codebase, and the one-way scoping
#251 recorded: light on `:root`, dark layered on an attribute selector."""
css = render_stylesheet([
_token("--fs-bg", {"base": "#f5f1e8", "dark": "#14171a"}),
])
assert ":root {" in css
assert '[data-theme="dark"] {' in css
assert css.index(":root {") < css.index('[data-theme="dark"] {')
def test_a_mode_block_contains_only_what_that_mode_declares():
"""A mode block is an OVERRIDE layer, exactly as the storage model has it.
Repeating every token in every block would make the sheet claim each mode
redefines the whole system."""
css = render_stylesheet([
_token("--fs-bg", {"base": "#f5f1e8", "dark": "#14171a"}),
_token("--fs-radius-md", {"base": "8px"}),
])
dark_block = css.split('[data-theme="dark"] {')[1]
assert "--fs-bg" in dark_block
assert "--fs-radius-md" not in dark_block
def test_the_root_selector_is_caller_chosen():
"""A container-scoped preview cannot use `:root`. A generator that hardcoded
it could not serve the preview surface at all."""
css = render_stylesheet(
[_token("--fs-x", {"base": "1px"})], root_selector="[data-preview]"
)
assert "[data-preview] {" in css
assert ":root {" not in css
# --- grouping and honesty ---------------------------------------------------
def test_tokens_are_grouped_by_purpose_with_the_group_named():
css = render_stylesheet([
_token("--fs-obsidian", {"base": "#14171a"}, group_name="surface"),
_token("--fs-radius-md", {"base": "8px"}, group_name="radius"),
])
assert "/* surface */" in css
assert "/* radius */" in css
def test_a_purpose_becomes_an_inline_comment_on_the_base_layer_only():
"""Repeating the same prose in every mode block is noise: the token means
the same thing in dark mode."""
css = render_stylesheet([
_token("--fs-obsidian", {"base": "#14171a", "dark": "#000000"},
purpose="page bg, deepest surface"),
])
assert css.count("page bg, deepest surface") == 1
def test_a_declared_token_with_no_value_appears_as_a_comment_not_a_silence():
"""The rulebook named it, so its absence is a FINDING. A commented line puts
that finding where the reader is already looking; dropping it would make the
sheet look complete."""
css = render_stylesheet([
_token("--fs-obsidian", {"base": "#14171a"}),
_token("--fs-radius-sm", {}),
])
assert "--fs-radius-sm" in css
assert "no value set yet" in css
# Commented, so it cannot be mistaken for a live declaration.
assert " --fs-radius-sm:" not in css
def test_an_empty_system_renders_a_sheet_with_no_blocks_rather_than_failing():
"""A system with no tokens is an ordinary state (rule #115), including one
that was just created."""
css = render_stylesheet([])
assert "{" not in css
assert "Generated" in css
# --- the reuse report -------------------------------------------------------
def test_two_tokens_sharing_a_value_are_reported_not_refused():
"""The operator's constraint, made checkable: reuse consistent values rather
than restating them. But a design system legitimately aligns colours on
purpose — "Success = Moss, by design" — so this reports and lets a human
decide which it is."""
dupes = duplicate_values([
_token("--fs-moss", {"base": "#4A5D3F"}),
_token("--fs-success", {"base": "#4a5d3f"}),
_token("--fs-obsidian", {"base": "#14171a"}),
])
assert dupes == {"#4a5d3f": ["--fs-moss", "--fs-success"]}
def test_tokens_that_agree_in_one_mode_but_differ_in_another_are_not_duplicates():
"""A near-miss is a different, weaker finding, and calling it a duplicate
would send someone to merge two tokens that genuinely diverge."""
assert duplicate_values([
_token("--fs-a", {"base": "#fff", "dark": "#000"}),
_token("--fs-b", {"base": "#fff", "dark": "#111"}),
]) == {"#fff": ["--fs-a", "--fs-b"]}
def test_valueless_tokens_never_count_as_duplicates_of_each_other():
"""Otherwise every unfilled token would collide with every other one and the
report would be nothing but noise on a fresh import."""
assert duplicate_values([_token("--fs-a", {}), _token("--fs-b", {})]) == {}
# --- reading the sheet from the other side ----------------------------------
#
# "The snippets use the tags from the sheet" made checkable. All three findings
# below are currently SILENT in this codebase — no error, no failing test.
def _tok(name, base=None, supersedes=()):
return SimpleNamespace(
name=name,
value_by_mode={"base": base} if base else {},
supersedes=list(supersedes),
group_name=None, purpose=None,
)
SHEET = [
_tok("--fs-obsidian", "#14171a"),
_tok("--fs-parchment", "#e8e4d8", supersedes=["#fff", "#ffffff"]),
]
def test_a_reference_to_a_token_that_does_not_exist_is_reported():
"""THE bug this exists for. `var(--color-accent)` where no such token exists
renders as nothing at all — invisible focus rings, unstyled elements, no
error and no failing test. It happened in this codebase this session."""
report = check_code_against_tokens("outline: 2px solid var(--color-accent);", SHEET)
assert report["unknown"] == ["--color-accent"]
assert report["used"] == []
def test_a_reference_that_resolves_is_reported_as_used_not_as_a_finding():
report = check_code_against_tokens("background: var(--fs-obsidian);", SHEET)
assert report["used"] == ["--fs-obsidian"]
assert report["unknown"] == []
def test_a_superseded_literal_is_found_and_names_its_replacement():
"""The reframe paying off end to end: the finding says what to WRITE, not
merely what is wrong. That is only possible because the token declared it."""
report = check_code_against_tokens("color: #fff;", SHEET)
assert report["superseded_literals"] == [
{"literal": "#fff", "use_instead": "--fs-parchment"}
]
def test_a_shorter_hex_does_not_match_inside_a_longer_one():
"""`#fff` must not fire on `#ffffff` — different colours, and a finding on
the wrong one sends someone to change code that was already correct."""
report = check_code_against_tokens("color: #ffffffee;", SHEET)
assert [f["literal"] for f in report["superseded_literals"]] == []
def test_the_literal_match_is_case_insensitive():
"""Rulebooks write `#FFFFFF` and code writes `#ffffff`. A case-sensitive
check would silently find nothing — the same trap normalize_hex exists for."""
report = check_code_against_tokens("color: #FFFFFF;", SHEET)
assert report["superseded_literals"] == [
{"literal": "#ffffff", "use_instead": "--fs-parchment"}
]
def test_a_token_defined_and_read_locally_is_reported_once_not_twice():
"""A snippet minting its own custom property is the bloat a shared sheet
exists to prevent — but when it also reads it back, that is ONE fact. The
unknown-reference check owns it, because "--btn-bg does not exist in the
sheet" is the more precise statement of the same problem."""
report = check_code_against_tokens(".btn { --btn-bg: #333; background: var(--btn-bg); }", SHEET)
assert report["unknown"] == ["--btn-bg"]
assert report["local_definitions"] == []
def test_a_local_definition_never_read_back_is_still_reported():
report = check_code_against_tokens(".btn { --btn-bg: #333; }", SHEET)
assert report["local_definitions"] == ["--btn-bg"]
def test_a_declaration_that_is_not_a_custom_property_is_not_mistaken_for_one():
report = check_code_against_tokens(".btn { color: red; background: blue; }", SHEET)
assert report["local_definitions"] == []
def test_clean_code_reports_nothing_to_act_on():
report = check_code_against_tokens(
".btn { background: var(--fs-obsidian); color: var(--fs-parchment); }", SHEET
)
assert report["unknown"] == []
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