AccessibilityMenu
MD, under 1950B gzippedOne panel for larger text and higher contrast.
- Category
- Accessibility & preferences
- Budget
- md, several states or a live subscription
- Runtime deps
- None
Preview
This is the exact fixture the conformance suite renders in CI. Switch the language to see the component mirror and reformat.
Install
npx shadcn@latest add https://kata-ui-rho.vercel.app/r/accessibility-menu.jsonCopies the source into your project. Pulls in 4 primitives: popover, cn, switch, use-stored-value.
Source
"use client";
import { useEffect } from "react";
import { useStoredValue } from "../lib/use-stored-value";
import { Popover } from "./popover";
import { Switch } from "./switch";
const FONT_KEY = "anywhere-ui:font-scale";
const CONTRAST_KEY = "anywhere-ui:high-contrast";
/**
* One panel for the display adjustments people actually reach for on a page
* that does not offer its own: larger text and higher contrast. Both persist
* across visits and apply via `documentElement` so the whole page responds,
* not just this panel's own subtree.
*/
export function AccessibilityMenu({ label = "Accessibility" }: { label?: string }) {
const [storedFont, setStoredFont] = useStoredValue(FONT_KEY, "false");
const [storedContrast, setStoredContrast] = useStoredValue(CONTRAST_KEY, "false");
const largeText = storedFont === "true";
const highContrast = storedContrast === "true";
useEffect(() => {
document.documentElement.style.fontSize = largeText ? "112.5%" : "";
}, [largeText]);
useEffect(() => {
document.documentElement.dataset.highContrast = String(highContrast);
}, [highContrast]);
return (
<Popover trigger={(props) => <button type="button" {...props}>{label}</button>}>
<div className="flex flex-col gap-3">
<Switch checked={largeText} onCheckedChange={(v) => setStoredFont(String(v))} label="Larger text" />
<Switch checked={highContrast} onCheckedChange={(v) => setStoredContrast(String(v))} label="High contrast" />
</div>
</Popover>
);
}
What CI checks
- Bundled, minified and gzipped against its tier budget
- Audited by axe in the state previewed above
- Rendered through react-dom/server with no browser globals
- Scanned for network calls, dangerous sinks, and unguarded animation