Skip to content
Fleetline

Form layout

Usage

Use this layout for every page-level form in Fleetline: logging a service, creating a work order, editing a vehicle. Dialog forms reuse the same fields but put actions in the modal footer.

Anatomy

  1. Error summary (invalid state) — .alert.alert--danger.form-layout__summary with links to each field
  2. Section — section.form-layout__section: .form-layout__intro (heading + description) and .form-layout__fields
  3. Fields — .field, fieldset.fieldset, fieldset.date-field, fieldset.checkbox-group
  4. Row (optional) — .form-layout__row for two short related fields
  5. Divider — hr.divider between sections
  6. Action bar — .form-layout__actions, optionally --sticky

Examples

Form layout

variant:single-column — quick log from a tablet

Log service

All fields are required unless marked optional.

km

As shown on the dashboard.

variant:two-column — short related fields only

Close work order WO-4182

Van KX-219 · Marta Quintero

h
€

Labour and parts sit side by side from 28rem of form width and stack below it. Longer fields always keep the full width.

variant:sections variant:sticky-actions — full "Log service" form, scroll inside the frame

Log service

Record a completed service so the next one is scheduled. All fields are required unless marked optional.

Vehicle

The odometer reading sets when the next service is due.

km

Last recorded: 79,950 km on 2 June 2026.


Service

What was done and when.

Service date

For example, 8 10 2026

Which checks were done?

Select all that apply.

Is the vehicle fit to drive?

Notes and follow-up

Dispatch sees these on the vehicle record.

Defects found, parts to order, anything the driver should know.

Up to 500 characters

Three sections with headings and one-line descriptions, separated by dividers. Scroll the frame: the action bar stays at the bottom edge, primary first.

state:invalid — after submit, focus moves to the summary

Log service

Error: Choose the vehicle you serviced.

Last recorded: 79,950 km on 2 June 2026.

Error: Enter a reading higher than the last one (79,950 km).

km
Service date

For example, 8 10 2026

Error: Service date must be a real date.

The summary title counts the problems; each link moves focus to its field and repeats the inline message word for word. Entered values are kept.

<form class="form-layout" action="/service-logs" method="post" novalidate aria-labelledby="log-t">
  <!-- after a failed submit: render the summary and focus it -->
  <div class="alert alert--danger error-summary form-layout__summary" tabindex="-1" aria-labelledby="sum-t">
    <svg class="alert__icon" aria-hidden="true">…</svg>
    <div class="alert__content">
      <h2 class="alert__title" id="sum-t">There are 2 problems with this service log</h2>
      <ul class="list alert__description"><li><a class="link" href="#odometer">Enter a reading higher than the last one</a></li>…</ul>
    </div>
  </div>
  <section class="form-layout__section" aria-labelledby="s-vehicle">
    <div class="form-layout__intro"><h2 class="text-h5" id="s-vehicle">Vehicle</h2><p class="text-small text-muted">…</p></div>
    <div class="form-layout__fields">
      <div class="field">…</div>
      <div class="form-layout__row"><div class="field">…</div><div class="field">…</div></div>
    </div>
  </section>
  <hr class="divider">
  …
  <div class="form-layout__actions form-layout__actions--sticky">
    <button type="submit" class="btn btn--primary">Save service log</button>
    <a class="btn btn--secondary" href="/vehicles/kx-219">Cancel</a>
  </div>
</form>

API

HookValuesPurpose
.form-layoutblock on <form>Single column, max --size-container-sm; size container for rows
.form-layout__section | __intro | __fieldspartsTopic group, its heading + description, the field stack
.form-layout__rowpartTwo short related fields side by side from 28rem
.form-layout__actions + --stickypart + modifierAction bar; sticky to the bottom of the scrollport
.form-layout__summarypart on .alert--dangerError summary; tabindex="-1" so it can take focus
aria-invalid="true" + .field__errorattribute + partInline error, referenced by aria-describedby

Do and don't

Do keep one column and put only short, related fields (labour + parts cost) in a row.
Don't lay unrelated fields out in two columns: people skip the right-hand column and the tab order zig-zags.

Accessibility

Keyboard and focus behavior
MomentBehavior
Submit with errorsFocus moves to the error summary (tabindex="-1"); its heading is read first, then the list of links
Enter on a summary linkMoves focus to the field (or the first input of a date field); the inline error is read through aria-describedby
TabFollows the visual order: sections top to bottom, row fields left to right, then the action bar

Tokens

Custom propertyPurpose
--size-container-smMaximum form width
--space-8, --space-5, --space-4, --space-3, --space-1Between sections, fields, row columns, actions, heading and description
--color-bg-surface, --color-border-default, --z-stickySticky action bar surface, top rule and stacking