@deriv-ds/design-intelligence-layer 0.6.1 → 0.6.2

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.
@@ -14,7 +14,7 @@ import { Icon } from "@deriv-ds/design-intelligence-layer";
14
14
  <Icon name="bell" />
15
15
 
16
16
  // Explicit weight + styling via className
17
- <Icon name="circle-check" weight="fill" className="size-4 text-success" />
17
+ <Icon name="circle-check" weight="fill" className="size-4 text-[var(--text-success-default)]" />
18
18
  <Icon name="user" weight="regular" className="text-subtle" />
19
19
  ```
20
20
 
@@ -39,7 +39,8 @@ ALWAYS use semantic tokens:
39
39
  ✅ text-prominent, text-subtle, text-on-prominent-static-inverse
40
40
  ✅ border-default, border-selected, ring-ring
41
41
  ✅ bg-primary, bg-primary-hover, bg-secondary-hover
42
- ✅ text-warning, text-info, text-alert-info-text, text-alert-error-text
42
+ ✅ text-warning, text-info, text-success, text-error (the four status text utilities), text-alert-info-text, text-alert-error-text
43
+ ✅ text-on-primary / text-on-success / text-on-error / text-on-warning / text-on-info (foreground on a solid coloured surface)
43
44
  ✅ Opacity on a token IS allowed: bg-primary/20, border-default/50, ring-primary/10
44
45
  ```
45
46
 
@@ -59,7 +60,7 @@ Token rules apply ONLY to:
59
60
  ### Rule 4 — Do NOT install or import these separately
60
61
 
61
62
  ```
62
- ❌ lucide-react or any icon library — icons come from FontAwesome Pro via the package's <Icon /> component (see icon-reference.md in this folder)
63
+ ❌ lucide-react or any icon library — icons come from `@deriv/quill-icons` via the package's <Icon /> component (see icon-reference.md in this folder)
63
64
  ❌ tailwindcss — the package ships its own Tailwind v4 config
64
65
  ❌ @apply with hsl(var(--token)) — Tailwind v4 uses CSS variables directly
65
66
  ❌ tailwind.config.js — configuration is handled by the package via CSS
@@ -135,14 +136,14 @@ import { Button, Card, Tag } from "@deriv-ds/design-intelligence-layer"
135
136
 
136
137
  ### 2.3 — Semantic (Status)
137
138
 
138
- | CSS Variable | Tailwind Class | Usage |
139
- | --------------------------- | ----------------------- | ------------------------- |
140
- | `--text-success-default` | `text-success` (via foundation) | Success / Positive outcomes (green-900 light, green-500 dark) |
141
- | `--text-error-default` | `text-error` (via foundation) | Error / Negative outcomes (red-900 light, red-500 dark) |
142
- | `--text-warning-default` | `text-warning` | Warning / Caution / Non-destructive alerts (yellow-900 light, yellow-500 dark) |
143
- | `--text-information-default`| `text-info` | Informational alerts (blue-900 light, blue-500 dark) |
139
+ | CSS Variable | Tailwind Class | Usage |
140
+ | --------------------------- | ------------------------------------ | ------------------------- |
141
+ | `--text-warning-default` | `text-warning` | Warning / Caution / Non-destructive alerts (yellow-900 light, yellow-500 dark) |
142
+ | `--text-information-default`| `text-info` | Informational alerts (blue-900 light, blue-500 dark) |
143
+ | `--text-success-default` | `text-success` | Success / Positive outcomes (green-900 light, green-500 dark) |
144
+ | `--text-error-default` | `text-error` | Error / Negative outcomes (red-900 light, red-500 dark) |
144
145
 
145
- > All semantic tokens support opacity variants (e.g. `bg-warning/10` for tinted backgrounds). Foundation background tokens are also available: `--background-success-default`, `--background-error-default`, `--background-warning-default`, `--background-information-default`. For structured alpha surfaces, use the primitive alpha scales — see Section 2.10.
146
+ > **All four status colours are bare utility classes:** `text-warning`, `text-info`, `text-success`, `text-error` (each mapped to its `--text-*-default` value in the theme bridge — theme-aware and opacity-friendly, e.g. `bg-success/10`). The older `text-[var(--text-success-default)]` / `text-[var(--text-error-default)]` form still resolves (the package's own blocks such as `hero-mobile-transaction.tsx` use it) but new code should prefer the utilities. Foundation background tokens are also available: `--background-success-default`, `--background-error-default`, `--background-warning-default`, `--background-information-default`. For text/icons on a **solid** coloured surface use `text-on-primary` / `text-on-success` / `text-on-error` / `text-on-warning` / `text-on-info`. For a **solid opaque status tint** that won't bleed on a coloured background use `bg-success-surface` / `bg-error-surface` / `bg-warning-surface` / `bg-info-surface` (built with `color-mix`, theme-aware). For structured alpha surfaces, use the primitive alpha scales — see Section 2.10.
146
147
 
147
148
  ### 2.4 — Text & Icon Colors
148
149
 
@@ -160,7 +161,7 @@ import { Button, Card, Tag } from "@deriv-ds/design-intelligence-layer"
160
161
  | `--border-subtle-default` | (via `--input`) | Subtle / light border (slate @ 8% alpha) |
161
162
  | `--border-selected-default` | `border-selected` | Selected / emphasized border (solid slate-1200) |
162
163
  | `--input` | `border-input` | Input resting state border (aliases `--border-subtle-default`) |
163
- | `--ring` | `ring-ring` / `border-ring` | Focus ring and focus border color (coral-700) |
164
+ | `--ring` | `ring-ring` | Focus ring color (blue-800 `#1789E1` chosen to clear WCAG 1.4.11's 3:1 minimum on both light and dark surfaces; see issue #38). Pair with `ring-2 ring-offset-2 ring-offset-primary-surface`, not `border-ring` |
164
165
 
165
166
  ### 2.6 — Supporting Colors
166
167
 
@@ -182,7 +183,7 @@ import { Button, Card, Tag } from "@deriv-ds/design-intelligence-layer"
182
183
  | Subtle hover tint | `bg-primary/[0.08]` |
183
184
  | Medium highlight | `bg-primary/[0.16]` |
184
185
  | Selected/active tint | `bg-primary/10` |
185
- | Status tint backgrounds | `bg-success/10`, `bg-error/10`, `bg-warning/10`, etc. |
186
+ | Status tint backgrounds | `bg-warning/10`, `bg-info/10`, `bg-success/10`, `bg-error/10` (all utilities); or the foundation tints `bg-[var(--background-success-default)]` / `bg-[var(--background-error-default)]` |
186
187
 
187
188
  ### 2.8 — Interactive States
188
189
 
@@ -225,6 +226,8 @@ import { Button, Card, Tag } from "@deriv-ds/design-intelligence-layer"
225
226
  <div className="transition-colors duration-fast ease-standard">...</div>
226
227
  ```
227
228
 
229
+ **Reduced motion:** the base layer includes a global `@media (prefers-reduced-motion: reduce)` rule that collapses all `animation-duration` / `transition-duration` to `0.01ms` and forces `scroll-behavior: auto` for every element. This is automatic — components using the duration/easing tokens above don't need to add anything extra. If you hand-roll a custom animation outside these tokens (e.g. a raw `@keyframes` or a JS-driven animation library), you must independently respect `prefers-reduced-motion` since the CSS rule only overrides CSS-driven animations/transitions.
230
+
228
231
  ### 2.10 — Primitive Alpha Scales
229
232
 
230
233
  > Raw alpha (opacity) variants. These are **internal CSS variables only** — not exposed as Tailwind utility classes. Reference them only from semantic or component tokens, never directly in components.
@@ -274,7 +277,7 @@ import { Button, Card, Tag } from "@deriv-ds/design-intelligence-layer"
274
277
  | `--primitive-emerald-alpha-*`| `oklch(0.725 0.150 166.0)` | Real-account highlights |
275
278
  | `--primitive-cherry-alpha-*`| `oklch(0.571 0.229 18.2)` | Reserved |
276
279
 
277
- > The `--overlay` semantic token resolves to `--primitive-black-50` (black at 0% effectively transparent). For modal backdrops, use `bg-overlay` which is handled by the semantic layer.
280
+ > The `--overlay` semantic token resolves to black at 50% opacity (via the `--overlay` CSS variable). Use `bg-overlay` for modal/dialog/sheet/drawer backdrops do not manually reconstruct it with `bg-black/50`. The alpha-stop number in the primitive scale naming convention (e.g. `alpha-50`) refers to the palette stop index, not the opacity percentage; the actual opacity is defined separately in the semantic token.
278
281
 
279
282
  ---
280
283
 
@@ -285,8 +288,8 @@ import { Button, Card, Tag } from "@deriv-ds/design-intelligence-layer"
285
288
  | IF you need to... | THEN use | NEVER use |
286
289
  | -------------------------------------- | --------------------------------- | -------------------------------------- |
287
290
  | Pick a primary CTA button color | `bg-primary` | Green, blue, or any other color for primary CTA |
288
- | Show SUCCESS / profit / positive | `text-success` (foundation) | Primary coral for profit |
289
- | Show ERROR / negative | `text-error` (foundation) | Any non-token red |
291
+ | Show SUCCESS / profit / positive | `text-success` | Primary coral for success, non-token green |
292
+ | Show ERROR / negative | `text-error` | Primary coral for error, non-token red |
290
293
  | Show WARNING / caution | `text-warning` | Raw orange hex or primary tint |
291
294
  | Color a tab container background | `bg-tabs` | `bg-secondary-surface` for tabs |
292
295
  | Color an active/selected tab | `bg-tabs-active` | `bg-primary-surface` for active tab |
@@ -301,10 +304,11 @@ import { Button, Card, Tag } from "@deriv-ds/design-intelligence-layer"
301
304
  | Add a default border | `border-default` | Solid black borders, raw hex |
302
305
  | Add a selected/strong border | `border-selected` | — |
303
306
  | Style an input (resting) | `border-input` | Solid colored backgrounds |
304
- | Style an input (focused) | `border-ring` + `ring-[3px] ring-ring/50` | Generic shadows on inputs |
307
+ | Style an interactive element (focused) | `ring-2 ring-ring ring-offset-2 ring-offset-primary-surface` | `ring-ring/50` (fails 3:1 contrast), generic shadows |
308
+ | Style an input (focused) | Border-based: `border-field-*-border-focus` (see Section 8.5 Input) | `ring-ring` on inputs — inputs use border, not ring, focus indication |
305
309
  | Pick a heading font | `font-display` | System fonts, raw font-family names |
306
310
  | Pick a body font | `font-body` | Raw font-family names |
307
- | Style button text | `font-display font-semibold`, sentence case | `uppercase` on buttons, `font-bold` |
311
+ | Style button text | `font-display font-extrabold`, sentence case | `uppercase` on buttons, `font-bold` |
308
312
  | Write white text on a dark surface | `text-on-prominent-static-inverse`| `text-white` or raw white hex |
309
313
 
310
314
  ---
@@ -368,7 +372,7 @@ These sizes differ from standard Tailwind defaults — use the design system val
368
372
  | Context | Class | Weight |
369
373
  | ------------------------ | -------------- | ------------------- |
370
374
  | Page headings | `font-display` | `font-extrabold` (800) |
371
- | Button labels | `font-display` | `font-semibold` (600), sentence case |
375
+ | Button labels | `font-display` | `font-extrabold` (800), sentence case |
372
376
  | Body text | `font-body` | `font-normal` (400) |
373
377
  | Tiny labels / decorative | `font-body` | `font-medium` (500) |
374
378
 
@@ -434,13 +438,13 @@ The design system uses a custom radius scale (base = 10px). Use these named toke
434
438
 
435
439
  ### 6.3 — Button Typography & States
436
440
 
437
- All buttons: `font-display font-semibold` (600), sentence case (no `uppercase`, no `tracking-wide`).
441
+ All buttons: `font-display font-extrabold` (800), sentence case (no `uppercase`, no `tracking-wide`).
438
442
 
439
443
  | State | Behavior |
440
444
  | -------- | ------------------------------------------------ |
441
445
  | Default | Base styling per variant |
442
446
  | Hover | Color shift per variant (see above) |
443
- | Focus | 3px ring with `ring-ring/50` opacity |
447
+ | Focus | 2px `ring-ring` (full opacity, blue-800) + 2px `ring-offset-primary-surface` — clears WCAG 1.4.11 3:1 |
444
448
  | Pressed | Pressed bg token per variant (e.g. `bg-button-primary-bg-pressed`) |
445
449
  | Loading | `opacity-24`, `pointer-events-none`, `data-loading`, `aria-busy` — applies to all variants |
446
450
  | Disabled | Dedicated disabled tokens: `bg-button-disabled-normal-bg`, `text-button-disabled-normal-text` — not generic opacity |
@@ -544,8 +548,6 @@ import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, Dialog
544
548
  | Kbd | [simple] | KbdGroup |
545
549
  | Label | [simple] | — |
546
550
  | Link | [simple] | — |
547
- | Menubar | [complex] | MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, MenubarSeparator, MenubarLabel, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarSub, MenubarSubTrigger, MenubarSubContent, MenubarShortcut |
548
- | Modal | [complex] | ModalTrigger, ModalContent, ModalHeader, ModalFooter, ModalTitle, ModalDescription, ModalAction, ModalCancel, ModalMedia |
549
551
  | NativeSelect | [simple] | NativeSelectOption, NativeSelectOptGroup |
550
552
  | NavigationMenu | [complex] | NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator, NavigationMenuViewport, navigationMenuTriggerStyle |
551
553
  | OTPField | [composed] | OTPFieldGroup, OTPFieldSlot, OTPFieldSeparator |
@@ -562,7 +564,7 @@ import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, Dialog
562
564
  | Sidebar | [complex] | SidebarProvider, SidebarTrigger, SidebarInset, SidebarHeader, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarRail, useSidebar |
563
565
  | Skeleton | [simple] | — |
564
566
  | Slider | [simple] | — |
565
- | Sonner (Toaster) | [simple] | — (also import `toast` from `sonner`) |
567
+ | Sonner (Toaster) | [simple] | — (the imperative `toast` is re-exported from the package: `import { Snackbar, toast } from "@deriv-ds/design-intelligence-layer"`) |
566
568
  | Spinner | [simple] | — |
567
569
  | Stepper | [simple] | — |
568
570
  | Switch | [simple] | — |
@@ -581,9 +583,9 @@ These are styling behaviors you can't discover from TypeScript types alone:
581
583
 
582
584
  | Component | Note |
583
585
  |-----------|------|
584
- | Button | `font-display font-semibold` (600), sentence case (no `uppercase`). Primary: `text-on-prominent-static-inverse`. Radius: `rounded-full` (pill). 4 variants: primary, secondary, tertiary, ghost |
586
+ | Button | `font-display font-extrabold` (800), sentence case (no `uppercase`). Primary: `text-on-prominent-static-inverse`. Radius: `rounded-full` (pill). 4 variants: primary, secondary, tertiary, ghost |
585
587
  | Card | Flat by default (no shadow). Add elevation manually: `className="shadow-sm"` or use elevation tokens `var(--elevation-100)` through `var(--elevation-500)` |
586
- | Input | Resting: `border-input`. Focus: `border-ring` + `ring-[3px] ring-ring/50`. Radius: `rounded-sm` (6px) |
588
+ | Input | Resting: `border-input`. Focus: border-based (`border-field-*-border-focus` + border-width step-up), no ring. Radius: `rounded-sm` (6px). **Variants:** `outline` (default), `fill`, `inverse` (sunken translucent fill + light foregrounds — for inputs ON a brand/coloured surface; also on `InputGroup`). **`readOnly`** renders a distinct borderless, softly-filled "display only" state (separate from the faded `disabled` state). |
587
589
  | Tooltip | Bubble: uses component tokens `--tooltip-bg-default` + `--tooltip-text-default`. Always wrap in `<TooltipProvider>` |
588
590
  | Sidebar | Menu buttons use `rounded-sm`. Selection: `bg-secondary-hover` + `text-primary` + `font-semibold` |
589
591
  | Breadcrumb | Active page: `text-primary font-medium`. Links: `text-subtle` |
@@ -595,7 +597,6 @@ These are styling behaviors you can't discover from TypeScript types alone:
595
597
  | Switch | Checked track: `bg-slider-range`. Checked thumb: `bg-primary` |
596
598
  | Slider | Thumb: square `rounded-[4px]`, `bg-primary`. Track range: `bg-slider-range` |
597
599
  | Dialog/Sheet | Footer buttons should use `size="md"`. Overlay: `bg-overlay` |
598
- | Menubar | Hover: `bg-primary/[0.08]`. Open state: `bg-primary/10` |
599
600
  | Toggle | Pressed: `bg-primary/10` + `border-primary` + `text-primary` |
600
601
  | Calendar | Selected: `bg-secondary-hover` + `text-primary font-bold`. Today: primary dot below date |
601
602
 
@@ -634,9 +635,9 @@ Landing page hero sections in multiple layout types.
634
635
  | Type 1 — Mobile | Single column stacked: text content centred above, image panel full-width below |
635
636
  | Type 2 — Desktop | Centred single-column: tagline pill + heading + body + single primary CTA with `<Icon name="arrow-right" />`; no image |
636
637
 
637
- **Components used:** `Button` (default `sm`, `secondary sm`); `<Icon name="arrow-up-right" />`, `<Icon name="arrow-right" />` (FontAwesome Pro via the `Icon` component)
638
+ **Components used:** `Button` (default `sm`, `secondary sm`); `<Icon name="arrow-up-right" />`, `<Icon name="arrow-right" />` (`@deriv/quill-icons` via the `Icon` component)
638
639
 
639
- **Tokens used:** `bg-primary-surface`, `bg-secondary-surface`, `border-default`, `text-prominent`, `text-subtle`, `bg-success`, `rounded-xl`, `rounded-full`, `px-layout-margin-inline`, `gap-layout-gutter`, `py-24`, `text-5xl`, `font-semibold`, `font-display`, `tracking-tight`, `text-lg`, `font-body`, `text-sm`, `shadow-sm`, `max-w-2xl`
640
+ **Tokens used:** `bg-primary-surface`, `bg-secondary-surface`, `border-default`, `text-prominent`, `text-subtle`, `bg-[var(--background-success-default)]`, `rounded-xl`, `rounded-full`, `px-layout-margin-inline`, `gap-layout-gutter`, `py-24`, `text-5xl`, `font-semibold`, `font-display`, `tracking-tight`, `text-lg`, `font-body`, `text-sm`, `shadow-sm`, `max-w-2xl`
640
641
 
641
642
  ---
642
643
 
@@ -652,18 +653,18 @@ Landing page hero sections in multiple layout types.
652
653
  | Using `text-on-decorative` | Use `text-subtle` |
653
654
  | Using `font-mono` expecting Inter | Use `font-display` or `font-body` |
654
655
  | Using `font-semibold` for headings | Use `font-extrabold` (800) for headings |
655
- | Using `font-bold` for buttons | Use `font-semibold` (600) for buttons |
656
+ | Using `font-bold` or `font-semibold` for buttons | Use `font-extrabold` (800) for buttons |
656
657
  | Using `uppercase` on buttons | Buttons use sentence case (no `uppercase`) |
657
658
  | Using standard TW sizes for headings (e.g. `text-6xl` = 60px) | Use design system values (`text-6xl` = 64px in this system) |
658
659
  | Using `rounded-[4px]` or `rounded-[2px]` | Use `rounded-xs` (4px) or `rounded-2xs` (2px) |
659
660
  | Using small radius on buttons | Buttons use `rounded-full` (pill shape, 999px) |
660
- | Installing or importing `lucide-react` | Use `<Icon name="..." weight="..." />` (FontAwesome Pro) — catalog in `icon-reference.md` |
661
+ | Installing or importing `lucide-react` | Use `<Icon name="..." weight="..." />` (`@deriv/quill-icons`) — catalog in `icon-reference.md` |
661
662
  | Adding `tailwind.config.js` | Tailwind v4 uses CSS config via the package |
662
663
  | Using `bg-gray-*`, `text-zinc-*`, etc. | Use semantic tokens only |
663
664
  | Using `bg-prominent`, `bg-card`, `bg-popover`, `bg-subtle` | Use `bg-primary-surface`, `bg-primary-canvas`, `bg-secondary-surface`, `bg-secondary-canvas` |
664
665
  | Using `text-on-prominent`, `text-on-subtle` | Use `text-prominent`, `text-subtle` |
665
666
  | Using `border-border-subtle`, `border-border-prominent` | Use `border-default`, `border-selected` |
666
- | Using `text-semantic-win`, `text-semantic-loss` | Use foundation tokens or component tokens for status colors |
667
+ | Using `text-semantic-win`, `text-semantic-loss` | Use the status utilities `text-success` / `text-error` (or `text-warning` / `text-info`) |
667
668
 
668
669
  ---
669
670
 
@@ -12,7 +12,13 @@ alwaysApply: true
12
12
 
13
13
  ### Available components (check this list first)
14
14
 
15
- Accordion, Alert, AlertDialog, Avatar, AvatarGroup, Badge, Breadcrumb, Button, Calendar, Card, Carousel, Chart, Checkbox, Collapsible, Combobox, Command, ContextMenu, Dialog, Drawer, DropdownMenu, EmptyState, Field, Form, HoverCard, Input, InputGroup, InputOTP, Item, Kbd, Label, Menubar, NativeSelect, NavigationMenu, Pagination, Popover, Progress, RadioGroup, Resizable, ScrollArea, Select, Separator, Sheet, Sidebar, Skeleton, Slider, Spinner, Switch, Table, Tabs, Textarea, Toast/Toaster, Toggle, ToggleGroup, Tooltip
15
+ Accordion, AlertDialog, Avatar, AvatarGroup, Badge, Breadcrumb, Button, Calendar, Card, Carousel, Chart, Checkbox, Collapsible, Combobox, Command, ContextMenu, Dialog, Drawer, DropdownMenu, Empty, Field, Form, HoverCard, Input, InputGroup, InputOTP, Item, Kbd, Label, Link, NativeSelect, NavigationMenu, NotificationBanner, Pagination, Popover, Progress, RadioGroup, Resizable, ScrollArea, SectionMessage, Select, Separator, Sheet, Sidebar, Skeleton, Slider, Sonner (Toaster), Spinner, Stepper, Switch, Table, Tabs, Tag, Textarea, Toggle, ToggleGroup, Tooltip
16
+
17
+ > **No `Alert` / `Banner` component.** For an inline status/alert message use `SectionMessage`. For a dismissible banner bar use `NotificationBanner`. Never invent an `Alert` or `Banner` import.
18
+
19
+ ### Complexity — ask before building with `[complex]` components
20
+
21
+ Each component in the design guide (Section 8) is tagged `[simple]` (drop-in, one import), `[composed]` (has required sub-components — use the full pattern), or `[complex]` (significant state/setup). Before building with a `[complex]` component, **ask the user clarifying questions first** rather than guessing at configuration. The `[complex]` set is: Calendar, Carousel, Chart, Combobox, Command, Dialog, Drawer, Field, Form, NavigationMenu, Resizable, Sheet, Sidebar.
16
22
 
17
23
  ### Decision flow
18
24
 
@@ -41,10 +47,10 @@ import { Button, Card, Badge } from "@deriv-ds/design-intelligence-layer"
41
47
  ### Never use
42
48
 
43
49
  ```
44
- ❌ Hardcoded hex: #2323FF, #000000, rgba(0,0,0,0.5)
50
+ ❌ Hardcoded hex: #FF444F, #000000, rgba(0,0,0,0.5)
45
51
  ❌ Raw Tailwind palette: bg-gray-100, text-zinc-500, text-slate-400, bg-black, bg-white
46
- ❌ Arbitrary color: bg-[#EEEEEE], text-[rgba(35,35,255,0.1)]
47
- ❌ Raw CSS var: bg-[var(--border-subtle)] ← NEVER do this
52
+ ❌ Arbitrary color: bg-[#EEEEEE], text-[rgba(255,68,79,0.1)]
53
+ ❌ Raw CSS var: bg-[var(--border-subtle)] ← NEVER do this (exception: the success/error status vars below)
48
54
  ❌ hsl(var()) syntax: hsl(var(--primary)) ← this is Tailwind v3, not v4
49
55
  ❌ Raw opacity on non-tokens: bg-black/50
50
56
  ```
@@ -53,41 +59,45 @@ import { Button, Card, Badge } from "@deriv-ds/design-intelligence-layer"
53
59
 
54
60
  #### Background tokens
55
61
  ```
56
- ✅ bg-prominent — page background (white #FFFFFF)
57
- ✅ bg-card — card/panel surface (white #FFFFFF)
58
- ✅ bg-popover popover/dropdown surface (white #FFFFFF)
59
- ✅ bg-subtle — subtle tinted surface (#F5F5F5)
62
+ ✅ bg-primary-canvas main page / canvas background
63
+ ✅ bg-primary-surface — card/panel/modal surface
64
+ ✅ bg-card / bg-popover alias bg-primary-surface (cards, dropdowns)
65
+ ✅ bg-secondary-surface — subtle / muted surface
66
+ ✅ bg-secondary-canvas — secondary canvas area
60
67
  ✅ bg-overlay — modal/dialog backdrop (black 50%) — ONLY for overlays
61
- ✅ bg-primary — brand blue #2323FF — CTAs and primary actions
62
- ✅ bg-primary-hover — darker blue #0B0BD2 — primary button hover
63
- ✅ bg-secondary-hover — light grey #EEEEEE — outline/secondary button hover
64
- ✅ bg-win — greenprofit/positive
65
- ✅ bg-loss — red — loss/negative
68
+ ✅ bg-primary — brand coral #FF444F — CTAs and primary actions
69
+ ✅ bg-primary-hover — darker coral #E12E3A — primary button hover
70
+ ✅ bg-secondary-hover — light grey — outline/secondary button hover
71
+ ✅ bg-tabs / bg-tabs-active tab container / active tab background
66
72
  ```
73
+ > Do NOT use `bg-prominent` or `bg-subtle` as backgrounds — those classes map to TEXT foreground values.
67
74
 
68
75
  #### Text tokens
69
76
  ```
70
- ✅ text-on-prominent — primary text (black #000000)
77
+ ✅ text-prominent — primary text (headings, body)
71
78
  ✅ text-on-prominent-static-inverse — always white — for text on dark/primary bg
72
- ✅ text-on-subtle — secondary text (mid grey)
79
+ ✅ text-subtle — secondary text (descriptions, labels)
73
80
 
74
- ✅ text-primary — brand blue #2323FF
75
- ✅ text-win — greenprofit/positive
76
- ✅ text-loss — redloss/negative
81
+ ✅ text-primary — brand coral #FF444F
82
+ ✅ text-warning / text-info / text-success / text-error the four status text utilities (theme-aware, opacity-friendly: bg-success/10)
83
+ ✅ text-on-primary / text-on-success / text-on-error / text-on-warning / text-on-info foreground on a SOLID coloured surface
77
84
  ```
85
+ > All four status text utilities exist (`text-warning`, `text-info`, `text-success`, `text-error`). The older `text-[var(--text-success-default)]` / `text-[var(--text-error-default)]` form still resolves (the package's own blocks use it) but prefer the bare utilities in new code.
86
+ > Use `text-on-*` foregrounds ONLY on the solid brand/status colour (`bg-primary`, `bg-success`, …). On a status *tint* (`bg-success/10`) use `text-success` / `text-error` instead.
87
+ > `text-on-prominent` still resolves but is ALWAYS-WHITE (for dark surfaces), not page body text — use `text-prominent`.
78
88
 
79
89
  #### Border tokens
80
90
  ```
81
- ✅ border-border-subtle light grey #EEEEEE — default UI borders, dividers, cards
82
- ✅ border-border-prominent pure black #000000 outline variant components
83
- ✅ border-border @deprecated alias for border-subtle (still works, prefer border-border-subtle)
84
- border-input input field borders (same value as border-subtle)
85
- ✅ ring-ring — focus ring (blue #2323FF)
91
+ ✅ border-default — default UI borders, dividers, cards (slate @ 16% alpha)
92
+ ✅ border-selected selected / emphasized / outline components (solid slate-1200)
93
+ ✅ border-input input field borders
94
+ ring-ring focus ring (blue #1789E1 WCAG 3:1 on light + dark; pair with ring-2 + ring-offset-2 ring-offset-primary-surface)
86
95
  ```
96
+ > `border-border-subtle` / `border-border-prominent` / `border-border` still resolve (deprecated aliases) — prefer `border-default` / `border-selected` in new code.
87
97
 
88
98
  #### Opacity on a token is fine
89
99
  ```
90
- ✅ bg-primary/20, border-border-subtle/50, ring-ring/10
100
+ ✅ bg-primary/20, border-default/50, ring-ring/10
91
101
  ```
92
102
 
93
103
  ---
@@ -109,7 +119,7 @@ Token rules apply **only** to: color (bg, text, border, ring, shadow color), bor
109
119
  ## Rule 4 — Do NOT install or configure these separately
110
120
 
111
121
  ```
112
- ❌ Do NOT install or import lucide-react or any icon library — use the package's <Icon /> component (FontAwesome Pro): <Icon name="bell" weight="bold" />. Catalog + weight conventions: guides/design-system-guide/icon-reference.md
122
+ ❌ Do NOT install or import lucide-react or any icon library — use the package's <Icon /> component (@deriv/quill-icons): <Icon name="bell" weight="bold" />. Catalog + weight conventions: guides/design-system-guide/icon-reference.md
113
123
  ❌ Do NOT install tailwindcss separately — the package ships its own Tailwind v4 setup
114
124
  ❌ Do NOT add a tailwind.config.js — configuration is handled by the package
115
125
  ❌ Do NOT use @apply with hsl(var(--token)) — Tailwind v4 uses CSS variables directly
@@ -141,20 +151,22 @@ Without this, Tailwind CSS will not process any styles. Next.js projects do NOT
141
151
 
142
152
  ### Button variants
143
153
  ```tsx
144
- <Button variant="primary" /> // Blue filled — main CTA
145
- <Button variant="secondary" /> // Black outline, white bg — secondary actions
146
- <Button variant="tertiary" /> // Text only, underline on hover — minimal
154
+ <Button variant="primary" /> // Coral filled — main CTA
155
+ <Button variant="secondary" /> // Outline, transparent bg — secondary actions
156
+ <Button variant="tertiary" /> // Text only — minimal
157
+ <Button variant="ghost" /> // Transparent, hover tint — lowest emphasis
147
158
  ```
159
+ > All buttons are pill-shaped (`rounded-full`) and use `font-display font-extrabold` (800), sentence case.
148
160
 
149
161
  ### Badge variants
150
162
  ```tsx
151
163
  // Solid
152
- <Badge variant="default" /> // Blue solid
164
+ <Badge variant="default" /> // Brand solid
153
165
  <Badge variant="default-success" /> // Green solid
154
166
  <Badge variant="default-fail" /> // Red solid
155
167
 
156
168
  // Tint
157
- <Badge variant="fill" /> // Blue tint bg
169
+ <Badge variant="fill" /> // Brand tint bg
158
170
  <Badge variant="fill-success" /> // Green tint bg
159
171
  <Badge variant="fill-fail" /> // Red tint bg
160
172
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deriv-ds/design-intelligence-layer",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Deriv Design System — shadcn/ui components with Tailwind CSS v4",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/styles.css CHANGED
@@ -74,6 +74,26 @@
74
74
 
75
75
  --color-warning: var(--text-warning-default);
76
76
  --color-info: var(--text-information-default);
77
+ /* Status colour utilities — mirror warning/info: theme-aware, opacity-friendly.
78
+ * `text-success` / `text-error` (status text), `bg-success/10` etc. (status tint). */
79
+ --color-success: var(--text-success-default);
80
+ --color-error: var(--text-error-default);
81
+ /* ── Paired foregrounds for text/icons ON a solid coloured surface ──
82
+ * Use ONLY with the SOLID brand/status colour (bg-primary, bg-success, bg-error,
83
+ * bg-warning, bg-info). On a status *tint* (bg-success/10, bg-[var(--background-success-default)])
84
+ * use the status text colour (text-success / text-error) instead. All resolve to the
85
+ * always-legible static-inverse foreground — additive only, no component is rewired. */
86
+ --color-on-primary: var(--on-prominent-static-inverse);
87
+ --color-on-success: var(--on-prominent-static-inverse);
88
+ --color-on-error: var(--on-prominent-static-inverse);
89
+ --color-on-warning: var(--on-prominent-static-inverse);
90
+ --color-on-info: var(--on-prominent-static-inverse);
91
+ /* Opaque status surfaces (bg-success-surface, bg-error-surface, …) — solid
92
+ * status tints that don't bleed on coloured backgrounds. See LAYER 2A. */
93
+ --color-success-surface: var(--background-success-opaque);
94
+ --color-error-surface: var(--background-error-opaque);
95
+ --color-warning-surface: var(--background-warning-opaque);
96
+ --color-info-surface: var(--background-information-opaque);
77
97
  --color-alert-info-text: var(--alert-info-text);
78
98
  --color-alert-info-border: var(--alert-info-border);
79
99
  --color-alert-error-text: var(--alert-error-text);
@@ -329,6 +349,13 @@
329
349
  --color-field-fill-border-focus: var(--field-fill-border-focus);
330
350
  --color-field-fill-border-success: var(--field-fill-border-success-focus);
331
351
  --color-field-fill-border-error: var(--field-fill-border-error-focus);
352
+ /* Field — inverse variant (input on brand/coloured surfaces) */
353
+ --color-field-inverse-bg-default: var(--field-inverse-bg-default);
354
+ --color-field-inverse-bg-hover: var(--field-inverse-bg-hover);
355
+ --color-field-inverse-bg-focus: var(--field-inverse-bg-focus);
356
+ --color-field-inverse-text: var(--field-inverse-text);
357
+ --color-field-inverse-subtle: var(--field-inverse-subtle);
358
+ --color-field-inverse-border-focus: var(--field-inverse-border-focus);
332
359
  /* ── Snackbar colour bridges ── */
333
360
  --color-snackbar-bg-default: var(--snackbar-bg-default);
334
361
  --color-snackbar-bg-fail: var(--snackbar-bg-fail);
@@ -757,6 +784,17 @@
757
784
  --background-pressed-static-light: var(--primitive-slate-alpha-200); /* #181C25 @ 16% */
758
785
  --background-primary-canvas: var(--primitive-slate-75); /* #F6F7F8 */
759
786
  --background-primary-surface: var(--primitive-slate-50); /* #FFFFFF */
787
+ /* ── Derived opaque status surfaces ──
788
+ * Solid (non-alpha) status tints that read identically on ANY background — no
789
+ * bleed-through when placed over a coloured card/hero (unlike the alpha
790
+ * background-*-default tints). Built with color-mix; inputs are theme-aware so
791
+ * the value recomputes automatically under .dark (lazy custom-property
792
+ * resolution), hence a single :root definition covers both themes. Exposed as
793
+ * bg-success-surface / bg-error-surface / bg-warning-surface / bg-info-surface. */
794
+ --background-success-opaque: color-mix(in oklch, var(--text-success-default) 12%, var(--background-primary-canvas));
795
+ --background-error-opaque: color-mix(in oklch, var(--text-error-default) 12%, var(--background-primary-canvas));
796
+ --background-warning-opaque: color-mix(in oklch, var(--text-warning-default) 12%, var(--background-primary-canvas));
797
+ --background-information-opaque: color-mix(in oklch, var(--text-information-default) 12%, var(--background-primary-canvas));
760
798
  --background-secondary-canvas: var(--primitive-slate-50); /* #FFFFFF */
761
799
  --background-secondary-surface: var(--primitive-slate-75); /* #F6F7F8 */
762
800
  --background-selected-default: var(--primitive-slate-alpha-100); /* #181C25 @ 8% */
@@ -1111,8 +1149,10 @@
1111
1149
  /* coral-800 (#E12E3A) — brand hover, same in both themes */
1112
1150
  --secondary-hover: var(--background-secondary-surface);
1113
1151
  /* TODO PR 3 — replace with button.secondary-normal-bg-hover when component tokens ship */
1114
- --ring: var(--primitive-coral-700);
1115
- /* coral-700 brand colour, same in both themes */
1152
+ /* Focus indicator. Figma spec is blue #2c9aff (blue-700), but that is only ~2.93:1 on
1153
+ white; blue-800 #1789e1 keeps the hue and clears WCAG 1.4.11 3:1 in both light
1154
+ (~3.68:1) and dark (~4.08:1). Paired with a 2px ring + 2px surface offset. See issue #38. */
1155
+ --ring: var(--primitive-blue-800);
1116
1156
  --overlay: var(--primitive-black-50);
1117
1157
  /* TODO — no dedicated overlay foundation token; keeping primitive alias. Revisit when overlay token ships. */
1118
1158
  --semantic-error: var(--text-error-default);
@@ -1218,12 +1258,24 @@
1218
1258
  @layer base {
1219
1259
  * {
1220
1260
  border-color: var(--color-default);
1221
- outline-color: color-mix(in oklch, var(--color-ring) 50%, transparent);
1261
+ /* Full opacity: a 50% alpha ring fails the 3:1 WCAG 1.4.11 focus-indicator minimum. See issue #38. */
1262
+ outline-color: var(--color-ring);
1222
1263
  }
1223
1264
 
1224
1265
  body {
1225
1266
  @apply bg-primary-surface text-prominent font-sans antialiased;
1226
1267
  }
1268
+
1269
+ @media (prefers-reduced-motion: reduce) {
1270
+ *,
1271
+ *::before,
1272
+ *::after {
1273
+ animation-duration: 0.01ms !important;
1274
+ animation-iteration-count: 1 !important;
1275
+ transition-duration: 0.01ms !important;
1276
+ scroll-behavior: auto !important;
1277
+ }
1278
+ }
1227
1279
  }
1228
1280
 
1229
1281
  /* ── Snackbar: dark mode override via :root.dark (specificity 0,1,1 beats :root 0,0,1) ── */
@@ -1597,6 +1649,15 @@
1597
1649
  --field-fill-border-focus: var(--primitive-slate-1200);
1598
1650
  --field-fill-border-success-focus: var(--primitive-green-900);
1599
1651
  --field-fill-border-error-focus: var(--primitive-red-900);
1652
+ /* Field — inverse: sunken translucent fill for inputs placed ON a brand/coloured
1653
+ * surface (hero cards, banners) where a light fill would blend in. Theme-agnostic
1654
+ * (the surface behind is coloured, not the page), so the light and dark values match. */
1655
+ --field-inverse-bg-default: var(--primitive-black-alpha-400); /* 32% black */
1656
+ --field-inverse-bg-hover: var(--primitive-black-alpha-500); /* 40% black */
1657
+ --field-inverse-bg-focus: var(--primitive-black-alpha-500); /* 40% black */
1658
+ --field-inverse-text: var(--primitive-slate-50); /* white */
1659
+ --field-inverse-subtle: var(--primitive-white-alpha-600); /* 48% white — label/placeholder/icons */
1660
+ --field-inverse-border-focus: var(--primitive-white-alpha-500); /* 40% white */
1600
1661
 
1601
1662
  /* Marker */
1602
1663
  --marker-text-asterisk: var(--primitive-red-900);
@@ -2474,6 +2535,15 @@
2474
2535
  --field-fill-border-focus: var(--primitive-slate-50);
2475
2536
  --field-fill-border-success-focus: var(--primitive-green-500);
2476
2537
  --field-fill-border-error-focus: var(--primitive-red-500);
2538
+ /* Field — inverse: sunken translucent fill for inputs placed ON a brand/coloured
2539
+ * surface (hero cards, banners) where a light fill would blend in. Theme-agnostic
2540
+ * (the surface behind is coloured, not the page), so the light and dark values match. */
2541
+ --field-inverse-bg-default: var(--primitive-black-alpha-400); /* 32% black */
2542
+ --field-inverse-bg-hover: var(--primitive-black-alpha-500); /* 40% black */
2543
+ --field-inverse-bg-focus: var(--primitive-black-alpha-500); /* 40% black */
2544
+ --field-inverse-text: var(--primitive-slate-50); /* white */
2545
+ --field-inverse-subtle: var(--primitive-white-alpha-600); /* 48% white — label/placeholder/icons */
2546
+ --field-inverse-border-focus: var(--primitive-white-alpha-500); /* 40% white */
2477
2547
 
2478
2548
  /* Marker */
2479
2549
  --marker-text-asterisk: var(--primitive-red-500);