--- // Form group: label (.form-label) + control slot + optional hint (.form-text). // When `for` is omitted and associate is true (default), the control is nested // inside the label for implicit association. Set associate={false} for groups // with multiple controls. import FormText from './FormText.astro' interface Props { label?: string /** .required asterisk on the label — also set required on the control */ required?: boolean /** .form-label-description at the inline end of the label */ description?: string /** .form-text under the control */ hint?: string /** for attribute of the label — must match the slotted control's id */ for?: string /** extra classes on the label (e.g. visually-hidden) */ labelClass?: string /** wrapper classes; replaces the default mb-3 */ class?: string /** marks the group invalid — pair with aria-invalid and .is-invalid on the control */ invalid?: boolean /** error message shown under the control (.invalid-feedback) */ error?: string /** nest the control in the label when `for` is omitted (default true) */ associate?: boolean } const { label, required, description, hint, for: htmlFor, labelClass, class: className = 'mb-3', invalid, error, associate = true } = Astro.props // Hint/error ids exist only when the caller provides `for` — an auto-derived // id is unreferenceable by the slotted control anyway, and label-based slugs // collide between same-label groups on one page. const hintId = hint && htmlFor ? `${htmlFor}-hint` : undefined const errorId = error && htmlFor ? `${htmlFor}-error` : undefined const wrapLabel = Boolean(label && !htmlFor && associate) ---