The rules — one grid, plain labels, the validator marks required
- Fields sit on the form grid:
.row.g-3withcol-12 col-md-6pairs (full-width fields plaincol-12) — the SAME mobile-first columns as everything else. - Every field:
label.form-label(withfor) + the control + optional.form-texthint under it. Placeholders are examples of INPUT (ana@firma.ro), never a substitute for the label. - There is NO visual required mark — required is the validator's
validate-requiredattribute and the inline error it renders. Labels stay plain (the framework pages set the canon). - Inputs stay the default size (
.form-control/.form-selectas they are) — no-sm/-lgfields in forms. - Buttons live in the card footer whenever the form owns the card (the
formelement wraps.card-body+.card-footer): Cancel LEFT as.btn-soft-outline-secondary, the action buttons RIGHT-aligned (.ms-autoon the first of them), the primary last. Modals: the same rule in.modal-footer— Cancel takes.me-auto. - When there is no footer to use (a form inside a tab pane), the same arrangement sits after the fields:
<div class="mt-4 d-flex justify-content-end gap-2">, Cancel (if any) pushed left with.me-auto. Any OTHER button arrangement is custom — outside the examples standard. - Selects with search/tags ride selekt, dates date-selekt, uploads file-selekt — plain
.form-select/type="date"only for trivial cases.
The standard form — grid, labels, hints, buttons in the card footer
Checks, radios, switches — the form-check family
Checkbox + inline radios
Switch — and the settings-row look (label first, start-aligned)
- Switches are ON/OFF settings that apply immediately or on save; checkboxes are declarations/selections.
role="switch"is required. .form-check-reverseis THE settings-row look — label first, the switch right after it, start-aligned; no.text-end(see settings).- Group validation and where the error lands:
templates/app/docs/components/forms.md+ validator.