Forms
Bootstrap form controls. Patterns below; usage → Rules.
Input
form-label + form-control. Optional form-text for hints.
html
<label class="form-label" for="company-name">Company name</label>
<input
class="form-control"
id="company-name"
type="text"
placeholder="Van den Berg Logistics"
/>
<label class="form-label" for="due-date">Due date</label>
<input
class="form-control"
id="due-date"
type="date"
placeholder="12 Mar 2026"
/>
<div class="form-text">Display as 12 Mar 2026</div>Input group
Prefix with input-group-text.
€
html
<label class="form-label" for="currency">Amount</label>
<div class="input-group">
<span class="input-group-text">€</span>
<input class="form-control" id="currency" type="text" placeholder="12.400" />
</div>Search
input-group-merge (not a Bootstrap class): type="text" + role="searchbox" (not type="search"), and bi-x-circle-fill to clear.
html
<label class="form-label" for="search-contacts">Search</label>
<div class="input-group input-group-merge">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input
class="form-control"
id="search-contacts"
type="text"
role="searchbox"
autocomplete="off"
placeholder="Search contacts"
value="Marieke"
/>
<button type="button" class="input-group-text" aria-label="Clear search">
<i class="bi bi-x-circle-fill"></i>
</button>
</div>Shortcut
Command or global search: trailing input-group-shortcut inside input-group-merge. Same action as the shortcut; aria-label on the control.
html
<div class="input-group input-group-merge">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input
class="form-control"
type="text"
role="searchbox"
autocomplete="off"
placeholder="Search…"
aria-label="Search"
/>
<button
type="button"
class="input-group-shortcut"
aria-label="Open search (⌘K)"
>
⌘K
</button>
</div>Sizes
form-control-sm is 32px / 13px (the floor). Default is 36px / 14px. -lg is 42px / 14px. Same for form-select.
html
<input
class="form-control form-control-sm"
type="text"
placeholder="Small — 32px · 13px"
/>
<input class="form-control" type="text" placeholder="Default — 36px · 14px" />
<input
class="form-control form-control-lg"
type="text"
placeholder="Large — 42px · 14px"
/>Textarea
form-control with rows. Same label pattern as input.
html
<label class="form-label" for="note">Note</label>
<textarea
class="form-control"
id="note"
rows="4"
placeholder="Imported from CSV"
></textarea>Select
form-select for a single owner or category choice.
html
<label class="form-label" for="owner">Owner</label>
<select class="form-select" id="owner">
<option>Marieke de Groot</option>
<option>Anouk Peters</option>
<option>Lars Hendriks</option>
</select>Checkbox
form-check with type="checkbox". Label-less in table rows (aria-label on the input). Optional .desc inside the label for a sub-line.
html
<input class="form-check-input" type="checkbox" checked aria-label="Checked" />
<input class="form-check-input" type="checkbox" aria-label="Unchecked" />
<div class="form-check">
<input class="form-check-input" id="email-opt-in" type="checkbox" checked />
<label class="form-check-label" for="email-opt-in">
Email opt-in
<span class="desc">Include in campaign sends.</span>
</label>
</div>Radio
Same form-check pattern with type="radio" and a shared name.
html
<div class="form-check">
<input class="form-check-input" id="priority-normal" name="priority" type="radio" checked />
<label class="form-check-label" for="priority-normal">Normal</label>
</div>
<div class="form-check">
<input class="form-check-input" id="priority-urgent" name="priority" type="radio" />
<label class="form-check-label" for="priority-urgent">Urgent</label>
</div>Pill radio group
Two to four short labels in a form. Same form-check markup inside sq-pill-radio. Icons are CSS on the label.
html
<div class="sq-pill-radio" role="radiogroup" aria-label="Interested">
<div class="form-check">
<input class="form-check-input" id="pill-interested-yes" name="pill-interested" type="radio" checked />
<label class="form-check-label" for="pill-interested-yes">Yes</label>
</div>
<div class="form-check">
<input class="form-check-input" id="pill-interested-no" name="pill-interested" type="radio" />
<label class="form-check-label" for="pill-interested-no">No</label>
</div>
<div class="form-check">
<input class="form-check-input" id="pill-interested-unsure" name="pill-interested" type="radio" />
<label class="form-check-label" for="pill-interested-unsure">Not sure</label>
</div>
</div>Switch
form-check form-switch — same checkbox input, toggle styling.
html
<div class="form-check form-switch">
<input class="form-check-input" id="notify-updates" type="checkbox" checked />
<label class="form-check-label" for="notify-updates">Notify me on status changes</label>
</div>Range
Bootstrap form-range. Label row: d-flex justify-content-between align-items-baseline, output.font-monospace.small.text-secondary (for matches the input id).
html
<div class="mb-3">
<div class="d-flex justify-content-between align-items-baseline gap-3 mb-1">
<label class="form-label mb-0" for="deal-probability">Deal probability</label>
<output class="font-monospace small text-secondary mb-0" for="deal-probability">35%</output>
</div>
<input
class="form-range"
id="deal-probability"
type="range"
min="0"
max="100"
value="35"
step="1"
/>
</div>Validation
is-valid / is-invalid with valid-feedback or invalid-feedback.
Looks good.
Assign an owner before activating this step.
html
<label class="form-label" for="company-valid">Company name</label>
<input
class="form-control is-valid"
id="company-valid"
type="text"
value="Van den Berg Logistics"
/>
<div class="valid-feedback">Looks good.</div>
<label class="form-label" for="owner-required">Owner</label>
<select class="form-select is-invalid" id="owner-required">
<option value="">Select an owner</option>
</select>
<div class="invalid-feedback">Assign an owner before activating this step.</div>Disabled
45% opacity, not-allowed cursor, no colour change.
html
<label class="form-label" for="company-locked">Company name</label>
<input
class="form-control"
id="company-locked"
type="text"
value="Van den Berg Logistics"
disabled
/>Rules
Labels and validation
- Labels are nouns, no colon (“Company name”, “Owner”).
- Validation: name the missing field and next step — not “Something went wrong”.
Control choice
- Switch: takes effect immediately. Checkbox: saved with the form submit.
- Short exclusive pair: stacked radios. Two–four options:
sq-pill-radio. More: select. Not segmented (presentation) ornav-pills(page content / property filter) — Choose a control.
Input groups
Search: input-group-merge (Sqrl custom), role="searchbox", clear with bi-x-circle-fill. Command palette: input-group-shortcut. Chrome icons: Iconography.
Sizes
form-control-sm is the floor (32px); default and -lg for larger fields.