UI Components Medium
High Contrast Theme
Theme system with high contrast mode compatible with Windows High Contrast and CSS forced-colors media query.
Open in Lab
MCP
css vanilla-js
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* ── Normal theme (default) ── */
:root {
--bg: #0a0a0a;
--bg-surface: #141414;
--bg-elevated: #1a1a1a;
--border: #262626;
--text-primary: #e5e5e5;
--text-secondary: #a3a3a3;
--text-muted: #737373;
--accent: #3b82f6;
--accent-hover: #2563eb;
--positive: #22c55e;
--negative: #ef4444;
--warning: #f59e0b;
--info: #3b82f6;
--link: #60a5fa;
--focus-ring: #3b82f6;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
--btn-primary-bg: #3b82f6;
--btn-primary-text: #ffffff;
--btn-secondary-bg: transparent;
--btn-secondary-text: #e5e5e5;
--btn-secondary-border: #404040;
--btn-danger-bg: #dc2626;
--btn-danger-text: #ffffff;
--input-bg: #171717;
--input-border: #333333;
--input-text: #e5e5e5;
}
/* ── High contrast theme ── */
.high-contrast {
--bg: #000000;
--bg-surface: #000000;
--bg-elevated: #000000;
--border: #ffffff;
--text-primary: #ffffff;
--text-secondary: #ffffff;
--text-muted: #ffffff;
--accent: #ffff00;
--accent-hover: #ffff00;
--positive: #00ff00;
--negative: #ff0000;
--warning: #ffff00;
--info: #00ffff;
--link: #ffff00;
--focus-ring: #ffffff;
--shadow: none;
--btn-primary-bg: #ffff00;
--btn-primary-text: #000000;
--btn-secondary-bg: #000000;
--btn-secondary-text: #ffffff;
--btn-secondary-border: #ffffff;
--btn-danger-bg: #ff0000;
--btn-danger-text: #ffffff;
--input-bg: #000000;
--input-border: #ffffff;
--input-text: #ffffff;
}
.high-contrast .card,
.high-contrast .alert,
.high-contrast .nav,
.high-contrast .form-section {
border-width: 2px;
}
.high-contrast .btn {
border-width: 2px;
font-weight: 700;
}
.high-contrast input,
.high-contrast select {
border-width: 2px;
}
/* ── Forced-colors simulation ── */
.forced-colors {
--bg: Canvas;
--bg-surface: Canvas;
--bg-elevated: Canvas;
--border: CanvasText;
--text-primary: CanvasText;
--text-secondary: CanvasText;
--text-muted: GrayText;
--accent: LinkText;
--accent-hover: ActiveText;
--positive: CanvasText;
--negative: CanvasText;
--warning: CanvasText;
--info: CanvasText;
--link: LinkText;
--focus-ring: Highlight;
--shadow: none;
--btn-primary-bg: ButtonFace;
--btn-primary-text: ButtonText;
--btn-secondary-bg: ButtonFace;
--btn-secondary-text: ButtonText;
--btn-secondary-border: ButtonText;
--btn-danger-bg: ButtonFace;
--btn-danger-text: ButtonText;
--input-bg: Field;
--input-border: FieldText;
--input-text: FieldText;
}
.forced-colors .card,
.forced-colors .alert,
.forced-colors .nav,
.forced-colors .form-section {
border-width: 2px;
box-shadow: none;
}
.forced-colors .btn {
border: 2px solid ButtonText;
}
.forced-colors .card-change {
font-weight: 700;
text-decoration: underline;
}
/* ── Respond to real OS preferences ── */
@media (prefers-contrast: more) {
:root {
--border: #ffffff;
--text-muted: #d4d4d4;
--shadow: none;
}
.card,
.alert,
.nav,
.form-section {
border-width: 2px;
}
.btn {
border-width: 2px;
}
}
@media (forced-colors: active) {
.card,
.alert,
.btn,
.nav,
input,
select {
border: 2px solid ButtonText;
}
.card-change.positive::before,
.card-change.negative::before {
font-weight: 700;
}
}
/* ── Layout ── */
body {
font-family: Inter, system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text-primary);
min-height: 100vh;
display: flex;
justify-content: center;
padding: 2rem;
transition: background 0.25s, color 0.25s;
}
.page {
width: 100%;
max-width: 720px;
}
.header {
margin-bottom: 1.5rem;
}
.header-title {
font-size: 1.75rem;
font-weight: 800;
letter-spacing: -0.02em;
margin-bottom: 0.375rem;
}
.header-sub {
color: var(--text-muted);
font-size: 0.875rem;
margin-bottom: 1rem;
}
/* ── Theme toggle ── */
.theme-toggle {
display: inline-flex;
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: 0.5rem;
overflow: hidden;
}
.toggle-btn {
background: none;
border: none;
color: var(--text-secondary);
padding: 0.5rem 1rem;
font-size: 0.8125rem;
font-weight: 500;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.toggle-btn:hover {
background: var(--bg-elevated);
}
.toggle-btn.active {
background: var(--accent);
color: var(--btn-primary-text);
font-weight: 700;
}
/* ── Nav ── */
.nav {
display: flex;
gap: 0.25rem;
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 0.375rem;
margin-bottom: 1.5rem;
box-shadow: var(--shadow);
}
.nav-link {
color: var(--text-secondary);
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-size: 0.8125rem;
font-weight: 500;
transition: background 0.15s, color 0.15s;
}
.nav-link:hover {
background: var(--bg-elevated);
color: var(--text-primary);
}
.nav-link.active {
background: var(--bg-elevated);
color: var(--text-primary);
font-weight: 600;
}
/* ── Cards ── */
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem;
margin-bottom: 1.5rem;
}
.card {
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.25rem;
box-shadow: var(--shadow);
}
.card-title {
font-size: 0.75rem;
color: var(--text-muted);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.5rem;
}
.card-value {
font-size: 1.5rem;
font-weight: 800;
margin-bottom: 0.375rem;
}
.card-change {
font-size: 0.75rem;
}
.card-change.positive {
color: var(--positive);
}
.card-change.negative {
color: var(--negative);
}
/* ── Form ── */
.form-section {
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: var(--shadow);
}
.section-title {
font-size: 1rem;
font-weight: 700;
margin-bottom: 1rem;
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-bottom: 1.25rem;
}
.form-group label {
display: block;
font-size: 0.75rem;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 0.375rem;
}
.form-group input[type="text"],
.form-group input[type="email"],
.form-group select {
width: 100%;
background: var(--input-bg);
border: 1px solid var(--input-border);
border-radius: 0.5rem;
color: var(--input-text);
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
outline: none;
transition: border-color 0.15s;
}
.form-group input:focus,
.form-group select:focus {
border-color: var(--focus-ring);
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
justify-content: flex-end;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.8125rem;
cursor: pointer;
color: var(--text-secondary);
}
.checkbox-label input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--accent);
}
.form-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.btn {
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-size: 0.8125rem;
font-weight: 600;
cursor: pointer;
border: 1px solid transparent;
transition: background 0.15s, border-color 0.15s;
}
.btn-primary {
background: var(--btn-primary-bg);
color: var(--btn-primary-text);
}
.btn-primary:hover {
opacity: 0.9;
}
.btn-secondary {
background: var(--btn-secondary-bg);
color: var(--btn-secondary-text);
border-color: var(--btn-secondary-border);
}
.btn-secondary:hover {
background: var(--bg-elevated);
}
.btn-danger {
background: var(--btn-danger-bg);
color: var(--btn-danger-text);
}
.btn-danger:hover {
opacity: 0.9;
}
/* ── Alerts ── */
.alerts {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1.5rem;
}
.alert {
padding: 0.875rem 1rem;
border-radius: 0.5rem;
font-size: 0.8125rem;
line-height: 1.5;
border: 1px solid var(--border);
}
.alert-info {
background: color-mix(in srgb, var(--info) 10%, var(--bg-surface));
border-left: 3px solid var(--info);
}
.alert-warning {
background: color-mix(in srgb, var(--warning) 10%, var(--bg-surface));
border-left: 3px solid var(--warning);
}
.alert-error {
background: color-mix(in srgb, var(--negative) 10%, var(--bg-surface));
border-left: 3px solid var(--negative);
}
/* ── Links section ── */
.links-section {
margin-bottom: 2rem;
}
.links-section .section-title {
margin-bottom: 0.75rem;
}
.body-text {
font-size: 0.9375rem;
line-height: 1.7;
color: var(--text-secondary);
}
.body-text a {
color: var(--link);
text-decoration: underline;
text-underline-offset: 2px;
}
.body-text a:hover {
color: var(--accent-hover);
}
@media (max-width: 600px) {
body {
padding: 1rem;
}
.page {
max-width: 100%;
}
.header-title {
font-size: 1.35rem;
}
.header-sub {
font-size: 0.8rem;
}
/* Theme toggle wraps */
.theme-toggle {
display: flex;
width: 100%;
}
.toggle-btn {
flex: 1;
padding: 0.5rem 0.5rem;
font-size: 0.75rem;
text-align: center;
}
/* Nav scrolls horizontally */
.nav {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
flex-wrap: nowrap;
}
.nav::-webkit-scrollbar {
display: none;
}
.nav-link {
white-space: nowrap;
flex-shrink: 0;
font-size: 0.78rem;
padding: 0.5rem 0.75rem;
}
/* Cards single column */
.cards {
grid-template-columns: 1fr;
}
.card {
padding: 1rem;
}
.card-value {
font-size: 1.25rem;
}
/* Form */
.form-section {
padding: 1rem;
}
.form-grid {
grid-template-columns: 1fr;
}
.form-actions {
flex-direction: column;
}
.btn {
width: 100%;
text-align: center;
}
/* Alerts */
.alert {
font-size: 0.78rem;
padding: 0.75rem;
}
.section-title {
font-size: 0.9rem;
}
.body-text {
font-size: 0.85rem;
}
}(function () {
var buttons = document.querySelectorAll(".toggle-btn");
var body = document.body;
var modes = {
normal: "",
"high-contrast": "high-contrast",
"forced-colors": "forced-colors",
};
function setMode(mode) {
// Remove all mode classes
body.classList.remove("high-contrast", "forced-colors");
// Add selected mode class
if (modes[mode]) {
body.classList.add(modes[mode]);
}
// Update button states
buttons.forEach(function (btn) {
var isActive = btn.getAttribute("data-mode") === mode;
btn.classList.toggle("active", isActive);
btn.setAttribute("aria-pressed", isActive ? "true" : "false");
});
}
// Attach click handlers
buttons.forEach(function (btn) {
btn.addEventListener("click", function () {
setMode(btn.getAttribute("data-mode"));
});
});
// Keyboard support for toggle group
var toggleGroup = document.querySelector(".theme-toggle");
toggleGroup.addEventListener("keydown", function (e) {
var current = document.querySelector(".toggle-btn.active");
var btns = Array.from(buttons);
var idx = btns.indexOf(current);
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
e.preventDefault();
var next = btns[(idx + 1) % btns.length];
next.focus();
setMode(next.getAttribute("data-mode"));
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
e.preventDefault();
var prev = btns[(idx - 1 + btns.length) % btns.length];
prev.focus();
setMode(prev.getAttribute("data-mode"));
}
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>High Contrast Theme</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page">
<header class="header">
<h1 class="header-title">High Contrast Theme</h1>
<p class="header-sub">Toggle between normal, high-contrast, and forced-colors simulation.</p>
<div class="theme-toggle" role="radiogroup" aria-label="Theme mode">
<button class="toggle-btn active" data-mode="normal" aria-pressed="true">Normal</button>
<button class="toggle-btn" data-mode="high-contrast" aria-pressed="false">High Contrast</button>
<button class="toggle-btn" data-mode="forced-colors" aria-pressed="false">Forced Colors</button>
</div>
</header>
<!-- Navigation -->
<nav class="nav" aria-label="Main navigation">
<a href="#" class="nav-link active" onclick="return false">Dashboard</a>
<a href="#" class="nav-link" onclick="return false">Projects</a>
<a href="#" class="nav-link" onclick="return false">Settings</a>
<a href="#" class="nav-link" onclick="return false">Help</a>
</nav>
<!-- Cards -->
<div class="cards">
<div class="card">
<h2 class="card-title">Active Users</h2>
<p class="card-value">12,847</p>
<p class="card-change positive">+14.2% from last month</p>
</div>
<div class="card">
<h2 class="card-title">Revenue</h2>
<p class="card-value">$48,290</p>
<p class="card-change positive">+8.7% from last month</p>
</div>
<div class="card">
<h2 class="card-title">Bounce Rate</h2>
<p class="card-value">23.4%</p>
<p class="card-change negative">+2.1% from last month</p>
</div>
</div>
<!-- Form elements -->
<div class="form-section">
<h2 class="section-title">Form Elements</h2>
<div class="form-grid">
<div class="form-group">
<label for="name-input">Full Name</label>
<input type="text" id="name-input" value="Jane Doe" />
</div>
<div class="form-group">
<label for="email-input">Email</label>
<input type="email" id="email-input" value="jane@example.com" />
</div>
<div class="form-group">
<label for="role-select">Role</label>
<select id="role-select">
<option>Admin</option>
<option selected>Editor</option>
<option>Viewer</option>
</select>
</div>
<div class="form-group checkbox-group">
<label class="checkbox-label">
<input type="checkbox" checked />
<span>Enable notifications</span>
</label>
<label class="checkbox-label">
<input type="checkbox" />
<span>Enable dark mode</span>
</label>
</div>
</div>
<div class="form-actions">
<button class="btn btn-primary">Save Changes</button>
<button class="btn btn-secondary">Cancel</button>
<button class="btn btn-danger">Delete Account</button>
</div>
</div>
<!-- Alerts -->
<div class="alerts">
<div class="alert alert-info" role="alert">
<strong>Info:</strong> Your subscription renews on April 15.
</div>
<div class="alert alert-warning" role="alert">
<strong>Warning:</strong> Storage is at 85% capacity.
</div>
<div class="alert alert-error" role="alert">
<strong>Error:</strong> Payment method expired. Please update.
</div>
</div>
<!-- Links -->
<div class="links-section">
<h2 class="section-title">Links & Text</h2>
<p class="body-text">
This paragraph contains <a href="#" onclick="return false">inline links</a> that should remain
clearly distinguishable in all contrast modes. The
<a href="#" onclick="return false">high contrast theme</a> uses bold borders and removes
subtle shadows. <a href="#" onclick="return false">Forced colors mode</a> maps all colors
to system keywords.
</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>A complete theme system demonstrating high contrast mode with support for the CSS prefers-contrast and forced-colors media queries. Includes a sample UI with cards, buttons, links, and form elements that adapts gracefully to Windows High Contrast Mode using system color keywords.