Forms

A complete guide to inputs, controls, layout, sizing, states and validation. Every example reacts to the style / dark / RTL controls above.

Anatomy of a field

Wrap each control in .field. Pair it with .label, optional .help text, and a hidden .error-text that appears on invalid input. .field stacks these with consistent spacing.

As it appears on your ID.
<div class="field">
  <label class="label" for="name">Full name</label>
  <input class="input" id="name" placeholder="Jane Doe">
  <span class="help">As it appears on your ID.</span>
</div>

Text inputs

The .input class works on every text-like type. Bare <input> is also styled in classless mode.

<input class="input" type="text" placeholder="Text">
<input class="input" type="email" placeholder="Email">
<input class="input" type="date">

Textarea & Select

<textarea class="textarea" placeholder="Your message…"></textarea>
<select class="select">
  <option>Free</option><option>Pro</option>
</select>

Sizes

Add .input-sm / .input-lg (and .select-sm / .select-lg).

<input class="input input-sm" placeholder="Small">
<input class="input" placeholder="Default">
<input class="input input-lg" placeholder="Large">

States

Disabled and read-only are handled natively; valid / invalid use the validation hooks below.

<input class="input" value="Disabled" disabled>
<input class="input" value="Read-only" readonly>
<input class="input is-valid" value="Looks good">
<input class="input is-invalid" value="Something off">

Checkbox, Radio & Switch

Native controls themed with accent-color, so they match every style automatically.

<div class="check"><input type="checkbox" id="c1"><label for="c1">Subscribe</label></div>
<div class="check"><input type="radio" name="plan" id="r1"><label for="r1">Monthly</label></div>
<label class="switch"><span>Notifications</span><input type="checkbox"><span class="track"></span></label>

File upload

<input type="file">

Input group

Join inputs with leading/trailing addons or buttons using .input-group + .input-addon. Radii and borders are handled on the ends, and it mirrors correctly in RTL.

@
https://
USD
<div class="input-group">
  <span class="input-addon">@</span>
  <input class="input" placeholder="username">
</div>

<div class="input-group">
  <input class="input" placeholder="Search…">
  <button class="btn btn-primary">Go</button>
</div>

Validation

LombokCSS layers two validation systems so you can use whichever fits:

1. Native, zero-JS

With required / type constraints, the browser sets :user-invalid after the user interacts. .field:has(:user-invalid) reveals the .error-text and hides .help automatically — no script. Type a bad email and click away:

We never share it. Please enter a valid email address.
Minimum 8 characters. Password is too short.
<div class="field">
  <label class="label" for="email">Email</label>
  <input class="input" id="email" type="email" required>
  <span class="help">We never share it.</span>
  <span class="error-text">Please enter a valid email address.</span>
</div>

2. Manual hook

When validating in JS (e.g. server response), toggle .is-invalid / .is-valid on the control, or .is-invalid on the .field to force the error text open.

// after a failed server check
field.classList.add("is-invalid");      // shows .error-text
input.classList.add("is-invalid");      // red border + focus ring
// on success
input.classList.replace("is-invalid", "is-valid");
Enter a promo code. This coupon has expired.
<div class="field is-invalid">
  <input class="input is-invalid" value="EXPIRED2023">
  <span class="error-text">This coupon has expired.</span>
</div>

Form layout

Compose multi-column forms with the grid utilities — no special form-grid classes needed.

<form class="grid grid-cols-1 md:grid-cols-2 gap-4">
  <div class="field">…first name…</div>
  <div class="field">…last name…</div>
  <div class="field col-span-full">…address…</div>
</form>

Accessibility notes

Always pair a <label for> with each control (or wrap the control in the label). Inputs show a visible :focus-visible ring derived from --lc-ring. For custom error messaging, link the message with aria-describedby and set aria-invalid="true" when you toggle the manual hook.