COMPONENTS
Buttons, forms, tables, and the dynamic parts — tabs, dialogs,
dropdowns, toasts. Each one accessible, keyboard-friendly, and
ready to copy. The classes live in
css/mono.css;
the behaviors in
js/mono.js.
02 — Text input & textarea
Label, field, hint — in that order. Error states use a dashed border
and a message wired up with aria-describedby; never
color alone.
As it should appear in print.
That doesn't look like an email address.
Available.
Disabled fields go gray, not ghostly.
SHOW CODE
<div class="field">
<label class="label" for="name">Name</label>
<input class="input" type="text" id="name" placeholder="Ada Lovelace" />
<p class="hint">As it should appear in print.</p>
</div>
<!-- Error state -->
<div class="field">
<label class="label" for="email">Email</label>
<input class="input" type="email" id="email"
aria-invalid="true" aria-describedby="email-error" />
<p class="error-text" id="email-error">That doesn't look like an email address.</p>
</div>
<!-- Textarea -->
<div class="field">
<label class="label" for="bio">Bio</label>
<textarea class="input" id="bio" rows="3"></textarea>
</div>
03 — Select
A native <select> with the chrome stripped and a
CSS triangle for a marker — it inverts with the theme because it's
drawn in --ink.
SHOW CODE
<div class="field">
<label class="label" for="font">Typeface</label>
<div class="select">
<select id="font">
<option>Space Mono</option>
<option>JetBrains Mono</option>
</select>
</div>
</div>
04 — Checkbox & radio
Square check, round radio — geometry tells them apart before the
cursor does. Both are real inputs under
appearance: none.
SHOW CODE
<label class="choice">
<input type="checkbox" class="check" checked /> Release notes
</label>
<label class="choice">
<input type="radio" name="theme" class="radio" checked /> Ink on paper
</label>
05 — Toggle switch
A checkbox in a track. The thumb slides; with
prefers-reduced-motion it simply jumps.
SHOW CODE
<label class="choice">
<input type="checkbox" class="switch" checked /> Autosave
</label>
06 — Range
A hairline track and a square thumb. Add
data-mono-output and the value renders live into any
element.
SHOW CODE
<label class="label" for="contrast">Contrast — <output id="contrast-out"></output></label>
<input type="range" class="range" id="contrast" min="0" max="100" value="80"
data-mono-output="contrast-out" data-unit="%" />
07 — File input
The native input, with its button restyled through
::file-selector-button. No hidden-input tricks, so
keyboard and screen reader behavior stay stock.
Black and white images preferred, obviously.
SHOW CODE
<div class="field">
<label class="label" for="attachment">Attachment</label>
<input type="file" class="file" id="attachment" />
</div>
08 — Fieldset & validation
The full pattern: a <fieldset> with a legend, an
error summary at the top, and per-field messages tied in with
aria-invalid and aria-describedby.
SHOW CODE
<div class="border border-ink p-4" role="alert">
<p class="text-step--1 font-bold">2 fields need attention</p>
<ul class="text-step--1 mt-2 list-disc list-inside">
<li><a href="#email">Email is not valid</a></li>
</ul>
</div>
<fieldset>
<legend class="label">Create account</legend>
<div class="field">
<label class="label" for="email">Email</label>
<input class="input" type="email" id="email"
aria-invalid="true" aria-describedby="email-err" />
<p class="error-text" id="email-err">Email is not valid.</p>
</div>
</fieldset>
09 — Table
Heavy rule under the header, hairlines between rows. Add
.table-striped for zebra rows or
.table-dense for data-heavy views.
| Typeface | Designer | Weights | Year |
|---|---|---|---|
| Space Mono | Colophon Foundry | 400, 700 | 2016 |
| JetBrains Mono | JetBrains | 100–800 | 2020 |
| IBM Plex Mono | Bold Monday | 400, 700 | 2017 |
| Fragment Mono | Wei Huang | 400 | 2022 |
SHOW CODE
<table class="table table-striped">
<caption>The roster, by the numbers</caption>
<thead>
<tr><th scope="col">Typeface</th><th scope="col">Year</th></tr>
</thead>
<tbody>
<tr><td>Space Mono</td><td>2016</td></tr>
</tbody>
</table>
10 — Tabs
Proper role="tablist" semantics with arrow-key
navigation and roving tabindex — wired automatically by
js/mono.js.
Strip away the non-essential. Color, decoration, complexity — gone.
Perfect what remains: typography, spacing, and proportion.
Create patterns and relationships with limited elements.
SHOW CODE
<div class="tabs" role="tablist" aria-label="Sections">
<button class="tab" role="tab" id="tab-1" aria-controls="panel-1"
aria-selected="true">Reduce</button>
<button class="tab" role="tab" id="tab-2" aria-controls="panel-2"
aria-selected="false" tabindex="-1">Refine</button>
</div>
<div class="tabpanel" id="panel-1" role="tabpanel" aria-labelledby="tab-1">…</div>
<div class="tabpanel" id="panel-2" role="tabpanel" aria-labelledby="tab-2" hidden>…</div>
11 — Accordion
Native <details> — zero JavaScript, fully
keyboard accessible, and the browser remembers how it works.
Does it work without JavaScript?
Yes. This accordion is a native
<details> element; the only thing CSS adds is the plus-minus marker.
Can more than one be open?
By default, yes. Add a shared
name attribute to make them exclusive in modern browsers.
SHOW CODE
<details class="accordion">
<summary>Does it work without JavaScript?</summary>
<p>Yes. It's a native element.</p>
</details>
12 — Modal
Native <dialog>: focus trapping, Escape, and the
backdrop come free from the browser. Clicking outside closes it.
SHOW CODE
<button class="btn" data-mono-modal-open="confirm">Open modal</button>
<dialog id="confirm" class="modal" aria-labelledby="confirm-title">
<h3 id="confirm-title" class="text-step-2 font-bold">Delete everything?</h3>
<p>…</p>
<button class="btn btn-secondary" data-mono-modal-close>Cancel</button>
<button class="btn" data-mono-modal-close>Proceed</button>
</dialog>
13 — Dropdown menu
A button with aria-expanded and a panel. Escape or an
outside click closes it.
SHOW CODE
<div class="dropdown" data-mono-dropdown>
<button class="btn btn-secondary" aria-expanded="false"
aria-haspopup="true">Actions ▾</button>
<ul class="dropdown-menu" hidden>
<li><button>Duplicate</button></li>
<li><button>Delete</button></li>
</ul>
</div>
14 — Tooltip
Pure CSS via data-tooltip; it appears on hover and on
keyboard focus. Keep it to a few words — tooltips are captions, not
paragraphs.
SHOW CODE
<button class="btn btn-secondary"
data-tooltip="Saves to local storage">Hover or focus me</button>
15 — Toast
Call mono.toast('message') from anywhere, or put the
message in a data-mono-toast attribute. Toasts land in
a role="status" region and dismiss themselves.
SHOW CODE
<!-- Declarative -->
<button class="btn" data-mono-toast="Changes saved.">Save changes</button>
<!-- Programmatic -->
<script>
mono.toast("Changes saved.");
</script>