{{-- The shared shell every field is built from: wrapper, label with the wireframe's required asterisk, the control itself, and the inline error. Use it directly whenever a form needs a control this library does not ship (a file picker, an input-group, a repeater row) and you still want the label, asterisk and error to look and behave like every other field: Props: name Field name, in HTML form (may use [] / [0][x]). Required. label Label text. Omitted entirely when blank. required Renders the red asterisk. The control itself is responsible for its own `required` attribute. for DOM id of the control the label points at. Omit when the group wraps several controls (a checkbox group). group-class Extra classes for the wrapper, e.g. "col-md-6". hint Optional help text, rendered as a .form-text line between the control and the error with id "{field id}_hint" — the control in the slot should list that id in its aria-describedby (alongside "{field id}_error" when it can be invalid) so the hint is announced with the field. One documented exception: x-form.file renders its own hint *inside* the label that wraps its input, where it is part of the accessible name already, so it deliberately keeps the id and stays out of aria-describedby. CR-2 (spec §12). The wrapper is now the wireframe's `.form-group` (docs/wireframes-v12/add-project.html), which owns the 20px bottom margin that `fv-row mb-5` used to supply; the label loses Metronic's `fw-semibold`, because `.form-label` is already 500 in the wireframe and the utility beat it with !important; and the hint drops `text-muted fs-8` for the same reason — `.form-text` is already 12px and muted. The asterisk is a real * — the wireframe's own element and class (`.form-label .required`) — rather than a `.form-label.required::after` pseudo-element, so the same markup can be reproduced by a non-Blade renderer. Do not add the `required` class to the *label* as well: theme-a.css still carries that pseudo-element rule and the asterisk would double up. It carries an aria-label because a bare "*" is announced as "star", or skipped altogether. `invalid-feedback` stays Bootstrap's name. It is a behaviour hook as much as a style: laravel-jsvalidation writes its client-side messages into that node and toggles `.is-invalid` on the control. The wireframe never drew a field with an error in it, so both are styled from the app-additions tail of nrems-wireframe.css. --- spatie/laravel-html variant: the element tree below is built with html() instead of literal Blade tags. The props, the helper calls and the rendered markup are the originals; only the way the string is produced changed. --}} @props([ 'name', 'label' => null, 'required' => false, 'for' => null, 'groupClass' => null, 'hint' => null, ]) @php $error = form_error_message($name); $group = html()->div() ->class('form-group') ->addClassIf(filled($groupClass), (string) $groupClass); if (filled($label)) { $group = $group->addChild( html()->label() ->class('form-label') ->attributeIf(filled($for), 'for', (string) $for) ->text((string) $label) ->addChildIf($required, html()->span() ->class('required') ->attribute('aria-label', __('forms.required_field')) ->text('*')) ); } // The slot is already-rendered HTML: it goes in as a raw string child. $group = $group->addChild($slot->toHtml()); if (filled($hint)) { $group = $group->addChild( html()->div() ->class('form-text') ->id(form_field_id($name).'_hint') ->text((string) $hint) ); } if ($error !== null) { $group = $group->addChild( html()->div() ->class('invalid-feedback d-block') ->id(form_field_id($name).'_error') ->text($error) ); } @endphp {!! $group->render() !!}