@layoutdesign/context 0.1.7

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 (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +424 -0
  3. package/dist/bin/cli.d.ts +3 -0
  4. package/dist/bin/cli.d.ts.map +1 -0
  5. package/dist/bin/cli.js +57 -0
  6. package/dist/bin/cli.js.map +1 -0
  7. package/dist/src/cli/import-zip.d.ts +2 -0
  8. package/dist/src/cli/import-zip.d.ts.map +1 -0
  9. package/dist/src/cli/import-zip.js +156 -0
  10. package/dist/src/cli/import-zip.js.map +1 -0
  11. package/dist/src/cli/init.d.ts +4 -0
  12. package/dist/src/cli/init.d.ts.map +1 -0
  13. package/dist/src/cli/init.js +104 -0
  14. package/dist/src/cli/init.js.map +1 -0
  15. package/dist/src/cli/install.d.ts +5 -0
  16. package/dist/src/cli/install.d.ts.map +1 -0
  17. package/dist/src/cli/install.js +192 -0
  18. package/dist/src/cli/install.js.map +1 -0
  19. package/dist/src/cli/list.d.ts +2 -0
  20. package/dist/src/cli/list.d.ts.map +1 -0
  21. package/dist/src/cli/list.js +36 -0
  22. package/dist/src/cli/list.js.map +1 -0
  23. package/dist/src/cli/serve.d.ts +2 -0
  24. package/dist/src/cli/serve.d.ts.map +1 -0
  25. package/dist/src/cli/serve.js +9 -0
  26. package/dist/src/cli/serve.js.map +1 -0
  27. package/dist/src/cli/use.d.ts +2 -0
  28. package/dist/src/cli/use.d.ts.map +1 -0
  29. package/dist/src/cli/use.js +54 -0
  30. package/dist/src/cli/use.js.map +1 -0
  31. package/dist/src/compliance/checker.d.ts +23 -0
  32. package/dist/src/compliance/checker.d.ts.map +1 -0
  33. package/dist/src/compliance/checker.js +31 -0
  34. package/dist/src/compliance/checker.js.map +1 -0
  35. package/dist/src/compliance/rules.d.ts +11 -0
  36. package/dist/src/compliance/rules.d.ts.map +1 -0
  37. package/dist/src/compliance/rules.js +147 -0
  38. package/dist/src/compliance/rules.js.map +1 -0
  39. package/dist/src/index.d.ts +9 -0
  40. package/dist/src/index.d.ts.map +1 -0
  41. package/dist/src/index.js +6 -0
  42. package/dist/src/index.js.map +1 -0
  43. package/dist/src/kit/loader.d.ts +16 -0
  44. package/dist/src/kit/loader.d.ts.map +1 -0
  45. package/dist/src/kit/loader.js +98 -0
  46. package/dist/src/kit/loader.js.map +1 -0
  47. package/dist/src/kit/parser.d.ts +21 -0
  48. package/dist/src/kit/parser.d.ts.map +1 -0
  49. package/dist/src/kit/parser.js +98 -0
  50. package/dist/src/kit/parser.js.map +1 -0
  51. package/dist/src/kit/registry.d.ts +4 -0
  52. package/dist/src/kit/registry.d.ts.map +1 -0
  53. package/dist/src/kit/registry.js +91 -0
  54. package/dist/src/kit/registry.js.map +1 -0
  55. package/dist/src/kit/types.d.ts +51 -0
  56. package/dist/src/kit/types.d.ts.map +1 -0
  57. package/dist/src/kit/types.js +11 -0
  58. package/dist/src/kit/types.js.map +1 -0
  59. package/dist/src/mcp/server.d.ts +6 -0
  60. package/dist/src/mcp/server.d.ts.map +1 -0
  61. package/dist/src/mcp/server.js +56 -0
  62. package/dist/src/mcp/server.js.map +1 -0
  63. package/dist/src/mcp/tools/check-compliance.d.ts +16 -0
  64. package/dist/src/mcp/tools/check-compliance.d.ts.map +1 -0
  65. package/dist/src/mcp/tools/check-compliance.js +44 -0
  66. package/dist/src/mcp/tools/check-compliance.js.map +1 -0
  67. package/dist/src/mcp/tools/design-in-figma.d.ts +24 -0
  68. package/dist/src/mcp/tools/design-in-figma.d.ts.map +1 -0
  69. package/dist/src/mcp/tools/design-in-figma.js +202 -0
  70. package/dist/src/mcp/tools/design-in-figma.js.map +1 -0
  71. package/dist/src/mcp/tools/get-component.d.ts +16 -0
  72. package/dist/src/mcp/tools/get-component.d.ts.map +1 -0
  73. package/dist/src/mcp/tools/get-component.js +52 -0
  74. package/dist/src/mcp/tools/get-component.js.map +1 -0
  75. package/dist/src/mcp/tools/get-design-system.d.ts +16 -0
  76. package/dist/src/mcp/tools/get-design-system.d.ts.map +1 -0
  77. package/dist/src/mcp/tools/get-design-system.js +51 -0
  78. package/dist/src/mcp/tools/get-design-system.js.map +1 -0
  79. package/dist/src/mcp/tools/get-screenshots.d.ts +23 -0
  80. package/dist/src/mcp/tools/get-screenshots.d.ts.map +1 -0
  81. package/dist/src/mcp/tools/get-screenshots.js +78 -0
  82. package/dist/src/mcp/tools/get-screenshots.js.map +1 -0
  83. package/dist/src/mcp/tools/get-tokens.d.ts +20 -0
  84. package/dist/src/mcp/tools/get-tokens.d.ts.map +1 -0
  85. package/dist/src/mcp/tools/get-tokens.js +50 -0
  86. package/dist/src/mcp/tools/get-tokens.js.map +1 -0
  87. package/dist/src/mcp/tools/list-components.d.ts +11 -0
  88. package/dist/src/mcp/tools/list-components.d.ts.map +1 -0
  89. package/dist/src/mcp/tools/list-components.js +38 -0
  90. package/dist/src/mcp/tools/list-components.js.map +1 -0
  91. package/dist/src/mcp/tools/preview.d.ts +21 -0
  92. package/dist/src/mcp/tools/preview.d.ts.map +1 -0
  93. package/dist/src/mcp/tools/preview.js +63 -0
  94. package/dist/src/mcp/tools/preview.js.map +1 -0
  95. package/dist/src/mcp/tools/push-to-figma.d.ts +24 -0
  96. package/dist/src/mcp/tools/push-to-figma.d.ts.map +1 -0
  97. package/dist/src/mcp/tools/push-to-figma.js +101 -0
  98. package/dist/src/mcp/tools/push-to-figma.js.map +1 -0
  99. package/dist/src/mcp/tools/update-tokens.d.ts +21 -0
  100. package/dist/src/mcp/tools/update-tokens.d.ts.map +1 -0
  101. package/dist/src/mcp/tools/update-tokens.js +187 -0
  102. package/dist/src/mcp/tools/update-tokens.js.map +1 -0
  103. package/dist/src/mcp/tools/url-to-figma.d.ts +29 -0
  104. package/dist/src/mcp/tools/url-to-figma.d.ts.map +1 -0
  105. package/dist/src/mcp/tools/url-to-figma.js +103 -0
  106. package/dist/src/mcp/tools/url-to-figma.js.map +1 -0
  107. package/dist/src/preview/server.d.ts +15 -0
  108. package/dist/src/preview/server.d.ts.map +1 -0
  109. package/dist/src/preview/server.js +146 -0
  110. package/dist/src/preview/server.js.map +1 -0
  111. package/dist/src/preview/static/index.html +493 -0
  112. package/dist/src/preview/transpile.d.ts +10 -0
  113. package/dist/src/preview/transpile.d.ts.map +1 -0
  114. package/dist/src/preview/transpile.js +40 -0
  115. package/dist/src/preview/transpile.js.map +1 -0
  116. package/dist/src/preview/ws.d.ts +17 -0
  117. package/dist/src/preview/ws.d.ts.map +1 -0
  118. package/dist/src/preview/ws.js +66 -0
  119. package/dist/src/preview/ws.js.map +1 -0
  120. package/kits/linear-lite/DESIGN.md +421 -0
  121. package/kits/linear-lite/kit.json +12 -0
  122. package/kits/linear-lite/tokens.css +46 -0
  123. package/kits/linear-lite/tokens.json +47 -0
  124. package/kits/notion-lite/DESIGN.md +528 -0
  125. package/kits/notion-lite/kit.json +12 -0
  126. package/kits/notion-lite/tokens.css +50 -0
  127. package/kits/notion-lite/tokens.json +51 -0
  128. package/kits/stripe-lite/DESIGN.md +539 -0
  129. package/kits/stripe-lite/kit.json +12 -0
  130. package/kits/stripe-lite/tokens.css +57 -0
  131. package/kits/stripe-lite/tokens.json +58 -0
  132. package/package.json +63 -0
@@ -0,0 +1,421 @@
1
+ # Linear — Design System
2
+
3
+ > Dark, minimal, developer-focused. Every element earns its place.
4
+
5
+ ---
6
+
7
+ ## Quick Reference
8
+
9
+ **Aesthetic:** Dense information display, near-black backgrounds, indigo accent, tight spacing. Feels fast and precise — no decorative flourishes.
10
+
11
+ ### Colour Palette
12
+ | Role | Token | Value |
13
+ |---|---|---|
14
+ | App background | `--linear-bg-app` | `#0A0A0F` |
15
+ | Surface (panels, sidebars) | `--linear-bg-surface` | `#12121A` |
16
+ | Elevated (modals, dropdowns) | `--linear-bg-elevated` | `#1A1A25` |
17
+ | Hover state | `--linear-bg-hover` | `#22222F` |
18
+ | Border (default) | `--linear-border` | `rgba(255,255,255,0.08)` |
19
+ | Border (strong) | `--linear-border-strong` | `rgba(255,255,255,0.15)` |
20
+ | Border (focus) | `--linear-border-focus` | `#5E6AD2` |
21
+ | Primary text | `--linear-text-primary` | `#E8E8ED` |
22
+ | Secondary text | `--linear-text-secondary` | `rgba(232,232,237,0.65)` |
23
+ | Muted text | `--linear-text-muted` | `rgba(232,232,237,0.4)` |
24
+ | Accent | `--linear-accent` | `#5E6AD2` |
25
+ | Accent hover | `--linear-accent-hover` | `#6E7AE2` |
26
+ | Accent subtle bg | `--linear-accent-subtle` | `rgba(94,106,210,0.15)` |
27
+ | Status: active | `--linear-status-active` | `#F2C94C` |
28
+ | Status: done | `--linear-status-done` | `#4CB782` |
29
+ | Status: backlog | `--linear-status-backlog` | `#8B8B99` |
30
+
31
+ ### Typography
32
+ - **UI font:** `Inter`, -apple-system, sans-serif — via `--linear-font-sans`
33
+ - **Code/mono font:** `JetBrains Mono`, monospace — via `--linear-font-mono`
34
+ - Body: 14px / 1.5 line-height
35
+ - Labels: 12px, `--linear-text-secondary`, letter-spacing 0.01em
36
+ - Headings: 15–18px, `--linear-text-primary`, font-weight 500–600
37
+
38
+ ### Spacing Scale
39
+ `4 / 8 / 12 / 16 / 24px` — tokens `xs / sm / md / lg / xl`
40
+
41
+ ### Border Radius
42
+ `4px (sm) / 6px (md) / 8px (lg)`
43
+
44
+ ### Key Design Rules
45
+ 1. **No white backgrounds.** Even modals use `--linear-bg-elevated`.
46
+ 2. **Borders are barely visible** — `rgba(255,255,255,0.08)` by default. Strengthen on hover/focus.
47
+ 3. **Accent sparingly.** Indigo only for primary actions and active states.
48
+ 4. **Status colours** carry meaning — yellow = in progress, green = done, grey = backlog.
49
+ 5. **No border-radius above 8px.** This is a tool, not a consumer app.
50
+ 6. **Transitions:** 120ms ease-out on interactive elements only.
51
+ 7. **Icons:** 16px, `--linear-text-secondary`, nudge to `--linear-text-primary` on hover.
52
+
53
+ ---
54
+
55
+ ## Design Tokens
56
+
57
+ ### Backgrounds
58
+ ```
59
+ --linear-bg-app: #0A0A0F /* root, page background */
60
+ --linear-bg-surface: #12121A /* sidebar, panels */
61
+ --linear-bg-elevated: #1A1A25 /* modals, dropdowns */
62
+ --linear-bg-hover: #22222F /* row/item hover */
63
+ ```
64
+
65
+ ### Borders
66
+ ```
67
+ --linear-border: rgba(255,255,255,0.08) /* default dividers */
68
+ --linear-border-strong: rgba(255,255,255,0.15) /* card edges, inputs */
69
+ --linear-border-focus: #5E6AD2 /* focused inputs */
70
+ ```
71
+
72
+ ### Text
73
+ ```
74
+ --linear-text-primary: #E8E8ED
75
+ --linear-text-secondary: rgba(232,232,237,0.65)
76
+ --linear-text-muted: rgba(232,232,237,0.4)
77
+ ```
78
+
79
+ ### Accent
80
+ ```
81
+ --linear-accent: #5E6AD2
82
+ --linear-accent-hover: #6E7AE2
83
+ --linear-accent-subtle: rgba(94,106,210,0.15)
84
+ ```
85
+
86
+ ### Status
87
+ ```
88
+ --linear-status-active: #F2C94C
89
+ --linear-status-done: #4CB782
90
+ --linear-status-backlog: #8B8B99
91
+ ```
92
+
93
+ ### Typography
94
+ ```
95
+ --linear-font-sans: 'Inter', -apple-system, sans-serif
96
+ --linear-font-mono: 'JetBrains Mono', monospace
97
+ ```
98
+
99
+ ### Spacing
100
+ ```
101
+ --linear-space-xs: 4px
102
+ --linear-space-sm: 8px
103
+ --linear-space-md: 12px
104
+ --linear-space-lg: 16px
105
+ --linear-space-xl: 24px
106
+ ```
107
+
108
+ ### Border Radius
109
+ ```
110
+ --linear-radius-sm: 4px
111
+ --linear-radius-md: 6px
112
+ --linear-radius-lg: 8px
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Components
118
+
119
+ ### Button
120
+
121
+ Three variants: primary (solid accent fill), secondary (surface bg + border), ghost (no bg until hover). Height 32px. Font size 14px, weight 500.
122
+
123
+ **Tokens used:** `--linear-accent`, `--linear-accent-hover`, `--linear-bg-surface`, `--linear-bg-hover`, `--linear-border`, `--linear-text-primary`, `--linear-radius-md`
124
+
125
+ ```tsx
126
+ type ButtonVariant = 'primary' | 'secondary' | 'ghost';
127
+
128
+ interface ButtonProps {
129
+ variant?: ButtonVariant;
130
+ children: React.ReactNode;
131
+ onClick?: () => void;
132
+ disabled?: boolean;
133
+ }
134
+
135
+ export function Button({ variant = 'primary', children, onClick, disabled }: ButtonProps) {
136
+ const base: React.CSSProperties = {
137
+ display: 'inline-flex',
138
+ alignItems: 'center',
139
+ gap: '6px',
140
+ height: '32px',
141
+ padding: '0 12px',
142
+ borderRadius: 'var(--linear-radius-md)',
143
+ fontSize: '14px',
144
+ fontWeight: 500,
145
+ fontFamily: 'var(--linear-font-sans)',
146
+ cursor: disabled ? 'not-allowed' : 'pointer',
147
+ opacity: disabled ? 0.45 : 1,
148
+ border: '1px solid transparent',
149
+ transition: 'background 120ms ease-out, border-color 120ms ease-out',
150
+ };
151
+
152
+ const variants: Record<ButtonVariant, React.CSSProperties> = {
153
+ primary: {
154
+ background: 'var(--linear-accent)',
155
+ color: '#fff',
156
+ },
157
+ secondary: {
158
+ background: 'var(--linear-bg-surface)',
159
+ color: 'var(--linear-text-primary)',
160
+ borderColor: 'var(--linear-border-strong)',
161
+ },
162
+ ghost: {
163
+ background: 'transparent',
164
+ color: 'var(--linear-text-secondary)',
165
+ },
166
+ };
167
+
168
+ return (
169
+ <button
170
+ style={{ ...base, ...variants[variant] }}
171
+ onClick={onClick}
172
+ disabled={disabled}
173
+ onMouseEnter={e => {
174
+ if (variant === 'primary') (e.currentTarget as HTMLButtonElement).style.background = 'var(--linear-accent-hover)';
175
+ if (variant === 'ghost') (e.currentTarget as HTMLButtonElement).style.background = 'var(--linear-bg-hover)';
176
+ }}
177
+ onMouseLeave={e => {
178
+ if (variant === 'primary') (e.currentTarget as HTMLButtonElement).style.background = 'var(--linear-accent)';
179
+ if (variant === 'ghost') (e.currentTarget as HTMLButtonElement).style.background = 'transparent';
180
+ }}
181
+ >
182
+ {children}
183
+ </button>
184
+ );
185
+ }
186
+ ```
187
+
188
+ ---
189
+
190
+ ### Input
191
+
192
+ Single-line text input. Dark surface background, `--linear-border-strong` border at rest, `--linear-border-focus` on focus with a subtle glow. Height 32px, 14px font.
193
+
194
+ **Tokens used:** `--linear-bg-surface`, `--linear-border-strong`, `--linear-border-focus`, `--linear-text-primary`, `--linear-text-muted`, `--linear-radius-md`
195
+
196
+ ```tsx
197
+ interface InputProps {
198
+ placeholder?: string;
199
+ value?: string;
200
+ onChange?: (value: string) => void;
201
+ label?: string;
202
+ }
203
+
204
+ export function Input({ placeholder, value, onChange, label }: InputProps) {
205
+ return (
206
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
207
+ {label && (
208
+ <label style={{
209
+ fontSize: '12px',
210
+ color: 'var(--linear-text-secondary)',
211
+ fontFamily: 'var(--linear-font-sans)',
212
+ letterSpacing: '0.01em',
213
+ }}>
214
+ {label}
215
+ </label>
216
+ )}
217
+ <input
218
+ value={value}
219
+ onChange={e => onChange?.(e.target.value)}
220
+ placeholder={placeholder}
221
+ style={{
222
+ height: '32px',
223
+ padding: '0 var(--linear-space-md)',
224
+ background: 'var(--linear-bg-surface)',
225
+ border: '1px solid var(--linear-border-strong)',
226
+ borderRadius: 'var(--linear-radius-md)',
227
+ color: 'var(--linear-text-primary)',
228
+ fontSize: '14px',
229
+ fontFamily: 'var(--linear-font-sans)',
230
+ outline: 'none',
231
+ transition: 'border-color 120ms ease-out, box-shadow 120ms ease-out',
232
+ }}
233
+ onFocus={e => {
234
+ e.currentTarget.style.borderColor = 'var(--linear-border-focus)';
235
+ e.currentTarget.style.boxShadow = '0 0 0 2px rgba(94, 106, 210, 0.2)';
236
+ }}
237
+ onBlur={e => {
238
+ e.currentTarget.style.borderColor = 'var(--linear-border-strong)';
239
+ e.currentTarget.style.boxShadow = 'none';
240
+ }}
241
+ />
242
+ </div>
243
+ );
244
+ }
245
+ ```
246
+
247
+ ---
248
+
249
+ ### Card
250
+
251
+ Container component. `--linear-bg-surface` background, `--linear-border` edge, `--linear-radius-lg` corners. On hover the border strengthens. No shadow — elevation is implied by background difference.
252
+
253
+ **Tokens used:** `--linear-bg-surface`, `--linear-bg-hover`, `--linear-border`, `--linear-border-strong`, `--linear-radius-lg`, `--linear-space-lg`, `--linear-space-xl`
254
+
255
+ ```tsx
256
+ interface CardProps {
257
+ children: React.ReactNode;
258
+ onClick?: () => void;
259
+ padding?: 'sm' | 'md' | 'lg';
260
+ }
261
+
262
+ export function Card({ children, onClick, padding = 'md' }: CardProps) {
263
+ const paddingMap = {
264
+ sm: 'var(--linear-space-md)',
265
+ md: 'var(--linear-space-lg)',
266
+ lg: 'var(--linear-space-xl)',
267
+ };
268
+
269
+ return (
270
+ <div
271
+ onClick={onClick}
272
+ style={{
273
+ background: 'var(--linear-bg-surface)',
274
+ border: '1px solid var(--linear-border)',
275
+ borderRadius: 'var(--linear-radius-lg)',
276
+ padding: paddingMap[padding],
277
+ cursor: onClick ? 'pointer' : 'default',
278
+ transition: 'border-color 120ms ease-out, background 120ms ease-out',
279
+ }}
280
+ onMouseEnter={e => {
281
+ if (!onClick) return;
282
+ (e.currentTarget as HTMLDivElement).style.borderColor = 'var(--linear-border-strong)';
283
+ (e.currentTarget as HTMLDivElement).style.background = 'var(--linear-bg-elevated)';
284
+ }}
285
+ onMouseLeave={e => {
286
+ if (!onClick) return;
287
+ (e.currentTarget as HTMLDivElement).style.borderColor = 'var(--linear-border)';
288
+ (e.currentTarget as HTMLDivElement).style.background = 'var(--linear-bg-surface)';
289
+ }}
290
+ >
291
+ {children}
292
+ </div>
293
+ );
294
+ }
295
+ ```
296
+
297
+ ---
298
+
299
+ ### Badge
300
+
301
+ Inline status label. Three semantic variants map to Linear's status colour system. Small, 12px text, tight padding. No shadow.
302
+
303
+ **Tokens used:** `--linear-status-active`, `--linear-status-done`, `--linear-status-backlog`, `--linear-accent`, `--linear-accent-subtle`, `--linear-radius-sm`
304
+
305
+ ```tsx
306
+ type BadgeVariant = 'active' | 'done' | 'backlog' | 'accent';
307
+
308
+ interface BadgeProps {
309
+ variant?: BadgeVariant;
310
+ children: React.ReactNode;
311
+ }
312
+
313
+ const badgeStyles: Record<BadgeVariant, React.CSSProperties> = {
314
+ active: {
315
+ background: 'rgba(242, 201, 76, 0.15)',
316
+ color: 'var(--linear-status-active)',
317
+ },
318
+ done: {
319
+ background: 'rgba(76, 183, 130, 0.15)',
320
+ color: 'var(--linear-status-done)',
321
+ },
322
+ backlog: {
323
+ background: 'rgba(139, 139, 153, 0.15)',
324
+ color: 'var(--linear-status-backlog)',
325
+ },
326
+ accent: {
327
+ background: 'var(--linear-accent-subtle)',
328
+ color: 'var(--linear-accent)',
329
+ },
330
+ };
331
+
332
+ export function Badge({ variant = 'backlog', children }: BadgeProps) {
333
+ return (
334
+ <span
335
+ style={{
336
+ display: 'inline-flex',
337
+ alignItems: 'center',
338
+ gap: '4px',
339
+ padding: '2px 8px',
340
+ borderRadius: 'var(--linear-radius-sm)',
341
+ fontSize: '12px',
342
+ fontWeight: 500,
343
+ fontFamily: 'var(--linear-font-sans)',
344
+ lineHeight: '18px',
345
+ ...badgeStyles[variant],
346
+ }}
347
+ >
348
+ {children}
349
+ </span>
350
+ );
351
+ }
352
+ ```
353
+
354
+ ---
355
+
356
+ ### Avatar
357
+
358
+ User avatar with image fallback to initials. Two sizes: sm (24px) and md (32px). Circular. Background uses `--linear-accent-subtle` for initials fallback.
359
+
360
+ **Tokens used:** `--linear-accent-subtle`, `--linear-accent`, `--linear-text-primary`, `--linear-border`
361
+
362
+ ```tsx
363
+ interface AvatarProps {
364
+ src?: string;
365
+ name: string;
366
+ size?: 'sm' | 'md';
367
+ }
368
+
369
+ function getInitials(name: string): string {
370
+ return name
371
+ .split(' ')
372
+ .map(n => n[0])
373
+ .slice(0, 2)
374
+ .join('')
375
+ .toUpperCase();
376
+ }
377
+
378
+ export function Avatar({ src, name, size = 'md' }: AvatarProps) {
379
+ const dimension = size === 'sm' ? 24 : 32;
380
+ const fontSize = size === 'sm' ? 10 : 13;
381
+
382
+ const base: React.CSSProperties = {
383
+ width: dimension,
384
+ height: dimension,
385
+ borderRadius: '50%',
386
+ display: 'inline-flex',
387
+ alignItems: 'center',
388
+ justifyContent: 'center',
389
+ fontSize,
390
+ fontWeight: 600,
391
+ fontFamily: 'var(--linear-font-sans)',
392
+ flexShrink: 0,
393
+ overflow: 'hidden',
394
+ border: '1px solid var(--linear-border)',
395
+ };
396
+
397
+ if (src) {
398
+ return (
399
+ <div style={base}>
400
+ <img src={src} alt={name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
401
+ </div>
402
+ );
403
+ }
404
+
405
+ return (
406
+ <div
407
+ style={{
408
+ ...base,
409
+ background: 'var(--linear-accent-subtle)',
410
+ color: 'var(--linear-accent)',
411
+ }}
412
+ >
413
+ {getInitials(name)}
414
+ </div>
415
+ );
416
+ }
417
+ ```
418
+
419
+ ---
420
+
421
+ <!-- Generated by Layout — layout.design -->
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "linear-lite",
3
+ "version": "1.0.0",
4
+ "displayName": "Linear",
5
+ "description": "Developer tool, dark-first design system inspired by Linear",
6
+ "source": "website-extraction",
7
+ "tier": "free",
8
+ "tokenCount": 24,
9
+ "componentCount": 5,
10
+ "aesthetic": "Dark, minimal, developer-focused",
11
+ "layoutUrl": "https://layout.design/kits/linear"
12
+ }
@@ -0,0 +1,46 @@
1
+ /* Linear Lite — Design Tokens */
2
+ /* Generated by Layout — layout.design */
3
+
4
+ :root {
5
+ /* Backgrounds */
6
+ --linear-bg-app: #0A0A0F;
7
+ --linear-bg-surface: #12121A;
8
+ --linear-bg-elevated: #1A1A25;
9
+ --linear-bg-hover: #22222F;
10
+
11
+ /* Borders */
12
+ --linear-border: rgba(255, 255, 255, 0.08);
13
+ --linear-border-strong: rgba(255, 255, 255, 0.15);
14
+ --linear-border-focus: #5E6AD2;
15
+
16
+ /* Text */
17
+ --linear-text-primary: #E8E8ED;
18
+ --linear-text-secondary: rgba(232, 232, 237, 0.65);
19
+ --linear-text-muted: rgba(232, 232, 237, 0.4);
20
+
21
+ /* Accent */
22
+ --linear-accent: #5E6AD2;
23
+ --linear-accent-hover: #6E7AE2;
24
+ --linear-accent-subtle: rgba(94, 106, 210, 0.15);
25
+
26
+ /* Status */
27
+ --linear-status-active: #F2C94C;
28
+ --linear-status-done: #4CB782;
29
+ --linear-status-backlog: #8B8B99;
30
+
31
+ /* Typography */
32
+ --linear-font-sans: 'Inter', -apple-system, sans-serif;
33
+ --linear-font-mono: 'JetBrains Mono', monospace;
34
+
35
+ /* Spacing */
36
+ --linear-space-xs: 4px;
37
+ --linear-space-sm: 8px;
38
+ --linear-space-md: 12px;
39
+ --linear-space-lg: 16px;
40
+ --linear-space-xl: 24px;
41
+
42
+ /* Radius */
43
+ --linear-radius-sm: 4px;
44
+ --linear-radius-md: 6px;
45
+ --linear-radius-lg: 8px;
46
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "https://tr.designtokens.org/format/",
3
+ "color": {
4
+ "bg": {
5
+ "app": { "$type": "color", "$value": "#0A0A0F" },
6
+ "surface": { "$type": "color", "$value": "#12121A" },
7
+ "elevated": { "$type": "color", "$value": "#1A1A25" },
8
+ "hover": { "$type": "color", "$value": "#22222F" }
9
+ },
10
+ "border": {
11
+ "default": { "$type": "color", "$value": "rgba(255, 255, 255, 0.08)" },
12
+ "strong": { "$type": "color", "$value": "rgba(255, 255, 255, 0.15)" },
13
+ "focus": { "$type": "color", "$value": "#5E6AD2" }
14
+ },
15
+ "text": {
16
+ "primary": { "$type": "color", "$value": "#E8E8ED" },
17
+ "secondary": { "$type": "color", "$value": "rgba(232, 232, 237, 0.65)" },
18
+ "muted": { "$type": "color", "$value": "rgba(232, 232, 237, 0.4)" }
19
+ },
20
+ "accent": {
21
+ "default": { "$type": "color", "$value": "#5E6AD2" },
22
+ "hover": { "$type": "color", "$value": "#6E7AE2" },
23
+ "subtle": { "$type": "color", "$value": "rgba(94, 106, 210, 0.15)" }
24
+ },
25
+ "status": {
26
+ "active": { "$type": "color", "$value": "#F2C94C" },
27
+ "done": { "$type": "color", "$value": "#4CB782" },
28
+ "backlog": { "$type": "color", "$value": "#8B8B99" }
29
+ }
30
+ },
31
+ "font": {
32
+ "sans": { "$type": "fontFamily", "$value": ["Inter", "-apple-system", "sans-serif"] },
33
+ "mono": { "$type": "fontFamily", "$value": ["JetBrains Mono", "monospace"] }
34
+ },
35
+ "spacing": {
36
+ "xs": { "$type": "dimension", "$value": "4px" },
37
+ "sm": { "$type": "dimension", "$value": "8px" },
38
+ "md": { "$type": "dimension", "$value": "12px" },
39
+ "lg": { "$type": "dimension", "$value": "16px" },
40
+ "xl": { "$type": "dimension", "$value": "24px" }
41
+ },
42
+ "borderRadius": {
43
+ "sm": { "$type": "dimension", "$value": "4px" },
44
+ "md": { "$type": "dimension", "$value": "6px" },
45
+ "lg": { "$type": "dimension", "$value": "8px" }
46
+ }
47
+ }