omnidesign 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/QUICKREF.md +150 -0
  3. package/README.md +576 -0
  4. package/bin/cli.js +390 -0
  5. package/bin/detect-ide.js +50 -0
  6. package/bin/install.js +8 -0
  7. package/logo.jpg +0 -0
  8. package/package.json +84 -0
  9. package/recipes/components/README.md +29 -0
  10. package/recipes/components/agent-card.md +314 -0
  11. package/recipes/components/ai-chat.md +252 -0
  12. package/recipes/components/bento-grid.md +186 -0
  13. package/recipes/components/code-block.md +503 -0
  14. package/recipes/components/file-upload.md +483 -0
  15. package/recipes/components/forms.md +238 -0
  16. package/recipes/components/hero-section.md +161 -0
  17. package/recipes/components/navbar.md +214 -0
  18. package/recipes/components/prompt-input.md +293 -0
  19. package/recipes/components/thinking-indicator.md +372 -0
  20. package/recipes/motion/README.md +3 -0
  21. package/recipes/motion/motion-system.md +437 -0
  22. package/recipes/patterns/README.md +3 -0
  23. package/skills/aider/omnidesign.md +67 -0
  24. package/skills/amp/SKILL.md +114 -0
  25. package/skills/antigravity/SKILL.md +114 -0
  26. package/skills/claude/omnidesign.md +111 -0
  27. package/skills/continue/omnidesign.yaml +29 -0
  28. package/skills/cursor/omnidesign.md +110 -0
  29. package/skills/kilo/SKILL.md +114 -0
  30. package/skills/opencode/omnidesign.md +110 -0
  31. package/skills/vscode/package.json +66 -0
  32. package/skills/zed/omnidesign.json +7 -0
  33. package/tokens/motion/README.md +3 -0
  34. package/tokens/primitives/README.md +3 -0
  35. package/tokens/primitives/color.json +219 -0
  36. package/tokens/primitives/motion.json +56 -0
  37. package/tokens/primitives/radii.json +37 -0
  38. package/tokens/primitives/shadows.json +34 -0
  39. package/tokens/primitives/spacing.json +67 -0
  40. package/tokens/primitives/typography.json +127 -0
  41. package/tokens/semantic/README.md +3 -0
  42. package/tokens/semantic/color.json +114 -0
  43. package/tokens/semantic/motion.json +44 -0
  44. package/tokens/semantic/radii.json +29 -0
  45. package/tokens/semantic/shadows.json +24 -0
  46. package/tokens/semantic/spacing.json +69 -0
  47. package/tokens/semantic/typography.json +118 -0
  48. package/tokens/shadows/README.md +3 -0
  49. package/tokens/themes/README.md +3 -0
  50. package/tokens/themes/berry.json +143 -0
  51. package/tokens/themes/brutalist.json +143 -0
  52. package/tokens/themes/coral.json +143 -0
  53. package/tokens/themes/corporate.json +143 -0
  54. package/tokens/themes/cream.json +143 -0
  55. package/tokens/themes/cyberpunk.json +143 -0
  56. package/tokens/themes/daylight.json +143 -0
  57. package/tokens/themes/deep-space.json +143 -0
  58. package/tokens/themes/forest.json +143 -0
  59. package/tokens/themes/graphite.json +143 -0
  60. package/tokens/themes/lavender.json +143 -0
  61. package/tokens/themes/midnight.json +143 -0
  62. package/tokens/themes/mint.json +143 -0
  63. package/tokens/themes/navy.json +143 -0
  64. package/tokens/themes/noir.json +143 -0
  65. package/tokens/themes/obsidian.json +143 -0
  66. package/tokens/themes/ocean.json +143 -0
  67. package/tokens/themes/paper.json +143 -0
  68. package/tokens/themes/ruby.json +143 -0
  69. package/tokens/themes/slate.json +143 -0
  70. package/tokens/themes/snow.json +143 -0
  71. package/tokens/themes/solar.json +143 -0
  72. package/tokens/themes/spring.json +143 -0
  73. package/tokens/themes/starry-night.json +143 -0
  74. package/tokens/themes/sunset.json +143 -0
  75. package/tokens/typography/FONT_GUIDE.md +381 -0
  76. package/tokens/typography/README.md +37 -0
  77. package/tokens/typography/font-collection.json +221 -0
@@ -0,0 +1,186 @@
1
+ # Bento Grid
2
+
3
+ A flexible, variable-size card grid layout inspired by bento box design. Supports mixed card dimensions (1×1, 2×1, 1×2, 2×2) with hover lift effects, focus states, and content variations.
4
+
5
+ ## When to Use
6
+
7
+ **Use bento grids for:**
8
+ - Portfolio or case study showcases
9
+ - Feature highlights with visual variety
10
+ - Product gallery with mixed content types
11
+ - Dashboard layouts with different widget sizes
12
+ - Blog or article collections with featured posts
13
+
14
+ **Don't use for:**
15
+ - Data tables (use proper table semantics)
16
+ - Uniform card lists (use standard grid)
17
+ - Content requiring strict reading order
18
+ - Mobile-first experiences (complex layouts break on small screens)
19
+
20
+ ## Component Anatomy
21
+
22
+ ```
23
+ ┌──────────────────────────────────────────────────────┐
24
+ │ [1×1 Card] [2×1 Card - Featured] │
25
+ │ [Spans 2 columns] │
26
+ ├──────────────────────────────────────────────────────┤
27
+ │ [1×2 Card] [1×1 Card] [1×1 Card] │
28
+ │ [Spans 2 [Standard] [Standard] │
29
+ │ rows] │
30
+ ├──────────────────────────────────────────────────────┤
31
+ │ [2×2 Card - Hero] │
32
+ │ [Spans 2×2 grid] │
33
+ └──────────────────────────────────────────────────────┘
34
+ ```
35
+
36
+ ## State Matrix
37
+
38
+ | State | Visual Treatment | Tokens Used |
39
+ |-------|------------------|-------------|
40
+ | **Default** | Card at rest, subtle border, shadow.card applied | `color.surface.raised`, `shadow.card`, `radius.card` |
41
+ | **Hover** | Card lifts 4px (transform: translateY(-4px)), shadow deepens, opacity 100% | `motion.hover`, `shadow.card` (elevated) |
42
+ | **Focus** | Focus ring around card (2px, inset), keyboard navigation visible | `color.focus.ring`, `space.inlineSm` |
43
+ | **Active** | Card background slightly darkened, border color strengthened | `color.surface.sunken`, `color.border.strong` |
44
+ | **Disabled** | Opacity 50%, cursor not-allowed, no hover effect | `color.text.muted`, `motion.hover` (disabled) |
45
+
46
+ ## Token Usage
47
+
48
+ ### Colors
49
+ - **Card Background**: `color.surface.raised` for elevated appearance
50
+ - **Card Border**: `color.border.default` for subtle separation
51
+ - **Text**: `color.text.default` for headings, `color.text.muted` for descriptions
52
+ - **Overlay**: `color.surface.overlay` with 10% opacity for hover darkening
53
+
54
+ ### Spacing
55
+ - **Card Padding**: `space.cardPadding` (6px) internal spacing
56
+ - **Grid Gap**: `space.cardGap` (4px) between cards
57
+ - **Content Stack**: `space.stackMd` (4px) between card title and description
58
+
59
+ ### Typography
60
+ - **Card Title**: `typography.heading.card` (xl, semibold)
61
+ - **Card Description**: `typography.body.default` or `typography.body.small`
62
+ - **Card Meta**: `typography.ui.caption` for dates, tags, or metadata
63
+
64
+ ### Shadows & Radii
65
+ - **Card Radius**: `radius.card` for rounded corners
66
+ - **Card Shadow**: `shadow.card` at rest, elevated shadow on hover
67
+ - **Focus Ring**: 2px inset ring with `color.focus.ring`
68
+
69
+ ## Implementation Notes
70
+
71
+ ### CSS/HTML Approach
72
+
73
+ **Structure:**
74
+ ```
75
+ <div class="bento-grid">
76
+ <article class="bento-card bento-card--1x1">
77
+ <img src="..." alt="..." class="bento-card__image">
78
+ <div class="bento-card__content">
79
+ <h3 class="bento-card__title">Title</h3>
80
+ <p class="bento-card__description">Description</p>
81
+ </div>
82
+ </article>
83
+
84
+ <article class="bento-card bento-card--2x1">
85
+ <!-- Featured card content -->
86
+ </article>
87
+
88
+ <!-- More cards... -->
89
+ </div>
90
+ ```
91
+
92
+ **Key CSS patterns:**
93
+ - Use CSS Grid with `display: grid; grid-template-columns: repeat(4, 1fr)` (4-column base)
94
+ - Apply `grid-column: span 1/2/4` and `grid-row: span 1/2` for sizing variants
95
+ - Use `transition: all 200ms ease-out` for smooth hover effects
96
+ - Apply `transform: translateY(-4px)` on hover for lift effect
97
+ - Use `box-shadow` with `shadow.card` token for depth
98
+ - Implement focus ring with `outline: 2px solid color.focus.ring; outline-offset: 2px`
99
+
100
+ **Responsive grid:**
101
+ - Desktop (1024px+): 4-column grid with full size variations
102
+ - Tablet (768px): 2-column grid, reduce card sizes (1×1 only, no 2×2)
103
+ - Mobile (375px): 1-column grid, all cards 1×1, stack vertically
104
+
105
+ **Content variations:**
106
+ - Image-only cards: Full-height image with overlay text
107
+ - Text-only cards: Heading + description + optional CTA
108
+ - Mixed cards: Image + text side-by-side or stacked
109
+ - Interactive cards: Hover reveals additional content or CTA
110
+
111
+ ### React Approach
112
+
113
+ **Component structure:**
114
+ - Create `<BentoGrid>` wrapper managing grid layout and responsive behavior
115
+ - Create `<BentoCard>` component accepting `size` prop (1x1, 2x1, 1x2, 2x2)
116
+ - Use `children` prop for flexible content (image, text, CTA, etc.)
117
+ - Implement `onHover` state for lift effect and shadow elevation
118
+ - Use CSS Grid with CSS-in-JS or Tailwind for responsive sizing
119
+
120
+ **State management:**
121
+ - Track hover state per card for visual feedback
122
+ - Use CSS `:hover` and `:focus-visible` for interactive states
123
+ - Implement `useCallback` for hover handlers to prevent unnecessary re-renders
124
+ - Consider `IntersectionObserver` for lazy-loading card images
125
+
126
+ **Responsive behavior:**
127
+ - Use `useMediaQuery` hook to detect breakpoints
128
+ - Adjust grid columns and card sizes based on viewport
129
+ - Stack cards vertically on mobile, maintain grid on desktop
130
+
131
+ ## Acceptance Criteria
132
+
133
+ - [ ] Grid uses CSS Grid with proper column/row spanning
134
+ - [ ] Cards support 1×1, 2×1, 1×2, and 2×2 sizes
135
+ - [ ] Hover effect lifts card 4px with shadow elevation
136
+ - [ ] Focus ring visible on keyboard navigation (2px, `color.focus.ring`)
137
+ - [ ] Card padding uses `space.cardPadding` token
138
+ - [ ] Grid gap uses `space.cardGap` token
139
+ - [ ] Responsive: 4-column desktop, 2-column tablet, 1-column mobile
140
+ - [ ] All card titles use `typography.heading.card` token
141
+ - [ ] All descriptions use `typography.body.default` or `typography.body.small`
142
+ - [ ] Card background uses `color.surface.raised` token
143
+ - [ ] Respects `prefers-reduced-motion` (no lift effect)
144
+ - [ ] All text passes color contrast ratio (4.5:1 for body, 3:1 for large text)
145
+
146
+ ## Accessibility
147
+
148
+ ### ARIA & Semantics
149
+ - Use `<article>` for each card (semantic content container)
150
+ - Heading hierarchy: `<h2>` or `<h3>` for card titles (depends on page context)
151
+ - Use `<img>` with descriptive `alt` text for images
152
+ - If card is clickable, use `<a>` or `<button>` with clear link text
153
+
154
+ ### Keyboard Navigation
155
+ - Tab order: Left-to-right, top-to-bottom through cards
156
+ - Focus ring must be visible with 2px width, `color.focus.ring`
157
+ - Ensure focus ring has sufficient contrast against card background
158
+ - Clickable cards must be keyboard accessible (use `<a>` or `<button>`)
159
+
160
+ ### Screen Readers
161
+ - Card titles must be descriptive and unique
162
+ - Image `alt` text must describe content, not just "image"
163
+ - If card contains multiple interactive elements, use `aria-label` for context
164
+ - Use `aria-current="page"` for active/selected cards
165
+
166
+ ### Color & Contrast
167
+ - Text must meet WCAG AA (4.5:1 for body, 3:1 for large text)
168
+ - Don't rely on color alone to indicate card state
169
+ - Ensure sufficient contrast between card background and text
170
+ - Test with WebAIM Contrast Checker
171
+
172
+ ### Motion
173
+ - Respect `prefers-reduced-motion: reduce` by disabling lift effect
174
+ - Hover animations should not auto-play
175
+ - Avoid rapid or flashing animations
176
+
177
+ ### Touch Targets
178
+ - Cards must be minimum 48px × 48px for touch targets
179
+ - Maintain `space.cardGap` (4px) between cards for touch accuracy
180
+ - Ensure clickable areas are easily tappable on mobile
181
+
182
+ ### Content Considerations
183
+ - Avoid relying on image alone to convey meaning
184
+ - Provide text alternative for image-heavy cards
185
+ - Ensure card order makes sense without visual layout
186
+ - Test with screen readers (NVDA, JAWS, VoiceOver)
@@ -0,0 +1,503 @@
1
+ # AI Code Block
2
+
3
+ Syntax-highlighted code display with copy, run, and diff capabilities for AI-generated code.
4
+
5
+ ## When to Use
6
+ - Displaying AI-generated code
7
+ - Code explanations with syntax highlighting
8
+ - Before/after code comparisons
9
+ - Interactive code playgrounds
10
+ - Terminal/command output
11
+
12
+ ## Anatomy
13
+
14
+ ```
15
+ ┌─────────────────────────────────────────────────────────────┐
16
+ │ CodeBlock │
17
+ │ ├─ BlockHeader (language, filename, actions) │
18
+ │ │ ├─ LanguageBadge │
19
+ │ │ ├─ Filename │
20
+ │ │ └─ ActionButtons (copy, run, download) │
21
+ │ ├─ BlockContent (syntax highlighted code) │
22
+ │ │ ├─ LineNumbers (optional) │
23
+ │ │ └─ CodeContent (highlighted) │
24
+ │ └─ BlockFooter (execution result, apply button) │
25
+ └─────────────────────────────────────────────────────────────┘
26
+ ```
27
+
28
+ ## Token Usage
29
+
30
+ ```css
31
+ /* Code Block Container */
32
+ .code-block {
33
+ background: var(--color-surface-sunken);
34
+ border: 1px solid var(--color-border-default);
35
+ border-radius: var(--radius-lg);
36
+ overflow: hidden;
37
+ font-family: var(--font-mono);
38
+ font-size: var(--font-size-sm);
39
+ line-height: var(--line-height-snug);
40
+ }
41
+
42
+ /* Block Header */
43
+ .code-header {
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: space-between;
47
+ padding: var(--spacing-sm) var(--spacing-md);
48
+ background: rgba(0, 0, 0, 0.2);
49
+ border-bottom: 1px solid var(--color-border-default);
50
+ }
51
+
52
+ .language-badge {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ padding: var(--spacing-2xs) var(--spacing-xs);
56
+ background: rgba(37, 99, 235, 0.15);
57
+ color: var(--color-interactive-primary);
58
+ border-radius: var(--radius-sm);
59
+ font-size: var(--font-size-xs);
60
+ font-weight: var(--font-weight-medium);
61
+ text-transform: uppercase;
62
+ letter-spacing: 0.05em;
63
+ }
64
+
65
+ .filename {
66
+ font-family: var(--font-mono);
67
+ font-size: var(--font-size-xs);
68
+ color: var(--color-text-muted);
69
+ margin-left: var(--spacing-sm);
70
+ }
71
+
72
+ /* Action Buttons */
73
+ .code-actions {
74
+ display: flex;
75
+ gap: var(--spacing-xs);
76
+ }
77
+
78
+ .code-action-btn {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: var(--spacing-2xs);
82
+ padding: var(--spacing-2xs) var(--spacing-xs);
83
+ background: transparent;
84
+ border: 1px solid var(--color-border-default);
85
+ border-radius: var(--radius-md);
86
+ color: var(--color-text-muted);
87
+ font-size: var(--font-size-xs);
88
+ cursor: pointer;
89
+ transition: all var(--duration-fast);
90
+ }
91
+
92
+ .code-action-btn:hover {
93
+ background: var(--color-surface-raised);
94
+ border-color: var(--color-border-strong);
95
+ color: var(--color-text-default);
96
+ }
97
+
98
+ .code-action-btn.success {
99
+ background: rgba(34, 197, 94, 0.15);
100
+ border-color: rgba(34, 197, 94, 0.3);
101
+ color: #22C55E;
102
+ }
103
+
104
+ /* Code Content */
105
+ .code-content {
106
+ padding: var(--spacing-md);
107
+ overflow-x: auto;
108
+ background: var(--color-surface-sunken);
109
+ }
110
+
111
+ /* Line Numbers */
112
+ .code-with-lines {
113
+ display: flex;
114
+ gap: var(--spacing-md);
115
+ }
116
+
117
+ .line-numbers {
118
+ display: flex;
119
+ flex-direction: column;
120
+ color: var(--color-text-muted);
121
+ font-size: var(--font-size-xs);
122
+ text-align: right;
123
+ user-select: none;
124
+ border-right: 1px solid var(--color-border-default);
125
+ padding-right: var(--spacing-sm);
126
+ }
127
+
128
+ .line-number {
129
+ line-height: 1.8;
130
+ }
131
+
132
+ /* Syntax Highlighting (Shiki/Prism compatible) */
133
+ .code-content pre {
134
+ margin: 0;
135
+ background: transparent !important;
136
+ }
137
+
138
+ .code-content code {
139
+ font-family: var(--font-mono);
140
+ font-size: inherit;
141
+ background: transparent;
142
+ padding: 0;
143
+ }
144
+
145
+ /* Diff View */
146
+ .code-diff {
147
+ display: flex;
148
+ flex-direction: column;
149
+ }
150
+
151
+ .diff-line {
152
+ display: flex;
153
+ padding: 0 var(--spacing-md);
154
+ line-height: 1.8;
155
+ }
156
+
157
+ .diff-line.added {
158
+ background: rgba(34, 197, 94, 0.1);
159
+ }
160
+
161
+ .diff-line.added::before {
162
+ content: '+';
163
+ color: #22C55E;
164
+ margin-right: var(--spacing-sm);
165
+ font-weight: var(--font-weight-bold);
166
+ }
167
+
168
+ .diff-line.removed {
169
+ background: rgba(239, 68, 68, 0.1);
170
+ }
171
+
172
+ .diff-line.removed::before {
173
+ content: '-';
174
+ color: var(--color-status-error);
175
+ margin-right: var(--spacing-sm);
176
+ font-weight: var(--font-weight-bold);
177
+ }
178
+
179
+ .diff-line.unchanged {
180
+ color: var(--color-text-muted);
181
+ }
182
+
183
+ .diff-line.unchanged::before {
184
+ content: ' ';
185
+ margin-right: var(--spacing-sm);
186
+ }
187
+
188
+ /* Apply Changes Button */
189
+ .code-footer {
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: space-between;
193
+ padding: var(--spacing-sm) var(--spacing-md);
194
+ background: var(--color-surface-raised);
195
+ border-top: 1px solid var(--color-border-default);
196
+ }
197
+
198
+ .apply-btn {
199
+ display: flex;
200
+ align-items: center;
201
+ gap: var(--spacing-xs);
202
+ padding: var(--spacing-xs) var(--spacing-md);
203
+ background: var(--color-interactive-primary);
204
+ color: var(--color-text-inverted);
205
+ border: none;
206
+ border-radius: var(--radius-md);
207
+ font-size: var(--font-size-sm);
208
+ font-weight: var(--font-weight-medium);
209
+ cursor: pointer;
210
+ transition: all var(--duration-fast);
211
+ }
212
+
213
+ .apply-btn:hover {
214
+ background: var(--color-interactive-primary-hover);
215
+ }
216
+
217
+ /* Terminal Style */
218
+ .terminal-block {
219
+ background: #0D1117;
220
+ border-radius: var(--radius-lg);
221
+ font-family: var(--font-mono);
222
+ font-size: var(--font-size-sm);
223
+ }
224
+
225
+ .terminal-header {
226
+ display: flex;
227
+ align-items: center;
228
+ gap: var(--spacing-sm);
229
+ padding: var(--spacing-sm) var(--spacing-md);
230
+ background: rgba(255, 255, 255, 0.05);
231
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
232
+ }
233
+
234
+ .terminal-buttons {
235
+ display: flex;
236
+ gap: 6px;
237
+ }
238
+
239
+ .terminal-btn {
240
+ width: 12px;
241
+ height: 12px;
242
+ border-radius: var(--radius-full);
243
+ }
244
+
245
+ .terminal-btn.red { background: #FF5F56; }
246
+ .terminal-btn.yellow { background: #FFBD2E; }
247
+ .terminal-btn.green { background: #27C93F; }
248
+
249
+ .terminal-content {
250
+ padding: var(--spacing-md);
251
+ color: #E6EDF3;
252
+ line-height: 1.6;
253
+ }
254
+
255
+ .terminal-prompt {
256
+ color: #7EE787;
257
+ }
258
+
259
+ .terminal-command {
260
+ color: #E6EDF3;
261
+ }
262
+
263
+ .terminal-output {
264
+ color: #8B949E;
265
+ }
266
+ ```
267
+
268
+ ## State Matrix
269
+
270
+ | Element | Default | Hover | Copied | Running |
271
+ |---------|---------|-------|--------|---------|
272
+ | Copy Button | ghost | raised | success green | - |
273
+ | Run Button | ghost | raised | - | spinner |
274
+ | Apply Button | primary | primary-hover | - | loading |
275
+ | Code Block | sunken bg | - | - | - |
276
+ | Diff Line | context-based | - | - | - |
277
+
278
+ ## Accessibility
279
+
280
+ ```html
281
+ <figure class="code-block" role="region" aria-label="Code example: example.tsx">
282
+ <figcaption class="code-header">
283
+ <div class="file-info">
284
+ <span class="language-badge">TSX</span>
285
+ <span class="filename">example.tsx</span>
286
+ </div>
287
+
288
+ <div class="code-actions">
289
+ <button
290
+ class="code-action-btn"
291
+ aria-label="Copy code to clipboard"
292
+ onClick={handleCopy}
293
+ >
294
+ <CopyIcon aria-hidden="true" />
295
+ <span>Copy</span>
296
+ </button>
297
+
298
+ <button
299
+ class="code-action-btn"
300
+ aria-label="Download file"
301
+ onClick={handleDownload}
302
+ >
303
+ <DownloadIcon aria-hidden="true" />
304
+ <span>Download</span>
305
+ </button>
306
+ </div>
307
+ </figcaption>
308
+
309
+ <div class="code-with-lines">
310
+ <div class="line-numbers" aria-hidden="true">
311
+ <span class="line-number">1</span>
312
+ <span class="line-number">2</span>
313
+ <span class="line-number">3</span>
314
+ </div>
315
+
316
+ <pre class="code-content">
317
+ <code>const example = "Hello World";</code>
318
+ </pre>
319
+ </div>
320
+ </figure>
321
+
322
+ <!-- Screen reader announcement -->
323
+ <div aria-live="polite" aria-atomic="true" class="sr-only">
324
+ Code copied to clipboard
325
+ </div>
326
+ ```
327
+
328
+ ## Example: Basic Code Block
329
+
330
+ ```tsx
331
+ function CodeBlock({ code, language, filename, onCopy }) {
332
+ const [copied, setCopied] = useState(false);
333
+
334
+ const handleCopy = async () => {
335
+ await navigator.clipboard.writeText(code);
336
+ setCopied(true);
337
+ setTimeout(() => setCopied(false), 2000);
338
+ onCopy?.();
339
+ };
340
+
341
+ return (
342
+ <figure className="code-block">
343
+ <figcaption className="code-header">
344
+ <div className="file-info">
345
+ <span className="language-badge">{language}</span>
346
+ {filename && <span className="filename">{filename}</span>}
347
+ </div>
348
+
349
+ <div className="code-actions">
350
+ <button
351
+ className={`code-action-btn ${copied ? 'success' : ''}`}
352
+ onClick={handleCopy}
353
+ aria-label={copied ? 'Copied' : 'Copy code'}
354
+ >
355
+ {copied ? <CheckIcon /> : <CopyIcon />}
356
+ <span>{copied ? 'Copied!' : 'Copy'}</span>
357
+ </button>
358
+ </div>
359
+ </figcaption>
360
+
361
+ <div className="code-content">
362
+ <pre>
363
+ <code dangerouslySetInnerHTML={{ __html: highlightedCode }} />
364
+ </pre>
365
+ </div>
366
+ </figure>
367
+ );
368
+ }
369
+ ```
370
+
371
+ ## Example: Code Diff View
372
+
373
+ ```tsx
374
+ function CodeDiff({ original, modified, filename }) {
375
+ const diff = computeDiff(original, modified);
376
+
377
+ return (
378
+ <figure className="code-block diff-view">
379
+ <figcaption className="code-header">
380
+ <span className="filename">{filename}</span>
381
+ <span className="diff-stats">
382
+ <span className="added">+{diff.additions}</span>
383
+ <span className="removed">-{diff.deletions}</span>
384
+ </span>
385
+ </figcaption>
386
+
387
+ <div className="code-diff">
388
+ {diff.lines.map((line, i) => (
389
+ <div
390
+ key={i}
391
+ className={`diff-line ${line.type}`}
392
+ aria-label={line.type === 'added' ? 'Added line' : line.type === 'removed' ? 'Removed line' : 'Unchanged line'}
393
+ >
394
+ <span className="diff-content">{line.content}</span>
395
+ </div>
396
+ ))}
397
+ </div>
398
+
399
+ <footer className="code-footer">
400
+ <span className="diff-summary">
401
+ {diff.additions} additions, {diff.deletions} deletions
402
+ </span>
403
+ <button className="apply-btn">
404
+ <CheckIcon />
405
+ Apply Changes
406
+ </button>
407
+ </footer>
408
+ </figure>
409
+ );
410
+ }
411
+ ```
412
+
413
+ ## Example: Terminal Output
414
+
415
+ ```tsx
416
+ function TerminalBlock({ command, output, workingDir = '~' }) {
417
+ return (
418
+ <div className="terminal-block" role="region" aria-label="Terminal output">
419
+ <div className="terminal-header">
420
+ <div className="terminal-buttons" aria-hidden="true">
421
+ <span className="terminal-btn red"></span>
422
+ <span className="terminal-btn yellow"></span>
423
+ <span className="terminal-btn green"></span>
424
+ </div>
425
+ <span className="terminal-title">Terminal</span>
426
+ </div>
427
+
428
+ <div className="terminal-content">
429
+ <div className="terminal-line">
430
+ <span className="terminal-prompt">➜ {workingDir}</span>
431
+ <span className="terminal-command"> {command}</span>
432
+ </div>
433
+
434
+ <div className="terminal-output">
435
+ {output.split('\n').map((line, i) => (
436
+ <div key={i}>{line}</div>
437
+ ))}
438
+ </div>
439
+ </div>
440
+ </div>
441
+ );
442
+ }
443
+ ```
444
+
445
+ ## Example: Runnable Code Block
446
+
447
+ ```tsx
448
+ function RunnableCodeBlock({ code, language, onRun }) {
449
+ const [isRunning, setIsRunning] = useState(false);
450
+ const [result, setResult] = useState(null);
451
+
452
+ const handleRun = async () => {
453
+ setIsRunning(true);
454
+ try {
455
+ const output = await onRun(code);
456
+ setResult({ success: true, output });
457
+ } catch (error) {
458
+ setResult({ success: false, error: error.message });
459
+ } finally {
460
+ setIsRunning(false);
461
+ }
462
+ };
463
+
464
+ return (
465
+ <figure className="code-block runnable">
466
+ <figcaption className="code-header">
467
+ <span className="language-badge">{language}</span>
468
+
469
+ <button
470
+ className="code-action-btn run-btn"
471
+ onClick={handleRun}
472
+ disabled={isRunning}
473
+ >
474
+ {isRunning ? <Spinner /> : <PlayIcon />}
475
+ <span>{isRunning ? 'Running...' : 'Run'}</span>
476
+ </button>
477
+ </figcaption>
478
+
479
+ <div className="code-content">
480
+ <pre><code>{code}</code></pre>
481
+ </div>
482
+
483
+ {result && (
484
+ <footer className="code-footer execution-result">
485
+ {result.success ? (
486
+ <div className="success-output">{result.output}</div>
487
+ ) : (
488
+ <div className="error-output">{result.error}</div>
489
+ )}
490
+ </footer>
491
+ )}
492
+ </figure>
493
+ );
494
+ }
495
+ ```
496
+
497
+ ## Tokens Used
498
+
499
+ - **color**: surface-sunken, surface-raised, interactive-primary, interactive-primary-hover, text-default, text-inverted, text-muted, border-default, border-strong, status-success, status-error
500
+ - **spacing**: 2xs, xs, sm, md
501
+ - **radii**: sm, md, lg
502
+ - **typography**: font-mono, size-xs, size-sm, weight-medium
503
+ - **motion**: duration-fast