Icon
Icon sources
Accessible SVG sources inherit content color without a framework adapter.
Material Symbols sources
import {
Icon,
Surface,
Text,
type IconSourceProps,
} from '@language-lit/material3-expressive'
function SparkSource(props: IconSourceProps) {
return (
<svg {...props} viewBox="0 0 24 24">
<path d="m12 1 2.15 7.35L21.5 10.5l-7.35 2.15L12 20l-2.15-7.35L2.5 10.5l7.35-2.15L12 1Z" />
<path d="m19 16 .85 2.65 2.65.85-2.65.85L19 23l-.85-2.65-2.65-.85 2.65-.85L19 16Z" />
</svg>
)
}
function ArrowSource(props: IconSourceProps) {
return (
<svg {...props} viewBox="0 0 24 24">
<path d="m13 5 7 7-7 7-1.4-1.4 4.6-4.6H4v-2h12.2l-4.6-4.6L13 5Z" />
</svg>
)
}
export function IconExample() {
return (
<Surface
as="section"
aria-labelledby="icon-example-title"
color="tertiary-container"
shape="extra-large"
className="icon-example"
>
<Icon
source={SparkSource}
decorative={false}
label="Expressive"
size={40}
/>
<div>
<Text as="h2" id="icon-example-title" variant="titleLarge" emphasis="emphasized">
Icon sources
</Text>
<Text as="p" variant="bodyMedium">
Accessible SVG sources inherit content color without a framework adapter.
</Text>
</div>
<Icon source={ArrowSource} mirrored aria-describedby="icon-rtl-note" />
<Text as="small" id="icon-rtl-note" variant="labelSmall">
Directional artwork mirrors only when requested.
</Text>
<div style={{ display: 'grid', gap: '0.75rem', gridColumn: '1 / -1' }}>
<Text as="h3" variant="titleMedium" emphasis="emphasized">
Material Symbols sources
</Text>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1.5rem' }}>
<div style={{ display: 'grid', justifyItems: 'center', gap: '0.25rem' }}>
<Icon source="home" symbolStyle="outlined" size={32} />
<Text as="small" variant="labelSmall">
outlined
</Text>
</div>
<div style={{ display: 'grid', justifyItems: 'center', gap: '0.25rem' }}>
<Icon source="home" symbolStyle="rounded" size={32} />
<Text as="small" variant="labelSmall">
rounded
</Text>
</div>
<div style={{ display: 'grid', justifyItems: 'center', gap: '0.25rem' }}>
<Icon source="home" symbolStyle="sharp" size={32} />
<Text as="small" variant="labelSmall">
sharp
</Text>
</div>
<div style={{ display: 'grid', justifyItems: 'center', gap: '0.25rem' }}>
<Icon
source="favorite"
decorative={false}
label="Favorite, minimum axis values"
symbolStyle="outlined"
fill={0}
weight={100}
grade={-50}
opticalSize={20}
roundness={0}
size={32}
/>
<Text as="small" variant="labelSmall">
low axes
</Text>
</div>
<div style={{ display: 'grid', justifyItems: 'center', gap: '0.25rem' }}>
<Icon
source="favorite"
decorative={false}
label="Favorite, maximum axis values"
symbolStyle="rounded"
fill={1}
weight={700}
grade={200}
opticalSize={48}
roundness={100}
size={32}
/>
<Text as="small" variant="labelSmall">
high axes
</Text>
</div>
<div style={{ display: 'grid', justifyItems: 'center', gap: '0.25rem' }}>
<Icon
source="star"
symbolStyle="sharp"
fill={0.5}
weight={400}
grade={0}
opticalSize={32}
roundness={50}
size={32}
/>
<Text as="small" variant="labelSmall">
mid axes
</Text>
</div>
</div>
</div>
</Surface>
)
}Icon renders passive, single-color Material iconography from either a React
SVG source component or a consumer-supplied Material Symbols font. It inherits
the surrounding content color and owns a predictable accessibility boundary.
import {
Icon,
type IconSourceProps,
} from '@language-lit/material3-expressive'
import '@language-lit/material3-expressive/styles.css'
function SearchIcon(props: IconSourceProps) {
return (
<svg {...props} viewBox="0 0 24 24">
<path d="M9 3a6 6 0 1 0 0 12 6 6 0 0 0 0-12Zm4 11 6 6" />
</svg>
)
}
<Icon source={SearchIcon} decorative={false} label="Search results" />Contract
source={SvgComponent}adapts a React component that renders one SVG. The source must forward the suppliedclassName,aria-hidden, andfocusableprops to its root<svg>.source="search"renders a Material Symbols ligature.symbolStyleselectsoutlined(default),rounded, orsharp.sizeis a positive CSS-pixel number. The sourced default is 24. The root is always a passivespan, and its ref is anHTMLSpanElement.mirroredopts directional artwork into horizontal mirroring under RTL. Icons do not mirror from their glyph name or shape automatically.- Native IDs, data/ARIA description attributes, classes, styles, and passive
event handlers are preserved.
children,role, accessible-name attributes,tabIndex, and raw HTML are owned by the component contract.
Icon has no click, toggle, selection, disabled, or keyboard behavior. Put a
decorative Icon inside a properly named IconButton or another native control;
do not attach control semantics to the Icon span.
Accessibility
Icons are decorative by default:
<button type="button" aria-label="Search">
<Icon source={SearchIcon} />
</button>The Icon root and its source visual are hidden from assistive technology, so the
control name is announced once. When an icon conveys standalone information,
use decorative={false} and provide a localized, non-empty label. The root
then exposes exactly one named img role while the source remains hidden.
fill, grade, weight, color, or animation must never be the only way an
accessible state is communicated. Interactive components own aria-pressed,
aria-selected, checked state, labels, descriptions, and focus behavior.
Material Symbols and Expressive axes
<Icon
source="favorite"
symbolStyle="rounded"
size={32}
fill={1}
weight={575}
grade={100}
opticalSize={32}
roundness={100}
/>The glyph adapter supports the current Material Symbols axes:
fill:FILL, sourced range 0–1weight:wght, sourced range 100–700grade:GRAD, sourced range -50–200opticalSize:opsz, sourced range 20–48roundness: ExpressiveROND, sourced range 0–100
When opticalSize is omitted, an explicit visual size also selects its
optical size, clamped to the font's 20–48 design range. Axis values are
continuous numbers so capable variable fonts are not restricted to a few named
instances. Development builds warn about values outside sourced ranges.
The library does not download, subset, or declare a Material Symbols font.
Applications should self-host or request only the glyphs and axes they use,
then load the corresponding Material Symbols Outlined, Rounded, or Sharp
family before rendering glyph sources. SVG sources need no font.
Both sources are deterministic under ordinary React server rendering. The package's root entry is a client boundary for React Server Components because it also exports the theme provider and hooks. A Next.js Server Component may pass the serializable string source; put an SVG component source call in a client module. This packaging constraint adds no Next.js code or types to Icon.
Tokens and color
The default component variables are:
--m3e-comp-icon-size--m3e-comp-icon-symbol-family-{outlined|rounded|sharp}--m3e-comp-icon-symbol-{fill|weight|grade|optical-size|roundness}
Explicit props set stable --m3e-icon-* instance variables on the root. Normal
consumer inline styles remain available for deliberate presentation overrides.
Icon uses inherited currentColor; a Surface supplies the corresponding
Material content role. Multicolor artwork and arbitrary images belong in an
image component rather than this tint-oriented icon primitive.
Bidirectionality, motion, and forced colors
Material Symbol ligature text is always laid out LTR so an RTL page does not
reorder its source name. mirrored affects only the visual source and only in
RTL. The component defines no transition or animation, so reduced motion needs
no alternate path. It keeps currentColor active and does not disable browser
forced-color adjustment.