RoadieRoadie
  • Home
  • Brand
  • Foundations
  • Content
  • Tokens
  • Components
  • Charts
  • Widgets
  • Appearance
HomeBrandFoundations
Appearance

Foundations

  • Visual

    • Colors
    • Elevation
    • Iconography
    • Shape
    • Theming
  • Content

    • Date and time
    • Layout
    • Prose styles
    • Typography
  • Behaviour

    • Accessibility
    • Interactions
    • Motion
    • View transitions
  • Building apps

    • Forms
    • Linking
    • Navigation
    • Performance
    • Records modelAlpha
    • Tables

Iconography

Docs updated
10 Oct 2026

Roadie uses Phosphor Icons at the Bold weight. The thicker strokes pair with the system's rounded, approachable visual language and stay legible at small sizes.

Principles

  • Use icons intentionally. Pair them with text labels wherever possible, and avoid decorative-only icons that add clutter without aiding comprehension.
  • Prefer existing icons. Browse Phosphor's library before considering custom SVGs. Reusing established metaphors keeps the system consistent.
  • Keep it consistent. Bold by default, Fill only for active or selected states, and Duotone only for large decorative icons in a tile or graphic above 48px. Never mix Regular, Thin, or Light weights.

Visual style

Bold weight provides thicker strokes that are more legible at small sizes and create a confident, approachable feel. Here are some commonly used icons in the system.

{/* Heart */}
<HeartIcon weight='bold' className='size-6' />
{/* Star */}
<StarIcon weight='bold' className='size-6' />
{/* Plus */}
<PlusIcon weight='bold' className='size-6' />

Icon sizes

Four fixed tiers map to component contexts. Set an icon's size with a Tailwind size-* class in className, never Phosphor's size prop. On the docs site, each tier is drawn at its class with its size in pixels.

  1. size-312px
  2. size-416px
  3. size-520px
  4. size-624px
TierClassUse for
XSsize-3Badges, tags, compact elements, and chevrons in small buttons
SMsize-4Buttons, and inline with body text (the default)
MDsize-5Navigation and standalone actions
LGsize-6Page headers, feature cards, and hero sections

Sizing inside components

Badge, Button, IconButton, and IconTile size the icons inside them, so an icon there needs no size class. Badge sizes its icon to the text (1em). Button and IconButton size an icon to the button, unless the icon has a size-* class of its own: 16px in a Button and 20px in an IconButton by default. IconTile sizes its icon to the tile.

Icon weights

Three weights are used in the system. Bold is the default for everything. Fill is reserved for active or selected states. Duotone is for large, decorative icons in a tile or graphic above 48px. Use it on feature surfaces, in an IconTile larger than lg, and in an EmptyState tile at its default size, where a bold outline alone looks hollow.

WeightWhen to use
boldThe default for all icons
fillActive or selected states only
duotoneDecorative icons in a tile or graphic above 48px, such as an IconTile
{/* bold */}
<HeartIcon weight='bold' className='size-5' />
<StarIcon weight='bold' className='size-5' />
<CheckCircleIcon weight='bold' className='size-5' />
{/* fill */}
<HeartIcon weight='fill' className='size-5' />

Navigator is the exception. Its destinations render duotone at 24px. Navigator applies the weight itself, so pass it a bare icon.

Bold and fill in navigation

Navigation items use Bold when inactive and Fill when active.

<Tabs defaultValue='favourites' emphasis='normal'>
<Tabs.List>
<Tabs.Tab
value='favourites'
render={(props, state) => (
<button {...props}>

Icon tiles

Frame an icon in an IconTile when it needs to stand on its own, such as leading a dialog, a list row, or an empty state. The tile sizes and centres the icon for you, so pass a bare icon as children with no size class.

<IconTile intent='accent' size='lg'>
<HeartIcon weight='bold' />
</IconTile>
<IconTile intent='success' size='lg'>
<CheckCircleIcon weight='bold' />
</IconTile>

Tile the item, not the action. Inside buttons, menu items, tabs, badges, and inputs, use a bold icon with no tile, at the size the control sets or size-4 where it sets none. See Icon Tile.

Tiles are the main place weight='duotone' is welcome. Use weight='bold' up to a 48px tile (lg), then switch to duotone above 48px, where a bold outline can look hollow on the larger surface. Keep Fill reserved for active states as elsewhere.

Icon colour

Icons inherit currentColor by default. Use text-* utilities to set icon colour, or let them inherit from a parent. An intent only sets variables, so it colours an icon through a text-* utility on the icon or its parent, such as a Badge.

{/* text-normal */}
<HeartIcon weight='bold' className='size-6 text-normal' />
{/* text-subtle */}
<HeartIcon weight='bold' className='size-6 text-subtle' />
{/* text-strong */}
<HeartIcon weight='bold' className='size-6 text-strong' />

Import conventions

Use the Icon suffix export

Phosphor exports each icon with an Icon suffix. Always use the suffixed export. The bare name is deprecated.

import {
HeartIcon,
StarIcon
} from '@phosphor-icons/react/ssr'

Do

Import the Icon-suffixed export directly.

import {
Heart,
Star
} from '@phosphor-icons/react/ssr'

Don’t

Bare names are deprecated and can collide with component or variable names.

Use the SSR import for server components

Phosphor's /ssr entry point doesn't use React context, so it works in server components.

// Server component (no 'use client')
import {
HeartIcon
} from '@phosphor-icons/react/ssr'

Do

Use @phosphor-icons/react/ssr in server components.

// Server component (no 'use client')
import {
HeartIcon
} from '@phosphor-icons/react'

Don’t

The standard entry reads React context, which server components can't use, so it fails there.

Guidelines

  • Always set weight='bold' explicitly. Phosphor's default is Regular, not Bold.
  • Use aria-label on icon-only buttons. IconButton enforces it as a required prop.
  • Browse icons at phosphoricons.com, and filter by Bold weight to see what's available.
  • Icons inherit currentColor. Use text-* utilities for colour, not inline styles or Phosphor's color prop.
  • Inside Badge, Button, IconButton, and IconTile, icons size themselves, so they need no size class.
  • Use @phosphor-icons/react/ssr in server components. Only use @phosphor-icons/react in files with 'use client'.

Pair icons with text labels

Icons are most effective when paired with text. Use icon-only buttons only when the action is universally understood, and always provide an aria-label.

Do

Pair icons with visible text. For icon-only buttons, use IconButton, which requires aria-label.

Don’t

Don't use a button with only an icon and no label or aria-label. Screen readers won't know what the button does.

Use Bold weight consistently

All icons use weight='bold'. Don't mix weights, which breaks visual harmony.

Do

All icons at Bold weight for a consistent stroke width.

Don’t

Mixing weights creates visual inconsistency. Regular, Thin, and Light look out of place next to Bold.

Reserve Fill for active states

Fill weight signals that something is selected or active. Don't use it as the default.

Do

Fill for the active item, Bold for everything else.

Don’t

Fill on inactive items removes the visual distinction for the active state.

Reserve duotone for large, decorative icons

Duotone adds depth at large sizes but muddies legibility when small.

Do

Use weight='duotone' in a tile or graphic above 48px. The two-tone fill reads as intentional depth on feature surfaces and in a large IconTile.

Favourites

Don’t

Don't use duotone for small inline or UI icons. The lighter secondary layer turns muddy at small sizes. Use weight='bold' instead.

Match icon size to context

Use the sizing scale to keep icons proportional to their surroundings.

Verified

Do

Let components size icons, or use the tier for the context.

Don’t

Oversized icons next to small text look unbalanced. Match the icon tier to the text size.

Previous page← ElevationNext pageShape →