How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

How-to Forms

Style a form without forking every input template

CSS scoped to the form and aimed at the platform's own class names, a few classes the editors apply, and at most one or two inputs overridden with a partial that falls back to the platform's markup.

Context

Context. A design arrives with forms that look nothing like the platform's default markup, and the first instinct is to rewrite every field in the template. That fork stops working the day an editor adds a field. The platform prints every field in the same predictable structure with stable class names, so most designs need only CSS, a few editor-applied classes, and at most one or two overridden inputs. There is no per-input template to override, so this is the whole toolkit.

Steps

  1. Put a class on the form tag in the form layout (<form class="site-form" …>) and scope every rule under it.
  2. Style the platform's own class names, not the Bootstrap names it also prints: .input-element-block (each field's wrapper), .element-label, .element-input, .input-element-validation-errors, and the editor classes such as .inputSelect for one type. The full list is on Rendering and styling.
  3. Agree a short list of layout classes with the editors (half-width, inline-options), and have them set each field's Custom CSS classes in the form builder. The class lands on the field's wrapper.
  4. Where the markup itself is wrong for the design or for accessibility (radio cards, for instance), override that one input type with a partial and keep the platform's markup for everything else (below).
  5. Check a failed submit: errors styled, answers kept, the summary links working.

Code

CSS
.site-form .input-element-block { margin-bottom: 1.25rem; }
.site-form .element-label { display: block; font-weight: 600; margin-bottom: .375rem; }
.site-form .element-input:not([type="radio"]):not([type="checkbox"]) {
  width: 100%; padding: .625rem .75rem; border: 1px solid var(--field-border); border-radius: 6px;
}
.site-form .element-input:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }
.site-form .input-element-validation-errors { color: var(--error); margin-top: .375rem; }
.site-form .input-element-block:has(.input-element-validation-errors) .element-input { border-color: var(--error); }
.site-form .half-width { display: inline-block; width: calc(50% - .5rem); }
@media (max-width: 600px) { .site-form .half-width { display: block; width: auto; } }

And the one place the template changes: the form layout prints each element through a partial that uses the site's markup for listed types and the platform's for the rest. The partial and the radio override it lists are on Rendering and styling.

Template code
{foreach $step->getElements()->filtered('parent', null) as $element}
  {include file='forms/partials/element.html' element=$element form=$form}
{/foreach}
{% for element in step.getElements().filtered('parent', null) %}
  {% include 'forms/partials/element.html' with {element: element, form: form} %}
{% endfor %}

Why this and not that

  • Not a template that writes out every field. It looks finished on the day it is built. Then an editor adds a field and it does not appear, renames one and it stops submitting, or adds a conditional rule that cannot find the field because its data-id was not copied.
  • Not the Bootstrap class names. form-control, form-select and the rest are printed whatever the theme is. On a non-Bootstrap site they mean nothing, and rules written against them break if Bootstrap is ever added.
  • An override takes over everything for its input: the name, the answer after a failed submit, the error, the ids and the data-id. Keep the list of overrides short, and use the fallback for every type you have not deliberately taken over.
Go deeper

Was this page helpful?

Your answer goes to the docs team.