@brewedby/ds 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.
@@ -0,0 +1,608 @@
1
+ # FYND_DESIGN_SYSTEM.md
2
+ > Drop this file into any Fynd project repo as `FYND_DESIGN_SYSTEM.md` or `CLAUDE.md`.
3
+ > It instructs AI tools (Claude, Cursor, Copilot) and developers on how to implement the Fynd One Design System correctly.
4
+ > **Last synced:** March 2026 — One Design System (Figma) + devlink/global.css
5
+
6
+ ---
7
+
8
+ ## Quick Setup
9
+
10
+ ### 1 — Fonts
11
+
12
+ Fynd uses **three font families** with distinct roles:
13
+
14
+ | Family | Role | Source |
15
+ |--------|------|--------|
16
+ | **Fynd Sans** | Titles, headings (h1–h2) | Proprietary — self-host only |
17
+ | **Inter Display** | Body text, subtext, descriptions | Google Fonts / @fontsource |
18
+ | **Inter** | Buttons, UI labels, nav, table headers | Google Fonts / @fontsource |
19
+
20
+ #### Fynd Sans (proprietary — required for headings)
21
+
22
+ Fynd Sans is a custom typeface and **cannot be loaded from any public CDN**.
23
+ Obtain the `.woff2` files from the design assets repo or by contacting design@fynd.com.
24
+
25
+ Place files at:
26
+ ```
27
+ your-project/
28
+ └── assets/
29
+ └── fonts/
30
+ ├── FyndSans-Regular.woff2
31
+ ├── FyndSans-Medium.woff2
32
+ ├── FyndSans-SemiBold.woff2
33
+ └── FyndSans-Bold.woff2
34
+ ```
35
+
36
+ Then declare via `@font-face` (already included in `fynd-tokens.css`):
37
+ ```css
38
+ @font-face {
39
+ font-family: 'Fynd Sans';
40
+ src: url('/assets/fonts/FyndSans-Regular.woff2') format('woff2');
41
+ font-weight: 400;
42
+ font-style: normal;
43
+ font-display: swap;
44
+ }
45
+ @font-face {
46
+ font-family: 'Fynd Sans';
47
+ src: url('/assets/fonts/FyndSans-Medium.woff2') format('woff2');
48
+ font-weight: 500;
49
+ font-style: normal;
50
+ font-display: swap;
51
+ }
52
+ @font-face {
53
+ font-family: 'Fynd Sans';
54
+ src: url('/assets/fonts/FyndSans-SemiBold.woff2') format('woff2');
55
+ font-weight: 600;
56
+ font-style: normal;
57
+ font-display: swap;
58
+ }
59
+ @font-face {
60
+ font-family: 'Fynd Sans';
61
+ src: url('/assets/fonts/FyndSans-Bold.woff2') format('woff2');
62
+ font-weight: 700;
63
+ font-style: normal;
64
+ font-display: swap;
65
+ }
66
+ ```
67
+
68
+ > ⚠️ Adjust the `url()` path to match your project's asset serving path (e.g. `/public/fonts/` in Next.js, `/src/assets/fonts/` in Vite).
69
+
70
+ #### Inter Display + Inter (open source)
71
+
72
+ ```html
73
+ <!-- Add to <head> -->
74
+ <link rel="preconnect" href="https://fonts.googleapis.com">
75
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
76
+ <link href="https://fonts.googleapis.com/css2?family=Inter+Display:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
77
+ ```
78
+
79
+ Or self-hosted via npm (recommended for production):
80
+ ```bash
81
+ npm install @fontsource/inter-display @fontsource/inter
82
+ ```
83
+ ```css
84
+ @import '@fontsource/inter-display/400.css';
85
+ @import '@fontsource/inter-display/500.css';
86
+ @import '@fontsource/inter-display/600.css';
87
+ @import '@fontsource/inter-display/700.css';
88
+ @import '@fontsource/inter/400.css';
89
+ @import '@fontsource/inter/500.css';
90
+ @import '@fontsource/inter/600.css';
91
+ ```
92
+
93
+ **Loaded weights summary:**
94
+ | Family | Weights | Role |
95
+ |--------|---------|------|
96
+ | Fynd Sans | 400, 500, 600, 700 | Headings — h1, h2, large display text |
97
+ | Inter Display | 400, 500, 600, 700 | Body text, subtext, descriptions |
98
+ | Inter | 400, 500, 600 | Buttons, nav links, badges, table headers |
99
+
100
+ ---
101
+
102
+ ### 2 — Icons
103
+ **Library: [Lucide Icons](https://lucide.dev)** (MIT license)
104
+
105
+ Chosen for: consistent 1.5px stroke weight, clean geometric style matching Fynd's aesthetic.
106
+
107
+ ```bash
108
+ # React / Next.js / Astro
109
+ npm install lucide-react
110
+
111
+ # Vanilla / HTML
112
+ <script src="https://unpkg.com/lucide@latest"></script>
113
+ ```
114
+
115
+ **Usage:**
116
+ ```jsx
117
+ // React / Astro (with React integration)
118
+ import { ShoppingBag, ArrowRight, ChevronDown } from 'lucide-react'
119
+
120
+ <ShoppingBag size={20} strokeWidth={1.5} />
121
+ ```
122
+
123
+ ```html
124
+ <!-- Vanilla HTML -->
125
+ <i data-lucide="shopping-bag"></i>
126
+ <script>lucide.createIcons();</script>
127
+ ```
128
+
129
+ **Icon sizing conventions:**
130
+ | Context | Size | Token |
131
+ |---------|------|-------|
132
+ | UI / inline | 16px | `--icon-size-ui` |
133
+ | Feature / cards | 20px | `--icon-size-feature` |
134
+ | Decorative / hero | 24px | `--icon-size-decorative` |
135
+
136
+ **Always use `strokeWidth={1.5}`** — this is the Fynd icon style.
137
+ **Never fill icons** — Lucide icons are outline only.
138
+
139
+ ---
140
+
141
+ ### 3 — CSS Tokens
142
+ Copy `fynd-tokens.css` to your project and import before all other styles:
143
+ ```css
144
+ /* styles/globals.css */
145
+ @import './fynd-tokens.css';
146
+ ```
147
+
148
+ Or drop into a CDN / shared package and reference via URL.
149
+
150
+ ---
151
+
152
+ ## Typography Rules
153
+
154
+ ### Font Families
155
+ ```css
156
+ font-family: var(--font-family--primary); /* Fynd Sans — headings, display titles */
157
+ font-family: var(--font-family--secondary); /* Inter Display — body, subtext */
158
+ font-family: var(--font-family--ui); /* Inter — buttons, UI labels */
159
+ ```
160
+
161
+ > **Rule:** `--font-family--primary` (Fynd Sans) is for h1, h2, and large marketing/display text only. Body copy, subtext, and descriptions always use `--font-family--secondary` (Inter Display). Interactive elements (buttons, nav, badges) always use `--font-family--ui` (Inter).
162
+
163
+ ### Headings
164
+ ```css
165
+ h1 {
166
+ font-family: var(--font-family--primary);
167
+ font-size: var(--font-size--h1); /* 72px */
168
+ font-weight: var(--font-weight--regular); /* 400 */
169
+ line-height: var(--line-height--110);
170
+ letter-spacing: var(--letter-spacing--heading-1); /* -0.04em */
171
+ }
172
+ h2 {
173
+ font-family: var(--font-family--primary);
174
+ font-size: var(--font-size--h2); /* 56px */
175
+ font-weight: var(--font-weight--regular);
176
+ line-height: var(--line-height--110);
177
+ letter-spacing: var(--letter-spacing--heading-2); /* -0.04em */
178
+ }
179
+ h3 { font-size: var(--font-size--h5); font-weight: 700; letter-spacing: var(--letter-spacing--heading-3); }
180
+ h4 { font-size: var(--font-size--body-l); font-weight: 700; letter-spacing: var(--letter-spacing--heading-4); }
181
+ h5 { font-size: var(--font-size--body-s); font-weight: 700; letter-spacing: var(--letter-spacing--heading-5); }
182
+ h6 { font-size: var(--font-size--body-xs); font-weight: 700; }
183
+ ```
184
+
185
+ ### Body Text
186
+ ```css
187
+ /* Default body */
188
+ font-family: var(--font-family--secondary);
189
+ font-size: var(--font-size--body-m); /* 16px */
190
+ font-weight: var(--font-weight--regular); /* 400 */
191
+ line-height: var(--line-height--150); /* 1.5 */
192
+ color: var(--text--title); /* #0e0e0e */
193
+
194
+ /* Subtext / captions */
195
+ color: var(--text--subtext); /* #5b5c5d */
196
+ font-size: var(--font-size--body-s); /* 14px */
197
+ line-height: var(--line-height--140);
198
+ ```
199
+
200
+ ### Verified Figma Type Styles
201
+ | Style | Size | Weight | Line Height | Family |
202
+ |-------|------|--------|-------------|--------|
203
+ | Heading / h1 | 72px | 400 | 110% | **Fynd Sans** |
204
+ | Heading / h2 | 56px | 400 | 110% | **Fynd Sans** |
205
+ | Body XS / Regular | 12px | 400 | 130% | Inter Display |
206
+ | Body S / Regular | 14px | 400 | 140% | Inter Display |
207
+ | Body M / Regular | 16px | 400 | 150% | Inter Display |
208
+ | Body XL / Medium | 20px | 500 | 140% | Inter Display |
209
+ | Button / m-prominent | 14px | 500 | 20px | Inter |
210
+
211
+ ---
212
+
213
+ ## Color Rules
214
+
215
+ ### ⚠️ Critical: Two near-blacks
216
+ | Use case | Value | Token |
217
+ |----------|-------|-------|
218
+ | Text, interactive dark fills, buttons | `#0e0e0e` | `--text--title` |
219
+ | Dark section backgrounds (CTA, table headers) | `#101319` | `--neutral--neutral-100--devlink` |
220
+
221
+ **Never swap these.** Text and interactive elements use `#0e0e0e`. Background sections use `#101319`.
222
+
223
+ ### Text Colors
224
+ ```css
225
+ color: var(--text--title); /* #0e0e0e — headings, body */
226
+ color: var(--text--subtext); /* #5b5c5d — captions, meta */
227
+ color: var(--text--title-inverse); /* #ffffff — text on dark bg */
228
+ color: var(--text--subtext-inverse); /* #a0a1a2 — muted text on dark bg */
229
+ ```
230
+
231
+ ### Backgrounds
232
+ ```css
233
+ background: var(--background--background-light); /* white */
234
+ background: var(--background--background-medium); /* #f8f8f9 */
235
+ background: var(--background--background-dark); /* #5b5c5d */
236
+ background: var(--background--background-darkest); /* #0e0e0e */
237
+ ```
238
+
239
+ ### Brand Color Usage Pattern
240
+ Each brand color (`blue`, `peach`, `green`, `gold`, `lavender`, `red`) exposes four aliases:
241
+ ```css
242
+ /* Example — blue */
243
+ background: var(--blue--blue-fill); /* light bg → blue-10 #f9fbff */
244
+ border: var(--blue--blue-stroke); /* border → blue-20 #d8e2f5 */
245
+ color: var(--blue--blue-primary); /* accent → blue-40 #5c98f7 */
246
+ color: var(--blue--blue-text); /* dark text → blue-90 #07285a */
247
+ ```
248
+
249
+ **Rule:** When placing text on a brand-coloured background, always use the `-text` (90-shade) alias. Never use `neutral-100` or `#0e0e0e` on a tinted background.
250
+
251
+ ### Status Colors (feature tables, data)
252
+ ```css
253
+ color: var(--status--yes); /* #0d7a3a — green, supported */
254
+ color: var(--status--partial); /* #9a6700 — amber, partial */
255
+ color: var(--status--no); /* #c13515 — red, not supported */
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Spacing Rules
261
+
262
+ Always use tokens, never magic numbers:
263
+ ```css
264
+ gap: var(--spacing--16); /* 16px */
265
+ padding: var(--spacing--24) var(--spacing--32);
266
+ margin: 0 0 var(--spacing--56);
267
+ ```
268
+
269
+ > **Figma shorthand:** Components from the Figma file use `var(--16, 16px)` syntax. This resolves via the shorthand aliases defined in `fynd-tokens.css` (e.g. `--16`, `--24`).
270
+
271
+ ---
272
+
273
+ ## Border Radius Rules
274
+
275
+ ```css
276
+ border-radius: var(--border-radius--pill); /* 250px — BUTTONS, chips, fully-rounded elements */
277
+ border-radius: var(--border-radius--tag); /* 2000px — badges, tags (from Figma) */
278
+ border-radius: var(--border-radius--16); /* 16px — cards */
279
+ border-radius: var(--border-radius--24); /* 24px — CTA boxes, large containers */
280
+ border-radius: var(--border-radius--8); /* 8px — inputs, small elements */
281
+ ```
282
+
283
+ > **Rule: All buttons use `--border-radius--pill` (250px).** This is the canonical Fynd button shape — fully rounded, not square-cornered.
284
+
285
+ ---
286
+
287
+ ## Component Patterns
288
+
289
+ ### Button
290
+
291
+ ```css
292
+ .btn {
293
+ display: inline-flex;
294
+ align-items: center;
295
+ gap: var(--spacing--8);
296
+ padding: var(--btn--padding-y) var(--btn--padding-x);
297
+ border-radius: var(--btn--border-radius); /* 250px — pill */
298
+ font-family: var(--btn--font-family); /* Inter */
299
+ font-size: var(--btn--font-size); /* 14px */
300
+ font-weight: var(--btn--font-weight); /* 500 */
301
+ line-height: var(--btn--line-height); /* 20px */
302
+ letter-spacing: var(--btn--letter-spacing); /* 0 */
303
+ transition: var(--btn--transition);
304
+ cursor: pointer;
305
+ border: none;
306
+ white-space: nowrap;
307
+ }
308
+
309
+ /* Variants */
310
+ .btn-primary { background: var(--btn--primary--bg); color: var(--btn--primary--color); }
311
+ .btn-primary:hover { background: var(--btn--primary--bg-hover); }
312
+
313
+ .btn-primary-light { background: var(--btn--primary-light--bg); color: var(--btn--primary-light--color); }
314
+ .btn-primary-light:hover { background: var(--btn--primary-light--bg-hover); }
315
+
316
+ .btn-secondary { background: var(--btn--secondary--bg); color: var(--btn--secondary--color); border: 1px solid var(--btn--secondary--border); }
317
+ .btn-secondary:hover { background: var(--btn--secondary--bg-hover); border-color: var(--btn--secondary--border-hover); }
318
+
319
+ .btn-secondary-light { background: var(--btn--secondary-light--bg); color: var(--btn--secondary-light--color); border: 1px solid var(--btn--secondary-light--border); }
320
+ .btn-secondary-light:hover { background: var(--btn--secondary-light--bg-hover); border-color: var(--btn--secondary-light--border-hover); }
321
+
322
+ /* Size modifier */
323
+ .btn-lg { padding: var(--btn--padding-y-lg) var(--btn--padding-x-lg); font-size: var(--font-size--ui-l); }
324
+ ```
325
+
326
+ ```jsx
327
+ // React
328
+ <button className="btn btn-primary">Get started</button>
329
+ <button className="btn btn-secondary btn-lg">Learn more</button>
330
+
331
+ // With Lucide icon
332
+ import { ArrowRight } from 'lucide-react'
333
+ <button className="btn btn-primary">
334
+ Get started <ArrowRight size={16} strokeWidth={1.5} />
335
+ </button>
336
+ ```
337
+
338
+ ### Badge / Tag
339
+
340
+ ```css
341
+ .badge {
342
+ display: inline-flex;
343
+ align-items: center;
344
+ padding: var(--badge--padding-y) var(--badge--padding-x); /* 8px 12px */
345
+ border-radius: var(--badge--border-radius); /* 2000px */
346
+ font-family: var(--font-family--secondary);
347
+ font-size: var(--badge--font-size); /* 12px */
348
+ font-weight: var(--badge--font-weight); /* 400 */
349
+ line-height: var(--line-height--130);
350
+ }
351
+
352
+ .badge-blue { background: var(--blue--blue-fill); color: var(--blue--blue-text); }
353
+ .badge-green { background: var(--green--green-fill); color: var(--green--green-text); }
354
+ .badge-gold { background: var(--gold--gold-fill); color: var(--gold--gold-text); }
355
+ .badge-lavender { background: var(--lavender--lavender-fill); color: var(--lavender--lavender-text); }
356
+ .badge-peach { background: var(--peach--peach-fill); color: var(--peach--peach-text); }
357
+ .badge-red { background: var(--red--red-fill); color: var(--red--red-text); }
358
+ ```
359
+
360
+ ### Card
361
+
362
+ ```css
363
+ .card {
364
+ background: var(--background--background-light);
365
+ border: 1px solid var(--neutral--neutral-20);
366
+ border-radius: var(--border-radius--16);
367
+ padding: var(--spacing--32);
368
+ }
369
+ .card:hover {
370
+ border-color: var(--neutral--neutral-30);
371
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
372
+ }
373
+
374
+ /* Pricing card — recommended variant */
375
+ .card-pricing-recommended {
376
+ border: 2px solid var(--neutral--neutral-100);
377
+ }
378
+ ```
379
+
380
+ ### CTA Dark Section
381
+ ```css
382
+ .cta-dark {
383
+ background: #0e0e0e;
384
+ border-radius: var(--border-radius--24);
385
+ padding: var(--spacing--80) var(--spacing--32);
386
+ text-align: center;
387
+ }
388
+ .cta-dark .cta-inner {
389
+ max-width: var(--layout--cta-content-max); /* 640px */
390
+ margin: 0 auto;
391
+ display: flex;
392
+ flex-direction: column;
393
+ align-items: center;
394
+ gap: var(--spacing--24);
395
+ }
396
+ ```
397
+
398
+ ### FAQ Accordion
399
+ ```css
400
+ .faq-layout { display: grid; grid-template-columns: 1fr 1.5fr; gap: 3rem; }
401
+ .faq-left { position: sticky; top: 6rem; height: fit-content; }
402
+ .faq-item { border-bottom: 1px solid var(--neutral--neutral-20); }
403
+ .faq-toggle { transition: transform 0.3s ease; }
404
+ .faq-item.open .faq-toggle { transform: rotate(45deg); }
405
+ .faq-answer { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; }
406
+ ```
407
+
408
+ ---
409
+
410
+ ## Layout
411
+
412
+ ### Container
413
+ ```css
414
+ .container {
415
+ max-width: var(--layout--container-max); /* 1200px */
416
+ margin: 0 auto;
417
+ padding: 0 var(--layout--container-padding); /* 0 2rem */
418
+ }
419
+ @media (max-width: 767px) {
420
+ .container { padding: 0 var(--layout--container-padding-mobile); } /* 0 1.25rem */
421
+ }
422
+ ```
423
+
424
+ ### Section Header
425
+ ```css
426
+ .section-header {
427
+ max-width: var(--layout--section-header-max); /* 720px */
428
+ margin: 0 auto var(--spacing--56);
429
+ text-align: center;
430
+ display: flex;
431
+ flex-direction: column;
432
+ align-items: center;
433
+ gap: var(--spacing--16);
434
+ }
435
+ ```
436
+
437
+ ### Responsive Grid Behaviour
438
+ | Component | Desktop | Tablet (≤991px) | Mobile (≤767px) |
439
+ |-----------|---------|-----------------|-----------------|
440
+ | Feature cards | 3 columns | 2 columns | 1 column |
441
+ | 50/50 narrative | 2 col, 3rem gap | 2 col, 2rem gap | 1 col, 1.5rem gap |
442
+ | Pricing | 2 columns | 2 columns | 1 column |
443
+ | FAQ | 2 col grid | 1 column | 1 column |
444
+ | Verdict cards | 3 columns | 3 columns | 1 column |
445
+ | Button group | horizontal | horizontal | vertical, full-width |
446
+ | Stat font size | 3rem | 3rem | 2.25rem |
447
+
448
+ ---
449
+
450
+ ## Dos and Don'ts
451
+
452
+ | ✅ Do | ❌ Don't |
453
+ |-------|---------|
454
+ | Use `--border-radius--pill` (250px) for all buttons | Use `8px` or fixed px on buttons |
455
+ | Use `Inter` (not Inter Display) for button text | Use Inter Display for button labels |
456
+ | Use `#0e0e0e` for text/interactive darks | Mix up `#0e0e0e` and `#101319` |
457
+ | Use `--badge--border-radius` (2000px) for tags | Use smaller radius for badge/pill shapes |
458
+ | Reference `--text--subtext` for secondary text | Hardcode `#5b5c5d` |
459
+ | Use stroke-only Lucide icons at 1.5px | Fill icons or mix icon libraries |
460
+ | Apply `-text` (90-shade) for text on tinted bg | Use black on brand-colour fills |
461
+ | Import `fynd-tokens.css` before all other styles | Override token values inline |
462
+
463
+ ---
464
+
465
+ ## CSS Variable Quick Reference
466
+
467
+ ### Naming Conventions
468
+ | Pattern | Example | Used in |
469
+ |---------|---------|---------|
470
+ | `--font-family--{role}` | `--font-family--primary` | Typography |
471
+ | `--font-size--{scale}` | `--font-size--body-m` | Typography |
472
+ | `--neutral--neutral-{n}` | `--neutral--neutral-60` | Raw neutrals |
473
+ | `--{color}--{color}-{shade}` | `--blue--blue-40` | Raw brand colors |
474
+ | `--{color}--{color}-{alias}` | `--blue--blue-fill` | Semantic shortcuts |
475
+ | `--text--{role}` | `--text--subtext` | Semantic text |
476
+ | `--background--{role}` | `--background--background-medium` | Semantic bg |
477
+ | `--border--{role}` | `--border--border-medium` | Semantic borders |
478
+ | `--spacing--{n}` | `--spacing--24` | Spacing scale |
479
+ | `--border-radius--{n}` | `--border-radius--pill` | Radius scale |
480
+ | `--btn--{variant}--{prop}` | `--btn--primary--bg` | Button tokens |
481
+ | `--badge--{prop}` | `--badge--border-radius` | Badge tokens |
482
+ | `--card--{type}--{prop}` | `--card--pricing--radius` | Card tokens |
483
+
484
+ ### Figma Slash → CSS Dash Mapping
485
+ | Figma variable | CSS token |
486
+ |---------------|-----------|
487
+ | `--text/title` | `--text--title` |
488
+ | `--text/subtext` | `--text--subtext` |
489
+ | `--background/background-light` | `--background--background-light` |
490
+ | `--typeface/font-family/title` | `--font-family--primary` |
491
+ | `--typeface/font-family/body` | `--font-family--secondary` |
492
+ | `--font-family/sans` | `--font-family--ui` |
493
+ | `--font-size/m` | `--font-size--ui-m` |
494
+ | `--line-height/m` | `--line-height--ui-m` |
495
+ | `--letter-spacing/baggy` | `--letter-spacing--baggy` |
496
+ | `--borderradius/core/full` | `--border-radius--pill` |
497
+ | `--16` (Figma shorthand) | `--spacing--16` or `--16` |
498
+
499
+ ---
500
+
501
+ ## Stack-Specific Setup
502
+
503
+ ### Astro
504
+ ```astro
505
+ ---
506
+ // src/layouts/Layout.astro
507
+ ---
508
+ <html>
509
+ <head>
510
+ <!-- Inter Display + Inter via Google Fonts -->
511
+ <link rel="preconnect" href="https://fonts.googleapis.com">
512
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
513
+ <link href="https://fonts.googleapis.com/css2?family=Inter+Display:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
514
+ <!-- Fynd Sans is declared via @font-face in fynd-tokens.css -->
515
+ </head>
516
+ <body>
517
+ <slot />
518
+ </body>
519
+ </html>
520
+
521
+ <style is:global>
522
+ @import '../styles/fynd-tokens.css'; /* @font-face for Fynd Sans lives here */
523
+ </style>
524
+ ```
525
+
526
+ ### Next.js (App Router)
527
+ ```tsx
528
+ // app/layout.tsx
529
+ // Note: Fynd Sans must be loaded via @font-face in globals.css (not next/font — proprietary font)
530
+ import { Inter_Display, Inter } from 'next/font/google'
531
+ import './globals.css' // contains @import './fynd-tokens.css' with Fynd Sans @font-face
532
+
533
+ const interDisplay = Inter_Display({
534
+ subsets: ['latin'],
535
+ weight: ['400','500','600','700'],
536
+ variable: '--font-inter-display'
537
+ })
538
+ const inter = Inter({
539
+ subsets: ['latin'],
540
+ weight: ['400','500','600'],
541
+ variable: '--font-inter'
542
+ })
543
+
544
+ export default function RootLayout({ children }) {
545
+ return (
546
+ <html className={`${interDisplay.variable} ${inter.variable}`}>
547
+ <body>{children}</body>
548
+ </html>
549
+ )
550
+ }
551
+ ```
552
+
553
+ > Place `FyndSans-*.woff2` files in `public/fonts/` and update the `url()` paths in `fynd-tokens.css` to `/fonts/FyndSans-Regular.woff2` etc.
554
+
555
+ ### React (Vite / CRA)
556
+ ```tsx
557
+ // main.tsx or index.tsx
558
+ import './styles/fynd-tokens.css' // Fynd Sans @font-face + all tokens
559
+ ```
560
+ > Place `FyndSans-*.woff2` files in `src/assets/fonts/` and update the `url()` paths accordingly.
561
+
562
+ ### Vanilla HTML
563
+ ```html
564
+ <head>
565
+ <!-- Fynd Sans via fynd-tokens.css @font-face (files must be on your server) -->
566
+ <link rel="stylesheet" href="fynd-tokens.css">
567
+ <!-- Inter Display + Inter -->
568
+ <link rel="preconnect" href="https://fonts.googleapis.com">
569
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
570
+ <link href="https://fonts.googleapis.com/css2?family=Inter+Display:wght@400;500;600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
571
+ <!-- Lucide icons -->
572
+ <script src="https://unpkg.com/lucide@latest"></script>
573
+ </head>
574
+ <body>
575
+ <script>lucide.createIcons();</script>
576
+ </body>
577
+ ```
578
+
579
+ ---
580
+
581
+ ## File Structure (when adding to a project)
582
+
583
+ ```
584
+ your-project/
585
+ ├── FYND_DESIGN_SYSTEM.md ← this file (AI context + dev reference)
586
+ ├── src/
587
+ │ └── styles/
588
+ │ ├── fynd-tokens.css ← import first, before all other styles
589
+ │ └── globals.css ← @import './fynd-tokens.css' at top
590
+ ```
591
+
592
+ ---
593
+
594
+ ## Source References
595
+
596
+ | Resource | URL / Location |
597
+ |----------|---------------|
598
+ | One Design System (Figma) | https://www.figma.com/design/qtxg951KvgNG3jYzQQU2s4/One-Design-system |
599
+ | **Fynd Sans** font files | Internal — contact design@fynd.com or pull from design assets repo |
600
+ | Inter Display (Google Fonts) | https://fonts.google.com/specimen/Inter+Display |
601
+ | Inter (Google Fonts) | https://fonts.google.com/specimen/Inter |
602
+ | @fontsource/inter-display | https://fontsource.org/fonts/inter-display |
603
+ | Lucide Icons | https://lucide.dev |
604
+ | lucide-react (npm) | https://www.npmjs.com/package/lucide-react |
605
+ | devlink/global.css | Internal — `devlink/global.css` (~2800 lines) |
606
+ | compare.css | Internal — `src/styles/compare.css` (501 lines) |
607
+
608
+ *Last synced: March 2026*