Input Group
A field with things tucked into it: ten group variants, eight inline addons and two block ones, all published separately so one group covers a search box, a currency prefix and a compose footer.
Default, Focus, Filled, Disabled, Invalid, drawn the same way: focus adds a 3px base.ring outside the border rather than replacing it, invalid takes the border to base.destructive, disabled is base.muted at 50%. A group and a plain input sit in a form as equals.
373 × 104 against 373 × 32, and the row becomes a column that pushes its addon to the bottom edge. That bottom strip is where the character count and the send button live, which is the block-end position from the source.
Eight inline ones and two block ones, on their own components. That is what lets a search field, a currency prefix and a compose footer all be the same group with a different thing tucked into it.
Everything that can sit at either end of a field
A tick, a search glyph, a text prefix, a shortcut key, a spinner, an icon button, an info target and a dropdown. They range from 16 × 16 to 49 × 20, and the three meant to be pressed carry a base.muted chip at radius.sm while the five meant to be read have no surface at all.
The strip along a textarea's edge
279 × 40 with 10 of padding, holding a character count and a send button. Its Align axis pushes that content to the left or the right of the strip.
Worth knowing: this Align is horizontal, not vertical. The source's align prop has block-start and block-end, meaning above or below the field, and the kit only draws the below case, using Start and End for where the content sits inside that strip.
A search field and a compose box, over and under the limit
917 by 644, five fields. The compose box sits at 36 of 280; the one beside it is invalid because it is at 312 of 280, which is the reason its border is red.
An addon is a side, not a corner
A leading addon leads in reading order, so under dir=rtl it sits on the right. That is the whole change for the inline ones - the row reverses and the glyph goes with it. The block addon is different: it is pinned to the bottom of a column, and bottom is bottom in every direction.
The distinction is worth holding onto because it decides whether something needs mirroring at all. Inline addons are ordered, so dir moves them. The block addon is anchored, so dir does not - and a component that mixes the two, as this one does, will need both rules applied at once.
Three components, twenty variants
Read straight out of the Penpot file. The axes below are the property names you will see in the variant dropdown.
Using the Input Group
It carries the same five states drawn the same way, so a group and a plain field sit in one form as equals: focus adds a 3px base.ring outside the border rather than replacing it, invalid takes the border to base.destructive, disabled is base.muted at 50%.
The box goes from 373 × 32 to 373 × 104 and the row becomes a column that pushes its addon to the bottom edge. That strip is where the character count and the send button live, which is the source's block-end position.
No, and it is worth knowing. The source's align takes inline-start, inline-end, block-start and block-end, so block-start means above the field. The kit's Addon Block only draws the below case, and uses Start and End for where the content sits horizontally inside that strip.
Because the thing tucked into a field varies far more than the field does. Eight inline variants and two block ones cover a tick, a search glyph, a text prefix, a shortcut key, a spinner, an icon button, an info target and a dropdown, without any of them multiplying the group's ten variants.
The ones that are. Kbd, Button and Dropdown carry a base.muted chip at radius.sm; the two icons, the spinner, the tick and the text prefix have no surface at all, because you do not click a currency symbol.