diff --git a/.cursor/rules/component-documentation.mdc b/.cursor/rules/component-documentation.mdc
deleted file mode 100644
index f839a92f2b7..00000000000
--- a/.cursor/rules/component-documentation.mdc
+++ /dev/null
@@ -1,872 +0,0 @@
----
-globs: 2nd-gen/packages/swc/components/*/stories/**
-alwaysApply: false
----
-# Component documentation standards
-
-Enforce comprehensive and consistent component documentation across 1st-gen and 2nd-gen implementations.
-
-## Scope
-
-Apply to component documentation in:
-
-- `1st-gen/packages/*/README.md`
-- `2nd-gen/packages/swc/components/*/stories/*.stories.ts`
-- `2nd-gen/packages/swc/components/*/stories/*.usage.mdx`
-
-## Documentation completeness requirements
-
-### Section order
-
-Document sections in this order (skip sections that don't apply):
-
-1. **Installation** - How to install and import (may be centralized)
-2. **Anatomy** - Component structure and key parts
-3. **Options** - Variants, sizes, styles, and configuration
- - Sizes
- - Variants
- - Quiet/subtle/emphasized
- - Outline
- - Static color
- - Positioning (e.g., fixed)
- - Other options as applicable
-4. **States** - Different states in this order:
- - Default
- - Selected
- - Active
- - Disabled
- - Readonly
- - Error (valid, invalid, etc.)
- - Loading/pending/indeterminate
- - Other states as applicable
-5. **Behaviors** - Built-in functionality:
- - Automatic behaviors (text wrapping, truncation, interactivity)
- - Methods
- - Events
-6. **Accessibility** - A11y features and best practices:
- - Features (keyboard navigation, ARIA states, roles, properties)
- - Best practices
-
-### Essential sections
-
-Every component must document:
-
-1. **Anatomy** - Component structure and key parts
-2. **Options** - Variants, sizes, styles, and configuration
-3. **Accessibility** - A11y features and best practices
-
-### Accessibility requirements for all stories
-
-**All stories must follow accessibility best practices.** Stories serve as examples for consumers, so they must demonstrate proper accessible usage:
-
-- **Include descriptive labels**: If a component requires a label (e.g., `label`, `aria-label`, slot content), every story must include it
-- **Use meaningful text content**: Avoid placeholder text - use realistic, descriptive content
-- **Demonstrate proper ARIA usage**: Show correct ARIA attributes when applicable
-- **Never demonstrate inaccessible patterns**: Stories should not show examples that would fail accessibility audits
-
-### Conditional sections
-
-Include if applicable:
-
-4. **Installation** - How to install and import (may be centralized)
-5. **States** - Different states (disabled, loading, error, etc.)
-6. **Behaviors** - Interactive behaviors, text wrapping, methods, events
-
-## Installation documentation
-
-**Note**: Installation is documented directly in usage.mdx, not as a Storybook story.
-
-### Required content
-
-- Package name and npm install command
-- Import statements
-- Basic usage example
-
-### Example format (in usage.mdx)
-
-```mdx
-## Installation
-
-Install the component package:
-
-```bash
-npm install @spectrum-web-components/component-name
-```
-
-Import the component:
-
-```javascript
-import '@spectrum-web-components/component-name/sp-component-name.js';
-```
-```
-
-## Anatomy documentation
-
-### Required content
-
-The anatomy section should document:
-
-- **All slots** - Named and default slots that accept content
-- **Content-rendering properties** - Properties that render additional visual content (e.g., `label`, `icon`, `src`, `value`)
-- Visual examples showing structure
-- Explanation of required vs optional elements
-
-**Consolidation rule**: Combine all slotted content combinations into a **single Anatomy story** to make it easier for consumers to view all variations at once.
-
-**Layout requirement**: Use `flexLayout: true` when showing multiple anatomy variations.
-
-### Example format
-
-```typescript
-/**
- * A component-name consists of:
- *
- * ### Slots
- *
- * - **Default slot**: Primary content (text or elements)
- * - **icon slot**: Optional icon element
- * - **description slot**: Additional descriptive content
- *
- * ### Content properties
- *
- * - **label**: Text label rendered by the component
- * - **icon**: Icon name to display
- * - **src**: Image source URL
- *
- * All combinations are shown below for comparison.
- */
-export const Anatomy: Story = {
- render: (args) => html`
- ${template({ ...args, /* text only */ })}
- ${template({ ...args, /* icon only */ })}
- ${template({ ...args, /* text + icon */ })}
- `,
- tags: ['anatomy'],
- parameters: {
- flexLayout: true,
- },
-};
-```
-
-### Common elements to document
-
-#### Slots
-
-- Default slot
-- Named slots (icon, description, footer, etc.)
-- Conditional slots (shown only in certain states)
-
-#### Content-rendering properties
-
-- `label` - Text label displayed by the component
-- `icon` - Icon name or identifier
-- `src` - Image or media source
-- `value` - Displayed value content
-- `placeholder` - Placeholder text
-- Any property that causes the component to render additional visible content
-
-## Options documentation
-
-**The Options section should document every attribute or property that isn't already covered in the Anatomy or States sections.** This includes configuration options, visual modifiers, and behavioral settings.
-
-Document options in this order (skip subsections that don't apply):
-
-1. Sizes
-2. Semantic variants
-3. Non-semantic variants
-4. Quiet/subtle/emphasized
-5. Outline
-6. Static color
-7. Positioning (e.g., fixed)
-8. Other options as applicable
-
-**Consolidation rules**: Combine related options into single stories for easier comparison:
-- All sizes → single `Sizes` story
-- All semantic variants → single `SemanticVariants` story
-- All non-semantic variants → single `NonSemanticVariants` story
-
-**Layout requirement**: For stories that display multiple variations (sizes, variants, etc.) within a single story, use the `flexLayout` decorator to ensure proper spacing and alignment.
-
-### Size options
-
-If component has sizes, document all sizes in a **single Sizes story**:
-
-- All available sizes (s, m, l, xl if supported)
-- Default size
-- Use cases for each size
-- Visual hierarchy guidance
-
-**Example**:
-
-```typescript
-/**
- * Components come in [X] sizes to fit various contexts:
- *
- * - **Small**: Used for [specific use case]
- * - **Medium**: Default size, used for [typical use case]
- * - **Large**: Used for [prominent use case]
- *
- * All sizes shown below for comparison.
- */
-export const Sizes: Story = {
- render: (args) => html`
- ${template({ ...args, size: 's' })}
- ${template({ ...args, size: 'm' })}
- ${template({ ...args, size: 'l' })}
- `,
- tags: ['options'],
- parameters: {
- flexLayout: true,
- },
-};
-```
-
-### Variant options
-
-#### Semantic variants
-
-Document all semantic variants in a **single SemanticVariants story** with clear use case guidance:
-
-```typescript
-/**
- * Semantic variants provide meaning through color:
- *
- * - **Positive**: approved, complete, success, new, purchased, licensed
- * - **Informative**: active, in use, live, published
- * - **Negative**: error, alert, rejected, failed
- * - **Neutral**: archived, deleted, paused, draft, not started, ended
- * - **Notice**: needs approval, pending, scheduled
- *
- * All semantic variants shown below for comparison.
- */
-export const SemanticVariants: Story = {
- render: (args) => html`
- ${template({ ...args, variant: 'positive' })}
- ${template({ ...args, variant: 'informative' })}
- ${template({ ...args, variant: 'negative' })}
- ${template({ ...args, variant: 'neutral' })}
- ${template({ ...args, variant: 'notice' })}
- `,
- tags: ['options'],
- parameters: {
- flexLayout: true,
- },
-};
-```
-
-#### Non-semantic variants
-
-Document all non-semantic variants in a **single NonSemanticVariants story**:
-
-```typescript
-/**
- * When components are for color-coded categories, they use non-semantic colors.
- * Non-semantic variants are ideally used for when there are 8 categories or less.
- *
- * All non-semantic variants shown below for comparison.
- */
-export const NonSemanticVariants: Story = {
- render: (args) => html`
- ${template({ ...args, variant: 'seafoam' })}
- ${template({ ...args, variant: 'indigo' })}
- ${/* ... other colors */ ''}
- `,
- tags: ['options'],
- parameters: {
- flexLayout: true,
- },
-};
-```
-
-### Quiet/subtle/emphasized options
-
-Document visual prominence modifiers:
-
-```typescript
-/**
- * - **Quiet**: Reduced visual prominence for less important actions
- * - **Subtle**: Even more reduced prominence
- * - **Emphasized**: Increased visual prominence for primary actions
- */
-```
-
-### Outline options
-
-Document outline style (typically semantic only):
-
-```typescript
-/**
- * The `outline` style provides a bordered appearance.
- * Note: Outline style is only available with semantic variants.
- */
-```
-
-### Static color pattern
-
-For components with static-color support, implement the three-story pattern using the static color decorators:
-
-1. **`StaticBlack`** - Individual story with `static-color="black"`
-2. **`StaticWhite`** - Individual story with `static-color="white"`
-3. **`StaticColors`** - Combined story showing both variants side-by-side
-
-Two decorators handle static color backgrounds:
-
-**`withStaticColorBackground`** (automatic):
-- Automatically detects `static-color` arg and applies appropriate gradient background
-- Used for individual `StaticBlack` and `StaticWhite` stories
-
-**`staticColorsDemo`** (opt-in):
-- Enable with `parameters.staticColorsDemo = true`
-- Displays two variants side-by-side with appropriate backgrounds
-- First child: dark gradient (for `static-color="white"`)
-- Last child: light gradient (for `static-color="black"`)
-
-#### Simple pattern (preferred)
-
-Map over the static color values and use story-level args for common properties:
-
-```typescript
-/**
- * When displaying over images or colored backgrounds, use the `static-color` attribute for better contrast,
- * e.g. `static-color="white"` on a dark background or `static-color="black"` on a light background.
- */
-export const StaticColors: Story = {
- render: (args) => html`
- ${['white', 'black'].map((color) => html`${template({ ...args, 'static-color': color })}`)}
- `,
- args: {
- progress: 60,
- label: 'Loading',
- },
- tags: ['options', '!test'],
- parameters: {
- flexLayout: false,
- staticColorsDemo: true,
- },
-};
-```
-
-#### Contextual pattern
-
-Use when components need additional descriptive content around them (e.g., dividers with surrounding text):
-
-```typescript
-/**
- * When displaying over images or colored backgrounds, use the `static-color` attribute for better contrast,
- * e.g. `static-color="white"` on a dark background or `static-color="black"` on a light background.
- */
-export const StaticColors: Story = {
- render: (args) => html`
- ${['white', 'black'].map(
- (color) => html`
-
-
Dashboard settings
- ${template({ ...args, 'static-color': color })}
-
Configure your dashboard preferences and layout options.
-
`
- )}
- `,
- args: {
- size: 'm',
- },
- parameters: {
- flexLayout: false,
- staticColorsDemo: true,
- },
- tags: ['options', '!test'],
-};
-```
-
-### Positioning options
-
-Document positioning modifiers like fixed:
-
-```typescript
-/**
- * The `fixed` attribute positions the component relative to the viewport.
- * Use for components that should remain visible during scrolling.
- */
-```
-
-## States documentation
-
-### Required for components with states
-
-**Consolidation rule**: Combine all states into a **single States story** (or minimal stories when states are complex) to make it easier for consumers to compare variations.
-
-**Layout requirement**: For stories showing multiple states, use the `flexLayout` decorator.
-
-Document states in this order (skip states that don't apply):
-
-1. Default - Normal/initial state
-2. Selected - When item is chosen
-3. Active - During interaction
-4. Disabled - When unavailable
-5. Readonly - When not editable
-6. Error - Validation states (valid, invalid, etc.)
-7. Loading/pending/indeterminate - Async states
-8. Other states as applicable
-
-**Example**:
-
-```typescript
-/**
- * Components can exist in various states.
- * All states shown below for comparison.
- */
-export const States: Story = {
- render: (args) => html`
- ${template({ ...args, /* default */ })}
- ${template({ ...args, selected: true })}
- ${template({ ...args, disabled: true })}
- ${template({ ...args, readonly: true })}
- ${template({ ...args, invalid: true })}
- `,
- tags: ['states'],
- parameters: {
- flexLayout: true,
- },
-};
-```
-
-If a state requires complex demonstration (e.g., animated indeterminate progress), it may warrant a separate story.
-
-### Default state template
-
-```typescript
-/**
- * The default state represents the component in its normal, interactive state.
- */
-```
-
-### Selected state template
-
-```typescript
-/**
- * The selected state indicates that the item has been chosen or activated.
- */
-```
-
-### Active state template
-
-```typescript
-/**
- * The active state represents the component during user interaction (e.g., mouse down, touch).
- */
-```
-
-### Disabled state template
-
-```typescript
-/**
- * A component in a disabled state shows that [functionality] exists, but is not available in that circumstance.
- * This can be used to maintain layout continuity and communicate that [functionality] may become available later.
- */
-```
-
-Include ARIA support documentation:
-
-```
-- **ARIA support**: When disabled, the component automatically sets `aria-disabled="true"`
-```
-
-### Readonly state template
-
-```typescript
-/**
- * A readonly component displays its value but cannot be modified by the user.
- * Unlike disabled, readonly components can still receive focus and be read by assistive technologies.
- */
-```
-
-### Error state template
-
-```typescript
-/**
- * Error states communicate validation status:
- *
- * - **Valid**: Input meets all requirements
- * - **Invalid**: Input fails validation with appropriate error messaging
- */
-```
-
-### Loading/pending/indeterminate state template
-
-```typescript
-/**
- * Loading states indicate asynchronous operations:
- *
- * - **Loading**: Data is being fetched or processed
- * - **Pending**: Action is awaiting completion
- * - **Indeterminate**: Progress cannot be determined
- */
-```
-
-## Behaviors documentation
-
-### Required for components with behaviors
-
-Document behaviors in this order (skip subsections that don't apply):
-
-1. Automatic behaviors - Built-in functionality that can't be modified via properties or attributes
-2. Methods - Public methods available on the component
-3. Events - Events dispatched by the component
-
-### Automatic behaviors
-
-Document built-in functionality like:
-
-- Text wrapping and truncation
-- Overflow handling
-- Focus management
-- Interactivity patterns
-
-```typescript
-/**
- * ### Text handling
- *
- * - Long text automatically wraps to multiple lines
- * - Text truncates with ellipsis when space is constrained
- *
- * ### Focus behavior
- *
- * - Focus is trapped within the component when open
- * - Focus returns to trigger element on close
- */
-```
-
-### Methods
-
-Document public methods:
-
-```typescript
-/**
- * ### Methods
- *
- * - `open()`: Opens the component programmatically
- * - `close()`: Closes the component programmatically
- * - `toggle()`: Toggles the open/closed state
- */
-```
-
-### Events
-
-Document dispatched events:
-
-```typescript
-/**
- * ### Events
- *
- * - `change`: Dispatched when the value changes
- * - `input`: Dispatched during user input
- * - `sp-opened`: Dispatched when the component opens
- * - `sp-closed`: Dispatched when the component closes
- */
-```
-
-## Accessibility documentation
-
-### Required structure
-
-Every component must have comprehensive accessibility documentation with two subsections:
-
-1. **Features** - Built-in accessibility features (keyboard navigation, ARIA states, roles, properties)
-2. **Best practices** - Guidelines for proper usage
-
-### Features template
-
-```typescript
-/**
- * ### Features
- *
- * The `` element implements several accessibility features:
- *
- * 1. **Keyboard navigation**: Description of keyboard interactions
- * 2. **ARIA role**: Automatically sets appropriate role
- * 3. **ARIA states**: Manages aria-disabled, aria-expanded, etc.
- * 4. **ARIA properties**: Sets aria-label, aria-labelledby, etc.
- *
- * ### Best practices
- *
- * - Specific, actionable guidance
- * - Another best practice
- * - Consider X for Y scenarios
- */
-```
-
-### Common accessibility features to document
-
-#### Keyboard navigation
-
-Document keyboard interactions first:
-
-```
-#### Keyboard navigation
-
-- Tab: Description of tab behavior
-- Space or Enter: Description of activation
-- Arrow keys: Description of navigation
-- Escape: Description of dismiss behavior
-```
-
-#### ARIA attributes
-
-- **ARIA role**: Document automatic role assignment
-- **ARIA labels**: Document labeling mechanism (aria-label, aria-labelledby)
-- **ARIA states**: Document state attributes (aria-disabled, aria-valuenow, etc.)
-- **ARIA properties**: Document other ARIA properties
-- **ARIA orientation**: Document for components with orientation
-
-**Examples**:
-
-```
-- **ARIA role**: Automatically sets `role="progressbar"` for proper semantic meaning
-- **Labeling**: Uses the `label` attribute value as `aria-label`
-- **Status communication**: Screen readers announce progress updates
-```
-
-#### Color meaning
-
-For components using color to convey information:
-
-```
-- **Color meaning**: Colors are used in combination with text labels to ensure that status information is not conveyed through color alone
-```
-
-### Best practices format
-
-Use bullet points with clear, actionable guidance:
-
-```
-- Use semantic variants when the status has specific meaning
-- Always provide a descriptive `label` that explains what the component represents
-- Ensure sufficient color contrast between the component and its background
-- Avoid using for critical actions; use appropriate interactive components instead
-```
-
-## Cross-reference: 1st-gen to 2nd-gen migration
-
-### Content to preserve from 1st-gen
-
-When migrating or enhancing 2nd-gen documentation, check 1st-gen for:
-
-1. **Size options** - Verify all sizes (especially xl) are documented
-2. **Variant lists** - Ensure all variants are listed (check for accent, etc.)
-3. **States** - Check for disabled, loading, or other states
-4. **Behavioral details** - Text wrapping, truncation, tooltip integration
-5. **Keyboard interactions** - If documented in 1st-gen
-6. **Do/Don't tables** - Valuable guidance often in 1st-gen
-7. **Advanced examples** - Tooltips, containers, complex layouts
-8. **Specific color contrast guidance** - Especially for static-color variants
-
-### Common gaps to check
-
-Compare 2nd-gen against 1st-gen README.md for:
-
-- [ ] Missing installation instructions
-- [ ] Missing size options (xl is common)
-- [ ] Missing semantic variants (accent is common)
-- [ ] Undocumented disabled state
-- [ ] Missing readonly state documentation
-- [ ] Missing ARIA disabled documentation
-- [ ] Lack of tooltip integration examples
-- [ ] Missing icon-only accessibility guidance
-- [ ] No keyboard navigation documentation
-- [ ] Missing static-color contrast guidance
-- [ ] No text truncation/wrapping behavior explanation
-- [ ] Missing methods documentation
-- [ ] Missing events documentation
-
-### Enhancement opportunities
-
-Areas where 2nd-gen often improves on 1st-gen:
-
-- Better structured accessibility sections
-- Clearer story organization
-- More comprehensive anatomy sections
-- Better visual comparisons (e.g., static colors side-by-side)
-- Improved testability
-
-## Story ordering within sections
-
-To control the display order of stories within a section, use the `section-order` parameter. Stories are sorted within each section by:
-
-1. **Lowest to highest `section-order` value** - Stories with lower numbers appear first
-2. **Alphabetically** - For stories with no `section-order` or with the same `section-order` value
-
-### Example: controlling story order
-
-```typescript
-export const Sizes: Story = {
- tags: ['options'],
- parameters: {
- 'section-order': 1,
- flexLayout: true,
- },
-};
-
-export const Variants: Story = {
- tags: ['options'],
- parameters: {
- 'section-order': 2,
- flexLayout: true,
- },
-};
-```
-
-## Documentation quality checklist
-
-### For each component
-
-Sections in correct order:
-- [ ] Playground story set to most common use case (used as docs page preview)
-- [ ] Installation section written directly in usage.mdx (if applicable)
-- [ ] Anatomy story documents all slots AND content-rendering properties (label, icon, src, etc.)
-- [ ] Options section covers all attributes/properties not in Anatomy or States:
- - [ ] Single Sizes story with all sizes for comparison
- - [ ] Single SemanticVariants story with all semantic variants (if applicable)
- - [ ] Single NonSemanticVariants story with all non-semantic variants (if applicable)
- - [ ] Quiet/subtle/emphasized documented (if applicable)
- - [ ] Outline documented (if applicable)
- - [ ] Static color with three-story pattern using `staticColorsDemo` decorator
- - [ ] Positioning documented (if applicable)
-- [ ] States section with consolidated story:
- - [ ] Single States story combining all states for comparison
- - [ ] Separate stories only for complex states (e.g., animated indeterminate)
-- [ ] Behaviors section (if applicable) with subsections:
- - [ ] Automatic behaviors (text wrapping, truncation, interactivity)
- - [ ] Methods documented
- - [ ] Events documented
-- [ ] Accessibility section with subsections:
- - [ ] Features (keyboard navigation, ARIA states, roles, properties)
- - [ ] Best practices with actionable guidance
-
-Layout and ordering:
-- [ ] **Stories showing multiple items use `flexLayout: true`** (sizes, variants, states, etc.)
-- [ ] **Story order controlled with `section-order` parameter** when alphabetical order isn't appropriate
-
-Content quality:
-- [ ] Semantic variants have usage examples
-- [ ] Non-semantic variants note 8-category limit
-- [ ] Color meaning documented for status indicators
-- [ ] Label requirements clearly stated
-- [ ] Do/don't guidance present
-
-Accessibility in all stories:
-- [ ] **All stories include required accessibility attributes** (labels, aria-label, text content)
-- [ ] **All stories use meaningful, descriptive content** (not placeholder text)
-- [ ] **No stories demonstrate inaccessible patterns**
-
-## Writing style standards
-
-### Capitalization
-
-- Use sentence case for headings and descriptions
-- Capitalize proper nouns and technical terms (JavaScript, CSS, ARIA, etc.)
-- Use proper capitalization for semantic meaning (Positive, Negative, etc.)
-
-### Formatting
-
-- Use `**bold**` for emphasis and term introductions
-- Use `code formatting` for attributes, values, and code references
-- Use bullet lists for multiple related items
-- Use numbered lists for sequential features or steps
-
-### Tone
-
-- Be clear and concise
-- Use present tense
-- Be prescriptive for best practices
-- Explain "why" not just "what"
-
-## Example documentation levels
-
-### Minimal (inadequate)
-
-```typescript
-/**
- * Component with variants.
- */
-export const Variants: Story = { /* ... */ };
-```
-
-❌ Too brief, no context or guidance
-
-### Adequate
-
-```typescript
-/**
- * Badges come in semantic and non-semantic variants for different use cases.
- */
-export const Variants: Story = { /* ... */ };
-```
-
-⚠️ Basic information but lacks detail
-
-### Excellent
-
-```typescript
-/**
- * Semantic variants allow you to render the badge with a descriptive name that maps to a design-system-aligned color.
- * This is the preferred way to assign color to a badge because it will align more consistently with other components in your UI with the same meaning.
- *
- * Use these variants for the following statuses:
- * - **Positive**: approved, complete, success, new, purchased, licensed
- * - **Informative**: active, in use, live, published
- * - **Negative**: error, alert, rejected, failed
- * - **Neutral**: archived, deleted, paused, draft, not started, ended
- */
-export const SemanticVariants: Story = { /* ... */ };
-```
-
-✅ Comprehensive explanation with use cases and guidance
-
-## Reference implementations
-
-### Exemplary migrations
-
-- **Asset**: 2nd-gen improves on minimal 1st-gen with better structure
-- **Progress circle**: Clean reference implementation
-- **Divider**: Maintains comprehensive 1st-gen content with improved organization
-
-### Components needing enhancement
-
-Check DOC-COMPARISON.md files in each component directory for:
-
-- Identified gaps vs 1st-gen
-- Priority recommendations
-- Specific missing content areas
-
-## Using documentation comparison reports
-
-Each migrated component has a `DOC-COMPARISON.md` file:
-
-```
-2nd-gen/packages/swc/components/[component-name]/DOC-COMPARISON.md
-```
-
-These reports identify:
-
-- **Missing content areas** - Critical gaps and nice-to-have additions
-- **Content depth differences** - Where 1st-gen or 2nd-gen excels
-- **Recommendations by priority** - High, medium, low priority enhancements
-- **Content quality assessment** - Table comparing specific areas
-- **Overall assessment** - Completeness score and summary
-
-Use these reports when:
-
-- Enhancing existing 2nd-gen documentation
-- Creating new component documentation
-- Reviewing documentation completeness
-- Prioritizing documentation work
-
-## Continuous improvement
-
-When updating component documentation:
-
-1. Review DOC-COMPARISON.md for identified gaps
-2. Check 1st-gen README.md for any additional details
-3. Ensure all sections in this standard are addressed
-4. Test examples in Storybook
-5. Verify accessibility claims with actual implementation
-6. Update DOC-COMPARISON.md if gaps are addressed
diff --git a/.cursor/rules/stories-documentation.mdc b/.cursor/rules/stories-documentation.mdc
new file mode 100644
index 00000000000..69041d2bd5a
--- /dev/null
+++ b/.cursor/rules/stories-documentation.mdc
@@ -0,0 +1,875 @@
+---
+globs: 2nd-gen/packages/swc/components/*/stories/**
+alwaysApply: true
+---
+# Component stories documentation standards
+
+Comprehensive guide for creating consistent, accurate, and accessible component documentation in Storybook stories.
+
+This guide integrates Shoelace's documentation patterns with Spectrum Web Components' structure, providing both visual and technical breakdowns while using TOC anchor linking.
+
+## Scope
+
+Apply to all component stories in:
+
+- `2nd-gen/packages/swc/components/*/stories/*.stories.ts`
+- `2nd-gen/packages/swc/components/*/stories/*.usage.mdx`
+
+## Documentation structure
+
+Organize documentation into these sections (skip sections that don't apply):
+
+1. **Overview** - Introduction and primary use case
+2. **Anatomy** - Both visual and technical structure
+3. **Options** - Configuration, variants, sizes, styles
+4. **States** - Different component states
+5. **Behaviors** - Methods, events, automatic behaviors
+6. **Accessibility** - Features and best practices
+
+**Note**: Installation instructions are programmatically generated by `DocumentTemplate.mdx` and should not be manually added to usage.mdx files.
+
+## Helpers section
+
+**Purpose**: Organize shared code, label mappings, and utilities used across multiple stories.
+
+**Location**: Between `export default meta` and first story (Playground/Autodocs).
+
+**When to use:**
+
+- Component has multiple variants, sizes, or options that need consistent labels
+- Stories require reusable label mappings or data structures
+- Complex rendering logic needs to be shared across stories
+
+**Pattern**:
+
+```typescript
+// ────────────────────
+// HELPERS
+// ────────────────────
+
+// Label mappings for sizes
+const sizeLabels = {
+ s: 'Small',
+ m: 'Medium',
+ l: 'Large',
+ xl: 'Extra-large',
+} as const satisfies Record;
+
+// Label mappings for semantic variants
+const semanticLabels = {
+ positive: 'Approved',
+ negative: 'Rejected',
+ informative: 'Active',
+ neutral: 'Archived',
+ notice: 'Pending',
+} as const satisfies Record;
+
+// Combined label objects for convenience
+const allLabels = { ...semanticLabels, ...colorLabels };
+```
+
+**Key principles:**
+
+- Use TypeScript `as const satisfies` for type-safe label mappings
+- Group related helpers together (size labels, variant labels, etc.)
+- Use clear, descriptive constant names (`sizeLabels`, not `labels`)
+- Provide realistic, meaningful labels (not "Label 1", "Label 2")
+
+**When NOT to use:**
+
+- Component has only 1-2 options (no need for label mappings)
+- Labels are self-explanatory (e.g., true/false)
+- Stories don't share any common code
+
+## Section patterns
+
+### Overview
+
+**Purpose**: Quick introduction showing the component in its most common use case.
+
+**Documentation**: The Overview story content comes from two sources:
+
+1. **JSDoc description above meta**: Provides the main description of what the component does and when to use it. This description:
+ - Is displayed as the primary documentation for the Overview story
+ - Can include markdown formatting and links to other components
+ - Should reference other components using Storybook paths: `[Badge](../?path=/docs/badge--readme)`
+ - Should provide fuller context than the subtitle
+
+2. **`parameters.docs.subtitle`**: Provides a brief summary displayed as the subtitle. This subtitle:
+ - Cannot include links (plain text only)
+ - Should be concise and to-the-point
+ - Should not repeat the JSDoc description verbatim
+ - Complements the JSDoc description
+
+**Pattern**:
+
+```typescript
+/**
+ * A badge is a non-interactive visual label that displays a status, category, or attribute.
+ * Badges can be used to highlight important information or to categorize items. For interactive
+ * labels, see [Button](../?path=/docs/button--readme).
+ */
+const meta: Meta = {
+ title: 'Badge',
+ component: 'swc-badge',
+ args,
+ argTypes,
+ render: (args) => template(args),
+ parameters: {
+ docs: { subtitle: `Visual label for status, category, or attribute` },
+ // ... other parameters
+ },
+ tags: ['migrated'],
+};
+
+export default meta;
+
+// ... HELPERS section if needed ...
+
+export const Overview: Story = {
+ tags: ['overview'],
+ args: {
+ // Most common/representative configuration
+ },
+};
+```
+
+**Note**: No JSDoc comment needed on the Overview story itself - the meta JSDoc and subtitle provide the documentation.
+
+### Anatomy
+
+**Purpose**: Document both visual structure (what users see) and technical structure (slots, parts, properties).
+
+**Required content:**
+
+- **All slots** with descriptions
+- **Content-rendering properties** (label, icon, src, value, etc.)
+- **CSS custom properties** (key design tokens)
+- Visual examples showing structure
+
+**Consolidation rule**: Combine all slotted content combinations into a **single Anatomy story**.
+
+**Pattern**:
+
+```typescript
+/**
+ * ### Visual structure
+ *
+ * A component-name consists of:
+ *
+ * 1. **Primary element** - Main visual component
+ * 2. **Secondary element** - Additional visual content
+ * 3. **Optional indicator** - Shown conditionally
+ *
+ * ### Technical structure
+ *
+ * #### Slots
+ *
+ * - **Default slot**: Primary content (text or HTML)
+ * - **icon slot**: Optional icon element
+ * - **description slot**: Additional descriptive content
+ *
+ * #### Properties
+ *
+ * Properties that render visual content:
+ *
+ * - **label**: Text label displayed by the component
+ * - **icon**: Icon identifier to display
+ * - **src**: Image source URL
+ * - **value**: Displayed value content
+ *
+ * #### CSS parts
+ *
+ * Exposed shadow DOM parts for styling:
+ *
+ * - **base**: The component's base wrapper
+ * - **track**: Progress track element
+ * - **fill**: Progress fill element
+ *
+ * #### CSS custom properties
+ *
+ * Key design tokens that control appearance:
+ *
+ * - **--spectrum-component-size**: Overall size of the component
+ * - **--spectrum-component-background-color**: Background color
+ * - **--spectrum-component-border-radius**: Corner rounding
+ *
+ * All variations shown below for comparison.
+ */
+export const Anatomy: Story = {
+ render: (args) => html`
+ ${template({ ...args, /* minimal */ })}
+ ${template({ ...args, /* with icon */ })}
+ ${template({ ...args, /* with additional content */ })}
+ `,
+ tags: ['anatomy'],
+ parameters: {
+ flexLayout: true,
+ },
+};
+```
+
+**Key principles:**
+
+- Start with visual structure (designer-focused)
+- Follow with technical structure (developer-focused)
+- Document all slots with clear descriptions
+- List content-rendering properties (label, icon, src, value, etc.)
+- Include CSS parts if exposed for styling customization
+- Document key CSS custom properties (design tokens)
+- Show all meaningful combinations in one story
+
+### Options
+
+**Purpose**: Document every attribute or property not covered in Anatomy, States, or Behaviors.
+
+**Order**: Sizes → Semantic variants → Non-semantic variants → Quiet/Subtle/Emphasized → Outline → Static color → Positioning → Other options
+
+**Consolidation rules**:
+
+- All sizes → single `Sizes` story
+- All semantic variants → single `SemanticVariants` story
+- All non-semantic variants → single `NonSemanticVariants` story
+
+**Layout requirement**: Use `flexLayout: true` for stories displaying multiple variations.
+
+**Pattern for sizes**:
+
+```typescript
+/**
+ * Component-names come in [X] sizes to fit various contexts:
+ *
+ * - **Small (s)**: Used for inline indicators or space-constrained areas
+ * - **Medium (m)**: Default size, used for typical use cases
+ * - **Large (l)**: Used for prominent displays or primary content areas
+ * - **Extra-large (xl)**: Maximum visibility (if applicable)
+ *
+ * All sizes shown below for comparison.
+ */
+export const Sizes: Story = {
+ render: (args) => html`
+ ${template({ ...args, size: 's', label: 'Small' })}
+ ${template({ ...args, size: 'm', label: 'Medium' })}
+ ${template({ ...args, size: 'l', label: 'Large' })}
+ `,
+ tags: ['options'],
+ parameters: {
+ flexLayout: true,
+ 'section-order': 1,
+ },
+};
+```
+
+**Pattern for semantic variants**:
+
+```typescript
+/**
+ * Semantic variants provide meaning through color:
+ *
+ * - **accent**: New, beta, prototype, draft
+ * - **informative**: Active, in use, live, published
+ * - **neutral**: Archived, deleted, paused, draft, not started, ended
+ * - **positive**: Approved, complete, success, new, purchased, licensed
+ * - **notice**: Needs approval, pending, scheduled
+ * - **negative**: Error, alert, rejected, failed
+ *
+ * All semantic variants shown below for comparison.
+ */
+export const SemanticVariants: Story = {
+ render: (args) => html`
+ ${template({ ...args, variant: 'positive', label: 'Positive' })}
+ ${template({ ...args, variant: 'informative', label: 'Informative' })}
+ ${template({ ...args, variant: 'negative', label: 'Negative' })}
+ ${template({ ...args, variant: 'neutral', label: 'Neutral' })}
+ ${template({ ...args, variant: 'notice', label: 'Notice' })}
+ `,
+ tags: ['options'],
+ parameters: {
+ flexLayout: true,
+ 'section-order': 2,
+ },
+};
+```
+
+**Pattern for static color**:
+
+```typescript
+/**
+ * Use the `static-color` attribute when displaying over images or colored backgrounds:
+ *
+ * - **white**: Use on dark or colored backgrounds for better contrast
+ * - **black**: Use on light backgrounds for better contrast
+ *
+ * Both variants shown below with appropriate backgrounds.
+ */
+export const StaticColors: Story = {
+ render: (args) => html`
+ ${['white', 'black'].map((color) => html`${template({ ...args, 'static-color': color })}`)}
+ `,
+ args: {
+ label: 'Loading',
+ },
+ tags: ['options', '!test'],
+ parameters: {
+ flexLayout: false,
+ staticColorsDemo: true,
+ 'section-order': 5,
+ },
+};
+```
+
+### States
+
+**Purpose**: Document all possible states the component can be in.
+
+**Order**: Default → Selected → Active → Disabled → Readonly → Error → Loading/Pending/Indeterminate → Other states
+
+**Consolidation rule**: Combine all states into a **single States story** when possible (or minimal stories when states are complex).
+
+**Pattern**:
+
+```typescript
+/**
+ * Components can exist in various states:
+ *
+ * - **Default**: Normal, interactive state
+ * - **Selected**: Item has been chosen or activated
+ * - **Disabled**: Functionality exists but is not available
+ * - **Error**: Validation failure or error condition
+ *
+ * All states shown below for comparison.
+ */
+export const States: Story = {
+ render: (args) => html`
+ ${template({ ...args, label: 'Default' })}
+ ${template({ ...args, selected: true, label: 'Selected' })}
+ ${template({ ...args, disabled: true, label: 'Disabled' })}
+ ${template({ ...args, invalid: true, label: 'Error' })}
+ `,
+ tags: ['states'],
+ parameters: {
+ flexLayout: true,
+ },
+};
+```
+
+**Pattern for complex states** (when animation or interaction is critical):
+
+```typescript
+/**
+ * The indeterminate state shows an animated loading indicator when progress is unknown or cannot be determined.
+ * The animation automatically loops until the state changes.
+ */
+export const Indeterminate: Story = {
+ tags: ['states'],
+ args: {
+ indeterminate: true,
+ label: 'Loading...',
+ },
+};
+```
+
+**Disabled state template**:
+
+```typescript
+/**
+ * A component in a disabled state shows that [functionality] exists, but is not available in that circumstance.
+ * This can be used to maintain layout continuity and communicate that [functionality] may become available later.
+ *
+ * **ARIA support**: When disabled, the component automatically sets `aria-disabled="true"`.
+ */
+```
+
+### Behaviors
+
+**Purpose**: Document methods, events, and automatic behaviors.
+
+**Pattern for automatic behaviors**:
+
+```typescript
+/**
+ * ### Text handling
+ *
+ * Long text content automatically wraps to multiple lines to fit the available space.
+ * When space is constrained, text truncates with an ellipsis (...).
+ *
+ * ### Focus management
+ *
+ * When opened, focus is automatically trapped within the component.
+ * When closed, focus returns to the triggering element.
+ */
+export const TextWrapping: Story = {
+ render: (args) => html`
+ ${template({ 'default-slot': 'Short text' })}
+ ${template({ 'default-slot': 'This is a much longer text that will wrap to multiple lines when the container becomes too narrow to fit it all on one line' })}
+ `,
+ tags: ['behaviors'],
+ parameters: {
+ flexLayout: true,
+ },
+};
+```
+
+**Pattern for methods**:
+
+```typescript
+/**
+ * ### Methods
+ *
+ * The component exposes the following public methods:
+ *
+ * - **open()**: Opens the component programmatically
+ * - **close()**: Closes the component programmatically
+ * - **toggle()**: Toggles between open and closed states
+ * - **reset()**: Resets the component to its initial state
+ *
+ * Example usage:
+ *
+ * ```javascript
+ * const component = document.querySelector('swc-component-name');
+ * component.open();
+ * ```
+ */
+export const Methods: Story = {
+ tags: ['behaviors'],
+ // ... implementation
+};
+```
+
+**Pattern for events**:
+
+```typescript
+/**
+ * ### Events
+ *
+ * The component dispatches the following custom events:
+ *
+ * - **change**: Fired when the value changes (bubbles: true, composed: true)
+ * - **input**: Fired during user input (bubbles: true, composed: true)
+ * - **swc-opened**: Fired when the component opens (bubbles: true, composed: true)
+ * - **swc-closed**: Fired when the component closes (bubbles: true, composed: true)
+ *
+ * Example event listener:
+ *
+ * ```javascript
+ * component.addEventListener('change', (event) => {
+ * console.log('Value changed:', event.target.value);
+ * });
+ * ```
+ */
+export const Events: Story = {
+ tags: ['behaviors'],
+ // ... implementation
+};
+```
+
+### Accessibility
+
+**Purpose**: Document built-in accessibility features and provide best practices guidance.
+
+**Required structure**: Two subsections - Features and Best practices.
+
+**Pattern**:
+
+```typescript
+/**
+ * ### Features
+ *
+ * The `` element implements several accessibility features:
+ *
+ * #### Keyboard navigation
+ *
+ * - Tab: Moves focus to/from the component
+ * - Space or Enter: Activates the component
+ * - Arrow keys: Navigate between items
+ * - Escape: Closes the component (if applicable)
+ *
+ * #### ARIA implementation
+ *
+ * 1. **ARIA role**: Automatically sets `role="progressbar"` (or appropriate role)
+ * 2. **Labeling**: Uses the `label` attribute as `aria-label`
+ * 3. **States**:
+ * - Sets `aria-valuenow` with current progress value
+ * - Sets `aria-disabled="true"` when disabled
+ * 4. **Status communication**: Screen readers announce value changes
+ *
+ * #### Visual accessibility
+ *
+ * - Progress is shown visually through multiple cues, not relying solely on color
+ * - High contrast mode is supported with appropriate color overrides
+ * - Static color variants ensure sufficient contrast on different backgrounds
+ *
+ * ### Best practices
+ *
+ * - Always provide a descriptive `label` that explains what the component represents
+ * - Use meaningful, specific labels (e.g., "Uploading document" instead of "Loading")
+ * - Ensure sufficient color contrast between the component and its background
+ * - Use semantic variants when status has specific meaning
+ * - Test with screen readers to verify announcements are clear
+ */
+export const Accessibility: Story = {
+ tags: ['a11y'],
+ args: {
+ // ... accessible example
+ },
+};
+```
+
+**Common accessibility features to document:**
+
+- **Keyboard navigation**: Document all keyboard interactions
+- **ARIA role**: Document automatic role assignment
+- **ARIA labels**: Document labeling mechanism (aria-label, aria-labelledby)
+- **ARIA states**: Document state attributes (aria-disabled, aria-valuenow, etc.)
+- **Color meaning**: For components using color to convey information
+- **High contrast mode**: If supported via forced-colors media query
+
+**Accessibility requirements for all stories:**
+
+All stories must demonstrate accessible usage:
+
+- Include required labels (`label`, `aria-label`, or slot content)
+- Use meaningful, realistic content (no placeholder text)
+- Show proper ARIA usage when applicable
+- Never demonstrate inaccessible patterns
+
+## 1st-gen to 2nd-gen comparison
+
+When creating 2nd-gen documentation, check 1st-gen (`1st-gen/packages/*/README.md`) for content to preserve or differences to highlight.
+
+### Where to document differences
+
+Document 1st-gen vs 2nd-gen differences directly in the relevant story's JSDoc comment where that difference is apparent.
+
+**Pattern for documenting differences**:
+
+```typescript
+/**
+ * Component-names come in [X] sizes...
+ *
+ * **Note**: The `xl` size is new in 2nd-gen and not available in 1st-gen.
+ *
+ * All sizes shown below for comparison.
+ */
+export const Sizes: Story = {
+ // ...
+};
+```
+
+Or for removed features:
+
+```typescript
+/**
+ * ### Methods
+ *
+ * The component exposes the following public methods...
+ *
+ * **Migration note**: The `validate()` method from 1st-gen has been removed in 2nd-gen.
+ * Use the `invalid` property instead for validation state.
+ */
+export const Methods: Story = {
+ // ...
+};
+```
+
+### Content to check from 1st-gen
+
+Compare 2nd-gen against 1st-gen for:
+
+1. **Size options**: Verify all sizes (especially `xl`) are documented
+2. **Variant lists**: Ensure all variants are listed (check for `accent`, etc.)
+3. **States**: Check for disabled, loading, or other states
+4. **Behavioral details**: Text wrapping, truncation, tooltip integration
+5. **Keyboard interactions**: If documented in 1st-gen
+6. **Methods and events**: Check for API changes
+7. **Advanced examples**: Tooltips, containers, complex layouts
+8. **Specific guidance**: Color contrast guidance for static-color variants
+
+### Common differences to highlight
+
+**New in 2nd-gen:**
+
+- Additional sizes (e.g., `xl`)
+- Additional variants (e.g., `accent`)
+- New properties (e.g., `subtle`, `outline`)
+- Improved ARIA implementation
+- Better high contrast support
+
+**Changed in 2nd-gen:**
+
+- Property names (e.g., `emphasized` → `outline`)
+- Slot names or structure
+- Event names or payloads
+- Default values
+
+**Removed from 2nd-gen:**
+
+- Deprecated properties
+- Removed methods
+- Unsupported variants
+
+### Using CEM for comparison
+
+Use the Custom Elements Manifest (CEM) to verify differences:
+
+1. **Check 1st-gen CEM**: `1st-gen/packages/component-name/custom-elements.json`
+2. **Check 2nd-gen types**: `2nd-gen/packages/core/components/component-name/*.types.ts`
+3. **Compare**:
+ - Properties and their types
+ - Slots and their descriptions
+ - Methods and their signatures
+ - Events and their details
+
+**Example verification**:
+
+```bash
+# Check 1st-gen properties
+grep -A 5 '"name": "size"' 1st-gen/packages/badge/custom-elements.json
+
+# Check 2nd-gen types
+grep "VALID_SIZES" 2nd-gen/packages/core/components/badge/Badge.types.ts
+```
+
+### Common gaps to check
+
+- [ ] Missing installation instructions
+- [ ] Missing size options (xl is common)
+- [ ] Missing semantic variants (accent is common)
+- [ ] Undocumented disabled state
+- [ ] Missing readonly state documentation
+- [ ] Missing ARIA disabled documentation
+- [ ] Lack of tooltip integration examples
+- [ ] Missing icon-only accessibility guidance
+- [ ] No keyboard navigation documentation
+- [ ] Missing static-color contrast guidance
+- [ ] No text truncation/wrapping behavior explanation
+- [ ] Missing methods documentation
+- [ ] Missing events documentation
+
+## Verification and accuracy
+
+**Critical**: Always verify documentation against the actual component implementation to prevent hallucinations and inaccuracies.
+
+### Verification process
+
+After creating or updating documentation:
+
+1. **Read the component source files:**
+ - Base class implementation (`2nd-gen/packages/core/components/*/Component.base.ts`)
+ - Component-specific implementation (`2nd-gen/packages/swc/components/*/Component.ts`)
+ - TypeScript type definitions (`2nd-gen/packages/core/components/*/Component.types.ts`)
+ - CSS stylesheets (`2nd-gen/packages/swc/components/*/component.css`)
+
+2. **Verify slots:**
+ - Check JSDoc comments in component class (`@slot` decorators)
+ - Verify slot names in the render method
+ - Confirm which slots are required vs. optional
+
+3. **Verify properties:**
+ - Check `@property` decorators in component and base classes
+ - Verify property types, defaults, and reflect values
+ - Confirm valid values (enums, arrays defined in types file)
+ - Check if properties exist in both base and subclass
+
+4. **Verify CSS custom properties:**
+ - Search CSS file for custom properties (e.g., `--spectrum-component-*`)
+ - Confirm properties are actually used in the styles
+ - Check if properties vary by size/variant
+
+5. **Verify ARIA implementation:**
+ - Search for `aria-*` attributes in component code
+ - Check for `role` assignments
+ - Verify attribute behavior (when added/removed)
+ - **Don't claim attributes that aren't implemented**
+
+6. **Verify behavior:**
+ - Check for methods in component class (public methods only)
+ - Look for event dispatching (`dispatchEvent`, custom events)
+ - Verify automatic behaviors in lifecycle methods
+ - Check CSS for animations, transitions, media queries
+
+7. **Verify constraints:**
+ - Look for validation logic (e.g., outline only with semantic variants)
+ - Check for console warnings about invalid combinations
+ - Verify size/variant/option restrictions
+
+### Common hallucination patterns to avoid
+
+❌ **Claiming attributes that don't exist:**
+
+```typescript
+// WRONG: Claiming a `label` attribute when component only has a slot
+- Provide label via the `label` attribute
+```
+
+✅ **Verify what actually exists:**
+
+```typescript
+// CORRECT: Check component source for actual API
+- Provide label via the default slot or `aria-label` attribute
+```
+
+❌ **Assuming ARIA attributes are set:**
+
+```typescript
+// WRONG: Claiming aria-valuemin/aria-valuemax without checking
+- Includes `aria-valuemin="0"` and `aria-valuemax="100"`
+```
+
+✅ **Verify ARIA implementation in code:**
+
+```typescript
+// CORRECT: Only document what the component actually sets
+- Sets `aria-valuenow` with the current progress value
+```
+
+❌ **Documenting non-existent CSS properties:**
+
+```typescript
+// WRONG: Inventing custom properties
+- **--spectrum-badge-animation-duration**: Animation timing
+```
+
+✅ **Search CSS file for actual properties:**
+
+```typescript
+// CORRECT: Only document properties that exist in CSS
+- **--spectrum-badge-background-color-default**: Background color
+```
+
+❌ **Claiming methods that don't exist:**
+
+```typescript
+// WRONG: Assuming common methods exist
+- **reset()**: Resets the component to initial state
+```
+
+✅ **Check component class for public methods:**
+
+```typescript
+// CORRECT: Only document methods defined in the class
+// (If no public methods exist, state this clearly)
+```
+
+### Verification tools
+
+Use these commands to verify claims:
+
+```bash
+# Search for properties
+grep -n "@property" 2nd-gen/packages/swc/components/component-name/Component.ts
+
+# Search for slots
+grep -n "@slot" 2nd-gen/packages/core/components/component-name/Component.base.ts
+
+# Search for ARIA attributes
+grep -n "aria-" 2nd-gen/packages/swc/components/component-name/Component.ts
+
+# Search for CSS custom properties
+grep -n "--spectrum-component-" 2nd-gen/packages/swc/components/component-name/component.css
+
+# Search for events
+grep -n "dispatchEvent\|new CustomEvent" 2nd-gen/packages/swc/components/component-name/Component.ts
+
+# Compare with 1st-gen
+diff <(grep "@property" 1st-gen/packages/component-name/*.ts) \
+ <(grep "@property" 2nd-gen/packages/swc/components/component-name/*.ts)
+```
+
+### Documentation after verification
+
+After verifying accuracy:
+
+1. Document what was checked in commit message or PR
+2. Note any inaccuracies found and fixed
+3. Include code references for major claims (in JSDoc comments if helpful)
+4. Consider creating a verification checklist for complex components
+
+## General guidelines
+
+### Documentation style
+
+- **Use sentence case** for all headings and descriptions
+- **Be specific** - Instead of "Loading", use "Uploading document" or "Processing request"
+- **Show, don't just tell** - Include visual examples for every concept
+- **Consolidate variations** - Combine related options into single stories for easier comparison
+- **Think multi-audience** - Balance designer needs (visual) with developer needs (technical)
+
+### Component linking
+
+When referencing other components in the JSDoc description above meta:
+
+- **Use Storybook paths**: Link to the component's overview story using relative paths
+- **Format**: `[ComponentName](../?path=/docs/component-name--readme)`
+- **Component name format**: Use kebab-case in the path (e.g., `action-button`, `progress-circle`)
+- **Always link to readme**: Use `--readme` as the story anchor
+
+**Examples**:
+
+```typescript
+/**
+ * A `` is a non-interactive visual label. For interactive labels,
+ * see [Action Button](../?path=/docs/action-button--readme).
+ */
+
+/**
+ * Progress circles are commonly used with [Button](../?path=/docs/button--readme)
+ * and [Card](../?path=/docs/card--readme) components to show loading states.
+ */
+```
+
+### Code examples
+
+- **Use realistic content** - Avoid placeholder text, use meaningful labels
+- **Always be accessible** - Include required labels, ARIA attributes
+- **Show complete patterns** - Don't abbreviate important details
+- **Use consistent formatting** - Follow project style guidelines
+
+### Story organization
+
+- **Use flexLayout** for multi-item comparisons
+- **Set section-order** to control display order within sections
+- **Tag appropriately** - Use correct tags for each section
+- **Add JSDoc comments** - Explain what each story demonstrates (except Playground and Overview)
+
+### JSDoc heading levels
+
+All headings in JSDoc comments must start at level 3 (`###`) or deeper:
+
+- **Level 3 (`###`)**: Top-level sections within a story (e.g., "Visual structure", "Features", "Methods")
+- **Level 4 (`####`)**: Sub-sections within those sections (e.g., "Slots", "ARIA implementation", "Keyboard navigation")
+
+This ensures proper hierarchy since JSDoc content is rendered within the story context, subordinate to the main section headings in the usage.mdx file (which use `##`).
+
+## Checklist
+
+When creating or updating documentation:
+
+- [ ] Overview story with common use case
+- [ ] JSDoc description above meta (explains component purpose, links to related components)
+- [ ] `parameters.docs.subtitle` is concise and non-repetitive (plain text, no links)
+- [ ] Helpers section for shared label mappings and utilities (if applicable)
+- [ ] Anatomy with both visual and technical structure
+- [ ] All slots documented with descriptions
+- [ ] All content-rendering properties listed
+- [ ] CSS parts documented (if exposed)
+- [ ] CSS custom properties (design tokens) documented
+- [ ] All configuration options documented
+- [ ] All states documented
+- [ ] Methods documented (if applicable)
+- [ ] Events documented (if applicable)
+- [ ] Automatic behaviors explained
+- [ ] Comprehensive accessibility section with features and best practices
+- [ ] All examples use accessible, meaningful content
+- [ ] Consistent flexLayout usage for comparisons
+- [ ] Proper story tags for all sections
+- [ ] JSDoc comments for all stories (except Playground and Overview)
+- [ ] JSDoc headings start at level 3 (`###`) or deeper
+- [ ] **Checked 1st-gen README.md for missing content or differences**
+- [ ] **Documented 1st-gen differences where apparent (new/changed/removed features)**
+- [ ] **Verified against component implementation** (no hallucinations)
+- [ ] All slots verified in component source
+- [ ] All properties verified with `@property` decorators
+- [ ] All CSS custom properties verified in stylesheet
+- [ ] ARIA attributes verified in component code
+- [ ] Methods and events verified in implementation
diff --git a/.cursor/rules/stories-format.mdc b/.cursor/rules/stories-format.mdc
index 7d4f3fe2e27..8f708ce5ed1 100644
--- a/.cursor/rules/stories-format.mdc
+++ b/.cursor/rules/stories-format.mdc
@@ -1,10 +1,12 @@
---
-description: 2nd-gen/packages/swc/components/*/stories/**
-alwaysApply: false
+globs: 2nd-gen/packages/swc/components/*/stories/**
+alwaysApply: true
---
# Storybook stories format standards
-Enforce consistent formatting and structure for Storybook stories files in 2nd-gen components.
+Enforce consistent formatting and technical structure for Storybook stories files in 2nd-gen components.
+
+**See also**: `.cursor/rules/stories-documentation.mdc` for comprehensive guidance on WHAT to document (content, patterns, examples).
## Scope
@@ -19,13 +21,13 @@ Required structure with visual separators between sections:
1. **Copyright header** (lines 1-11)
2. **Imports**
3. **METADATA** - Meta object with component configuration
-4. **AUTODOCS STORY** - Playground story
-5. **ANATOMY STORIES** - Component structure (if applicable)
-6. **OPTIONS STORIES** - Variants, sizes, styles
-7. **STATES STORIES** - Component states (if applicable)
-8. **BEHAVIORS STORIES** - Built-in functionality (if applicable)
-9. **ACCESSIBILITY STORIES** - A11y demonstration
-10. **HELPER FUNCTIONS** - Utilities like size label mappers (if needed)
+4. **HELPERS** - Shared label mappings and utilities (if needed)
+5. **AUTODOCS STORY** - Playground story
+6. **ANATOMY STORIES** - Component structure (if applicable)
+7. **OPTIONS STORIES** - Variants, sizes, styles
+8. **STATES STORIES** - Component states (if applicable)
+9. **BEHAVIORS STORIES** - Built-in functionality (if applicable)
+10. **ACCESSIBILITY STORIES** - A11y demonstration
#### Visual separators
@@ -34,6 +36,10 @@ Required structure with visual separators between sections:
// METADATA
// ────────────────
+// ────────────────────
+// HELPERS
+// ────────────────────
+
// ────────────────────
// AUTODOCS STORY
// ────────────────────
@@ -57,23 +63,68 @@ Required structure with visual separators between sections:
// ────────────────────────────────
// ACCESSIBILITY STORIES
// ────────────────────────────────
+```
+
+## Key Principles
+
+### Always Pass Args Through
+
+When using custom `render` functions, **always** spread `...args` into template calls:
+
+```typescript
+// ✅ Good - args are passed through
+export const MyStory: Story = {
+ render: (args) => html`
+ ${template({ ...args, size: 's' })}
+ `,
+};
-// ────────────────────────
-// HELPER FUNCTIONS
-// ────────────────────────
+// ❌ Bad - args are lost
+export const MyStory: Story = {
+ render: (args) => html`
+ ${template({ size: 's' })}
+ `,
+};
+```
+
+This ensures:
+
+- Storybook controls work correctly
+- Args from the Playground/global level are respected
+- Component defaults can be overridden
+
+### When to Use render vs args
+
+- **Use `args` directly**: When the default render is sufficient (single component, no wrapper)
+- **Use `render: (args) =>`**: When you need multiple instances, custom HTML structure, or conditional rendering
+
+```typescript
+// ✅ Good - simple case uses args
+export const Overview: Story = {
+ args: { size: 'm', label: 'Example' },
+ tags: ['overview'],
+};
+
+// ✅ Good - complex case uses render with args
+export const Sizes: Story = {
+ render: (args) => html`
+ ${template({ ...args, size: 's' })}
+ ${template({ ...args, size: 'm' })}
+ ${template({ ...args, size: 'l' })}
+ `,
+ tags: ['options'],
+};
```
### Usage file (`.usage.mdx`)
-Keep minimal—use `` to pull in stories by tag. Only include sections with corresponding stories, except Installation which is written directly in MDX.
+Keep minimal—use `` to pull in stories by tag. Only include sections with corresponding stories.
+
+**Note**: Installation instructions are programmatically generated by `DocumentTemplate.mdx` and should not be manually added.
```mdx
import { SpectrumStories } from '../../../.storybook/blocks';
-## Installation
-
-Package name, npm install command, and import statements go here directly (not as a story).
-
## Anatomy
@@ -96,9 +147,11 @@ Package name, npm install command, and import statements go here directly (not a
```
**Section rules**:
+
- Use `hideTitle` for Anatomy and Accessibility only
- Follow this section order, skipping inapplicable sections
- Use sentence case for headings
+- Do not include an Installation section (it's auto-generated)
## Meta configuration
@@ -107,6 +160,11 @@ Package name, npm install command, and import statements go here directly (not a
```typescript
/**
* Component description explaining its purpose and key features.
+ *
+ * This description is displayed in the Overview story. It should provide context about
+ * what the component does and when to use it. If referencing other components, link to
+ * their Storybook paths using relative URLs (e.g., `` becomes
+ * `[Badge](../?path=/docs/badge--overview)`).
*/
const meta: Meta = {
title: 'Component name',
@@ -116,7 +174,9 @@ const meta: Meta = {
render: (args) => template(args),
parameters: {
actions: { handles: events }, // If events exist
- docs: { subtitle: `Component description` }, // Required
+ docs: {
+ subtitle: `Component description` // Required - displayed in Overview, cannot include links
+ },
design: { type: 'figma', url: 'https://www.figma.com/...'}, // Recommended
stackblitz: { url: 'https://stackblitz.com/...'}, // Recommended
},
@@ -124,6 +184,13 @@ const meta: Meta = {
};
```
+**Important notes:**
+
+- **JSDoc description above meta**: Displayed in the Overview story. Can include markdown links to other components.
+- **`parameters.docs.subtitle`**: Displayed as the subtitle in the Overview story. Cannot include links (plain text only).
+- **Avoid repetition**: The subtitle and JSDoc description should complement each other, not duplicate content. The subtitle is a brief summary; the JSDoc provides fuller context.
+- **Component links**: When referencing other components in the JSDoc description, use relative Storybook paths: `[ComponentName](../?path=/docs/component-name--overview)`
+
## Layout and decorators
Use `flexLayout: true` for stories displaying multiple items (sizes, variants, states). This applies flex layout with consistent spacing.
@@ -208,6 +275,7 @@ export const StaticColors: Story = {
| Tag | Usage |
|-----|-------|
| `'autodocs'`, `'dev'` | Playground story only |
+| `'overview'` | Overview story |
| `'anatomy'` | Anatomy stories |
| `'options'` | Variant, size, and style stories |
| `'states'` | State demonstration stories |
@@ -233,6 +301,8 @@ export const Playground: Story = {
};
```
+**Note**: Use `args` directly (not `render`) when the default render is sufficient. Only use `render: (args) => html` when you need custom rendering.
+
Include comprehensive JSDoc comment above the meta object explaining what the component does.
Every story export must have a JSDoc comment explaining:
@@ -241,7 +311,7 @@ Every story export must have a JSDoc comment explaining:
- Any important context or usage notes
- Best practices if relevant
-**Exception**: Do NOT add JSDoc comments above the Playground story.
+**Exceptions**: Do NOT add JSDoc comments above the Playground or Overview stories.
Use markdown formatting within JSDoc:
@@ -259,10 +329,27 @@ export const Playground: Story = {
};
```
+### Overview
+
+Quick introduction showing the component in its most common use case. No JSDoc comment needed.
+
+```typescript
+export const Overview: Story = {
+ args: {
+ // Most common/representative configuration
+ },
+ tags: ['overview'],
+};
+```
+
+**Note**: Use `args` directly when possible. Only use `render: (args) =>` if you need custom HTML structure around the component.
+
### Anatomy
Document all slots and content-rendering properties (e.g., `label`, `icon`, `src`). Combine variations into one story.
+**Important**: When using `render: (args) =>`, **always** spread `...args` into template calls to ensure Storybook controls work correctly.
+
```typescript
/**
* A component-name consists of:
@@ -455,6 +542,7 @@ export const Accessibility: Story = {
Every story export requires a JSDoc comment explaining what it demonstrates, **except Playground**.
Use markdown formatting:
+
- `**Bold**` for emphasis
- Bullet lists for multiple points
- Backticks for code
@@ -561,7 +649,7 @@ See `asset.stories.ts` for complete examples.
### ❌ Don't
-- Add JSDoc to Playground story
+- Add JSDoc to Playground or Overview story
- Use 'usage' tag (deprecated)
- Omit `subtitle` in meta parameters
- Use placeholder text
@@ -572,7 +660,7 @@ See `asset.stories.ts` for complete examples.
- Use `` in usage.mdx
- Use `flexLayout: true` for multi-item stories
- Tag stories correctly: `anatomy`, `options`, `states`, `behaviors`, `a11y`
-- Include comprehensive JSDoc (except Playground)
+- Include comprehensive JSDoc (except Playground and Overview)
- Use meaningful, realistic content
## Checklist
@@ -580,6 +668,9 @@ See `asset.stories.ts` for complete examples.
- [ ] Copyright header (2025)
- [ ] Visual separators between sections
- [ ] Meta: title, component, args, argTypes, render, `parameters.docs.subtitle`, `tags: ['migrated']`
+- [ ] Meta JSDoc description above meta object (with component links if applicable)
+- [ ] Subtitle is concise and non-repetitive (plain text only, no links)
+- [ ] Overview: `['overview']` tag, common use case args, no JSDoc on story itself
- [ ] Playground: `['autodocs', 'dev']` tags, no JSDoc, common use case args
- [ ] Anatomy: all slots + content properties, `['anatomy']` tag, `flexLayout: true`
- [ ] Options: all uncovered attributes, `['options']` tag, `flexLayout: true`
diff --git a/2nd-gen/packages/core/components/badge/Badge.types.ts b/2nd-gen/packages/core/components/badge/Badge.types.ts
index ecaca256daf..1b9439c2db4 100644
--- a/2nd-gen/packages/core/components/badge/Badge.types.ts
+++ b/2nd-gen/packages/core/components/badge/Badge.types.ts
@@ -10,30 +10,33 @@
* governing permissions and limitations under the License.
*/
+import type { ElementSize } from '@spectrum-web-components/core/shared/base/index.js';
+
/*
* @todo The S1 types can be removed once we are no longer maintaining 1st-gen.
*/
-import type { ElementSize } from '@spectrum-web-components/core/shared/base/index.js';
-
-export const VALID_SIZES: ElementSize[] = ['s', 'm', 'l'] as const;
-
export const FIXED_VALUES = [
- 'inline-start',
- 'inline-end',
'block-start',
'block-end',
+ 'inline-start',
+ 'inline-end',
] as const;
-export type FixedValues = (typeof FIXED_VALUES)[number];
+export const BADGE_VALID_SIZES = [
+ 's',
+ 'm',
+ 'l',
+ 'xl',
+] as const satisfies readonly ElementSize[];
export const BADGE_VARIANTS_SEMANTIC = [
'accent',
- 'neutral',
'informative',
+ 'neutral',
'positive',
- 'negative',
'notice',
+ 'negative',
] as const;
export const BADGE_VARIANTS_COLOR_S1 = [
@@ -72,6 +75,11 @@ export const BADGE_VARIANTS_S2 = [
...BADGE_VARIANTS_COLOR_S2,
] as const;
+export type FixedValues = (typeof FIXED_VALUES)[number];
+export type BadgeSize = (typeof BADGE_VALID_SIZES)[number];
+export type BadgeSemanticVariant = (typeof BADGE_VARIANTS_SEMANTIC)[number];
+export type BadgeColorVariantS1 = (typeof BADGE_VARIANTS_COLOR_S1)[number];
+export type BadgeColorVariantS2 = (typeof BADGE_VARIANTS_COLOR_S2)[number];
export type BadgeVariantS1 = (typeof BADGE_VARIANTS_S1)[number];
export type BadgeVariantS2 = (typeof BADGE_VARIANTS_S2)[number];
export type BadgeVariant = BadgeVariantS1 | BadgeVariantS2;
diff --git a/2nd-gen/packages/core/components/divider/Divider.base.ts b/2nd-gen/packages/core/components/divider/Divider.base.ts
index 60497882806..ec8e61e1c1e 100644
--- a/2nd-gen/packages/core/components/divider/Divider.base.ts
+++ b/2nd-gen/packages/core/components/divider/Divider.base.ts
@@ -29,6 +29,7 @@ import {
*/
export abstract class DividerBase extends SizedMixin(SpectrumElement, {
validSizes: DIVIDER_VALID_SIZES,
+ /**@todo the design spec says the default size is small but we declare no default size */
noDefaultSize: true,
}) {
/**
diff --git a/2nd-gen/packages/core/components/divider/Divider.types.ts b/2nd-gen/packages/core/components/divider/Divider.types.ts
index 25aee5d3dd9..cc4bf0fbc52 100644
--- a/2nd-gen/packages/core/components/divider/Divider.types.ts
+++ b/2nd-gen/packages/core/components/divider/Divider.types.ts
@@ -12,7 +12,11 @@
import type { ElementSize } from '@spectrum-web-components/core/shared/base/index.js';
-export const DIVIDER_VALID_SIZES: ElementSize[] = ['s', 'm', 'l'] as const;
+export const DIVIDER_VALID_SIZES = [
+ 's',
+ 'm',
+ 'l',
+] as const satisfies ElementSize[];
export const DIVIDER_STATIC_COLORS = ['white', 'black'] as const;
export type DividerStaticColor = (typeof DIVIDER_STATIC_COLORS)[number];
diff --git a/2nd-gen/packages/core/components/progress-circle/ProgressCircle.base.ts b/2nd-gen/packages/core/components/progress-circle/ProgressCircle.base.ts
index 1777635784f..773ce15373f 100644
--- a/2nd-gen/packages/core/components/progress-circle/ProgressCircle.base.ts
+++ b/2nd-gen/packages/core/components/progress-circle/ProgressCircle.base.ts
@@ -20,7 +20,6 @@ import { getLabelFromSlot } from '@spectrum-web-components/core/shared/get-label
import {
PROGRESS_CIRCLE_VALID_SIZES,
- ProgressCircleSize,
ProgressCircleStaticColor,
} from './ProgressCircle.types.js';
@@ -37,11 +36,9 @@ import {
* @slot - Accessible label for the progress circle.
*
* Used to provide context about what is loading or progressing.
- *
- * @fires progress-change - Dispatched when the progress value changes
*/
export abstract class ProgressCircleBase extends SizedMixin(SpectrumElement, {
- validSizes: PROGRESS_CIRCLE_VALID_SIZES as ProgressCircleSize[],
+ validSizes: PROGRESS_CIRCLE_VALID_SIZES,
}) {
// ─────────────────────────
// API TO OVERRIDE
@@ -90,7 +87,6 @@ export abstract class ProgressCircleBase extends SizedMixin(SpectrumElement, {
* Accessible label for the progress circle.
*
* Used to provide context about what is loading or progressing.
- * @required for accessibility
*/
@property({ type: String })
public label = '';
diff --git a/2nd-gen/packages/core/components/progress-circle/ProgressCircle.types.ts b/2nd-gen/packages/core/components/progress-circle/ProgressCircle.types.ts
index cf0072a4a62..5132c14d5b3 100644
--- a/2nd-gen/packages/core/components/progress-circle/ProgressCircle.types.ts
+++ b/2nd-gen/packages/core/components/progress-circle/ProgressCircle.types.ts
@@ -16,7 +16,7 @@ export const PROGRESS_CIRCLE_VALID_SIZES: ElementSize[] = [
's',
'm',
'l',
-] as const;
+] as const satisfies ElementSize[];
export const PROGRESS_CIRCLE_STATIC_COLORS_S1 = ['white'] as const;
export const PROGRESS_CIRCLE_STATIC_COLORS_S2 = [
...PROGRESS_CIRCLE_STATIC_COLORS_S1,
@@ -30,4 +30,3 @@ export type ProgressCircleStaticColorS2 =
export type ProgressCircleStaticColor =
| ProgressCircleStaticColorS1
| ProgressCircleStaticColorS2;
-export type ProgressCircleSize = (typeof PROGRESS_CIRCLE_VALID_SIZES)[number];
diff --git a/2nd-gen/packages/core/components/status-light/StatusLight.types.ts b/2nd-gen/packages/core/components/status-light/StatusLight.types.ts
index 170247a15fe..96842142abf 100644
--- a/2nd-gen/packages/core/components/status-light/StatusLight.types.ts
+++ b/2nd-gen/packages/core/components/status-light/StatusLight.types.ts
@@ -62,6 +62,22 @@ export const STATUSLIGHT_VARIANTS_S2 = [
...STATUSLIGHT_VARIANTS_COLOR_S2,
] as const;
+export type StatusLightSemanticVariantS1 =
+ (typeof STATUSLIGHT_VARIANTS_SEMANTIC_S1)[number];
+export type StatusLightSemanticVariantS2 =
+ (typeof STATUSLIGHT_VARIANTS_SEMANTIC_S2)[number];
+export type StatusLightSemanticVariant =
+ | StatusLightSemanticVariantS1
+ | StatusLightSemanticVariantS2;
+
+export type StatusLightColorVariantS1 =
+ (typeof STATUSLIGHT_VARIANTS_COLOR_S1)[number];
+export type StatusLightColorVariantS2 =
+ (typeof STATUSLIGHT_VARIANTS_COLOR_S2)[number];
+export type StatusLightColorVariant =
+ | StatusLightColorVariantS1
+ | StatusLightColorVariantS2;
+
export type StatusLightVariantS1 = (typeof STATUSLIGHT_VARIANTS_S1)[number];
export type StatusLightVariantS2 = (typeof STATUSLIGHT_VARIANTS_S2)[number];
export type StatusLightVariant = StatusLightVariantS1 | StatusLightVariantS2;
diff --git a/2nd-gen/packages/core/shared/base/Base.ts b/2nd-gen/packages/core/shared/base/Base.ts
index aa50148c017..fdda3c31812 100644
--- a/2nd-gen/packages/core/shared/base/Base.ts
+++ b/2nd-gen/packages/core/shared/base/Base.ts
@@ -63,23 +63,25 @@ export function SpectrumMixin>(
): T & Constructor {
class SpectrumMixinElement extends constructor {
/**
- * @private
+ * @internal
*/
public override shadowRoot!: ShadowRoot;
private _dirParent?: HTMLElement;
/**
- * @private
+ * @internal
*/
public override dir!: 'ltr' | 'rtl';
/**
- * @private
+ * @internal
*/
public get isLTR(): boolean {
return this.dir === 'ltr';
}
-
+ /**
+ * @internal
+ */
public hasVisibleFocusInTree(): boolean {
const getAncestors = (root: Document = document): HTMLElement[] => {
// eslint-disable-next-line @spectrum-web-components/document-active-element
diff --git a/2nd-gen/packages/core/shared/base/version.ts b/2nd-gen/packages/core/shared/base/version.ts
index 2177cdd7401..622cc87119d 100644
--- a/2nd-gen/packages/core/shared/base/version.ts
+++ b/2nd-gen/packages/core/shared/base/version.ts
@@ -1,13 +1,2 @@
-/**
- * Copyright 2025 Adobe. All rights reserved.
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License. You may obtain a copy
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
- * OF ANY KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
// Generated by genversion.
export const version = '1.10.0';
diff --git a/2nd-gen/packages/core/shared/observe-slot-presence.ts b/2nd-gen/packages/core/shared/observe-slot-presence.ts
index f1ef4569f61..a17d2eb2bc4 100644
--- a/2nd-gen/packages/core/shared/observe-slot-presence.ts
+++ b/2nd-gen/packages/core/shared/observe-slot-presence.ts
@@ -55,7 +55,7 @@ export function ObserveSlotPresence>(
}
/**
- * @private
+ * @internal
*/
public get slotContentIsPresent(): boolean {
if (lightDomSelectors.length === 1) {
@@ -71,6 +71,9 @@ export function ObserveSlotPresence>(
}
private [slotContentIsPresent]: Map = new Map();
+ /**
+ * @internal
+ */
public getSlotContentPresence(selector: string): boolean {
if (this[slotContentIsPresent].has(selector)) {
return this[slotContentIsPresent].get(selector) || false;
@@ -80,6 +83,9 @@ export function ObserveSlotPresence>(
);
}
+ /**
+ * @internal
+ */
public managePresenceObservedSlot = (): void => {
let changes = false;
lightDomSelectors.forEach((selector) => {
diff --git a/2nd-gen/packages/core/shared/observe-slot-text.ts b/2nd-gen/packages/core/shared/observe-slot-text.ts
index 26411ab9822..a90a95dac90 100644
--- a/2nd-gen/packages/core/shared/observe-slot-text.ts
+++ b/2nd-gen/packages/core/shared/observe-slot-text.ts
@@ -59,6 +59,9 @@ export function ObserveSlotText>(
});
}
+ /**
+ * @internal
+ */
@property({ type: Boolean, attribute: false })
public slotHasContent = false;
@@ -68,6 +71,9 @@ export function ObserveSlotText>(
})
private [assignedNodesList]!: NodeListOf;
+ /**
+ * @internal
+ */
public manageTextObservedSlot(): void {
if (!this[assignedNodesList]) {
return;
diff --git a/2nd-gen/packages/swc/.storybook/DocumentTemplate.mdx b/2nd-gen/packages/swc/.storybook/DocumentTemplate.mdx
index f5692e38167..6790d10ac3a 100644
--- a/2nd-gen/packages/swc/.storybook/DocumentTemplate.mdx
+++ b/2nd-gen/packages/swc/.storybook/DocumentTemplate.mdx
@@ -13,7 +13,7 @@ import {
GettingStarted,
SpectrumDocs,
SpectrumStories,
- StaticPrimaryStory,
+ OverviewStory,
} from './blocks';
import '@spectrum-web-components/tabs/sp-tabs.js';
import '@spectrum-web-components/tabs/sp-tab.js';
@@ -22,8 +22,9 @@ import '@spectrum-web-components/tabs/sp-tab-panel.js';
export const checkIsSingleStory = () => {
const resolvedOf = useOf('meta', ['meta']);
const { stories } = resolvedOf.csfFile;
- const visibleStories = Object.values(stories).filter((story) =>
- story.tags?.includes('autodocs' || 'dev')
+ const visibleStories = Object.values(stories).filter(
+ (story) =>
+ story.tags?.includes('autodocs') || story.tags?.includes('dev')
);
return visibleStories.length === 1;
};
@@ -48,7 +49,7 @@ export const AdvancedExamplesStories = () => {
-
+
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/banner_introduction_S2_desktop_2x_1689964089087.png b/2nd-gen/packages/swc/.storybook/assets/images/banner_introduction_S2_desktop_2x_1689964089087.png
new file mode 100644
index 00000000000..ba705002dd1
Binary files /dev/null and b/2nd-gen/packages/swc/.storybook/assets/images/banner_introduction_S2_desktop_2x_1689964089087.png differ
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/banner_principles_S2_desktop_2x_1689964204945.png b/2nd-gen/packages/swc/.storybook/assets/images/banner_principles_S2_desktop_2x_1689964204945.png
new file mode 100644
index 00000000000..b4a0016021b
Binary files /dev/null and b/2nd-gen/packages/swc/.storybook/assets/images/banner_principles_S2_desktop_2x_1689964204945.png differ
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/bg.e23742a5.png b/2nd-gen/packages/swc/.storybook/assets/images/bg.e23742a5.png
new file mode 100644
index 00000000000..ffbf4bce944
Binary files /dev/null and b/2nd-gen/packages/swc/.storybook/assets/images/bg.e23742a5.png differ
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/example-ava.png b/2nd-gen/packages/swc/.storybook/assets/images/example-ava.png
deleted file mode 100644
index d77edcca8db..00000000000
Binary files a/2nd-gen/packages/swc/.storybook/assets/images/example-ava.png and /dev/null differ
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/example-ava@2x.png b/2nd-gen/packages/swc/.storybook/assets/images/example-ava@2x.png
deleted file mode 100644
index 6eb727b7a19..00000000000
Binary files a/2nd-gen/packages/swc/.storybook/assets/images/example-ava@2x.png and /dev/null differ
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/gradient-background-dark.png b/2nd-gen/packages/swc/.storybook/assets/images/gradient-background-dark.png
deleted file mode 100644
index 961283356b0..00000000000
Binary files a/2nd-gen/packages/swc/.storybook/assets/images/gradient-background-dark.png and /dev/null differ
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/gradient-background-light.png b/2nd-gen/packages/swc/.storybook/assets/images/gradient-background-light.png
deleted file mode 100644
index ea5f9512b7f..00000000000
Binary files a/2nd-gen/packages/swc/.storybook/assets/images/gradient-background-light.png and /dev/null differ
diff --git a/2nd-gen/packages/swc/.storybook/assets/images/logo.svg b/2nd-gen/packages/swc/.storybook/assets/images/logo.svg
new file mode 100644
index 00000000000..b0d13e9c54a
--- /dev/null
+++ b/2nd-gen/packages/swc/.storybook/assets/images/logo.svg
@@ -0,0 +1,34 @@
+
+
\ No newline at end of file
diff --git a/2nd-gen/packages/swc/.storybook/blocks/GettingStarted.tsx b/2nd-gen/packages/swc/.storybook/blocks/GettingStarted.tsx
index 5cb311a7d22..a089b2870af 100644
--- a/2nd-gen/packages/swc/.storybook/blocks/GettingStarted.tsx
+++ b/2nd-gen/packages/swc/.storybook/blocks/GettingStarted.tsx
@@ -27,6 +27,8 @@ export const GettingStarted = ({ of }: { of?: any }) => {
const markdownContent = `## Getting started
+Add the package to your project:
+
\`\`\`zsh
yarn add @spectrum-web-components/${packageName}
\`\`\`
diff --git a/2nd-gen/packages/swc/.storybook/blocks/StaticPrimaryStory.tsx b/2nd-gen/packages/swc/.storybook/blocks/OverviewStory.tsx
similarity index 70%
rename from 2nd-gen/packages/swc/.storybook/blocks/StaticPrimaryStory.tsx
rename to 2nd-gen/packages/swc/.storybook/blocks/OverviewStory.tsx
index 38353d9f8b4..33130bd288e 100644
--- a/2nd-gen/packages/swc/.storybook/blocks/StaticPrimaryStory.tsx
+++ b/2nd-gen/packages/swc/.storybook/blocks/OverviewStory.tsx
@@ -1,15 +1,17 @@
import { Canvas, Story, useOf } from '@storybook/addon-docs/blocks';
import React from 'react';
+import type { ActionItem } from 'storybook/internal/components';
import { formatComponentName } from '../helpers/index.js';
-export const StaticPrimaryStory = () => {
+export const OverviewStory = () => {
const resolvedOf = useOf('meta', ['meta']);
+
const primaryStory = Object.values(resolvedOf.csfFile.stories).find(
- (story) =>
- story.name === 'Playground' || story.id.endsWith('--playground')
+ (story) => story.tags?.includes('overview')
);
+
if (!primaryStory) return null;
- primaryStory.argTypes = undefined;
+ primaryStory.args = null;
// Extract component name and create GitHub link
const componentName = formatComponentName(resolvedOf.preparedMeta?.title);
@@ -17,12 +19,12 @@ export const StaticPrimaryStory = () => {
'https://github.com/adobe/spectrum-web-components/tree/main/2nd-gen/packages/swc/components';
// Build additional actions dynamically based on available metadata
- const additionalActions = [];
+ const additionalActions: ActionItem[] = [];
// Spectrum Design Guidance - requires valid component name
if (
componentName &&
- !resolvedOf.csfFile.meta.parameters?.missingDesignDocs
+ !resolvedOf.csfFile.meta?.parameters?.missingDesignDocs
) {
additionalActions.push({
title: 'Read Spectrum Design Guidance',
@@ -36,12 +38,12 @@ export const StaticPrimaryStory = () => {
}
// Figma - requires design URL in parameters
- if (resolvedOf.csfFile.meta.parameters?.design?.url) {
+ if (resolvedOf.csfFile.meta?.parameters?.design?.url) {
additionalActions.push({
title: 'View Figma',
onClick: () => {
window.open(
- resolvedOf.csfFile.meta.parameters.design.url,
+ resolvedOf.csfFile.meta?.parameters?.design?.url,
'_blank'
);
},
@@ -59,28 +61,24 @@ export const StaticPrimaryStory = () => {
}
// Stackblitz - requires stackblitz URL in meta parameters
- if (resolvedOf.csfFile.meta.parameters?.stackblitz?.url) {
+ if (resolvedOf.csfFile.meta?.parameters?.stackblitz?.url) {
additionalActions.push({
title: 'Debug in Stackblitz',
onClick: () => {
window.open(
- resolvedOf.csfFile.meta.parameters.stackblitz.url,
+ resolvedOf.csfFile.meta?.parameters?.stackblitz?.url,
'_blank'
);
},
});
}
- const canvasOptions = {
- additionalActions,
- withToolbar: false,
- sourceState: 'none',
- layout: 'centered',
- };
-
return (
-
+
);
};
diff --git a/2nd-gen/packages/swc/.storybook/blocks/SpectrumStories.tsx b/2nd-gen/packages/swc/.storybook/blocks/SpectrumStories.tsx
index c321939618e..178f0cb6125 100644
--- a/2nd-gen/packages/swc/.storybook/blocks/SpectrumStories.tsx
+++ b/2nd-gen/packages/swc/.storybook/blocks/SpectrumStories.tsx
@@ -1,10 +1,11 @@
import {
Canvas,
Description,
+ Story,
Markdown,
useOf,
} from '@storybook/addon-docs/blocks';
-import React from 'react';
+import React, { Fragment } from 'react';
/**
* A block that renders all stories tagged with a specified tag from the component's stories file.
@@ -22,9 +23,8 @@ export const SpectrumStories = ({
of?: any;
tag?: string;
hideTitle?: boolean;
- order?: string[];
}) => {
- const resolvedOf = useOf(of || 'meta', ['story', 'meta']);
+ const resolvedOf = useOf(of || 'meta', ['meta']);
// Get stories and filter by tag
let taggedStories = Object.values(
@@ -33,8 +33,6 @@ export const SpectrumStories = ({
: [resolvedOf.story]
).filter((story: any) => story.tags?.includes(tag));
- console.log('taggedStories', taggedStories);
-
// Sort by explicit order if provided, otherwise preserve current order
taggedStories = taggedStories.sort((a: any, b: any) => {
const aIndex = a.parameters['section-order'] ?? taggedStories.length;
@@ -47,15 +45,13 @@ export const SpectrumStories = ({
return null;
}
- return (
- <>
- {taggedStories.map((story: any) => (
-
- {!hideTitle && {`### ${story.name}`}}
-
-
-
- ))}
- >
- );
+ const spectrumStories = taggedStories.map((story: any) => (
+
+ {!hideTitle && {`### ${story.name}`}}
+
+
+
+ ));
+
+ return spectrumStories;
};
diff --git a/2nd-gen/packages/swc/.storybook/blocks/index.ts b/2nd-gen/packages/swc/.storybook/blocks/index.ts
index bc81c667da2..175e5ea041c 100644
--- a/2nd-gen/packages/swc/.storybook/blocks/index.ts
+++ b/2nd-gen/packages/swc/.storybook/blocks/index.ts
@@ -1,4 +1,4 @@
export * from './GettingStarted';
+export * from './OverviewStory';
export * from './SpectrumDocs';
export * from './SpectrumStories';
-export * from './StaticPrimaryStory';
diff --git a/2nd-gen/packages/swc/.storybook/decorators/flex-layout.ts b/2nd-gen/packages/swc/.storybook/decorators/flex-layout.ts
index a84fd110186..cf32f31ac2e 100644
--- a/2nd-gen/packages/swc/.storybook/decorators/flex-layout.ts
+++ b/2nd-gen/packages/swc/.storybook/decorators/flex-layout.ts
@@ -15,6 +15,14 @@ import { makeDecorator } from '@storybook/preview-api';
import type { DecoratorFunction } from '@storybook/types';
import { styleMap } from 'lit/directives/style-map.js';
+export const FLEX_LAYOUT_TYPES = [
+ 'column-center',
+ 'column-stretch',
+ 'row-wrap',
+ 'row-nowrap',
+] as const;
+export type FlexLayoutType = (typeof FLEX_LAYOUT_TYPES)[number];
+
/**
* Decorator that wraps stories in a styled container.
* This is useful for components that benefit from horizontal layout with gap spacing.
@@ -34,21 +42,74 @@ export const withFlexLayout: DecoratorFunction = makeDecorator({
const { parameters } = context;
const { flexLayout, styles } = parameters;
- // Build styles object based on flexLayout and custom styles parameters
- const stylesObj = flexLayout
- ? {
- display: 'flex',
- gap: '24px',
- alignItems: 'center',
- ...styles,
- }
- : { ...styles };
- if (Object.keys(stylesObj).length === 0) {
+ // If no flexLayout or styles provided, render story normally
+ if (!flexLayout && !styles) {
+ return StoryFn(context);
+ }
+
+ let compiledStyles: Record = {};
+
+ switch (flexLayout as FlexLayoutType) {
+ case 'column-center':
+ compiledStyles = {
+ display: 'flex',
+ gap: 'var(--spectrum-spacing-100)',
+ flexDirection: 'column',
+ alignItems: 'center',
+ 'max-inline-size': '80ch',
+ ...styles,
+ };
+ break;
+
+ case 'column-stretch':
+ compiledStyles = {
+ display: 'flex',
+ gap: 'var(--spectrum-spacing-100)',
+ flexDirection: 'column',
+ alignItems: 'stretch',
+ 'max-inline-size': '80ch',
+ ...styles,
+ };
+ break;
+
+ case 'row-wrap':
+ compiledStyles = {
+ display: 'flex',
+ gap: 'var(--spectrum-spacing-200)',
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ alignItems: 'center',
+ justifyContent: 'center',
+ 'max-inline-size': '80ch',
+ ...styles,
+ };
+ break;
+
+ case 'row-nowrap':
+ compiledStyles = {
+ display: 'flex',
+ gap: 'var(--spectrum-spacing-200)',
+ flexDirection: 'row',
+ flexWrap: 'nowrap',
+ alignItems: 'center',
+ 'max-inline-size': '80ch',
+ ...styles,
+ };
+ break;
+
+ default:
+ // If flexLayout is not a valid type but styles exist, use styles only
+ compiledStyles = styles || {};
+ break;
+ }
+
+ // If no styles were compiled, render story normally
+ if (Object.keys(compiledStyles).length === 0) {
return StoryFn(context);
}
return html`
-
${StoryFn(context)}
+
${StoryFn(context)}
`;
},
});
diff --git a/2nd-gen/packages/swc/.storybook/decorators/helpers.ts b/2nd-gen/packages/swc/.storybook/decorators/helpers.ts
new file mode 100644
index 00000000000..4591c7d745b
--- /dev/null
+++ b/2nd-gen/packages/swc/.storybook/decorators/helpers.ts
@@ -0,0 +1,90 @@
+import { global } from '@storybook/global';
+
+/**
+ * @description Fetches the style container for the given ID or creates a new one
+ * @param {HTMLElement} container - required container in which to add the style tag
+ * @param {string} id - required ID for the style container
+ * @param {string} styles - required styles to add to the container
+ * @param {boolean} [add=true] - optional flag to add or remove the styles
+ * @param {string} [context] - optional context to print in the style tag
+ * @returns {void}
+ **/
+export function toggleStyles(
+ container,
+ id,
+ styles,
+ add = true,
+ context = undefined
+) {
+ if (!container && !id) return;
+
+ let style = container.querySelector(`#${id}`);
+ const isNewTag = !style;
+
+ // If we're removing the styles, remove the tag from the container
+ if (!add) {
+ if (!isNewTag) style.remove();
+ return;
+ }
+
+ if (isNewTag) style = document.createElement('style');
+
+ style.id = id;
+ style.innerHTML = styles;
+
+ if (context) style.setAttribute('data-context', context);
+ if (isNewTag) container.appendChild(style);
+}
+
+/**
+ * @type (id: string, container: HTMLElement) => HTMLElement
+ * @description Fetches the style container for the given ID or creates a new one
+ **/
+export function fetchStyleContainer(id, container) {
+ if (!id) return;
+
+ const { document } = global;
+ if (!container) container = document.body;
+
+ let styleContainer = container.querySelector(`#${id}`);
+ if (styleContainer) return styleContainer;
+
+ const styles = document.createElement('div');
+ styles.id = id;
+ // @todo add styles to the top part of the container
+ container.appendChild(styles);
+ return styles;
+}
+
+/**
+ * @type (id: string, isDocs: boolean = false) => HTMLElement[]
+ * @description Fetches the style container for the given ID or creates a new one
+ **/
+export function fetchContainers(id, isDocs = false, isTesting = false) {
+ if (!id) return [];
+ const { document } = global;
+
+ let containers = [];
+
+ // Storybook IDs used to target the container element for the docs pages
+ const roots = [
+ ...document.querySelectorAll(`#story--${id}`),
+ ...document.querySelectorAll(`#story--${id}--primary`),
+ ];
+
+ // viewMode is either "docs" or "story"
+ if (isDocs && roots.length > 0) {
+ containers = roots.map((root) => root.closest('.docs-story') ?? root);
+ } else if (isTesting) {
+ // Only capture the top-level container for testing previews
+ containers.push(
+ ...document.querySelectorAll(
+ 'body,[data-testing-preview],[data-testing-preview] [data-inner-container]'
+ )
+ );
+ }
+
+ if (containers.length === 0) containers = [document.body];
+
+ return containers;
+}
diff --git a/2nd-gen/packages/swc/.storybook/decorators/index.ts b/2nd-gen/packages/swc/.storybook/decorators/index.ts
index b2d7af56efd..6556c0de7e8 100644
--- a/2nd-gen/packages/swc/.storybook/decorators/index.ts
+++ b/2nd-gen/packages/swc/.storybook/decorators/index.ts
@@ -1,3 +1,3 @@
-export { withStaticColorBackground } from './static-color-background';
export { withFlexLayout } from './flex-layout';
-export { staticColors } from './static-colors';
+export { withStaticColorsDemo } from './static-colors-demo';
+export { withTextDirectionWrapper } from './text-direction';
diff --git a/2nd-gen/packages/swc/.storybook/decorators/static-color-background.ts b/2nd-gen/packages/swc/.storybook/decorators/static-color-background.ts
deleted file mode 100644
index baca8445361..00000000000
--- a/2nd-gen/packages/swc/.storybook/decorators/static-color-background.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright 2025 Adobe. All rights reserved.
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License. You may obtain a copy
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
- * OF ANY KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
- */
-
-import { html } from 'lit';
-import { makeDecorator } from '@storybook/preview-api';
-import type { DecoratorFunction } from '@storybook/types';
-import gradientBackgroundDark from '../assets/images/gradient-background-dark.png';
-import gradientBackgroundLight from '../assets/images/gradient-background-light.png';
-
-/**
- * Static color background settings - matching spectrum-css gradients
- */
-const staticColorSettings = {
- black: `url(${gradientBackgroundLight}) no-repeat center center`,
- white: `url(${gradientBackgroundDark}) no-repeat center center`,
-} as const;
-
-/**
- * Decorator that applies background colors based on static-color arg.
- * Wraps the story in a div with the appropriate background when static-color is set.
- */
-export const withStaticColorBackground: DecoratorFunction = makeDecorator({
- name: 'withStaticColorBackground',
- parameterName: 'staticColorBackground',
- wrapper: (StoryFn, context) => {
- const { args } = context;
- const staticColor = args?.[
- 'static-color'
- ] as keyof typeof staticColorSettings;
-
- const background =
- staticColor && staticColorSettings[staticColor]
- ? staticColorSettings[staticColor]
- : '';
-
- // If no static color is set, just return the story as-is
- if (!background) {
- return StoryFn(context);
- }
-
- // Wrap the story with the background.
- // Note: withFlexLayout will handle the flex styling if enabled,
- // so we only add justify-content: center here for additional centering.
- return html`
-
- ${StoryFn(context)}
-
- `;
- },
-});
diff --git a/2nd-gen/packages/swc/.storybook/decorators/static-colors.ts b/2nd-gen/packages/swc/.storybook/decorators/static-colors-demo.ts
similarity index 95%
rename from 2nd-gen/packages/swc/.storybook/decorators/static-colors.ts
rename to 2nd-gen/packages/swc/.storybook/decorators/static-colors-demo.ts
index 75d271913ab..f75ed9d2756 100644
--- a/2nd-gen/packages/swc/.storybook/decorators/static-colors.ts
+++ b/2nd-gen/packages/swc/.storybook/decorators/static-colors-demo.ts
@@ -22,8 +22,8 @@ import { styleMap } from 'lit/directives/style-map.js';
*
* Enable per-story by setting parameters.staticColorsDemo = true
*/
-export const staticColors: DecoratorFunction = makeDecorator({
- name: 'staticColors',
+export const withStaticColorsDemo: DecoratorFunction = makeDecorator({
+ name: 'withStaticColorsDemo',
parameterName: 'staticColorsDemo',
wrapper: (StoryFn, context) => {
const { parameters } = context;
diff --git a/2nd-gen/packages/swc/.storybook/decorators/text-direction.ts b/2nd-gen/packages/swc/.storybook/decorators/text-direction.ts
new file mode 100644
index 00000000000..cae88df99e0
--- /dev/null
+++ b/2nd-gen/packages/swc/.storybook/decorators/text-direction.ts
@@ -0,0 +1,28 @@
+import { makeDecorator, useEffect } from '@storybook/preview-api';
+import { fetchContainers } from './helpers.js';
+
+/**
+ * @type import('@storybook/csf').DecoratorFunction
+ * @description Sets the text direction of the document, using the global set with a toolbar control. These properties are assigned to the document root element.
+ **/
+export const withTextDirectionWrapper = makeDecorator({
+ name: 'withTextDirectionWrapper',
+ parameterName: 'textDecoration',
+ wrapper: (StoryFn, context) => {
+ const {
+ globals: { textDirection = 'ltr' } = {},
+ id,
+ viewMode,
+ } = context;
+
+ useEffect(() => {
+ if (!textDirection) return;
+
+ for (const container of fetchContainers(id, viewMode === 'docs')) {
+ container.dir = textDirection;
+ }
+ }, [textDirection]);
+
+ return StoryFn(context);
+ },
+});
diff --git a/2nd-gen/packages/swc/.storybook/guides/contributor-guides/using-the-issue-tracker.mdx b/2nd-gen/packages/swc/.storybook/guides/contributor-guides/using-the-issue-tracker.mdx
index f34fb99234d..17b15fa2b59 100644
--- a/2nd-gen/packages/swc/.storybook/guides/contributor-guides/using-the-issue-tracker.mdx
+++ b/2nd-gen/packages/swc/.storybook/guides/contributor-guides/using-the-issue-tracker.mdx
@@ -1,5 +1,4 @@
import { Meta } from '@storybook/addon-docs/blocks';
-import '@spectrum-web-components/table/elements.js';
@@ -38,118 +37,13 @@ Clear bug reports speed up the triage process, help us replicate the issue, and
Providing the correct issue severity classification helps us adequately assess and prioritize your issue. We reserve the right to adjust the severity of your bug during triage.
Below is our issue severity classification criteria:
-
-
- Severity level
- Description
- Examples
-
-
-
- 🔥 SEV 1
-
- A critical design or functionality issue that breaks the design
- system, causes significant usability problems, or exposes
- critical security vulnerabilities. This issue impacts all users
- and/or essential workflows, making users unable to complete any
- tasks. Requires immediate attention.
-
-
- Broken navigation system, complete unresponsiveness on all
- devices, components not rendering, inaccessible primary actions,
- security vulnerabilities.
-
-
- Accessibility: The end user is not able to complete core tasks
- or activities (e.g., key navigational elements not accessible
- via keyboard, missing or incorrect form labels that prevent
- screen reader users from completing forms or actions, critical
- color contrast issues that prevent users from reading or
- interacting with essential content).
-
-
-
- 🔴 SEV 2
-
- A significant problem affecting the design, usability, or
- functionality of the system or components for a subset of users.
- This issue causes major disruptions and prevent users from
- completing tasks or workflows.
-
-
- Accessibility: Does not conform with WCAG 2.1 Level AA criteria
- and blocks core user tasks (no known workaround).
-
-
- Content that is out of view or unreachable by customers,
- critical buttons or links not functioning, or actions that
- cannot be submitted. Unintentional breaking changes causing
- disruptions in production environments.
-
-
- Accessibility: Issue with a component or controller with
- widespread use and blocks core user tasks (no known workaround).
-
-
-
- 🟠 SEV 3
-
- A design or functionality issue that causes noticeable errors or
- minor usability problems for users which either cause confusion
- or degrade the user experience, but do not prevent task
- completion.
-
-
- Accessibility: Does not conform with WCAG 2.1 Level AA criteria
- and is either non-blocking for core user tasks or blocking for
- non-core user tasks.
-
-
- Misleading labels, inconsistent component behavior, unexpected
- interactions, decreases in system performance.
-
-
- Accessibility: Workarounds are available and discoverable for
- the end user to complete core user tasks, or the end user is not
- able to complete non-core user tasks (e.g., inadequate ARIA
- labels, improper focus management, insufficient color contrast
- for non-critical elements).
-
-
-
- 🟡 SEV 4
-
- A minor design flaw that affects user experience, but doesn't
- have a serious impact on overall usability or functionality.
- This issue does not prevent users from completing tasks.
-
-
- Accessibility: Does not conform with WCAG 2.1 Level AA criteria
- but has lower user impact.
-
-
- Minor visual inconsistencies, non-critical content misalignment,
- or minor layout issues.
-
-
- Accessibility: A WCAG violation is present in areas not
- frequently visited by end users, or it has a lower impact on
- overall accessibility and usability.
-
-
-
- 🟢 SEV 5
-
- A low-level design inconsistency or minor issue that slightly
- affects usability or aesthetics, with minimal impact on users.
-
-
- Slight color deviations, minor typographical errors, or small
- spacing inconsistencies that do not impact functionality.
-
-
-
-
+| Severity level | Description | Examples |
+| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 🔥 SEV 1 | A critical design or functionality issue that breaks the design system, causes significant usability problems, or exposes critical security vulnerabilities. This issue impacts all users and/or essential workflows, making users unable to complete any tasks. Requires immediate attention. | Broken navigation system, complete unresponsiveness on all devices, components not rendering, inaccessible primary actions, security vulnerabilities.
**Accessibility:** The end user is not able to complete core tasks or activities (e.g., key navigational elements not accessible via keyboard, missing or incorrect form labels that prevent screen reader users from completing forms or actions, critical color contrast issues that prevent users from reading or interacting with essential content). |
+| 🔴 SEV 2 | A significant problem affecting the design, usability, or functionality of the system or components for a subset of users. This issue causes major disruptions and prevent users from completing tasks or workflows.
**Accessibility:** Does not conform with WCAG 2.1 Level AA criteria and blocks core user tasks (no known workaround). | Content that is out of view or unreachable by customers, critical buttons or links not functioning, or actions that cannot be submitted. Unintentional breaking changes causing disruptions in production environments.
**Accessibility:** Issue with a component or controller with widespread use and blocks core user tasks (no known workaround). |
+| 🟠 SEV 3 | A design or functionality issue that causes noticeable errors or minor usability problems for users which either cause confusion or degrade the user experience, but do not prevent task completion.
**Accessibility:** Does not conform with WCAG 2.1 Level AA criteria and is either non-blocking for core user tasks or blocking for non-core user tasks. | Misleading labels, inconsistent component behavior, unexpected interactions, decreases in system performance.
**Accessibility:** Workarounds are available and discoverable for the end user to complete core user tasks, or the end user is not able to complete non-core user tasks (e.g., inadequate ARIA labels, improper focus management, insufficient color contrast for non-critical elements). |
+| 🟡 SEV 4 | A minor design flaw that affects user experience, but doesn't have a serious impact on overall usability or functionality. This issue does not prevent users from completing tasks.
**Accessibility:** Does not conform with WCAG 2.1 Level AA criteria but has lower user impact. | Minor visual inconsistencies, non-critical content misalignment, or minor layout issues.
**Accessibility:** A WCAG violation is present in areas not frequently visited by end users, or it has a lower impact on overall accessibility and usability. |
+| 🟢 SEV 5 | A low-level design inconsistency or minor issue that slightly affects usability or aesthetics, with minimal impact on users. | Slight color deviations, minor typographical errors, or small spacing inconsistencies that do not impact functionality. |
## Requesting a feature or a new component
diff --git a/2nd-gen/packages/swc/.storybook/guides/project-planning/workstreams/2nd-gen-component-migration/add-stories-for-2nd-gen-swc-component.mdx b/2nd-gen/packages/swc/.storybook/guides/project-planning/workstreams/2nd-gen-component-migration/add-stories-for-2nd-gen-swc-component.mdx
index 441b4cdbfe0..8fc354f0191 100644
--- a/2nd-gen/packages/swc/.storybook/guides/project-planning/workstreams/2nd-gen-component-migration/add-stories-for-2nd-gen-swc-component.mdx
+++ b/2nd-gen/packages/swc/.storybook/guides/project-planning/workstreams/2nd-gen-component-migration/add-stories-for-2nd-gen-swc-component.mdx
@@ -62,7 +62,17 @@ const meta: Meta = {
render: (args) => template(args),
parameters: {
actions: { handles: events },
- docs: { subtitle: 'Brief component description' },
+ docs: {
+ subtitle: 'Brief component description',
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/1234567890/Component-Name',
+ },
+ stackblitz: {
+ url: 'https://stackblitz.com/edit/swc-component-name',
+ },
+ },
+ flexLayout: 'row-wrap',
},
tags: ['migrated'],
};
@@ -75,9 +85,16 @@ export default meta;
- `title` - Component name as it appears in Storybook sidebar
- `component` - The custom element tag name
- `args`, `argTypes` - From `getStorybookHelpers()`
-- `render` - Template function
+- `render` - Template function from `getStorybookHelpers()`
- `parameters.docs.subtitle` - Brief description shown in docs header
-- `tags: ['migrated']` - Indicates refined format
+
+**Optional meta fields:**
+
+- `parameters.docs.design` - Design documentation for the component. Only add if the component has design documentation.
+- `parameters.docs.stackblitz` - Stackblitz URL for the component. Only add if the component has a Stackblitz example.
+- `parameters.actions.handles` - Event handlers to bind to the component. Only add if the component has event handlers defined in the component's API.
+- `parameters.flexLayout` - Default flex layout to use for the component's stories. Only add if the component has multiple items or elements that need to be aligned.
+- `tags: ['migrated']` - Indicates refined format. Only add if the component has been migrated to the 2nd-gen format.
### Step 3: Add the Playground story
@@ -108,11 +125,28 @@ Document all slots and content-rendering properties. Combine all variations into
// ──────────────────────────
/**
- * A component-name consists of:
+ * ### Visual structure
+ *
+ * A badge consists of:
+ *
+ * 1. **Container** - Colored background with rounded corners
+ * 2. **Label** - Text content describing the status or category (required)
+ * 3. **Icon** (optional) - Visual indicator positioned before the label
+ *
+ * ### Technical structure
+ *
+ * #### Slots
*
- * - **Default slot**: Primary content (text or elements)
- * - **icon slot**: Optional icon element
- * - **label property**: Text label rendered by the component
+ * - **Default slot**: Text label displayed in the badge (required for accessibility)
+ * - **icon slot**: Optional icon element positioned to the left of the label
+ *
+ * #### Properties
+ *
+ * - **Variant**: Color scheme ([semantic](#semantic-variants) or [non-semantic](#non-semantic-variants) variants)
+ * - [**Size**](#sizes): Visual size
+ * - [**Subtle**](#subtle): Boolean for reduced visual prominence
+ * - [**Outline**](#outline): Boolean for bordered style (semantic variants only)
+ * - [**Fixed**](#fixed): Border radius adjustment for edge positioning
*/
export const Anatomy: Story = {
render: (args) => html`
@@ -120,7 +154,6 @@ export const Anatomy: Story = {
${template({ ...args, 'icon-slot': '✓' })}
${template({ ...args, 'default-slot': 'Both', 'icon-slot': '✓' })}
`,
- parameters: { flexLayout: true },
tags: ['anatomy'],
};
```
@@ -180,44 +213,20 @@ export const SemanticVariants: Story = {
**Static color pattern** (if component supports `static-color`):
-Create three stories for components displayed over images or colored backgrounds:
+Create a single story for the static color pattern:
```typescript
-/**
- * Use `static-color` for display over images or colored backgrounds.
- */
-export const StaticBlack: Story = {
- args: { 'static-color': 'black' },
- parameters: {
- flexLayout: false,
- styles: { color: 'black' },
- 'section-order': 10,
- },
- tags: ['options'],
-};
-
-export const StaticWhite: Story = {
- args: { 'static-color': 'white' },
- parameters: {
- flexLayout: false,
- styles: { color: 'white' },
- 'section-order': 11,
- },
- tags: ['options'],
-};
-
export const StaticColors: Story = {
render: (args) => html`
- ${['white', 'black'].map(
+ ${component.STATIC_COLORS.map(
(color) => html`${template({ ...args, 'static-color': color })}`
)}
`,
parameters: {
- flexLayout: true,
staticColorsDemo: true,
- 'section-order': 12,
+ 'section-order': 2,
},
- tags: ['options', '!test'],
+ tags: ['options'],
};
```
@@ -239,8 +248,10 @@ export const States: Story = {
${template({ ...args, selected: true, 'default-slot': 'Selected' })}
${template({ ...args, disabled: true, 'default-slot': 'Disabled' })}
`,
- parameters: { flexLayout: true },
tags: ['states'],
+ parameters: {
+ sectionOrder: 0,
+ },
};
```
@@ -253,6 +264,9 @@ For complex states (animated, indeterminate), create separate stories:
export const Indeterminate: Story = {
args: { indeterminate: true },
tags: ['states'],
+ parameters: {
+ sectionOrder: 1,
+ },
};
```
@@ -270,9 +284,11 @@ Document automatic behaviors (text wrapping, truncation), methods, and events.
*/
export const TextWrapping: Story = {
render: () => html`
-
- Long content that wraps to multiple lines
-
+ ${template({
+ ...args,
+ 'default-slot': 'Long content that wraps to multiple lines',
+ styles: { 'max-inline-size': '120px' },
+ })}
`,
tags: ['behaviors'],
};
@@ -290,39 +306,62 @@ export const TextWrapping: Story = {
/**
* ### Features
*
- * The `` element implements:
+ * The `` element implements several accessibility features:
+ *
+ * #### Color meaning
*
- * 1. **Keyboard navigation**: Tab to focus, Enter to activate
- * 2. **ARIA role**: Automatically sets appropriate role
- * 3. **Color meaning**: Status conveyed through text, not just color
+ * - Colors are used in combination with text labels and/or icons to ensure that status information is not conveyed through color alone
+ * - Users with color vision deficiencies can understand component meaning through text content
+ * - High contrast mode is supported with appropriate color overrides
+ *
+ * #### Non-interactive element
+ *
+ * - Component has no interactive behavior and are not focusable
+ * - Screen readers will announce the component content as static text
+ * - No keyboard interaction is required or expected
*
* ### Best practices
*
- * - Always provide a descriptive text label
- * - Ensure sufficient color contrast
- * - Use semantic variants when status has specific meaning
+ * - Use semantic variants (`positive`, `negative`, `notice`, `informative`, `neutral`, `accent`) when the status has specific meaning
+ * - Include clear, descriptive labels that explain the status without relying on color alone
+ * - For icon-only components, provide descriptive text in the default slot or use the `aria-label` attribute directly on the element
+ * - Ensure sufficient color contrast between the badge and its background
+ * - Components are not interactive elements - for interactive status indicators, consider using buttons, tags, or links instead
+ * - When using multiple components together, ensure they're clearly associated with their related content
+ * - Use consistent component variants across your application for the same statuses
+ * - Test with screen readers to verify component content is announced in context
+ * - Consider placement carefully - components should be close to the content they describe
*/
export const Accessibility: Story = {
render: () => html`
- Approved
- Rejected
+ ${template({
+ ...args,
+ variant: 'positive',
+ 'default-slot': 'Approved',
+ })}
+ ${template({
+ ...args,
+ variant: 'negative',
+ 'default-slot': 'Rejected',
+ })}
`,
tags: ['a11y'],
};
```
-### Step 9: Add helper functions (if needed)
+### Step 9: Add helpers (if needed)
Place utility functions at the bottom.
```typescript
// ────────────────────────
-// HELPER FUNCTIONS
+// HELPERS
// ────────────────────────
+const sizeLabels = { s: 'Small', m: 'Medium', l: 'Large', xl: 'Extra-large' };
+
function sizeLabel(size: string): string {
- const labels = { s: 'Small', m: 'Medium', l: 'Large', xl: 'Extra-large' };
- return labels[size] || size;
+ return sizeLabels[size as keyof typeof sizeLabels] || size;
}
```
@@ -332,27 +371,6 @@ function sizeLabel(size: string): string {
Create `stories/[component].usage.mdx` to display stories organized by section.
-```mdx
-import { SpectrumStories } from '../../../.storybook/blocks';
-
-## Installation
-
-Install the component:
-
-\`\`\`bash
-npm install @adobe/swc
-\`\`\`
-
-Import and use:
-
-\`\`\`javascript
-import '@adobe/swc/component-name';
-\`\`\`
-
-\`\`\`html
-Content
-\`\`\`
-
## Anatomy
@@ -387,7 +405,7 @@ import '@adobe/swc/component-name';
### JSDoc comments
-Every story **except Playground** needs a JSDoc comment explaining:
+Every story **except Playground AND Overview** needs a JSDoc comment explaining:
- What it demonstrates
- Usage guidance or best practices
@@ -406,11 +424,11 @@ All stories must demonstrate accessible usage:
### Layout
-Use `flexLayout: true` for stories showing multiple items:
+Use `flexLayout: 'row-wrap' | 'column-stretch' | 'row-nowrap' | 'column-center'` for stories showing multiple items or elements that need to be aligned:
```typescript
parameters: {
- flexLayout: true;
+ flexLayout: 'row-wrap';
}
```
@@ -418,8 +436,8 @@ Customize with `styles`:
```typescript
parameters: {
- flexLayout: true,
- styles: { 'flex-wrap': 'wrap', 'max-inline-size': '80ch' }
+ flexLayout: 'row-wrap',
+ styles: { alignItems: 'flex-start' }
}
```
@@ -428,6 +446,7 @@ parameters: {
| Tag | Usage |
| --------------------- | --------------------------------------- |
| `'autodocs'`, `'dev'` | Playground story only |
+| `'overview'` | Overview story only |
| `'anatomy'` | Anatomy stories |
| `'options'` | Options stories (sizes, variants, etc.) |
| `'states'` | States stories |
@@ -465,20 +484,15 @@ Always use static image IDs to ensure visual regression tests (VRTs) produce con
**Example usage** (from `asset.stories.ts`):
```typescript
-const anatomyArgs = [
- {
- variant: 'file',
- label: 'README.md',
- },
- {
- label: 'User avatar',
- 'default-slot': ``,
- },
-];
-
export const Anatomy: Story = {
render: (args) => html`
- ${anatomyArgs.map((arg) => template({ ...args, ...arg }))}
+ ${template({ ...args, variant: 'file', label: 'README.md' })}
+ ${template({ ...args, variant: 'folder', label: 'packages/swc/' })}
+ ${template({
+ ...args,
+ label: 'User avatar',
+ 'default-slot': ``,
+ })}
`,
tags: ['anatomy'],
};
@@ -494,11 +508,12 @@ Before submitting:
- [ ] Visual separators between all sections
- [ ] Meta object has all required fields including `subtitle` and `tags: ['migrated']`
- [ ] Playground story has `['autodocs', 'dev']` tags and no JSDoc
-- [ ] Anatomy story documents slots and content properties
+- [ ] Overview story has `['overview']` tags and no JSDoc
+- [ ] Anatomy story documents visual and technical structure
- [ ] Options stories cover all attributes not in other sections
-- [ ] Stories use `flexLayout: true` when showing multiple items
+- [ ] Stories use `flexLayout: 'row-wrap' | 'column-stretch' | 'row-nowrap' | 'column-center'` when showing multiple items or elements that need to be aligned
- [ ] Story order controlled with `section-order` parameter
-- [ ] All stories have JSDoc comments (except Playground)
+- [ ] All stories have JSDoc comments (except Playground and Overview)
- [ ] All stories are accessible with meaningful content
- [ ] Accessibility story documents features and best practices
- [ ] Image assets use `picsum.photos` with static IDs for VRT consistency (if applicable)
diff --git a/2nd-gen/packages/swc/.storybook/helpers/README.md b/2nd-gen/packages/swc/.storybook/helpers/README.md
index 945ad1c7f79..cbee2ac2c13 100644
--- a/2nd-gen/packages/swc/.storybook/helpers/README.md
+++ b/2nd-gen/packages/swc/.storybook/helpers/README.md
@@ -2,18 +2,52 @@
Reusable utilities for creating consistent Storybook stories across components.
-## Available helpers
+## formatComponentName
-### `formatComponentName`
+Formats a Storybook story title into a component name in either kebab-case or PascalCase.
-Formats a component tag name for display (e.g., `swc-badge` → `Badge`).
+### Usage
+
+Import the helper in your stories file:
```typescript
import { formatComponentName } from '../../../.storybook/helpers/index.js';
+```
+
+### Parameters
+
+- `title` (string): The Storybook story title (e.g., `'Components/Button'`)
+- `typeCase` ('kebab' | 'pascal'): The desired output format (defaults to `'kebab'`)
-const displayName = formatComponentName('swc-badge'); // "Badge"
+### Examples
+
+```typescript
+// Get kebab-case component name (default)
+formatComponentName('Components/Action Button');
+// Returns: 'action-button'
+
+// Get PascalCase component name
+formatComponentName('Components/Action Button', 'pascal');
+// Returns: 'ActionButton'
+
+// Typical usage in stories
+export default {
+ title: 'Components/Progress Circle',
+ component: 'swc-progress-circle',
+ // ...
+} as Meta;
+
+const componentName = formatComponentName(meta.title);
+// Returns: 'progress-circle'
```
+### What it does
+
+1. Extracts the component name from the last segment of the story title path
+2. Converts it to lowercase
+3. Replaces spaces with hyphens for kebab-case
+4. Optionally converts to PascalCase by capitalizing each word and removing hyphens
+
## Adding new helpers
When adding new helpers to this directory:
diff --git a/2nd-gen/packages/swc/.storybook/learn-about-swc/first-gen-vs-second-gen.mdx b/2nd-gen/packages/swc/.storybook/learn-about-swc/first-gen-vs-second-gen.mdx
index 812bb8aac20..c5a9261b3af 100644
--- a/2nd-gen/packages/swc/.storybook/learn-about-swc/first-gen-vs-second-gen.mdx
+++ b/2nd-gen/packages/swc/.storybook/learn-about-swc/first-gen-vs-second-gen.mdx
@@ -1,9 +1,11 @@
import { Meta } from '@storybook/addon-docs/blocks';
-import '@spectrum-web-components/table/elements.js';
+import headerImage from '../assets/images/banner_introduction_S2_desktop_2x_1689964089087.png';
import '@spectrum-web-components/accordion/sp-accordion.js';
import '@spectrum-web-components/accordion/sp-accordion-item.js';
-
+
+
+
# 1st-gen vs 2nd-gen
@@ -11,66 +13,19 @@ SWC 2nd-gen is the next version of Spectrum Web Components. It updates the desig
## Key differences
-
-
- Area
- 1st-gen SWC
- 2nd-gen SWC
-
-
-
- Design
- Matches Spectrum 1
- Updated for Spectrum 2 design
-
-
- Architecture
-
- Earlier component patterns; less consistent
-
-
- More consistent structure and APIs across the library
-
-
-
- Tokens and scales
- Based on Spectrum 1 tokens
-
- Updated tokens, scales, and interaction patterns
-
-
-
- Styling system
- Older styling approach
-
- Modernized styling aligned with Spectrum 2
-
-
-
- Accessibility
- Basic support
-
- Improved and more complete accessibility coverage
-
-
-
- Performance
- Larger packages and more overhead
-
- Smaller packages and better runtime performance
-
-
-
- Long-term support
- Maintained for existing products
- Recommended for all new work
-
-
-
+| Area | 1st-gen SWC | 2nd-gen SWC |
+| ----------------- | ------------------------------------------- | ----------------------------------------------------- |
+| Design | Matches Spectrum 1 | Updated for Spectrum 2 design |
+| Architecture | Earlier component patterns; less consistent | More consistent structure and APIs across the library |
+| Tokens and scales | Based on Spectrum 1 tokens | Updated tokens, scales, and interaction patterns |
+| Styling system | Older styling approach | Modernized styling aligned with Spectrum 2 |
+| Accessibility | Basic support | Improved and more complete accessibility coverage |
+| Performance | Larger packages and more overhead | Smaller packages and better runtime performance |
+| Long-term support | Maintained for existing products | Recommended for all new work |
## What this means for teams
-
+
Use 2nd-gen SWC. It matches the current Spectrum guidelines and offers
better performance and consistency.
diff --git a/2nd-gen/packages/swc/.storybook/learn-about-swc/overview.mdx b/2nd-gen/packages/swc/.storybook/learn-about-swc/overview.mdx
index 97108adf3ec..5f0aa9879c2 100644
--- a/2nd-gen/packages/swc/.storybook/learn-about-swc/overview.mdx
+++ b/2nd-gen/packages/swc/.storybook/learn-about-swc/overview.mdx
@@ -1,7 +1,10 @@
import { Meta } from '@storybook/addon-docs/blocks';
+import headerImage from '../assets/images/bg.e23742a5.png';
+
+
# Overview
Spectrum Web Components (SWC) is Adobe’s version of the Spectrum design system using modern [web components](https://developer.mozilla.org/docs/Web/Web_Components) built with [Lit](https://lit.dev/). The 2nd-gen release updates the architecture, aligns components with [Spectrum 2](https://s2.spectrum.adobe.com/), and improves performance.
@@ -10,9 +13,9 @@ SWC gives you UI components that work in many environments. You can use them in
This page gives you a simple overview of what SWC is and how the 2nd-gen version is different.
-## What is Spectrum Web Components (SWC)?
+## What is Spectrum Web Components
-SWC is a set of reusable UI components built with Lit. These components follow Spectrum design and interaction patterns so Adobe products can share a consistent look and feel.
+Spectrum Web Components (SWC) is a set of reusable UI components built with Lit. These components follow Spectrum design and interaction patterns so Adobe products can share a consistent look and feel.
Each component includes:
@@ -25,7 +28,7 @@ SWC components are published as individual packages. You can install only the co
Using SWC helps teams build Spectrum-aligned interfaces without creating new code for every framework.
-## What’s new in 2nd-gen SWC
+## What is new in 2nd-gen SWC
The 2nd-gen version builds on the work from 1st-gen SWC. It includes updates for Spectrum 2 along with improvements that make the system more consistent and easier to maintain.
diff --git a/2nd-gen/packages/swc/.storybook/learn-about-swc/when-to-use-swc.mdx b/2nd-gen/packages/swc/.storybook/learn-about-swc/when-to-use-swc.mdx
index e4c0e17a621..fafe0f4cf63 100644
--- a/2nd-gen/packages/swc/.storybook/learn-about-swc/when-to-use-swc.mdx
+++ b/2nd-gen/packages/swc/.storybook/learn-about-swc/when-to-use-swc.mdx
@@ -1,64 +1,62 @@
import { Meta } from '@storybook/addon-docs/blocks';
+import headerImage from '../assets/images/banner_principles_S2_desktop_2x_1689964204945.png';
import '@spectrum-web-components/accordion/sp-accordion.js';
import '@spectrum-web-components/accordion/sp-accordion-item.js';
-
+
+
+
# When to use SWC
Spectrum Web Components (SWC) is a good choice when you want to build interfaces that match the Spectrum design system and can run in many different environments. This page explains when SWC is the right tool and when you may need extra support or a different approach.
-## Use SWC when:
+## SWC is a good choice when
-
+
- SWC gives you ready-made components that follow Spectrum design and
- behavior. Use them when your project must look and feel like other Adobe
- experiences.
+ SWC gives you ready-made components that follow Spectrum design and behavior. Use them when your project must look and feel like other Adobe experiences.
+
SWC works in plain HTML, JavaScript, or TypeScript. It also works with
- frameworks like React through wrapper packages. This makes it a good
- option for teams using mixed technologies. Use 2nd-gen SWC. It matches
- the current Spectrum guidelines and offers better performance and
- consistency.
+ frameworks like React through wrapper packages. This makes it a good option
+ for teams using mixed technologies.
+
- Because SWC components share a common architecture and API, they behave
- the same across all projects. This reduces the amount of one-off code
- your team needs to maintain. Use 2nd-gen SWC. It matches the current
- Spectrum guidelines and offers better performance and consistency.
+ Because SWC components share a common architecture and API, they behave the
+ same across all projects. This reduces the amount of one-off code your team
+ needs to maintain.
+
- SWC includes accessibility features that follow Spectrum and web
- standards. This helps teams meet accessibility requirements without
- writing them from scratch. Use 2nd-gen SWC. It matches the current
- Spectrum guidelines and offers better performance and consistency.
+ SWC includes accessibility features that follow Spectrum and web standards.
+ This helps teams meet accessibility requirements without writing them from
+ scratch.
+
- Since SWC ships as individual packages, you can install only the
- components you need. This keeps your project lighter and easier to
- maintain.
+ Since SWC ships as individual packages, you can install only the components you need. This keeps your project lighter and easier to maintain.
+
-## SWC may not be enough on its own when:
+## SWC may not be enough on its own
-
+
- If your app uses advanced React, Vue, or Angular patterns, you might
- need extra setup or wrapper components. SWC can still work, but you may
- need additional engineering support.
+ If your app uses advanced React, Vue, or Angular patterns, you might need extra setup or wrapper components. SWC can still work, but you may need additional engineering support.
+
- SWC is built to match Spectrum. If your product uses patterns that do
- not follow Spectrum, you may need custom components or additional
- styling.
+ SWC is built to match Spectrum. If your product uses patterns that do not follow Spectrum, you may need custom components or additional styling.
-
- SWC 2nd-gen changes some APIs and patterns. Large migrations may require
- planning, refactoring, or testing before you switch fully.
+
+
+ SWC 2nd-gen changes some APIs and patterns. Large migrations may require planning, refactoring, or testing before you switch fully.
+
## Questions to help you decide
@@ -69,8 +67,8 @@ Spectrum Web Components (SWC) is a good choice when you want to build interfaces
- Does your project need built-in accessibility support?
- Are you using only a small set of components or the full library?
-If you answered “yes” to most of these, SWC is likely a good fit.
+**If you answered “yes” to most of these, SWC is likely a good fit.**
-## Still not sure?
+### Still not sure?
Check the examples in the component documentation or explore how other Adobe teams use SWC. You can also reach out to the SWC team if you need help deciding whether SWC is the right fit for your project.
diff --git a/2nd-gen/packages/swc/.storybook/main.ts b/2nd-gen/packages/swc/.storybook/main.ts
index 0fdef779962..86ee8aa25dc 100644
--- a/2nd-gen/packages/swc/.storybook/main.ts
+++ b/2nd-gen/packages/swc/.storybook/main.ts
@@ -16,7 +16,7 @@ const config = {
{
directory: 'learn-about-swc',
files: '*.mdx',
- titlePrefix: 'About SWC',
+ titlePrefix: 'Learn about SWC',
},
{
directory: 'guides',
diff --git a/2nd-gen/packages/swc/.storybook/manager.ts b/2nd-gen/packages/swc/.storybook/manager.ts
index e8a717ef544..b31ffaea665 100644
--- a/2nd-gen/packages/swc/.storybook/manager.ts
+++ b/2nd-gen/packages/swc/.storybook/manager.ts
@@ -16,17 +16,10 @@ addons.setConfig({
base: 'light',
brandTitle: 'Adobe | Spectrum Web Components',
- brandUrl: 'https://opensource.adobe.com/spectrum-web-components', // TODO: Add the correct URL once we are publishing 2nd-gen
+ brandUrl: '?path=/docs/about-swc-overview--readme', // TODO: Add the correct URL once we are publishing 2nd-gen
brandImage: logo,
brandTarget: '_self',
- typography: {
- fonts: {
- base: 'adobe-clean, "Adobe Clean", "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Trebuchet MS", "Lucida Grande", sans-serif',
- code: '"Source Code Pro", Monaco, monospace',
- },
- },
-
// colorPrimary: "#7326d3",
colorSecondary: 'rgb(2, 101, 220)',
diff --git a/2nd-gen/packages/swc/.storybook/preview-head.html b/2nd-gen/packages/swc/.storybook/preview-head.html
index cd8c4bb514a..5988a28f2f2 100644
--- a/2nd-gen/packages/swc/.storybook/preview-head.html
+++ b/2nd-gen/packages/swc/.storybook/preview-head.html
@@ -1,4 +1,17 @@
-
+
+
+
+
+
+
+
+Adobe | Spectrum Web Components
+
+
+
+
+
+
@@ -67,13 +80,18 @@
p,
li,
ul,
- ol,
+ ol {
+ font-size: 1rem !important;
+ }
+
table,
input,
select,
textarea,
- nav {
- font-size: 1rem !important;
+ nav,
+ sp-table *,
+ .toc-list-item {
+ font-size: 0.875rem !important;
}
h1 {
@@ -81,15 +99,15 @@
}
h2:not(.sbdocs-subtitle) {
- font-size: var(--spectrum-heading-size-xl) !important;
+ font-size: var(--spectrum-heading-size-l) !important;
}
h3 {
- font-size: var(--spectrum-heading-size-l) !important;
+ font-size: var(--spectrum-heading-size-m) !important;
}
h4 {
- font-size: var(--spectrum-heading-size-m) !important;
+ font-size: var(--spectrum-heading-size-s) !important;
}
#stories,
@@ -103,7 +121,6 @@
position: relative;
color: rgb(46, 52, 56);
border-bottom: 1px solid rgba(38, 85, 115, 0.15);
- text-transform: capitalize !important;
letter-spacing: unset;
line-height: unset;
}
@@ -111,8 +128,9 @@
.sbdocs-toc--custom {
width: 20rem !important;
nav {
- width: 20rem !important;
- font-size: 1rem !important;
+ width: auto;
+ max-width: 15rem !important;
+ font-size: 0.75rem !important;
}
}
@@ -120,8 +138,35 @@
color: rgb(2, 101, 220) !important;
}
+ .sbdocs-wrapper {
+ justify-content: space-between !important;
+ }
+
.sbdocs-content {
- max-width: calc(1000px - 20rem) !important;
+ max-width: 80ch !important;
+ margin: auto !important;
+ }
+
+ .docs-image {
+ background-size: 100% 100%;
+ border-radius: 1rem;
+ background-position: center;
+ background-repeat: no-repeat;
+ width: 100%;
+ max-height: 13rem;
+ }
+
+ .docs-accordion {
+ border-color: 1px solid rgba(38, 85, 115, 0.15) !important;
+
+ sp-accordion-item:first-child {
+ border-block-start: none !important;
+ }
+
+ sp-accordion-item {
+ border-color: 1px solid rgba(38, 85, 115, 0.15) !important;
+ border-block-end: 1px solid rgba(38, 85, 115, 0.15) !important;
+ }
}
/* Fix for Storybook's zoom wrapper interfering with forced-colors mode */
diff --git a/2nd-gen/packages/swc/.storybook/preview.ts b/2nd-gen/packages/swc/.storybook/preview.ts
index 3cc74a03483..2e657dde85e 100644
--- a/2nd-gen/packages/swc/.storybook/preview.ts
+++ b/2nd-gen/packages/swc/.storybook/preview.ts
@@ -11,13 +11,14 @@ import {
setStorybookHelpersConfig,
type Options,
} from '@wc-toolkit/storybook-helpers';
-import { FontLoader } from './loaders/font-loader';
import customElements from './custom-elements.json';
import {
- withStaticColorBackground,
- staticColors,
withFlexLayout,
+ withStaticColorsDemo,
+ withTextDirectionWrapper,
} from './decorators';
+import { FontLoader } from './loaders/font-loader';
+import { globalTypes } from './types';
const storybookHelperOptions: Options = {
categoryOrder: [
@@ -39,7 +40,11 @@ setStorybookHelpersConfig(storybookHelperOptions);
setCustomElementsManifest(customElements);
const preview = {
- decorators: [withStaticColorBackground, staticColors, withFlexLayout],
+ decorators: [
+ withStaticColorsDemo,
+ withFlexLayout,
+ withTextDirectionWrapper,
+ ],
parameters: {
layout: 'centered',
controls: {
@@ -82,21 +87,52 @@ const preview = {
},
canvas: {
withToolbar: true,
- sourceState: 'shown',
layout: 'centered',
+ sourceState: 'shown',
},
source: {
excludeDecorators: true,
- type: 'dynamic',
+ type: 'auto',
language: 'html',
+ transform: async (source: string) => {
+ try {
+ const prettier = await import('prettier/standalone');
+ const prettierPluginHtml = await import(
+ 'prettier/plugins/html'
+ );
+ const prettierPluginBabel = await import(
+ 'prettier/plugins/babel'
+ );
+ const prettierPluginEstree = await import(
+ 'prettier/plugins/estree'
+ );
+
+ return prettier.format(source, {
+ parser: 'html',
+ plugins: [
+ prettierPluginHtml.default,
+ prettierPluginBabel.default,
+ prettierPluginEstree.default,
+ ],
+ tabWidth: 2,
+ useTabs: false,
+ singleQuote: true,
+ printWidth: 80,
+ });
+ } catch (error) {
+ // If formatting fails, return the original source
+ console.error('Failed to format source code:', error);
+ return source;
+ }
+ },
},
},
options: {
storySort: {
method: 'alphabetical-by-kind',
order: [
- 'About SWC',
- ['Overview', 'When to use SWC', 'First Gen vs Second Gen'],
+ 'Learn about SWC',
+ ['Overview', 'When to use SWC', '1st-gen vs 2nd-gen'],
'Components',
'Guides',
[
@@ -156,6 +192,7 @@ const preview = {
},
tags: ['!autodocs', '!dev'], // We only want the playground stories to be visible in the docs and sidenav. Since a majority of our stories are tagged with '!autodocs' and '!dev', we set those tags globally. We can opt in to visibility by adding the 'autodocs' or 'dev' tags to individual stories.
loaders: [FontLoader],
+ globalTypes,
};
export default preview;
diff --git a/2nd-gen/packages/swc/.storybook/types/global.js b/2nd-gen/packages/swc/.storybook/types/global.js
new file mode 100644
index 00000000000..a2bda7d8f8d
--- /dev/null
+++ b/2nd-gen/packages/swc/.storybook/types/global.js
@@ -0,0 +1,21 @@
+/**
+ * Rendered as controls; these properties are assigned to the document root element
+ */
+
+/** @type import('@storybook/types').GlobalTypes */
+export default {
+ textDirection: {
+ title: 'Text direction',
+ description: 'Direction of the content flow',
+ defaultValue: 'ltr',
+ type: 'string',
+ toolbar: {
+ icon: 'transfer',
+ items: [
+ { value: 'ltr', title: 'Left to right' },
+ { value: 'rtl', title: 'Right to left' },
+ ],
+ dynamicTitle: true,
+ },
+ },
+};
diff --git a/2nd-gen/packages/swc/.storybook/types/index.js b/2nd-gen/packages/swc/.storybook/types/index.js
new file mode 100644
index 00000000000..a0c98c2fb7d
--- /dev/null
+++ b/2nd-gen/packages/swc/.storybook/types/index.js
@@ -0,0 +1,18 @@
+/*!
+ * Copyright 2024 Adobe. All rights reserved.
+ *
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+import globalTypes from './global.js';
+
+export * from './states.js';
+export * from './variants.js';
+export { globalTypes };
diff --git a/2nd-gen/packages/swc/.storybook/types/states.js b/2nd-gen/packages/swc/.storybook/types/states.js
new file mode 100644
index 00000000000..e8f23c93d41
--- /dev/null
+++ b/2nd-gen/packages/swc/.storybook/types/states.js
@@ -0,0 +1,160 @@
+// Optional controls for components to leverage in their stories
+export const isOpen = {
+ name: 'Open',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isSelected = {
+ name: 'Selected',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isValid = {
+ name: 'Valid',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isInvalid = {
+ name: 'Invalid',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isFocused = {
+ name: 'Focused',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isHovered = {
+ name: 'Hovered',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isActive = {
+ name: 'Active',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isKeyboardFocused = {
+ name: 'Keyboard Focused',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isLoading = {
+ name: 'Loading',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isIndeterminate = {
+ name: 'Indeterminate',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isDisabled = {
+ name: 'Disabled',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isDragged = {
+ name: 'Dragging',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isRequired = {
+ name: 'Required',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isReadOnly = {
+ name: 'Read-only',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
+
+export const isChecked = {
+ name: 'Checked',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: { type: 'boolean' },
+};
+
+export const isPending = {
+ name: 'Pending',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'State',
+ },
+ control: 'boolean',
+};
diff --git a/2nd-gen/packages/swc/.storybook/types/variants.js b/2nd-gen/packages/swc/.storybook/types/variants.js
new file mode 100644
index 00000000000..f14f6ebd648
--- /dev/null
+++ b/2nd-gen/packages/swc/.storybook/types/variants.js
@@ -0,0 +1,68 @@
+/**
+ *
+ * @param {("xxs"|"xs"|"s"|"m"|"l"|"xl"|"xxl"|"xxxl"|number)[]} options
+ * @returns
+ */
+export const size = (options = ['s', 'm', 'l', 'xl'], hasLabels = true) => ({
+ name: 'Size',
+ type: { name: 'string', required: true },
+ table: {
+ type: { summary: 'string' },
+ category: 'Component',
+ },
+ options,
+ control: {
+ type: 'select',
+ labels: hasLabels
+ ? {
+ xxs: 'Extra-extra-small',
+ xs: 'Extra-small',
+ s: 'Small',
+ m: 'Medium',
+ l: 'Large',
+ xl: 'Extra-large',
+ xxl: 'Extra-extra-large',
+ xxxl: 'Extra-extra-extra-large',
+ }
+ : undefined,
+ },
+});
+
+export const isEmphasized = {
+ name: 'Emphasized styling',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'Component',
+ },
+ control: { type: 'boolean' },
+};
+
+export const isQuiet = {
+ name: 'Quiet styling',
+ type: { name: 'boolean' },
+ table: {
+ type: { summary: 'boolean' },
+ category: 'Component',
+ },
+ control: { type: 'boolean' },
+};
+
+export const staticColor = {
+ name: 'Static color',
+ description:
+ 'Used when component is layered over a background or visual contrary to the general theme.',
+ type: { name: 'string' },
+ table: {
+ type: { summary: 'string' },
+ category: 'Advanced',
+ },
+ options: ['white', 'black'],
+ control: {
+ type: 'select',
+ labels: {
+ white: 'Over dark background',
+ black: 'Over light background',
+ },
+ },
+};
diff --git a/2nd-gen/packages/swc/cem.config.js b/2nd-gen/packages/swc/cem.config.js
index 250f36762ea..d2e32d18620 100644
--- a/2nd-gen/packages/swc/cem.config.js
+++ b/2nd-gen/packages/swc/cem.config.js
@@ -11,7 +11,11 @@
*/
export default {
- globs: ['components/**/*.ts', '../core/components/**/*.ts'],
+ globs: [
+ 'components/**/*.ts',
+ '../core/components/**/*.ts',
+ '../core/shared/**/*.ts',
+ ],
exclude: ['**/*.stories.ts', '**/*.test.ts', '**/*.spec.ts'],
outdir: '.storybook',
litelement: true,
diff --git a/2nd-gen/packages/swc/components/asset/DOC-COMPARISON.md b/2nd-gen/packages/swc/components/asset/DOC-COMPARISON.md
deleted file mode 100644
index 46c38a296e8..00000000000
--- a/2nd-gen/packages/swc/components/asset/DOC-COMPARISON.md
+++ /dev/null
@@ -1,161 +0,0 @@
-# Asset: 1st-gen vs 2nd-gen documentation comparison
-
-## Summary
-
-The 2nd-gen documentation covers all the essential features of the asset component. The 1st-gen documentation is quite minimal, and 2nd-gen actually expands on it in several areas. Overall, this is a successful migration with enhancements.
-
-## Missing content areas
-
-### Nice-to-have additions
-
-1. **Package metadata**
- - **1st-gen**: Includes NPM version, bundle size, and Stackblitz badges
- - **2nd-gen**: No package metadata
- - **Priority**: Low
- - **Recommendation**: Consider adding if useful for developer reference.
-
-2. **Installation instructions**
- - **1st-gen**: Shows yarn add command and import statements in dedicated section
- - **2nd-gen**: Missing installation section
- - **Priority**: Low (may be intentional - centralized elsewhere)
- - **Recommendation**: If not documented globally, consider adding brief usage section.
-
-3. **Height styling example**
- - **1st-gen**: Shows `style="height: 128px"` in example
- - **2nd-gen**: No mention of height styling
- - **Priority**: Low
- - **Recommendation**: Consider adding note about sizing/height control in behaviors or options section.
-
-## Content depth differences
-
-### Description/overview
-
-**1st-gen**:
-
-- Provides detailed description: "File and folder representations will center themselves horizontally and vertically... Images will be contained to the element, growing to the element's full height..."
-- Clear behavioral expectations
-
-**2nd-gen**:
-
-- Uses same description in JSDoc
-- Better organized into structured sections
-- More concise in presentation
-
-**Winner**: Tie - both communicate well, different styles
-
-### Anatomy
-
-**1st-gen**:
-
-- Minimal anatomy description
-- Just shows basic examples
-
-**2nd-gen advantage**:
-
-- Structured anatomy section
-- Clear bullet points explaining parts
-- Demonstrates image use case visually
-- Better organized
-
-**Winner**: 2nd-gen significantly better
-
-### Examples/variants
-
-**1st-gen**:
-
-- Shows file and folder variants
-- Shows both with and without labels
-- Uses CSS flexbox demo container
-
-**2nd-gen**:
-
-- Shows file and folder as separate option stories
-- Has dedicated anatomy story
-- Each story has clear JSDoc explanation
-
-**Winner**: 2nd-gen - better organization and discoverability
-
-### Accessibility
-
-**1st-gen**:
-
-- No accessibility section at all
-
-**2nd-gen advantage**:
-
-- Dedicated accessibility section
-- Documents labeling feature
-- Provides best practices
-- Notes when labels are optional (decorative)
-
-**Winner**: 2nd-gen significantly better
-
-## Areas where 2nd-gen improves on 1st-gen
-
-1. **Better structure**: Clear separation of anatomy, options, and accessibility
-2. **Accessibility documentation**: 1st-gen has none, 2nd-gen has comprehensive section
-3. **Story organization**: Better discoverability and testability
-4. **JSDoc comments**: Each story has clear explanation
-5. **Anatomy section**: More detailed explanation of component parts
-
-## Recommendations by priority
-
-### High priority
-
-None - 2nd-gen covers all critical functionality
-
-### Medium priority
-
-1. **Add sizing guidance** - Mention that assets can be sized with CSS height/width
-2. **Add behavioral note** about how images vs icons are displayed differently
-
-### Low priority
-
-3. Consider adding package metadata badges
-4. Add installation/usage section if not documented globally
-5. Consider adding more complex examples (multiple assets in a grid/list)
-
-## Content quality assessment
-
-| Area | 1st-gen | 2nd-gen | Notes |
-| ----------------- | ----------------- | ----------------- | --------------------------------------- |
-| **Installation** | ✅ Documented | ❌ Missing | May be intentional |
-| **Anatomy** | ⚠️ Minimal | ✅ Comprehensive | 2nd-gen much better |
-| **Variants** | ✅ Both shown | ✅ Both shown | Both adequate, 2nd-gen better organized |
-| **Labeling** | ✅ Examples shown | ✅ Examples shown | Both show with/without labels |
-| **Sizing** | ✅ Height example | ⚠️ Not mentioned | Minor gap |
-| **Accessibility** | ❌ Not documented | ✅ Comprehensive | 2nd-gen major improvement |
-| **Behaviors** | ✅ Described | ✅ Described | Both explain centering/containment |
-
-## Overall assessment
-
-**Strengths of 2nd-gen migration**:
-
-- Significantly better accessibility documentation
-- Much more organized structure
-- Clear anatomy section with explanations
-- Better story organization for testing
-- Comprehensive JSDoc comments
-
-**Strengths of 1st-gen preserved**:
-
-- All variant types maintained
-- Behavioral expectations preserved
-- Label flexibility maintained
-
-**Minor gaps**:
-
-- Missing sizing/height guidance (low priority)
-- No installation section (may be intentional)
-
-**Completeness score**: 95% - This is actually an improvement over 1st-gen in most areas. The 2nd-gen documentation is more complete and better organized than the original.
-
-## Recommendation
-
-This component migration is exemplary. The 2nd-gen documentation not only preserves all essential content from 1st-gen but significantly enhances it with:
-
-- Proper accessibility documentation
-- Better structure and organization
-- Clear explanations for each feature
-
-Only minor enhancements suggested (sizing guidance), but not critical.
diff --git a/2nd-gen/packages/swc/components/asset/stories/asset.stories.ts b/2nd-gen/packages/swc/components/asset/stories/asset.stories.ts
index 1e8747c112e..beef8e0ffbf 100644
--- a/2nd-gen/packages/swc/components/asset/stories/asset.stories.ts
+++ b/2nd-gen/packages/swc/components/asset/stories/asset.stories.ts
@@ -20,7 +20,7 @@ import '@adobe/swc/asset';
// METADATA
// ────────────────
-const { events, args, argTypes, template } = getStorybookHelpers('swc-asset');
+const { args, argTypes, template } = getStorybookHelpers('swc-asset');
argTypes.variant = {
...argTypes.variant,
@@ -28,10 +28,6 @@ argTypes.variant = {
options: [undefined, 'file', 'folder'],
};
-argTypes.label = {
- control: { type: 'text' },
-};
-
// since we cant't use HTML templates in a slot control,
// we need to use a select option and render a predefined HTML template based on the selected option
argTypes['default-slot'] = {
@@ -40,9 +36,8 @@ argTypes['default-slot'] = {
};
/**
- * Use an `` element to visually represent a file, folder or image in your application.
- * File and folder representations will center themselves horizontally and vertically in the space provided to the element.
- * Images will be contained to the element, growing to the element's full height while centering itself within the width provided.
+ * The `file` and `folder` variants center themselves horizontally and vertically in the space provided.
+ * Images are contained within the element, growing to the element's full height while centering within the width provided.
*/
const meta: Meta = {
title: 'Asset',
@@ -51,12 +46,10 @@ const meta: Meta = {
argTypes,
render: (args) => template(args),
parameters: {
- actions: {
- handles: events,
- },
docs: {
- subtitle: `Assets visually represent a file, folder or image in your application.`,
+ subtitle: `Visually represent files, folders, or images in your application`,
},
+ flexLayout: 'row-nowrap',
},
tags: ['migrated'],
};
@@ -68,7 +61,6 @@ export default meta;
// ────────────────────
export const Playground: Story = {
- render: (args) => template(args),
args: {
label: 'Background',
'default-slot': ``,
@@ -76,34 +68,45 @@ export const Playground: Story = {
tags: ['autodocs', 'dev'],
};
+// ────────────────────
+// OVERVIEW STORY
+// ────────────────────
+
+export const Overview: Story = {
+ args: {
+ label: 'Background',
+ 'default-slot': ``,
+ },
+ tags: ['overview'],
+};
+
// ──────────────────────────
// ANATOMY STORIES
// ──────────────────────────
-const anatomyArgs = [
- {
- variant: 'file',
- label: 'packages/swc/',
- },
- {
- label: 'Avatar',
- 'default-slot': ``,
- },
-];
/**
- * An asset is made up of the following parts:
+ * An asset consists of:
*
- * - A large file or folder icon based on the asset `variant`
- * - An accessible label for the asset
- * - Optional content to be displayed in the asset when an acceptable value for `variant` is not present
+ * 1. **Icon or image content** - Either a file/folder icon or custom slotted content
+ * 2. **Accessible label** - Provides context for assistive technologies
+ *
+ * The asset automatically centers its content both horizontally and vertically within the available space.
+ *
+ * ### Content
+ *
+ * - **Default slot**: Custom content to display (typically an image) when variant is not set
+ * - **Label**: Accessible label for screen readers (used as `aria-label` on the icon SVGs)
*/
export const Anatomy: Story = {
render: (args) => html`
- ${anatomyArgs.map((arg) => template({ ...args, ...arg }))}
+ ${template({ ...args, variant: 'file', label: 'README.md' })}
+ ${template({ ...args, variant: 'folder', label: 'packages/swc/' })}
+ ${template({
+ ...args,
+ label: 'images/profile_sm.png',
+ 'default-slot': ``,
+ })}
`,
- parameters: {
- flexLayout: true,
- },
tags: ['anatomy'],
};
@@ -112,23 +115,33 @@ export const Anatomy: Story = {
// ──────────────────────────
/**
- * When the `file` variant is added, a file icon displays instead of the slotted content.
- */
-export const File: Story = {
- args: {
- variant: 'file',
- label: 'README.md',
- },
- tags: ['options'],
-};
-
-/**
- * When the `folder` variant is added, a folder icon displays instead of the slotted content.
+ * Assets support two built-in icon variants for representing files and folders:
+ *
+ * - **`file`**: Displays a file icon, useful for representing documents, files, or file types
+ * - **`folder`**: Displays a folder icon, useful for representing directories or collections
+ *
+ * When no variant is specified, the asset displays custom content provided via the default slot (typically an image).
*/
-export const Folder: Story = {
- args: {
- variant: 'folder',
- label: 'packages/swc/',
+export const Variants: Story = {
+ render: (args) => html`
+ ${template({
+ ...args,
+ variant: 'file',
+ label: 'README.md',
+ })}
+ ${template({
+ ...args,
+ variant: 'folder',
+ label: 'packages/swc/',
+ })}
+ ${template({
+ ...args,
+ label: 'banners/sunset.jpg',
+ 'default-slot': ``,
+ })}
+ `,
+ parameters: {
+ 'section-order': 1,
},
tags: ['options'],
};
@@ -137,33 +150,47 @@ export const Folder: Story = {
// ACCESSIBILITY STORIES
// ────────────────────────────────
-const accessibilityArgs = [
- {
- variant: 'folder',
- label: '/packages/swc/ folder',
- },
- {
- label: 'Sara Sawyer avatar',
- 'default-slot': ``,
- },
-];
/**
* ### Features
*
- * The `` element implements several accessibility features:
+ * The `` element implements several accessibility features:
*
- * 1. **Labeling**: Uses the `label` attribute value as `aria-label`
+ * #### ARIA implementation
*
- * ### Best Practices
+ * - **Icon labeling**: File and folder SVG icons automatically use the `label` property as `aria-label`
+ * - **Non-interactive**: Assets have no interactive behavior and are not focusable
*
- * - Always provide a descriptive `label` that explains what the asset represents, unless the asset is purely decorative
+ * #### Visual accessibility
+ *
+ * - Icons use sufficient color contrast in both light and dark modes
+ * - High contrast mode is supported with appropriate color overrides
+ * - Content automatically centers for consistent layout and visual balance
+ *
+ * ### Best practices
+ *
+ * - Always provide a descriptive `label` attribute for file and folder variants
+ * - Use specific, meaningful labels or alt text (e.g., "Project proposal PDF", "projects/2025/proposal.pdf", or not just "File")
+ * - The `label` on the asset itself should describe the asset's purpose or context
+ * - For decorative images, use an empty `alt=""` attribute on the img tag
+ * - Test with screen readers to verify assets are announced appropriately in context
*/
export const Accessibility: Story = {
render: (args) => html`
- ${accessibilityArgs.map((arg) => template({ ...args, ...arg }))}
+ ${template({
+ ...args,
+ variant: 'file',
+ label: 'Project proposal document',
+ })}
+ ${template({
+ ...args,
+ variant: 'folder',
+ label: 'Design assets directory',
+ })}
+ ${template({
+ ...args,
+ label: 'User profile photo',
+ 'default-slot': ``,
+ })}
`,
- parameters: {
- flexLayout: true,
- },
tags: ['a11y'],
};
diff --git a/2nd-gen/packages/swc/components/badge/Badge.ts b/2nd-gen/packages/swc/components/badge/Badge.ts
index 0c34f6f9517..41e306226c4 100644
--- a/2nd-gen/packages/swc/components/badge/Badge.ts
+++ b/2nd-gen/packages/swc/components/badge/Badge.ts
@@ -16,11 +16,11 @@ import { classMap } from 'lit/directives/class-map.js';
import { when } from 'lit/directives/when.js';
import {
+ BADGE_VALID_SIZES,
BADGE_VARIANTS_COLOR_S2,
BADGE_VARIANTS_S2,
BadgeBase,
type BadgeVariantS2 as BadgeVariant,
- VALID_SIZES,
} from '@spectrum-web-components/core/components/badge';
import styles from './badge.css';
@@ -59,7 +59,7 @@ export class Badge extends BadgeBase {
/**
* @internal
*/
- static override readonly VALID_SIZES = VALID_SIZES;
+ static override readonly VALID_SIZES = BADGE_VALID_SIZES;
/**
* The variant of the badge.
diff --git a/2nd-gen/packages/swc/components/badge/DOC-COMPARISON.md b/2nd-gen/packages/swc/components/badge/DOC-COMPARISON.md
deleted file mode 100644
index 3f4ab2b9bcb..00000000000
--- a/2nd-gen/packages/swc/components/badge/DOC-COMPARISON.md
+++ /dev/null
@@ -1,138 +0,0 @@
-# Badge: 1st-gen vs 2nd-gen documentation comparison
-
-## Summary
-
-Overall, the 2nd-gen documentation has been successfully migrated with most key concepts preserved. However, there are several notable content gaps and detail differences that should be addressed for completeness.
-
-## Missing content areas
-
-### Critical gaps
-
-1. **XL size option**
- - **1st-gen**: Documents and demonstrates `size="xl"` with examples
- - **2nd-gen**: Only shows `s`, `m`, `l` sizes
- - **Priority**: High
- - **Recommendation**: Verify if `xl` size is supported in 2nd-gen implementation. If yes, add to stories and documentation.
-
-2. **Accent variant**
- - **1st-gen**: Shows `variant="accent"` as a semantic variant
- - **2nd-gen**: Not documented in semantic variants list
- - **Priority**: High
- - **Recommendation**: Verify if accent is available and include if supported.
-
-3. **Interactive badges keyboard interactions**
- - **1st-gen**: Documents keyboard interactions (Tab, Space, Enter)
- - **2nd-gen**: No keyboard interaction documentation
- - **Priority**: Medium
- - **Recommendation**: If badges can be interactive, document keyboard support; otherwise, reinforce non-interactive nature.
-
-### Nice-to-have additions
-
-4. **Tooltip integration for long text**
- - **1st-gen**: Shows example with `` and `` for truncated text
- - **2nd-gen**: Only shows text wrapping without tooltip integration
- - **Priority**: Medium
- - **Recommendation**: Add example showing tooltip usage for truncated/long badge labels.
-
-5. **Icon-only badge with aria-label**
- - **1st-gen**: Explicitly shows icon-only badge with tooltip and emphasizes aria-label requirement
- - **2nd-gen**: Anatomy shows icon-only example but doesn't emphasize accessibility requirements
- - **Priority**: Medium
- - **Recommendation**: Add a11y guidance emphasizing aria-label requirement for icon-only badges.
-
-6. **Usage badges (NPM, bundlephobia)**
- - **1st-gen**: Includes NPM version and bundle size badges at top
- - **2nd-gen**: No package metadata displayed
- - **Priority**: Low
- - **Recommendation**: Consider adding package metadata if useful for users.
-
-## Content depth differences
-
-### Behavioral documentation
-
-**1st-gen advantage**:
-
-- More detailed text wrapping behavior: "If there is no room for a second line of text, the badge should truncate and include a tooltip"
-- Explicit statement: "Badges are not interactive by default"
-- Fixed positioning shown with visual demo in styled container
-
-**2nd-gen advantage**:
-
-- Better organized with clear story sections (Anatomy, Options, Accessibility)
-- More concise descriptions that get to the point faster
-
-### Accessibility guidance
-
-**1st-gen advantage**:
-
-- Do/Don't table with multiple best practices
-- Separate section "Always include a label" with detailed explanation
-- "Don't override semantic colors" section with explicit warning
-- Tooltip usage examples
-
-**2nd-gen advantage**:
-
-- Consolidated a11y features in structured format
-- Clear bullet point best practices
-- Better integration with interactive stories
-
-**Gap**: 2nd-gen is missing the do/don't table which provides valuable guidance at a glance.
-
-### Variant documentation
-
-**1st-gen**: Uses `` component to organize semantic vs non-semantic variants interactively
-
-**2nd-gen**: Shows both variant types as separate stories
-
-Both approaches work well; 2nd-gen is more testable/automatable.
-
-## Recommendations by priority
-
-### High priority
-
-1. **Add XL size support** (if available in implementation)
-2. **Add accent variant** (if available in implementation)
-3. **Verify non-semantic color variant list** - Ensure all colors from 1st-gen are available in 2nd-gen
-
-### Medium priority
-
-4. **Add tooltip integration example** for long/truncated badges
-5. **Add icon-only badge accessibility section** emphasizing aria-label requirements
-6. **Document keyboard interactions** or reinforce non-interactive nature more explicitly
-7. **Add do/don't table** to accessibility section for quick reference
-
-### Low priority
-
-8. Consider adding package metadata badges (NPM, bundlephobia)
-9. Add visual demo for fixed positioning similar to 1st-gen
-
-## Content quality assessment
-
-| Area | 1st-gen | 2nd-gen | Notes |
-| --------------------- | ---------------- | ---------------- | ------------------------------------------ |
-| **Installation** | ✅ Comprehensive | ❌ Missing | 2nd-gen assumes users know how to install |
-| **Anatomy** | ✅ Good | ✅ Good | Both clear, 2nd-gen slightly more concise |
-| **Sizes** | ✅ Has XL | ⚠️ Missing XL | May be intentional if XL removed |
-| **Variants** | ✅ Comprehensive | ✅ Comprehensive | Both cover semantic and non-semantic well |
-| **Fixed positioning** | ✅ Visual demo | ✅ Examples | 1st-gen has better visual demonstration |
-| **Text wrapping** | ✅ With tooltip | ⚠️ Basic example | 1st-gen shows truncation + tooltip pattern |
-| **Accessibility** | ✅ Extensive | ✅ Good | 1st-gen more detailed with do/don't table |
-| **Keyboard** | ✅ Documented | ❌ Missing | May not be applicable if non-interactive |
-
-## Overall assessment
-
-**Strengths of 2nd-gen migration**:
-
-- Clean, organized story structure
-- Better separation of concerns (anatomy, options, a11y)
-- More maintainable automated testing approach
-- Consistent with progress-circle format
-
-**Areas needing enhancement**:
-
-- Add missing size/variant options (if supported)
-- Expand accessibility section with more detailed guidance
-- Add practical examples (tooltips, truncation)
-- Consider adding do/don't table for quick reference
-
-**Completeness score**: 85% - Most essential content migrated successfully, with some enhancement opportunities identified.
diff --git a/2nd-gen/packages/swc/components/badge/stories/badge.stories.ts b/2nd-gen/packages/swc/components/badge/stories/badge.stories.ts
index 1c0369c2b40..ae4ae490b2b 100644
--- a/2nd-gen/packages/swc/components/badge/stories/badge.stories.ts
+++ b/2nd-gen/packages/swc/components/badge/stories/badge.stories.ts
@@ -18,21 +18,23 @@ import { Badge } from '@adobe/swc/badge';
import '@adobe/swc/badge';
+import {
+ BADGE_VALID_SIZES,
+ BADGE_VARIANTS_COLOR_S2,
+ BADGE_VARIANTS_S2,
+ BADGE_VARIANTS_SEMANTIC,
+ type BadgeColorVariantS2,
+ type BadgeSemanticVariant,
+ type BadgeSize,
+ FIXED_VALUES,
+ type FixedValues,
+} from '../../../../core/components/badge/Badge.types.js';
+
// ────────────────
// METADATA
// ────────────────
-const { events, args, argTypes, template } = getStorybookHelpers('swc-badge');
-
-const parameters = {
- flexLayout: true,
- styles: {
- gap: 'var(--spectrum-spacing-200)',
- 'flex-wrap': 'wrap',
- 'justify-content': 'center',
- 'max-inline-size': '80ch',
- },
-};
+const { args, argTypes, template } = getStorybookHelpers('swc-badge');
argTypes.variant = {
...argTypes.variant,
@@ -56,10 +58,11 @@ argTypes.size = {
};
/**
- * Badges are for showing a small amount of color-categorized metadata. They're ideal for getting a user's attention.
+ * Similar to [status lights](/?path=/docs/components-status-light--readme), they use color and text to convey status or category information.
*
- * There are two additional styles - subtle fill and outline - in addition to the default, bold fill style.
- * Because outline and subtle fill styles draw a similar level of attention, choose only one to use consistently within a single product. Bold fill can be paired with either style, and is reserved for high-attention badging only.
+ * Badges come in three styles: bold fill (default), subtle fill, and outline.
+ * Choose one style consistently within a product - `outline` and `subtle` fill draw similar attention levels.
+ * Reserve bold fill for high-attention badging only.
*/
const meta: Meta = {
title: 'Badge',
@@ -68,18 +71,74 @@ const meta: Meta = {
argTypes,
render: (args) => template(args),
parameters: {
- actions: {
- handles: events,
- },
docs: {
- subtitle: `Badges are for showing a small amount of color-categorized metadata. They're ideal for getting a user's attention.`,
+ subtitle: `Display small amounts of color-categorized metadata to get a user's attention.`,
},
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/Mngz9H7WZLbrCvGQf3GnsY/S2---Desktop?node-id=36806-6551',
+ },
+ stackblitz: {
+ url: 'https://stackblitz.com/edit/vitejs-vite-4glrpeeb?file=package.json',
+ },
+ flexLayout: 'row-wrap',
},
tags: ['migrated'],
};
export default meta;
+// ────────────────────
+// HELPERS
+// ────────────────────
+
+const sizeLabels = {
+ s: 'Small',
+ m: 'Medium',
+ l: 'Large',
+ xl: 'Extra-large',
+} as const satisfies Record;
+
+const semanticLabels = {
+ accent: 'New',
+ informative: 'Active',
+ neutral: 'Archived',
+ positive: 'Approved',
+ notice: 'Pending approval',
+ negative: 'Rejected',
+} as const satisfies Record;
+
+const nonSemanticLabels = {
+ fuchsia: 'Marketing',
+ indigo: 'Engineering',
+ magenta: 'Design',
+ purple: 'Product',
+ seafoam: 'Support',
+ yellow: 'Busy',
+ gray: 'Available',
+ red: 'Sales',
+ orange: 'Research',
+ chartreuse: 'Quality',
+ celery: 'Documentation',
+ green: 'Legal',
+ cyan: 'Analytics',
+ blue: 'Security',
+ pink: 'Creative',
+ turquoise: 'Training',
+ brown: 'Facilities',
+ cinnamon: 'Compliance',
+ silver: 'Version 1.2.10',
+} as const satisfies Record;
+
+const allVariantsLabels = { ...semanticLabels, ...nonSemanticLabels };
+
+const fixedLabels = {
+ 'block-start': 'Block start',
+ 'block-end': 'Block end',
+ 'inline-start': 'Inline start',
+ 'inline-end': 'Inline end',
+} as const satisfies Record;
+
// ────────────────────
// AUTODOCS STORY
// ────────────────────
@@ -88,41 +147,57 @@ export const Playground: Story = {
render: (args) => template(args),
args: {
size: 'm',
- 'default-slot': 'New',
+ variant: 'informative',
+ 'default-slot': 'Active',
},
tags: ['autodocs', 'dev'],
};
+// ──────────────────────────────
+// OVERVIEW STORIES
+// ──────────────────────────────
+
+export const Overview: Story = {
+ render: (args) => html` ${template(args)} `,
+ tags: ['overview'],
+ args: {
+ size: 'm',
+ variant: 'informative',
+ 'default-slot': 'Active',
+ },
+};
+
// ──────────────────────────
// ANATOMY STORIES
// ──────────────────────────
-const anatomyArgs = [
- {
- 'default-slot': 'Label only',
- },
- {
- 'icon-slot': '✓',
- },
- {
- 'icon-slot': '✓',
- 'default-slot': 'Icon and label',
- },
-];
/**
- * A badge is made up of the following parts:
+ * A badge consists of:
+ *
+ * 1. **Container** - Colored background with rounded corners
+ * 2. **Label** - Text content describing the status or category (required)
+ * 3. **Icon** (optional) - Visual indicator positioned before the label
*
- * - Text can be displayed within the badge by using the default slot
- * - An optional icon element can be used to display an icon within the badge
+ * ### Content
*
- * Badges can contain either a label, an icon, or both.
+ * - **Default slot**: Text content describing the status or category (required for accessibility)
+ * - **icon slot**: (optional) - Visual indicator positioned before the label
*/
export const Anatomy: Story = {
render: (args) => html`
- ${anatomyArgs.map((arg) => template({ ...args, ...arg }))}
+ ${template({ ...args, 'default-slot': 'Label only' })}
+ ${template({ ...args, 'icon-slot': '✓', 'aria-label': 'Icon only' })}
+ ${template({
+ ...args,
+ 'icon-slot': '✓',
+ 'default-slot': 'Icon and label',
+ })}
`,
- parameters: parameters,
tags: ['anatomy'],
+ args: {
+ variant: 'informative',
+ size: 'm',
+ },
};
// ──────────────────────────
@@ -132,113 +207,163 @@ export const Anatomy: Story = {
/**
* Badges come in four sizes to fit various contexts:
*
- * - **Small (s)**: Compact spaces, inline with text
- * - **Medium (m)**: Default size, most common usage
- * - **Large (l)**: Increased emphasis
- * - **Extra-large (xl)**: Maximum visibility
+ * - **Small (`s`)**: Compact spaces, inline with text, or in tables
+ * - **Medium (`m`)**: Default size for most common usage scenarios
+ * - **Large (`l`)**: Increased emphasis in cards or content areas
+ * - **Extra-large (`xl`)**: Maximum visibility for primary status indicators
+ *
+ * The `m` size is the default and most frequently used option. Use larger sizes sparingly to create a hierarchy of importance on a page.
*/
export const Sizes: Story = {
- render: () => html`
- Small
- Medium
- Large
- Extra-large
+ render: (args) => html`
+ ${BADGE_VALID_SIZES.map((size) =>
+ template({
+ ...args,
+ size,
+ 'default-slot': sizeLabels[size],
+ })
+ )}
`,
- parameters: { ...parameters, 'section-order': 0 },
+ parameters: { 'section-order': 1 },
tags: ['options'],
+ args: {
+ variant: 'informative',
+ },
};
/**
- * Semantic variants allow you to render the badge with a descriptive name that maps to a design-system-aligned color. This is the preferred way to assign color to a badge because it will align more consistently with other components in your UI with the same meaning.
+ * Semantic variants provide meaning through color and should be used when status has specific significance.
+ * These variants align consistently with other design system components that use the same semantic meanings.
*
* Use these variants for the following statuses:
- * - **Positive**: approved, complete, success, new, purchased, licensed
- * - **Informative**: active, in use, live, published
- * - **Negative**: error, alert, rejected, failed
- * - **Neutral**: archived, deleted, paused, draft, not started, ended
+ *
+ * - **accent**: New, beta, prototype, draft
+ * - **informative**: Active, in use, live, published
+ * - **neutral**: Archived, deleted, paused, not started, ended
+ * - **positive**: Approved, complete, success, purchased, licensed
+ * - **notice**: Pending, expiring soon, limited, deprecated
+ * - **negative**: Rejected, error, alert, failed
*/
export const SemanticVariants: Story = {
- render: () => html`
- Approved
- Published
- Rejected
- Pending
- Archived
+ render: (args) => html`
+ ${BADGE_VARIANTS_SEMANTIC.map((variant) =>
+ template({
+ ...args,
+ variant,
+ 'default-slot': semanticLabels[variant],
+ })
+ )}
`,
- parameters: { ...parameters, 'section-order': 1 },
+ parameters: { 'section-order': 2 },
tags: ['options'],
};
+SemanticVariants.storyName = 'Semantic variants';
/**
- * When badges are for color-coded categories, they use non-semantic colors. Non-semantic variants are ideally used for when there are 8 categories or less.
+ * Non-semantic variants use distinctive colors for visual categorization without inherent meaning.
+ * These are ideal for color-coding categories, teams, or projects - especially when there are 8 categories or fewer.
+ *
+ * Use non-semantic variants when:
+ * - Categories don't have universal status meanings
+ * - Visual distinction matters more than semantic meaning
+ * - Creating department, team, or project color schemes
+ *
+ * > **Note**: 2nd-gen adds `pink`, `turquoise`, `brown`, `cinnamon`, and `silver` variants.
+ * 1st-gen variants `gray`, `red`, `orange`, `green`, and `blue` are not available in 2nd-gen.
*/
export const NonSemanticVariants: Story = {
- render: () => html`
- Design
- Engineering
- Marketing
- Sales
- Support
- Finance
- Operations
- HR
- Legal
+ render: (args) => html`
+ ${BADGE_VARIANTS_COLOR_S2.map((variant) =>
+ template({
+ ...args,
+ variant,
+ 'default-slot': nonSemanticLabels[variant],
+ })
+ )}
`,
- parameters: { ...parameters, 'section-order': 2 },
+ parameters: { 'section-order': 3 },
tags: ['options'],
};
NonSemanticVariants.storyName = 'Non-semantic variants';
/**
- * The `outline` style is only valid for semantic color variants.
+ * The `outline` style provides a bordered appearance with a transparent background.
+ * This style reduces visual weight while maintaining semantic meaning.
+ *
+ * **Important**: The outline style is only valid for semantic variants (`accent`, `informative`, `neutral`, `positive`, `notice`, `negative`).
+ * Attempting to use `outline` with non-semantic color variants will not apply the style.
*/
export const Outline: Story = {
- argTypes: {
- variant: {
- control: { type: 'select' },
- options: Badge.VARIANTS_SEMANTIC,
- },
- },
- render: () => html`
- Approved
- Published
- Rejected
- Pending
- Archived
+ render: (args) => html`
+ ${BADGE_VARIANTS_SEMANTIC.map((variant) =>
+ template({
+ ...args,
+ variant,
+ outline: true,
+ 'default-slot': semanticLabels[variant],
+ })
+ )}
`,
- parameters: { ...parameters, 'section-order': 3 },
+ parameters: { 'section-order': 4 },
tags: ['options'],
+ args: {},
};
/**
- * The `subtle` style is available for all variants. It is useful when you want to reduce the visual prominence of the badge while still mapping to the design system color palette.
+ * The `subtle` style reduces visual prominence with a softer background fill.
+ * Unlike outline, subtle is available for **all** variants (semantic and non-semantic).
+ *
+ * Use subtle style when:
+ * - Multiple badges appear together and need less visual competition
+ * - Status is secondary to main content
+ * - Maintaining design system color palette while reducing emphasis
*/
export const Subtle: Story = {
- render: () => html`
- Approved
- Published
- Rejected
- Pending
- Archived
- Design
- Engineering
+ render: (args) => html`
+ ${BADGE_VARIANTS_S2.map((variant) =>
+ template({
+ ...args,
+ variant,
+ subtle: true,
+ 'default-slot': allVariantsLabels[variant],
+ })
+ )}
`,
- parameters: { ...parameters, 'section-order': 4 },
+ parameters: { 'section-order': 5 },
tags: ['options'],
+ args: {},
};
/**
- * Badge can be displayed as if it is "fixed" to the edge of a UI. The `fixed` attribute can be leveraged to alter the border rounding based on the position you would like to achieve. Fixed positioning options include `block-start`, `block-end`, `inline-start`, and `inline-end`.
+ * The `fixed` attribute adjusts border radius based on edge positioning, creating the appearance that the badge is "fixed" to a UI edge.
+ *
+ * Fixed positioning options:
+ *
+ * - **block-start**: Top edge (removes top-left and top-right border radius)
+ * - **block-end**: Bottom edge (removes bottom-left and bottom-right border radius)
+ * - **inline-start**: Left edge (removes top-left and bottom-left border radius)
+ * - **inline-end**: Right edge (removes top-right and bottom-right border radius)
+ *
+ * This is purely visual styling - actual positioning must be handled separately with CSS.
+ *
+ * All fixed positions shown below for comparison.
*/
export const Fixed: Story = {
- render: () => html`
- Top edge
- Bottom edge
- Left edge
- Right edge
+ render: (args) => html`
+ ${FIXED_VALUES.map((fixed) =>
+ template({
+ ...args,
+ fixed,
+ 'default-slot': fixedLabels[fixed],
+ })
+ )}
`,
- parameters: { ...parameters, 'section-order': 5 },
+ parameters: { 'section-order': 6 },
tags: ['options'],
+ args: {
+ variant: 'informative',
+ size: 'm',
+ },
};
// ──────────────────────────────
@@ -246,15 +371,24 @@ export const Fixed: Story = {
// ──────────────────────────────
/**
- * When a badge's label is too long for the available horizontal space, it wraps to form another line. Text wrapping can be enforced when a `max-inline-size` is applied to the badge.
+ * When a badge's label is too long for the available horizontal space, it wraps to form multiple lines.
+ * Text wrapping can be controlled by applying a `max-inline-size` constraint to the badge.
+ *
+ * This ensures badges remain readable even with longer status messages or category names.
*/
export const TextWrapping: Story = {
- render: () => html`
-
- Document review pending approval from manager
-
+ render: (args) => html`
+ ${template({
+ ...args,
+ variant: 'informative',
+ 'default-slot': 'Document review pending approval from manager',
+ style: 'max-inline-size: 120px',
+ })}
`,
tags: ['behaviors'],
+ args: {
+ size: 'm',
+ },
};
// ────────────────────────────────
@@ -264,31 +398,77 @@ export const TextWrapping: Story = {
/**
* ### Features
*
- * The `` element implements several accessibility features:
+ * The `` element implements several accessibility features:
*
- * 1. **Color Meaning**: Colors are used in combination with text labels to ensure that status information is not conveyed through color alone
+ * #### Color meaning
*
- * ### Best Practices
+ * - Colors are used in combination with text labels and/or icons to ensure that status information is not conveyed through color alone
+ * - Users with color vision deficiencies can understand badge meaning through text content
+ * - High contrast mode is supported with appropriate color overrides
*
- * - Use semantic variants (`positive`, `negative`, `notice`, `informative`, `neutral`) when the status has specific meaning
- * - Include a clear, descriptive text label that explains the status
+ * #### Non-interactive element
+ *
+ * - Badges have no interactive behavior and are not focusable
+ * - Screen readers will announce the badge content as static text
+ * - No keyboard interaction is required or expected
+ *
+ * ### Best practices
+ *
+ * - Use semantic variants (`positive`, `negative`, `notice`, `informative`, `neutral`, `accent`) when the status has specific meaning
+ * - Include clear, descriptive labels that explain the status without relying on color alone
+ * - For icon-only badges, provide descriptive text in the default slot or use the `aria-label` attribute directly on the element
* - Ensure sufficient color contrast between the badge and its background
- * - Avoid using badges for interactive elements; consider using buttons, tags, or links instead
+ * - Badges are not interactive elements - for interactive status indicators, consider using buttons, tags, or links instead
+ * - When using multiple badges together, ensure they're clearly associated with their related content
+ * - Use consistent badge variants across your application for the same statuses
+ * - Test with screen readers to verify badge content is announced in context
+ * - Consider placement carefully - badges should be close to the content they describe
*/
export const Accessibility: Story = {
- render: () => html`
- approved
- rejected
- needs approval
- new feature
- version 1.2.10
- available
- busy
- out of office
+ render: (args) => html`
+ ${template({
+ ...args,
+ variant: 'positive',
+ 'default-slot': 'Approved',
+ })}
+ ${template({
+ ...args,
+ variant: 'negative',
+ 'default-slot': 'Rejected',
+ })}
+ ${template({
+ ...args,
+ variant: 'notice',
+ 'default-slot': 'Pending approval',
+ })}
+ ${template({
+ ...args,
+ variant: 'informative',
+ 'default-slot': 'Active',
+ })}
+ ${template({
+ ...args,
+ variant: 'neutral',
+ 'default-slot': 'Archived',
+ })}
+ ${template({
+ ...args,
+ variant: 'celery',
+ 'default-slot': 'Documentation',
+ })}
+ ${template({
+ ...args,
+ variant: 'yellow',
+ 'default-slot': 'Busy',
+ })}
+ ${template({
+ ...args,
+ variant: 'silver',
+ 'default-slot': 'Version 1.2.10',
+ })}
`,
tags: ['a11y'],
+ args: {
+ size: 'm',
+ },
};
-
-// ────────────────────────
-// HELPER FUNCTIONS
-// ────────────────────────
diff --git a/2nd-gen/packages/swc/components/divider/DOC-COMPARISON.md b/2nd-gen/packages/swc/components/divider/DOC-COMPARISON.md
deleted file mode 100644
index 9f638337ad9..00000000000
--- a/2nd-gen/packages/swc/components/divider/DOC-COMPARISON.md
+++ /dev/null
@@ -1,182 +0,0 @@
-# Divider: 1st-gen vs 2nd-gen documentation comparison
-
-## Summary
-
-The 2nd-gen documentation successfully covers all core features of the divider component. Both versions are quite comprehensive. The migration maintains quality with only minor differences in presentation approach.
-
-## Missing content areas
-
-### Nice-to-have additions
-
-1. **Package metadata**
- - **1st-gen**: Includes NPM version, bundle size, and Stackblitz badges
- - **2nd-gen**: No package metadata
- - **Priority**: Low
- - **Recommendation**: Consider adding if useful for developer reference.
-
-2. **Design documentation link**
- - **1st-gen**: Links to Spectrum design documentation page
- - **2nd-gen**: No external design doc link
- - **Priority**: Low
- - **Recommendation**: Consider adding link to official design documentation.
-
-3. **Installation instructions**
- - **1st-gen**: Shows yarn add command and import statements
- - **2nd-gen**: Missing installation section
- - **Priority**: Low (may be intentional - centralized elsewhere)
-
-4. **Vertical divider with action buttons example**
- - **1st-gen**: Shows practical example with action buttons in flex container
- - **2nd-gen**: Shows simpler demonstration without real components
- - **Priority**: Low
- - **Recommendation**: Current 2nd-gen example is sufficient; more complex examples could be added as "advanced usage" if desired.
-
-5. **Color contrast guidance for static colors**
- - **1st-gen**: Best practice mentions "Ensure sufficient color contrast when using `static-color` variants"
- - **2nd-gen**: General contrast guidance but not specific to static-color
- - **Priority**: Low
- - **Recommendation**: Consider adding this specific guidance.
-
-## Content depth differences
-
-### Size documentation
-
-**1st-gen**:
-
-- Uses interactive tabs for each size
-- Shows size with heading and description text in each example
-- Embeds usage guidance within each size panel
-
-**2nd-gen**:
-
-- Clean story showing all sizes side-by-side
-- Consolidated usage guidance in JSDoc
-- Better for visual comparison
-
-**Winner**: Different approaches, both effective. 2nd-gen slightly better for visual comparison.
-
-### Vertical orientation
-
-**1st-gen advantage**:
-
-- Shows practical example with real components (action buttons)
-- Demonstrates the `align-self: stretch; height: auto;` pattern in context
-
-**2nd-gen**:
-
-- Simpler demonstration
-- Still mentions the flex container pattern in description
-- Easier to understand in isolation
-
-**Winner**: 1st-gen provides more practical context, but 2nd-gen is clearer
-
-### Static color
-
-**1st-gen**:
-
-- Uses tabs to separate black/white variants
-- Shows each in appropriately colored background containers
-- More verbose examples with headings and descriptions
-
-**2nd-gen advantage**:
-
-- Side-by-side comparison in one story
-- Cleaner visual comparison
-- Tagged as `'!test'` appropriately
-
-**Winner**: 2nd-gen - better visual comparison approach
-
-### Accessibility
-
-**1st-gen**:
-
-- Same features documented (role, orientation)
-- Four best practices
-- Includes contrast guidance specific to static-color
-
-**2nd-gen**:
-
-- Same features documented
-- Three best practices
-- Missing the static-color contrast specific guidance
-
-**Winner**: 1st-gen slightly more comprehensive (has contrast guidance)
-
-## Areas where 2nd-gen improves on 1st-gen
-
-1. **Story organization**: Better structured sections (anatomy, options, accessibility)
-2. **Visual comparisons**: Static color side-by-side is clearer
-3. **JSDoc comments**: Each story has clear explanation
-4. **Anatomy section**: Dedicated section explaining component parts
-5. **Testability**: Better organized for automated testing
-
-## Recommendations by priority
-
-### High priority
-
-None - all critical functionality documented
-
-### Medium priority
-
-1. **Add contrast guidance** specific to static-color variants in best practices
-2. **Consider adding design doc link** if it exists for 2nd-gen
-
-### Low priority
-
-3. Consider adding package metadata badges
-4. Add installation/usage section if not documented globally
-5. Consider adding more complex vertical divider example with actual components
-6. Add note about `align-self: stretch; height: auto;` more prominently for vertical dividers
-
-## Content quality assessment
-
-| Area | 1st-gen | 2nd-gen | Notes |
-| ------------------ | -------------------- | ------------------- | --------------------------------------- |
-| **Installation** | ✅ Documented | ❌ Missing | May be intentional |
-| **Anatomy** | ⚠️ Implicit | ✅ Explicit section | 2nd-gen better organized |
-| **Sizes** | ✅ Comprehensive | ✅ Comprehensive | Both excellent, different approaches |
-| **Vertical** | ✅ Practical example | ✅ Clear example | 1st-gen more practical, 2nd-gen clearer |
-| **Static color** | ✅ Good | ✅ Better visual | 2nd-gen improved presentation |
-| **Accessibility** | ✅ Very good | ✅ Good | 1st-gen has contrast guidance |
-| **Best practices** | ✅ 4 practices | ✅ 3 practices | 1st-gen includes contrast note |
-
-## Overall assessment
-
-**Strengths of 2nd-gen migration**:
-
-- Better structured sections (anatomy, options, a11y)
-- Improved visual presentation for static colors
-- Clear JSDoc documentation
-- Better organized for testing
-- Explicit anatomy section
-
-**Strengths of 1st-gen preserved**:
-
-- All size options maintained
-- All orientation options maintained
-- All static color options maintained
-- Accessibility features documented
-
-**Minor gaps**:
-
-- Missing static-color specific contrast guidance (low priority)
-- Simpler vertical divider example (acceptable trade-off)
-- No installation section (may be intentional)
-
-**Completeness score**: 92% - Excellent migration that maintains all essential content with improved organization. Only minor enhancement opportunities.
-
-## Recommendation
-
-This is a high-quality migration. The 2nd-gen documentation:
-
-- Preserves all essential features from 1st-gen
-- Improves organization and structure
-- Enhances visual presentation (especially static colors)
-- Maintains comprehensive accessibility documentation
-
-Minor enhancements suggested:
-
-- Add static-color contrast guidance to best practices
-- Consider more prominent mention of flex container pattern for vertical dividers
-
-Overall, this migration successfully translates 1st-gen content to the refined 2nd-gen format while maintaining quality.
diff --git a/2nd-gen/packages/swc/components/divider/stories/divider.stories.ts b/2nd-gen/packages/swc/components/divider/stories/divider.stories.ts
index ccea9e7e141..82755cf04bc 100644
--- a/2nd-gen/packages/swc/components/divider/stories/divider.stories.ts
+++ b/2nd-gen/packages/swc/components/divider/stories/divider.stories.ts
@@ -37,59 +37,82 @@ argTypes['static-color'] = {
};
/**
- * Dividers bring clarity to a layout by grouping and dividing content that exists in close proximity. It can also be used to establish rhythm and hierarchy.
+ * A divider is a visual separator that brings clarity to a layout by grouping and dividing
+ * content in close proximity. Dividers help establish rhythm and hierarchy, making it easier
+ * for users to scan and understand content structure.
*/
const meta: Meta = {
title: 'Divider',
component: 'swc-divider',
args,
argTypes,
- render: (args) => template(args),
+ render: (args) => html` ${template({ ...args })} `,
parameters: {
actions: {
handles: events,
},
docs: {
- subtitle: `Dividers bring clarity to a layout by grouping and dividing content in close proximity.`,
+ subtitle: `Visual separator for grouping and dividing content`,
},
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/Mngz9H7WZLbrCvGQf3GnsY/S2---Desktop?node-id=13642-334',
+ },
+ stackblitz: {
+ url: 'https://stackblitz.com/edit/vitejs-vite-rqfjtpgz?file=package.json',
+ },
+ flexLayout: 'row-nowrap',
},
tags: ['migrated'],
};
export default meta;
-type DividerSize = typeof Divider.prototype.size;
-
// ────────────────────
// AUTODOCS STORY
// ────────────────────
export const Playground: Story = {
- args: {
- size: 'm',
- },
+ args: {},
tags: ['autodocs', 'dev'],
};
+// ──────────────────────────
+// OVERVIEW STORY
+// ──────────────────────────
+
+export const Overview: Story = {
+ render: (args) => html`
+
Content above the divider
+ ${template({ ...args, size: 'm' })}
+
Content below the divider
+ `,
+ args: {},
+ parameters: {
+ flexLayout: 'column-stretch',
+ },
+ tags: ['overview'],
+};
+
// ──────────────────────────
// ANATOMY STORIES
// ──────────────────────────
/**
- * A divider consists of a line with the following aspects:
+ * A divider consists of:
*
- * - An optional size
- * - An optional orientation
- * - An optional static color for backgrounds that have color
+ * 1. **Line** - The visual separator element that creates visual separation between content
*/
export const Anatomy: Story = {
render: (args) => html`
-
Content above the divider
+
Content above the divider
${template({ ...args, size: 'm' })}
Content below the divider
`,
tags: ['anatomy'],
- args: {},
+ parameters: {
+ flexLayout: 'column-stretch',
+ },
};
// ──────────────────────────
@@ -99,100 +122,73 @@ export const Anatomy: Story = {
/**
* Dividers come in three sizes to fit various contexts:
*
- * - **Small**: Used to divide similar components such as table rows, action button groups, and components within a panel
- * - **Medium**: Used for dividing subsections on a page, or to separate different groupings of components such as panels, rails, etc.
- * - **Large**: Should only be used for page titles or section titles
+ * - **Small (`s`)**: Used to divide similar components such as table rows, action button groups, and components within a panel
+ * - **Medium (`m`)**: Used for dividing subsections on a page, or to separate different groupings of components such as panels, rails, etc.
+ * - **Large (`l`)**: Should only be used for page titles or section titles
*/
export const Sizes: Story = {
- render: () =>
- html`${Divider.VALID_SIZES.map((size) => {
- const label = sizeLabel(size);
- return html`
-
${label}
-
-
Content below the ${label.toLowerCase()} divider.
-
`;
- })}`,
+ render: (args) => html`
+
+
Small
+ ${template({ ...args, size: 's' })}
+
Content below the small divider.
+
+
+
Medium
+ ${template({ ...args, size: 'm' })}
+
Content below the medium divider.
+
+
+
Large
+ ${template({ ...args, size: 'l' })}
+
Content below the large divider.
+
+ `,
parameters: {
- flexLayout: true,
- styles: {
- 'flex-direction': 'row',
- gap: '16px',
- },
+ 'section-order': 1,
},
tags: ['options'],
};
/**
- * The default horizontal divider is used to separate content stacked vertically. To separate horizontal content, use the `vertical` attribute.
- *
- * When a vertical divider is used inside of a flex container, use `align-self: stretch; height: auto;` on the divider.
+ * The default horizontal divider is used to separate content stacked vertically. To separate
+ * horizontal content, use the `vertical` attribute.
*/
export const Vertical: Story = {
- args: {
- vertical: true,
- },
- render: () =>
- html`${Divider.VALID_SIZES.map((size) => {
- const label = sizeLabel(size);
- return html`
- ${label}
-
- `;
- })}`,
- parameters: {
- flexLayout: true,
- styles: {
- 'flex-direction': 'row',
- gap: '16px',
- },
- },
- tags: ['options'],
-};
-
-/**
- * When displaying over images or colored backgrounds, use the `static-color` attribute for better contrast, e.g. `static-color="white"` on a dark background or `static-color="black"` on a light background:
- */
-export const StaticBlack: Story = {
- args: {
- 'static-color': 'black',
- },
- render: (args: Record) => html`
-
Content above the divider on a light background
- ${template({ ...args, size: 'm' as DividerSize })}
-
- ${template({ ...args, size: 'm' as DividerSize })}
-
Content below the divider
- `,
- parameters: {
- flexLayout: false,
- styles: {
- color: 'white',
- },
+ vertical: true,
},
};
/**
- * When displaying over images or colored backgrounds, use the `static-color` attribute for better contrast, e.g. `static-color="white"` on a dark background or `static-color="black"` on a light background:
+ * Use the `static-color` attribute when displaying over images or colored backgrounds:
+ *
+ * - **white**: Use on dark or colored backgrounds for better contrast
+ * - **black**: Use on light backgrounds for better contrast
*/
export const StaticColors: Story = {
render: (args) => html`
@@ -211,11 +207,12 @@ export const StaticColors: Story = {
size: 'm',
},
parameters: {
- flexLayout: false,
staticColorsDemo: true,
+ 'section-order': 3,
},
- tags: ['options', '!test'],
+ tags: ['options'],
};
+StaticColors.storyName = 'Static colors';
// ────────────────────────────────
// ACCESSIBILITY STORIES
@@ -224,39 +221,38 @@ export const StaticColors: Story = {
/**
* ### Features
*
- * The `` element implements the following accessibility features:
+ * The `` element implements several accessibility features:
+ *
+ * #### ARIA implementation
*
* 1. **ARIA role**: Automatically sets `role="separator"` to ensure proper semantic meaning for assistive technologies
* 2. **Orientation support**: When `vertical` is true, automatically sets `aria-orientation="vertical"` to indicate the divider's orientation
*
+ * #### Visual accessibility
+ *
+ * - Dividers use sufficient thickness and color contrast to be perceivable
+ * - Static color variants ensure contrast on different backgrounds
+ * - High contrast mode is supported with appropriate color overrides
+ *
* ### Best practices
*
- * - Medium or large dividers can be used with header text to visually create a section or page title. Place the divider below the header for best results
+ * - Place medium or large dividers below header text to visually create a section or page title
* - Use dividers to create meaningful visual separation, not just decorative lines
* - Use dividers sparingly; excessive use can diminish their visual impact
+ * - Ensure sufficient color contrast when using `static-color` variants on colored backgrounds
+ * - Consider using headings or other semantic elements for screen reader users when dividers mark major content transitions
*/
export const Accessibility: Story = {
- render: () => html`
+ render: (args) => html`
Review the project timeline, milestones, and deliverables for the
current sprint.
`,
+ parameters: {
+ flexLayout: 'column-stretch',
+ },
tags: ['a11y'],
};
-
-// ────────────────────────
-// HELPER FUNCTIONS
-// ────────────────────────
-
-/* @todo Pull this up into a utility function for more components to leverage */
-function sizeLabel(size?: DividerSize): string {
- const labels: Record = {
- s: 'Small',
- m: 'Medium',
- l: 'Large',
- };
- return size ? labels[size] || size : '';
-}
diff --git a/2nd-gen/packages/swc/components/progress-circle/stories/progress-circle.stories.ts b/2nd-gen/packages/swc/components/progress-circle/stories/progress-circle.stories.ts
index 4799b7ab5eb..4df3b7b6446 100644
--- a/2nd-gen/packages/swc/components/progress-circle/stories/progress-circle.stories.ts
+++ b/2nd-gen/packages/swc/components/progress-circle/stories/progress-circle.stories.ts
@@ -43,9 +43,8 @@ argTypes['static-color'] = {
};
/**
+ * Progress circles show the progression of a system operation such as downloading, uploading, processing, etc. in a visual way.
* They can represent determinate or indeterminate progress.
- * By default, they represent determinate progress. To represent determinate progress, set the `progress` attribute to a value between 0 and 100.
- * To represent indeterminate progress, set the `indeterminate` attribute to `true`.
*/
const meta: Meta = {
title: 'Progress circle',
@@ -61,10 +60,11 @@ const meta: Meta = {
stackblitz: {
url: 'https://stackblitz.com/edit/vitejs-vite-xx1plot6?file=package.json',
},
+ flexLayout: 'row-wrap',
},
args,
argTypes,
- render: (args) => template(args), // This is the default render function for the component. Think of this like a beforeEach setup function for the stories below.
+ render: (args) => template(args),
tags: ['migrated'],
};
@@ -79,7 +79,19 @@ export const Playground: Story = {
args: {
progress: 50,
size: 'm',
- label: 'Loading progress',
+ label: 'Uploading document',
+ },
+};
+
+// ──────────────────────────
+// OVERVIEW STORIES
+// ──────────────────────────
+
+export const Overview: Story = {
+ tags: ['overview'],
+ args: {
+ progress: 50,
+ label: 'Uploading document',
},
};
@@ -88,18 +100,36 @@ export const Playground: Story = {
// ──────────────────────────
/**
- * A progress circle consists of several key parts:
+ * A progress circle consists of:
+ *
+ * 1. **Track** - Background ring showing the full progress range
+ * 2. **Fill** - Colored ring segment showing current progress
+ * 3. **Label** - Accessible text describing the operation (not visually rendered)
*
- * - An accessible label (via `label` attribute)
- * - A progress value (via `progress` attribute)
- * - An indeterminate state (via `indeterminate` attribute)
- * - An optional size
- * - An optional static color for backgrounds that have color
+ * ### Content
+ * - **Default slot**: Alternative way to provide an accessible label (the `label` attribute is preferred)
+ * - **Label**: Accessible text describing what is loading or progressing (not visually rendered)
*/
export const Anatomy: Story = {
render: (args) => html`
- ${template({ ...args, progress: 25, size: 'l', label: 'Loading' })}
- ${template({ ...args, indeterminate: true, label: 'Saving progress' })}
+ ${template({
+ ...args,
+ progress: 0,
+ size: 'l',
+ label: 'Starting upload',
+ })}
+ ${template({
+ ...args,
+ progress: 50,
+ size: 'l',
+ label: 'Uploading document',
+ })}
+ ${template({
+ ...args,
+ progress: 100,
+ size: 'l',
+ label: 'Upload complete',
+ })}
`,
tags: ['anatomy'],
args: {},
@@ -112,92 +142,95 @@ export const Anatomy: Story = {
/**
* Progress circles come in three sizes to fit various contexts:
*
- * - **Small**: Used for inline indicators or space-constrained areas
- * - **Medium**: Default size, used for typical loading states
- * - **Large**: Used for prominent loading states or primary content areas
+ * - **Small (`s`)**: Used for inline indicators or space-constrained areas, such as in tables or alongside small text
+ * - **Medium (`m`)**: Default size, used for typical loading states in cards, forms, or content areas
+ * - **Large (`l`)**: Used for prominent loading states, primary content areas, or full-page loading indicators
*/
export const Sizes: Story = {
render: (args) => html`
- ${template({ ...args, size: 's', label: 'Small progress' })}
- ${template({ ...args, size: 'm', label: 'Medium progress' })}
- ${template({ ...args, size: 'l', label: 'Large progress' })}
+ ${template({ ...args, size: 's', label: 'Processing small item' })}
+ ${template({ ...args, size: 'm', label: 'Processing medium item' })}
+ ${template({ ...args, size: 'l', label: 'Processing large item' })}
`,
tags: ['options'],
args: {
progress: 25,
},
-};
-
-/**
- * When displaying over images or colored backgrounds, use the `static-color` attribute for better contrast,
- * e.g. `static-color="white"` on a dark background or `static-color="black"` on a light background.
- */
-export const StaticWhite: Story = {
- args: {
- 'static-color': 'white',
- progress: 60,
- label: 'Loading on dark background',
- },
-};
-
-export const StaticBlack: Story = {
- args: {
- 'static-color': 'black',
- progress: 60,
- label: 'Loading on light background',
+ parameters: {
+ 'section-order': 1,
},
};
/**
- * When displaying over images or colored backgrounds, use the `static-color` attribute for better contrast,
- * e.g. `static-color="white"` on a dark background or `static-color="black"` on a light background.
+ * Use the `static-color` attribute when displaying over images or colored backgrounds:
+ *
+ * - **white**: Use on dark or colored backgrounds for better contrast
+ * - **black**: Use on light backgrounds for better contrast
*/
export const StaticColors: Story = {
render: (args) => html`
- ${['white', 'black'].map(
+ ${ProgressCircle.STATIC_COLORS.map(
(color) => html`${template({ ...args, 'static-color': color })}`
)}
`,
args: {
progress: 60,
- label: 'Loading',
+ label: 'Processing media',
},
- tags: ['options', '!test'],
+ tags: ['options'],
parameters: {
- flexLayout: false,
staticColorsDemo: true,
+ 'section-order': 2,
},
};
+StaticColors.storyName = 'Static colors';
// ──────────────────────────
// STATES STORIES
// ──────────────────────────
/**
- * Set the `progress` attribute to a value between 0 and 100 to represent determinate progress. This automatically sets `aria-valuenow` to the provided value.
+ * Progress circles can show specific progress values from 0% to 100%.
+ * Set the `progress` attribute to a value between 0 and 100 to represent determinate progress.
+ * This automatically sets `aria-valuenow` to the provided value for screen readers.
*/
export const ProgressValues: Story = {
render: (args) => html`
- ${template({ ...args, progress: 25, label: '25% progress' })}
- ${template({ ...args, progress: 50, label: '50% progress' })}
- ${template({ ...args, progress: 75, label: '75% progress' })}
- ${template({ ...args, progress: 100, label: '100% progress' })}
+ ${template({ ...args, progress: 0, label: 'Starting download' })}
+ ${template({ ...args, progress: 25, label: 'Downloading (25%)' })}
+ ${template({ ...args, progress: 50, label: 'Downloading (50%)' })}
+ ${template({ ...args, progress: 75, label: 'Downloading (75%)' })}
+ ${template({ ...args, progress: 100, label: 'Download complete' })}
`,
tags: ['states'],
args: {
size: 'm',
},
+ parameters: {
+ 'section-order': 1,
+ },
};
+ProgressValues.storyName = 'Progress values';
/**
- * Set the `indeterminate` attribute to render an animated loading indicator when the progress is unknown. This removes `aria-valuenow` from the element.
+ * The indeterminate state shows an animated loading indicator when progress is unknown or cannot be determined.
+ * Set the `indeterminate` attribute to `true` to activate this state.
+ * This removes `aria-valuenow` from the element and provides appropriate feedback to assistive technologies.
+ *
+ * Use indeterminate progress when:
+ * - The operation duration is unknown
+ * - Progress cannot be accurately measured
+ * - Multiple sub-operations are running in parallel
*/
export const Indeterminate: Story = {
tags: ['states'],
args: {
indeterminate: true,
size: 'm',
- label: 'Loading...',
+ label: 'Processing request',
+ },
+ parameters: {
+ 'section-order': 2,
},
};
@@ -210,27 +243,43 @@ export const Indeterminate: Story = {
*
* The `` element implements several accessibility features:
*
+ * #### ARIA implementation
+ *
* 1. **ARIA role**: Automatically sets `role="progressbar"` for proper semantic meaning
* 2. **Labeling**:
* - Uses the `label` attribute value as `aria-label`
- * - When determinate, adds `aria-valuenow` with the current progress
- * - Includes `aria-valuemin="0"` and `aria-valuemax="100"` for the progress range
- * 3. **Status communication**:
- * - Screen readers announce progress updates
- * - Indeterminate state is properly conveyed to assistive technologies
+ * - Alternative: Content in the default slot can provide the label
+ * 3. **Progress state** (determinate):
+ * - Sets `aria-valuenow` with the current `progress` value
+ * 4. **Loading state** (indeterminate):
+ * - Removes `aria-valuenow` when `indeterminate="true"`
+ * - Screen readers understand this as an ongoing operation with unknown duration
+ * 5. **Status communication**: Screen readers announce progress updates as values change
+ *
+ * #### Visual accessibility
+ *
+ * - Progress is shown visually through the fill amount, not relying solely on color
+ * - High contrast mode is supported with appropriate color overrides
+ * - Static color variants ensure sufficient contrast on different backgrounds
*
* ### Best practices
*
* - Always provide a descriptive `label` that explains what the progress represents
- * - Use determinate progress when possible to give users a clear sense of completion
+ * - Use specific, meaningful labels (e.g., "Uploading profile photo" instead of "Loading")
+ * - Use determinate progress (`progress="50"`) when possible to give users a clear sense of completion
* - For determinate progress, ensure the `progress` value accurately reflects the actual progress
+ * - Use indeterminate progress only when duration is truly unknown
* - Consider using `size="l"` for primary loading states to improve visibility
+ * - Ensure sufficient color contrast between the progress circle and its background
+ * - Use `static-color="white"` on dark backgrounds or `static-color="black"` on light backgrounds
+ * - Test with screen readers to verify progress announcements are clear and timely
+ * - Avoid updating progress values more frequently than every 1-2 seconds to prevent announcement overload
*/
export const Accessibility: Story = {
tags: ['a11y'],
args: {
progress: 60,
size: 'l',
- label: 'Uploading document',
+ label: 'Uploading presentation slides',
},
};
diff --git a/2nd-gen/packages/swc/components/status-light/DOC-COMPARISON.md b/2nd-gen/packages/swc/components/status-light/DOC-COMPARISON.md
deleted file mode 100644
index 4679052f210..00000000000
--- a/2nd-gen/packages/swc/components/status-light/DOC-COMPARISON.md
+++ /dev/null
@@ -1,152 +0,0 @@
-# Status light: 1st-gen vs 2nd-gen documentation comparison
-
-## Summary
-
-The 2nd-gen documentation successfully covers the core concepts but is missing some important features and states documented in 1st-gen. The migration is good but incomplete.
-
-## Missing content areas
-
-### Critical gaps
-
-1. **XL size option**
- - **1st-gen**: Documents and demonstrates `size="xl"`
- - **2nd-gen**: Only shows `s`, `m`, `l` sizes
- - **Priority**: High
- - **Recommendation**: Verify if `xl` size is supported in 2nd-gen implementation. If yes, add to stories and documentation.
-
-2. **Disabled state**
- - **1st-gen**: Has dedicated section showing disabled state with example: `disabled`
- - **2nd-gen**: No disabled state documentation
- - **Priority**: High
- - **Recommendation**: Add disabled state to States section if supported. Include visual example and accessibility note about `aria-disabled="true"`.
-
-3. **ARIA disabled support**
- - **1st-gen**: Explicitly documents: "When disabled, the component automatically sets `aria-disabled='true'`"
- - **2nd-gen**: No mention of ARIA disabled support
- - **Priority**: High (if disabled is supported)
- - **Recommendation**: Add to accessibility features section.
-
-### Nice-to-have additions
-
-4. **Package metadata**
- - **1st-gen**: Includes NPM version, bundle size, and Stackblitz badges
- - **2nd-gen**: No package metadata
- - **Priority**: Low
- - **Recommendation**: Consider adding if useful for developer reference.
-
-5. **Installation instructions**
- - **1st-gen**: Shows yarn add command and import statements
- - **2nd-gen**: Missing installation section
- - **Priority**: Low (may be intentional - centralized elsewhere)
- - **Recommendation**: If not documented globally, consider adding brief usage section.
-
-## Content depth differences
-
-### States documentation
-
-**1st-gen advantage**:
-
-- Has dedicated "States" section
-- Documents disabled state with clear explanation: "shows that a status exists, but is not available in that circumstance"
-- Provides use case: "maintain layout continuity and communicate that a status may become available later"
-
-**2nd-gen**:
-
-- No states section at all
-- This is a significant gap if disabled state is supported
-
-### Size documentation
-
-**1st-gen advantage**:
-
-- Four sizes: s, m, l, xl
-- Interactive tabs for each size
-
-**2nd-gen advantage**:
-
-- Clean story-based approach
-- Better for automated testing
-- Clear size hierarchy explanation
-
-**Gap**: Missing xl size (if supported in implementation)
-
-### Accessibility guidance
-
-**1st-gen advantage**:
-
-- Documents ARIA disabled support explicitly
-- Four best practices including "Consider the disabled state"
-
-**2nd-gen advantage**:
-
-- Well-structured features section
-- Clear formatting with numbered features and bullet points
-
-**Gap**: Missing ARIA disabled documentation and disabled state best practice
-
-### Variant documentation
-
-Both 1st-gen and 2nd-gen cover semantic and non-semantic variants comprehensively. The approaches differ but both are effective:
-
-- **1st-gen**: Uses interactive tabs
-- **2nd-gen**: Uses separate stories with clear descriptions
-
-## Recommendations by priority
-
-### High priority
-
-1. **Add disabled state documentation** (if supported)
- - Add to States section in stories
- - Show visual example
- - Document ARIA disabled support in accessibility section
-
-2. **Add XL size support** (if available in implementation)
- - Include in size story
- - Update size documentation
-
-3. **Verify non-semantic color variants** - Ensure all colors from 1st-gen are available
-
-### Medium priority
-
-4. **Expand accessibility section** with ARIA disabled information (if applicable)
-5. **Add States section** to usage.mdx if disabled or other states exist
-
-### Low priority
-
-6. Consider adding package metadata badges
-7. Add installation/usage section if not documented globally
-
-## Content quality assessment
-
-| Area | 1st-gen | 2nd-gen | Notes |
-| ----------------- | ----------------- | ------------------- | ------------------------------------------------------- |
-| **Installation** | ✅ Comprehensive | ❌ Missing | May be intentional |
-| **Anatomy** | ✅ Good | ✅ Good | Both clear and concise |
-| **Sizes** | ✅ Has XL | ⚠️ Missing XL | Verify if XL removed intentionally |
-| **Variants** | ✅ Comprehensive | ✅ Comprehensive | Both cover semantic/non-semantic well |
-| **States** | ✅ Disabled state | ❌ Missing entirely | Critical gap if disabled supported |
-| **Text wrapping** | ❌ Not documented | ✅ Has example | 2nd-gen advantage |
-| **Accessibility** | ✅ Good | ✅ Good | 1st-gen has ARIA disabled, 2nd-gen has better structure |
-
-## Overall assessment
-
-**Strengths of 2nd-gen migration**:
-
-- Clean, organized story structure
-- Better separation with clear sections (anatomy, options, a11y)
-- Added text wrapping example (not in 1st-gen)
-- More maintainable testing approach
-
-**Critical gaps to address**:
-
-- Missing disabled state (if supported in implementation)
-- Missing xl size (if supported)
-- Missing ARIA disabled documentation
-
-**Areas needing enhancement**:
-
-- Add States section if disabled or other states exist
-- Verify all size and variant options are documented
-- Consider adding installation instructions
-
-**Completeness score**: 80% - Core content migrated successfully, but missing critical state documentation and potentially one size option.
diff --git a/2nd-gen/packages/swc/components/status-light/stories/status-light.stories.ts b/2nd-gen/packages/swc/components/status-light/stories/status-light.stories.ts
index eb6aa4d5a40..93b73ed57dc 100644
--- a/2nd-gen/packages/swc/components/status-light/stories/status-light.stories.ts
+++ b/2nd-gen/packages/swc/components/status-light/stories/status-light.stories.ts
@@ -14,7 +14,12 @@ import type { Meta, StoryObj as Story } from '@storybook/web-components';
import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers';
import { StatusLight } from '@adobe/swc/status-light';
-import { capitalize } from '@spectrum-web-components/core/shared/utilities';
+import {
+ STATUSLIGHT_VARIANTS_COLOR_S2,
+ STATUSLIGHT_VARIANTS_SEMANTIC_S2,
+ StatusLightColorVariantS2,
+ StatusLightSemanticVariantS2,
+} from '@spectrum-web-components/core/components/status-light';
import '@adobe/swc/status-light';
@@ -24,17 +29,6 @@ import '@adobe/swc/status-light';
const { args, argTypes, template } = getStorybookHelpers('swc-status-light');
-const parameters = {
- flexLayout: true,
- styles: {
- gap: 'var(--spectrum-spacing-200)',
- 'flex-wrap': 'wrap',
- 'justify-content': 'center',
- // Used 80ch because that's generally considered the maximum readable width for text in a web page.
- 'max-inline-size': '80ch',
- },
-};
-
argTypes.variant = {
...argTypes.variant,
control: { type: 'select' },
@@ -47,23 +41,27 @@ argTypes.size = {
options: StatusLight.VALID_SIZES,
};
-args['default-slot'] = 'Status light';
-args.size = 'm';
-
/**
- * An `` is a great way to convey semantic meaning, such as statuses and categories.
- * It provides visual indicators through colored dots accompanied by descriptive text.
+ * Status lights describe the condition of an entity. Much like [badges](../?path=/docs/components-badge--readme), they can be used to convey semantic meaning, such as statuses and categories.
*/
const meta: Meta = {
title: 'Status light',
component: 'swc-status-light',
- argTypes,
parameters: {
docs: {
subtitle: `Status lights convey semantic meaning through colored dots accompanied by descriptive text.`,
},
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/design/Mngz9H7WZLbrCvGQf3GnsY/S2---Desktop?node-id=36797-954',
+ },
+ stackblitz: {
+ url: 'https://stackblitz.com/edit/vitejs-vite-y2kz1rkx?file=package.json',
+ },
+ flexLayout: 'row-wrap',
},
args,
+ argTypes,
render: (args) => template(args),
tags: ['migrated'],
};
@@ -71,16 +69,57 @@ const meta: Meta = {
export default meta;
// ────────────────────
-// AUTODOCS STORY
+// HELPERS
// ────────────────────
-type StatusLightVariant = typeof StatusLight.prototype.variant;
-type StatusLightSize = typeof StatusLight.prototype.size;
+const semanticLabels = {
+ info: 'Active',
+ neutral: 'Archived',
+ positive: 'Approved',
+ notice: 'Pending approval',
+ negative: 'Rejected',
+} as const satisfies Record;
+
+const nonSemanticLabels = {
+ yellow: 'Operations',
+ chartreuse: 'Quality',
+ celery: 'Documentation',
+ seafoam: 'Support',
+ cyan: 'Analytics',
+ indigo: 'Engineering',
+ purple: 'Product',
+ fuchsia: 'Marketing',
+ magenta: 'Design',
+ pink: 'Creative',
+ turquoise: 'Training',
+ brown: 'Facilities',
+ cinnamon: 'Compliance',
+ silver: 'Version 1.2.10',
+} as const satisfies Record;
+
+// ────────────────────
+// AUTODOCS STORY
+// ────────────────────
export const Playground: Story = {
tags: ['autodocs', 'dev'],
args: {
- 'default-slot': 'New Feature',
+ size: 'm',
+ variant: 'info',
+ 'default-slot': 'Active',
+ },
+};
+
+// ────────────────────
+// OVERVIEW STORY
+// ────────────────────
+
+export const Overview: Story = {
+ tags: ['overview'],
+ args: {
+ size: 'm',
+ variant: 'info',
+ 'default-slot': 'Active',
},
};
@@ -89,7 +128,14 @@ export const Playground: Story = {
// ──────────────────────────
/**
- * A status light consists of a colored dot indicator and a required text label. The dot's color represents the status or category, while the text provides additional context.
+ * A status light consists of:
+ *
+ * 1. **Colored dot indicator** - Visual representation of status or category
+ * 2. **Text label** - Descriptive text providing context
+ *
+ * ### Content
+ *
+ * - **Default slot**: Text content describing the status or category (required for accessibility)
*/
export const Anatomy: Story = {
render: (args) => html`
@@ -110,96 +156,89 @@ export const Anatomy: Story = {
// ──────────────────────────
/**
- * Status lights come in four different sizes: small, medium, large, and extra-large. The medium size is the default and most frequently used option. Use the other sizes sparingly; they should be used to create a hierarchy of importance within the page.
+ * Status lights come in four sizes to fit various contexts:
+ *
+ * - **Small (`s`)**: Used for inline indicators or space-constrained areas
+ * - **Medium (`m`)**: Default size, used for typical use cases
+ * - **Large (`l`)**: Used for prominent displays or primary content areas
+ * - **Extra-large (`xl`)**: Maximum visibility for high-priority statuses
+ *
+ * All sizes shown below for comparison.
*/
export const Sizes: Story = {
- render: () =>
- html`${StatusLight.VALID_SIZES.map(
- (size: StatusLightSize) => html`
-
- ${sizeMap(size)}
-
- `
- )} `,
- parameters: { ...parameters, 'section-order': 0 },
+ render: (args) => html`
+ ${template({ ...args, size: 's', 'default-slot': 'Small' })}
+ ${template({ ...args, size: 'm', 'default-slot': 'Medium' })}
+ ${template({ ...args, size: 'l', 'default-slot': 'Large' })}
+ ${template({ ...args, size: 'xl', 'default-slot': 'Extra-large' })}
+ `,
+ parameters: { 'section-order': 1 },
tags: ['options'],
};
-
/**
- * When status lights have a semantic meaning, they use semantic colors. Use these variants for the following statuses:
- *
- * - **Informative**: active, in use, live, published
- * - **Neutral**: archived, deleted, paused, draft, not started, ended
- * - **Positive**: approved, complete, success, new, purchased, licensed
- * - **Notice**: needs approval, pending, scheduled, syncing, indexing, processing
- * - **Negative**: error, alert, rejected, failed
+ * Semantic variants provide meaning through color:
*
- * Semantic status lights should never be used for color coding categories or labels, and vice versa.
+ * - **`info`**: Active, in use, live, published
+ * - **`neutral`**: Archived, deleted, paused, draft, not started, ended
+ * - **`positive`**: Approved, complete, success, new, purchased, licensed
+ * - **`notice`**: Needs approval, pending, scheduled, syncing, indexing, processing
+ * - **`negative`**: Error, alert, rejected, failed
*/
export const SemanticVariants: Story = {
- render: () =>
- html` ${StatusLight.VARIANTS_SEMANTIC.map(
- (variant: StatusLightVariant) => html`
- ${capitalize(variant)}
- `
- )}`,
- parameters: { ...parameters, 'section-order': 1 },
+ render: (args) => html`
+ ${STATUSLIGHT_VARIANTS_SEMANTIC_S2.map(
+ (variant: StatusLightSemanticVariantS2) =>
+ template({
+ ...args,
+ variant,
+ 'default-slot': semanticLabels[variant],
+ })
+ )}
+ `,
+ parameters: { 'section-order': 2 },
tags: ['options'],
};
/**
- * When status lights are used to color code categories and labels that are commonly found in data visualization, they use label colors. The ideal usage for these is when there are 8 or fewer categories or labels being color coded.
+ * Non-semantic variants use color-coded categories, ideal for data visualization and labeling.
+ * Best used when there are **8 or fewer** categories being color coded.
+ *
+ * **Note**: The `pink`, `turquoise`, `brown`, `cinnamon`, and `silver` variants are new in 2nd-gen and not available in 1st-gen.
*/
export const NonSemanticVariants: Story = {
- render: () =>
- html`${StatusLight.VARIANTS_COLOR.map(
- (variant: StatusLightVariant) => html`
- ${capitalize(variant)}
- `
- )}`,
- parameters: { ...parameters, 'section-order': 2 },
+ render: (args) => html`
+ ${STATUSLIGHT_VARIANTS_COLOR_S2.map(
+ (variant: StatusLightColorVariantS2) =>
+ template({
+ ...args,
+ variant,
+ 'default-slot': nonSemanticLabels[variant],
+ })
+ )}
+ `,
+ parameters: { 'section-order': 3 },
tags: ['options'],
};
NonSemanticVariants.storyName = 'Non-semantic variants';
-// ──────────────────────────
-// STATES STORIES
-// ──────────────────────────
-
-/**
- * A status light in a disabled state shows that a status exists, but is not available in that circumstance.
- * This can be used to maintain layout continuity and communicate that a status may become available later.
- *
- * - **ARIA support**: When disabled, the component automatically sets `aria-disabled="true"`
- */
-/*
- @todo ois this story needed?
- export const Disabled: Story = {
- render: () => html`
- Approved (enabled)
- Approved (disabled)
- `,
- tags: ['states'],
- parameters: parameters,
-};*/
-
// ──────────────────────────────
// BEHAVIORS STORIES
// ──────────────────────────────
/**
* When the text is too long for the horizontal space available, it wraps to form another line.
+ * You can control the wrapping behavior by setting a `max-inline-size` style on the component.
*/
export const TextWrapping: Story = {
- render: () =>
- html`
- Document processing in progress - please wait while we validate your
- submission
- `,
+ render: (args) => html`
+ ${template({
+ ...args,
+ variant: 'positive',
+ 'default-slot':
+ 'Document processing in progress - please wait while we validate your submission',
+ style: 'max-inline-size: 200px',
+ })}
+ `,
tags: ['behaviors'],
};
@@ -212,48 +251,67 @@ export const TextWrapping: Story = {
*
* The `` element implements several accessibility features:
*
- * 1. **Color meaning**: Colors are used in combination with text labels to ensure that status information is not conveyed through color alone
- * 2. **ARIA support**: When disabled, the component automatically sets `aria-disabled="true"`
+ * #### Visual accessibility
+ *
+ * - Status information is conveyed through both color and text labels, not relying on color alone
+ * - High contrast mode is supported with appropriate color overrides
+ * - Sufficient color contrast is maintained between the status dot and background
+ *
+ * #### Semantic meaning
+ *
+ * - Semantic variants provide consistent color associations for common statuses
+ * - Text labels provide clear context for all users
*
* ### Best practices
*
- * - Use semantic variants (`positive`, `negative`, `notice`, `info`, `neutral`) when the status has specific meaning
- * - Include a clear, descriptive text label that explains the status
+ * - Always provide a descriptive text label that explains the status
+ * - Use semantic variants (`info`, `positive`, `negative`, `notice`, `neutral`) when the status has specific meaning
+ * - Use meaningful, specific labels (e.g., "Approved" instead of "Green")
* - Ensure sufficient color contrast between the status light and its background
+ * - For non-semantic variants, ensure the text label provides complete context
*/
-// @todo disabled state
-// - Consider using the disabled state to maintain layout continuity when a status is temporarily unavailable
export const Accessibility: Story = {
- render: () => html`
- approved
- rejected
- needs approval
- new feature
- version 1.2.10
- online
- busy
- away
+ render: (args) => html`
+ ${template({
+ ...args,
+ variant: 'positive',
+ 'default-slot': semanticLabels['positive'],
+ })}
+ ${template({
+ ...args,
+ variant: 'negative',
+ 'default-slot': semanticLabels['negative'],
+ })}
+ ${template({
+ ...args,
+ variant: 'notice',
+ 'default-slot': semanticLabels['notice'],
+ })}
+ ${template({
+ ...args,
+ variant: 'info',
+ 'default-slot': semanticLabels['info'],
+ })}
+ ${template({
+ ...args,
+ variant: 'neutral',
+ 'default-slot': semanticLabels['neutral'],
+ })}
+ ${template({
+ ...args,
+ variant: 'celery',
+ 'default-slot': nonSemanticLabels['celery'],
+ })}
+ ${template({
+ ...args,
+ variant: 'yellow',
+ 'default-slot': nonSemanticLabels['yellow'],
+ })}
+ ${template({
+ ...args,
+ variant: 'silver',
+ 'default-slot': nonSemanticLabels['silver'],
+ })}
`,
tags: ['a11y'],
};
-
-// ────────────────────────
-// HELPER FUNCTIONS
-// ────────────────────────
-
-/* @todo Pull this up into a utility function for more components to leverage. Are all sizes accounted for? */
-function sizeMap(str?: StatusLightSize): string {
- const sizeLabels = {
- labels: {
- xxs: 'Extra-extra-small',
- xs: 'Extra-small',
- s: 'Small',
- m: 'Medium',
- l: 'Large',
- xl: 'Extra-large',
- xxl: 'Extra-extra-large',
- },
- };
-
- return str ? sizeLabels.labels[str] : '';
-}
diff --git a/2nd-gen/packages/swc/package.json b/2nd-gen/packages/swc/package.json
index 1da141bc3c2..1b6bec38eab 100644
--- a/2nd-gen/packages/swc/package.json
+++ b/2nd-gen/packages/swc/package.json
@@ -73,6 +73,7 @@
"playwright": "1.53.1",
"postcss": "8.5.6",
"postcss-preset-env": "10.4.0",
+ "prettier": "3.4.2",
"react": "19.1.1",
"react-dom": "19.1.1",
"remark-gfm": "4.0.0",
diff --git a/yarn.lock b/yarn.lock
index 091ab7b90c3..9153158e5bd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -195,6 +195,7 @@ __metadata:
playwright: "npm:1.53.1"
postcss: "npm:8.5.6"
postcss-preset-env: "npm:10.4.0"
+ prettier: "npm:3.4.2"
react: "npm:19.1.1"
react-dom: "npm:19.1.1"
remark-gfm: "npm:4.0.0"
@@ -25726,6 +25727,15 @@ __metadata:
languageName: node
linkType: hard
+"prettier@npm:3.4.2":
+ version: 3.4.2
+ resolution: "prettier@npm:3.4.2"
+ bin:
+ prettier: bin/prettier.cjs
+ checksum: 10c0/99e076a26ed0aba4ebc043880d0f08bbb8c59a4c6641cdee6cdadf2205bdd87aa1d7823f50c3aea41e015e99878d37c58d7b5f0e663bba0ef047f94e36b96446
+ languageName: node
+ linkType: hard
+
"prettier@npm:3.6.2":
version: 3.6.2
resolution: "prettier@npm:3.6.2"