npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@deriv-ds/design-intelligence-layer

v0.7.1

Published

Deriv Design System — shadcn/ui components with Tailwind CSS v4

Downloads

143

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-layer

CI / 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-age to 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 skips node_modules by default. Without this line, Tailwind won't generate CSS for the component classes. Adjust the path if your CSS file is not one level below node_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. font-mono is not overridden by this package and falls back to Tailwind's default monospace stack.

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 tailwindcss

Requires React 18+ and Tailwind CSS v4+.

Icons

Icons are provided by @deriv/quill-icons via the bundled Icon component — do not install lucide-react or @fortawesome/*. Three weights cover the density range: regular (thin outline), bold (medium outline — default), and fill (solid). Use the bundled Icon component:

import { Icon } from "@deriv-ds/design-intelligence-layer"

<Icon name="bell" />
<Icon name="circle-check" weight="fill" className="text-[var(--text-success-default)]" />

The full icon catalog is at node_modules/@deriv-ds/design-intelligence-layer/guides/design-system-guide/icon-reference.md.


What's inside

  • 60+ 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 .dark on <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 hardcoded duration-200
  • Elevation scale — five composed box-shadow shorthands (--elevation-100 through --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, AvatarGroupCount | | Badge | Badge, BadgeDot | | Breadcrumb | Breadcrumb, BreadcrumbList, BreadcrumbItem, ... | | Button | Button, buttonVariants, SocialButton | | 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 | | Download Badge | DownloadBadge (+ DOWNLOAD_BADGE_PLATFORMS) | | Drawer | Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, DrawerClose | | Dropdown Menu | DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, ... | | Email or Phone Input | EmailOrPhoneInput | | 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 | | Icon | Icon (wraps @deriv/quill-icons; weight="regular\|bold\|fill") | | Input | Input | | Input Group | InputGroup, InputGroupAddon, InputGroupButton, InputGroupText | | List Item | ListItem, ListItemMedia, ListItemContent, ListItemTitle, ListItemDescription, ListItemIcon, ListItemActions | | Kbd | Kbd, KbdGroup | | Label | Label | | Link | Link | | Loading Spinner | LoadingSpinner | | Native Select | NativeSelect, NativeSelectOptGroup, NativeSelectOption | | Navigation Menu | NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, ... | | Notification | NotificationBanner, NotificationItem, NotificationDivider | | OTP Field | CodeInput, CodeInputGroup, CodeInputSlot, CodeInputSeparator | | Pagination | Pagination, PaginationContent, PaginationLink, ... | | Phone Input | PhoneInput (also exports COUNTRIES, DEFAULT_COUNTRY, Country) | | 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 (+ imperative toast — re-exported from sonner) | | Spinner | Spinner | | Stepper | Stepper | | Switch | Switch | | Tab | Tab, TabList, TabTrigger, TabContent | | Table | Table, TableHeader, TableBody, TableRow, TableHead, TableCell | | Tag | Tag, tagVariants | | Textarea | Textarea | | Toggle | Toggle, toggleVariants | | Toggle Group | ToggleGroup, ToggleGroupItem | | Tooltip | Tooltip, TooltipTrigger, TooltipContent, TooltipProvider |


Blocks

Blocks are pre-composed UI patterns (navbars, heroes, cards, …) built from package primitives. They ship as named exports — import them directly, just like any component:

import { Navbar, HeroDesktopMain, Section, AccountActivatedCard, AddAccountCard } from "@deriv-ds/design-intelligence-layer";

Wire each block's props/children to your real content — don't re-implement them. The build skill's Block Matching gate does this automatically (it imports the matching block instead of mocking up a look-alike).

Available blocks

| Block | Variants / exports | Description | |---|---|---| | NavBar | Navbar (responsive) · NavMobileBottomBar · NavDesktopSidebar — props activeId, badges, onNavChange | App navigation. Navbar auto-switches: sidebar ≥768px, bottom tab bar <768px. | | Header | HeaderApp · HeaderAppHome · HeaderBranding | App top header. Home (avatar + notifications + Ask Amy), in-app sub-page (back, tabs, actions), or branding bar (language + actions). | | Hero — home | HeroMobileHomeTitle · HeroMobileHomeTotalAssets · HeroDesktopHomeOnboarding · HeroDesktopHomeWithBalance (+ *Skeleton) | Home top area: greeting/title, total-assets balance, or onboarding hero. Mobile/desktop pair. | | Hero — main / secondary / transaction | HeroMobileMain · HeroDesktopMain · HeroMobileSecondary · HeroDesktopSecondary · HeroMobileTransaction (+ *Skeleton) | Primary/secondary page hero with account tabs + action buttons; transaction details card. | | Hero action button | HeroActionButton | The prominent circular action button used inside heroes. | | Home — banner | HomeBanner · BannerCard — props banner, layout, onClose | Promo / announcement banner (carousel of cards, or a single dismissible card). | | Home — highlights | HomeHighlights · HomeHighlightCard — prop cards | Row/grid of highlight cards. | | Home — explore | HomeExploreDeriv | "Explore Deriv" discovery section. | | Trading — action buttons | TradingActionButtonsPrimary · TradingActionButtonsSecondary (+ *Skeleton) | Deposit / withdraw / transfer action button cluster. | | Trading — empty | TradingEmpty (+ TradingEmptySkeleton) | Empty trading list / no-accounts state. | | Account activated | AccountActivatedCard | "Account activated" confirmation card. | | Add account card | AddAccountCard — prop state | Dashed placeholder card prompting the user to add another trading account. | | Feedback / success | FeedbackSuccessScreen · FeedbackTradeWithAccounts · FeedbackTransferSuggestion | Post-action success / confirmation screens. | | Referral | Referral — props metrics, … | Refer-a-friend / invite panel with metrics. | | Section | Section | Section header pattern: eyebrow tag, title (+ chevron), control cluster, segmented control, content slot (children). |

Block source files live in components/blocks/. Most heroes and trading blocks also export a *Skeleton loading variant. See .claude/skills/build/references/components-and-tokens.md for the full catalog with "matches when" hints.


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 16px

All 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 dot

Badges 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 · 18px icon · 4px gap
<Tag size="md" />   // 32px · 14px font · 20px icon · 4px gap

Section 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 · 24px icon
<SectionMessage size="md" />  // 16px message · 18px title · 24px icon

Alert & Banner — use SectionMessage / NotificationBanner

There is no Alert or Banner component in this package. The same patterns are covered by existing primitives — reach for these instead:

| You may be looking for | Use this | Import | |---|---|---| | Alert (inline status message) | SectionMessage | SectionMessage, SectionMessageIcon, SectionMessageTitle, SectionMessageDescription | | Banner (dismissible notice bar) | NotificationBanner | NotificationBanner, NotificationItem, NotificationDivider |

// Alert → SectionMessage (role="alert"; variants: default | information | success | warning | danger)
<SectionMessage variant="information">
  <SectionMessageIcon><Icon name="circle-info" weight="fill" /></SectionMessageIcon>
  <SectionMessageTitle>Heads up</SectionMessageTitle>
  <SectionMessageDescription>Your session expires soon.</SectionMessageDescription>
</SectionMessage>

// Banner → NotificationBanner (dismissible via onClose)
<NotificationBanner
  title="New feature available"
  description="Check out the latest update."
  onClose={() => setOpen(false)}
/>

Component behaviour notes

Dialog — footer buttons

DialogFooter makes its direct child buttons fill the full width automatically. Stack your buttons inside DialogFooter and they will always be full-width.

Drawer — 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-xs

Tab — hover, sizes & sliding indicator

Tab is a line/underline tab set (Tab, TabList, TabTrigger, TabContent — note the singular names; Tabs/TabsList do not exist). Hover shows coral text (text-primary, #FF444F) on a transparent background.

The selected indicator is a single shared element rendered by TabList (not a per-trigger after pseudo-element). It slides and resizes between the active triggers using duration-base / ease-standard, tracking data-state="active" via a MutationObserver, and repositions on container/font resize (ResizeObserver + document.fonts.ready). It animates for orientation both horizontal (bottom underline) and vertical (right-edge bar), and stays static on the very first placement so it does not animate in from the origin on mount. The indicator thickness follows the size token (--tab-{sm|md|lg}-border-width). No consumer API changes — this is a behaviour-only upgrade.

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:

  1. Primitives (--primitive-*) — raw colour ramps, alpha scales, durations, easings. Never reference directly.
  2. Foundation semantics (--background-*, --text-*, --border-*, --icon-*, --elevation-*) — paired between :root (light) and .dark (dark).
  3. 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 | #11141B | Page / app canvas | | bg-secondary-canvas | --background-secondary-canvas | #FFFFFF | #181C25 | Alternate canvas | | bg-primary-surface | --background-primary-surface | #FFFFFF | #181C25 | 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 | #181C25 | 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, and static-dark flavours (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-dark variants exist for every status token and follow the same theme-pairing rules as text-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 | #1789E1 | #1789E1 | Focus rings (blue-800 — chosen for WCAG 1.4.11 3:1 contrast on both themes; see issue #38) |

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-border is deprecated. It now aliases --border-subtle-default for backward compatibility. Prefer border-subtle-default in 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

Semantic spacing tokens (--semantic-size-spacing-*) for padding, gap, and margin. Each aliases the matching --primitive-size-*. The scale mirrors the full primitive range so any standard step is available.

| Token | Value (px) | Token | Value (px) | |---|---|---|---| | --semantic-size-spacing-4 | 4 | --semantic-size-spacing-32 | 32 | | --semantic-size-spacing-6 | 6 | --semantic-size-spacing-40 | 40 | | --semantic-size-spacing-8 | 8 | --semantic-size-spacing-48 | 48 | | --semantic-size-spacing-12 | 12 | --semantic-size-spacing-56 | 56 | | --semantic-size-spacing-14 | 14 | --semantic-size-spacing-64 | 64 | | --semantic-size-spacing-16 | 16 | --semantic-size-spacing-72 | 72 | | --semantic-size-spacing-18 | 18 | --semantic-size-spacing-80 | 80 | | --semantic-size-spacing-20 | 20 | --semantic-size-spacing-88 | 88 | | --semantic-size-spacing-24 | 24 | --semantic-size-spacing-96 | 96 |

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-200 or ease-in-out directly.

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-canvas

Primitive 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 — Client app

The styles export includes pre-built typography classes using Inter (loaded from Google Fonts, weights 300–800). This is the type scale for Deriv trading/account product UI — for deriv.com marketing pages see Typography — Marketing website below.

Heading scale (Inter · ExtraBold 800 · letter-spacing −0.02em)

| Class | Font size | Line height | |---|---|---| | 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-semibold Tailwind 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 | Tailwind's default monospace stack — code, tabular figures (not overridden by this package) |


Typography — Marketing website

Separate, additive typography + spacing namespace for deriv.com marketing/brand pages, ported from @deriv-web-design/tokens (github.com/deriv-com/website-design-design-system-global). Do not use these on product/app UI — keep using Typography — Client app there. Never mix both families on one screen.

One responsive set per role, using discrete breakpoint modes: each marketing-* class uses its Base (desktop, ≥1280px) value and steps down at Tablet (≤1279px) and Mobile (≤767px). Only h1–h6 headings resize; body/meta are fixed at every viewport.

Heading scale (Inter · ExtraBold 800 · discrete Base/Tablet/Mobile)

| Class | Font size (mobile · tablet · desktop) | Line height (mobile · tablet · desktop) | Letter spacing | |---|---|---|---| | marketing-h1 | 48 · 64 · 80px | 56 · 72 · 88px | -0.035rem | | marketing-h2 | 40 · 48 · 56px | 48 · 56 · 64px | -0.03rem | | marketing-h3 | 32 · 40 · 48px | 40 · 48 · 56px | -0.025rem | | marketing-h4 | 24 · 32 · 40px | 32 · 40 · 48px | normal | | marketing-h5 | 20 · 24 · 28px | 28 · 32 · 36px | normal | | marketing-h6 | 16 · 20 · 24px | 24 · 28 · 32px | normal |

Body & meta scale (same value at every viewport)

| Class | Font size | Line height | Weight | |---|---|---|---| | marketing-body-lg | 18px | 28px | 400 | | marketing-body-md | 16px | 24px | 400 | | marketing-body-sm | 14px | 24px | 400 | | marketing-body-xs | 12px | 20px | 400 | | marketing-label-lg | 18px | 28px | 400 | | marketing-label-md | 16px | 24px | 400 | | marketing-label-sm | 12px | 20px | 400 | | marketing-caption | 12px | 20px | 400 | | marketing-link-md | 16px | 24px | 600 |

<h1 className="marketing-h1 text-prominent">Trade smarter</h1>
<p  className="marketing-body-lg text-subtle">Fluid heading that scales with the viewport.</p>

Tailwind utilities

Every role above is also bridged as a Tailwind text-* utility (composed with its paired line-height/weight/letter-spacing): text-marketing-h1 … text-marketing-h6, text-marketing-body-lg … text-marketing-link-md.

<section className="text-marketing-h2 p-10 gap-6 flex flex-col">

Spacing scale (responsive · Base/Tablet/Mobile)

Marketing surfaces have their own responsive spacing namespace, --marketing-spacing-*, consumed via Tailwind utilities p-marketing-32, gap-marketing-32, m-marketing-32, etc. Tokens are named by their Base (desktop) px value. Steps 24 and below are fixed; 32+ step down at Tablet (≤1279px) and Mobile (≤767px), which share the same value.

| Utility suffix | Base (≥1280px) | Tablet (768–1279px) | Mobile (<768px) | |---|---|---|---| | marketing-0 | 0 | 0 | 0 | | marketing-2 | 2px | 2px | 2px | | marketing-4 | 4px | 4px | 4px | | marketing-8 | 8px | 8px | 8px | | marketing-12 | 12px | 12px | 12px | | marketing-16 | 16px | 16px | 16px | | marketing-24 | 24px | 24px | 24px | | marketing-32 | 32px | 24px | 24px | | marketing-40 | 40px | 32px | 32px | | marketing-48 | 48px | 32px | 32px | | marketing-56 | 56px | 32px | 32px | | marketing-64 | 64px | 48px | 48px | | marketing-72 | 72px | 48px | 48px | | marketing-80 | 80px | 48px | 48px | | marketing-88 | 88px | 56px | 56px | | marketing-96 | 96px | 56px | 56px | | marketing-112 | 112px | 56px | 56px |

Standard Tailwind spacing (p-10, gap-6) still works for one-off layout on marketing pages.


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 8 — 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.md

This tells any AI agent to:

  1. Read the design intelligence guides (principles, accessibility, brand voice, personas) before building any screen
  2. Read the design system guide, then follow the component and token rules
  3. Run the guide checklists before completing any view

Design principles (bundled in the package):

node_modules/@deriv-ds/design-intelligence-layer/guides/design-principles/deriv-design-principles.md

Accessibility & responsive standards (WCAG 2.2 AA — bundled in the package):

node_modules/@deriv-ds/design-intelligence-layer/guides/accessibility-standards/deriv-accessibility-standards.md

Brand voice (bundled in the package):

node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md

Personas (trader modes and money-in-motion copy rules — bundled in the package):

node_modules/@deriv-ds/design-intelligence-layer/guides/personas/deriv-personas.md

Design system guide (tokens, components, blocks — bundled in the package):

node_modules/@deriv-ds/design-intelligence-layer/guides/design-system-guide/deriv-ds-guide.md

All five guides apply to all projects built with this package — landing pages, product screens, and apps. Every AI agent must read all of them before starting any build. Run the design principles, accessibility, and personas checklists before completing any screen. Use the brand voice to guide all user-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/deriv-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/deriv-accessibility-standards.md — apply WCAG 2.2 AA standards and run its severity-ranked accessibility checklist on every screen (Critical items block release)
3. Read node_modules/@deriv-ds/design-intelligence-layer/guides/brand-voice/deriv-brand-voice.md — apply the brand voice: channel-specific voice, banned phrases, vocabulary, and formatting rules for all user-facing copy
4. Check if the component exists in the package — import it, don't re-implement
6. Use only design token classes (bg-primary-canvas, text-prominent, border-default, etc.) — no hardcoded hex or raw Tailwind palette colors
7. Do not install lucide-react — icons come from @deriv/quill-icons 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.

Claude Code skill (build)

The package ships a Claude Code skill that drives every UI build strictly from the design system — bootstrap, build-from-prompt, build-from-Figma, and integrate-a-pasted-block, all with the token/component/icon rules enforced. It also runs a Block Matching gate: before mocking up any region (nav, hero, cards, …) it matches the region against the package's shipped blocks and imports the real one instead of re-creating a look-alike. Copy it into your project's skills directory:

mkdir -p .claude/skills
cp -R node_modules/@deriv-ds/design-intelligence-layer/.claude/skills/build .claude/skills/

Re-copy after each version bump so the skill matches the installed release. Once copied, Claude Code triggers it automatically when you ask to build Deriv UI (or invoke /build).

Cursor / Kiro: copy the same skill folder as above, plus the shipped shim so /build (Cursor) or #build (Kiro) invokes it:

# Cursor
mkdir -p .cursor/commands
cp node_modules/@deriv-ds/design-intelligence-layer/.cursor/commands/build.md .cursor/commands/build.md

# Kiro
mkdir -p .kiro/steering
cp node_modules/@deriv-ds/design-intelligence-layer/.kiro/steering/build.md .kiro/steering/build.md

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 come from @deriv/quill-icons — 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 come from @deriv/quill-icons via the bundled Icon component
  • radix-ui — headless primitives
  • class-variance-authority — variant API
  • cmdk — command palette
  • vaul — drawer
  • sonner — toast
  • recharts — charts
  • react-day-picker — calendar
  • embla-carousel-react — carousel
  • react-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

# Check the build-skill catalog matches the package's real exports
npm run check:skill-catalog

Authoring skills (block, component)

This repo ships two Claude Code authoring skills that scaffold a new export end-to-end so it lands consistently across the component file, the playground demo, the package export, the nav wiring, and the docs/catalog. They live only in this source repo (.claude/skills/block/, .claude/skills/component/) and are not published to npm — they edit components/, src/index.ts, and the playground, which only exist here. Anyone with this repo checked out gets them automatically. Package consumers building product UI use the build skill instead (npm-published, described above).

| Skill | Invoke | What it does | |---|---|---| | block | /block or "add a block" | Scaffolds a new block: components/blocks/<name>.tsx, playground page, src/index.ts export, nav wiring, and the build-skill block catalog (.claude/skills/build/references/components-and-tokens.md) so the consumer build skill's Block Matching gate can discover and import it. | | component | /component or "add a component" | Scaffolds a new UI primitive: components/ui/<name>.tsx, export, playground, nav wiring, and consumer docs (this README's component table, the design guide, and the build-skill cheatsheet). |

Other agents: the /-invocation above is Claude Code's. Cursor users invoke the same skills as /block and /component via this repo's .cursor/commands/ shims; Kiro users pull them in with #block / #component via .kiro/steering/. Each shim just points the agent at the canonical .claude/skills/<name>/SKILL.md, so all agents run the same rules. (These shims live in this repo only — not published to npm, same as the skills themselves.)

npm run check:skill-catalog guards the build-skill catalog these skills update — it fails if a component or block is exported but undocumented (or documented but not exported), so the build skill's catalog never drifts from the package's real surface.

Adding or editing blocks

Block source files live in components/blocks/<name>.tsx, with a matching playground page in app/components/<name>-page.tsx. To add a new block, use the block authoring skill (above) — it scaffolds the block file, playground page, src/index.ts export, nav wiring, and the build-skill block catalog in one pass. Run npm run check:skill-catalog afterwards to confirm the catalog stays in sync.

Updating design tokens

Design tokens are managed in Figma and exported as CSS variables. To update:

  1. Update the CSS custom properties in app/globals.css (playground) and src/styles.css (published package)
  2. Update guides/rules/design-system-consuming-project.mdc to reflect any token renames
  3. 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

Changelog

See CHANGELOG.md for the full version history. The file follows the Keep a Changelog format and ships with the package.