Leo is Brave's cross-platform design system. It powers the Brave Browser (desktop and mobile), the new tab and search experiences, brave.com marketing, and the broader privacy-product portfolio.
Components are authored in Svelte and shipped to consumers as React, Svelte and Web Component wrappers. Tokens are authored once and exported to CSS, Tailwind, Skia (C++), Java/XML (Android), and Swift (iOS).
See README.md for consumer setup — wiring up variables.css,
the Tailwind plugin, and the data-theme="dark|light" theming rule.
Generated output, do not hand-edit.
tokens/,react/,web-components/,shared/,build/,types/,icons-skia/,icons-sf/, andstorybook-static/are all produced bynpm run buildand listed in .gitignore. Edit the sources under src/ (and icons/ for raw SVGs) and rebuild.
nala is the codename used for Leo on the native side of the stack. The
generated Skia/C++ token files live in the nala:: namespace
(see tokens/skia/ and the templates under
src/tokens/transformation/skia/templates/)
and per-PR Storybook previews are deployed to
https://<pr-number>.pr-nala.s.brave.dev/ by
.github/workflows/deploy.yml. For
everything in this repo, "Leo" and "nala" refer to the same design system.
Tokens are authored in src/tokens/ (*.tokens.json /
*.variables.json) and compiled to per-platform output by
npm run transform-tokens (also part of npm run build). The generated
files are the source of truth for what each platform actually consumes.
The full list of CSS variables — semantic and primitive, light and dark — lives in tokens/css/variables.css (the universal/web base). Per-product files extend or override the universal set:
- tokens/css/variables-browser.css — desktop browser chrome.
- tokens/css/variables-ios.css — iOS web surfaces.
- tokens/css/variables-android.css — Android web surfaces.
- tokens/css/variables-marketing.css — brave.com.
- tokens/css/variables-search.css — search.brave.com.
- tokens/css/variables-news.css — Brave News.
- tokens/css/variables-newtab.css — new tab page.
- tokens/css/variables-web3.css — wallet / web3.
A Tailwind plugin and JS / TS exports are emitted alongside each CSS file
in tokens/tailwind/ and as variables*.js /
variables*.ts in tokens/css/.
- Skia / C++ — tokens/skia/ (the
nala::color id enum, color mixer, andradius.h/spacing.hheaders). - Android — tokens/android/ (XML resources under
values/andvalues-night/, plus icon drawables). - iOS / Swift — tokens/ios-swift/
(
Colors.xcassets,ColorSetAccessors.swift,Gradients.swift). - JSON — tokens/json/ (Style Dictionary output for downstream tooling).
The Storybook deployed at the nala preview URL above also renders every token group as live swatches, which is the easiest way to browse them visually.
All component source lives in src/components/, one
folder per atom (button, input, alert, dialog, dropdown, menu, tooltip,
checkbox, radioButton, toggle, segmentedControl, tabs, progress, icon,
etc.). Each is authored in Svelte with stories alongside in
<name>.stories.svelte.
At build time (npm run build), every component is emitted in two
consumer formats:
- React —
react/<name>.js, imported as@brave/leo/react/<name>. - Web Component —
web-components/<name>.js, imported as@brave/leo/web-components/<name>and used via<leo-<name>>tags.
See src/components/README.md for the component authoring recipe and the React / Web Component caveats.
Icons live in icons/ (~2200 SVGs synced from Figma by
.github/workflows/update-icons.yml)
and are consumed via the typed
<Icon name="..." /> component, with
autocomplete from @brave/leo/icons/meta.
Every component and token group has a <name>.stories.svelte file —
Storybook is the live visual reference for both. Run it locally via
npm run storybook (:6006), or browse the per-PR preview at the nala
URL above.
npm install # also runs the build via the `prepare` script
npm run build # transform-tokens → skiafy-icons → android-icons → rollup
npm run dev # rollup --watch for iterating on a component
npm run storybook # Storybook on :6006
npm run test # jest