State Layers

Material Design 3 interaction-state feedback for hover, focus, pressed, and dragged behavior. This page is the visual companion to the foundation guidance in state-layers.md and demonstrates the live utility classes and tokens used in the pattern library.

Overview

State layers are subtle color overlays. They communicate that an element is interactive without replacing the component's base surface or text color. In this system the base utility uses currentColor, and variant classes override the overlay color when semantic emphasis is needed.

Base behavior
.state-layer adds hover, focus-visible, and active overlays using semantic opacity tokens.
Variants
The live utility supports primary, secondary, tertiary, and error overlay colors.
Ripple
Ripple is enabled separately via data-ripple or .ripple-container, not via a dedicated combined class.

Token Matrix

These are the canonical state-layer opacity tokens. The dragged token exists in the token set even though the base utility class does not apply a drag interaction automatically.

Hover
--md-sys-state-hover-opacity
0.08 opacity for pointer hover.
Focus
--md-sys-state-focus-opacity
0.12 opacity for keyboard focus-visible.
Pressed
--md-sys-state-pressed-opacity
0.12 opacity for active or pressed feedback.
Dragged
--md-sys-state-dragged-opacity
0.16 opacity for drag feedback when a component chooses to implement it.

Basic Usage

Apply state-layer to any interactive element. Use semantic HTML when possible, and add tabindex only for non-semantic interactive containers.

Custom Interactive Block
<button type="button" class="state-layer">Surface Action</button>

<div class="state-layer" tabindex="0" role="button">
  Custom Interactive Block
</div>

Variants

The live utility layer supports four semantic overlay variants in addition to the default currentColor behavior.

Default
.state-layer
Uses the element's current text color as the overlay source.
Primary Overlay
.state-layer-primary
Secondary Overlay
.state-layer-secondary
Tertiary Overlay
.state-layer-tertiary
Error Overlay
.state-layer-error

States

Hover, focus-visible, and active are built into the base utility. Dragged is a tokenized pattern that components can opt into.

Interactive Surface
Tab to focus, hover with a pointer, and press to see the live state-layer behavior.
Dragged preview
Dragged Token
--md-sys-state-dragged-opacity
Shown here as a manual example because the base utility does not implement drag events directly.

Ripple Effects

Ripple is separate from state layers in the current implementation. Add data-ripple or ripple-container to an element to enable click-position ripple feedback.

<button type="button" data-ripple>Ripple Only</button>

<button type="button" class="state-layer" data-ripple>
  State Layer + Ripple
</button>

The current library does not expose a dedicated ripple-state-layer class. The combined pattern is simply class="state-layer" data-ripple.

Pattern Examples

These examples show where state layers appear in the pattern library beyond simple buttons.

Inbox
Sent
Archive
List Rows
Useful for custom nav rows, menus, and data list affordances.
Icon Buttons
Circular controls commonly use full-radius state-layer surfaces.

API Reference

Utility Type Description
state-layer CSS class Adds hover, focus-visible, and pressed overlay behavior.
state-layer-primary CSS class Changes overlay color to the semantic primary color.
state-layer-secondary CSS class Changes overlay color to the semantic secondary color.
state-layer-tertiary CSS class Changes overlay color to the semantic tertiary color.
state-layer-error CSS class Changes overlay color to the semantic error color.
data-ripple HTML attribute Enables ripple creation at the click position.
ripple-container CSS class Alternative hook for ripple-enabled containers.

Relevant Tokens

Token Default Description
--md-sys-state-hover-opacity 0.08 Opacity for hover overlay.
--md-sys-state-focus-opacity 0.12 Opacity for focus-visible overlay.
--md-sys-state-pressed-opacity 0.12 Opacity for pressed overlay.
--md-sys-state-dragged-opacity 0.16 Opacity guideline for dragged surfaces.

Accessibility

Keyboard discoverability
Use semantic controls where possible so focus-visible behavior is automatic and meaningful.
Non-semantic containers
If a div or custom surface is interactive, add tabindex and the correct ARIA role.
Content legibility
Overlay feedback should remain subtle enough that labels and icons stay readable.
Disabled behavior
The utility skips hover, focus, and pressed overlays for disabled states.

Usage Guidelines

When to use

  • For custom interactive surfaces that need MD3-style hover, focus, and pressed feedback.
  • For lightweight utility-driven prototypes and composition examples.
  • For patterns like list rows, icon-only actions, and custom card actions.

When not to use

  • When a component already implements its own internal state feedback.
  • When semantic button or link styling should come from the component itself rather than a generic utility.
  • When you need drag interaction logic; the dragged token exists, but drag behavior must be implemented by the component.

Common mistakes

  • Using foreground on-* colors as standalone surfaces instead of overlay colors.
  • Adding tabindex to elements that should simply be native buttons or links.
  • Documenting a combined utility class that does not exist; use class="state-layer" data-ripple instead.