IconWithContainerMaterial

Updated: Sep 24, 2026
Use IconWithContainerMaterial when an icon needs a material treatment rather than a bare background, such as an application identity mark or status glyph. The component draws the container material, inset icon, and material foreground layers as one non-interactive surface.
The component owns the material rendering, shape, and icon centering for a consistent appearance. The caller owns the element size and icon content.
import {
  CornerRadius,
  IconImage,
  IconWithContainerMaterial,
  RoundedRectangleShapeProvider,
  type IconSource,
} from '@wearables-ui-toolkit/mrbd';

const CIRCLE = new RoundedRectangleShapeProvider(CornerRadius.FULL);

function StatusGlyph({ icon }: { icon: IconSource }) {
  return (
    <div className="status-glyph">
      <IconWithContainerMaterial
        shapeProvider={CIRCLE}
        icon={<IconImage source={icon} />}
      />
    </div>
  );
}
.status-glyph {
  width: var(--uit-icon-2xl);
  height: var(--uit-icon-2xl);
}
Choose another material when the surface needs a different treatment. To tint the icon, set color on the surface. Keep the material in useMemo so its instance remains stable across renders. Each mounted surface must own its material instance because mounting one material instance on two surfaces at the same time throws:
import { useMemo } from 'react';
import type { CSSProperties } from 'react';
import {
  IconImage,
  IconWithContainerMaterial,
  MaterialLibrary,
  type IconSource,
} from '@wearables-ui-toolkit/mrbd';

const ACCENT_TINT: CSSProperties = { color: 'var(--uit-color-icon-accent)' };

function AlertGlyph({ icon }: { icon: IconSource }) {
  const material = useMemo(() => MaterialLibrary.defaultWithDropShadow(), []);

  return (
    <div className="alert-glyph">
      <IconWithContainerMaterial
        material={material}
        icon={<IconImage source={icon} />}
        style={ACCENT_TINT}
      />
    </div>
  );
}
.alert-glyph {
  width: var(--uit-icon-xlarge);
  height: var(--uit-icon-xlarge);
}
The component is also available from its subpath:
import { IconWithContainerMaterial } from '@wearables-ui-toolkit/foundation/components/IconWithContainerMaterial';

Props

The full prop type is IconWithContainerMaterialProps. It extends the standard React HTML attributes for a div. The component forwards unlisted attributes and the ref to the root element.
PropTypeDefaultDescription
icon
ReactNode
—
Required. Content rendered in the centered icon slot, usually an IconImage.
material
ContainerMaterial
MaterialLibrary.default()
Material painted behind and over the icon.
shapeProvider
ShapeProvider
RoundedRectangleShapeProvider at CornerRadius.LARGE
Geometry used for the material’s fill and stroke paths.
visualState
VisualState
VisualState.DEFAULT
Static state the material renders. It adds no interaction.
padding
number
—
Pixel inset for the icon slot on all four sides, replacing the default proportional inset. Ignored when renderAtIntrinsicSize is set.
renderAtIntrinsicSize
boolean
false
Center a fixed-size icon box instead of insetting the slot.
intrinsicIconSize
number
24
Side length in pixels of that box, matching the medium icon size. Used only when renderAtIntrinsicSize is set.
className
string
''
Appended after the component’s own class, so a rule of equal specificity declared later wins.
style
CSSProperties
{}
Inline styles on the root element.

Types and enums

The component declares no enums. It uses these shared types:
  • VisualState from the interaction primitives: NONE, DEFAULT, FOCUSED, PRESSED.
  • ContainerMaterial and ShapeProvider from the material system, plus MaterialLibrary and the shape providers for building the supplied values.

Behavior

Sizing. The surface fills its parent on both axes. The caller owns the wrapper size and should typically use icon-size tokens for a responsive, consistent box.
Icon placement. By default, the component insets the icon slot from each edge by one fifth of the surface. This leaves the centered icon at three fifths of the surface on each axis. Supply padding for an explicit pixel inset, or use renderAtIntrinsicSize to center a box of intrinsicIconSize without an inset. The slot centers its content, and a vector icon inside it fills the slot.
Material. When material is omitted, the surface resolves MaterialLibrary.default(). The material paints behind and over the icon, so foreground layers such as strokes and glows remain above the glyph.
Non-interactive behavior. The surface is not focusable, pressable, or clickable. By default, it renders neither a role nor a tabindex attribute. Because the component is never focusable, it drops a passed tabIndex instead of applying it. Content scale remains constant, so the icon does not breathe with material state as content in an interactive container does.
Static state.visualState renders a state without adding its interaction behavior. For example, a surface rendered with the focused visual state does not own focus.
Tint. The root sets color to the primary text token so an icon painted with currentColor remains legible on the material. Set another semantic icon color token through className or style to change the tint consistently.
Pointer events. The icon slot ignores pointer events. Input therefore reaches the interactive element that owns the surface instead of the icon.
Instance stability. A new material value makes the surface reattach the material and repaint every layer. A new shapeProvider value makes it recompute the shape path. Keep both values stable across renders for consistent performance. Each mounted surface must own a material instance because mounting one instance on two surfaces at once throws. Keep that instance in useMemo, and freely share the immutable shape provider. The parent recreates the icon element on every render regardless, so material and shape-provider stability avoid the meaningful extra work.

Accessibility

  • The surface contributes no role or accessible name because it is decorative.
  • The icon inside the surface is also decorative, so this subtree is not announced.
  • The interactive component or region that owns the surface must provide the accessible name and state. Do not rely on the glyph alone because users who cannot perceive it still need the same information.
  • Size the element with icon-size tokens and let the component own the inset.
  • Prefer IconImage for icon so tint and sizing follow the shared icon-slot contract.
  • Choose the shape with a shape provider and a semantic corner radius.
  • Keep one material instance per mounted surface and reuse it across renders. Reuse an immutable shape provider wherever the same shape applies.

Avoid

  • Avoid recreating this appearance with nested rounded shapes or a background element stacked behind an icon; the component keeps the layers consistent.
  • Use Container when the region is interactive. IconWithContainerMaterial does not provide focus, press, or click behavior.
  • Avoid setting both padding and renderAtIntrinsicSize; intrinsic-size rendering ignores padding.
  • Do not nest another container-like component, button, chip, tag, or page header inside this surface. These unsupported compositions create conflicting surface ownership and interaction semantics.
  • Prefer the default proportional inset when it produces the intended size rather than pinning the icon to pixels.