Forms
Every form field on the site — account pages, addresses, the storefront
password gate — is styled by one file:
frontend/entrypoints/css/blocks/forms.css. Add the class, get the brand
style. There is no per-section form styling.
The look matches the newsletter input in the footer (Figma node 2994:8516):
a white pill with a 1px Leaf border and Deep Green text.
The classes
Section titled “The classes”| Class | Use on |
|---|---|
.form-control |
Any input, select or textarea |
.form-label |
The <label> above a field |
.form-hint |
Helper text below a field |
.form-error |
An inline error message below a field |
.form-checkbox |
input[type="checkbox"] |
.form-radio |
input[type="radio"] |
.form-alert + .form-alert-error |
Error banner above a form |
.form-alert + .form-alert-success |
Success banner above a form |
A minimal field:
<label for="CustomerEmail" class="form-label">Email</label><input type="email" id="CustomerEmail" name="customer[email]" class="form-control" required>.form-control handles input, select and textarea from the same class —
selects get a chevron and the padding to clear it, textareas swap the pill
radius for a rounded rectangle (fully rounded ends clip the first and last
lines of text).
What you get automatically
Section titled “What you get automatically”- 50px tall, matching the design, from padding rather than a fixed height — so the field grows with its content instead of clipping.
- Focus: a 2px Deep Green outline at 2px offset, plus the border darkening
to Deep Green. Uses
outline, notbox-shadow, so it survives Windows High Contrast mode. - Disabled: muted Leaf tints and
cursor-not-allowed. - Invalid: a 2px Deep Green border, triggered by
aria-invalid="true".
Errors
Section titled “Errors”Set aria-invalid and point the field at its message:
<input type="email" id="CustomerEmail" class="form-control" aria-invalid="true" aria-describedby="CustomerEmail-error"><span class="form-error" id="CustomerEmail-error">Enter a valid email address.</span>Form-level banners go above the form and need a role so they are announced:
{% if form.errors %} <div class="form-alert form-alert-error" role="alert"> <p>{{ form.errors | default_errors }}</p> </div>{% endif %}Use role="alert" for errors and role="status" for success messages.
Accessibility rules baked in
Section titled “Accessibility rules baked in”These are not optional extras — they are why the classes look the way they do.
Errors are never signalled by colour alone. .form-error and the alert
banners each carry a leading glyph (! for errors, ✓ for success) so the two
are distinguishable without colour vision. The glyphs are pseudo-elements, so
they stay out of the accessible name; the message itself is announced through
role and aria-describedby.
The invalid border is Deep Green, not Soft Coral. Soft Coral measures 1.45:1 against a white fill — well under the 3:1 WCAG 2.2 AA floor for non-text UI — so as a border it is invisible to low-vision users. It is used as a background wash behind the error text instead, where the contrast requirement falls on the Deep Green text over it (9.9:1). The invalid state is carried by border thickness rather than hue.
Checkboxes and radios have a 44px tap target. The visible box is 20px, per the design; a transparent pseudo-element extends the hit area to 44px without changing the look.
Shopify-rendered checkboxes
Section titled “Shopify-rendered checkboxes”Some Shopify form objects emit a bare <input> you cannot add a class to —
form.set_as_default_checkbox on the address forms is the one in this theme.
Mark the wrapper instead:
<label class="flex items-center gap-3" data-shopify-checkbox> {{ form.set_as_default_checkbox }} <span class="text-body-sm text-deep-green">Set as default address</span></label>[data-shopify-checkbox] input[type="checkbox"] picks up the same styling.
Live examples
Section titled “Live examples”The style guide renders every field type and state — including focus, invalid,
disabled, hint text and both alert banners — at
/pages/contact-us?view=style-guide.