Summary
Required form fields carry the HTML required attribute but nothing visible marks them as required. On the Bulk Import create form, "Submission Name" and "Recipient base URL" look identical to the four optional fields below them. The same is true of "Tenant ID" on the Tenants page. There is no required-field convention; one should be defined and applied wherever a form has a mix of required and optional inputs.
Current behavior
Open /ui/bulk-import and expand "Create Bulk Submission". Six text fields render, all with the same .field / .field__label / .field__input markup:
| Field |
Required? |
Visual difference |
Submission Name (crates/ui/templates/pages/bulk-import.html:24-27) |
yes — required |
none |
Recipient base URL (:28-33) |
yes — required |
none |
Submitter system (:35-39) |
no |
none |
Submitter value (:40-44) |
no |
none |
Submission ID (:45-49) |
no |
none |
.field__label (crates/ui/assets/app.css:1169-1175) has no required variant, and there is no :required selector anywhere in app.css. The only signal the user gets is the browser's native validation bubble after they press submit — which arrives after the form is filled out, not while they are deciding what to fill in.
Same pattern on the Tenants add-tenant form (crates/ui/templates/pages/tenants.html:68-79): "Tenant Name" is optional and "Tenant ID" is required, and the two are visually identical.
Every required input in the app has this problem — there are only six:
crates/ui/templates/pages/bulk-import.html:26 — name
crates/ui/templates/pages/bulk-import.html:30 — recipient_base_url
crates/ui/templates/pages/bulk-import-detail.html:28 — manifest_url
crates/ui/templates/pages/tenants.html:76 — tenant id
crates/ui/templates/partials/search-builder.html:21 — query url
A required-marker convention does already exist, but only inside the schema-driven resource editor: crates/ui/templates/partials/editor-body.html:179 and :191 append <em>*</em> to required element names, styled at crates/ui/assets/app.css:3750-3753 as a non-italic accent-colored mark. Nothing outside the editor uses it.
Expected behavior
- Required fields are visually distinguishable from optional ones before submit — a marker on the label, following one convention across the app.
- The convention is the same one the resource editor already uses, or a deliberate replacement applied to both.
- The marker is not the only signal for assistive tech:
required stays on the input, and if the marker is a bare glyph it is either aria-hidden with an accessible-name alternative or given text the screen reader can read.
- Applied to all five required inputs listed above, not just Bulk Import.
Evidence
crates/ui/templates/pages/bulk-import.html:24-49 — required and optional fields, indistinguishable markup
crates/ui/templates/pages/tenants.html:68-79 — optional display_name next to required id
crates/ui/assets/app.css:1169-1200 — .field__label / .field__input / .field__hint; no required variant, no :required rule
crates/ui/templates/partials/editor-body.html:179, :191 and crates/ui/assets/app.css:3750-3753 — the existing <em>*</em> marker, editor-only
crates/ui/src/bulk_import.rs:291-330 — CreateForm takes name and recipient_base_url as non-optional String, confirming which fields the server actually needs
Suggested approach
- Add a
.field__label--required (or a .field__req span) to app.css reusing the editor's treatment — accent-colored, non-italic marker — and document it in crates/ui/README.md alongside the copy conventions.
- Decide the accessible spelling: either the marker carries
aria-hidden="true" and the input keeps required (screen readers announce "required" from the attribute), or the label includes a localized "required" word. Pick one and apply it everywhere.
- Apply to the five required inputs listed above.
- Consider whether the inverse convention is better for forms that are mostly required — marking the optional fields instead. The Bulk Import form is 2 required of 6, so marking required is the right call there.
- Add the marker string to
locales/en|de|es/main.ftl if the chosen approach needs localized text.
Scope / out of scope
Visual and accessible marking of required fields. Not in scope: changing which fields are required, adding client-side validation beyond what required already does, or restyling the error state shown after a failed submit (error presentation is tracked in #676).
Summary
Required form fields carry the HTML
requiredattribute but nothing visible marks them as required. On the Bulk Import create form, "Submission Name" and "Recipient base URL" look identical to the four optional fields below them. The same is true of "Tenant ID" on the Tenants page. There is no required-field convention; one should be defined and applied wherever a form has a mix of required and optional inputs.Current behavior
Open
/ui/bulk-importand expand "Create Bulk Submission". Six text fields render, all with the same.field/.field__label/.field__inputmarkup:crates/ui/templates/pages/bulk-import.html:24-27)required:28-33)required:35-39):40-44):45-49).field__label(crates/ui/assets/app.css:1169-1175) has no required variant, and there is no:requiredselector anywhere inapp.css. The only signal the user gets is the browser's native validation bubble after they press submit — which arrives after the form is filled out, not while they are deciding what to fill in.Same pattern on the Tenants add-tenant form (
crates/ui/templates/pages/tenants.html:68-79): "Tenant Name" is optional and "Tenant ID" isrequired, and the two are visually identical.Every required input in the app has this problem — there are only six:
crates/ui/templates/pages/bulk-import.html:26—namecrates/ui/templates/pages/bulk-import.html:30—recipient_base_urlcrates/ui/templates/pages/bulk-import-detail.html:28—manifest_urlcrates/ui/templates/pages/tenants.html:76— tenantidcrates/ui/templates/partials/search-builder.html:21— queryurlA required-marker convention does already exist, but only inside the schema-driven resource editor:
crates/ui/templates/partials/editor-body.html:179and:191append<em>*</em>to required element names, styled atcrates/ui/assets/app.css:3750-3753as a non-italic accent-colored mark. Nothing outside the editor uses it.Expected behavior
requiredstays on the input, and if the marker is a bare glyph it is eitheraria-hiddenwith an accessible-name alternative or given text the screen reader can read.Evidence
crates/ui/templates/pages/bulk-import.html:24-49— required and optional fields, indistinguishable markupcrates/ui/templates/pages/tenants.html:68-79— optionaldisplay_namenext to requiredidcrates/ui/assets/app.css:1169-1200—.field__label/.field__input/.field__hint; no required variant, no:requiredrulecrates/ui/templates/partials/editor-body.html:179,:191andcrates/ui/assets/app.css:3750-3753— the existing<em>*</em>marker, editor-onlycrates/ui/src/bulk_import.rs:291-330—CreateFormtakesnameandrecipient_base_urlas non-optionalString, confirming which fields the server actually needsSuggested approach
.field__label--required(or a.field__reqspan) toapp.cssreusing the editor's treatment — accent-colored, non-italic marker — and document it incrates/ui/README.mdalongside the copy conventions.aria-hidden="true"and the input keepsrequired(screen readers announce "required" from the attribute), or the label includes a localized "required" word. Pick one and apply it everywhere.locales/en|de|es/main.ftlif the chosen approach needs localized text.Scope / out of scope
Visual and accessible marking of required fields. Not in scope: changing which fields are required, adding client-side validation beyond what
requiredalready does, or restyling the error state shown after a failed submit (error presentation is tracked in #676).