UI Components Easy
Custom Focus Ring
Custom visible focus ring system using CSS :focus-visible — high-contrast, brand-colored, and offset variants. WCAG compliant.
Open in Lab
MCP
css
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* Global focus ring — applied via :focus-visible */
:focus {
outline: none;
}
:focus-visible {
outline: 2.5px solid #6366f1;
outline-offset: 3px;
border-radius: 6px;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: #0d1117;
color: #e6edf3;
min-height: 100vh;
padding: 32px 16px;
display: flex;
justify-content: center;
}
.demo {
width: 100%;
max-width: 720px;
}
.section-title {
font-size: 20px;
font-weight: 800;
margin-bottom: 8px;
}
.section-desc {
font-size: 14px;
color: #8b949e;
margin-bottom: 28px;
line-height: 1.6;
}
kbd {
background: #21262d;
border: 1px solid #30363d;
border-bottom-width: 2px;
border-radius: 5px;
padding: 1px 6px;
font-size: 12px;
font-family: Menlo, monospace;
color: #e6edf3;
}
.ring-showcase {
display: flex;
flex-direction: column;
gap: 28px;
}
.ring-group {
}
.ring-group-title {
font-size: 11px;
font-weight: 700;
color: #4a555f;
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 12px;
}
.ring-row {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.ring-col {
display: flex;
flex-direction: column;
gap: 10px;
}
/* Buttons */
.demo-btn {
padding: 9px 20px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.15s;
}
.demo-btn:hover {
opacity: 0.85;
}
.demo-btn--primary {
background: #6366f1;
color: #fff;
}
.demo-btn--secondary {
background: #21262d;
color: #e6edf3;
border: 1px solid #30363d;
}
.demo-btn--danger {
background: #f85149;
color: #fff;
}
.demo-btn--ghost {
background: transparent;
color: #8b949e;
border: 1px solid #30363d;
}
.demo-btn--danger:focus-visible {
outline-color: #f85149;
}
.demo-btn--primary:focus-visible {
outline-color: #818cf8;
}
/* Inputs */
.demo-input,
.demo-textarea,
.demo-select {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
color: #e6edf3;
font-size: 14px;
padding: 9px 12px;
width: 100%;
max-width: 380px;
outline: none;
font-family: inherit;
transition: border-color 0.15s;
}
.demo-input:focus-visible,
.demo-textarea:focus-visible,
.demo-select:focus-visible {
border-color: #6366f1;
outline: 2.5px solid rgba(99, 102, 241, 0.4);
outline-offset: 2px;
}
.demo-input::placeholder,
.demo-textarea::placeholder {
color: #4a555f;
}
.demo-textarea {
resize: vertical;
min-height: 80px;
}
/* Links */
.demo-link {
color: #6366f1;
font-size: 14px;
font-weight: 600;
text-decoration: none;
border-bottom: 1px solid rgba(99, 102, 241, 0.3);
}
.demo-link:hover {
border-bottom-color: #6366f1;
}
.demo-link--subtle {
color: #8b949e;
border-bottom-color: transparent;
}
.demo-link--subtle:hover {
color: #e6edf3;
}
/* Card links */
.demo-card-link {
display: block;
background: #161b22;
border: 1px solid #21262d;
border-radius: 10px;
padding: 14px 18px;
text-decoration: none;
transition: border-color 0.15s, transform 0.15s;
min-width: 160px;
}
.demo-card-link:hover {
border-color: #30363d;
transform: translateY(-1px);
}
.demo-card-link:focus-visible {
outline-offset: 4px;
}
.dcl-title {
font-size: 14px;
font-weight: 600;
color: #e6edf3;
margin-bottom: 4px;
}
.dcl-sub {
font-size: 12px;
color: #6c7086;
}
/* Checkboxes */
.demo-checkbox,
.demo-radio {
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
color: #8b949e;
cursor: pointer;
}
.demo-checkbox input,
.demo-radio input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.cb-custom {
width: 18px;
height: 18px;
border: 2px solid #30363d;
border-radius: 5px;
background: #161b22;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
position: relative;
}
.demo-checkbox input:checked + .cb-custom {
background: #6366f1;
border-color: #6366f1;
}
.demo-checkbox input:checked + .cb-custom::after {
content: "";
width: 10px;
height: 6px;
border-left: 2px solid #fff;
border-bottom: 2px solid #fff;
transform: rotate(-45deg) translateY(-1px);
display: block;
}
.demo-checkbox input:focus-visible + .cb-custom {
outline: 2.5px solid #6366f1;
outline-offset: 2px;
}
.rb-custom {
width: 18px;
height: 18px;
border: 2px solid #30363d;
border-radius: 50%;
background: #161b22;
flex-shrink: 0;
position: relative;
transition: all 0.15s;
}
.demo-radio input:checked + .rb-custom {
border-color: #6366f1;
}
.demo-radio input:checked + .rb-custom::after {
content: "";
position: absolute;
inset: 3px;
background: #6366f1;
border-radius: 50%;
}
.demo-radio input:focus-visible + .rb-custom {
outline: 2.5px solid #6366f1;
outline-offset: 2px;
}// Show "mouse" vs "keyboard" mode indicator
let usingKeyboard = false;
document.addEventListener("keydown", (e) => {
if (e.key === "Tab") usingKeyboard = true;
});
document.addEventListener("mousedown", () => {
usingKeyboard = false;
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Custom Focus Ring</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h2 class="section-title">Custom Focus Ring System</h2>
<p class="section-desc">Press <kbd>Tab</kbd> to navigate. All interactive elements use a consistent, high-contrast focus ring.</p>
<div class="ring-showcase">
<div class="ring-group">
<h3 class="ring-group-title">Buttons</h3>
<div class="ring-row">
<button class="demo-btn demo-btn--primary">Primary</button>
<button class="demo-btn demo-btn--secondary">Secondary</button>
<button class="demo-btn demo-btn--danger">Danger</button>
<button class="demo-btn demo-btn--ghost">Ghost</button>
</div>
</div>
<div class="ring-group">
<h3 class="ring-group-title">Inputs</h3>
<div class="ring-col">
<input class="demo-input" type="text" placeholder="Text input" />
<textarea class="demo-textarea" placeholder="Textarea" rows="3"></textarea>
<select class="demo-select">
<option>Select option…</option>
<option>Option A</option>
<option>Option B</option>
</select>
</div>
</div>
<div class="ring-group">
<h3 class="ring-group-title">Links & Cards</h3>
<div class="ring-row">
<a class="demo-link" href="#">Documentation →</a>
<a class="demo-link demo-link--subtle" href="#">Subtle link</a>
</div>
<div class="ring-row" style="margin-top:12px">
<a class="demo-card-link" href="#">
<p class="dcl-title">Card link</p>
<p class="dcl-sub">Entire card is focusable</p>
</a>
<a class="demo-card-link" href="#">
<p class="dcl-title">Another card</p>
<p class="dcl-sub">Keyboard accessible</p>
</a>
</div>
</div>
<div class="ring-group">
<h3 class="ring-group-title">Checkboxes & Radios</h3>
<div class="ring-col">
<label class="demo-checkbox">
<input type="checkbox" checked />
<span class="cb-custom"></span>
Receive notifications
</label>
<label class="demo-checkbox">
<input type="checkbox" />
<span class="cb-custom"></span>
Subscribe to newsletter
</label>
<label class="demo-radio">
<input type="radio" name="plan" checked />
<span class="rb-custom"></span>
Free plan
</label>
<label class="demo-radio">
<input type="radio" name="plan" />
<span class="rb-custom"></span>
Pro plan
</label>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Custom focus ring system using :focus-visible to show rings only during keyboard navigation. Demonstrates brand-colored, high-contrast, inset, and animated ring variants across buttons, links, and inputs.