Other State
Overview
Other state modifiers let you apply utility styles conditionally based on miscellaneous element states. They target elements that are open (like <details> and popovers) or inert (non-interactive), generating variant utility classes for these specific conditions.
Why Use Other State Modifiers?
Other state modifiers help you:
- Style disclosure widgets: Apply styles to open
<details>elements and popovers - Handle inert state: Style elements that are non-interactive due to the
inertattribute - Build accessible patterns: Create visual feedback for native HTML patterns like disclosure and popover
useOpenModifier
The useOpenModifier() function creates a modifier that applies styles to elements that are in an open state. This includes <details> elements with the open attribute and elements using the Popover API.
import { styleframe } from "styleframe";
import { useRotateUtility, useDisplayUtility, useOpacityUtility } from "@styleframe/theme";
import { useOpenModifier } from "@styleframe/theme";
const s = styleframe();
const open = useOpenModifier(s);
useRotateUtility(s, {
90: '90deg',
}, [open]);
useOpacityUtility(s, {
100: '1',
}, [open]);
export default s;
._rotate\:90 { rotate: 90deg; }
._opacity\:100 { opacity: 1; }
._open\:rotate\:90 {
&:is([open], :popover-open) { rotate: 90deg; }
}
._open\:opacity\:100 {
&:is([open], :popover-open) { opacity: 1; }
}
<!-- Rotate icon when details is open -->
<details class="_open:rotate:90">
<summary>Click to expand</summary>
<p>Expanded content here.</p>
</details>
<!-- Style popover when open -->
<div popover class="_opacity:100 _open:opacity:100">
Popover content
</div>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
open | &:is([open], :popover-open) |
open modifier uses :is([open], :popover-open) to match both the <details> element's open attribute and the Popover API's :popover-open pseudo-class, giving you a single modifier for both patterns.useInertModifier
The useInertModifier() function creates a modifier that applies styles to inert elements and their descendants. The inert HTML attribute makes an element and all its descendants non-interactive and invisible to assistive technology.
import { styleframe } from "styleframe";
import { useOpacityUtility, usePointerEventsUtility } from "@styleframe/theme";
import { useInertModifier } from "@styleframe/theme";
const s = styleframe();
const inert = useInertModifier(s);
useOpacityUtility(s, {
50: '0.5',
}, [inert]);
usePointerEventsUtility(s, {
none: 'none',
}, [inert]);
export default s;
._opacity\:50 { opacity: 0.5; }
._pointer-events\:none { pointer-events: none; }
._inert\:opacity\:50 {
&:is([inert], [inert] *) { opacity: 0.5; }
}
._inert\:pointer-events\:none {
&:is([inert], [inert] *) { pointer-events: none; }
}
<!-- Dim and disable interaction for inert content -->
<div inert class="_inert:opacity:50 _inert:pointer-events:none">
<button>This button is non-interactive</button>
<a href="#">This link is non-interactive</a>
</div>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
inert | &:is([inert], [inert] *) |
inert modifier targets both the inert element itself and all its descendants using &:is([inert], [inert] *). This ensures that styles are applied consistently throughout the entire inert subtree.useOtherStateModifiers
The useOtherStateModifiers() function registers all other state modifiers at once and returns them as a destructurable object.
Returned Modifiers
| Key | Modifier Name | CSS Selector |
|---|---|---|
open | open | &:is([open], :popover-open) |
inert | inert | &:is([inert], [inert] *) |
Examples
Animated Details Element
import { styleframe } from "styleframe";
import { useRotateUtility, useTransitionDurationUtility } from "@styleframe/theme";
import { useOpenModifier, useMotionSafeModifier } from "@styleframe/theme";
const s = styleframe();
const open = useOpenModifier(s);
const motionSafe = useMotionSafeModifier(s);
useRotateUtility(s, {
0: '0deg',
90: '90deg',
}, [open]);
useTransitionDurationUtility(s, {
200: '200ms',
}, [motionSafe]);
export default s;
<details>
<summary>
<span class="_rotate:0 _open:rotate:90 _motion-safe:transition-duration:200">
▶
</span>
Toggle section
</summary>
<p>Section content revealed when opened.</p>
</details>
Modal with Inert Background
import { styleframe } from "styleframe";
import { useOpacityUtility, useBlurUtility } from "@styleframe/theme";
import { useInertModifier } from "@styleframe/theme";
const s = styleframe();
const inert = useInertModifier(s);
useOpacityUtility(s, {
50: '0.5',
}, [inert]);
useBlurUtility(s, {
sm: '4px',
}, [inert]);
export default s;
<!-- Background content becomes inert when modal is open -->
<main inert class="_inert:opacity:50 _inert:blur:sm">
<p>Page content (disabled while modal is open)</p>
</main>
<dialog open>
<p>Modal content (interactive)</p>
</dialog>
Best Practices
- Use
openfor native disclosure patterns: Style<details>and popover elements with theopenmodifier instead of JavaScript class toggling - Use
inertfor background content: When showing modals or dialogs, applyinertto background content for both accessibility and visual dimming - Combine with motion modifiers: Use
motion-safewithopentransitions for accessible animated disclosure widgets - Test popover support: The
:popover-openpseudo-class is a newer API; verify browser support for your target audience
FAQ
open modifier uses &:is([open], :popover-open) which matches both the <details> element's open attribute and the Popover API's :popover-open pseudo-class. This makes it work seamlessly with both patterns.inert attribute affects the entire subtree, making all descendants non-interactive. The modifier selector &:is([inert], [inert] *) matches both the inert element and its children, ensuring consistent styling throughout the disabled region.[open, hover] generates _open:property:value, _hover:property:value, and _open:hover:property:value. This allows for different hover effects when an element is open vs closed.