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.31.0 · main

Date and time

tier 2 — classes

.form-input on a date, time, month or week input. The box is the text field's; what the class adds is the parts the browser draws in its own colours, and the two things iOS does to a date input that a desktop never shows.

The four inputs

date, time, datetime-local and month, each a .form-input. Pick the type for the picker and the keyboard the platform gives you, and keep value in the wire format — 2026-09-18, 08:30 — whatever the reader's locale shows. A datetime-local is local to the browser and carries no zone: the hint says which zone the server will read it in, or two people book the same hour.

Local to the browser. Say which zone the server will read it in.

<div class="sedna-grid sedna-grid--sm sedna-grid--fit" style="max-width:560px">
    <div class="form-field">
        <label class="form-label" for="dt-date">Delivery date</label>
        <input class="form-input" id="dt-date" type="date" value="2026-09-18" />
    </div>
    <div class="form-field">
        <label class="form-label" for="dt-time">Not before</label>
        <input class="form-input" id="dt-time" type="time" value="08:30" />
    </div>
    <div class="form-field">
        <label class="form-label" for="dt-local">Maintenance window starts</label>
        <input class="form-input" id="dt-local" type="datetime-local" value="2026-09-20T22:00" />
        <p class="form-hint">Local to the browser. Say which zone the server will read it in.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="dt-month">Billing period</label>
        <input class="form-input" id="dt-month" type="month" value="2026-09" />
    </div>
</div>

From and to

Two dates in a .sedna-grid inside one .form-section, so the legend names the pair. min and max are what the picker greys out; move the end's min with the start, or the picker offers a range that the server will reject.

Report period

No later than today. The end moves with the start.

<fieldset class="form-section" style="max-width:560px">
    <legend>Report period</legend>
    <div class="form-section-body">
        <div class="sedna-grid sedna-grid--sm sedna-grid--fit">
            <div class="form-field">
                <label class="form-label" for="dt-from">From</label>
                <input class="form-input" id="dt-from" type="date" value="2026-09-01" min="2026-01-01" max="2026-09-30" />
            </div>
            <div class="form-field">
                <label class="form-label" for="dt-to">To</label>
                <input class="form-input" id="dt-to" type="date" value="2026-09-13" min="2026-09-01" max="2026-09-30"
                       aria-describedby="dt-to-hint" />
                <p class="form-hint" id="dt-to-hint">No later than today. The end moves with the start.</p>
            </div>
        </div>
    </div>
</fieldset>

Slots and weeks

step in seconds turns a time input into slots — 900 is a quarter hour — and min/max bound the day. week is for a planning horizon; its value is the ISO week, 2026-W38.

Quarter hours between 08:00 and 18:00.

<div class="sedna-grid sedna-grid--sm sedna-grid--fit" style="max-width:560px">
    <div class="form-field">
        <label class="form-label" for="dt-slot">Pickup slot</label>
        <input class="form-input" id="dt-slot" type="time" value="14:00" min="08:00" max="18:00" step="900"
               aria-describedby="dt-slot-hint" />
        <p class="form-hint" id="dt-slot-hint">Quarter hours between 08:00 and 18:00.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="dt-week">Planning week</label>
        <input class="form-input" id="dt-week" type="week" value="2026-W38" />
    </div>
</div>

Three sizes

The input's own tiers, .form-input--sm and -lg, so a date beside a button is the button's height. A date is a fixed shape, so it takes a .sedna-w width rather than filling the row.

<div class="sedna-col sedna-gap-3" style="max-width:560px">
    <div class="sedna-row sedna-gap-2">
        <input class="form-input form-input--sm sedna-w sedna-w--md" type="date" value="2026-09-18" aria-label="Date, small" />
        <button class="btn btn--sm" type="button">Apply</button>
    </div>
    <div class="sedna-row sedna-gap-2">
        <input class="form-input sedna-w sedna-w--md" type="date" value="2026-09-18" aria-label="Date, default" />
        <button class="btn" type="button">Apply</button>
    </div>
    <div class="sedna-row sedna-gap-2">
        <input class="form-input form-input--lg sedna-w sedna-w--md" type="date" value="2026-09-18" aria-label="Date, large" />
        <button class="btn btn--lg" type="button">Apply</button>
    </div>
</div>

Read-only, disabled and rejected

The same three states as a text field: readonly is still submitted, disabled is not, and a rejected date carries aria-invalid and a .form-error that says what a valid one would be.

Set when the contract was signed. Still submitted.

Follows the contract start. Not submitted.

In the past. Go-live has to be a working day from tomorrow on.

<div class="sedna-grid sedna-grid--sm sedna-grid--fit" style="max-width:560px">
    <div class="form-field">
        <label class="form-label" for="dt-locked">Contract start</label>
        <input class="form-input" id="dt-locked" type="date" value="2024-03-01" readonly />
        <p class="form-hint">Set when the contract was signed. Still submitted.</p>
    </div>
    <div class="form-field">
        <label class="form-label" for="dt-off">Renewal</label>
        <input class="form-input" id="dt-off" type="date" value="2027-03-01" disabled />
        <p class="form-hint">Follows the contract start. Not submitted.</p>
    </div>
    <div class="form-field sedna-span-full">
        <label class="form-label form-label--required" for="dt-bad">Go-live</label>
        <input class="form-input" id="dt-bad" type="date" value="2026-09-10" required
               aria-invalid="true" aria-describedby="dt-bad-err" />
        <p class="form-error" id="dt-bad-err"><i class="ri-error-warning-line"></i> <span>In the past. Go-live has to be a working day from tomorrow on.</span></p>
    </div>
</div>

A typeable date with a calendar

A text field people type into — 3.11.26, 03112026 — beside a button that opens a .datepicker, as one .input-group. Input that cannot be read as a date sets aria-invalid on the input, and the group draws the rejected edge. Opened from the button, the picker would line up with the button at the group's end; an anchor-name on the group and the same name as the popover's position-anchor line it up with the field instead, and it still flips at the screen's edge.

February has no 31st. Type a date such as 27.02.26, or pick one.

February 2026
<div class="form-field" style="max-width:320px">
    <label class="form-label form-label--required" for="golive">Go-live</label>
    <!-- The group is the picker's anchor, so the panel lines up with the field's edge, not the button's. -->
    <div class="input-group" style="anchor-name: --golive-field">
        <input class="form-input" id="golive" type="text" inputmode="numeric" autocomplete="off"
               value="31.02.26" aria-invalid="true" aria-describedby="golive-err" required />
        <button class="btn btn--icon" type="button" popovertarget="golive-picker" aria-label="Pick the go-live date">
            <i class="ri-calendar-line"></i>
        </button>
    </div>
    <p class="form-error" id="golive-err">
        <i class="ri-error-warning-line"></i>
        <span>February has no 31st. Type a date such as 27.02.26, or pick one.</span>
    </p>
</div>

<div class="popover datepicker" id="golive-picker" popover style="position-anchor: --golive-field">
    <div class="datepicker-head">
        <button class="btn btn--ghost btn--icon btn--sm" type="button" aria-label="Previous month"><i class="ri-arrow-left-s-line"></i></button>
        <span class="datepicker-title">February 2026</span>
        <button class="btn btn--ghost btn--icon btn--sm" type="button" aria-label="Next month"><i class="ri-arrow-right-s-line"></i></button>
    </div>
    <div class="cal-month cal-month--mini">
        <div class="cal-weekdays" aria-hidden="true">
            <span class="cal-weekday">Mo</span>
            <span class="cal-weekday">Tu</span>
            <span class="cal-weekday">We</span>
            <span class="cal-weekday">Th</span>
            <span class="cal-weekday">Fr</span>
            <span class="cal-weekday">Sa</span>
            <span class="cal-weekday">Su</span>
        </div>
        <div class="cal-row">
            <span class="cal-day cal-day--outside"></span>
            <span class="cal-day cal-day--outside"></span>
            <span class="cal-day cal-day--outside"></span>
            <span class="cal-day cal-day--outside"></span>
            <span class="cal-day cal-day--outside"></span>
            <span class="cal-day cal-day--outside"></span>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 1 February 2026" aria-pressed="false">1</button>
        </div>
        <div class="cal-row">
            <button class="cal-day" type="button" aria-label="Monday 2 February 2026" aria-pressed="false">2</button>
            <button class="cal-day" type="button" aria-label="Tuesday 3 February 2026" aria-pressed="false">3</button>
            <button class="cal-day" type="button" aria-label="Wednesday 4 February 2026" aria-pressed="false">4</button>
            <button class="cal-day" type="button" aria-label="Thursday 5 February 2026" aria-pressed="false">5</button>
            <button class="cal-day" type="button" aria-label="Friday 6 February 2026" aria-pressed="false">6</button>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 7 February 2026" aria-pressed="false">7</button>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 8 February 2026" aria-pressed="false">8</button>
        </div>
        <div class="cal-row">
            <button class="cal-day" type="button" aria-label="Monday 9 February 2026" aria-pressed="false">9</button>
            <button class="cal-day" type="button" aria-label="Tuesday 10 February 2026" aria-pressed="false">10</button>
            <button class="cal-day" type="button" aria-label="Wednesday 11 February 2026" aria-pressed="false">11</button>
            <button class="cal-day" type="button" aria-label="Thursday 12 February 2026" aria-pressed="false" aria-current="date">12</button>
            <button class="cal-day" type="button" aria-label="Friday 13 February 2026" aria-pressed="false">13</button>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 14 February 2026" aria-pressed="false">14</button>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 15 February 2026" aria-pressed="false">15</button>
        </div>
        <div class="cal-row">
            <button class="cal-day" type="button" aria-label="Monday 16 February 2026" aria-pressed="false">16</button>
            <button class="cal-day" type="button" aria-label="Tuesday 17 February 2026" aria-pressed="false">17</button>
            <button class="cal-day" type="button" aria-label="Wednesday 18 February 2026" aria-pressed="false">18</button>
            <button class="cal-day" type="button" aria-label="Thursday 19 February 2026" aria-pressed="false">19</button>
            <button class="cal-day" type="button" aria-label="Friday 20 February 2026" aria-pressed="false">20</button>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 21 February 2026" aria-pressed="false">21</button>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 22 February 2026" aria-pressed="false">22</button>
        </div>
        <div class="cal-row">
            <button class="cal-day" type="button" aria-label="Monday 23 February 2026" aria-pressed="false">23</button>
            <button class="cal-day" type="button" aria-label="Tuesday 24 February 2026" aria-pressed="false">24</button>
            <button class="cal-day" type="button" aria-label="Wednesday 25 February 2026" aria-pressed="false">25</button>
            <button class="cal-day" type="button" aria-label="Thursday 26 February 2026" aria-pressed="false">26</button>
            <button class="cal-day" type="button" aria-label="Friday 27 February 2026" aria-pressed="false">27</button>
            <button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 28 February 2026" aria-pressed="false">28</button>
            <span class="cal-day cal-day--outside"></span>
        </div>
    </div>
    <div class="datepicker-actions">
        <button class="btn btn--sm btn--ghost" type="button">Today</button>
        <button class="btn btn--sm" type="button" popovertarget="golive-picker" popovertargetaction="hide">Cancel</button>
    </div>
</div>

What the browser draws

The calendar glyph is a browser glyph in a fixed colour, invisible on a dark surface until it is inverted — --picker-invert flips it with the theme — and the picker panel follows color-scheme, set on <html> from --color-scheme. On iOS a date input has an intrinsic width and centres its value, and both ignore the class, so the field ran past its column and its text sat in the middle; the stylesheet gives it min-width: 0 and appearance: none and starts the value where every other field's starts. The picker still opens on tap.