Input group
Markup canonic · components/input-group.json
Anatomie · addon-uri în interiorul bordurii
Iconiță, text static, buton, spinner, scurtătură — lipite de control, în aceeași bordură. Regula: bordura trece pe carcasă, controlul o pierde. Echivalentul lui InputGroup din shadcn; din v8.2 are contract propriu (components/input-group.json), fiindcă are clase, variante, stări și o componentă React ale lui.
Granița e cea din capul paginii: eticheta, ajutorul și eroarea sunt ale câmpului; ce e aici e în bordură. Căutarea din topbar n-are etichetă, deci e .input-group singur.
.input-group carcasa · h 40 · padding 0 16 · gap 8 · r --r-lg ├── .input sau textarea.input controlul, copil direct — fără bordură, fundal, înălțime ├── .input-group-addon slotul · 14px fog · svg 16 · implicit inline-start │ ├── .input-group-text text static (Lei · https:// · 0/280) · .ink pentru unități │ ├── .badge.sm.outline scurtătură — badge alb, pe un <kbd> │ ├── .btn.xs / .btn.sm buton PLAT: .ghost · .outline · .outline-pink — o ACȚIUNE │ └── .select-anchor o VALOARE din listă (moneda): .select-trigger.sm.bare └── .input-group-addon.inline-end al doilea slot
în carcasă — padding 16, gap 8 între addon și control · scurtătura la capăt: 8 sus, dreapta și jos
Aliniere
Pe input: inline-start (implicit) și inline-end. block-start și block-end — o bară pe rând propriu; carcasa devine coloană: toolbar pe textarea, sau eticheta în bordură când bara ține un .field-label. Addon-ul se scrie după control în DOM: poziția o face order, ordinea de tab rămâne cea corectă.
Ce intră în addon
Butonul din addon e o acțiune a câmpului, nu CTA-ul ecranului — de asta e plat și nu se ridică la hover; grupul anulează ridicarea chiar și pe o variantă plină pusă din greșeală. Stările (hover, focus, eroare, disabled) sunt pe carcasă, cu valorile inputului. O valoare nu e buton: moneda aleasă dintr-o listă e .select-anchor + .select-trigger.sm.bare, cu lista select-ului — Select spune că un declanșator care poartă o valoare nu e niciodată .btn.
.select-trigger.sm.bare în addon + .popover.align-end — mărimea normală a lui Money input. Deschis, carcasa se aprinde, iar trigger-ul își ține doar fill-ul bej: halo-ul e al carcasei.input-group însemna până acum un rând de câmpuri — ăla e .field-row. .field.input-em a dispărut: eticheta din bordură e .input-group cu un addon block-start care ține .field-label, iar eticheta trece după input în DOM (ordinea de tab). .search-wrap e deprecat: iconița lui era position: absolute și nu rezerva spațiu. Rămâne în CSS doar cât îl folosește referința Dublin (oglinda producției); dispare odată cu sync-ul în BFF. Focusul la click pe addon e un handler în aplicație (carcasa e <div>, ca la shadcn, fiindcă addon-ul poate conține butoane).Eticheta în interiorul bordurii
Un input group cu eticheta ca bară block-start — forma din shadcn. Eticheta e pink în repaus, pink-deep pe focus; bordura rămâne neutră. Doar client-facing (Dublin, landing) — formularele interne folosesc .field neutru. Se recunoaște prin conținut, .block-start > .field-label, nu printr-o clasă nouă.
În eroare, eticheta nu devine roșie și nu rămâne pink: brandul nu poartă sens negativ, iar pink lângă roșu pe tint roșu măsoară 3.33:1. Devine ink (17.48:1).
React · react/input-group.tsx
Split din react/input.tsx în v8.2, în același pas cu contractul. Exportă InputGroup, InputGroupAddon, InputGroupText. Mărimea și tonul sunt props (size="lg", tone="tonal"), nu clase scrise la call-site.
<InputGroup size="lg">
<Input size="lg" placeholder="exemplu.ro" />
<InputGroupAddon align="inline-start">
<InputGroupText>https://</InputGroupText>
</InputGroupAddon>
</InputGroup>
Carcasa e un <div role="group">, nu un <label>: un addon poate conține butoane, iar un buton într-un label ar fura click-ul câmpului. Sub 1024px carcasa e obligatoriu lg, în pereche cu controlul — o carcasă de 40px în jurul unui control de 44px taie controlul.