Accessibility β
GCM has accessibility preferences layered two deep: every user can tune the app to their own needs (font size, contrast, motion, dyslexic font, etc.), and admins can set organization defaults that apply to anyone who hasn't customized their own. The same page handles both β the user section is at the top, the admin section at the bottom.
The accessibility module is a feature module, so it must be enabled in Modules for the page to appear in the sidebar.

Open the accessibility page β
Click Accessibility in the sidebar (or open it from inside Settings β Accessibility β the page renders identically from both entry points). The form is divided into four user-facing sections plus an admin section at the bottom.
Live preview card β
The card at the top of the page renders sample text, buttons, and links using your current pending preferences. As you tick toggles below, the preview updates in real time so you can see the effect before saving. The actual app chrome also updates immediately β pending changes apply to the DOM as you make them.
TIP
If a preference makes the app unreadable, click Discard in the page header (which appears as soon as you have unsaved changes) to revert. The preference is buffered in pending state until you click Save β so you can experiment safely.
Visual size β
UI scale β
A four-way picker (Default, Large, Larger, Largest) that scales everything in the UI. Defaults to whatever the browser zoom is set to; bump it up if 100% feels small.
The scale affects font sizes, padding, button heights, and icon sizes proportionally β so the layout doesn't break, it just gets bigger.
Reading comfort β
Line height β
Three options β Normal, Relaxed, Loose. Increases the vertical space between lines of text. Helpful for users with dyslexia or fatigue.
Letter spacing β
Toggle on to add ~0.05em between letters. Some users with dyslexia find this easier to scan.
Bold text β
Toggle on to make all body text semi-bold. Helps low-vision users. Avoid combining with high contrast if it makes text feel overwhelming.
Dyslexic font β
Switches the body font to OpenDyslexic β a typeface designed for readers with dyslexia, with weighted bottoms and disambiguated letterforms.
Color and contrast β
High contrast β
Toggle to switch to a high-contrast colour scheme β black backgrounds, white text, brighter highlights. Useful in bright light or for users with low vision.
Color blind mode β
A dropdown with options for protanopia (red-blind), deuteranopia (green-blind), and tritanopia (blue-blind). Each option applies a CSS filter to the entire app that shifts hues into a range the chosen colour-blind mode can distinguish. Pick None to disable.
Motion and navigation β
Reduced motion β
Disables animations and transitions throughout the app β page transitions, dropdown opens, toast slide-ins. Mirrors the browser's prefers-reduced-motion media query but overrides it explicitly. Helpful for users with vestibular disorders.
Highlight links β
Underlines every link in the app and uses a brighter colour. Helps users who use peripheral scanning to navigate.
Enhanced focus β
Replaces the default focus ring with a thicker, higher-contrast ring. Visible on every interactive element. Essential for keyboard users.
Large cursor β
Makes the system cursor larger when hovering inside the app. Works on Chromium-based browsers and Safari. Firefox doesn't support custom cursor sizing, so the toggle is a no-op there β the description text notes this.
Saving user preferences β
Click Save changes in the header bar (or at the bottom of the form). The pending state is committed to user_accessibility_preferences and the next time you sign in, the same preferences apply.
If you change your mind, click Discard to revert to whatever was last saved on the server. The DOM updates back to that state.
You can also click Reset to org defaults at the bottom to delete your user-level preferences entirely β the next page load uses whatever the admin set as defaults. Useful if you've been experimenting and want to start fresh.
Organization defaults (admins only) β
The admin section appears below a separator. These are the defaults that apply to any user who hasn't set their own preferences. They take effect for new users on first login, and for existing users who've reset to defaults.
The org-default panel is smaller than the user panel β only the most-requested defaults are exposed:
- UI scale
- High contrast
- Reduced motion
- Bold text
Defaults are saved as you toggle (no separate save button β updateOrgDefault writes immediately to org_accessibility_defaults). A user's own preferences always override the org default when set.
WARNING
Changing an org default doesn't override users who've already customized β they keep their own preferences. The default only affects users who haven't picked anything yet, or who hit Reset to org defaults.
How preferences are applied β
The useAccessibilityPrefs hook reads three sources and resolves them in priority order:
- User's saved preferences (
user_accessibility_preferences) - Org defaults (
org_accessibility_defaults) - System defaults (hard-coded sane values)
The resolved preference is applied to the DOM by adding classes to the <html> element (a11y-scale-large, a11y-high-contrast, etc.) and setting CSS custom properties. Tailwind utility classes then react to those β data-[a11y=high-contrast]:bg-black and so on.
This means preferences load before the first render β there's no flash of unstyled content even on a fresh login.
What's saved where β
user_accessibility_preferencesβ one row per user. Columns for each preference (ui_scale,line_height,letter_spacing, etc.). RLS scopes toauth.uid().org_accessibility_defaultsβ one row per org. Columns for the subset of preferences that have org-level defaults. RLS scopes bycurrent_org_id().
Common pitfalls β
"I set high contrast and now I can't see the colour pickers in Branding." Yes, that's the trade-off β high contrast overrides custom colours. Set your branding first, then turn on high contrast.
"I changed the dyslexic font but my receipt PDFs still use the default." PDFs are rendered server-side and don't pick up user accessibility prefs. Only the in-app rendering is affected.
"Reduced motion doesn't disable the toast animation." A small handful of essential micro-animations (the loading spinner, the unsaved-changes bar slide-in) ignore reduced motion because removing them removes useful feedback. Everything else respects the toggle.
"My org default for high contrast didn't apply to a colleague." They've already set their own preference (even implicitly β first-login defaults snapshot the org defaults at that moment). They can hit Reset to org defaults to pull in the new default.
