UI Components Medium
High Contrast Toggle
Accessibility toolbar with high contrast mode, dyslexia-friendly font, and large text toggle. Persists to localStorage.
Open in Lab
MCP
vanilla-js css
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--bg: #0d1117;
--surface: #161b22;
--border: #21262d;
--text: #e6edf3;
--text-muted: #8b949e;
--accent: #6366f1;
--card-bg: #1c2128;
--font-size: 14px;
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
/* High contrast theme */
[data-theme~="high-contrast"] {
--bg: #000;
--surface: #0a0a0a;
--border: #fff;
--text: #fff;
--text-muted: #ddd;
--accent: #ffff00;
--card-bg: #111;
}
/* Dyslexia font (simulated with spacing) */
[data-theme~="dyslexia"] {
--font-family: "Comic Sans MS", "Chalkboard SE", cursive;
letter-spacing: 0.05em;
word-spacing: 0.12em;
line-height: 1.9;
}
/* Large text */
[data-theme~="large-text"] {
--font-size: 18px;
}
body {
font-family: var(--font-family);
font-size: var(--font-size);
background: var(--bg);
color: var(--text);
min-height: 100vh;
padding: 32px 16px;
display: flex;
justify-content: center;
transition: background 0.3s, color 0.3s;
}
.demo {
width: 100%;
max-width: 620px;
display: flex;
flex-direction: column;
gap: 20px;
}
/* Settings bar */
.settings-bar {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 18px;
transition: background 0.3s, border-color 0.3s;
}
.settings-title {
font-size: 15px;
font-weight: 700;
color: var(--text);
margin-bottom: 16px;
}
.settings-grid {
display: flex;
flex-direction: column;
gap: 12px;
}
.setting-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.setting-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.setting-label {
font-size: var(--font-size);
font-weight: 600;
color: var(--text);
}
.setting-desc {
font-size: 12px;
color: var(--text-muted);
}
/* Toggle switch */
.toggle-switch {
width: 48px;
height: 26px;
background: var(--border);
border: 2px solid var(--border);
border-radius: 13px;
cursor: pointer;
position: relative;
flex-shrink: 0;
transition: background 0.2s, border-color 0.2s;
}
.toggle-switch[aria-checked="true"] {
background: var(--accent);
border-color: var(--accent);
}
.toggle-switch:focus-visible {
outline: 2.5px solid var(--accent);
outline-offset: 2px;
}
.toggle-knob {
position: absolute;
top: 3px;
left: 3px;
width: 16px;
height: 16px;
background: #fff;
border-radius: 50%;
transition: transform 0.2s;
}
.toggle-switch[aria-checked="true"] .toggle-knob {
transform: translateX(22px);
}
/* Preview area */
.preview-area {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 14px;
transition: all 0.3s;
}
.preview-heading {
font-size: 16px;
font-weight: 700;
color: var(--text);
}
.preview-text {
font-size: var(--font-size);
color: var(--text);
line-height: 1.7;
}
.preview-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 14px 16px;
display: flex;
flex-direction: column;
gap: 8px;
}
.preview-card-title {
font-size: var(--font-size);
font-weight: 700;
color: var(--text);
}
.preview-card-body {
font-size: 13px;
color: var(--text-muted);
line-height: 1.6;
}
.preview-btn {
align-self: flex-start;
padding: 8px 18px;
background: var(--accent);
color: var(--bg);
border: 2px solid var(--accent);
border-radius: 8px;
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: opacity 0.15s;
}
.preview-btn:hover {
opacity: 0.85;
}
.preview-link {
color: var(--accent);
font-size: var(--font-size);
font-weight: 600;
text-decoration: underline;
}const html = document.documentElement;
function getThemes() {
return new Set(html.dataset.theme.trim().split(/\s+/).filter(Boolean));
}
function setTheme(themes) {
html.dataset.theme = [...themes].join(" ") || "default";
}
function bindToggle(id, themeName) {
const btn = document.getElementById(id);
btn.addEventListener("click", () => {
const themes = getThemes();
themes.delete("default");
if (themes.has(themeName)) {
themes.delete(themeName);
btn.setAttribute("aria-checked", "false");
} else {
themes.add(themeName);
btn.setAttribute("aria-checked", "true");
}
if (themes.size === 0) themes.add("default");
setTheme(themes);
});
}
bindToggle("contrastToggle", "high-contrast");
bindToggle("dyslexiaToggle", "dyslexia");
bindToggle("largeTextToggle", "large-text");
// Persist to sessionStorage
const stored = sessionStorage.getItem("a11y-theme");
if (stored) {
const themes = new Set(stored.split(" ").filter(Boolean));
setTheme(themes);
themes.forEach((t) => {
const map = {
"high-contrast": "contrastToggle",
dyslexia: "dyslexiaToggle",
"large-text": "largeTextToggle",
};
if (map[t]) document.getElementById(map[t]).setAttribute("aria-checked", "true");
});
}
const observer = new MutationObserver(() => {
sessionStorage.setItem("a11y-theme", html.dataset.theme);
});
observer.observe(html, { attributes: true, attributeFilter: ["data-theme"] });<!DOCTYPE html>
<html lang="en" data-theme="default">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>High Contrast Toggle</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<div class="settings-bar">
<h2 class="settings-title">Accessibility Settings</h2>
<div class="settings-grid">
<div class="setting-item">
<div class="setting-info">
<span class="setting-label">High Contrast</span>
<span class="setting-desc">Increase contrast for better readability</span>
</div>
<button class="toggle-switch" id="contrastToggle" role="switch" aria-checked="false" aria-label="Toggle high contrast">
<span class="toggle-knob"></span>
</button>
</div>
<div class="setting-item">
<div class="setting-info">
<span class="setting-label">Dyslexia Font</span>
<span class="setting-desc">Use OpenDyslexic-style font spacing</span>
</div>
<button class="toggle-switch" id="dyslexiaToggle" role="switch" aria-checked="false" aria-label="Toggle dyslexia font">
<span class="toggle-knob"></span>
</button>
</div>
<div class="setting-item">
<div class="setting-info">
<span class="setting-label">Large Text</span>
<span class="setting-desc">Increase base font size</span>
</div>
<button class="toggle-switch" id="largeTextToggle" role="switch" aria-checked="false" aria-label="Toggle large text">
<span class="toggle-knob"></span>
</button>
</div>
</div>
</div>
<div class="preview-area" id="previewArea">
<h3 class="preview-heading">Preview Area</h3>
<p class="preview-text">The quick brown fox jumps over the lazy dog. This text demonstrates how the accessibility settings affect readability. Toggle the options above to see how the content adapts.</p>
<div class="preview-card">
<p class="preview-card-title">Sample Content Card</p>
<p class="preview-card-body">This card shows how interactive elements and backgrounds change with different accessibility modes.</p>
<button class="preview-btn">Action Button</button>
</div>
<a href="#" class="preview-link">A sample hyperlink →</a>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Accessibility toolbar with three toggles: high contrast mode (black/white), dyslexia-friendly font (OpenDyslexic-style spacing), and enlarged text. Each preference persists to localStorage across page loads.