Accessibility
Overview
Accessibility utilities help you create inclusive web experiences by controlling visibility for screen readers and managing forced color adjustments for high contrast modes.
Why Use Accessibility Utilities?
Accessibility utilities help you:
- Support assistive technologies: Hide content visually while keeping it accessible to screen readers
- Honor user preferences: Respect forced color modes for users who need high contrast
- Maintain semantic HTML: Keep content accessible without compromising visual design
- Follow WCAG guidelines: Meet accessibility standards with proven utility patterns
useForcedColorAdjustUtility
The useForcedColorAdjustUtility() function creates utility classes for controlling how elements render in forced color modes (like Windows High Contrast Mode).
import { styleframe } from "styleframe";
import { useForcedColorAdjustUtility } from "@styleframe/theme";
const s = styleframe();
// Uses built-in defaults: auto, none
useForcedColorAdjustUtility(s);
export default s;
._forced-color-adjust\:auto {
forced-color-adjust: auto;
}
._forced-color-adjust\:none {
forced-color-adjust: none;
}
<!-- Preserve original colors in high contrast mode -->
<div class="_forced-color-adjust:none">
<span style="color: red;">Critical status indicator</span>
</div>
<!-- Allow browser to adjust colors (default behavior) -->
<div class="_forced-color-adjust:auto">
Regular content
</div>
Default Values
The useForcedColorAdjustUtility includes these default values:
| Key | Value | Description |
|---|---|---|
auto | auto | Allow browser to adjust colors in forced color mode |
none | none | Prevent color adjustments, preserving original colors |
none: Use forced-color-adjust: none sparingly on elements where color is essential for understanding (like data visualizations or status indicators).useSrOnlyUtility
The useSrOnlyUtility() function creates a utility class that visually hides content while keeping it accessible to screen readers.
import { styleframe } from "styleframe";
import { useSrOnlyUtility } from "@styleframe/theme";
const s = styleframe();
// Creates sr-only class with default: true
useSrOnlyUtility(s);
export default s;
._sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
<!-- Hide text visually but keep accessible to screen readers -->
<button>
<svg aria-hidden="true"><!-- icon --></svg>
<span class="_sr-only">Close menu</span>
</button>
<!-- Accessible form label -->
<label class="_sr-only" for="search">Search</label>
<input type="search" id="search" placeholder="Search...">
This pattern visually hides the element but keeps it in the accessibility tree so screen readers can announce it.
useNotSrOnlyUtility
The useNotSrOnlyUtility() function creates a utility class that undoes the sr-only styles, making content visible again.
import { styleframe } from "styleframe";
import { useSrOnlyUtility, useNotSrOnlyUtility } from "@styleframe/theme";
const s = styleframe();
useSrOnlyUtility(s);
useNotSrOnlyUtility(s);
export default s;
._sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
._not-sr-only {
position: static;
width: auto;
height: auto;
padding: 0;
margin: 0;
overflow: visible;
clip: auto;
white-space: normal;
}
<!-- Make sr-only content visible again -->
<a href="#main" class="_sr-only _focus:not-sr-only">
Skip to main content
</a>
<!-- Toggle visibility based on state -->
<span class="_sr-only _not-sr-only">Now visible</span>
This is useful when you want content to be screen-reader-only by default but visible on certain states (like focus).
Examples
Skip Link Pattern
A common accessibility pattern is a "skip to content" link that's only visible when focused:
import { styleframe } from "styleframe";
import { useSrOnlyUtility, useNotSrOnlyUtility } from "@styleframe/theme";
import { useColor } from "@styleframe/theme";
const s = styleframe();
const { ref, selector, modifier } = s;
const { colorPrimary } = useColor(s, { primary: '#006cff' } as const);
// Create accessibility utilities
useSrOnlyUtility(s);
useNotSrOnlyUtility(s);
// Create focus modifier
const focus = modifier('focus', ({ declarations }) => ({
'&:focus': declarations,
}));
// Apply not-sr-only on focus using a selector
selector('.skip-link', {
position: 'absolute',
width: '1px',
height: '1px',
padding: '0',
margin: '-1px',
overflow: 'hidden',
clip: 'rect(0, 0, 0, 0)',
whiteSpace: 'nowrap',
borderWidth: '0',
'&:focus': {
position: 'fixed',
top: '1rem',
left: '1rem',
width: 'auto',
height: 'auto',
padding: '1rem',
margin: '0',
overflow: 'visible',
clip: 'auto',
whiteSpace: 'normal',
backgroundColor: ref(colorPrimary),
color: 'white',
zIndex: '9999',
},
});
export default s;
._sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
._not-sr-only {
position: static;
width: auto;
height: auto;
padding: 0;
margin: 0;
overflow: visible;
clip: auto;
white-space: normal;
}
.skip-link {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.skip-link:focus {
position: fixed;
top: 1rem;
left: 1rem;
width: auto;
height: auto;
padding: 1rem;
margin: 0;
overflow: visible;
clip: auto;
white-space: normal;
background-color: var(--color--primary);
color: white;
z-index: 9999;
}
Icon Button with Accessible Label
import { styleframe } from "styleframe";
import { useSrOnlyUtility } from "@styleframe/theme";
const s = styleframe();
const { selector } = s;
useSrOnlyUtility(s);
// Style for icon button
selector('.icon-button', {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
padding: '0.5rem',
border: 'none',
borderRadius: '0.25rem',
cursor: 'pointer',
});
export default s;
._sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.icon-button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.5rem;
border: none;
border-radius: 0.25rem;
cursor: pointer;
}
Usage in HTML:
<button class="icon-button">
<svg aria-hidden="true" width="24" height="24">
<path d="..." />
</svg>
<span class="_sr-only">Delete item</span>
</button>
Best Practices
- Always provide text alternatives: Icon buttons, images, and decorative elements should have accessible text
- Use sr-only for context: Add screen-reader-only text when visual context isn't available to assistive technology users
- Test with screen readers: Verify your sr-only content is announced correctly
- Be careful with forced-color-adjust: Only disable it when color is critical to understanding
- Consider focus states: Make sr-only content visible on focus when appropriate (like skip links)
- Don't use display: none: It removes content from the accessibility tree entirely
FAQ
sr-only when you need full sentences or longer descriptions. Use aria-label for short, single-word or short-phrase labels. The sr-only technique is more flexible and can include complex content like instructions.display: none or visibility: hidden, which may be treated as hidden content by search engines.not-sr-only or custom focus styles, you can transition properties like opacity, transform, or position to create smooth reveal animations for skip links and similar patterns.none preserves your original colors, which is useful for elements where specific colors convey meaning.