Icons
All icons live in packages/web-ui/icons/ and are exported from two paths:
| Import path | Contents |
|---|---|
@dbt-labs/web-ui/icons | All icons including social re-exports |
@dbt-labs/web-ui/icons/social | Social platform icons only |
Quick start
// Most icons
import { Arrow, Chevron, Close, CheckCircle } from '@dbt-labs/web-ui/icons'
// Social icons (available from both paths)
import { GitHub, LinkedIn, X } from '@dbt-labs/web-ui/icons/social'
import { GitHub, LinkedIn, X } from '@dbt-labs/web-ui/icons'// Render at default size (24×24px)
<Close />
// Resize with Tailwind size utilities
<Close className='size-6' />
// Recolor with Tailwind text utilities
<Alert className='size-5 text-red-600' />
// Directional icon — pass a prop instead of importing a separate file
<Chevron direction='right' />
<Arrow direction='left' className='size-4' />How icons work
currentColor
Every icon uses currentColor for its stroke or fill. That means the icon inherits whatever text color is active on the element — control it with Tailwind text utilities:
// Inherits the surrounding text color automatically
<p className='text-neutral-secondary'>
<Clock className='inline-block size-4' /> 5 min read
</p>
// Override the color explicitly
<Alert className='text-error size-5' />The one exception is Error, which uses hardcoded colors for its illustration — currentColor has no effect on it.
Sizing
Icons default to 24×24px (or 16×16px for Chevron weight='bold'). Use size-* to resize:
<Search className='size-4' /> // 16px
<Search className='size-5' /> // 20px
<Search className='size-6' /> // 24px — matches the SVG's natural size
<Search className='size-10' /> // 40pxNaming convention
The import path acts as the namespace, so no Icon suffix is used. Icons are named for what they communicate, not their shape:
// ✓ Correct
import { Close, Alert, Hyperlink } from '@dbt-labs/web-ui/icons'
// ✗ Old names — do not use
import { XMarkIcon, AlertIcon, LinkIcon } from '@dbt-labs/web-ui/icons'Variant props
Several icons cover multiple related visuals through a single component with typed props. This avoids separate files for each direction or style:
| Component | Prop | Options | Default |
|---|---|---|---|
Arrow | direction | 'left' | 'right' | 'right' |
Chevron | direction | 'up' | 'down' | 'left' | 'right' | 'down' |
Chevron | weight | 'normal' | 'bold' | 'normal' |
CheckCircle | variant | 'mono' | 'brand' | 'mono' |
User | variant | 'default' | 'plus' | 'default' |
LinkedIn | variant | null | 'solid' | null |
// Chevron adapts to context — one import handles all four directions
<Chevron /> // ∨ down (dropdown open indicator)
<Chevron direction='right' /> // > right (list item affordance)
<Chevron direction='up' /> // ∧ up (collapsed → expanded)
<Chevron weight='bold' direction='down' /> // bold stroke variant
// CheckCircle: brand uses orange + black; mono inherits currentColor
<CheckCircle variant='brand' /> // feature checklist (orange)
<CheckCircle /> // success state (inherits color)Link naming conflict
Hyperlink is named deliberately to avoid a collision with next/link's Link export. If you import both in the same file you can use them without an alias:
import Link from 'next/link'
import { Hyperlink } from '@dbt-labs/web-ui/icons'Navigation
Icons used for orientation, movement, and menu interactions.
Arrow
Directional movement — links, sliders, carousels.
| Prop | Type | Default |
|---|---|---|
| direction | 'left' | 'right' | 'right' |
| className | string | — |
Chevron
Dropdowns, accordions, collapsible nav. Default is down-pointing.
| Prop | Type | Default |
|---|---|---|
| direction | 'up' | 'down' | 'left' | 'right' | 'down' |
| weight | 'normal' | 'bold' | 'normal' |
| className | string | — |
Close
Dismiss dialogs, modals, drawers, and banners. Prefer this over a generic × glyph.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Login
Sign-in and authentication entry points.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Menu
Hamburger toggle for mobile navigation. Pair with Close for open/closed state.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Status
Icons that communicate state, feedback, or metadata.
Alert
Warning states and attention-required callouts.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
CheckCircle
Success states and feature checklists. variant='brand' uses the orange/black brand palette; default uses currentColor.
| Prop | Type | Default |
|---|---|---|
| variant | 'mono' | 'brand' | 'mono' |
| className | string | — |
Clock
Time estimates — read time, event duration, countdown.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Error
Error and empty-state illustrations. Note: renders a full-color SVG, not currentColor.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Info
Supplemental context and informational tooltips.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Action
Icons for interactive controls and content actions.
Hyperlink
External links and copy-URL affordances.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Minus
Remove, collapse, or decrement actions.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Plus
Add, expand, or increment actions.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Quote
Testimonials, pull quotes, and case study highlights.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Search
Search inputs and triggers.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
User
Account and profile affordances. variant='plus' for sign-up / invite flows.
| Prop | Type | Default |
|---|---|---|
| variant | 'default' | 'plus' | 'default' |
| className | string | — |
Media
Icons for audio, video, and content-type labeling.
Microphone
Podcast episodes, audio content labels.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Play
Video play buttons and media triggers.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Video
Video content labels and camera affordances.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Social
Social platform icons. Available from both @dbt-labs/web-ui/icons and @dbt-labs/web-ui/icons/social.
Bluesky
| Prop | Type | Default |
|---|---|---|
| className | string | — |
GitHub
| Prop | Type | Default |
|---|---|---|
| className | string | — |
| Prop | Type | Default |
|---|---|---|
| className | string | — |
variant='solid' fills the icon for profile/card contexts.
| Prop | Type | Default |
|---|---|---|
| variant | null | 'solid' | null |
| className | string | — |
RSS
| Prop | Type | Default |
|---|---|---|
| className | string | — |
X
X (formerly Twitter) social icon. Not to be confused with Close (the dismiss ×).
| Prop | Type | Default |
|---|---|---|
| className | string | — |
YouTube
| Prop | Type | Default |
|---|---|---|
| className | string | — |
Adding a new icon
Before writing code
- SVG path data from Figma, agreed name (PascalCase, communicative purpose —
BookmarknotRibbonShape) - Decide if it has variants upfront — define the prop interface before touching a file
- Write an intent note: one sentence on when and why to use it
No variants — 2 steps
1. Create packages/web-ui/icons/Bookmark.tsx
export const Bookmark = ({ className }: { className?: string }) => (
<svg
width='24'
height='24'
viewBox='0 0 24 24'
fill='none'
xmlns='http://www.w3.org/2000/svg'
className={className}
>
<path
d='M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z'
stroke='currentColor'
strokeWidth='1.5'
strokeLinecap='square'
strokeLinejoin='round'
/>
</svg>
)2. Regenerate the barrel
npm run barrel -w packages/web-uiThis runs scripts/generate-icon-barrel.mjs, which globs every .tsx file in icons/ and icons/social/, sorts them alphabetically, and rewrites both index.ts files. You never edit icons/index.ts by hand — the script owns it.
The barrel also runs automatically before every npm run build and npm run dev via Turborepo, so if you forget, the next build will pick it up.
With variants — 1 file, typed prop
type StarVariant = 'outline' | 'filled'
export const Star = ({
className,
variant = 'outline',
}: {
className?: string
variant?: StarVariant
}) => (
<svg viewBox='0 0 24 24' fill='none' className={className}>
{variant === 'filled' ? (
<path d='...' fill='currentColor' />
) : (
<path d='...' stroke='currentColor' strokeWidth='1.5' />
)}
</svg>
)Adding a future 'half-filled' variant: add to the StarVariant union, add a JSX branch. One file touched; TypeScript enforces the union everywhere it's used. Run npm run barrel -w packages/web-ui only if the filename changed — variants live in the same file so the barrel doesn't need updating.
Social icon
Drop the file in packages/web-ui/icons/social/ and run the barrel script — it handles icons/social/index.ts the same way. The icon is then available from both import paths:
import { NewSocialIcon } from '@dbt-labs/web-ui/icons/social'
import { NewSocialIcon } from '@dbt-labs/web-ui/icons'Documentation checklist
- Add a card under the correct category above
- Visual preview, component name, props table, intent note
- For variant icons, show each variant in the preview
- Add an
<IconDownload>component inside the card's<div className='p-4'>section, after the prop table — useicon={<ComponentName />}for single-variant icons, orvariants={[...]}for multi-variant ones - Verify it renders in the Wildflower dev server
Deleting an icon
grep -r 'ComponentName' apps/ packages/— find all import sites first- Migrate or remove all callsites
- Delete the
.tsxfile and the docs card above - Run
npm run barrel -w packages/web-ui— the deleted file is automatically removed fromindex.ts npm run build— TypeScript strict mode surfaces any missed references