Inline Edit
Click the text, it becomes a field. Nothing on the page moves.
Walkthrough
1. Basic
Click the title. The line under it does not move: the reading view, the input and an invisible copy of the text all share one grid cell.
This line stays exactly where it is.
<script lang="ts"> import { InlineEdit } from 'omaris'; let title = $state('Untitled board');</script><div class="w-96 max-w-full rounded-shape-md border border-border p-4"> <InlineEdit bind:value={title} label="Board name" class="text-xl font-semibold" /> <p class="pt-2 text-sm text-muted-foreground">This line stays exactly where it is.</p></div> 2. Multiline
A textarea that grows by the line. Enter is a newline here, ⌘/Ctrl+Enter saves, Escape cancels.
<script lang="ts"> import { InlineEdit } from 'omaris'; let note = $state('Two lines of notes,\nand the box grows with them.'); let empty = $state('');</script><div class="flex w-96 max-w-full flex-col gap-4"> <InlineEdit bind:value={note} multiline block label="Notes" /> <InlineEdit bind:value={empty} placeholder="Add a description" block label="Description" /></div> 3. Async
A save that can fail. The value is only written once the promise settles, so a rejection leaves the field open on what was typed — with the reason floating under it, where it cannot push the page around.
<script lang="ts"> import { InlineEdit } from 'omaris'; let name = $state('Anything but "no"'); async function save(next: string) { await new Promise((resolve) => setTimeout(resolve, 800)); if (next.toLowerCase() === 'no') throw new Error('That name is taken'); }</script><div class="w-80 max-w-full"> <InlineEdit bind:value={name} label="Name" onsave={save} validate={(next) => (next.length < 2 ? 'At least two characters' : null)} /></div> 4. Overridden
A right-aligned figure in a table row: monospace, tabular, no pencil.
Revenue
Cost
<script lang="ts"> import { InlineEdit } from 'omaris'; let revenue = $state('12 500'); let cost = $state('4 200');</script><div class="w-64 max-w-full overflow-hidden rounded-shape-md border border-border"> {#each [{ label: 'Revenue', get: () => revenue, set: (v: string) => void (revenue = v) }, { label: 'Cost', get: () => cost, set: (v: string) => void (cost = v) }] as row (row.label)} <div class="flex items-center justify-between border-b border-border px-3 py-2 last:border-b-0"> <span class="text-sm text-muted-foreground">{row.label}</span> <InlineEdit value={row.get()} onsave={row.set} align="end" size="sm" pencil={false} label={row.label} class="font-mono tabular-nums" classes={{ view: 'hover:bg-primary/10' }} /> </div> {/each}</div>