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.
.state-layer adds hover, focus-visible, and
active overlays using semantic opacity tokens.
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.
Basic Usage
Apply state-layer to any interactive element. Use
semantic HTML when possible, and add tabindex only for
non-semantic interactive containers.
<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.
States
Hover, focus-visible, and active are built into the base utility. Dragged is a tokenized pattern that components can opt into.
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.
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
div or custom surface is interactive, add
tabindex and the correct ARIA role.
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
tabindexto elements that should simply be native buttons or links. -
Documenting a combined utility class that does not exist; use
class="state-layer" data-rippleinstead.