Components
Input
Two text fields, split the same way the buttons are. Input is the shadcn
primitive on Curation's tokens — bordered, h-9, ring focus — for inline forms.
InputBox is the ported legacy pill: a 3.13rem filled row with slots inside
the fill.
Inline
A plain <input> on the semantic tokens: border-input hairline, transparent
ground, the three-pixel ring-ring/50 focus, and aria-invalid flipping the
border and ring to destructive.
import { Input } from "@/components/ui/input";
<Input placeholder="Search your lists" />
<Input aria-invalid placeholder="Something went wrong here" />Input box
The ported InputBox.vue: a filled bg-ink/[0.03] row, rounded-lg
(legacy's 1rem, snapped to the ladder), semibold text with the placeholder one ink rung down. It
closes the login screen and the home composer — the fields that are the
screen, rather than a row in a form.
import { InputBox } from "@/components/base/input-box";
<InputBox placeholder="Email address" />Adornments
left is the glyph that says what the field is before anything is typed in
it; right is the slot the login screen puts its “send code” countdown in
(CodeButton, in app/[locale]/login/login-shell.tsx — text rather than a
pill, because the primary action is the button below the field). Both sit
inside the fill and share the row's gap, so neither needs to know the field's
padding.
<InputBox
left={<SearchIcon className="size-4 text-ink-tertiary" />}
right={<CodeButton state="ready" onClick={send}>Send code</CodeButton>}
placeholder="Verification code"
/>