Table of contents
InlineEdit
A value shown as plain text that becomes a field when you click it — for a figure that is read far more often than it is set: a dimension in a readout, a layer's name.
A permanent input there is noise, a dialog is too heavy. The contract:
- At rest it is a real
<button>whose accessible name carries both the value and the action ("39 mm, edit"). Readonly, it is plain text; disabled, a native disabled button — dimmed, out of the tab order, and exempt from contrast rules the way every disabled control in the kit is. Neither one opens. - Editing swaps in the kit's Input (type="text") or NumberInput (type="number") in place, focused with its contents selected.
- Enter and blur commit, Escape cancels. Enter and Escape hand focus back to the button so a keyboard user is not dropped on
<body>; blur leaves focus wherever the user sent it. - An arrow step inside NumberInput is a commit to NumberInput, not to this: only Enter, blur and Escape leave edit mode, and onValueCommit fires once, and only when the value actually changed.
Motion: the text never moves. The field is sized to its contents and pulled out by its own padding, so its text lands exactly where the resting text was; opening only fades the field's border, fill and ring in around it. Closing leaves a ghost of that border shrinking onto the text — tinted when the commit changed the value. Both snap under reduced motion.
Forms · two-way: bind:value bind:editing · keyboard contract
<script lang="ts">
import { InlineEdit } from '@l4st/driftwood';
</script>
| Light | Dark |
|---|---|
![]() |
![]() |
Props
| Prop | Type | Default | What it does |
|---|---|---|---|
value bind: |
T |
— | The value. Written back only on a commit that changed it. |
type |
'text' | 'number' |
'text' |
Which field editing opens: Input or NumberInput. |
min |
number |
— | Lower bound, for type="number". Clamped on commit by NumberInput. |
max |
number |
— | Upper bound, for type="number". |
step |
number |
— | Arrow-key and rounding increment, for type="number". |
suffix |
string |
— | Unit shown after the value, and inside the NumberInput, e.g. 'mm'. |
placeholder |
string |
— | Shown in place of the value while it is empty, and in the empty field. |
format |
(value: T) => string |
— | How the resting text reads. Defaults to the value, a space, and the suffix. |
editing bind: |
boolean |
false |
Whether the field is open. Set it to open the editor from outside. |
disabled |
boolean |
false |
Shown dimmed, out of the tab order; never opens. |
readonly |
boolean |
false |
Shown as text; never opens. |
id |
string |
— | Goes on the button at rest and on the field while editing. |
aria-label |
string |
— | What the value is, e.g. 'Height'. Prefixed to the button's name and names the field. |
editLabel |
string |
'edit' |
The action word the button's accessible name ends with. |
class |
string |
— | Merged with the component's own classes, last — so yours win. |
inputClass |
string |
— | Merged onto the field while editing. By default it hugs its text; give it a width here to fix one. |
onValueCommit |
(value: T) => void |
— | Enter or blur, with a value different from the one editing started with. |
onCancel |
() => void |
— | Escape: the edit was abandoned and the value is unchanged. |
Keyboard
| Key | Does |
|---|---|
| Enter / Space | open the field, from the text |
| Enter | commit, and return focus to the text |
| Escape | cancel, restore the value and return focus to the text |
| Tab | commit by leaving — focus goes where Tab sends it |
| ↑ / ↓ | step, when it is a number — the field stays open |
Example
The markup behind the screenshot above, lifted from src/routes/shots/ — so it
compiles, and it is exactly what produced the image.
<div class="flex items-center justify-between gap-4">
<span class="text-muted-foreground">Layer</span>
<InlineEdit bind:value={layerName} aria-label="Layer name" />
</div>
<div class="flex items-center justify-between gap-4">
<span class="text-muted-foreground">Height</span>
<InlineEdit type="number" bind:value={binHeight} min={7} max={400} suffix="mm" aria-label="Height" />
</div>
<div class="flex items-center justify-between gap-4">
<span class="text-muted-foreground">Depth</span>
<InlineEdit type="number" value={42} suffix="mm" aria-label="Depth" editing />
</div>
Notes
- Reach for it when the value is read far more than it is set. A readout of dimensions, a name in a list header: a permanent field there is noise around every figure. If the value changes often, or sits in a form next to other fields, use a plain Input or NumberInput instead: a form should look like a form.
valueonly changes on a commit. Typing goes into a draft, so a binding never sees a half-typed "1" on the way to "120", and Escape has nothing to undo.onValueCommitfires once per edit, and not at all when the result equals what editing started with.- Arrows do not close it. NumberInput treats an arrow step as a commit of
its own. Here it only moves the draft, so a user can step three times and
then press Enter. The draft is clamped and snapped by NumberInput, so what
lands in
valueis always withinmin/maxand on thestepgrid. - Name it with
aria-label. The button's accessible name is built from it, as "Height, 39 mm, edit", and the field gets it as its own name while it is open.editLabelis the word at the end, so it can be translated. bind:editingopens it from outside, for a "Rename" item in a menu, say. The field takes focus the same way it does on a click. The genericTfollowsvalue, soonValueCommitreceives anumberwhen you bind a number.inputClasssizes the open field. The defaults (w-40for text,w-28for a number, bothh-8) sit in a line oftext-smwithout moving it much. A wider value wants a wider field.
Forms · Field · Input · FileInput · FileList · Textarea · CodeEditor · Combobox · RadioGroup · NumberInput · Label · Select · MultiSelect · DatePicker · TagsInput · Checkbox · Switch · Slider
driftwood
Layout
Actions
Forms
- Field
- Input
- FileInput
- FileList
- Textarea
- CodeEditor
- Combobox
- RadioGroup
- NumberInput
- InlineEdit
- Label
- Select
- MultiSelect
- DatePicker
- TagsInput
- Checkbox
- Switch
- Slider
Feedback
Charts
Overlays
Navigation
Generated from the source by pnpm wiki · repository · v0.24.0

