Forms

Pure-CSS styled replacements for native form controls. Each keeps the real <input> in the DOM (accessible, form-submittable) and renders a styled sibling that reacts to :checked. No JavaScript.

How Form Components Work

Every form component uses the same pattern: the native <input> is visually hidden (but stays in the DOM for keyboard + screen-reader support), and a styled sibling element is rendered next to it.

The visual element reads its size and color from the wrapping <label> (currentColor + em units), so you scale the whole control by applying Dopamine classes to the label.

Markup contract: the <input> must come before the visual sibling in the DOM. The CSS uses :checked ~ .foo__box to flip the visual on state change.

Include everything at once

All four form controls in one file (CSS):

<link rel="stylesheet" href="css/components/forms/forms.css">

Or via Sass:

@use 'dopamine-fluid/addons/components/forms/forms';

Each control is also available as its own individual file — use those if you only need one or two (see the per-control sections below).

Input

Minimal base for text-like <input>s (text, email, password, search, tel, url, number, date, time) and <textarea>. Resets browser defaults and keeps everything at currentColor / inherit — size, radius, and colors come from Dopamine classes applied on top.

Demo

Minimum HTML

<div>
  <label for="email">Email</label>
  <input type="email" id="email" class="df-input" placeholder="jane@example.com">
</div>

<div>
  <label for="message">Message</label>
  <textarea id="message" class="df-input" rows="4"></textarea>
</div>

Pair the <label for> with the input's id so clicking the label text focuses the input. Apply Dopamine classes directly on the .df-input for padding, radius, and font-size.

Include

<link rel="stylesheet" href="css/components/forms/input.css">

Or via Sass:

@use 'dopamine-fluid/addons/components/forms/input';

df-input — applies to any text-like <input> or <textarea>. Border uses currentColor; font + color are inherited.

textarea.df-input — automatically gets resize: vertical and a sensible minimum height.

Checkbox

Styled checkbox using the visually-hidden-input + sibling-selector technique. Native <input> stays in the DOM for accessibility and form submission; a styled .df-checkbox__box sibling visualizes the state via :checked ~ .checkbox__box. Pure CSS — no JS.

Demo

Minimum HTML

<label class="df-checkbox">
  <input type="checkbox" class="df-checkbox__input">
  <span class="df-checkbox__box"></span>
  <span class="df-checkbox__label">Label text</span>
</label>

The <input> must come before .df-checkbox__box in the DOM (the sibling selector depends on it). The box and label inherit currentColor and font-size — scale with Dopamine classes on .df-checkbox.

Include

<link rel="stylesheet" href="css/components/forms/checkbox.css">

Or via Sass:

@use 'dopamine-fluid/addons/components/forms/checkbox';

df-checkbox — wrapping <label> (click anywhere inside it toggles the input).

df-checkbox__input — the real <input type="checkbox">, visually hidden but keyboard-focusable.

df-checkbox__box — the styled square; inherits currentColor for border + fill.

df-checkbox__label — the text (optional; omit for a box-only checkbox).

Radio

Same technique as checkbox but circular. Radios with the same name="" attribute belong to one exclusive group. Pure CSS.

Demo

Minimum HTML

<label class="df-radio">
  <input type="radio" name="plan" class="df-radio__input">
  <span class="df-radio__box"></span>
  <span class="df-radio__label">Option A</span>
</label>
<label class="df-radio">
  <input type="radio" name="plan" class="df-radio__input">
  <span class="df-radio__box"></span>
  <span class="df-radio__label">Option B</span>
</label>

Include

<link rel="stylesheet" href="css/components/forms/radio.css">

Or via Sass:

@use 'dopamine-fluid/addons/components/forms/radio';

df-radio — wrapping <label>.

df-radio__input — the real <input type="radio">.

df-radio__box — the styled circle.

df-radio__label — the text.

Switch

Styled toggle switch (pill track + sliding thumb). Uses <input type="checkbox"> underneath — same form semantics as a checkbox, different visual. Pure CSS.

Demo

Minimum HTML

<label class="df-switch">
  <input type="checkbox" class="df-switch__input">
  <span class="df-switch__track"></span>
  <span class="df-switch__label">Label text</span>
</label>

Include

<link rel="stylesheet" href="css/components/forms/switch.css">

Or via Sass:

@use 'dopamine-fluid/addons/components/forms/switch';

df-switch — wrapping <label>.

df-switch__input — the real <input type="checkbox">.

df-switch__track — the pill-shaped track; thumb is a ::after inside it.

df-switch__label — the text.

Select

Native <select> with a styled rounded chevron. The dropdown option list stays native — the browser/OS handles it, which keeps accessibility, keyboard navigation, and mobile pickers intact. Only the closed control (border, padding, arrow) is styled.

Demo

Minimum HTML

<label for="plan">Plan</label>
<select id="plan" class="df-select">
  <option>Free</option>
  <option>Pro</option>
</select>

Apply Dopamine classes directly on the .df-select for padding, radius, and font-size — same pattern as .df-input.

Include

<link rel="stylesheet" href="css/components/forms/select.css">

Or via Sass:

@use 'dopamine-fluid/addons/components/forms/select';

df-select — applies to a native <select>. Border uses currentColor; font + color are inherited.

The chevron stroke is a fixed neutral — override background-image on .df-select to retheme.

GitHub