Input

One component, eighteen variants: three kinds of field down, six states across, and no gaps in the grid.

Input · Components frameLive in the browser · drawn to match the file
Variant
State
Placeholder
Default · Default373 × 32 in the file
Password only differs once filled

An empty password field is a text field: same placeholder, same border, same everything. Four of its six states are pixel-identical to Default on purpose. Only the two filled ones show dots, because only then is there something to hide.

focus keeps the border

It does not swap the 1px base.input line for a ring, it adds a 3px base.ring outside and leaves the border where it was. That is the same focus treatment every other control in the kit gets, so a focused input and a focused button read as the same event.

Invalid and Invalid (Filled)

Both turn the border to base.destructive; only the filled one turns the text red too. An empty field flagged on submit keeps its muted placeholder, because the placeholder is not the thing that is wrong.

All eighteen

Three variants down, six states across

A full grid with no gaps: every variant carries every state. The rule the grid enforces is that a state named Filled has something in it - including the File column, where filled means a chosen file rather than a prompt to choose one.

DefaultFilePasswordDefault
Placeholder
Choose file No file chosen
Placeholder
Focus
Placeholder
Choose file No file chosen
Placeholder
Filled
Placeholder
Choose file invoice-2026.pdf
••••••••••
Disabled
Placeholder
Choose file No file chosen
Placeholder
Invalid
Placeholder
Choose file No file chosen
Placeholder
Invalid (Filled)
Placeholder
Choose file invoice-2026.pdf
••••••••••
The usage frame

Six blocks, fifteen fields, real copy in all of them

917 by 1040. A sign-up form, upload and validation, a read-only account section, an empty form and a required field flagged on submit. Every instance overrides the placeholder with something a person would actually have typed.

Section
Full name
Ana Kovač
Email
ana@
Password
••••••••••
In the file

One component, eighteen variants

Read straight out of the Penpot file. The axes below are the property names you will see in the variant dropdown.

InputVariant × StateDefault · File · Password × Default · Focus · Filled · Disabled · Invalid · Invalid (Filled) - 373 × 32 throughout18

Using the Input

Because an empty password field is a text field. Same placeholder, same border, same height. Only Filled and Invalid (Filled) differ, showing dots instead of characters, because only then is there something to hide. The duplication is the correct answer rather than a shortcut.

Nothing. It keeps the 1px base.input line exactly where it was and adds a 3px base.ring outside it. Swapping the border for a ring would make a focused field a different shape from an unfocused one; adding to it keeps focus reading as the same event it is on every other control in the kit.

Both take the border to base.destructive. Only the filled one takes the text there too. A field flagged on submit while still empty keeps its muted placeholder, because the placeholder is not the thing that failed validation - the absence of a value is.

It carries a 74-wide "Choose file" label at 14/500 in base.foreground, then the filename beside it at 14/400. That label is a button in the browser and a fixed-width block here, which is why the File column's value text is 269 wide against everyone else's 349.

Forms · 18 variants