Brand
Iconography
One vocabulary: Lucide, stroke only, never filled. An icon here is friendly line work — light, open, and quick to read. Stroke 1.75px, inheriting currentColor, so every glyph picks up the color of its context without per-icon color work (ADR-0010). The set is shared with the proposed system; only the paint around it changes.
The vocabulary
Five constraints define the whole system. They are deliberately few — the discipline is what keeps a thousand-glyph library feeling like one friendly set.
- library
- Lucide — broad coverage, well-maintained, open-source. When Lucide lacks a glyph, a custom one is authored in the same style via the ADR-0010 RFC path, never redrawn ad hoc.
- style
- Stroke only — never filled. The open line work stays light and approachable; filled icons would weigh the surface down.
- stroke width
- Default
1.75px. The one exception: 12px icons inside badges bump to1.85for crispness at that size. - color
- Icons inherit
currentColor— the context cascades its color through (gold for primary and active, plain ink elsewhere). No per-icon color overrides. - grid
- A
24×24viewBox, square endcaps unless rounded fits the glyph better. Custom additions keep the same geometry.
Specimens
A working sample of the glyphs the product reaches for most — navigation, record actions, people, time. Each renders exactly as the system ships it: stroke 1.75, currentColor, on the native 24px grid. Hover one — the official brand says hello with a little lift.
Inlined as JSX with stroke=currentColor — toggle the appearance and the brand; the set re-inks itself with zero per-icon work.
Severity glyphs
Severity is never color alone (ADR-0017). Under red-green color-blindness, the caution and critical hues collapse into similar olives — so every severity carries a disambiguating glyph and names itself in text. These are the shipped defaults, rendered by the same severity-icon module Toast, Banner and Badge consume.
A named gap: ADR-0017 specifies octagon-alert — the universal stop-sign silhouette — for critical, but the shipped Beta messengers currently render circle-x. The divergence is shown here, not hidden; reconciliation is pending.
The brand mark
The engraved dial glyph belongs to the proposed system — under the official brand it does not exist. The face of this brand is the gold ring mark: a solid fill, not a stroke, and it stays brand artwork. It lives in brand slots and lockups, never in the icon set, and it is never stroke-converted or recolored.
Vendor marks
The second ADR-0010 exception, amended in: where a vendor requires its own mark — the Google "G" on a sign-in button is the live case — it renders per that vendor's guidelines. Full color, correct geometry, never recolored, never reduced to a stroke glyph. This applies only where the vendor mandates the mark (chiefly SSO buttons); it is not a license for multicolor icons.
Sizing
There is no dedicated icon-size token ramp yet — a named gap, shared with the proposed system. Icons size from their context: the native grid, the chrome that holds them, or the badge they sit in. The working sizes are these.
- 24px — native grid
- The authored size. Doc specimens and standalone glyphs render here.
- 18px — chrome
- Ambient buttons and app-bar affordances; the icon shrinks, the stroke holds at 1.75.
- 12px — badge
- The smallest legal size, and the one stroke exception:
stroke 1.85for crispness (ADR-0010).
Don't
- No emoji, ever. Emoji break the voice register, render differently per platform, and have no accessibility contract (ADR-0009). Lucide fills the role emoji might otherwise take.
- No filled icons. The open line work is the shared vocabulary across both brands; mixing in filled glyphs fractures it.
- Never recolor a vendor mark. The Google "G" keeps its four colors; a monochrome or gold "G" violates the vendor's guidelines and ADR-0010 both.
- Never carry severity by color alone. Pair the tone with its glyph and name it in text (ADR-0017) — the glyph survives greyscale; the hue does not.
- Brand artwork is not an icon. The ring mark, lockups and patterns stay in brand slots — under this brand no mark crosses into the icon set at all.
Accessibility
- Decorative icons disappear. An icon next to its label is decoration — aria-hidden="true" so screen readers hear the words once.
- Icon-only controls carry a name. A glyph with no visible text needs aria-label; the icon itself announces nothing.
- Meaning gets words. If a glyph carries information (a severity, a state), the information also appears as text — the icon is the fast cue, the text confirms.
- Contrast rides currentColor. Because icons inherit the text color of their context, an icon passes contrast wherever its text does — one check, not two.
Usage
import { Download, FileText } from 'lucide-react'
// Decorative beside its label — hidden from the accessibility tree:
<Button variant="ghost" tone="secondary">
<FileText strokeWidth={1.75} aria-hidden="true" />
Risk report
</Button>
// Icon-only — the control carries the name instead:
<Button variant="ghost" tone="secondary" aria-label="Download report">
<Download strokeWidth={1.75} aria-hidden="true" />
</Button>import { defaultSeverityIcon, type SeverityTone } from '@halo-compliance/ui'
// The shared severity register — glyph + tone + the word, never color alone:
const tone: SeverityTone = 'caution'
<span className="finding-status">
{defaultSeverityIcon(tone)}
Caution: this control needs attention
</span>