| title | User Personalization and Accessibility Best Practices |
|---|
This document defines accessibility requirements for implementing user personalization features that empower users to customize their browsing experience based on their individual needs and preferences.
User choice matters. This guidance explains how to provide legitimate personalization options while avoiding problematic accessibility overlays, ensuring WCAG 2.2 Level AA compliance and respecting user preferences.
All users should be able to customize content presentation to meet their individual needs without compromising information, functionality, or accessibility. Personalization features must complement, not replace, proper accessible design.
Accessibility overlays are third-party tools that attempt to automatically "fix" accessibility issues on websites through JavaScript injections, typically triggered by a widget icon. They promise instant accessibility compliance without addressing underlying code issues.
Do NOT use accessibility overlays as a substitute for proper accessible design.
Accessibility overlays have significant limitations:
- They cannot fix underlying structural accessibility issues
- They often create new barriers for assistive technology users
- They provide a false sense of compliance
- They may interfere with users' own assistive technologies
- They don't address server-side or backend accessibility
- They can create inconsistent experiences across different pages
Key resource:
- Overlay Fact Sheet - Comprehensive documentation of overlay problems, signed by over 800 accessibility professionals
There is ONE acceptable use case for overlay-like technology:
Custom remediation for legacy systems where:
- The underlying codebase cannot be modified (e.g., vendor-locked legacy systems)
- The remediation is developed specifically for your site's known issues
- It's a temporary solution while planning proper accessibility fixes
- Users are informed it's a stopgap measure
- Active work continues toward proper accessible implementation
As noted in the Web Almanac 2024 Accessibility Chapter (User Personalization Widgets and Overlay Remediation section):
"Custom remediation for legacy sites can be fine when implemented thoughtfully as a bridge to proper accessibility."
Instead of overlays, implement user personalization widgets that allow users to adjust presentation without claiming to "fix" accessibility.
Accessibility overlay: Claims to make site accessible, injects fixes Personalization widget: Offers user preferences, doesn't claim compliance
Browsers have excellent built-in personalization tools, but many users don't know about them. Personalization widgets can:
- Make browser features more discoverable
- Provide site-specific preferences that persist
- Offer additional customization beyond browser defaults
- Help users who don't have their own assistive technology in that environment
- Benefit users not actively using assistive technology
Modern CSS provides powerful media queries that respect user preferences at the browser level.
/* User prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* User prefers dark color scheme */
@media (prefers-color-scheme: dark) {
:root {
--color-text: #e8e8e8;
--color-background: #1a1a1a;
}
}
/* User prefers high contrast */
@media (prefers-contrast: more) {
:root {
--color-text: #000000;
--color-background: #ffffff;
--border-width: 2px;
}
}
/* User has forced colors mode active (Windows High Contrast) */
@media (forced-colors: active) {
.custom-focus-indicator {
forced-color-adjust: none;
outline: 2px solid CanvasText;
}
}
/* User prefers reduced transparency */
@media (prefers-reduced-transparency: reduce) {
.modal-overlay {
opacity: 1;
background-color: var(--color-background);
}
}Browsers allow font size adjustment, but many users prefer visible controls.
Pattern: A+/A- buttons
<div class="font-size-controls" role="group" aria-label="Font size controls">
<button
id="decrease-font"
aria-label="Decrease font size"
aria-describedby="font-size-status">
A-
</button>
<button
id="reset-font"
aria-label="Reset font size to default"
aria-describedby="font-size-status">
A
</button>
<button
id="increase-font"
aria-label="Increase font size"
aria-describedby="font-size-status">
A+
</button>
<div id="font-size-status" class="visually-hidden" role="status" aria-live="polite"></div>
</div>// Font size adjustment
const fontSizes = ['small', 'medium', 'large', 'x-large'];
let currentSize = 1; // medium
function adjustFontSize(delta) {
currentSize = Math.max(0, Math.min(fontSizes.length - 1, currentSize + delta));
document.documentElement.style.fontSize = fontSizes[currentSize];
localStorage.setItem('preferredFontSize', currentSize);
// Announce change to screen readers
const status = document.getElementById('font-size-status');
status.textContent = `Font size changed to ${fontSizes[currentSize]}`;
}
// Restore preference on load
const savedSize = localStorage.getItem('preferredFontSize');
if (savedSize !== null) {
currentSize = parseInt(savedSize);
document.documentElement.style.fontSize = fontSizes[currentSize];
}Key requirements:
- Provide clear labels and ARIA descriptions
- Announce changes to screen readers via
aria-liveregion - Persist preferences using
localStorageor cookies - Allow reset to default
- Don't prevent browser zoom from working
Research on dyslexia fonts is mixed, but user choice is valuable.
Studies suggest mixed results:
- PMC Article: Typography for Dyslexia
- The Dyslexia Friendly Font Myth
- Do Dyslexia Fonts Actually Work?
Recommendation: Offer choice, let users decide what works for them.
<div class="font-family-selector">
<label for="font-select">Font style:</label>
<select id="font-select" aria-describedby="font-change-status">
<option value="default">Default (brand font)</option>
<option value="sans-serif">Sans-serif</option>
<option value="serif">Serif</option>
<option value="monospace">Monospace</option>
<option value="comic-sans">Comic Sans</option>
<option value="opendyslexic">OpenDyslexic</option>
</select>
<div id="font-change-status" class="visually-hidden" role="status" aria-live="polite"></div>
</div>:root {
--font-default: "Brand Font", system-ui, sans-serif;
}
[data-font="sans-serif"] {
--font-default: system-ui, -apple-system, sans-serif;
}
[data-font="serif"] {
--font-default: Georgia, "Times New Roman", serif;
}
[data-font="monospace"] {
--font-default: "Courier New", monospace;
}
[data-font="comic-sans"] {
--font-default: "Comic Sans MS", cursive, sans-serif;
}
[data-font="opendyslexic"] {
--font-default: "OpenDyslexic", sans-serif;
}
body {
font-family: var(--font-default);
}Important notes:
- Respect brand identity by making default your brand font
- Ensure all font options remain readable and accessible
- Test contrast in all font options
- Allow users to revert to default
<fieldset class="reading-preferences">
<legend>Reading preferences</legend>
<div class="preference-option">
<label for="line-spacing">Line spacing:</label>
<select id="line-spacing">
<option value="normal">Normal</option>
<option value="relaxed">Relaxed (1.5)</option>
<option value="loose">Loose (2.0)</option>
</select>
</div>
<div class="preference-option">
<label for="letter-spacing">Letter spacing:</label>
<select id="letter-spacing">
<option value="normal">Normal</option>
<option value="wide">Wide</option>
</select>
</div>
<div class="preference-option">
<label for="word-spacing">Word spacing:</label>
<select id="word-spacing">
<option value="normal">Normal</option>
<option value="wide">Wide</option>
</select>
</div>
</fieldset>WCAG 1.4.12 Text Spacing requirements: Users must be able to set:
- Line height to at least 1.5 times font size
- Paragraph spacing to at least 2 times font size
- Letter spacing to at least 0.12 times font size
- Word spacing to at least 0.16 times font size
See Light/Dark Mode Accessibility Best Practices for comprehensive guidance on theme switching.
<fieldset class="contrast-preferences">
<legend>Color theme:</legend>
<div role="radiogroup" aria-labelledby="theme-legend">
<input type="radio" id="theme-auto" name="theme" value="auto" checked>
<label for="theme-auto">Auto (follow system)</label>
<input type="radio" id="theme-light" name="theme" value="light">
<label for="theme-light">Light</label>
<input type="radio" id="theme-dark" name="theme" value="dark">
<label for="theme-dark">Dark</label>
<input type="radio" id="theme-high-contrast" name="theme" value="high-contrast">
<label for="theme-high-contrast">High contrast</label>
</div>
</fieldset>The Fluid Project is an open-source community developing inclusive design approaches and tools. Their Preferences Framework (also known as UI Options) demonstrates best-in-class user personalization and serves as a reference implementation for accessibility-focused customization.
The Fluid Project is housed at the Inclusive Design Research Centre (IDRC) at OCAD University in Toronto. The project focuses on creating flexible, customizable, and accessible user experiences through open-source software development and research.
Key Fluid Project GitHub Organization: https://github.com/fluid-project
Fluid Infusion is a JavaScript framework for building accessible web applications. Its Preferences Framework (UI Options) component provides a comprehensive user preference system that can be integrated into any website.
Core Infusion Projects:
- Fluid Infusion - Main framework and preferences system
- UI Options - Standalone preferences component
- Fluid Skinning System (FSS) - Accessible CSS framework that works with preferences
- Infusion Documentation - Comprehensive documentation and guides
Text Customization:
- Text size adjustment (5 levels)
- Line spacing controls
- Font family selection (including OpenDyslexic)
- Letter spacing adjustment
- Word spacing adjustment
Visual Customization:
- High contrast themes (multiple options)
- Dark mode / light mode
- Background color options
- Custom color schemes
Navigation Enhancement:
- Table of contents generation
- Keyboard shortcuts
- Enhanced focus indicators
Input Enhancement:
- Enhanced form inputs
- Self-voicing (text-to-speech)
- Highlight selection
Persistence:
- All preferences stored in cookies
- Preferences persist across sessions
- Works across multiple pages
- Fluid Infusion Preferences Framework Documentation
- UI Options Demo
- Fluid Project GitHub Organization
- Fluid Project Website
- Inclusive Design Research Centre
Implementation approach:
<!-- Preference toggle button with universal accessibility icon -->
<button
id="preferences-toggle"
aria-label="Open accessibility preferences"
aria-expanded="false"
aria-controls="preferences-panel">
<svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24">
<!-- Universal accessibility icon -->
<path d="M12 2C10.9 2 10 2.9 10 4s.9 2 2 2 2-.9 2-2-.9-2-2-2zm8 7h-3.18C16.4 7.84 15.3 7 14 7h-4c-1.3 0-2.4.84-2.82 2H4c-.55 0-1 .45-1 1s.45 1 1 1h3.18c.42 1.16 1.52 2 2.82 2h1v6c0 .55.45 1 1 1s1-.45 1-1v-6h1c1.3 0 2.4-.84 2.82-2H20c.55 0 1-.45 1-1s-.45-1-1-1z"/>
</svg>
</button>
<div
id="preferences-panel"
class="preferences-panel"
hidden
role="dialog"
aria-labelledby="preferences-title"
aria-modal="true">
<h2 id="preferences-title">Display Preferences</h2>
<p class="preferences-description">
These settings let you customize how you view this site.
Your preferences will be saved for future visits.
</p>
<!-- Font size controls -->
<!-- Line spacing controls -->
<!-- Font family selection -->
<!-- Contrast theme selection -->
<!-- Additional preferences -->
<div class="preferences-actions">
<button id="save-preferences">Save and Close</button>
<button id="reset-preferences">Reset to Defaults</button>
</div>
</div>Critical distinction:
Include clear messaging that this is NOT an accessibility overlay:
<div class="preferences-notice" role="note">
<p>
<strong>Note:</strong> This is a preference editor, not an accessibility overlay.
It allows you to customize how you view content without claiming to make
inaccessible content accessible. Learn more about
<a href="https://overlayfactsheet.com/en/">why accessibility overlays are problematic</a>.
</p>
</div>SkipTo by PayPal provides a keyboard-accessible menu of landmarks and headings, helping users navigate long pages.
Why SkipTo matters:
Browsers should expose page structure natively but often don't provide an easy way for all users to jump to sections. SkipTo fills this gap.
Key resource:
Implementation:
<!-- Include SkipTo -->
<script src="https://paypal.github.io/skipto/downloads/js/skipto.min.js"></script>
<!-- Configure SkipTo -->
<script>
var SkipToConfig = {
settings: {
skipTo: {
displayOption: 'popup',
attachElement: 'body',
colorTheme: 'auto'
}
}
};
</script>Features:
- Keyboard shortcut (Alt+0 or Option+0) to open menu
- Lists all landmarks and headings
- Visual and screen reader accessible
- Customizable appearance
- Works with existing site structure
Integration with preferences:
Consider adding SkipTo toggle to your preferences panel:
<div class="preference-option">
<input type="checkbox" id="enable-skipto" checked>
<label for="enable-skipto">
Enable SkipTo navigation menu (Alt+0)
</label>
<p class="preference-description">
Provides quick keyboard access to page sections and headings
</p>
</div>Accessibility overlays have popularized a universal accessibility icon (person in a circle). Use this icon for legitimate personalization features to:
- Create visual consistency users recognize
- Signal where accessibility preferences are located
- Distinguish your preferences from page content
Icon pattern:
<button
class="accessibility-preferences-btn"
aria-label="Open display preferences">
<!-- SVG icon with proper ARIA -->
<svg
role="img"
aria-hidden="true"
focusable="false"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="32"
height="32">
<title>Accessibility</title>
<path d="M12 2C10.9 2 10 2.9 10 4s.9 2 2 2 2-.9 2-2-.9-2-2-2zm8 7h-3.18C16.4 7.84 15.3 7 14 7h-4c-1.3 0-2.4.84-2.82 2H4c-.55 0-1 .45-1 1s.45 1 1 1h3.18c.42 1.16 1.52 2 2.82 2h1v6c0 .55.45 1 1 1s1-.45 1-1v-6h1c1.3 0 2.4-.84 2.82-2H20c.55 0 1-.45 1-1s-.45-1-1-1z" fill="currentColor"/>
</svg>
<span class="btn-text">Preferences</span>
</button>Placement:
- Consistent location (typically top-right corner)
- Visible and easily discoverable
- Large enough touch target (44×44px minimum)
- Clear focus indicator
When implementing user personalization:
- Do NOT use third-party accessibility overlays
- Reference Overlay Fact Sheet to explain why
- Implement CSS media queries for user preferences
- Provide visible preference controls for users who need them
- Use proper ARIA for all controls (labels, live regions, states)
- Persist preferences using
localStorageor cookies - Announce changes to screen readers
- Test with keyboard-only navigation
- Test with screen readers (NVDA, JAWS, VoiceOver)
- Ensure preferences don't break responsive design
- Verify all preference combinations maintain WCAG 2.2 AA contrast
- Include clear messaging that this is NOT an overlay
- Consider including SkipTo for page navigation
- Use recognizable accessibility icon for preferences button
- Provide reset option to return to defaults
- Document that users' own assistive technology takes precedence
// Example: Test font size preferences persist
describe('Font size preferences', () => {
it('should persist font size selection', () => {
cy.visit('/');
// Increase font size
cy.get('#increase-font').click();
// Verify change applied
cy.get('html').should('have.css', 'font-size', 'large');
// Verify localStorage updated
cy.window().then((win) => {
expect(win.localStorage.getItem('preferredFontSize')).to.eq('2');
});
// Reload page
cy.reload();
// Verify preference persisted
cy.get('html').should('have.css', 'font-size', 'large');
});
it('should announce changes to screen readers', () => {
cy.visit('/');
cy.get('#increase-font').click();
// Check aria-live region updated
cy.get('#font-size-status')
.should('have.attr', 'role', 'status')
.and('contain', 'Font size changed');
});
});-
Keyboard accessibility:
- Tab through all preference controls
- Verify focus indicators visible
- Test keyboard activation (Enter/Space)
- Verify keyboard shortcuts (if implemented)
-
Screen reader testing:
- Verify all controls properly labeled
- Check that changes are announced
- Test with NVDA (Windows), JAWS (Windows), VoiceOver (macOS/iOS)
-
Preference persistence:
- Set preferences, refresh page
- Close browser, reopen
- Test in incognito/private mode
-
Contrast validation:
- Test all theme combinations meet WCAG 2.2 AA
- Verify in forced-colors mode (Windows High Contrast)
- Check with color blindness simulators
-
Mobile testing:
- Touch targets adequate (44×44px minimum)
- Preferences panel accessible on small screens
- Works with zoom enabled
- Light/Dark Mode Accessibility Best Practices - Comprehensive theming guidance
- Keyboard Accessibility Best Practices - Keyboard interaction patterns
- Forms Accessibility Best Practices - Form control accessibility
- Overlay Fact Sheet - Why overlays are problematic
- Fluid Infusion Preferences Framework - Reference implementation
- PayPal SkipTo - Page navigation tool
- MDN: CSS Media Queries for Accessibility
- Web Almanac 2024: Accessibility Chapter
This guide references normative WCAG 2.2 requirements available in machine-readable YAML format via wai-yaml-ld:
- WCAG 2.2 normative:
wcag-2.2-normative.yaml - WCAG 1.4.12 Text Spacing:
wcag-2.2-normative.yaml#1.4.12 - WCAG 1.4.3 Contrast (Minimum):
wcag-2.2-normative.yaml#1.4.3 - WCAG 2.4.1 Bypass Blocks:
wcag-2.2-normative.yaml#2.4.1(related to SkipTo) - ARIA authoring practices:
wai-aria-informative.yaml
User personalization done right empowers users without claiming to fix accessibility. The key distinctions:
❌ Don't:
- Use third-party accessibility overlays
- Claim personalization "makes site accessible"
- Replace proper accessible design with widgets
- Interfere with users' assistive technology
✅ Do:
- Respect CSS media queries for user preferences
- Provide visible preference controls as helpers
- Be transparent about what features do
- Persist user choices
- Test thoroughly with assistive technology
- Reference the Overlay Fact Sheet
- Consider SkipTo for navigation
- Use clear labeling and announcements
User choice is powerful. Give users control over their experience while maintaining a solid accessible foundation.