Skip to content

ExpressiveCSS

Browse documentation

Time Picker

Pick a time from a clock face, in 12-hour or 24-hour form.

View as Markdown

Add time-picker to a text input. AutoInit() starts every .time-picker except those marked no-autoinit.

The clock is inline, not a modal. The picker appends it to the input's parent, and it stays visible. There is no openByDefault flag.

<div class="field">
  <input type="text" class="time-picker" id="lunchtime">
  <label for="lunchtime">Lunchtime</label>
</div>

Wrap the input in its own .field (or another small parent). The picker appends the clock inside that parent.

To pick a time, click an hour on the dial, then a minute. With the default autoSubmit: true, choosing the minute writes the input (HH:MM AM or HH:MM PM).

You can also type in the digital fields. Enter in any of them writes the typed time. Digital edits use native input events, including paste. When focus leaves a field after an invalid edit, the field restores the last valid value. An empty field takes the current hour, minute or second and leaves the selected AM/PM period as it is.

Click the host input or press Enter in it to reload its current value into the clock. Changing the active clock field, or editing its value, cancels an older delayed clock update.

The clock hand's canvas fades with --md-sys-motion-duration-short3, which defaults to 150ms. Override that token to change the CSS fade. The JavaScript duration option and its view and clock-reset timers stay separate; a CSS override does not reschedule them. Reduced motion disables the fade, including when the token has a nonzero override.

Initialization

The IIFE bundle exposes Expressive.Timepicker. Call init yourself when you need options other than the defaults, or let Expressive.AutoInit() start every .time-picker. Unlike Datepicker, the default options already show the clock.

document.addEventListener('DOMContentLoaded', function() {
  const elems = document.querySelectorAll('.time-picker');
  const instances = Expressive.Timepicker.init(elems, {
    // specify options here
  });
});

You can also pass per-instance options through AutoInit:

Expressive.AutoInit(document.body, {
  Timepicker: { twelveHour: false }
});

Display

By default the clock is a .timepicker-container in the page. displayPlugin: 'modal' puts it in a dialog instead. There is no autoClose option.

To render the clock into a specific element instead of the input's parent, pass a selector or a DOM node as container.

Modal dialog

Set displayPlugin: 'modal' to open the clock in a modal <dialog class="display-modal">, drawn as a basic dialog over a scrim. It opens when the user clicks the input or confirms it with Enter. The dialog takes the input's label as its name, and focus moves to the hour field.

Ok, or Enter in a field, writes the time. Cancel, Escape or a press on the scrim closes the dialog without changing the input and calls onCancel. Focus then goes back to the input. A modal clock always shows Cancel and Ok, so it sets autoSubmit: false for you.

Expressive.Timepicker.init(elem, {
  displayPlugin: 'modal'
});

In a selective Sass build, add "dialog" and "modal-display" to $components.

Docked popover

Set displayPlugin: 'docked' to wrap the clock in a .display-docked popover. The popover appears when the user clicks the input or confirms it with Enter, and hides when they click outside.

Expressive.Timepicker.init(elem, {
  displayPlugin: 'docked'
});

The popover stays inside the input's parent and accounts for the parent's positioning and scroll offsets. You can use positioned wrappers such as .field directly.

displayPluginOptions is optional and takes margin (default 5), transition (10), duration (250), and align ('left').

Options

Name Type Default Description
duration Number 350 Transition between the hours and minutes views, in milliseconds.
container Element or String null Element or selector to render the clock into. When omitted, the picker appends the clock to the input's parent.
showClearBtn Boolean false Show a Clear button in the footer.
autoSubmit Boolean true If true, choosing a minute writes the input. If false, Ok and Cancel buttons are added.
defaultTime String 'now' Initial time. 'now' or a 'HH:MM' or 'HH:MM:SS' string (optionally with AM/PM).
fromNow Number 0 Millisecond offset added to 'now'.
i18n Object See below Labels for Cancel, Clear, Ok, Hours, Minutes and Seconds.
twelveHour Boolean true, or the language's If true, use a 12-hour clock with AM/PM. If false, use 24-hour hours on two rings. With a lang on or around the input, it defaults to the clock that language uses.
showSeconds Boolean false Add a seconds field after the minutes and write HH:MM:SS. The dial still sets hours and minutes only.
minTime String null The earliest time a person can choose, as 24-hour HH:MM or HH:MM:SS. Without showSeconds it rounds up to a whole minute.
maxTime String null The latest time a person can choose, as 24-hour HH:MM or HH:MM:SS. Without showSeconds it rounds down to a whole minute.
vibrate Boolean true Vibrate the device when the clock hand changes value.
onSelect Function null Called when a time is chosen on the dial. Receives (hour, minute).
onInputInteraction Function null Called when the input is clicked or confirmed with Enter.
onDone Function null Called once when Ok is activated by click, Enter or Space. The button is created when autoSubmit is false.
onCancel Function null Called once when Cancel is activated by click, Enter or Space. The button is created when autoSubmit is false.
displayPlugin String null Set to 'docked' for a click-to-open popover, or 'modal' for a dialog with Cancel and Ok.
displayPluginOptions Object null Options for the docked plugin: margin, transition, duration, align. The modal plugin takes none.
dialRadius Number 135 Radius of the dial, in pixels. The clock face is drawn at twice this size inside .timepicker-plate, which CSS fixes at 260px. Change the plate's width and height in step with this option.
outerRadius Number 105 Distance from the center to the minutes and the outer hour ring, in pixels.
innerRadius Number 70 Distance from the center to the inner hour ring of a 24-hour clock, in pixels.
tickRadius Number 20 Radius of the circle that marks the selected value, in pixels. Each .timepicker-tick is 40px in CSS, twice the default, so resize the ticks in step with this option.

Internationalization

Pass a partial i18n object. Missing keys keep the English defaults. The hours, minutes and seconds keys give the inputs their accessible names.

The AM/PM buttons expose their selected state and support Enter and Space. The hour, minute and second fields are spin buttons. Up and Down step them by one, and Page Up and Page Down step by five, wrapping round. The hand follows the hour and minute. Hours step round the day, so in 12-hour time, passing 12 switches AM and PM. Up from 11 AM comes 12 PM. Tab moves between the fields, the AM/PM buttons and the footer.

The dial itself takes pointer input only. The spin buttons set the same values from the keyboard and for a screen reader, so a focusable dial would only add a second Tab stop for them.

Key Default
cancel 'Cancel'
clear 'Clear'
done 'Ok'
hours'Hours'
minutes'Minutes'
seconds'Seconds'

12-hour and 24-hour

Set twelveHour: false for a 24-hour dial, with hours 1 to 12 on the inner ring and 13 to 00 on the outer ring. The written value is then HH:MM with no meridian. The intro clock above is 12-hour with AM/PM.

twelveHour defaults to true. With a lang on or around the input, it defaults to the clock that language uses. That is 24-hour for de or fr, and 12-hour for en-US.

Expressive.Timepicker.init(document.getElementById('timepicker-24'), {
  twelveHour: false
});

Seconds

Set showSeconds: true to add a third field after the minutes. It is a spin button named by i18n.seconds. It steps and takes typed values like the minute field, and an emptied field takes the current second. The written value becomes HH:MM:SS, with AM or PM on a 12-hour clock. A value read without seconds, such as 14:30, opens at zero seconds.

The dial keeps its hours and minutes views, as the Material 3 time picker does. You set seconds in the field only. Under autoSubmit, choosing a minute on the dial writes the time with the seconds the field shows.

Expressive.Timepicker.init(document.getElementById('timepicker-seconds'), {
  showSeconds: true
});

A type="time" input gets 24-hour HH:MM:SS. Give it step="1" so the browser doesn't flag the seconds as a step mismatch.

<input type="time" step="1" class="time-picker" id="alarm">

Time range

Set minTime and maxTime to bound the times a person can choose. Pass them as 24-hour 'HH:MM' or 'HH:MM:SS' strings. Either one alone bounds one end of the day. The range can't cross midnight. If minTime is after maxTime, the picker logs a warning and ignores both. Without showSeconds, the picker writes whole minutes, so minTime rounds up and maxTime rounds down to a minute.

An hour stays available while any minute of it is in range. With 09:00 to 17:30, the hour 17 is available and its minutes stop at 30. The picker dims dial ticks outside the range, and the hand won't stop on them. Releasing on one changes nothing and fires no select.

In the fields, Up and Down stop at the last allowed value instead of wrapping. The picker refuses a typed time outside the range. Each field's aria-valuemin and aria-valuemax follow the allowed values. On a 12-hour clock, the picker disables the AM or PM button when no time in that half of the day is allowed.

A value outside the range, in the input or defaultTime, opens at the nearest bound. So does a minute that an hour change leaves outside the range. For example, choosing 17 at 16:45 gives 17:30.

Expressive.Timepicker.init(document.getElementById('timepicker-range'), {
  minTime: '09:00',
  maxTime: '17:30'
});

The picker doesn't check a value typed straight into the input. On a type="time" input, also set min and max, so the browser flags a value outside the range.

<input type="time" min="09:00" max="17:30" class="time-picker" id="appointment">

Methods

All methods are called on the plugin instance. You can get the instance like this:
const instance = Expressive.Timepicker.getInstance(elem);
.showView();

Show the hours or minutes face.

String: 'hours' or 'minutes'.

instance.showView('hours');
.done();

Write the current time to the input, with seconds when showSeconds is on. Pass a truthy argument to clear the input instead.

instance.done();
instance.done(true);
.clear();

Clear the input. Same as done(true).

instance.clear();
.destroy();

Destroy the plugin instance, remove the clock, and tear down its event handlers. The method also cancels pending automatic submission and clock updates, so the destroyed instance cannot write to the input.

instance.destroy();

Properties

Name Type Description
el Element The input the plugin was initialized with.
options Object The options the instance was initialized with.
time String The last value written by done(), without the AM/PM suffix. It ends in :SS when showSeconds is on.
hours Number The hour currently shown on the clock.
minutes Number The minute currently shown on the clock.
seconds Number The second of the time on the clock. The seconds field shows it when showSeconds is on.
amOrPm String 'AM' or 'PM' when twelveHour is true.
currentView String 'hours' or 'minutes'.

No matching pages

  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.

    Default source: Mist Iris.

    ExpressiveCSS presets: Lagoon, Harbor, Breeze, Cobalt, Iris.

    Bold

    Soft

    Mist

    Glow