Hover
Hover overlays are translucent state tokens layered on top of any background rather than hardcoded colours. This ensures correct contrast in both Noir and Light themes without duplicating rules. The :hover state gives visual feedback on interactive elements and is only visible when a pointer is over the element.
Example
Hover — Dark
Some AnchorLorem Ipsum is simply dummy text of the printing and typesetting industry.
state-hover overlay
bg-surface tile
Hover — Light
Some AnchorLorem Ipsum is simply dummy text of the printing and typesetting industry.
state-hover overlay
disabled
| Token | Value | Role |
|---|---|---|
| --diwa-state-hover | rgba(148,149,152,.18) | Standard hover overlay — works on any bg in both themes |
| --diwa-state-active | rgba(126,127,130,.20) | Pressed / active overlay |
| --diwa-state-disabled | #52525b / #949598 | Disabled element fill — theme-aware |
ℹ
Experimental: The getHoverStyle() utility is still experimental — its interface may change in future versions.
Usage
Do
- ✓Always wrap :hover rules in @media (hover: hover) to prevent sticky hover states on touch devices.
- ✓Apply the hover overlay via a ::after pseudo-element so the component background remains unchanged on any surface.
- ✓Use the state-hover token on all interactive elements — buttons, cards, list items, anchors.
- ✓Pair hover with a :active overlay (state-active) to complete the full press interaction feedback.
Don't
- ✕Don't omit @media (hover: hover) — tapping a card on iOS will leave a permanently stuck hover overlay.
- ✕Don't apply hover to non-interactive elements such as static text, images, or decorative dividers.
- ✕Don't hardcode the hover colour — always use the state-hover token so both themes work correctly.
- ✕Don't rely on hover as the sole indicator of interactivity — pair it with cursor: pointer and focus styles.
Styles
Import the JS helper or use the CSS pseudo-element pattern directly. The borderRadius parameter rounds the overlay to match the host element.
// JS
import { getHoverStyle } from '@diwacopilot/components/styles';
// Returns CSS-in-JS object with pseudo-element hover overlay
const hoverStyle = getHoverStyle({ borderRadius: 'small' });
// { '&::after': { content: "''", position: 'absolute', inset: 0,
// borderRadius: 'var(--diwa-border-radius-sm)', background: 'var(--diwa-state-hover)', ... } }
/* ─── CSS — pseudo-element overlay pattern ───────────────────────────── */
/* Standard component / Shadow DOM */
:host {
position: relative;
}
:host::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
background: transparent;
transition: background var(--diwa-motion-duration-short) var(--diwa-motion-easing-base);
}
@media (hover: hover) {
:host(:hover)::after {
background: var(--diwa-state-hover);
}
}
:host(:active)::after {
background: var(--diwa-state-active);
}
/* Plain CSS — same pattern for non-Shadow DOM */
@media (hover: hover) {
.card:hover::after {
content: '';
position: absolute;
inset: 0;
background: var(--diwa-state-hover);
border-radius: inherit;
pointer-events: none;
}
}@use '@diwacopilot/components/styles' as *;
:host {
position: relative;
}
:host::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
pointer-events: none;
background: transparent;
transition: background $diwa-motion-duration-short $diwa-motion-easing-base;
}
@media (hover: hover) {
:host(:hover)::after { background: $diwa-state-hover; }
}
:host(:active)::after { background: $diwa-state-active; }