Take Basecoat's form layer: one component draws 117 rows, and the framework already has the rest #290
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#290
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
#262 brought Basecoat in one component at a time and adopted four: button, popover, dropdown-menu and switch. The framework ships thirty-eight, and twenty-four of them need no JavaScript at all — which answers the only objection recorded against the rest.
scripts/sync-vendor.mjssays Basecoat's scripts are not vendored because a menu driven by script has no path with scripts off; the form components are not those.field,label,input,textarea,native-select,checkbox,radioandinput-groupare stylesheet and nothing else.This issue is the form layer. It is the largest single surface in the application and the one where adoption costs least, because #263 already put every form row behind one component.
What Postulo draws today
Four classes in
assets/css/app.css, written before there was a framework to take them from:field-inputassets/css/app.css:319— the box, the border, the focus ring,aria-invalidfield-labelassets/css/app.css:476field-helpassets/css/app.css:480field-errorassets/css/app.css:484They are applied in two ways, and the ratio is the argument for doing this now:
<c-field>(templates/cotton/field.html), which draws label, widget, help and errors in one place. Changing what a form row is means changing that one file.field-inputby hand, across 29 templates — the rows a single field cannot draw: the identifier rows, the telephone widget, the postal address rows, the formsets, and the settings pages with custom layouts.What Basecoat's form components are, and why they fit this shape exactly
They are element-scoped inside a wrapper, not a class per control:
So a form row is
<div class="field">with an ordinary<label>and an ordinary<input>inside it, and nothing carries a class at all. That is what<c-field>already emits the shape of. The swap is one component's innards plus the paint inassets/css/basecoat.css, and 117 rows change at once.It also brings what Postulo has never had:
data-orientation="horizontal"— a label beside its control rather than above it, which is what the checkbox rows on the settings pages hand-build withflex items-center gap-3today.[data-invalid]on the field, so an error styles the whole row rather than only the message. Postulo marksaria-invalidon the input alone.input-group(74 lines, no script) — a control with something attached to it. Postulo hand-builds exactly this three times:partials/phone_widget.html:21-34,partials/postal_addresses.html:57andserver/defaults.html:27, each arelativewrapper withabsolute inset-y-0 start-2.5over a select padded withps-9, to put a flag over a chooser. One component replaces all three, and the next field that wants a prefix or a unit gets it for nothing.native-select— Postulo styles<select>withfield-input, which is an input's box on a control that is not one.The order to do it in
Each import is a deliberate act, as #262 established, and
tests/test_stylesheet.pyrefuses an import whose classesapp.cssalso defines. None of these collide with a name Postulo owns —app.cssdefinesfield-input,field-label,field-help,field-error, not a barefield,labelorinput— so this family can arrive without a rename, which is not true ofcardoralert.label,input,textarea,native-select,field— and<c-field>emits the structure. The fourfield-*classes stay for one commit so the hand-written rows keep working.checkboxandradio, which the settings and plugin pages draw by hand.input-group, and the three flag fields become one component.<c-field-row>or the shape each wants — and the fourfield-*classes are deleted.tests/test_template_lint.pygains a rule refusing them, as it did for the retired button classes.What must not change
The floor is tested and none of this is allowed to lower it:
tests/test_contrast.pyholds the field border at 3:1 against the page and the field in both themes, and the focus ring opaque (#274). Basecoat's border isborder-input, which is already Postulo's token, so the paint decides this and the test keeps deciding it.app.cssopts the field's focus outline back in, because a box-shadow ring is discarded (#227, #277). Basecoat uses a ring too, so that block has to cover the new selectors.tests/e2e/test_target_size.py), reflow at 320 in three languages, the text-spacing override and 200% zoom (tests/e2e/test_text_spacing.py). A denser control is exactly what those catch.Done when
<c-field>emits Basecoat's field structure, the fourfield-*classes are gone fromapp.cssand from every template, andtest_template_lint.pyrefuses them.input-groupcomponent.assets/css/basecoat.csspaints the new families, andtest_stylesheet.py's ownership guard still passes.test_contrast.pyholds the same ratios against the new selectors.