Skip to content

AccessibilityMenu

MD · 1950B gzip budget

One panel for larger text and higher contrast.

Category
Accessibility & preferences
Budget
md, several states or a live subscription
External runtime
React / React DOM only
axe fixtureSSR rendersource scanbundle budget

Preview

Live fixture
Preview locale

Try the component here. Locale-aware formatting and direction follow the selected language; example content and labels are not automatically translated. Overlay demos use interactive launchers.

Install

npx shadcn@latest add https://gear5-ui.vercel.app/r/accessibility-menu.json

Copies the source into your project. Pulls in 4 shared registry items: popover, cn, switch, use-stored-value.

New here? Set up Tailwind and import aliases first →

Source & props

Edit on GitHub ↗

Prop types and inline documentation are included below. This is repository source; the installer rewrites shared imports for your project.

"use client";

import { useEffect } from "react";
import { useStoredValue } from "../lib/use-stored-value";
import { Popover } from "./popover";
import { Switch } from "./switch";

const FONT_KEY = "gear5-ui:font-scale";
const CONTRAST_KEY = "gear5-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

Quality contract
  • 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