Annotations
StyleScribe uses JSDoc-style annotations in CSS/SCSS comments to generate component documentation.
Basic Syntax
Annotations start with @ and appear in CSS block comments:
/**
* @title Button
* @description A clickable button element
* @group Actions
*/
.btn {
/* styles */
}
Required Annotations
Every component needs at minimum:
| Annotation | Description |
|---|---|
@title |
Display name in documentation |
@description |
What the component does |
@group |
Category for navigation |
All Annotations
Metadata
| Annotation | Type | Description |
|---|---|---|
@title |
string | Display name |
@description |
string (multiline) | Detailed description |
@navtitle |
string | Short name for navigation |
@group |
string | Category grouping |
@order |
number | Sort order within group |
@verified |
boolean | Production-ready flag |
@draft |
boolean | Work-in-progress flag |
Semantic
| Annotation | Type | Description |
|---|---|---|
@role |
string | ARIA role for examples |
@maintag |
string | Primary HTML tag (default: div) |
@classname |
string | Override auto-detected class |
Component Structure
| Annotation | Type | Description |
|---|---|---|
@variations |
array | Modifier classes |
@additional_variations |
array | Extra modifiers |
@elements |
array | BEM child elements |
@dependencies |
array | Required components |
@examples |
array | Code examples |
Annotation Formats
Simple Values
@title Button
@description A clickable button
@group Actions
@order 1
@verified true
Comma-Separated Arrays
Keys ending in 's' are parsed as arrays:
@variations primary, secondary, danger, ghost
@elements icon, label, content
@dependencies icon, spinner
YAML Arrays
For more detail, use YAML format:
@variations
- name: primary
description: Main call-to-action
- name: secondary
description: Secondary actions
- name: danger
description: Destructive actions
Elements with Custom HTML
Why use html: or pug:? By default, elements render as generic placeholders like <div class="...">Icon</div> in the "All Variations" preview section. This looks broken for components with icons. Use html: to define proper markup.
The {{class}} placeholder is replaced with the full BEM class (e.g., ds-btn__icon becomes sol-btn__icon after prefix transformation).
Using the Icon Component (Recommended):
If you have an icon component, use it in your element HTML:
@elements
- name: icon
description: Button icon
html: <span class="{{class}} ds-icon ds-icon--add"></span>
- name: label
description: Button text
html: <span class="{{class}}">Button</span>
This approach:
- Uses your existing icon component (
ds-icon) - Inherits color via
currentColor - Scales with text using
emunits - Works with all your defined icon tokens
Inline SVG (Alternative):
@elements
- name: icon
description: SVG icon
html: <svg class="{{class}}" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
- name: label
description: Button text
html: <span class="{{class}}">Button</span>
Pug syntax for cleaner templates:
@elements
- name: icon
pug: span.{{class}}.ds-icon.ds-icon--add
- name: label
pug: span.{{class}} Button
Tip: See the Icons Guide for how to add icon tokens to your design system.
How Variation Previews Work
Stylescribe uses the first @example as a template for all variation previews. This preserves proper HTML nesting for complex components.
How it works:
- Takes the HTML from your first
@example - Removes any existing variation classes (e.g.,
--open,--active) - Adds the new variation class (e.g.,
--primary,--scrollable) - If no examples exist, falls back to flat element generation
Example: Given this first example:
<div class="dropdown">
<button class="dropdown__trigger">Open</button>
<div class="dropdown__menu">
<button class="dropdown__item">Edit</button>
<button class="dropdown__item">Delete</button>
</div>
</div>
The "scrollable" variation preview becomes:
<div class="dropdown dropdown--scrollable">
<button class="dropdown__trigger">Open</button>
<div class="dropdown__menu">
<button class="dropdown__item">Edit</button>
<button class="dropdown__item">Delete</button>
</div>
</div>
Why First Example Matters (Critical for Nested Components)
Simple components (button, badge, input) work with flat structures. But nested components (dropdown, modal, tabs, accordion) require proper HTML hierarchy.
Problem without examples: Elements render as flat siblings:
<!-- BROKEN: Menu items outside the menu! -->
<div class="dropdown dropdown--scrollable">
<button class="dropdown__trigger">Open</button>
<div class="dropdown__menu"></div> <!-- Empty! -->
<button class="dropdown__item">Edit</button> <!-- Should be inside menu -->
</div>
Solution: Structure your first @example correctly:
@examples
- title: Basic Menu
code: |
<div class="dropdown">
<button class="dropdown__trigger">Open</button>
<div class="dropdown__menu">
<button class="dropdown__item">Edit</button>
<button class="dropdown__item">Delete</button>
</div>
</div>
Best practices for nested components:
- First example MUST show complete nested HTML structure
- Include all key child elements in proper hierarchy
- Additional examples can show partial structures or specific states
Elements with Custom HTML (Fallback)
When no @examples exist, Stylescribe falls back to flat element generation using @elements. Use html: or pug: to control how elements render:
Without html::
<button class="sol-btn sol-btn--primary">
<div class="sol-btn__icon">Icon</div> <!-- Generic fallback -->
<div class="sol-btn__label">Label</div> <!-- Generic fallback -->
</button>
With html::
<button class="sol-btn sol-btn--primary">
<svg class="sol-btn__icon" ...>...</svg> <!-- Proper SVG -->
<span class="sol-btn__label">Button</span> <!-- Proper span -->
</button>
Note: For simple flat components,
@elementswithhtml:works well. For nested components, always provide a properly structured first@example.
Examples Annotation
The @examples annotation is critical for:
- Live previews in the documentation
- Interactive playground for testing variations
- Variation previews (first example is used as template)
Important: For nested components, structure your first example with complete HTML hierarchy. See "Why First Example Matters" above.
Basic Format
@examples
- title: Default Button
code: |
<button class="btn">Click me</button>
With Description
@examples
- title: Primary Button
description: Use for main call-to-action
code: |
<button class="btn btn--primary">Submit</button>
Multiple Examples
@examples
- title: Sizes
code: |
<button class="btn btn--sm">Small</button>
<button class="btn">Default</button>
<button class="btn btn--lg">Large</button>
- title: Variants
code: |
<button class="btn btn--primary">Primary</button>
<button class="btn btn--secondary">Secondary</button>
Full-Width Examples
@examples
- title: Card Grid
fullWidth: true
code: |
<div class="grid">
<div class="card">One</div>
<div class="card">Two</div>
</div>
Complete Example
/**
* @title Alert
* @navtitle Alert
* @description Contextual feedback messages for user actions
* @group Communication
* @order 2
* @verified true
*
* @role alert
* @maintag div
*
* @variations
* - name: info
* description: Informational messages
* - name: success
* description: Success confirmations
* - name: warning
* description: Warning messages
* - name: error
* description: Error messages
*
* @elements
* - name: icon
* description: Status icon
* html: <span class="{{class}}">ℹ</span>
* - name: content
* description: Message text
* html: <span class="{{class}}">Alert message</span>
* - name: close
* description: Dismiss button
* html: <button class="{{class}}">×</button>
*
* @dependencies icon, button
*
* @examples
* - title: Info Alert
* code: |
* <div class="alert alert--info">
* <span class="alert__content">This is an informational message.</span>
* </div>
* - title: With Close Button
* code: |
* <div class="alert alert--success">
* <span class="alert__content">Action completed successfully!</span>
* <button class="alert__close">×</button>
* </div>
*/
.alert {
/* Component tokens */
--alert-bg: var(--color-semantic-surface);
--alert-text: var(--color-semantic-text);
--alert-border: var(--color-semantic-border);
--alert-padding: var(--spacing-md);
--alert-radius: var(--border-radius-md);
/* Base styles */
display: flex;
align-items: center;
gap: var(--spacing-sm);
padding: var(--alert-padding);
background: var(--alert-bg);
color: var(--alert-text);
border: 1px solid var(--alert-border);
border-radius: var(--alert-radius);
/* Variants */
&--info {
--alert-bg: var(--color-info-50);
--alert-text: var(--color-info-700);
--alert-border: var(--color-info-200);
}
&--success {
--alert-bg: var(--color-success-50);
--alert-text: var(--color-success-700);
--alert-border: var(--color-success-200);
}
}
.alert__content {
flex: 1;
}
.alert__close {
/* close button styles */
}
Parsing Rules
- Single values:
@key valueassigns string - Arrays: Keys ending in 's' parse as comma-separated lists
- YAML: Indented content after
@keyis parsed as YAML - Multiline:
@descriptioncaptures all text until next annotation - CSS Variables: Automatically extracted as
cssVars
Debugging
Run the build to see parsed annotations:
stylescribe build
cat build/components.json | jq '.components[0]'
Each component's JSON includes all parsed annotations plus:
cssVars: Array of CSS custom properties usedfile: Source file pathcss: Compiled CSS