ARIA State Modifiers
useAriaStateModifiers is included in the Modifiers Preset (useModifiersPreset) and you can configure it through the preset's ariaStates option. For most projects, applying it via the preset is the recommended approach.Overview
ARIA state modifiers let you apply utility styles conditionally based on ARIA attribute values. They wrap utility declarations in attribute selectors like [aria-expanded="true"], generating variant utility classes that respond to accessibility state changes.
Why Use ARIA State Modifiers?
ARIA state modifiers help you:
- Style accessible components: Apply styles based on ARIA attributes used by screen readers
- Avoid JavaScript class toggling: Let ARIA attributes drive both accessibility and visual presentation
- Build inclusive UIs: Keep visual state in sync with the accessibility tree
- Follow WAI-ARIA patterns: Style disclosure widgets, tabs, accordions, and more using standard ARIA attributes
useAriaBusyModifier
The useAriaBusyModifier() function creates a modifier that applies styles when aria-busy="true".
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-busy | &[aria-busy="true"] |
useAriaCheckedModifier
The useAriaCheckedModifier() function creates a modifier that applies styles when aria-checked="true".
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-checked | &[aria-checked="true"] |
useAriaDisabledModifier
The useAriaDisabledModifier() function creates a modifier that applies styles when aria-disabled="true".
import { styleframe } from "styleframe";
import { useOpacityUtility, useCursorUtility } from "@styleframe/theme";
import { useAriaDisabledModifier } from "@styleframe/theme";
const s = styleframe();
const ariaDisabled = useAriaDisabledModifier(s);
useOpacityUtility(s, { 50: '0.5' }, [ariaDisabled]);
useCursorUtility(s, { 'not-allowed': 'not-allowed' }, [ariaDisabled]);
export default s;
._opacity\:50 { opacity: 0.5; }
._cursor\:not-allowed { cursor: not-allowed; }
._aria-disabled\:opacity\:50 {
&[aria-disabled="true"] { opacity: 0.5; }
}
._aria-disabled\:cursor\:not-allowed {
&[aria-disabled="true"] { cursor: not-allowed; }
}
<!-- Style custom disabled button -->
<div
role="button"
aria-disabled="true"
class="_aria-disabled:opacity:50 _aria-disabled:cursor:not-allowed"
>
Disabled action
</div>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-disabled | &[aria-disabled="true"] |
useAriaExpandedModifier
The useAriaExpandedModifier() function creates a modifier that applies styles when aria-expanded="true".
import { styleframe } from "styleframe";
import { useDisplayUtility, useRotateUtility } from "@styleframe/theme";
import { useAriaExpandedModifier } from "@styleframe/theme";
const s = styleframe();
const ariaExpanded = useAriaExpandedModifier(s);
useDisplayUtility(s, {
block: 'block',
none: 'none',
}, [ariaExpanded]);
useRotateUtility(s, {
180: '180deg',
}, [ariaExpanded]);
export default s;
._display\:block { display: block; }
._display\:none { display: none; }
._rotate\:180 { rotate: 180deg; }
._aria-expanded\:display\:block {
&[aria-expanded="true"] { display: block; }
}
._aria-expanded\:rotate\:180 {
&[aria-expanded="true"] { rotate: 180deg; }
}
<!-- Accordion trigger with rotating icon -->
<button aria-expanded="false" class="_aria-expanded:rotate:180">
<span>Toggle section</span>
<svg class="icon"><!-- chevron icon --></svg>
</button>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-expanded | &[aria-expanded="true"] |
useAriaHiddenModifier
The useAriaHiddenModifier() function creates a modifier that applies styles when aria-hidden="true".
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-hidden | &[aria-hidden="true"] |
useAriaPressedModifier
The useAriaPressedModifier() function creates a modifier that applies styles when aria-pressed="true".
import { styleframe } from "styleframe";
import { useColorDesignTokens } from "@styleframe/theme";
import { useBackgroundColorUtility } from "@styleframe/theme";
import { useAriaPressedModifier } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColorDesignTokens(s, { primary: '#006cff' } as const);
const ariaPressed = useAriaPressedModifier(s);
useBackgroundColorUtility(s, {
primary: ref(colorPrimary),
muted: '#e9ecef',
}, [ariaPressed]);
export default s;
._background-color\:primary { background-color: var(--color--primary); }
._background-color\:muted { background-color: #e9ecef; }
._aria-pressed\:background-color\:primary {
&[aria-pressed="true"] { background-color: var(--color--primary); }
}
<!-- Toggle button -->
<button
role="button"
aria-pressed="false"
class="_background-color:muted _aria-pressed:background-color:primary"
>
Toggle
</button>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-pressed | &[aria-pressed="true"] |
useAriaReadonlyModifier
The useAriaReadonlyModifier() function creates a modifier that applies styles when aria-readonly="true".
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-readonly | &[aria-readonly="true"] |
useAriaRequiredModifier
The useAriaRequiredModifier() function creates a modifier that applies styles when aria-required="true".
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-required | &[aria-required="true"] |
useAriaSelectedModifier
The useAriaSelectedModifier() function creates a modifier that applies styles when aria-selected="true".
import { styleframe } from "styleframe";
import { useColorDesignTokens } from "@styleframe/theme";
import { useBackgroundColorUtility, useBorderColorUtility } from "@styleframe/theme";
import { useAriaSelectedModifier } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColorDesignTokens(s, { primary: '#006cff' } as const);
const ariaSelected = useAriaSelectedModifier(s);
useBackgroundColorUtility(s, {
primary: ref(colorPrimary),
white: '#ffffff',
}, [ariaSelected]);
useBorderColorUtility(s, {
primary: ref(colorPrimary),
}, [ariaSelected]);
export default s;
._aria-selected\:background-color\:primary {
&[aria-selected="true"] { background-color: var(--color--primary); }
}
._aria-selected\:border-color\:primary {
&[aria-selected="true"] { border-color: var(--color--primary); }
}
<!-- Tab navigation -->
<div role="tablist">
<button role="tab" aria-selected="true"
class="_background-color:white _aria-selected:background-color:primary">
Tab 1
</button>
<button role="tab" aria-selected="false"
class="_background-color:white _aria-selected:background-color:primary">
Tab 2
</button>
</div>
CSS Selector
| Modifier Name | CSS Selector |
|---|---|
aria-selected | &[aria-selected="true"] |
useAriaStateModifiers
The useAriaStateModifiers() function registers all ARIA state modifiers at once and returns them as a destructurable object.
Returned Modifiers
| Key | Modifier Name | CSS Selector |
|---|---|---|
ariaBusy | aria-busy | &[aria-busy="true"] |
ariaChecked | aria-checked | &[aria-checked="true"] |
ariaDisabled | aria-disabled | &[aria-disabled="true"] |
ariaExpanded | aria-expanded | &[aria-expanded="true"] |
ariaHidden | aria-hidden | &[aria-hidden="true"] |
ariaPressed | aria-pressed | &[aria-pressed="true"] |
ariaReadonly | aria-readonly | &[aria-readonly="true"] |
ariaRequired | aria-required | &[aria-required="true"] |
ariaSelected | aria-selected | &[aria-selected="true"] |
Examples
Accessible Accordion
import { styleframe } from "styleframe";
import { useDisplayUtility, useRotateUtility, useOpacityUtility } from "@styleframe/theme";
import { useAriaStateModifiers } from "@styleframe/theme";
const s = styleframe();
const { ariaExpanded, ariaHidden } = useAriaStateModifiers(s);
useDisplayUtility(s, {
block: 'block',
none: 'none',
}, [ariaExpanded, ariaHidden]);
useRotateUtility(s, {
180: '180deg',
}, [ariaExpanded]);
export default s;
<div class="accordion">
<button aria-expanded="true" class="_aria-expanded:rotate:180">
Section 1 <span class="chevron">▼</span>
</button>
<div aria-hidden="false">
<p>Accordion content here.</p>
</div>
</div>
Tab Panel with Selected State
import { styleframe } from "styleframe";
import { useColorDesignTokens } from "@styleframe/theme";
import {
useBackgroundColorUtility,
useTextColorUtility,
useBorderColorUtility,
} from "@styleframe/theme";
import { useAriaSelectedModifier } from "@styleframe/theme";
const s = styleframe();
const { ref } = s;
const { colorPrimary } = useColorDesignTokens(s, { primary: '#006cff' } as const);
const ariaSelected = useAriaSelectedModifier(s);
useBackgroundColorUtility(s, {
primary: ref(colorPrimary),
transparent: 'transparent',
}, [ariaSelected]);
useTextColorUtility(s, {
white: '#ffffff',
dark: '#212529',
}, [ariaSelected]);
useBorderColorUtility(s, {
primary: ref(colorPrimary),
transparent: 'transparent',
}, [ariaSelected]);
export default s;
<div role="tablist">
<button
role="tab"
aria-selected="true"
class="_background-color:transparent _aria-selected:background-color:primary _text-color:dark _aria-selected:text-color:white"
>
Active Tab
</button>
<button
role="tab"
aria-selected="false"
class="_background-color:transparent _aria-selected:background-color:primary _text-color:dark _aria-selected:text-color:white"
>
Inactive Tab
</button>
</div>
Best Practices
- Use ARIA modifiers for custom components: Native form elements have built-in pseudo-classes; use ARIA modifiers for custom widgets
- Keep ARIA attributes in sync: Ensure JavaScript updates ARIA attributes so styles reflect the current state
- Prefer semantic HTML: When possible, use native elements (like
<details>) that have built-in ARIA support - Test with screen readers: Verify that ARIA attributes are correctly announced alongside the visual changes
- Use aria-expanded for disclosure widgets: Accordions, dropdowns, and menus should use
aria-expandedfor both accessibility and styling
FAQ
:disabled, :checked) for native form elements like <input>, <select>, and <button>. Use ARIA state modifiers for custom components built with <div>, <span>, or other non-form elements that need aria-* attributes for accessibility.[aria-*="true"]) that work with any HTML element. The selector matches as long as the element has the corresponding ARIA attribute set to "true".[ariaExpanded, hover] generates _aria-expanded:property:value, _hover:property:value, and combined variants for nuanced interactive styling.