Light JSMIT licensed

Why Minimal Interfaces Fail Accessibility

Four failures side by side, each with the criterion it breaks printed on it and a switch that shows the corrected version in place: low-contrast secondary text, an icon-only button with no accessible name, placeholders used as labels, and a 32px target chosen because 44px looked heavy. Every one of these ships weekly in the name of clean design.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time

Untitled UI · Affiliate link

Get Untitled UI Pro

The code

<section class="mu-13" aria-labelledby="mu-13-title">
  <h2 class="mu-13__sr" id="mu-13-title">Accessibility failures and fixes</h2>
  <div class="mu-13__theme">
    <input class="mu-13__theme-input" type="checkbox" id="mu-13-theme">
    <label class="mu-13__theme-label" for="mu-13-theme">
      <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
      <span class="mu-13__sr">Toggle dark theme</span>
    </label>
  </div>

  <div class="mu-13__stage" id="mu-13-board">

    <div class="mu-13__board">
      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p1">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p1">Low-contrast secondary text</h3>
          <p class="mu-13__crit">SC 1.4.3 &middot; Contrast (Minimum)</p>
        </header>
        <div class="mu-13__sample">
          <p class="mu-13__sample-title">Monthly recurring revenue</p>
          <p class="mu-13__secondary">Updated 6 minutes ago by Alex Chen. Includes 42 hours of overage on the Basalt retainer.</p>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">2.9:1 &mdash; fails</b><b class="mu-13__measure-ok">7.0:1 &mdash; passes</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>

      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p2">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p2">Icon-only button, no name</h3>
          <p class="mu-13__crit">SC 4.1.2 &middot; Name, Role, Value</p>
        </header>
        <div class="mu-13__sample">
          <div class="mu-13__toolbar">
            <span class="mu-13__fail-only">
              <button class="mu-13__icon-btn" type="button">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
              </button>
              <button class="mu-13__icon-btn" type="button">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
              </button>
            </span>
            <span class="mu-13__fix-only">
              <button class="mu-13__icon-btn" type="button" aria-label="Add line item">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
                <span class="mu-13__tip">Add line item</span>
              </button>
              <button class="mu-13__icon-btn" type="button" aria-label="Delete invoice">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
                <span class="mu-13__tip">Delete invoice</span>
              </button>
            </span>
          </div>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">No accessible name</b><b class="mu-13__measure-ok">aria-label + visible tip</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>

      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p3">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p3">Placeholder used as a label</h3>
          <p class="mu-13__crit">SC 3.3.2 &middot; Labels or Instructions</p>
        </header>
        <div class="mu-13__sample">
          <div class="mu-13__fail-only">
            <input class="mu-13__input" type="text" placeholder="Card number" inputmode="numeric">
            <input class="mu-13__input" type="text" placeholder="MM / YY" inputmode="numeric">
          </div>
          <div class="mu-13__fix-only mu-13__fields">
            <div class="mu-13__field">
              <label class="mu-13__label" for="mu-13-card">Card number</label>
              <input class="mu-13__input" id="mu-13-card" type="text" placeholder="4242 4242 4242 4242" inputmode="numeric">
            </div>
            <div class="mu-13__field">
              <label class="mu-13__label" for="mu-13-exp">Expiry</label>
              <input class="mu-13__input" id="mu-13-exp" type="text" placeholder="MM / YY" inputmode="numeric">
            </div>
          </div>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">Label disappears on input</b><b class="mu-13__measure-ok">Label persists, hint remains</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>

      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p4">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p4">Target under 44px</h3>
          <p class="mu-13__crit">SC 2.5.8 &middot; Target Size (Minimum)</p>
        </header>
        <div class="mu-13__sample">
          <div class="mu-13__target-row">
            <button class="mu-13__target" type="button">Approve</button>
            <button class="mu-13__target" type="button">Reject</button>
          </div>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">32 &times; 28px</b><b class="mu-13__measure-ok">44 &times; 44px</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>
    </div>
  </div>
</section>
.mu-13 {
  --mu-13-target-fail: 28px;
  --mu-13-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mu-13-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --mu-13-bg: #ffffff;
  --mu-13-bg: oklch(100% 0 0);
  --mu-13-surface: #f7f8f9;
  --mu-13-surface: oklch(97.5% 0.003 250);
  --mu-13-ink: #15181b;
  --mu-13-ink: oklch(19.5% 0.006 250);
  --mu-13-ink-2: #4f585f;
  --mu-13-ink-2: oklch(45% 0.012 250);
  --mu-13-fail-ink: #a9b0b5;
  --mu-13-fail-ink: oklch(75% 0.006 250);
  --mu-13-line: #d3d8db;
  --mu-13-line: oklch(86% 0.005 250);
  --mu-13-bad: #a11d1d;
  --mu-13-bad: oklch(44% 0.18 25);
  --mu-13-ok: #15613a;
  --mu-13-ok: oklch(43% 0.11 155);
  --mu-13-acc: #0f4faf;
  --mu-13-acc: oklch(44% 0.17 262);
  --mu-13-ring: #0f4faf;
  --mu-13-ring: oklch(44% 0.17 262);
  --mu-13-acc-ink: #ffffff;
  --mu-13-acc-ink: oklch(100% 0 0);
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  position: relative;
  box-sizing: border-box;
  background: var(--mu-13-bg);
  color: var(--mu-13-ink);
  font-family: var(--mu-13-ui);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

.mu-13 *,
.mu-13 *::before,
.mu-13 *::after {
  box-sizing: border-box;
}

@media (prefers-color-scheme: dark) {
  .mu-13 {
    --mu-13-bg: #101315;
    --mu-13-bg: oklch(17.5% 0.006 250);
    --mu-13-surface: #191d20;
    --mu-13-surface: oklch(23% 0.007 250);
    --mu-13-ink: #eef1f3;
    --mu-13-ink: oklch(95.5% 0.004 250);
    --mu-13-ink-2: #a5aeb5;
    --mu-13-ink-2: oklch(74% 0.01 250);
    --mu-13-fail-ink: #454d53;
    --mu-13-fail-ink: oklch(40% 0.01 250);
    --mu-13-line: #333b41;
    --mu-13-line: oklch(33% 0.011 250);
    --mu-13-bad: #f08a8a;
    --mu-13-bad: oklch(74% 0.14 25);
    --mu-13-ok: #62c78f;
    --mu-13-ok: oklch(78% 0.12 155);
    --mu-13-acc: #86b3ff;
    --mu-13-acc: oklch(80% 0.12 262);
    --mu-13-ring: #86b3ff;
    --mu-13-ring: oklch(80% 0.12 262);
    --mu-13-acc-ink: #0c1526;
    --mu-13-acc-ink: oklch(18% 0.03 258);
  }
}

.mu-13[data-theme="dark"],
.mu-13:has(.mu-13__theme-input:checked) {
  --mu-13-bg: #101315;
  --mu-13-bg: oklch(17.5% 0.006 250);
  --mu-13-surface: #191d20;
  --mu-13-surface: oklch(23% 0.007 250);
  --mu-13-ink: #eef1f3;
  --mu-13-ink: oklch(95.5% 0.004 250);
  --mu-13-ink-2: #a5aeb5;
  --mu-13-ink-2: oklch(74% 0.01 250);
  --mu-13-fail-ink: #454d53;
  --mu-13-fail-ink: oklch(40% 0.01 250);
  --mu-13-line: #333b41;
  --mu-13-line: oklch(33% 0.011 250);
  --mu-13-bad: #f08a8a;
  --mu-13-bad: oklch(74% 0.14 25);
  --mu-13-ok: #62c78f;
  --mu-13-ok: oklch(78% 0.12 155);
  --mu-13-acc: #86b3ff;
  --mu-13-acc: oklch(80% 0.12 262);
  --mu-13-ring: #86b3ff;
  --mu-13-ring: oklch(80% 0.12 262);
  --mu-13-acc-ink: #0c1526;
  --mu-13-acc-ink: oklch(18% 0.03 258);
}

@media (prefers-color-scheme: light) {
  .mu-13:has(.mu-13__theme-input:checked) {
    --mu-13-bg: #101315;
    --mu-13-bg: oklch(17.5% 0.006 250);
    --mu-13-surface: #191d20;
    --mu-13-surface: oklch(23% 0.007 250);
    --mu-13-ink: #eef1f3;
    --mu-13-ink: oklch(95.5% 0.004 250);
    --mu-13-ink-2: #a5aeb5;
    --mu-13-ink-2: oklch(74% 0.01 250);
    --mu-13-fail-ink: #454d53;
    --mu-13-fail-ink: oklch(40% 0.01 250);
    --mu-13-line: #333b41;
    --mu-13-line: oklch(33% 0.011 250);
    --mu-13-bad: #f08a8a;
    --mu-13-bad: oklch(74% 0.14 25);
    --mu-13-ok: #62c78f;
    --mu-13-ok: oklch(78% 0.12 155);
    --mu-13-acc: #86b3ff;
    --mu-13-acc: oklch(80% 0.12 262);
    --mu-13-ring: #86b3ff;
    --mu-13-ring: oklch(80% 0.12 262);
    --mu-13-acc-ink: #0c1526;
    --mu-13-acc-ink: oklch(18% 0.03 258);
  }
}

@media (prefers-color-scheme: dark) {
  .mu-13:has(.mu-13__theme-input:checked) {
    --mu-13-bg: #ffffff;
    --mu-13-bg: oklch(100% 0 0);
    --mu-13-surface: #f7f8f9;
    --mu-13-surface: oklch(97.5% 0.003 250);
    --mu-13-ink: #15181b;
    --mu-13-ink: oklch(19.5% 0.006 250);
    --mu-13-ink-2: #4f585f;
    --mu-13-ink-2: oklch(45% 0.012 250);
    --mu-13-fail-ink: #a9b0b5;
    --mu-13-fail-ink: oklch(75% 0.006 250);
    --mu-13-line: #d3d8db;
    --mu-13-line: oklch(86% 0.005 250);
    --mu-13-bad: #a11d1d;
    --mu-13-bad: oklch(44% 0.18 25);
    --mu-13-ok: #15613a;
    --mu-13-ok: oklch(43% 0.11 155);
    --mu-13-acc: #0f4faf;
    --mu-13-acc: oklch(44% 0.17 262);
    --mu-13-ring: #0f4faf;
    --mu-13-ring: oklch(44% 0.17 262);
    --mu-13-acc-ink: #ffffff;
    --mu-13-acc-ink: oklch(100% 0 0);
  }
}

.mu-13__sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.mu-13__theme {
  position: absolute;
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  z-index: 5;
}

.mu-13__theme-input {
  position: absolute;
  inset: 0;
  inline-size: 44px;
  block-size: 44px;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.mu-13__theme-label {
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-surface);
  color: var(--mu-13-ink-2);
  cursor: pointer;
}

.mu-13__theme-label svg {
  inline-size: 20px;
  block-size: 20px;
}

.mu-13__theme-input:focus-visible + .mu-13__theme-label {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__stage {
  max-inline-size: 78rem;
  margin-inline: auto;
  padding: 4rem 1.25rem 5rem;
  display: grid;
  gap: 2.5rem;
  grid-template-columns: minmax(0, 1fr);
}

.mu-13__board {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}

.mu-13__panel {
  min-inline-size: 0;
  display: grid;
  gap: 0.875rem;
  align-content: start;
  padding: 1.25rem;
  background: var(--mu-13-surface);
  border: 1px solid var(--mu-13-line);
  border-radius: 6px;
}

.mu-13__panel-head {
  display: grid;
  gap: 2px;
  min-inline-size: 0;
}

.mu-13__panel-title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.mu-13__crit {
  margin: 0;
  font-family: var(--mu-13-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--mu-13-bad);
}

.mu-13__sample {
  min-inline-size: 0;
  display: grid;
  gap: 0.5rem;
  padding: 1rem;
  background: var(--mu-13-bg);
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
}

.mu-13__sample p {
  margin: 0;
  min-inline-size: 0;
}

.mu-13__sample-title {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.012em;
}

.mu-13__secondary {
  font-size: 0.875rem;
  color: var(--mu-13-fail-ink);
  text-wrap: pretty;
}

.mu-13__panel.is-fixed .mu-13__secondary {
  color: var(--mu-13-ink-2);
}

.mu-13__fix-only {
  display: none;
}

.mu-13__panel.is-fixed .mu-13__fail-only {
  display: none;
}

.mu-13__panel.is-fixed .mu-13__fix-only {
  display: grid;
}

.mu-13__toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  min-inline-size: 0;
}

.mu-13__toolbar > span {
  display: flex;
  gap: 0.5rem;
}

.mu-13__panel.is-fixed .mu-13__toolbar > .mu-13__fix-only {
  display: flex;
}

.mu-13__icon-btn {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  min-inline-size: 44px;
  min-block-size: 44px;
  padding: 0;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-surface);
  color: var(--mu-13-ink);
  cursor: pointer;
}

.mu-13__icon-btn svg {
  inline-size: 20px;
  block-size: 20px;
}

.mu-13__icon-btn:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__tip {
  position: absolute;
  inset-block-start: calc(100% + 6px);
  inset-inline-start: 0;
  padding: 4px 8px;
  border-radius: 3px;
  white-space: nowrap;
  background: var(--mu-13-ink);
  color: var(--mu-13-bg);
  font-size: 0.6875rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}

.mu-13__icon-btn:hover .mu-13__tip,
.mu-13__icon-btn:focus-visible .mu-13__tip {
  opacity: 1;
}

.mu-13__fields {
  gap: 0.75rem;
}

.mu-13__field {
  display: grid;
  gap: 0.25rem;
  min-inline-size: 0;
}

.mu-13__label {
  font-size: 0.8125rem;
  font-weight: 500;
}

.mu-13__input {
  inline-size: 100%;
  min-inline-size: 0;
  min-block-size: 44px;
  padding-inline: 0.625rem;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-bg);
  color: var(--mu-13-ink);
  font: inherit;
  font-size: 0.875rem;
}

.mu-13__input::placeholder {
  color: var(--mu-13-ink-2);
  opacity: 1;
}

.mu-13__input:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
  border-color: var(--mu-13-acc);
}

.mu-13__fail-only > .mu-13__input + .mu-13__input {
  margin-block-start: 0.5rem;
}

.mu-13__target-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.mu-13__target {
  min-block-size: var(--mu-13-target-fail);
  block-size: var(--mu-13-target-fail);
  padding-inline: 0.625rem;
  border: 1px solid var(--mu-13-line);
  border-radius: 3px;
  background: var(--mu-13-surface);
  color: var(--mu-13-ink);
  font: inherit;
  font-size: 0.75rem;
  cursor: pointer;
}

.mu-13__target:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__panel.is-fixed .mu-13__target {
  min-block-size: 44px;
  block-size: 44px;
  min-inline-size: 44px;
  padding-inline: 1.125rem;
  border-color: transparent;
  background: var(--mu-13-acc);
  color: var(--mu-13-acc-ink);
  font-size: 0.875rem;
  font-weight: 600;
  border-radius: 4px;
}

.mu-13__panel.is-fixed .mu-13__target + .mu-13__target {
  background: transparent;
  color: var(--mu-13-ink);
  border-color: var(--mu-13-line);
}

.mu-13__measure {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  font-family: var(--mu-13-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.mu-13__measure-fail {
  color: var(--mu-13-bad);
  font-weight: 600;
}

.mu-13__measure-ok {
  display: none;
  color: var(--mu-13-ok);
  font-weight: 600;
}

.mu-13__panel.is-fixed .mu-13__measure-fail {
  display: none;
}

.mu-13__panel.is-fixed .mu-13__measure-ok {
  display: inline;
}

.mu-13__switch {
  justify-self: start;
  min-block-size: 44px;
  min-inline-size: 44px;
  display: inline-flex;
  align-items: center;
  padding-inline: 0.875rem;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-bg);
  color: var(--mu-13-ink);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
}

.mu-13__switch:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__switch[aria-pressed="true"] {
  border-color: var(--mu-13-ok);
  color: var(--mu-13-ok);
}

@media (prefers-reduced-motion: reduce) {
  .mu-13__tip {
    transition: none;
  }
}

@media (min-width: 56rem) {
  .mu-13__stage {
    padding-inline: 3rem;
  }

  .mu-13__panel {
    padding: 1.5rem;
  }
}
(() => {
  const board = document.querySelector('#mu-13-board');
  if (!board) return;
  const switches = [...board.querySelectorAll('[data-mu-13-switch]')];
  const handlers = new Map();

  for (const btn of switches) {
    const panel = btn.closest('[data-mu-13-panel]');
    if (!panel) continue;
    const onClick = () => {
      const next = btn.getAttribute('aria-pressed') !== 'true';
      btn.setAttribute('aria-pressed', String(next));
      panel.classList.toggle('is-fixed', next);
      btn.querySelector('.mu-13__switch-label').textContent =
        next ? 'Show the failure' : 'Show the fix';
    };
    btn.addEventListener('click', onClick);
    handlers.set(btn, onClick);
  }

  board.__muCleanup = () => {
    for (const [btn, fn] of handlers) btn.removeEventListener('click', fn);
    handlers.clear();
  };
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Minimalist UI from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Why Minimal Interfaces Fail Accessibility
Source: https://codefronts.com/design-styles/css-minimalist-ui/why-minimal-interfaces-fail-accessibility/

Four failures side by side, each with the criterion it breaks printed on it and a switch that shows the corrected version in place: low-contrast secondary text, an icon-only button with no accessible name, placeholders used as labels, and a 32px target chosen because 44px looked heavy. Every one of these ships weekly in the name of clean design.
## HTML
```html
<section class="mu-13" aria-labelledby="mu-13-title">
  <h2 class="mu-13__sr" id="mu-13-title">Accessibility failures and fixes</h2>
  <div class="mu-13__theme">
    <input class="mu-13__theme-input" type="checkbox" id="mu-13-theme">
    <label class="mu-13__theme-label" for="mu-13-theme">
      <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 4a8 8 0 0 0 0 16Z" fill="currentColor"/></svg>
      <span class="mu-13__sr">Toggle dark theme</span>
    </label>
  </div>

  <div class="mu-13__stage" id="mu-13-board">

    <div class="mu-13__board">
      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p1">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p1">Low-contrast secondary text</h3>
          <p class="mu-13__crit">SC 1.4.3 &middot; Contrast (Minimum)</p>
        </header>
        <div class="mu-13__sample">
          <p class="mu-13__sample-title">Monthly recurring revenue</p>
          <p class="mu-13__secondary">Updated 6 minutes ago by Alex Chen. Includes 42 hours of overage on the Basalt retainer.</p>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">2.9:1 &mdash; fails</b><b class="mu-13__measure-ok">7.0:1 &mdash; passes</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>

      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p2">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p2">Icon-only button, no name</h3>
          <p class="mu-13__crit">SC 4.1.2 &middot; Name, Role, Value</p>
        </header>
        <div class="mu-13__sample">
          <div class="mu-13__toolbar">
            <span class="mu-13__fail-only">
              <button class="mu-13__icon-btn" type="button">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
              </button>
              <button class="mu-13__icon-btn" type="button">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
              </button>
            </span>
            <span class="mu-13__fix-only">
              <button class="mu-13__icon-btn" type="button" aria-label="Add line item">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
                <span class="mu-13__tip">Add line item</span>
              </button>
              <button class="mu-13__icon-btn" type="button" aria-label="Delete invoice">
                <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
                <span class="mu-13__tip">Delete invoice</span>
              </button>
            </span>
          </div>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">No accessible name</b><b class="mu-13__measure-ok">aria-label + visible tip</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>

      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p3">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p3">Placeholder used as a label</h3>
          <p class="mu-13__crit">SC 3.3.2 &middot; Labels or Instructions</p>
        </header>
        <div class="mu-13__sample">
          <div class="mu-13__fail-only">
            <input class="mu-13__input" type="text" placeholder="Card number" inputmode="numeric">
            <input class="mu-13__input" type="text" placeholder="MM / YY" inputmode="numeric">
          </div>
          <div class="mu-13__fix-only mu-13__fields">
            <div class="mu-13__field">
              <label class="mu-13__label" for="mu-13-card">Card number</label>
              <input class="mu-13__input" id="mu-13-card" type="text" placeholder="4242 4242 4242 4242" inputmode="numeric">
            </div>
            <div class="mu-13__field">
              <label class="mu-13__label" for="mu-13-exp">Expiry</label>
              <input class="mu-13__input" id="mu-13-exp" type="text" placeholder="MM / YY" inputmode="numeric">
            </div>
          </div>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">Label disappears on input</b><b class="mu-13__measure-ok">Label persists, hint remains</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>

      <article class="mu-13__panel" data-mu-13-panel aria-labelledby="mu-13-p4">
        <header class="mu-13__panel-head">
          <h3 class="mu-13__panel-title" id="mu-13-p4">Target under 44px</h3>
          <p class="mu-13__crit">SC 2.5.8 &middot; Target Size (Minimum)</p>
        </header>
        <div class="mu-13__sample">
          <div class="mu-13__target-row">
            <button class="mu-13__target" type="button">Approve</button>
            <button class="mu-13__target" type="button">Reject</button>
          </div>
        </div>
        <p class="mu-13__measure"><b class="mu-13__measure-fail">32 &times; 28px</b><b class="mu-13__measure-ok">44 &times; 44px</b></p>
        <button class="mu-13__switch" type="button" aria-pressed="false" data-mu-13-switch>
          <span class="mu-13__switch-label">Show the fix</span>
        </button>
      </article>
    </div>
  </div>
</section>
```
## CSS
```css
.mu-13 {
  --mu-13-target-fail: 28px;
  --mu-13-ui: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mu-13-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --mu-13-bg: #ffffff;
  --mu-13-bg: oklch(100% 0 0);
  --mu-13-surface: #f7f8f9;
  --mu-13-surface: oklch(97.5% 0.003 250);
  --mu-13-ink: #15181b;
  --mu-13-ink: oklch(19.5% 0.006 250);
  --mu-13-ink-2: #4f585f;
  --mu-13-ink-2: oklch(45% 0.012 250);
  --mu-13-fail-ink: #a9b0b5;
  --mu-13-fail-ink: oklch(75% 0.006 250);
  --mu-13-line: #d3d8db;
  --mu-13-line: oklch(86% 0.005 250);
  --mu-13-bad: #a11d1d;
  --mu-13-bad: oklch(44% 0.18 25);
  --mu-13-ok: #15613a;
  --mu-13-ok: oklch(43% 0.11 155);
  --mu-13-acc: #0f4faf;
  --mu-13-acc: oklch(44% 0.17 262);
  --mu-13-ring: #0f4faf;
  --mu-13-ring: oklch(44% 0.17 262);
  --mu-13-acc-ink: #ffffff;
  --mu-13-acc-ink: oklch(100% 0 0);
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  position: relative;
  box-sizing: border-box;
  background: var(--mu-13-bg);
  color: var(--mu-13-ink);
  font-family: var(--mu-13-ui);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

.mu-13 *,
.mu-13 *::before,
.mu-13 *::after {
  box-sizing: border-box;
}

@media (prefers-color-scheme: dark) {
  .mu-13 {
    --mu-13-bg: #101315;
    --mu-13-bg: oklch(17.5% 0.006 250);
    --mu-13-surface: #191d20;
    --mu-13-surface: oklch(23% 0.007 250);
    --mu-13-ink: #eef1f3;
    --mu-13-ink: oklch(95.5% 0.004 250);
    --mu-13-ink-2: #a5aeb5;
    --mu-13-ink-2: oklch(74% 0.01 250);
    --mu-13-fail-ink: #454d53;
    --mu-13-fail-ink: oklch(40% 0.01 250);
    --mu-13-line: #333b41;
    --mu-13-line: oklch(33% 0.011 250);
    --mu-13-bad: #f08a8a;
    --mu-13-bad: oklch(74% 0.14 25);
    --mu-13-ok: #62c78f;
    --mu-13-ok: oklch(78% 0.12 155);
    --mu-13-acc: #86b3ff;
    --mu-13-acc: oklch(80% 0.12 262);
    --mu-13-ring: #86b3ff;
    --mu-13-ring: oklch(80% 0.12 262);
    --mu-13-acc-ink: #0c1526;
    --mu-13-acc-ink: oklch(18% 0.03 258);
  }
}

.mu-13[data-theme="dark"],
.mu-13:has(.mu-13__theme-input:checked) {
  --mu-13-bg: #101315;
  --mu-13-bg: oklch(17.5% 0.006 250);
  --mu-13-surface: #191d20;
  --mu-13-surface: oklch(23% 0.007 250);
  --mu-13-ink: #eef1f3;
  --mu-13-ink: oklch(95.5% 0.004 250);
  --mu-13-ink-2: #a5aeb5;
  --mu-13-ink-2: oklch(74% 0.01 250);
  --mu-13-fail-ink: #454d53;
  --mu-13-fail-ink: oklch(40% 0.01 250);
  --mu-13-line: #333b41;
  --mu-13-line: oklch(33% 0.011 250);
  --mu-13-bad: #f08a8a;
  --mu-13-bad: oklch(74% 0.14 25);
  --mu-13-ok: #62c78f;
  --mu-13-ok: oklch(78% 0.12 155);
  --mu-13-acc: #86b3ff;
  --mu-13-acc: oklch(80% 0.12 262);
  --mu-13-ring: #86b3ff;
  --mu-13-ring: oklch(80% 0.12 262);
  --mu-13-acc-ink: #0c1526;
  --mu-13-acc-ink: oklch(18% 0.03 258);
}

@media (prefers-color-scheme: light) {
  .mu-13:has(.mu-13__theme-input:checked) {
    --mu-13-bg: #101315;
    --mu-13-bg: oklch(17.5% 0.006 250);
    --mu-13-surface: #191d20;
    --mu-13-surface: oklch(23% 0.007 250);
    --mu-13-ink: #eef1f3;
    --mu-13-ink: oklch(95.5% 0.004 250);
    --mu-13-ink-2: #a5aeb5;
    --mu-13-ink-2: oklch(74% 0.01 250);
    --mu-13-fail-ink: #454d53;
    --mu-13-fail-ink: oklch(40% 0.01 250);
    --mu-13-line: #333b41;
    --mu-13-line: oklch(33% 0.011 250);
    --mu-13-bad: #f08a8a;
    --mu-13-bad: oklch(74% 0.14 25);
    --mu-13-ok: #62c78f;
    --mu-13-ok: oklch(78% 0.12 155);
    --mu-13-acc: #86b3ff;
    --mu-13-acc: oklch(80% 0.12 262);
    --mu-13-ring: #86b3ff;
    --mu-13-ring: oklch(80% 0.12 262);
    --mu-13-acc-ink: #0c1526;
    --mu-13-acc-ink: oklch(18% 0.03 258);
  }
}

@media (prefers-color-scheme: dark) {
  .mu-13:has(.mu-13__theme-input:checked) {
    --mu-13-bg: #ffffff;
    --mu-13-bg: oklch(100% 0 0);
    --mu-13-surface: #f7f8f9;
    --mu-13-surface: oklch(97.5% 0.003 250);
    --mu-13-ink: #15181b;
    --mu-13-ink: oklch(19.5% 0.006 250);
    --mu-13-ink-2: #4f585f;
    --mu-13-ink-2: oklch(45% 0.012 250);
    --mu-13-fail-ink: #a9b0b5;
    --mu-13-fail-ink: oklch(75% 0.006 250);
    --mu-13-line: #d3d8db;
    --mu-13-line: oklch(86% 0.005 250);
    --mu-13-bad: #a11d1d;
    --mu-13-bad: oklch(44% 0.18 25);
    --mu-13-ok: #15613a;
    --mu-13-ok: oklch(43% 0.11 155);
    --mu-13-acc: #0f4faf;
    --mu-13-acc: oklch(44% 0.17 262);
    --mu-13-ring: #0f4faf;
    --mu-13-ring: oklch(44% 0.17 262);
    --mu-13-acc-ink: #ffffff;
    --mu-13-acc-ink: oklch(100% 0 0);
  }
}

.mu-13__sr {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.mu-13__theme {
  position: absolute;
  inset-block-start: 1rem;
  inset-inline-end: 1rem;
  z-index: 5;
}

.mu-13__theme-input {
  position: absolute;
  inset: 0;
  inline-size: 44px;
  block-size: 44px;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.mu-13__theme-label {
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-surface);
  color: var(--mu-13-ink-2);
  cursor: pointer;
}

.mu-13__theme-label svg {
  inline-size: 20px;
  block-size: 20px;
}

.mu-13__theme-input:focus-visible + .mu-13__theme-label {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__stage {
  max-inline-size: 78rem;
  margin-inline: auto;
  padding: 4rem 1.25rem 5rem;
  display: grid;
  gap: 2.5rem;
  grid-template-columns: minmax(0, 1fr);
}

.mu-13__board {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}

.mu-13__panel {
  min-inline-size: 0;
  display: grid;
  gap: 0.875rem;
  align-content: start;
  padding: 1.25rem;
  background: var(--mu-13-surface);
  border: 1px solid var(--mu-13-line);
  border-radius: 6px;
}

.mu-13__panel-head {
  display: grid;
  gap: 2px;
  min-inline-size: 0;
}

.mu-13__panel-title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.mu-13__crit {
  margin: 0;
  font-family: var(--mu-13-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--mu-13-bad);
}

.mu-13__sample {
  min-inline-size: 0;
  display: grid;
  gap: 0.5rem;
  padding: 1rem;
  background: var(--mu-13-bg);
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
}

.mu-13__sample p {
  margin: 0;
  min-inline-size: 0;
}

.mu-13__sample-title {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.012em;
}

.mu-13__secondary {
  font-size: 0.875rem;
  color: var(--mu-13-fail-ink);
  text-wrap: pretty;
}

.mu-13__panel.is-fixed .mu-13__secondary {
  color: var(--mu-13-ink-2);
}

.mu-13__fix-only {
  display: none;
}

.mu-13__panel.is-fixed .mu-13__fail-only {
  display: none;
}

.mu-13__panel.is-fixed .mu-13__fix-only {
  display: grid;
}

.mu-13__toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  min-inline-size: 0;
}

.mu-13__toolbar > span {
  display: flex;
  gap: 0.5rem;
}

.mu-13__panel.is-fixed .mu-13__toolbar > .mu-13__fix-only {
  display: flex;
}

.mu-13__icon-btn {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: 44px;
  block-size: 44px;
  min-inline-size: 44px;
  min-block-size: 44px;
  padding: 0;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-surface);
  color: var(--mu-13-ink);
  cursor: pointer;
}

.mu-13__icon-btn svg {
  inline-size: 20px;
  block-size: 20px;
}

.mu-13__icon-btn:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__tip {
  position: absolute;
  inset-block-start: calc(100% + 6px);
  inset-inline-start: 0;
  padding: 4px 8px;
  border-radius: 3px;
  white-space: nowrap;
  background: var(--mu-13-ink);
  color: var(--mu-13-bg);
  font-size: 0.6875rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}

.mu-13__icon-btn:hover .mu-13__tip,
.mu-13__icon-btn:focus-visible .mu-13__tip {
  opacity: 1;
}

.mu-13__fields {
  gap: 0.75rem;
}

.mu-13__field {
  display: grid;
  gap: 0.25rem;
  min-inline-size: 0;
}

.mu-13__label {
  font-size: 0.8125rem;
  font-weight: 500;
}

.mu-13__input {
  inline-size: 100%;
  min-inline-size: 0;
  min-block-size: 44px;
  padding-inline: 0.625rem;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-bg);
  color: var(--mu-13-ink);
  font: inherit;
  font-size: 0.875rem;
}

.mu-13__input::placeholder {
  color: var(--mu-13-ink-2);
  opacity: 1;
}

.mu-13__input:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
  border-color: var(--mu-13-acc);
}

.mu-13__fail-only > .mu-13__input + .mu-13__input {
  margin-block-start: 0.5rem;
}

.mu-13__target-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.mu-13__target {
  min-block-size: var(--mu-13-target-fail);
  block-size: var(--mu-13-target-fail);
  padding-inline: 0.625rem;
  border: 1px solid var(--mu-13-line);
  border-radius: 3px;
  background: var(--mu-13-surface);
  color: var(--mu-13-ink);
  font: inherit;
  font-size: 0.75rem;
  cursor: pointer;
}

.mu-13__target:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__panel.is-fixed .mu-13__target {
  min-block-size: 44px;
  block-size: 44px;
  min-inline-size: 44px;
  padding-inline: 1.125rem;
  border-color: transparent;
  background: var(--mu-13-acc);
  color: var(--mu-13-acc-ink);
  font-size: 0.875rem;
  font-weight: 600;
  border-radius: 4px;
}

.mu-13__panel.is-fixed .mu-13__target + .mu-13__target {
  background: transparent;
  color: var(--mu-13-ink);
  border-color: var(--mu-13-line);
}

.mu-13__measure {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  font-family: var(--mu-13-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.mu-13__measure-fail {
  color: var(--mu-13-bad);
  font-weight: 600;
}

.mu-13__measure-ok {
  display: none;
  color: var(--mu-13-ok);
  font-weight: 600;
}

.mu-13__panel.is-fixed .mu-13__measure-fail {
  display: none;
}

.mu-13__panel.is-fixed .mu-13__measure-ok {
  display: inline;
}

.mu-13__switch {
  justify-self: start;
  min-block-size: 44px;
  min-inline-size: 44px;
  display: inline-flex;
  align-items: center;
  padding-inline: 0.875rem;
  border: 1px solid var(--mu-13-line);
  border-radius: 4px;
  background: var(--mu-13-bg);
  color: var(--mu-13-ink);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
}

.mu-13__switch:focus-visible {
  outline: 2px solid var(--mu-13-ring);
  outline-offset: 2px;
}

.mu-13__switch[aria-pressed="true"] {
  border-color: var(--mu-13-ok);
  color: var(--mu-13-ok);
}

@media (prefers-reduced-motion: reduce) {
  .mu-13__tip {
    transition: none;
  }
}

@media (min-width: 56rem) {
  .mu-13__stage {
    padding-inline: 3rem;
  }

  .mu-13__panel {
    padding: 1.5rem;
  }
}
```

## JavaScript
```js
(() => {
  const board = document.querySelector('#mu-13-board');
  if (!board) return;
  const switches = [...board.querySelectorAll('[data-mu-13-switch]')];
  const handlers = new Map();

  for (const btn of switches) {
    const panel = btn.closest('[data-mu-13-panel]');
    if (!panel) continue;
    const onClick = () => {
      const next = btn.getAttribute('aria-pressed') !== 'true';
      btn.setAttribute('aria-pressed', String(next));
      panel.classList.toggle('is-fixed', next);
      btn.querySelector('.mu-13__switch-label').textContent =
        next ? 'Show the failure' : 'Show the fix';
    };
    btn.addEventListener('click', onClick);
    handlers.set(btn, onClick);
  }

  board.__muCleanup = () => {
    for (const [btn, fn] of handlers) btn.removeEventListener('click', fn);
    handlers.clear();
  };
})();
```

How this works

These four failures are not carelessness — they are aesthetic decisions with an accessibility cost nobody priced. Each one makes a screenshot look better. Lighter secondary text is calmer. An icon with no label is tidier. A placeholder instead of a label removes a line of text. A 32px button is more elegant than a 44px one. That is exactly why this collection has to name them: minimalism is the design language most likely to produce them.

Contrast, SC 1.4.3. Body text needs 4.5:1; large text — 24px, or 18.66px bold — needs 3:1. The failing panel prints 2.9:1, which is the value you land on when you pick a grey that looks refined on a good display in a dark room. Fixed, it is 7.0:1 and the panel is not noticeably louder, because the hierarchy was never coming from the colour.

Names, labels and targets. An icon-only button has no accessible name unless you give it one: aria-label, or visually hidden text, and SC 4.1.2 requires it. A placeholder is not a label — it disappears on focus, fails SC 3.3.2, and is invisible to a user who has already started typing; keep the placeholder for the format hint and add a real <label>. And SC 2.5.8 sets 24×24px as the AA floor with 44×44px as the AAA target, which is what every mobile guideline has recommended for a decade — so use 44 and stop having the argument.

Say where minimalism is simply the wrong choice. For dense data tools, safety-critical controls, and any interface where discoverability matters more than calm, a denser and more explicit design serves users better. A hidden nav, an unlabelled icon row and a whisper-quiet secondary action are all legitimate in a marketing page and dangerous in a hospital console. The honest position is that restraint is a tool with a domain, not a virtue that applies everywhere.

Make it yours

  • Change --mu-13-fail-ink to test other &ldquo;subtle&rdquo; greys and watch the printed ratio move.
  • Set --mu-13-target-fail to see where a shrunken target stops being tappable on a real phone.
  • Swap the tooltip on the fixed icon button for permanently visible text if the icon is not universally understood.
  • Retune the focus ring via --mu-13-ring; keep it at 3:1 against both the control and the surface.
  • Delete the switches and the script to ship the four corrected panels as a reference board.
  • Recolour by editing the two --mu-13-acc declarations, hex first then oklch.

Gotchas — read before shipping

  • Light grey secondary text at 2.8:1 sold as subtlety is the single most common accessibility failure in minimalist work. It is measurable, so measure it.
  • An icon-only button with no accessible name is unusable with a screen reader and unlabelled for voice control, which cannot target a control it cannot name.
  • A placeholder used as a label vanishes the moment the user types, so the one person who most needs the label loses it exactly when they need it.
  • Choosing a 32px target because 44px looked heavy trades an aesthetic gain for a functional failure. Remove the border or reduce the padding instead — the target can stay big and look light.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by :has(), oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.

oklch() and the :has() theme toggle set the floor, not the accessibility work — everything corrected here (labels, accessible names, target sizes, focus rings) is plain HTML and CSS that works in every engine. :focus-visible needs Chrome 86 / Safari 15.4 / Firefox 85. The 24-line switch script has a cleanup handle; with script blocked, each panel stays on its failing state with the criterion still printed, so the teaching survives.

Techniques used in this demo

Search CodeFronts

Loading…