Icon box

Icon container

Intent

Icon-box is a fixed-size colored square that centers an SVG or glyph. Use it where you want an icon to read as a chip rather than an inline mark -- feature grids, media rows with a heading and blurb, notification feed glyphs, stat-tile heads. For plain inline icons inside a button or navigation link, you don't need this component; the root --icon-size token already sizes SVGs inside button and sidebar-nav via their own rules.

The glyph inside an icon-box inherits currentColor, which tracks the component's --icon-box-color token. Monochrome SVGs with stroke="currentColor" or fill="currentColor" automatically pick up the variant color; SVGs with hard-coded colors ignore it.

Basic usage

A <div class="icon-box"> wrapping an SVG. The default tone uses the neutral surface and text tokens -- reach for a color variant when the icon itself should carry meaning.

<div class="icon-box">
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"></path>
        <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"></path>
    </svg>
</div>

Variants

Size

Sizes step through the density grammar: .is-sm for dense rows and compact feature lists; the default for typical feature grids; .is-lg for hero marks or standalone callouts. The SVG inside doesn't auto-scale -- match the glyph's explicit width and height to the variant, or size it in relative units (em, %) if you want one markup shape to work across sizes.

<div class="cluster">
    <div class="icon-box is-sm">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
        </svg>
    </div>
    <div class="icon-box">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
        </svg>
    </div>
    <div class="icon-box is-lg">
        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
        </svg>
    </div>
</div>

Color

Status tones remap the scoped tokens to the matching semantic color family -- a subtle tinted background paired with the darker primary/success/warning/danger/accent color for the glyph. The default (no modifier) uses neutral surface tokens and reads as chrome rather than status.

<div class="cluster">
    <div class="icon-box">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"></path>
            <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"></path>
        </svg>
    </div>
    <div class="icon-box is-primary">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="M12 16v-4"></path>
            <path d="M12 8h.01"></path>
        </svg>
    </div>
    <div class="icon-box is-success">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
            <polyline points="22 4 12 14.01 9 11.01"></polyline>
        </svg>
    </div>
    <div class="icon-box is-warning">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3z"></path>
            <path d="M12 9v4"></path>
            <path d="M12 17h.01"></path>
        </svg>
    </div>
    <div class="icon-box is-danger">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="m15 9-6 6"></path>
            <path d="m9 9 6 6"></path>
        </svg>
    </div>
    <div class="icon-box is-accent">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
            <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
        </svg>
    </div>
</div>

Composed patterns

Feature row with .media

The canonical use. Pair .icon-box with the .media composition primitive: put .media-figure on the icon-box so the row layout treats it as the figure slot, and drop heading + body in .media-body. Each class does one job -- icon-box themes the chip, .media handles the figure-plus-body row.

Fast builds

esbuild-based pipeline produces the full CSS and docs site in under a second on typical hardware.

Semantic HTML first

Components style real elements -- no div-tag soup, no wrapper fences around native widgets.

Tokens all the way down

Every scoped component token resolves through the semantic layer, so a palette change flows through the entire library.

<div class="stack">
    <div class="media">
        <div class="icon-box is-primary media-figure">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
            </svg>
        </div>
        <div class="media-body">
            <h4>Fast builds</h4>
            <p>esbuild-based pipeline produces the full CSS and docs site in under a second on typical hardware.</p>
        </div>
    </div>
    <div class="media">
        <div class="icon-box is-success media-figure">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
                <polyline points="22 4 12 14.01 9 11.01"></polyline>
            </svg>
        </div>
        <div class="media-body">
            <h4>Semantic HTML first</h4>
            <p>Components style real elements -- no div-tag soup, no wrapper fences around native widgets.</p>
        </div>
    </div>
    <div class="media">
        <div class="icon-box is-accent media-figure">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
            </svg>
        </div>
        <div class="media-body">
            <h4>Tokens all the way down</h4>
            <p>Every scoped component token resolves through the semantic layer, so a palette change flows through the entire library.</p>
        </div>
    </div>
</div>

Class reference

  • .icon-box -- the atom. Typically applied to a <div> wrapping an SVG.
  • .is-sm, .is-lg -- size variants.
  • .is-primary, .is-success, .is-warning, .is-danger, .is-accent -- color variants. Default (no modifier) uses neutral surface tokens.

Customization

--icon-box-radius defaults to --radius-md; override with --radius-pill for a circular chip. Full token list and defaults: src/css/icon.css.

Accessibility

  • Decorative glyph, hide it. When an icon-box sits next to text that already conveys the meaning (the feature-row pattern above: the heading and blurb say everything), mark the SVG aria-hidden="true" so screen readers don't announce a redundant "image".
  • Meaningful glyph, name it. When the icon-box stands alone or carries information not present in adjacent text, give the SVG role="img" and an inner <title> element, or put an aria-label on the <svg>. The title / label is what assistive tech announces.
  • Icon-box isn't focusable. It's a <div> -- not a button, not a link. If you want a whole-row or whole-tile click target, wrap the row in an <a> or put an <a> on the heading (as card's .is-interactive pattern does). Don't add tabindex, role="button", or a click handler to the icon-box itself.

CSS Reference: Icon Box

Colored box that centers an SVG or glyph

Source: src/css/icon.css

Tokens

TokenDefaultDescription
--icon-box-size2.5remWidth and height of the box
--icon-box-bgvar(--color-surface-raised)Background color of the box
--icon-box-colorvar(--color-text)Icon color inside the box
--icon-box-radiusvar(--radius-md)Border radius of the box

Classes

ClassDescription
.icon-boxcomponent root
.is-smSmaller box at 2rem
.is-lgLarger box at 3rem
.is-primaryPrimary color variant
.is-successSuccess color variant
.is-warningWarning color variant
.is-dangerDanger color variant
.is-accentAccent color variant