Contrast Mode Switch
A free React theme switcher for high contrast mode, with a morphing icon and a stable accessible name.
Deck display
Standard, for the chart table and the run home after dark.
Next tidal gate
Barmouth bar carries 2.1 m at 06:40. Slip St Tudwals by 03:15 and five knots over the ground puts you on the bar with an hour of flood still under you.
Position
GPS and AIS agree to 3 m
Customise
Code
const [highContrast, setHighContrast] = useState(false);
<ThemeContrastSwitch
highContrast={highContrast}
onHighContrastChange={setHighContrast}
/>Contrast Mode Switch is a free React high contrast toggle for Next.js, styled with Tailwind and animated with Motion, built as a single native switch button whose thumb translates across the track while a decorative glyph plays a rotate, iris or crossfade transition. It ships with a stable accessible name, separate state text, and a visible focus ring, so it drops straight into a settings panel or preferences menu.
The switch is fully controlled and has no side effects of its own: pass the current highContrast value and it calls onHighContrastChange with the opposite one on activation, never touching document classes, storage or a media query. Wiring the result to an actual contrast mode, a CSS class, or nothing at all stays entirely in your app. Reduced motion snaps the thumb and glyph to their final state instantly, with the controlled semantics unchanged.
Install
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/theme-contrast-switchUsage
"use client";
import { useState } from "react";
import { ThemeContrastSwitch } from "@/components/theme-contrast-switch";
export default function Page() {
const [highContrast, setHighContrast] = useState(false);
return (
<ThemeContrastSwitch
highContrast={highContrast}
onHighContrastChange={setHighContrast}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| highContrast | boolean | required | Controlled contrast state. |
| onHighContrastChange | (value: boolean) => void | required | Called after explicit user activation with the opposite value. |
| size | number | 44 | Control height in CSS pixels, clamped to 36 through 64. |
| orientation | "icon-only" | "labelled" | "labelled" | Visible-label treatment for the current-state text. |
| iconMotion | "rotate" | "iris" | "swap" | "iris" | Decorative state-transition style for the glyph. |
| transitionDuration | number | 0.3 | Non-spring transition duration in seconds, clamped to 0 through 0.7. |
| radius | number | 999 | Track and thumb corner radius in CSS pixels, clamped to 0 through 999. |
| className | string | undefined | Additional classes merged onto the switch button. |
Customisation
Retheme the checked track and thumb border with contrast-switch-accent, the checked glyph with contrast-switch-accent-text, the resting track with contrast-switch-track, and the shared hover and focus-visible ring with contrast-switch-focus. Every token defaults to a semantic token, so retheming is optional:
.te-theme-contrast-switch {
--contrast-switch-accent: var(--ring);
--contrast-switch-accent-text: var(--primary-accent);
--contrast-switch-track: var(--surface);
--contrast-switch-focus: var(--primary-accent);
}Frequently asked questions
- Is Contrast Mode Switch free for commercial use?
- Yes. Contrast Mode Switch is MIT licensed, so you can use it in personal and commercial projects without attribution.
- Is Contrast Mode Switch accessible and performant?
- Yes. It renders one native switch button with an accurate checked state, a stable accessible name that never changes with state, and separate description text announcing the current state. State is shown through thumb position, glyph shape and that text together, never colour alone, and hover and focus-visible receive the same ring treatment. The thumb and every icon mode animate only transform and opacity, and reduced motion snaps every transition to its final state instantly with identical controlled semantics.
- How do I customise the Contrast Mode Switch?
- Use size, orientation, iconMotion, transitionDuration and radius to tune the control's scale, label treatment, glyph transition and shape. Use the four component tokens (contrast-switch-accent, contrast-switch-accent-text, contrast-switch-track, contrast-switch-focus) to retheme the checked accent, the checked glyph, the resting track and the focus ring without touching the component source. The accent and its glyph are separate tokens because a fill rung and a text rung cannot share one value and both clear AA.