@deriv-ds/design-intelligence-layer
v0.5.1
Published
Deriv Design System — shadcn/ui components with Tailwind CSS v4
Readme
Quill Design System
A component library and design token system built on shadcn/ui with a custom Figma-driven token architecture, Tailwind CSS v4, and React 19.
Published as @deriv-ds/design-intelligence-layer on npm.
Quick Start
Installation
npm install @deriv-ds/design-intelligence-layerCI / time-sensitive installs: If the package was published less than 72 hours ago and npm's safe-chain hold is blocking it, append
--safe-chain-skip-minimum-package-ageto the command above.
1. Add the CSS imports
In your main CSS file (e.g. globals.css), add these three lines in this order:
@import "@deriv-ds/design-intelligence-layer/styles";
@import "tailwindcss";
@source "../node_modules/@deriv-ds/design-intelligence-layer/dist";Why
@source? Tailwind v4 skipsnode_modulesby default. Without this line, Tailwind won't generate CSS for the component classes. Adjust the path if your CSS file is not one level belownode_modules(e.g. in a monorepo use../../node_modules/...).
1b. Vite projects — add the Tailwind plugin
If your project uses Vite (e.g. Vite + React), you must add the Tailwind CSS plugin to your Vite config:
npm install @tailwindcss/vite// vite.config.js
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default {
plugins: [react(), tailwindcss()],
}Next.js projects do not need this — Next.js uses PostCSS for Tailwind automatically.
2. Fonts (handled automatically)
The styles import loads Inter from Google Fonts automatically (weights 300–800). No additional font setup is needed.
Use font-body, font-display, or font-sans in your Tailwind classes — all resolve to Inter. Use font-mono for code (Red Hat Mono).
3. Use components
import { Button, Card, Badge } from "@deriv-ds/design-intelligence-layer"
export default function App() {
return (
<Card>
<Button variant="primary">Get Started</Button>
<Badge>Live</Badge>
</Card>
)
}Peer dependencies
Make sure these are installed in your project:
npm install react react-dom tailwindcssRequires React 18+ and Tailwind CSS v4+.
Icons
Icons use FontAwesome Pro 6 (Deriv-licensed). Do not install lucide-react — it is not used in this package. Use the bundled Icon component:
import { Icon } from "@deriv-ds/design-intelligence-layer"
<Icon name="bell" />
<Icon name="circle-check" weight="fill" className="text-success" />The full icon catalog is at node_modules/@deriv-ds/design-intelligence-layer/guides/design-system-guide/icon-reference.md.
What's inside
- 61 UI components — buttons, forms, dialogs, charts, sidebars, and more
- Design tokens — three-layer architecture (primitives → foundation semantics → component tokens) synced from Figma
- Light + Dark themes — toggle
.darkon<html>to switch; every semantic token has a paired override - Inter typography — heading (mega → h6) and body (xl → xs) scales ready to use
- 17 colour ramps — slate, blue, sapphire, blueberry, grape, magenta, cherry, coral, red, orange, yellow, mustard, green, emerald, tiffany, teal, seawater
- Transition tokens — semantic duration (
duration-fast,duration-base, etc.) and easing (ease-standard,ease-enter, etc.) — no hardcodedduration-200 - Elevation scale — five composed
box-shadowshorthands (--elevation-100through--elevation-500) that auto-adapt to the active theme - TypeScript — full type definitions included
- ESM + CJS — works with any bundler
Available components
| Component | Import |
|---|---|
| Accordion | Accordion, AccordionItem, AccordionTrigger, AccordionContent |
| Aspect Ratio | AspectRatio |
| Avatar | Avatar, AvatarImage, AvatarFallback, AvatarBadge, AvatarGroup |
| Badge | Badge, BadgeDot |
| Breadcrumb | Breadcrumb, BreadcrumbList, BreadcrumbItem, ... |
| Button | Button, buttonVariants |
| Calendar | Calendar, CalendarDayButton |
| Card | Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter |
| Carousel | Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext |
| Chart | ChartContainer, ChartTooltip, ChartLegend, ChartStyle |
| Checkbox | Checkbox |
| Chip | Chip, chipVariants |
| Collapsible | Collapsible, CollapsibleTrigger, CollapsibleContent |
| Combobox | Combobox, ComboboxInput, ComboboxContent, ComboboxItem, ... |
| Command | Command, CommandDialog, CommandInput, CommandList, ... |
| Context Menu | ContextMenu, ContextMenuTrigger, ContextMenuContent, ... |
| Dialog | Dialog, DialogTrigger, DialogContent, DialogHeader, ... |
| Direction | DirectionProvider, useDirection |
| Drawer | Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, DrawerClose |
| Dropdown Menu | DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, ... |
| Empty State | Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent |
| Field | Field, FieldLabel, FieldDescription, FieldError, FieldGroup |
| Form | Form, FormItem, FormLabel, FormControl, FormField, FormMessage |
| Hover Card | HoverCard, HoverCardTrigger, HoverCardContent |
| Input | Input |
| Input Group | InputGroup, InputGroupAddon, InputGroupButton, InputGroupText |
| Item | Item, ItemMedia, ItemContent, ItemTitle, ItemDescription |
| Kbd | Kbd, KbdGroup |
| Label | Label |
| Link | Link |
| Loading Spinner | LoadingSpinner |
| Modal | AlertDialog, AlertDialogTrigger, AlertDialogContent, ... |
| Native Select | NativeSelect, NativeSelectOptGroup, NativeSelectOption |
| Navigation Menu | NavigationMenu, NavigationMenuList, NavigationMenuTrigger, ... |
| Notification | NotificationBanner, NotificationItem, NotificationDivider |
| OTP Field | CodeInput, CodeInputGroup, CodeInputSlot, CodeInputSeparator |
| Pagination | Pagination, PaginationContent, PaginationLink, ... |
| Popover | Popover, PopoverTrigger, PopoverContent, PopoverAnchor |
| Progress | Progress |
| Radio Group | RadioGroup, RadioGroupItem |
| Resizable | ResizableHandle, ResizablePanel, ResizablePanelGroup |
| Scroll Area | ScrollArea, ScrollBar |
| Search Field | SearchField, searchFieldVariants |
| Section Message | SectionMessage, SectionMessageTitle, SectionMessageDescription |
| Segmented Control | SegmentedControl, SegmentedControlList, SegmentedControlTrigger, SegmentedControlContent |
| Select | Select, SelectTrigger, SelectContent, SelectItem, ... |
| Separator | Separator |
| Sheet | Sheet, SheetTrigger, SheetContent, SheetHeader, ... |
| Sidebar | Sidebar, SidebarProvider, SidebarMenu, SidebarMenuItem, ... |
| Skeleton | Skeleton |
| Slider | Slider |
| Snackbar | Snackbar |
| Spinner | Spinner |
| Stepper | Stepper |
| Switch | Switch |
| Tab | Tab, TabList, TabTrigger, TabContent |
| Table | Table, TableHeader, TableBody, TableRow, TableHead, TableCell |
| Tabs | Tabs, TabsList, TabsTrigger, TabsContent |
| Tag | Tag, tagVariants |
| Textarea | Textarea |
| Toggle | Toggle, toggleVariants |
| Toggle Group | ToggleGroup, ToggleGroupItem |
| Tooltip | Tooltip, TooltipTrigger, TooltipContent, TooltipProvider |
Blocks
Blocks are pre-composed UI patterns built from package primitives. They live in the Blocks tab of the development playground and are not exported from the npm package.
To use a block in your project, click the "Copy block" button in the playground next to the block label. It copies an AI-ready prompt with the full block source to your clipboard. Paste it into your AI agent (Claude Code, Cursor, Kiro, Copilot) and the agent will:
- Drop the block file into your project
- Ask you for values to replace each
// TODO - Bundle in any shared dependencies (e.g.
HeroActionButton)
The agent doesn't generate or invent — it pastes the exact code from the playground and only swaps the dummy data for your real data.
Available blocks
| Block | Playground tab |
|---|---|
| [mobile] section - hero (home) | Blocks → Hero |
| [desktop] section - hero (home) | Blocks → Hero |
| [mobile] section - hero (main) | Blocks → Hero |
| [desktop] section - hero (main) | Blocks → Hero |
| [mobile] section - hero (secondary) | Blocks → Hero |
| [desktop] section - hero (secondary) | Blocks → Hero |
| [mobile] section - hero (transaction details) | Blocks → Hero |
| NavBar | Blocks → NavBar |
The block source files live in app/components/hero-*.tsx. The playground reads them and builds the AI prompt at dev/build time via scripts/generate-block-sources.mjs.
Manual fallback
If you don't want to use an AI agent, open the block file directly on GitHub and copy everything between the 🟢 BLOCK TO COPY and 🔵 PLAYGROUND ONLY markers. Replace each // TODO with your own data. If the block imports HeroActionButton, also copy app/components/hero-action-button.tsx.
Button variants and sizes
The brand colour is coral #FF444F (--primitive-coral-700). Hover darkens to #E12E3A (coral-800), pressed to #C41C28 (coral-900).
<Button variant="primary" /> // Coral filled (#FF444F) — main CTA, white text
<Button variant="secondary" /> // 1.5px outline (slate-1200 / white in dark) — secondary actions
<Button variant="tertiary" /> // Filled neutral surface (slate-100 / slate-1000 in dark)
<Button variant="ghost" /> // Transparent, hover tint only
// Theme tone modifiers (per variant): normal · inverse · static-light · static-dark
// e.g. <Button variant="primary" tone="static-light" />
// Sizes (height · font-size · min-width)
<Button size="lg" /> // 48px · 18px · 96px min, 20px padding-x
<Button size="md" /> // 40px · 16px · 80px min, 16px padding-x (default)
<Button size="sm" /> // 32px · 14px · 64px min, 12px padding-x
// Icon-only sizes (square)
<Button size="icon-lg" /> // 48 × 48 — icon 24px
<Button size="icon-md" /> // 40 × 40 — icon 20px
<Button size="icon-sm" /> // 32 × 32 — icon 16pxAll sizes use a fully pill-shaped border (border-radius: 999px).
Badge variants
// Status fills (solid backgrounds, white text)
<Badge variant="number" /> // Red (#C40000 light · #FF4D4D dark)
<Badge variant="red" /> // Status red
<Badge variant="yellow" /> // Status yellow (#C47D00 / #FFBE4D)
<Badge variant="green" /> // Status green (#007A22 / #4DBC6B)
<Badge variant="blue" /> // Status info blue (#0777C4 / #53B9FF)
// Sizes
<Badge size="sm" /> // 20px height · 12px font · 8px dot
<Badge size="md" /> // 24px height · 14px font · 16px dotBadges are pill-shaped (border-radius: 999px).
Tag variants
Tags are 4px-radius chips for filtering, metadata, and categorisation. Two visual styles — outline and fill — across five tones.
<Tag tone="neutral" /> // slate (default)
<Tag tone="red" />
<Tag tone="yellow" />
<Tag tone="green" />
<Tag tone="blue" />
<Tag variant="outline" /> // border + text, transparent fill
<Tag variant="fill" /> // tinted background (red @ 8%, etc.)
// Sizes
<Tag size="sm" /> // 24px · 12px font · 16px icon · 4px gap
<Tag size="md" /> // 32px · 14px font · 18px icon · 8px gapSection message variants
<SectionMessage variant="default" /> // Neutral grey tint
<SectionMessage variant="information" /> // Blue tint + info icon
<SectionMessage variant="success" /> // Green tint + check icon
<SectionMessage variant="warning" /> // Yellow tint + alert icon
<SectionMessage variant="danger" /> // Red tint + danger icon
// Sizes
<SectionMessage size="sm" /> // 14px message · 16px title · 20px icon
<SectionMessage size="md" /> // 16px message · 18px title · 22px iconAlert variants
<Alert variant="info" /> // blue (#0777C4 / #53B9FF) — informational (default)
<Alert variant="error" /> // red (#C40000 / #FF4D4D) — error stateComponent behaviour notes
AlertDialog — button fill
AlertDialogAction and AlertDialogCancel fill the full container width only when size="sm". On size="default" (the default), buttons are natural width and right-aligned.
<AlertDialogContent size="sm"> // buttons fill width (50/50 grid)
<AlertDialogContent size="default"> // buttons natural width, right-alignedDrawer — footer buttons
DrawerFooter makes all direct child buttons fill the full width automatically via [&>*]:w-full. Stack your buttons inside DrawerFooter and they will always be full-width.
Drawer — handle bar (bottom direction)
When direction="bottom", DrawerContent renders a handle bar at the top that follows the component-bottom-sheet design tokens:
| Property | Token | Value |
|---|---|---|
| Container height | --bottom-sheet-size-height-handle-container | 32px |
| Container bg (default) | --bottom-sheet-bg-handle-default | slate-50 / slate-1100 |
| Bar height | --bottom-sheet-size-height-handle-bar | 4px |
| Bar width | --bottom-sheet-size-width-handle-bar | 40px |
| Bar color | --bottom-sheet-bg-handle-bar | black-alpha-300 / white-alpha-300 |
| Bar radius | --bottom-sheet-size-radius-handle-bar | 999 (pill) |
Link — size lg
The lg variant of Link uses text-base (16px). The md and sm variants use text-sm (14px) and text-xs (12px) respectively.
<Link size="lg" /> // 16px — font-base
<Link size="md" /> // 14px — font-sm
<Link size="sm" /> // 12px — font-xsTabs — line variant hover
On the line variant, hover state shows text-utility-brand (coral #FF444F) with no background fill. On the default (pill) variant, hover shows a slate-tinted background with text-prominent.
Tab sizes:
| Size | Min height | Min width | Padding-x | Font size |
|---|---|---|---|---|
| sm | 32px | 72px | 12px | 14px |
| md | 40px | 80px | 16px | 16px |
| lg | 48px | 96px | 20px | 18px |
Design tokens
All tokens are CSS custom properties, loaded automatically via @deriv-ds/design-intelligence-layer/styles. Tokens follow a three-layer architecture:
- Primitives (
--primitive-*) — raw colour ramps, alpha scales, durations, easings. Never reference directly. - Foundation semantics (
--background-*,--text-*,--border-*,--icon-*,--elevation-*) — paired between:root(light) and.dark(dark). - Component tokens (
--button-*,--field-*,--tag-*,--badge-*, etc.) — derived from foundation semantics.
Theme switching
Toggle dark mode by adding .dark to the <html> element:
<html class="dark"> <!-- or remove .dark for light mode -->Every semantic token has a paired light/dark value, so component code never needs theme-aware conditionals.
Background tokens
| Tailwind class | CSS variable | Light | Dark | Usage |
|---|---|---|---|---|
| bg-primary-canvas | --background-primary-canvas | #F6F7F8 | #181C25 | Page / app canvas |
| bg-secondary-canvas | --background-secondary-canvas | #FFFFFF | #181C25 | Alternate canvas |
| bg-primary-surface | --background-primary-surface | #FFFFFF | #20242F | Cards, panels, default surface |
| bg-secondary-surface | --background-secondary-surface | #F6F7F8 | #20242F | Subtle / nested surface |
| bg-overlay | --overlay | black 50% | black 50% | Modal / dialog / drawer backdrop |
| bg-tabs | --tabs | slate @ 4% | white @ 4% | Tab container background (variant="default") |
| bg-tabs-active | --tabs-active | #FFFFFF | #11141B | Active / selected tab pill |
| bg-primary | --primary | #FF444F | #FF444F | Brand coral — CTAs (same in both themes) |
| bg-primary-hover | --primary-hover | #E12E3A | #E12E3A | Primary button hover (coral-800) |
| bg-secondary-hover | --secondary-hover | secondary-surface | secondary-surface | Outline / secondary hover tint |
| bg-slider-range | --slider-range | coral @ 40% | coral @ 40% | Slider filled range |
Status backgrounds (24% tints)
| Tailwind class | Light | Dark | Usage |
|---|---|---|---|
| bg-success-default | green @ 24% | green @ 24% | Positive / win state surface |
| bg-error-default | red @ 24% | red @ 24% | Negative / loss state surface |
| bg-warning-default | yellow @ 24% | yellow @ 24% | Caution surface |
| bg-information-default | blue @ 24% | blue @ 24% | Informational surface |
Interaction tints (state layers)
| Tailwind class | Light | Dark | Usage |
|---|---|---|---|
| bg-hover-default | slate @ 8% | white @ 8% | Hover state layer |
| bg-pressed-default | slate @ 16% | white @ 16% | Pressed state layer |
| bg-selected-default | slate @ 8% | white @ 8% | Selected state layer |
Each interaction tint also ships in
inverse,static-light, andstatic-darkflavours (e.g.bg-hover-static-light) for placement on themed surfaces.
Text tokens
| Tailwind class | Light | Dark | Usage |
|---|---|---|---|
| text-prominent | #181C25 | #FFFFFF | Primary on-canvas text |
| text-subtle | slate @ 48% | white @ 48% | Secondary / supporting text |
| text-disabled-default | slate @ 16% | white @ 16% | Disabled labels |
| text-prominent-inverse | #FFFFFF | #181C25 | Text on inverse surfaces |
| text-prominent-static-dark | #FFFFFF | #FFFFFF | Always white (text on coloured fills) |
| text-prominent-static-light | #181C25 | #181C25 | Always slate (text on light fills) |
Status text
| Tailwind class | Light | Dark | Usage |
|---|---|---|---|
| text-success-default | #007A22 | #4DBC6B | Profit / positive |
| text-error-default | #C40000 | #FF4D4D | Loss / negative |
| text-warning-default | #C47D00 | #FFBE4D | Caution |
| text-information-default | #0777C4 | #53B9FF | Informational |
Utility text (brand & account modes)
| Tailwind class | Value (both themes) | Usage |
|---|---|---|
| text-utility-brand | #FF444F (coral-700) | Brand-tinted inline text |
| text-utility-real | #00C390 (emerald-700) | Real-account indicator |
| text-utility-demo | #F55F0A (orange-700) | Demo-account indicator |
The
*-inverse,*-static-light, and*-static-darkvariants exist for every status token and follow the same theme-pairing rules astext-prominent.
Border tokens
| Tailwind class | CSS variable | Light | Dark | Usage |
|---|---|---|---|---|
| border-default | --border-default-default | slate @ 16% | white @ 16% | Default global border (used by * selector) |
| border-subtle-default | --border-subtle-default | slate @ 8% | white @ 8% | Cards, dividers, panels |
| border-prominent-default | --border-prominent-default | slate @ 16% | white @ 16% | Heavier dividers |
| border-selected | --border-selected-default | #181C25 | #FFFFFF | Selected state borders (chip, list-item) |
| border-disabled-default | --border-disabled-default | slate @ 8% | white @ 8% | Disabled component borders |
| border-input | --input | slate @ 8% | white @ 8% | Input field borders (alias of border-subtle) |
| border-border | --border | slate @ 8% | white @ 8% | @deprecated alias — prefer border-subtle-default |
| ring-ring | --ring | #FF444F | #FF444F | Focus rings (brand coral) |
Status borders
| Tailwind class | Light | Dark |
|---|---|---|
| border-success-default | #007A22 | #4DBC6B |
| border-error-default | #C40000 | #FF4D4D |
| border-warning-default | #C47D00 | #FFBE4D |
| border-information-default | #0777C4 | #53B9FF |
⚠️
border-borderis deprecated. It now aliases--border-subtle-defaultfor backward compatibility. Preferborder-subtle-defaultin all new code.
Icon tokens
Icons follow the same prominent / subtle / disabled / status / utility taxonomy as text:
| Tailwind class | Light | Dark | Usage |
|---|---|---|---|
| text-icon-prominent (via --icon-prominent-default) | #181C25 | #FFFFFF | Default icon colour |
| text-icon-subtle | slate @ 48% | white @ 48% | Secondary icons |
| text-icon-disabled | slate @ 16% | white @ 16% | Disabled icons |
| text-icon-success | #007A22 | #4DBC6B | Success icons |
| text-icon-error | #C40000 | #FF4D4D | Error icons |
| text-icon-warning | #C47D00 | #FFBE4D | Warning icons |
| text-icon-information | #0777C4 | #53B9FF | Info icons |
| text-icon-utility-brand | #FF444F | #FF444F | Brand-tinted icon |
| text-icon-utility-real | #00C390 | #00C390 | Real-account icon |
| text-icon-utility-demo | #F55F0A | #F55F0A | Demo-account icon |
Icon component sizes:
| Size | Width × height | Flag width × height |
|---|---|---|
| sm | 16 × 16 | 24 × 16 |
| md | 20 × 20 | 30 × 20 |
| lg | 24 × 24 | 36 × 24 |
Elevation tokens
Five composed box-shadow shorthands. Defined once in :root — .dark swaps only the layer colours, so a single --elevation-* reference works in both themes.
| Token | Layer 1 (y / blur) | Layer 2 | Layer 3 | Used for |
|---|---|---|---|---|
| --elevation-100 | 1 / 2 | — | — | Base raise (chips, segmented control segments) |
| --elevation-200 | 1 / 2 | 2 / 6 | — | Inputs, low cards |
| --elevation-300 | 1 / 2 | 4 / 12 | 8 / 24 | Menus, tooltips, popovers |
| --elevation-400 | 2 / 4 | 8 / 24 | 16 / 48 | Notifications, snackbars, banner |
| --elevation-500 | 4 / 8 | 16 / 48 | 32 / 96 | Bottom sheets, dialogs |
Layer colours: black @ 4% / 8% (light) → black @ 8% / 16% (dark).
Spacing scale
| Token | Value (px) | Token | Value (px) |
|---|---|---|---|
| spacing-0 | 0 | spacing-16 | 16 |
| spacing-1 | 1 | spacing-20 | 20 |
| spacing-1-5 | 1.5 | spacing-24 | 24 |
| spacing-2 | 2 | spacing-32 | 32 |
| spacing-4 | 4 | spacing-40 | 40 |
| spacing-6 | 6 | spacing-48 | 48 |
| spacing-7 | 7 | spacing-56 | 56 |
| spacing-8 | 8 | spacing-64 | 64 |
| spacing-12 | 12 | spacing-72 | 72 |
| spacing-14 | 14 | spacing-80 | 80 |
Radius scale
| Token | Value | Used for |
|---|---|---|
| radius-0 | 0px | Sharp edges |
| radius-4 | 4px | Tags, tabs |
| radius-8 | 8px | Inputs, fields, cards (default) |
| radius-12 | 12px | Mid-tier surfaces |
| radius-16 | 16px | Section messages, notifications, bottom sheets |
| radius-24 | 24px | Bottom sheet handles |
| radius-48 | 48px | Large feature blocks |
| radius-999 | 999px | Pills — buttons, badges, chips, segmented control |
Border-width scale
border-0 (0) · border-1 (1) · border-1-5 (1.5) · border-2 (2) · border-4 (4) · border-8 (8)
1.5px is the standard for selected / focused outlines (chips, list-item, segmented control segments, tabs).
Transition tokens
Duration — primitive scale:
| Tailwind class | CSS variable | Value | Used for |
|---|---|---|---|
| duration-instant | --primitive-duration-instant | 50ms | Focus rings, hover tints |
| duration-fast | --primitive-duration-fast | 100ms | Buttons, inputs, badges |
| duration-base | --primitive-duration-base | 200ms | Dropdowns, popovers, accordions |
| duration-slow | --primitive-duration-slow | 300ms | Dialogs, sheets, drawers closing |
| duration-open | --primitive-duration-open | 500ms | Sheets, drawers entering |
| duration-blink | --primitive-duration-blink | 1000ms | OTP caret blink |
Easing — primitive scale:
| Tailwind class | CSS variable | Value | Used for |
|---|---|---|---|
| ease-standard | --primitive-ease-standard | cubic-bezier(0.2, 0, 0, 1) | General UI — bidirectional state changes |
| ease-enter | --primitive-ease-enter | cubic-bezier(0, 0, 0.2, 1) | Overlays / surfaces entering |
| ease-exit | --primitive-ease-exit | cubic-bezier(0.4, 0, 1, 1) | Overlays / surfaces leaving |
| ease-linear | --primitive-ease-linear | linear | Sidebar width, progress bar |
Always use token utilities — never raw
duration-200orease-in-outdirectly.
Using opacity with tokens
Opacity on tokens is allowed and encouraged:
✅ bg-primary/20 → coral at 20% opacity
✅ border-subtle-default/50 → subtle border at 50% opacity
✅ ring-ring/10 → focus ring at 10% opacity
❌ bg-black/50 → NOT a token, use bg-overlay instead
❌ bg-white → NOT a token, use bg-primary-surface or bg-secondary-canvasPrimitive colour ramps
The system ships 17 colour ramps. Each ramp has solid stops 100 → 1300 (slate adds 50, 75, 1400) and an opacity scale 50 → 1100. Reference these only via semantic or component tokens — never in product code.
| Ramp | 700 (mid / brand stop) | Notes |
|---|---|---|
| slate | #414652 | Neutral / surface scale (50 → 1400) |
| red | #E6190E | Error / danger |
| orange | #F55F0A | Demo-account utility |
| yellow | #FF9C13 | Warning |
| mustard | #F7C60B | Decorative warm |
| green | #008832 | Success |
| emerald | #00C390 | Real-account utility |
| tiffany | #17EABD | Decorative |
| teal | #00CCCC | Decorative |
| seawater | #0AA0B0 | Decorative |
| blue | #2C9AFF | Information |
| sapphire | #0C28F7 | Decorative |
| blueberry | #4902E0 | Decorative |
| grape | #7F0DCF | Decorative |
| magenta | #CB0DF7 | Decorative |
| cherry | #DE0040 | Decorative |
| coral | #FF444F | Brand primary |
Opacity scale stops (per ramp): 50 · 75 · 100 · 200 · 300 · 400 · 500 · 600 · 700 · 800 · 900 · 1000 · 1100
→ alpha values: 0% · 4% · 8% · 16% · 24% · 32% · 40% · 48% · 56% · 64% · 72% · 80% · 88%
White and black opacity scales follow the same stops.
Typography
The styles export includes pre-built typography classes using Inter (loaded from Google Fonts, weights 300–800).
Heading scale (Inter · ExtraBold 800 · letter-spacing −0.02em)
| Class | Font size | Line height |
|---|---|---|
| heading-mega | 72px | 72px |
| heading-hero | 64px | 64px |
| heading-h1 | 56px | 56px |
| heading-h2 | 40px | 40px |
| heading-h3 | 32px | 32px |
| heading-h4 | 24px | 24px |
| heading-h5 | 20px | 20px |
| heading-h6 | 16px | 16px |
| heading-xs | 24px | 24px | (@deprecated — use heading-h4)
Body scale (Inter · Regular 400)
| Class | Font size | Line height |
|---|---|---|
| body-xl | 20px | 30px |
| body-lg | 18px | 27px |
| body-md | 16px | 24px |
| body-sm | 14px | 21px |
| body-xs | 12px | 18px |
Body text bold weight is 700. Body regular is 400. Apply via standard
font-bold/font-medium/font-semiboldTailwind utilities (medium = 500, semibold = 600).
Font utilities
| Tailwind class | Font family |
|---|---|
| font-display | Inter — headings, display text |
| font-body or font-sans | Inter — body text |
| font-mono | Red Hat Mono — code, tabular figures |
Upgrading the design system
When you bump @deriv-ds/design-intelligence-layer and run npm install (or npm ci):
| How you use the package | What happens |
|-------------------------|----------------|
| You import components only from @deriv-ds/design-intelligence-layer | After install and a rebuild, your app uses the new implementations in node_modules/.../dist — buttons, cards, etc. reflect the version you installed. |
| You copied components/ui/* (or similar) into your repo | Those files do not auto-update. You must delete them and switch to package imports, or manually merge changes from the new package. |
Overrides: Passing large className strings onto Quill components can mask new defaults (e.g. old radius after a "pill button" update). After upgrading, review those callsites.
Cursor / AI agents: The package ships agent rules at node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc. Cursor does not load rules from node_modules by default — re-copy that file into .cursor/rules/ after each upgrade so instructions match the release (same command as in AI Agent Setup below). The rules include Rule 7 — Package version upgrades: agents should search for duplicated components, align with the package, and tell you explicitly if local component code was replaced.
AI Agent Setup
All AI tools (Cursor, Windsurf, Claude Code, Copilot, and others)
The package ships an AGENTS.md file that most AI tools read automatically. After installing the package, copy it to your project root:
cp node_modules/@deriv-ds/design-intelligence-layer/AGENTS.md ./AGENTS.mdThis tells any AI agent to:
- Read the design principles before building any screen
- Follow the component and token rules
- Run the 7-point checklist before completing any view
Design principles file (bundled in the package):
node_modules/@deriv-ds/design-intelligence-layer/guides/design-principles/quill-design-principles.mdAccessibility standards file (bundled in the package):
node_modules/@deriv-ds/design-intelligence-layer/guides/accessibility-standards/quill-accessibility-standards.mdPersonas file (bundled in the package):
node_modules/@deriv-ds/design-intelligence-layer/guides/personas/deriv-user-field-guide.mdBrand voice (bundled in the package):
node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/quill-brand-voice.mdAll four files apply to all projects built with this package — landing pages, product screens, and games. Every AI agent must read all of them before starting any build. Run the design principles and accessibility checklists before completing any screen. Use the personas and brand voice to guide all player-facing copy.
Cursor
Copy the included rule file into your project:
mkdir -p .cursor/rules
cp node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc .cursor/rules/Re-run this cp after every design-system version bump so your workspace rules stay in sync with the installed package.
Claude Code
Add the following to your project's CLAUDE.md:
## Design System
This project uses @deriv-ds/design-intelligence-layer (Quill Design System). Before writing any UI:
1. Read node_modules/@deriv-ds/design-intelligence-layer/guides/design-principles/quill-design-principles.md — apply the 8 principles and run the 7-point checklist on every screen
2. Read node_modules/@deriv-ds/design-intelligence-layer/guides/accessibility-standards/quill-accessibility-standards.md — apply WCAG 2.1 AA standards and run the 9-point accessibility checklist on every screen
3. Read node_modules/@deriv-ds/design-intelligence-layer/guides/personas/deriv-user-field-guide.md — understand the player personas that shape all copy and UX
4. Read node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/quill-brand-voice.md — apply the brand voice: channel-specific voice, banned phrases, vocabulary, and formatting rules for all player-facing copy
5. Check if the component exists in the package — import it, don't re-implement
6. Use only design token classes (bg-prominent, text-on-prominent, border-border-subtle, etc.) — no hardcoded hex or raw Tailwind palette colors
7. Do not install lucide-react — icons use FontAwesome Pro via the bundled Icon component; do not install tailwindcss or other bundled dependencies separately
8. If no token exists for a value, ask before using a hardcoded value
9. After upgrading the package: prefer package imports over local copies of components; if replacing local UI code with the package version, tell the user what was overwritten; re-copy guides/rules/design-system-consuming-project.mdc into .cursor/rules if using Cursor
See node_modules/@deriv-ds/design-intelligence-layer/guides/rules/design-system-consuming-project.mdc for full rules.
See node_modules/@deriv-ds/design-intelligence-layer/README.md for complete token and component reference.Common mistakes
| Wrong | Right | Why |
|---|---|---|
| bg-gray-100 | bg-secondary-surface | Raw Tailwind palette — use tokens |
| bg-white | bg-primary-surface | Not a semantic token; breaks dark mode |
| bg-[#181C25] | bg-primary-canvas (dark) | Hardcoded hex — use the canvas token |
| text-white | text-prominent-static-dark | Use static-dark for "always white" cases |
| text-black | text-prominent | Not a semantic token; breaks dark mode |
| bg-black/50 | bg-overlay | Overlay has its own token |
| bg-[#2323FF] or bg-blue-600 | bg-primary | Brand is coral #FF444F, not blue |
| bg-[#FF444F] | bg-primary | Hardcoded hex — use token |
| border-border | border-subtle-default | Old name — prefer the new explicit name |
| bg-[var(--primary)] | bg-primary | Raw CSS var — Tailwind v4 maps tokens directly |
| hsl(var(--primary)) | bg-primary | Tailwind v3 syntax — not needed in v4 |
| font-[Plus_Jakarta_Sans] | font-sans (Inter) | Project font is Inter, not Plus Jakarta Sans |
| import ... from 'lucide-react' | import { Icon } from "@deriv-ds/design-intelligence-layer" | Icons use FontAwesome Pro — lucide-react is not in this package |
Do NOT install separately
These are bundled with the package. Installing them separately can cause version conflicts:
lucide-react— not used; icons are FontAwesome Pro via the bundledIconcomponentradix-ui— headless primitivesclass-variance-authority— variant APIcmdk— command palettevaul— drawersonner— toastrecharts— chartsreact-day-picker— calendarembla-carousel-react— carouselreact-resizable-panels— resizable panels
Development (contributors only)
# Clone
git clone https://github.com/deriv-com/deriv-intelligence-layer.git
cd deriv-intelligence-layer
# Install dependencies
npm install
# Start dev server (Next.js component playground)
npm run dev
# Build the library (ESM + CJS + types)
npm run build
# Build the Next.js showcase app
npm run build:next
# Regenerate block sources for the "Copy block" button
# (auto-runs on `npm run dev` — only needed manually after editing a hero block file)
npm run generate:blocksAdding or editing hero blocks
Block files live in app/components/hero-*.tsx. Each file has two clearly marked sections:
🟢 BLOCK TO COPY— the block source consumers receive when they click "Copy block"🔵 PLAYGROUND ONLY— the wrapper with switchers, skeletons, and intersection observers (excluded from the prompt)
The scripts/generate-block-sources.mjs script reads each block file, extracts the section between the two markers, builds an AI-ready prompt, and writes app/blocks-source.generated.ts. The CopyBlockPromptButton component reads from that generated file. The script auto-runs on every npm run dev.
When adding a new hero block:
- Create
app/components/hero-<name>-page.tsxwith both 🟢 and 🔵 markers - Add an entry to the
BLOCKSarray inscripts/generate-block-sources.mjs - Add
<CopyBlockPromptButton blockId="..." />to the block's playground header row
Updating design tokens
Design tokens are managed in Figma and exported as CSS variables. To update:
- Update the CSS custom properties in
app/globals.css(playground) andsrc/styles.css(published package) - Update
guides/rules/design-system-consuming-project.mdcto reflect any token renames - Update this README's token tables
Tech stack
- React 19 + TypeScript
- Tailwind CSS v4 — CSS-first configuration
- shadcn/ui (New York style) — base component primitives
- Radix UI — accessible headless primitives
- Figma — source of truth for design tokens
