diff --git a/frontend/src/views/DesignSystemsView.vue b/frontend/src/views/DesignSystemsView.vue index 828fb16..a11b09f 100644 --- a/frontend/src/views/DesignSystemsView.vue +++ b/frontend/src/views/DesignSystemsView.vue @@ -530,7 +530,7 @@ function isColourish(value: string): boolean {
@@ -581,7 +581,7 @@ function isColourish(value: string): boolean {
@@ -852,7 +852,7 @@ function isColourish(value: string): boolean {
@@ -864,7 +864,7 @@ function isColourish(value: string): boolean {
@@ -873,7 +873,7 @@ function isColourish(value: string): boolean {

Distinct from purpose: purpose is what the token is FOR, this is diff --git a/src/scribe/mcp/tools/design_systems.py b/src/scribe/mcp/tools/design_systems.py index 7043a34..c8af5db 100644 --- a/src/scribe/mcp/tools/design_systems.py +++ b/src/scribe/mcp/tools/design_systems.py @@ -33,7 +33,8 @@ async def create_design_system( """Create a design system, optionally inheriting from another. Args: - title: What this system is, e.g. "FabledSword" or "Scribe" (required). + title: What this system is — a house style, or one app within it + (required). description: What it covers and when it applies. guidance: The narrative a token table cannot hold — aesthetic, voice and tone, what is deliberately out of scope. Markdown, free-form. @@ -226,18 +227,22 @@ async def create_design_token( Args: design_system_id: The system that owns this token. - name: The custom-property name, e.g. "--fs-obsidian" (required). + name: The custom-property name, e.g. "--surface-page" (required). + Name it for its PURPOSE, not its value: a name like "--obsidian" + or "--button-bg" stops being true the moment the value or the + element changes. value_by_mode: Values keyed by mode, e.g. - {"base": "#f7f5ef", "dark": "#14171a"}. Use "base" for the value + {"base": "#14171a", "light": "#f7f5ef"}. Use "base" for the value that applies when no mode is more specific; a token that is not mode-dependent needs only "base". In a system WITH a parent, an omitted mode is inherited rather than blanked. group_name: Free-text grouping — "surface", "text", "radius", whatever this system's own vocabulary is. - purpose: What the token is for, e.g. "page bg, deepest surface". + purpose: What the token is for, e.g. "page background, deepest + surface". rationale: WHY it is this value — a different question from purpose. - "Success equals Moss, aligned by design" is a rationale; "page bg, - deepest surface" is a purpose. + "Deliberately the same value as the primary action colour" is a + rationale; "page background, deepest surface" is a purpose. supersedes: Literal values this token should be used INSTEAD OF, e.g. ["#fff", "#ffffff"]. This is how a design system records what a prohibition was trying to say — not "white is banned" but "write diff --git a/src/scribe/mcp/tools/rulebooks.py b/src/scribe/mcp/tools/rulebooks.py index ccb9979..55ff7dd 100644 --- a/src/scribe/mcp/tools/rulebooks.py +++ b/src/scribe/mcp/tools/rulebooks.py @@ -45,7 +45,8 @@ async def create_rulebook(title: str, description: str = "") -> dict: Two ways a rulebook reaches projects, set by its always_on flag (toggle via update_rulebook): - always_on = true -> binds EVERY one of your projects automatically. - Use for universal cross-project norms (e.g. "FabledSword family"). + Use for universal cross-project norms that apply across every + project, not just one. - always_on = false -> binds only projects that subscribe (subscribe_project_to_rulebook). Use for a THEMED body of rules a category of projects shares (e.g. a design system that visual apps @@ -54,7 +55,7 @@ async def create_rulebook(title: str, description: str = "") -> dict: to any single project. Project-specific rules go in create_project_rule. Args: - title: Rulebook name (e.g. "FabledSword family"). + title: Rulebook name. description: Optional short description of what this rulebook covers. """ uid = current_user_id() diff --git a/src/scribe/models/design_system.py b/src/scribe/models/design_system.py index 8aa2c6e..d2d8570 100644 --- a/src/scribe/models/design_system.py +++ b/src/scribe/models/design_system.py @@ -116,22 +116,24 @@ class DesignToken(Base, TimestampMixin, SoftDeleteMixin): group_name: Mapped[str | None] = mapped_column(Text, nullable=True) purpose: Mapped[str | None] = mapped_column(Text, nullable=True) # WHY this token is this value — a different question from `purpose`, which - # is 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. + # is what it is FOR. "Deliberately the same value as the primary action + # colour" is a rationale; "page background, deepest surface" is a purpose. + # Design guidance carries the first routinely and a token row had nowhere to + # put it. rationale: Mapped[str | None] = mapped_column(Text, nullable=True) # Literal values this token should be used INSTEAD OF, e.g. ["#fff", # "#ffffff"] on a text-on-action token. # # This is how a design system records the thing a prohibition was trying to - # say. "Pure white is never text" is the shadow of a positive fact — text is - # Parchment — and a system that stores what things ARE has no row for a ban. + # say. "Pure white is never text" is the shadow of a positive fact — some + # other colour IS the text colour — and a system that stores what things ARE + # has no row for a ban. # Recording the replacement keeps the check and makes it actionable: a # finding can name what to write instead of merely objecting. # # It has to be DECLARED rather than inferred, because the superseded literal - # and the token's own value are usually different colours (#fff is not - # #E8E4D8). No value-matching rule could ever connect them. + # and the token's own value are usually different colours entirely. No + # value-matching rule could ever connect them. # # Consumed by the source lint (#2277), not by the drift panel: these # literals live in component CSS, which the panel cannot see and says so. diff --git a/src/scribe/services/design_stylesheet.py b/src/scribe/services/design_stylesheet.py index 1a2ef27..0c61c9f 100644 --- a/src/scribe/services/design_stylesheet.py +++ b/src/scribe/services/design_stylesheet.py @@ -18,8 +18,8 @@ has recall, locations, drift checks and merge. So the division is: this sheet says what the values MEAN; snippets say what things LOOK LIKE, in terms of those values. A token named after an element -(`--fs-button-bg`) is the smell that the two have been mixed — it multiplies -with every new element, where a purpose name (`--fs-action-primary`) is reused. +(`--button-bg`) is the smell that the two have been mixed — it multiplies +with every new element, where a purpose name (`--action-primary`) is reused. SAFETY ------ @@ -203,7 +203,8 @@ def duplicate_values(tokens: Sequence) -> dict[str, list[str]]: Two names for one value are either a deliberate alias or the same idea recorded twice — the token-level form of the duplicated-definition shape. Reported rather than refused: a design system legitimately aligns colours on - purpose ("Success = Moss, by design"), and a generator that rejected that + purpose (one palette entry defined as equal to another), and a generator + that rejected that would be wrong about the operator's intent. Compares the BASE value only. Two tokens agreeing in one mode and diverging