Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Think of it as OpenAPI for design systems.

---

> **Status: v0.3 draft available.**
> **Status: v0.4 draft available.**
>
> The current draft is [`spec/dspack-v0.3.md`](./spec/dspack-v0.3.md) (written as a delta over [v0.2](./spec/dspack-v0.2.md)), with a matching [JSON Schema](./schema/dspack.v0.3.schema.json), a companion [surface schema](./schema/dspack.surface.v0_1.schema.json), and a [shadcn/ui reference example](./examples/shadcn-ui.dspack.json). v0.3 adds the machine-checkable **governance blocks** — `intents`, `rules`, `examples` — and is strictly additive: a valid v0.2 document with `"dspack": "0.3"` validates against the v0.3 schema (see the [migration guide](./spec/migration-v0.2-to-v0.3.md)). Earlier specs and schemas ([v0.2](./spec/dspack-v0.2.md), [v0.1](./spec/dspack-v0.1.md)) are preserved for reference. This is a draft — breaking changes may occur before v1.0. Contributions to the design are welcome at any stage.
> The current draft is [`spec/dspack-v0.4.md`](./spec/dspack-v0.4.md) (written as a delta over [v0.3](./spec/dspack-v0.3.md)), with a matching [JSON Schema](./schema/dspack.v0.4.schema.json), a companion [surface schema](./schema/dspack.surface.v0_1.schema.json), and a [shadcn/ui reference example](./examples/shadcn-ui.dspack.json). v0.4 adds **component categories** (a contract-defined registry that rules can select by) and the **`required-props` rule type** ("this component must carry named content directly" — the refinement driven by measured projection-gap failures), and is strictly additive: a valid v0.3 document with `"dspack": "0.4"` validates against the v0.4 schema (see the [migration guide](./spec/migration-v0.3-to-v0.4.md)). Earlier specs and schemas ([v0.3](./spec/dspack-v0.3.md), [v0.2](./spec/dspack-v0.2.md), [v0.1](./spec/dspack-v0.1.md)) are preserved for reference. This is a draft — breaking changes may occur before v1.0. Contributions to the design are welcome at any stage.

---

Expand Down Expand Up @@ -121,6 +121,7 @@ The following milestones represent the current intended direction. They are not
| **shadcn/ui example dspack** | A reference dspack file for the [shadcn/ui](https://ui.shadcn.com) component library — _[available](./examples/shadcn-ui.dspack.json)_ |
| **v0.2 spec draft** | Adds structured generation constraints: lifecycle status, accessibility, composition rules, contextual constraints, variant semantics, token hierarchy, themes, layout primitives, and anti-pattern severity — _[available](./spec/dspack-v0.2.md)_ |
| **v0.3 spec draft** | Adds the machine-checkable governance blocks: named intents, typed deterministic rules with rationales, compilable examples, and the companion dspack surface format — _[available](./spec/dspack-v0.3.md)_ |
| **v0.4 spec draft** | Adds component categories (contract-defined registry, category-based rule selection) and the `required-props` rule type, both driven by measured pipeline evidence — _[available](./spec/dspack-v0.4.md)_ |
| **ds-mcp v0 release** | First release of the reference implementation, validated against the v0.2 spec — _[available](https://github.com/aestheticfunction/ds-mcp)_ |
| **Community RFCs** | Open RFC process for proposing additions and changes to the spec |
| **v1.0 spec stabilization** | First stable, versioned release of the specification; breaking changes require a formal process after this point |
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ Example dspack files live in this directory. Examples are intended to help imple

- [`shadcn-ui.dspack.json`](./shadcn-ui.dspack.json) — a reference dspack file for the [shadcn/ui](https://ui.shadcn.com) component library

The shadcn/ui example demonstrates all five top-level concepts (tokens, components, patterns, anti-patterns, framework bindings) and validates against the [v0.1 JSON Schema](../schema/dspack.v0.1.schema.json).
The shadcn/ui example demonstrates the corpus concepts (tokens, components, patterns, anti-patterns, framework bindings), the v0.3 governance blocks (intents, rules, examples), and the v0.4 additions (component categories, a `required-props` rule, a category-based `forbidden-composition` rule). It validates against the [v0.4 JSON Schema](../schema/dspack.v0.4.schema.json).
81 changes: 63 additions & 18 deletions examples/shadcn-ui.dspack.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"$schema": "../schema/dspack.v0.3.schema.json",
"dspack": "0.3",
"$schema": "../schema/dspack.v0.4.schema.json",
"dspack": "0.4",
"name": "shadcn/ui",
"description": "A collection of reusable components built with Radix UI and Tailwind CSS. Components are copied into your project, not installed as a dependency.",
"version": "2.0.0",
"version": "2.1.0",
"metadata": {
"source": "https://ui.shadcn.com",
"license": "MIT"
Expand Down Expand Up @@ -311,7 +311,8 @@
],
"tokens": ["primary", "primary-foreground", "secondary", "secondary-foreground", "destructive", "destructive-foreground", "border", "ring", "radius"],
"relatedComponents": ["toggle"],
"tags": ["interactive", "form", "action"]
"tags": ["interactive", "form", "action"],
"categories": ["interactive"]
},
"alert-dialog": {
"name": "Alert Dialog",
Expand Down Expand Up @@ -360,7 +361,8 @@
"description": "The element that opens the alert dialog.",
"required": true,
"slot": "trigger",
"acceptsChildren": "components"
"acceptsChildren": "components",
"categories": ["interactive"]
},
{
"id": "alert-dialog-content",
Expand Down Expand Up @@ -403,14 +405,16 @@
"name": "AlertDialogAction",
"description": "The button that confirms the destructive action.",
"required": true,
"acceptsChildren": "text"
"acceptsChildren": "text",
"categories": ["interactive"]
},
{
"id": "alert-dialog-cancel",
"name": "AlertDialogCancel",
"description": "The button that cancels and closes the dialog.",
"required": true,
"acceptsChildren": "text"
"acceptsChildren": "text",
"categories": ["interactive"]
}
],
"notes": "AlertDialogContent must contain AlertDialogTitle and AlertDialogDescription for accessibility. AlertDialogAction and AlertDialogCancel must appear in AlertDialogFooter. Place cancel before confirm in reading order."
Expand All @@ -434,7 +438,8 @@
],
"tokens": ["background", "foreground", "primary", "primary-foreground", "destructive", "destructive-foreground", "border", "radius"],
"relatedComponents": ["dialog", "button"],
"tags": ["modal", "confirmation", "destructive", "accessibility"]
"tags": ["modal", "confirmation", "destructive", "accessibility"],
"categories": ["overlay"]
},
"dialog": {
"name": "Dialog",
Expand Down Expand Up @@ -483,7 +488,8 @@
"name": "DialogTrigger",
"description": "The element that opens the dialog.",
"slot": "trigger",
"acceptsChildren": "components"
"acceptsChildren": "components",
"categories": ["interactive"]
},
{
"id": "dialog-content",
Expand Down Expand Up @@ -524,7 +530,8 @@
"id": "dialog-close",
"name": "DialogClose",
"description": "A button that closes the dialog.",
"acceptsChildren": "text"
"acceptsChildren": "text",
"categories": ["interactive"]
}
]
},
Expand All @@ -542,7 +549,8 @@
],
"tokens": ["background", "foreground", "border", "radius"],
"relatedComponents": ["alert-dialog"],
"tags": ["modal", "overlay", "form"]
"tags": ["modal", "overlay", "form"],
"categories": ["overlay"]
},
"card": {
"name": "Card",
Expand Down Expand Up @@ -659,7 +667,8 @@
],
"tokens": ["background", "foreground", "border", "ring", "radius-sm"],
"relatedComponents": [],
"tags": ["form", "input", "text"]
"tags": ["form", "input", "text"],
"categories": ["interactive"]
},
"badge": {
"name": "Badge",
Expand Down Expand Up @@ -733,7 +742,8 @@
"description": "The element that opens the dropdown menu.",
"required": true,
"slot": "trigger",
"acceptsChildren": "components"
"acceptsChildren": "components",
"categories": ["interactive"]
},
{
"id": "dropdown-menu-content",
Expand All @@ -747,13 +757,15 @@
"id": "dropdown-menu-item",
"name": "DropdownMenuItem",
"description": "A single actionable item in the menu.",
"acceptsChildren": "text"
"acceptsChildren": "text",
"categories": ["interactive"]
},
{
"id": "dropdown-menu-checkbox-item",
"name": "DropdownMenuCheckboxItem",
"description": "A menu item that can be toggled on/off.",
"acceptsChildren": "text"
"acceptsChildren": "text",
"categories": ["interactive"]
},
{
"id": "dropdown-menu-radio-group",
Expand All @@ -765,7 +777,8 @@
"id": "dropdown-menu-radio-item",
"name": "DropdownMenuRadioItem",
"description": "A radio-style menu item for single selection within a group.",
"acceptsChildren": "text"
"acceptsChildren": "text",
"categories": ["interactive"]
},
{
"id": "dropdown-menu-label",
Expand Down Expand Up @@ -795,7 +808,8 @@
"id": "dropdown-menu-sub-trigger",
"name": "DropdownMenuSubTrigger",
"description": "The item that opens a sub-menu on hover or keyboard navigation.",
"acceptsChildren": "text"
"acceptsChildren": "text",
"categories": ["interactive"]
},
{
"id": "dropdown-menu-sub-content",
Expand All @@ -807,7 +821,8 @@
},
"tokens": ["background", "foreground", "accent", "muted", "muted-foreground", "border", "radius"],
"relatedComponents": ["button"],
"tags": ["interactive", "menu", "overlay"]
"tags": ["interactive", "menu", "overlay"],
"categories": ["interactive", "overlay"]
},
"table": {
"name": "Table",
Expand Down Expand Up @@ -931,6 +946,16 @@
"tags": ["data", "table", "tabular", "display"]
}
},
"categories": {
"interactive": {
"name": "Interactive",
"description": "Receives pointer or keyboard activation: buttons, inputs, menu items, and trigger/action/cancel sub-components. Category-based composition rules select by this instead of enumerating ids."
},
"overlay": {
"name": "Overlay",
"description": "Renders content in a layer above the page with its own focus and dismiss semantics: dialogs, alert dialogs, dropdown menus."
}
},
"patterns": [
{
"id": "destructive-action-confirmation",
Expand Down Expand Up @@ -1049,6 +1074,26 @@
"rationale": "Nested interactive elements create ambiguous click targets and are an accessibility violation: screen readers cannot determine intent and click handling varies across browsers.",
"examples": ["ex.delete-account-confirmation"],
"tags": ["accessibility", "interaction"]
},
{
"id": "rule.trigger-carries-label",
"type": "required-props",
"severity": "must",
"component": "button",
"within": "alert-dialog-trigger",
"requiredText": true,
"rationale": "The trigger button must present its label as its own text. Assistive technology reads the control's accessible name from the element itself, and protocol projections lift a trigger label from the label-bearing component's direct text — a label nested in a child (e.g. a badge) or absent entirely produces a control with no accessible name and an instance downstream emitters must refuse.",
"examples": ["ex.delete-account-confirmation"],
"tags": ["accessibility", "projection"]
},
{
"id": "rule.alertdialog-no-nested-overlays",
"type": "forbidden-composition",
"severity": "must",
"component": "alert-dialog",
"forbiddenCategories": ["overlay"],
"rationale": "An alert dialog is a single focused interruption. Stacking another overlay (dialog, dropdown menu, another alert dialog) inside it breaks focus containment and dismiss semantics and buries the confirmation decision under a second layer.",
"tags": ["accessibility", "interaction"]
}
],
"examples": [
Expand Down
61 changes: 61 additions & 0 deletions fixtures/negative/component-unknown-category-ref.dspack.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"dspack": "0.4",
"name": "negative-fixture",
"categories": {
"interactive": {
"description": "Receives activation."
}
},
"components": {
"button": {
"name": "Button",
"description": "A button.",
"categories": [
"interactive",
"clickable"
],
"props": {
"variant": {
"type": "enum",
"values": [
"default",
"destructive"
]
}
}
},
"alert-dialog": {
"name": "AlertDialog",
"description": "A confirmation dialog.",
"composition": {
"subComponents": [
{
"id": "alert-dialog-trigger",
"name": "AlertDialogTrigger",
"categories": [
"interactive"
]
},
{
"id": "alert-dialog-title",
"name": "AlertDialogTitle"
},
{
"id": "alert-dialog-cancel",
"name": "AlertDialogCancel",
"categories": [
"interactive"
]
}
]
}
}
},
"intents": [
{
"id": "destructive-action",
"description": "Irreversible operations."
}
],
"_comment": "CONSISTENCY: component/sub-component categories must be registered in the top-level categories registry."
}
69 changes: 69 additions & 0 deletions fixtures/negative/rule-required-props-empty.dspack.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"dspack": "0.4",
"name": "negative-fixture",
"categories": {
"interactive": {
"description": "Receives activation."
}
},
"components": {
"button": {
"name": "Button",
"description": "A button.",
"categories": [
"interactive"
],
"props": {
"variant": {
"type": "enum",
"values": [
"default",
"destructive"
]
}
}
},
"alert-dialog": {
"name": "AlertDialog",
"description": "A confirmation dialog.",
"composition": {
"subComponents": [
{
"id": "alert-dialog-trigger",
"name": "AlertDialogTrigger",
"categories": [
"interactive"
]
},
{
"id": "alert-dialog-title",
"name": "AlertDialogTitle"
},
{
"id": "alert-dialog-cancel",
"name": "AlertDialogCancel",
"categories": [
"interactive"
]
}
]
}
}
},
"intents": [
{
"id": "destructive-action",
"description": "Irreversible operations."
}
],
"_comment": "SCHEMA: a required-props rule must carry at least one of requiredText / requiredProps.",
"rules": [
{
"id": "rule.fixture",
"type": "required-props",
"severity": "must",
"rationale": "Fixture rationale.",
"component": "button"
}
]
}
Loading
Loading