Time Picker - National Design System

A time field that accepts a typed value or a picked one, with hour, minute and second selectors in a dropdown panel

Time Picker

The default field takes a 24-hour value in five minute steps. Type into it directly, or open the panel and pick each part.

<div class="nds-form-container nds-time-picker"> <div class="nds-form-header"> <label for="appointment-time"> <span class="nds-label">Appointment time</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action"> <button type="button" class="nds-btn nds-subtle nds-md time-picker-toggle" aria-label="Time Toggler"> <i class="nds-icon nds-hgi-clock-01" aria-hidden="true"></i> </button> </div> <input type="text" id="appointment-time" class="nds-input nds-time-input" placeholder="HH:mm"> <input type="hidden" class="nds-time-value" name="appointmentTime"> </div> </div>

Time Formats

One format string sets the display and the selectors the panel shows. Pick 12-hour for public-facing forms and 24-hour for operational tools.

Formats set by data-format
<form class="nds-form nds-grid" style="--max-col:1"> <!-- 24-hour: hour and minute selectors --> <div class="nds-form-container nds-time-picker" data-format="HH:mm"> <div class="nds-form-header"> <label for="time-24"> <span class="nds-label">24-hour (HH:mm)</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action"> <button type="button" class="nds-btn nds-subtle nds-md time-picker-toggle" aria-label="Time Toggler"> <i class="nds-icon nds-hgi-clock-01" aria-hidden="true"></i> </button> </div> <input type="text" id="time-24" class="nds-input nds-time-input" placeholder="HH:mm"> <input type="hidden" class="nds-time-value" name="time24"> </div> </div> <!-- 12-hour: adds a meridiem selector --> <div class="nds-form-container nds-time-picker" data-format="hh:mm A"> <div class="nds-form-header"> <label for="time-12"> <span class="nds-label">12-hour with meridiem (hh:mm A)</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action"> <button type="button" class="nds-btn nds-subtle nds-md time-picker-toggle" aria-label="Time Toggler"> <i class="nds-icon nds-hgi-clock-01" aria-hidden="true"></i> </button> </div> <input type="text" id="time-12" class="nds-input nds-time-input" placeholder="hh:mm A"> <input type="hidden" class="nds-time-value" name="time12"> </div> </div> <!-- 24-hour: adds a second selector --> <div class="nds-form-container nds-time-picker" data-format="HH:mm:ss"> <div class="nds-form-header"> <label for="time-seconds"> <span class="nds-label">With seconds (HH:mm:ss)</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action"> <button type="button" class="nds-btn nds-subtle nds-md time-picker-toggle" aria-label="Time Toggler"> <i class="nds-icon nds-hgi-clock-01" aria-hidden="true"></i> </button> </div> <input type="text" id="time-seconds" class="nds-input nds-time-input" placeholder="HH:mm:ss"> <input type="hidden" class="nds-time-value" name="timeSeconds"> </div> </div> </form>

Minute Steps

Set the minute list to the slots the service actually offers. The default step is five minutes.

Steps set by data-step
<form class="nds-form nds-grid" style="--max-col:1"> <!-- Minute list renders 00, 15, 30, 45 --> <div class="nds-form-container nds-time-picker" data-format="hh:mm A" data-step="15"> <div class="nds-form-header"> <label for="slot-15"> <span class="nds-label">Quarter hour slots</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action"> <button type="button" class="nds-btn nds-subtle nds-md time-picker-toggle" aria-label="Time Toggler"> <i class="nds-icon nds-hgi-clock-01" aria-hidden="true"></i> </button> </div> <input type="text" id="slot-15" class="nds-input nds-time-input" placeholder="hh:mm A"> <input type="hidden" class="nds-time-value" name="slot15"> </div> </div> <!-- Minute list renders 00, 30 --> <div class="nds-form-container nds-time-picker" data-format="hh:mm A" data-step="30"> <div class="nds-form-header"> <label for="slot-30"> <span class="nds-label">Half hour slots</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action"> <button type="button" class="nds-btn nds-subtle nds-md time-picker-toggle" aria-label="Time Toggler"> <i class="nds-icon nds-hgi-clock-01" aria-hidden="true"></i> </button> </div> <input type="text" id="slot-30" class="nds-input nds-time-input" placeholder="hh:mm A"> <input type="hidden" class="nds-time-value" name="slot30"> </div> </div> </form>

Time Bounds and Validation

Limit the field to a working window. Options outside it are disabled, and a typed value outside it blocks the submit.

Required, limited to 09:00 through 17:30
<div class="nds-form-container nds-time-picker" data-format="hh:mm A" data-step="30" data-required> <div class="nds-form-header"> <label for="visit-time"> <span class="nds-label">Visit time</span> </label> </div> <div class="nds-form-control"> <div class="nds-form-action"> <button type="button" class="nds-btn nds-subtle nds-md time-picker-toggle" aria-label="Time Toggler"> <i class="nds-icon nds-hgi-clock-01" aria-hidden="true"></i> </button> </div> <input type="text" id="visit-time" class="nds-input nds-time-input" placeholder="hh:mm A" data-min-time="09:00" data-max-time="17:30"> <input type="hidden" class="nds-time-value" name="visitTime"> </div> </div>

Built-in Features

Auto-initialization

Activates on any page carrying a .nds-time-input. The panel builds on first open, so a page of time fields costs nothing until one is used.

Type or Pick

The field stays typeable. Entries such as 9:30 or 2:30 PM are accepted and padded on commit.

12 or 24-Hour Display

One format string sets both the visible text and which selectors the panel shows, so there is no separate mode flag to keep in sync.

Minute Steps

Set the minute list to your real booking interval. A value that sits off the step grid keeps its own option rather than being rounded away.

Time Bounds

Out-of-range options are disabled as you narrow the time, and a pick that falls out of range moves to the nearest allowed option instead of clearing.

Native Validation

A typed value is checked on change, so an unreadable or out-of-range time blocks the submit through the browser's own constraint validation.

Bilingual Labels

Selector labels and the meridiem follow the page language and switch with it at runtime, while the submitted value stays unchanged.

Programmatic Control

Read and write the field from JavaScript with getValue, setValue and clear.

Usage Guidelines

Best Practices

  • Use for any time of day: appointment slots, opening hours, shift starts, submission cut-offs
  • For a date and a time together, place a Date Picker and a Time Picker as two fields. One field holding both is harder to correct when only the time is wrong
  • Do not use for a duration such as two hours thirty minutes. A duration is a quantity, so use Text Inputs with number fields instead
  • Choose hh:mm A for public-facing forms and HH:mm for operational or internal tools
  • Add seconds only when the value needs them. Most appointment and opening-hour fields do not
  • Set data-step to the interval the service offers. The default of five minutes already shortens the list, and a step of one renders sixty options
  • Read the value from the hidden .nds-time-value field, or from getValue. The visible field holds localized display text
  • Write data-min-time and data-max-time in 24-hour form whatever the display format is
  • Add nds-darker or nds-lighter to the container for a filled field on a plain background

Data Attributes

AttributeDescription
data-formatSet on .nds-form-container. Tokens HH, H, hh, h, mm, ss, A, a. Any other character passes through as text. Lowercase hour tokens select 12-hour, and token presence decides which selectors the panel shows. Defaults to HH:mm
data-stepSet on .nds-form-container. Minute interval for the minute list, from 1 to 60. Defaults to 5. Seconds always step by one
data-requiredSet on .nds-form-container. Marks the field required and blocks the submit while it is empty
data-min-timeSet on .nds-time-input. Earliest allowed time, always 24-hour HH:mm or HH:mm:ss
data-max-timeSet on .nds-time-input. Latest allowed time, same 24-hour form

Value and Submission

The field has two inputs. The visible .nds-time-input is display text and carries no name, so it never submits. The hidden .nds-time-value carries the name and always holds a 24-hour value.

A 12-hour field showing 02:30 PM submits 14:30. The meridiem is a display choice and is never stored on its own, so the server reads the same value whatever language the page was in.

Both inputs dispatch native input and change events on every commit. The component dispatches no custom events, so listen on either input.

Keyboard

Type straight into the field. Alt plus ArrowDown opens the panel, where each selector behaves as a standard form select: Tab moves between them, Enter or Space opens a list, the arrow keys move through it, and Escape closes.

JavaScript API

The NDS.TimePicker API reads and writes the field in 24-hour form. Fields present at load initialize on their own. Call NDS.TimePicker.init() after injecting a field into the page.

// ── Initialize ─────────────────────────────────────── // Attaches a picker to every .nds-time-input. Safe to call again // after injecting new fields: existing fields are left alone. NDS.TimePicker.init(); NDS.TimePicker.reinit(); // same function // Build one field now. Returns the instance, or null on bad markup. const picker = NDS.TimePicker.create(document.getElementById('visit-time')); // ── Read and write ────────────────────────────────── // Every method accepts the input, the .nds-form-control, // or the .nds-form-container. const field = document.getElementById('visit-time'); // Always 24-hour, with seconds only when the format asks for them. // Returns '' until every selector has a value. NDS.TimePicker.getValue(field); // → '14:30' // Takes a 24-hour string. Returns false when it cannot be read or // falls outside data-min-time / data-max-time, and writes nothing // in that case, so the field never holds a partial value. NDS.TimePicker.setValue(field, '14:30'); // → true NDS.TimePicker.setValue(field, '25:00'); // → false NDS.TimePicker.setValue(field, '18:00'); // → false when max is 17:30 // Empty the field and every selector. NDS.TimePicker.clear(field); // → true // ── Observe changes ───────────────────────────────── // No custom events. Both inputs fire native input and change, // so listen on whichever one you need. field.addEventListener('change', () => { console.log(NDS.TimePicker.getValue(field)); }); // ── Teardown ──────────────────────────────────────── // Releases listeners and removes the panel. Call before discarding // the markup; NDS.TimePicker.create() can rebuild on it afterwards. field._ndsTimePicker.destroy();
Last Modified Date: 21/09/2026 - 05:58 PM
Was this page useful?
60% of users said Yes from 2843 Feedbacks