Component Catalogue

LLM-first developer tool for generating Component Catalogues

View the Project on GitHub adieyal/component-catalogue

Scenarios

Scenarios define specific configurations of a component for documentation, testing, and the catalogue UI. Each component must have at least one scenario.

Scenario Structure

{
  "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"
}

Fields

Required Fields

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

Optional Fields

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

RenderNode Structure

The render field uses a declarative structure to define DOM elements:

{
  "element": "tag-name",
  "attrs": { },
  "props": { },
  "slots": { },
  "children": [ ],
  "text": "string"
}

element

The HTML tag name or custom element name.

{ "element": "my-button" }
{ "element": "div" }

attrs

HTML attributes set via setAttribute(). Boolean attributes use true/false.

{
  "attrs": {
    "variant": "primary",
    "size": "large",
    "disabled": true,
    "aria-label": "Submit form"
  }
}

props

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
    }
  }
}

slots

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:

children

Child elements (not using slots).

{
  "element": "div",
  "children": [
    { "element": "span", "text": "Child 1" },
    { "element": "span", "text": "Child 2" }
  ]
}

text

Text content for the element.

{ "element": "p", "text": "Hello world" }

Examples

Simple Component

{
  "id": "badge-default",
  "title": "Default Badge",
  "componentId": "badge",
  "primary": true,
  "render": {
    "element": "my-badge",
    "attrs": { "variant": "info" },
    "slots": { "default": "New" }
  }
}

Component with Multiple Slots

{
  "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" }
        }
      ]
    }
  }
}

Component with Complex Props

{
  "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" }
      ]
    }
  }
}

Wrapper for Layout Testing

{
  "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" }
      }
    ]
  }
}

File Organization

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

Best Practices

  1. One Primary Scenario - Mark exactly one scenario as primary: true
  2. Descriptive IDs - Use component-variant naming (e.g., button-primary)
  3. Cover States - Create scenarios for all important states
  4. Test Edge Cases - Long text, empty content, error states
  5. Group Related - Use multiple elements in one scenario for comparisons

Next Steps