Material library

Updated: Sep 24, 2026
Choose a MaterialLibrary factory before building layers. The library covers the defined surface roles and preserves the standard material’s coordinated focus, press, and partial-focus behavior. Each factory returns a fresh ContainerMaterial.
Create one instance per mounted host inside useMemo, keyed on the factory’s dependencies. This keeps ownership and transitions stable across renders.

Usage

import { useMemo } from 'react';
import { Container, MaterialLibrary } from '@wearables-ui-toolkit/mrbd';

export function GhostRow({ label, onOpen }: { label: string; onOpen: () => void }) {
  const material = useMemo(() => MaterialLibrary.unbounded(), []);

  return (
    <Container
      material={material}
      focusable
      clickable
      onClick={onOpen}
      className="ghost-row"
    >
      <span className="uit-text-body1">{label}</span>
    </Container>
  );
}
.ghost-row {
  padding-block: var(--uit-spacing-small);
  padding-inline: var(--uit-spacing-medium);
  color: var(--uit-color-text-primary);
}

Standard interactive surfaces

FactoryDescription
MaterialLibrary.default(options?)
The standard interactive material. Takes the same DefaultContainerMaterialOptions as createDefaultContainerMaterial.
MaterialLibrary.defaultWithDropShadow()
The standard material with its drop-shadow layer kept.
MaterialLibrary.button()
Button surface. The same layer stack as default().
MaterialLibrary.flat()
Alias of default(); the standard material already drops its shadow layer.
MaterialLibrary.notificationCenterItem()
Notification item surface: the standard material with a drop shadow.
MaterialLibrary.unbounded()
Transparent at rest. The idle fill is replaced by a layer that draws nothing, and the glow stroke is hidden in NONE and DEFAULT, so only focus and press paint.

Static and non-interactive surfaces

FactoryDescription
MaterialLibrary.defaultStatic(options?)
Static surface for non-interactive hosts: solid idle fill plus the standard glow stroke, with no focus fill, inner shadow, noise, or press overlay. Options are withDropShadow, idleColor, and secondary.
MaterialLibrary.reducedOpacityStatic()
Idle fill at reduced opacity, keeping the standard press overlay and glow stroke. No focus fill and no inner shadow.
MaterialLibrary.statusIndicatorPanel()
Status-indicator panel: a top-to-bottom surface scrim that fades to transparent just before the bottom edge of the path, plus inner shadow, press overlay, and glow stroke.
MaterialLibrary.clockPill()
Clock pill: nothing at rest, a vertical translucent surface gradient on focus and press, the standard press overlay, and a glow stroke hidden in NONE and DEFAULT.
MaterialLibrary.panel()
Panel surface. See Panel.
MaterialLibrary.actionHint()
Action-hint surface: a diagonal linear gradient from a faint white to transparent, plus a low-alpha radial glow stroke. Both layers paint identically in every visual state.
defaultStatic({ secondary: true }) additionally draws its idle fill with the screen blend mode and turns on backgroundBlendsWithBackdrop, so the surface lightens whatever is behind it instead of covering it.

Panel

MaterialLibrary.panel() wraps every layer of the standard material in a per-state opacity multiplier — half at NONE and DEFAULT, three quarters at FOCUSED, nine tenths at PRESSED — and interpolates that multiplier across a transition like any other per-state value. The wrapper scales each layer’s own alpha while it still paints onto the shared canvas, so screen and overlay layers keep blending against the layers beneath them.
It then adds a panel-scrim layer: a vertical gradient from transparent to opaque that reaches full opacity a short distance above the bottom edge. The scrim fills its own rectangle rather than the rounded shape path, and it paints at full opacity in every state.

Cards

FactoryDescription
MaterialLibrary.card()
Card surface: a solid background fill, and in the foreground a press overlay, an idle border stroke visible only in DEFAULT, and back and front glow strokes that appear on focus with hard-light blending. No focus gradient, inner shadow, noise, or drop shadow. Only the front glow tracks partial focus.
MaterialLibrary.fullScreenCard()
Full-screen card: the same solid fill, a foreground press overlay, and a single foreground hard-light border stroke that stays visible in every state except NONE, widens on focus and press, and tracks partial focus.
Both card materials set foregroundBlendsWithContent, so their strokes blend with the card’s live content rather than with an isolated buffer.

Component-role surfaces

Use these factories only for their named component role. Their behavior follows that role’s contract, so choosing one only for its current appearance can create inconsistent states later.
FactoryDescription
MaterialLibrary.contextMenuItem()
Context-menu item: the standard material with no idle fill and the glow stroke hidden in NONE and DEFAULT, so unfocused items sit directly on the menu surface and the border fades up from zero.
MaterialLibrary.controlTileCheckedIcon()
Checked control-tile icon backdrop: a single solid fill in the highlight color.
MaterialLibrary.textInput()
Text input field: the standard stack with a replaced idle fill, plus a directional inner shadow and a border stroke that are visible at rest and fade out on focus — the inverse of the standard material.

Themed and semantic surfaces

These take the standard layer stack and recolor three layers — the idle fill, the four-stop focus gradient, and the inner-glow tint. Noise, the press overlay, the glow stroke, timing, shape behavior, and partial-focus support are unchanged, so a themed surface still reads as the same component.
FactoryDescription
MaterialLibrary.themedPrimary(theme?)
Standard stack recolored by a theme object. Defaults to the neutral primary theme.
MaterialLibrary.themedSecondary()
Quieter neutral variant.
MaterialLibrary.themedPrimaryBlue()
Blue accent variant.
MaterialLibrary.themedPrimaryGreen()
Green accent variant.
MaterialLibrary.themedPrimaryRed()
Red accent variant.
MaterialLibrary.positive(useDefaultIdleState?)
Positive semantic variant. Defaults to true, keeping the neutral idle fill; pass false to use the accent color at rest.
MaterialLibrary.negative(useDefaultIdleState?)
Negative semantic variant, with the same idle-state switch.
MaterialLibrary.system()
Neutral system variant.
MaterialLibrary.outboundMessage(theme?)
Outbound-message material. Uses the standard theme by default; pass a theme object for application-specific colors.
MaterialLibrary.inboundMessage()
Inbound message surface.
The theme object has six string color fields:
FieldRole
idleFill
Resting surface. Quiet enough that focus is clearly distinguishable.
gradientStep1
Brightest focus core, at the start of the gradient.
gradientStep2
Transition color after the core.
gradientStep3
Deep falloff toward the outer region.
gradientStep4
Darkest outer extent, close to the surrounding surface.
glowTint
Inner-glow tint. Usually related to step 1.
Material layers paint onto a canvas, so these fields need resolved color strings; a var(--uit-color-...) reference will not resolve there. Keep the values in one application palette module and pass them in, rather than writing color literals at each call site. Build the ramp perceptually — decreasing lightness and usually chroma from step 1 to step 4, stable hue — instead of varying alpha on one value.
const material = useMemo(
  () =>
    MaterialLibrary.themedPrimary({
      idleFill: appPalette.primary.idle,
      gradientStep1: appPalette.primary.focusCore,
      gradientStep2: appPalette.primary.focusMid,
      gradientStep3: appPalette.primary.focusFalloff,
      gradientStep4: appPalette.primary.focusExtent,
      glowTint: appPalette.primary.glow,
    }),
  [appPalette],
);

Image-backed surfaces

MaterialLibrary.contentFocus() is for interactive containers whose content supplies the visible fill, such as a full-bleed image. It keeps the content unchanged at rest, then places a blurred radial light in the foreground during focus and press. The light follows partial focus, including discrete edge feedback. The same material is available directly as createContentFocusContainerMaterial().
MaterialLibrary.backgroundImageBlur(imageSrc?) renders four layers: a blurred image clipped to the shape and a dark fill overlay in the background, then a press overlay and a glow stroke in the foreground. The stroke is visible in every state except NONE and widens on focus and press. The material sets foregroundBlendsWithContent and reports supportsDiscretePartialFocus as false, so it does not follow partial focus.
The same material is available directly as createBackgroundImageBlurContainerMaterial(imageSrc?) and as the BackgroundImageBlurContainerMaterial class. Unlike other materials it is mutable: setImage(url) swaps the source and notifies the host to repaint, and getImage() returns the current source. Because the instance holds that state, the one-instance-per-host rule matters even more here. Passing null clears the image. The source is fetched through the shared canvas image cache, which loads images in anonymous cross-origin mode, so a remote origin has to serve permissive cross-origin resource sharing headers for the bitmap to reach the canvas.
const material = useMemo(
  () => createBackgroundImageBlurContainerMaterial(coverUrl),
  [],
);

useEffect(() => {
  material.setImage(coverUrl);
}, [material, coverUrl]);
withInset and the other derivations preserve the subclass and its current image.

Standalone factories

These are exported alongside the library and return the same materials as their MaterialLibrary entries, for code that does not want the whole object:
  • createDefaultContainerMaterial(options?)
  • createClockPillContainerMaterial()
  • createReducedOpacityStaticContainerMaterial()
  • createStatusIndicatorPanelContainerMaterial()
  • createBackgroundImageBlurContainerMaterial(imageSrc?)
  • createContentFocusContainerMaterial()
Every other entry — the cards, the panel, the themed variants, the static surfaces, and the component-role surfaces — is reachable only through MaterialLibrary.
  • Choose the factory whose role matches the surface so its states communicate the expected meaning.
  • Memoize the result and reuse it across the host’s rerenders to keep transition state intact.
  • Recolor through themedPrimary before considering custom layers; it preserves the standard stack.

Avoid

  • Do not pass one instance to multiple mounted hosts; that violates the single-owner contract.
  • Do not run factory calls during render, which restarts transitions. Memoize the result instead.
  • Do not use component-role factories with other components; behavior and visuals can become misaligned.
  • Do not use a material to change component anatomy, intrinsic size, focus bounds, or accessibility semantics.