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

Form layout

tier 2 — classes

.form-field stacks a label, a control and a hint; a .sedna-grid puts two side by side; .form-section groups them and .form-actions closes the form. The controls themselves each have their own page under Inputs.

Fields

Label, control, hint. The hint explains the consequence, not the control.

How often the queue refreshes. Shorter means more database load.

<div style="max-width:420px; display:flex; flex-direction:column; gap:16px">
    <div class="form-field">
        <label class="form-label" for="f-name">Display name</label>
        <input class="form-input" id="f-name" type="text" value="Alex Fischer" />
    </div>
    <div class="form-field">
        <label class="form-label" for="f-poll">Poll interval</label>
        <input class="form-input" id="f-poll" type="text" value="30 seconds" />
        <p class="form-hint">How often the queue refreshes. Shorter means more database load.</p>
    </div>
    <div class="form-field">
        <label class="form-label form-label--required" for="f-owner">Owner</label>
        <input class="form-input" id="f-owner" type="text" value="Fulfilment" required />
    </div>
</div>

Two-up rows

.sedna-grid sedna-grid--sm sedna-grid--fit lays fields out side by side and collapses to one column when there is not room for two — no breakpoint to pick.

<div class="sedna-grid sedna-grid--sm sedna-grid--fit" style="max-width:620px">
    <div class="form-field">
        <label class="form-label" for="f-from">Active from</label>
        <input class="form-input" id="f-from" type="time" value="18:00" />
    </div>
    <div class="form-field">
        <label class="form-label" for="f-to">Active to</label>
        <input class="form-input" id="f-to" type="time" value="06:00" />
    </div>
</div>

A field across the whole row

.sedna-span-full on a child of that grid — the long field among the short ones: a description, a Markdown editor, a checkbox line, a hint that belongs to the row rather than to one field. It works in any grid, down to a single-column phone, so there is no breakpoint and no style="grid-column:1 / -1" in the markup.

Shown on the queue's own page and in the picker.

<div class="sedna-grid sedna-grid--sm sedna-grid--fit" style="max-width:620px">
    <div class="form-field">
        <label class="form-label" for="fr-name">Queue name</label>
        <input class="form-input" id="fr-name" type="text" value="Returns — north" />
    </div>
    <div class="form-field">
        <label class="form-label" for="fr-owner">Owner</label>
        <input class="form-input" id="fr-owner" type="text" value="Alex Fischer" />
    </div>
    <div class="form-field sedna-span-full">
        <label class="form-label" for="fr-what">What this queue is for</label>
        <textarea class="form-input" id="fr-what" rows="3">Returns that need a manual credit note before the refund is sent.</textarea>
        <p class="form-hint">Shown on the queue's own page and in the picker.</p>
    </div>
    <label class="form-check sedna-span-full">
        <input type="checkbox" checked /> Hold new items until someone claims them
    </label>
</div>

Sections and actions

.form-section is a <fieldset> with the browser's border and inset legend removed. Use the real element — the legend is announced with every control in the group, which a heading above a <div> is not — and put the fields in .form-section-body, because a <legend> is not a flex item and lands somewhere different in every engine if the fieldset is the flex container.

Escalation

Who is paged when nobody has acknowledged in time.

minutes
<form style="max-width:520px">
    <fieldset class="form-section">
        <legend>Escalation</legend>
        <p class="form-section-hint">Who is paged when nobody has acknowledged in time.</p>
        <div class="form-section-body">
            <div class="form-field">
                <label class="form-label form-label--required" for="f-team">Team</label>
                <select class="form-input form-select" id="f-team" required>
                    <option selected>Fulfilment</option>
                    <option>Network</option>
                </select>
            </div>
            <div class="form-field">
                <label class="form-label" for="f-after">Escalate after</label>
                <div class="input-group">
                    <input class="form-input" id="f-after" type="number" value="15" />
                    <span class="input-affix">minutes</span>
                </div>
            </div>
        </div>
    </fieldset>

    <div class="form-actions form-actions--split">
        <button class="btn btn-danger" type="button"><i class="ri-delete-bin-line"></i> Delete rule</button>
        <span style="display:flex; gap:8px">
            <button class="btn" type="button">Cancel</button>
            <button class="btn btn-primary" type="submit">Save</button>
        </span>
    </div>
</form>

Where the actions sit

.form-actions wraps instead of overflowing, so three buttons on a phone stack rather than pushing the commit button off-screen. --split puts a destructive action where it cannot be hit by aiming for Save; --start aligns the row to the leading edge, for a form embedded in a page rather than closing a dialog.

Default — the row ends where the form ends.

--split — the destructive one is put out of reach of the safe one.

--start — for a narrow form whose fields all begin at the same edge.

<div class="sedna-col" style="max-width:520px; gap:20px">
    <div class="card">
        <div class="card-body">
            <p class="form-hint" style="margin:0 0 12px">Default — the row ends where the form ends.</p>
            <div class="form-actions">
                <button class="btn" type="button">Cancel</button>
                <button class="btn btn-primary" type="button">Save</button>
            </div>
        </div>
    </div>
    <div class="card">
        <div class="card-body">
            <p class="form-hint" style="margin:0 0 12px"><code>--split</code> — the destructive one is put out of reach of the safe one.</p>
            <div class="form-actions form-actions--split">
                <button class="btn btn-danger" type="button"><i class="ri-delete-bin-line"></i> Delete rule</button>
                <button class="btn btn-primary" type="button">Save</button>
            </div>
        </div>
    </div>
    <div class="card">
        <div class="card-body">
            <p class="form-hint" style="margin:0 0 12px"><code>--start</code> — for a narrow form whose fields all begin at the same edge.</p>
            <div class="form-actions form-actions--start">
                <button class="btn btn-primary" type="button">Add member</button>
                <button class="btn" type="button">Cancel</button>
            </div>
        </div>
    </div>
</div>

A group that is not a fieldset

.form-section-title on a plain heading, for a region rather than a set of related controls. Same rule, without claiming a grouping that is not there — a <legend> tells a screen reader these answers belong together.

Escalation

A fieldset and a legend, because these three controls are one decision.

Danger zone

A heading and .form-section-title, because this groups content rather than controls — nothing here shares an answer.

<div class="sedna-col" style="max-width:520px; gap:24px">
    <fieldset class="form-section">
        <legend>Escalation</legend>
        <p class="form-section-hint">A <code>fieldset</code> and a <code>legend</code>, because these three controls are one decision.</p>
        <div class="form-section-body">
            <label class="form-check"><input type="radio" name="st-esc" checked /> Page the on-call rota</label>
            <label class="form-check"><input type="radio" name="st-esc" /> Page the team channel</label>
            <label class="form-check"><input type="radio" name="st-esc" /> Do not page</label>
        </div>
    </fieldset>

    <section class="form-section">
        <h3 class="form-section-title">Danger zone</h3>
        <p class="form-section-hint">A heading and <code>.form-section-title</code>, because this groups content rather than controls — nothing here shares an answer.</p>
        <div class="form-section-body">
            <div class="form-actions form-actions--start">
                <button class="btn btn-danger" type="button">Transfer ownership</button>
                <button class="btn btn-danger" type="button">Delete project</button>
            </div>
        </div>
    </section>
</div>

A whole form

Every piece on this page at once, as a starting point to paste and cut down.

Reservation

What is being held, and for whom.

When it expires
hours

After this the slot returns to the pool without asking.

<form style="max-width:640px">
    <fieldset class="form-section">
        <legend>Reservation</legend>
        <p class="form-section-hint">What is being held, and for whom.</p>
        <div class="form-section-body">
            <div class="sedna-grid sedna-grid--sm sedna-grid--fit">
                <div class="form-field">
                    <label class="form-label form-label--required" for="wf-order">Order</label>
                    <input class="form-input" id="wf-order" type="text" value="ORD-4182" required />
                </div>
                <div class="form-field">
                    <label class="form-label form-label--required" for="wf-cust">Customer</label>
                    <input class="form-input" id="wf-cust" type="text" value="Priya Nair" required />
                </div>
            </div>
            <div class="form-field">
                <label class="form-label" for="wf-note">Note on the hold</label>
                <textarea class="form-input" id="wf-note" rows="3"
                          placeholder="What the warehouse should know before releasing it"></textarea>
            </div>
        </div>
    </fieldset>

    <fieldset class="form-section">
        <legend>When it expires</legend>
        <div class="form-section-body">
            <div class="form-field">
                <label class="form-label" for="wf-hold">Hold for</label>
                <div class="input-group sedna-w sedna-w--sm">
                    <input class="form-input" id="wf-hold" type="number" value="24" />
                    <span class="input-affix">hours</span>
                </div>
                <p class="form-hint">After this the slot returns to the pool without asking.</p>
            </div>
            <label class="switch">
                <input type="checkbox" checked />
                <span class="choice-text">
                    <span>Warn me before it expires</span>
                    <span class="choice-hint">One notification, an hour ahead.</span>
                </span>
            </label>
        </div>
    </fieldset>

    <div class="form-actions form-actions--split">
        <button class="btn btn-danger" type="button"><i class="ri-close-circle-line"></i> Release now</button>
        <span style="display:flex; gap:8px">
            <button class="btn" type="button">Cancel</button>
            <button class="btn btn-primary" type="submit">Hold order</button>
        </span>
    </div>
</form>

A settings card

Fields in a card, actions in a footer row.

Notifications

Notifications are suppressed during this window. The queue still updates.

<div class="card" style="max-width:520px">
    <div class="card-head"><strong>Notifications</strong></div>
    <div class="card-body" style="display:flex; flex-direction:column; gap:14px">
        <label class="form-check"><input type="checkbox" checked /> Notify me about new pending items</label>
        <div class="form-field">
            <label class="form-label" for="f-quiet">Quiet hours</label>
            <input class="form-input" id="f-quiet" type="text" value="22:00 – 07:00" />
            <p class="form-hint">Notifications are suppressed during this window. The queue still updates.</p>
        </div>
    </div>
    <div class="modal-footer">
        <button class="btn">Reset</button>
        <button class="btn btn-primary">Save</button>
    </div>
</div>