LLM-first developer tool for generating Component Catalogues
Scenarios define specific configurations of a component for documentation, testing, and the catalogue UI. Each component must have at least one scenario.
{
"id": "button-primary",
"title": "Primary Button",
"description": "The main call-to-action button style",
"componentId": "button",
"primary": true,
"tags": ["cta", "action"],
"render": {
"element": "my-button",
"attrs": {
"variant": "primary",
"size": "medium"
},
"slots": {
"default": "Click me"
}
},
"viewport": {
"width": 400,
"height": 200
},
"background": "#f5f5f5"
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique kebab-case identifier |
title |
string | Human-readable name |
componentId |
string | Parent component ID |
render |
object | RenderNode defining the DOM structure |
| Field | Type | Description |
|---|---|---|
description |
string | Explanation of the scenario |
primary |
boolean | Mark as primary scenario (shown first) |
tags |
string[] | Searchable tags |
viewport |
object | Default viewport size for screenshots |
background |
string | Background color override |
The render field uses a declarative structure to define DOM elements:
{
"element": "tag-name",
"attrs": { },
"props": { },
"slots": { },
"children": [ ],
"text": "string"
}
The HTML tag name or custom element name.
{ "element": "my-button" }
{ "element": "div" }
HTML attributes set via setAttribute(). Boolean attributes use true/false.
{
"attrs": {
"variant": "primary",
"size": "large",
"disabled": true,
"aria-label": "Submit form"
}
}
JavaScript properties set directly on the element. Use for objects/arrays.
{
"props": {
"items": [
{ "id": 1, "name": "Item 1" },
{ "id": 2, "name": "Item 2" }
],
"config": {
"sortable": true,
"paginated": false
}
}
}
Named slot content. Use "default" for the default slot.
{
"slots": {
"default": "Main content",
"header": "Header text",
"footer": {
"element": "span",
"text": "Footer content"
}
}
}
Slot content can be:
Child elements (not using slots).
{
"element": "div",
"children": [
{ "element": "span", "text": "Child 1" },
{ "element": "span", "text": "Child 2" }
]
}
Text content for the element.
{ "element": "p", "text": "Hello world" }
{
"id": "badge-default",
"title": "Default Badge",
"componentId": "badge",
"primary": true,
"render": {
"element": "my-badge",
"attrs": { "variant": "info" },
"slots": { "default": "New" }
}
}
{
"id": "card-full",
"title": "Card with All Slots",
"componentId": "card",
"render": {
"element": "my-card",
"slots": {
"header": {
"element": "h3",
"text": "Card Title"
},
"default": {
"element": "p",
"text": "Card content goes here."
},
"footer": [
{
"element": "my-button",
"attrs": { "variant": "secondary" },
"slots": { "default": "Cancel" }
},
{
"element": "my-button",
"attrs": { "variant": "primary" },
"slots": { "default": "Save" }
}
]
}
}
}
{
"id": "data-table-populated",
"title": "Table with Data",
"componentId": "data-table",
"render": {
"element": "my-data-table",
"attrs": {
"sortable": true,
"striped": true
},
"props": {
"columns": [
{ "key": "name", "label": "Name" },
{ "key": "email", "label": "Email" }
],
"rows": [
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@example.com" }
]
}
}
}
{
"id": "button-group",
"title": "Button Group",
"componentId": "button",
"render": {
"element": "div",
"attrs": {
"style": "display: flex; gap: 8px;"
},
"children": [
{
"element": "my-button",
"attrs": { "variant": "primary" },
"slots": { "default": "Save" }
},
{
"element": "my-button",
"attrs": { "variant": "secondary" },
"slots": { "default": "Cancel" }
}
]
}
}
Scenarios are stored in the component’s scenarios/ directory:
registry/components/button/
├── component.json
├── docs.md
└── scenarios/
├── default.json # Primary scenario
├── variants.json # All variants together
├── sizes.json # Size variations
├── disabled.json # Disabled state
└── loading.json # Loading state
primary: truecomponent-variant naming (e.g., button-primary)