Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.17.0 · main

Text fields

tier 2 — classes

.form-input on any text-like <input>. It is width: 100%, because a field in a form row is the common case — the last example is the way out of that.

The text input

One class over every text-like type. Pick the type for the keyboard and the validation a phone gives you for free, not for the appearance.

<div class="sedna-col" style="max-width:420px; gap:16px">
    <div class="form-field">
        <label class="form-label" for="ti-host">Host</label>
        <input class="form-input" id="ti-host" type="text" placeholder="exch-02.example.com" />
    </div>
    <div class="form-field">
        <label class="form-label" for="ti-mail">Notification address</label>
        <input class="form-input" id="ti-mail" type="email" placeholder="ops@example.com" />
    </div>
    <div class="form-field">
        <label class="form-label" for="ti-find">Find an order</label>
        <input class="form-input" id="ti-find" type="search" placeholder="Order number or customer" />
    </div>
    <div class="form-field">
        <label class="form-label" for="ti-token">API token</label>
        <input class="form-input" id="ti-token" type="password" placeholder="Paste the token" />
    </div>
    <div class="form-field">
        <label class="form-label" for="ti-hook">Webhook</label>
        <input class="form-input" id="ti-hook" type="url" placeholder="https://hooks.example.com/orders" />
    </div>
</div>

Not editable, three ways

The distinction that gets got wrong: readonly is still submitted and still selectable, disabled is neither, and a value that was never editable should not be a control at all.

disabled — not editable, not submitted, out of the tab order.

readonly — not editable, still submitted, still focusable and copyable.

2026-02-14 09:22 UTC

.form-value-display — never was a control. Nothing to focus, nothing to submit.

<div class="sedna-col" style="max-width:460px; gap:16px">
    <div class="form-field">
        <label class="form-label" for="st-plan">Plan</label>
        <input class="form-input" id="st-plan" type="text" value="Enterprise" disabled />
        <p class="form-hint"><code>disabled</code> — not editable, not submitted, out of the tab order.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="st-id">Tenant id</label>
        <input class="form-input" id="st-id" type="text" value="acct-4471-eu" readonly />
        <p class="form-hint"><code>readonly</code> — not editable, still submitted, still focusable and copyable.</p>
    </div>
    <div class="form-field">
        <label class="form-label">Created</label>
        <div class="form-value-display">2026-02-14 09:22 UTC</div>
        <p class="form-hint"><code>.form-value-display</code> — never was a control. Nothing to focus, nothing to submit.</p>
    </div>
</div>

Read-only values

.form-value-display is the shape of an input without being one. It takes the input's height, so a form of mixed editable and read-only rows does not step up and down.

alex.fischer@example.com
admin
<div class="sedna-grid sedna-grid--sm sedna-grid--fit" style="max-width:620px">
    <div class="form-field">
        <label class="form-label">Signed in as</label>
        <div class="form-value-display">alex.fischer@example.com</div>
    </div>
    <div class="form-field">
        <label class="form-label">Role</label>
        <div class="form-value-display">admin</div>
    </div>
</div>

Editable beside read-only

That height agreement, in a .sedna-grid where it is visible.

alex.fischer@example.com
admin
<div class="sedna-grid sedna-grid--sm sedna-grid--fit" style="max-width:620px">
    <div class="form-field">
        <label class="form-label" for="mr-name">Display name</label>
        <input class="form-input" id="mr-name" type="text" value="Alex Fischer" />
    </div>
    <div class="form-field">
        <label class="form-label">Signed in as</label>
        <div class="form-value-display">alex.fischer@example.com</div>
    </div>
    <div class="form-field">
        <label class="form-label">Role</label>
        <div class="form-value-display">admin</div>
    </div>
    <div class="form-field">
        <label class="form-label" for="mr-team">Team</label>
        <input class="form-input" id="mr-team" type="text" value="Fulfilment" />
    </div>
</div>

Input groups

One border around a control and its affixes. The affixes are real siblings rather than padding plus a background image, so a button stays clickable and a unit stays selectable. The focus ring moves out to the group and the inner control gives up its own, or the group draws two rings nested inside each other. An .input-clear inside the group empties the field and shows only while it holds a value — give the field a placeholder, which is how the stylesheet tells.

seconds
<div style="max-width:460px; display:flex; flex-direction:column; gap:16px">
    <div class="form-field">
        <label class="form-label" for="f-search">Search</label>
        <div class="input-group">
            <span class="input-affix"><i class="ri-search-line"></i></span>
            <input class="form-input" id="f-search" type="search" value="orders-console" placeholder="Host or order number" />
            <button class="input-clear" type="button" aria-label="Clear the search"><i class="ri-close-line"></i></button>
        </div>
    </div>
    <div class="form-field">
        <label class="form-label" for="f-timeout">Timeout</label>
        <div class="input-group">
            <input class="form-input" id="f-timeout" type="number" value="30" />
            <span class="input-affix">seconds</span>
        </div>
    </div>
    <div class="form-field">
        <label class="form-label" for="f-token">API token</label>
        <div class="input-group">
            <input class="form-input" id="f-token" type="text" value="np_live_8f2c…" readonly />
            <button class="btn btn-icon" type="button" aria-label="Copy the API token"><i class="ri-file-copy-line"></i></button>
        </div>
    </div>
</div>

What an affix can be

A unit, a currency, a button — on either side, and more than one. The order in the markup is the order on screen.

EUR
seconds
<div class="sedna-col" style="max-width:460px; gap:16px">
    <div class="form-field">
        <label class="form-label" for="ak-amount">Refund amount</label>
        <div class="input-group">
            <span class="input-affix">EUR</span>
            <input class="form-input" id="ak-amount" type="number" value="318.50" />
        </div>
    </div>
    <div class="form-field">
        <label class="form-label" for="ak-timeout">Timeout</label>
        <div class="input-group">
            <input class="form-input" id="ak-timeout" type="number" value="30" />
            <span class="input-affix">seconds</span>
        </div>
    </div>
    <div class="form-field">
        <label class="form-label" for="ak-scan">Scan a label</label>
        <div class="input-group">
            <button class="btn btn-icon" type="button" aria-label="Scan with the camera"><i class="ri-qr-scan-2-line"></i></button>
            <input class="form-input" id="ak-scan" type="text" placeholder="SHP-0000" />
            <button class="btn" type="button">Look up</button>
        </div>
    </div>
</div>

Three sizes

.form-input-sm, the default and .form-input-lg, matching .btn-sm, .btn and .btn-lg exactly. An input group takes the tier on the group.input-group--sm or --lg — because the group is what draws the border.

<div class="sedna-col sedna-gap-2" style="max-width:420px">
    <div class="sedna-row">
        <input class="form-input form-input-sm" type="search" placeholder="Small" aria-label="Small field" />
        <button class="btn btn-sm">Small</button>
    </div>
    <div class="sedna-row">
        <input class="form-input" type="search" placeholder="Default" aria-label="Default field" />
        <button class="btn">Default</button>
    </div>
    <div class="sedna-row">
        <input class="form-input form-input-lg" type="search" placeholder="Large" aria-label="Large field" />
        <button class="btn btn-lg">Large</button>
    </div>
    <span class="input-group input-group--sm">
        <span class="input-affix"><i class="ri-search-line"></i></span>
        <input class="form-input" type="search" placeholder="Small group" aria-label="Small group" />
        <button class="btn btn-icon" type="button" aria-label="Clear"><i class="ri-close-line"></i></button>
    </span>
    <span class="input-group input-group--lg">
        <span class="input-affix"><i class="ri-search-line"></i></span>
        <input class="form-input" type="search" placeholder="Large group" aria-label="Large group" />
        <button class="btn btn-icon" type="button" aria-label="Clear"><i class="ri-close-line"></i></button>
    </span>
</div>

Making one narrower

.sedna-w-fit sizes to the content, .sedna-w takes a width from --sedna-w with --sm, --md and --lg as 8/12/18rem. They sit in the utilities layer, so they win over the field's own rule without !important — and an app never has to style a library class in its own stylesheet to get a short search box.

.sedna-w--sm — 8rem, because a page number is never long.

.sedna-w--md — 12rem, for a value with a known, short shape.

.sedna-w--lg — 18rem, for one that is long but still not a sentence.

.sedna-w with --sedna-w — any width, without an inline width on a library class.

.sedna-w-fit — as wide as its content asks for.

<div class="sedna-col" style="max-width:520px; gap:16px">
    <div class="form-field">
        <label class="form-label" for="wd-note">Reference</label>
        <input class="form-input" id="wd-note" type="text" value="Fills the row — the default" />
    </div>
    <div class="form-field">
        <label class="form-label" for="wd-page">Page</label>
        <input class="form-input sedna-w sedna-w--sm" id="wd-page" type="number" value="12" />
        <p class="form-hint"><code>.sedna-w--sm</code> — 8rem, because a page number is never long.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="wd-postcode">Postcode and town</label>
        <input class="form-input sedna-w sedna-w--md" id="wd-postcode" type="text" value="8004 Zürich" />
        <p class="form-hint"><code>.sedna-w--md</code> — 12rem, for a value with a known, short shape.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="wd-mail">Notification address</label>
        <input class="form-input sedna-w sedna-w--lg" id="wd-mail" type="email" value="ops@example.com" />
        <p class="form-hint"><code>.sedna-w--lg</code> — 18rem, for one that is long but still not a sentence.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="wd-filter">Filter</label>
        <input class="form-input sedna-w" id="wd-filter" type="search" style="--sedna-w: 22rem"
               placeholder="Order number or customer" />
        <p class="form-hint"><code>.sedna-w</code> with <code>--sedna-w</code> — any width, without an inline <code>width</code> on a library class.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="wd-code">Country</label>
        <input class="form-input sedna-w-fit" id="wd-code" type="text" size="4" value="DE" />
        <p class="form-hint"><code>.sedna-w-fit</code> — as wide as its content asks for.</p>
    </div>
</div>