Selection
Time pickers
Lets people select a time on a clock dial, in text fields or on scroll wheels.
- COMPONENT_SPEC §28
- CSS · custom elements · React
- Open in the playground
Examples
Clock dial
Hours, then minutes on the dial, with AM / PM in 12-hour locales. Drag the hand or use the arrow keys.
The live demo runs with JavaScript. Its code is below.
import { useState } from 'react';
import { TimePicker, type TimeValue } from '@kyralabs/m3exp-web/react';
export default function Dial() {
const [time, setTime] = useState<TimeValue>({ hour: 7, minute: 30 });
return (
<div>
<TimePicker value={time} onChange={setTime} layout="vertical" />
<p>
Alarm: {String(time.hour).padStart(2, '0')}:{String(time.minute).padStart(2, '0')}
</p>
</div>
);
}
<m3e-time-picker id="alarm" hour="7" minute="30" layout="vertical"></m3e-time-picker>
<p>Alarm: <output id="alarm-value">07:30</output></p>
<script>
document.getElementById('alarm').addEventListener('change', (event) => {
const { hour, minute } = event.detail;
document.getElementById('alarm-value').textContent =
`${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;
});
</script>
Run the HTML version (custom elements, no framework).
Text input
Type the hours and minutes; here in 24-hour time.
The live demo runs with JavaScript. Its code is below.
import { TimeInput } from '@kyralabs/m3exp-web/react';
export default function Input() {
return <TimeInput defaultValue={{ hour: 14, minute: 5 }} is24Hour />;
}
<m3e-time-input hour="14" minute="5" hour-cycle="24"></m3e-time-input>
Run the HTML version (custom elements, no framework).
Scroll wheels
The expressive scroll wheels (always vibrant).
The live demo runs with JavaScript. Its code is below.
import { TimeScroll } from '@kyralabs/m3exp-web/react';
export default function Scroll() {
return <TimeScroll defaultValue={{ hour: 9, minute: 45 }} />;
}
<m3e-time-scroll hour="9" minute="45"></m3e-time-scroll>
Run the HTML version (custom elements, no framework).
In a dialog
Compose's TimePickerDialog: a title, the picker, Cancel and OK.
The live demo runs with JavaScript. Its code is below.
import { useState } from 'react';
import { Button, TimePicker, TimePickerDialog, type TimeValue } from '@kyralabs/m3exp-web/react';
export default function Dialog() {
const [open, setOpen] = useState(false);
const [time, setTime] = useState<TimeValue>({ hour: 18, minute: 0 });
const [draft, setDraft] = useState(time);
return (
<>
<Button
variant="tonal"
onPress={() => {
setDraft(time);
setOpen(true);
}}
>
Pick a time
</Button>
<p>
Time: {String(time.hour).padStart(2, '0')}:{String(time.minute).padStart(2, '0')}
</p>
<TimePickerDialog
aria-label="Select time"
title="Select time"
isOpen={open}
onOpenChange={setOpen}
dismissButton={
<Button
variant="text"
onPress={() => {
setOpen(false);
}}
>
Cancel
</Button>
}
confirmButton={
<Button
variant="text"
onPress={() => {
setTime(draft);
setOpen(false);
}}
>
OK
</Button>
}
>
<TimePicker value={draft} onChange={setDraft} />
</TimePickerDialog>
</>
);
}
<button type="button" id="open-time" class="m3e-button m3e-button--tonal">Pick a time</button>
<p>Time: <output id="time-value">18:00</output></p>
<m3e-dialog id="time-dialog" variant="custom" label="Select time">
<div class="m3e-time-picker-dialog">
<div class="m3e-time-picker-dialog__title">Select time</div>
<div class="m3e-time-picker-dialog__content">
<m3e-time-picker hour="18" minute="0"></m3e-time-picker>
</div>
<div class="m3e-time-picker-dialog__actions">
<span class="m3e-time-picker-dialog__spacer"></span>
<button type="button" class="m3e-button m3e-button--text" data-action="cancel">Cancel</button>
<button type="button" class="m3e-button m3e-button--text" data-action="ok">OK</button>
</div>
</div>
</m3e-dialog>
<script>
const dialog = document.getElementById('time-dialog');
const picker = dialog.querySelector('m3e-time-picker');
document.getElementById('open-time').addEventListener('click', () => dialog.show());
dialog.addEventListener('click', (event) => {
const action = event.target.closest('[data-action]')?.dataset.action;
if (action === 'ok') {
document.getElementById('time-value').textContent =
`${String(picker.hour).padStart(2, '0')}:${String(picker.minute).padStart(2, '0')}`;
}
if (action) dialog.close();
});
</script>
Run the HTML version (custom elements, no framework).
API
React
<TimePicker>
Time picker with a clock dial (COMPONENT_SPEC §28; Compose TimePicker): the hour / minute selectors, AM/PM (12-hour) and the dial, built and run by m3e-elements' TimePickerLayout (the code <m3e-time-picker> runs, so both layers render one DOM contract).
| Prop | Type | Default | Description |
|---|---|---|---|
vibrant
|
boolean
|
Compose's vibrant (expressive) TimePicker overload. | |
layout
|
"horizontal" | "vertical" | "auto"
|
TimePickerLayoutType; auto (default): horizontal in a landscape window, as Compose.
|
|
value
|
TimeValue
|
The time (controlled). | |
defaultValue
|
TimeValue
|
The initial time (uncontrolled; default 00:00, as rememberTimePickerState). | |
is24Hour
|
boolean
|
24-hour clock; default: the locale's (owner decision M10 T5). | |
selection
|
"hour" | "minute"
|
Which of hour / minute is being picked (controlled). | |
defaultSelection
|
"hour" | "minute"
|
The initial selection (default hour). | |
locale
|
string
|
BCP 47 locale for the digits and the default clock; default: the closest lang.
|
|
labels
|
Partial<TimePickerStrings>
|
String overrides (Compose's strings by default). | |
onChange
|
(value: TimeValue) => void
|
The user picked a time. | |
onSelectionChange
|
(selection: TimePickerSelection) => void
|
Also accepts HTML attributes not listed here.
<TimeInput>
Time picker with text fields (COMPONENT_SPEC §28; Compose TimeInput), built and run by m3e-elements' TimeInputLayout (shared with <m3e-time-input>).
| Prop | Type | Default | Description |
|---|---|---|---|
vibrant
|
boolean
|
Compose's vibrant TimeInput overload. | |
uncontained
|
boolean
|
Compose's TimeInput with a toggle: taller fields and a side column of AM, PM and a mode toggle
button (to the scroll picker), which calls onModeToggle.
|
|
value
|
TimeValue
|
The time (controlled). | |
defaultValue
|
TimeValue
|
The initial time (uncontrolled; default 00:00, as rememberTimePickerState). | |
is24Hour
|
boolean
|
24-hour clock; default: the locale's (owner decision M10 T5). | |
selection
|
"hour" | "minute"
|
Which of hour / minute is being picked (controlled). | |
defaultSelection
|
"hour" | "minute"
|
The initial selection (default hour). | |
locale
|
string
|
BCP 47 locale for the digits and the default clock; default: the closest lang.
|
|
labels
|
Partial<TimePickerStrings>
|
String overrides (Compose's strings by default). | |
onModeToggle
|
(next: TimePickerDisplayMode) => void
|
The side column's mode toggle was pressed; next is the mode it asks for ('scroll').
|
|
onChange
|
(value: TimeValue) => void
|
The user picked a time. | |
onSelectionChange
|
(selection: TimePickerSelection) => void
|
Also accepts HTML attributes not listed here.
<TimeScroll>
Time picker with scroll wheels (COMPONENT_SPEC §28; Compose TimeScroll, vibrant only), built and run by m3e-elements' TimeScrollLayout (shared with <m3e-time-scroll>).
| Prop | Type | Default | Description |
|---|---|---|---|
uncontained
|
boolean
|
Compose's TimeScroll with a toggle (see TimeInputProps.uncontained; to the text input). | |
value
|
TimeValue
|
The time (controlled). | |
defaultValue
|
TimeValue
|
The initial time (uncontrolled; default 00:00, as rememberTimePickerState). | |
is24Hour
|
boolean
|
24-hour clock; default: the locale's (owner decision M10 T5). | |
selection
|
"hour" | "minute"
|
Which of hour / minute is being picked (controlled). | |
defaultSelection
|
"hour" | "minute"
|
The initial selection (default hour). | |
locale
|
string
|
BCP 47 locale for the digits and the default clock; default: the closest lang.
|
|
labels
|
Partial<TimePickerStrings>
|
String overrides (Compose's strings by default). | |
onModeToggle
|
(next: TimePickerDisplayMode) => void
|
The side column's mode toggle was pressed; next is the mode it asks for ('input').
|
|
onChange
|
(value: TimeValue) => void
|
The user picked a time. | |
onSelectionChange
|
(selection: TimePickerSelection) => void
|
Also accepts HTML attributes not listed here.
<TimePickerDialog>
Time picker dialog (Compose TimePickerDialog): the title, the picker, the actions (mode toggle, dismiss, confirm) on a dialog surface, in m3e-react's <BasicAlertDialog> (M9 dialog motion, focus and dismissal).
| Prop | Type | Default | Description |
|---|---|---|---|
title
|
ReactNode
|
TimePickerDialogDefaults.Title: e.g. "Select Time" (picker) or "Enter Time" (input). | |
confirmButton
required
|
ReactNode
|
The confirm button (a text button, enabled while the input is valid). | |
modeToggleButton
|
ReactNode
|
The mode toggle at the start of the actions (<TimePickerModeToggle>).
|
|
children
required
|
ReactNode
|
The picker. | |
aria-label
|
string
|
The dialog's name (default "Dialog", Compose's pane title). | |
isOpen
|
boolean
|
Open (controlled). | |
defaultOpen
|
boolean
|
The initial open state (uncontrolled). | |
dismissButton
|
ReactNode
|
||
onOpenChange
|
(isOpen: boolean) => void
|
Called when the dialog asks to close (Escape, a press on the scrim, the full-screen close
button, a browser close request: Compose's onDismissRequest) with false. Controlled dialogs
close when isOpen becomes false; leave it true to keep the dialog open.
|
Also accepts HTML attributes not listed here.
<VibrantTimePickerDialog>
Vibrant time picker dialog (Compose VibrantTimePickerDialog): no title, tighter padding.
| Prop | Type | Default | Description |
|---|---|---|---|
title
|
ReactNode
|
TimePickerDialogDefaults.Title: e.g. "Select Time" (picker) or "Enter Time" (input). | |
confirmButton
required
|
ReactNode
|
The confirm button (a text button, enabled while the input is valid). | |
modeToggleButton
|
ReactNode
|
The mode toggle at the start of the actions (<TimePickerModeToggle>).
|
|
children
required
|
ReactNode
|
The picker. | |
aria-label
|
string
|
The dialog's name (default "Dialog", Compose's pane title). | |
isOpen
|
boolean
|
Open (controlled). | |
defaultOpen
|
boolean
|
The initial open state (uncontrolled). | |
dismissButton
|
ReactNode
|
||
onOpenChange
|
(isOpen: boolean) => void
|
Called when the dialog asks to close (Escape, a press on the scrim, the full-screen close
button, a browser close request: Compose's onDismissRequest) with false. Controlled dialogs
close when isOpen becomes false; leave it true to keep the dialog open.
|
Also accepts HTML attributes not listed here.
<TimePickerModeToggle>
TimePickerDialogDefaults.DisplayModeToggle / ScrollDisplayModeToggle: an icon button (keyboard, schedule or swipe_vertical, on-surface-variant) with a plain tooltip above that repeats its name.
| Prop | Type | Default | Description |
|---|---|---|---|
displayMode
required
|
"picker" | "input" | "scroll"
|
The mode shown now. | |
scroll
|
boolean
|
Between input and scroll (ScrollDisplayModeToggle) instead of picker and input (DisplayModeToggle). | |
labels
|
Partial<TimePickerStrings>
|
||
onDisplayModeChange
required
|
(next: TimePickerDisplayMode) => void
|
Pressed: next is the mode to show (picker ↔ input, or input ↔ scroll with scroll).
|
Custom elements
<m3e-time-picker>
import '@kyralabs/m3e-elements/time-picker.js';
<m3e-time-picker hour="7" minute="30"> (COMPONENT_SPEC §28; Compose TimePicker): the dial picker. Attributes hour (0–23), minute, hour-cycle="12 | 24" (default: the locale's, from lang), selection="hour | minute", vibrant (the expressive overload), layout="auto | vertical | horizontal" (auto: horizontal in a landscape window, as Compose). Property labels (string overrides). Events change (detail: { hour, minute }) and selection-change (detail: { selection }). The DOM is m3e-css's .m3e-time-picker contract, built and run by TimePickerLayout (shared with m3e-react).
| Attribute | Property | Type | Notes |
|---|---|---|---|
hour
|
hour
|
number
|
|
minute
|
minute
|
number
|
|
hour-cycle
|
hourCycle
|
string
|
|
selection
|
selection
|
string
|
Reflected. |
vibrant
|
vibrant
|
boolean
|
Reflected. |
layout
|
layout
|
string
|
| Event | Detail | Description |
|---|---|---|
change
|
TimePickerChangeDetail
|
The user changed the time (the inputs' native input / change stop inside).
|
selection-change
|
TimePickerSelectionChangeDetail
|
The selected field (hour or minute) changed. |
<m3e-time-input>
import '@kyralabs/m3e-elements/time-input.js';
<m3e-time-input hour="7" minute="30"> (COMPONENT_SPEC §28; Compose TimeInput): the text input picker. The <m3e-time-picker> attributes, events and labels (no layout), plus uncontained (Compose's TimeInput with a toggle: the side column with AM, PM and a mode toggle button, which fires mode-toggle). Built and run by TimeInputLayout (shared with m3e-react).
| Attribute | Property | Type | Notes |
|---|---|---|---|
hour
|
hour
|
number
|
|
minute
|
minute
|
number
|
|
hour-cycle
|
hourCycle
|
string
|
|
selection
|
selection
|
string
|
Reflected. |
vibrant
|
vibrant
|
boolean
|
Reflected. |
uncontained
|
uncontained
|
boolean
|
Reflected. |
| Event | Detail | Description |
|---|---|---|
mode-toggle
|
null
|
The uncontained side column's mode toggle was pressed.
|
<m3e-time-scroll>
import '@kyralabs/m3e-elements/time-scroll.js';
<m3e-time-scroll hour="7" minute="30"> (COMPONENT_SPEC §28; Compose TimeScroll, vibrant only): the scroll wheel picker. The <m3e-time-input> attributes and events (vibrant has no effect: TimeScroll is always vibrant). Built and run by TimeScrollLayout (shared with m3e-react).
| Attribute | Property | Type | Notes |
|---|---|---|---|
hour
|
hour
|
number
|
|
minute
|
minute
|
number
|
|
hour-cycle
|
hourCycle
|
string
|
|
selection
|
selection
|
string
|
Reflected. |
vibrant
|
vibrant
|
boolean
|
Reflected. |
uncontained
|
uncontained
|
boolean
|
Reflected. |
| Event | Detail | Description |
|---|---|---|
mode-toggle
|
null
|
The uncontained side column's mode toggle was pressed.
|
CSS
time-picker.css
@import '@kyralabs/m3e-css/time-picker.css';
Time pickers (COMPONENT_SPEC §28): Compose's TimePicker (the dial), TimeInput, TimeScroll and the TimePickerDialog / VibrantTimePickerDialog surfaces, each baseline or vibrant (expressive; TimeScroll is vibrant only). The dial, the inputs and the wheels need m3e-elements' time picker layouts (shared with m3e-react), which build this DOM and write its state; the CSS draws it.
<div class="m3e-time-picker [m3e-time-picker--vibrant] [m3e-time-picker--horizontal]
[m3e-time-picker--24h]" data-selection="hour | minute">
<div class="m3e-time-picker__display">
<div class="m3e-time-picker__fields"> (always left to right)
<button class="m3e-time-picker__selector" role="radio" aria-checked="true">
<span class="m3e-time-picker__value">07</span></button>
<span class="m3e-time-picker__separator" aria-hidden="true">:</span>
<button class="m3e-time-picker__selector" role="radio" aria-checked="false">…</button>
</div>
<div class="m3e-time-picker__period" role="radiogroup"> (12-hour only)
<button class="m3e-time-picker__period-item" role="radio" aria-checked="true">AM</button>
<button class="m3e-time-picker__period-item" role="radio" aria-checked="false">PM</button>
</div>
</div>
<div class="m3e-time-picker__dial" style="--_m3e-time-picker-cos: …; --_m3e-time-picker-sin: …">
<div class="m3e-time-picker__face" data-face="hour" data-active role="radiogroup">
<span class="m3e-time-picker__label [m3e-time-picker__label--inner]" role="radio"
style="--_m3e-time-picker-label-cos: …; --_m3e-time-picker-label-sin: …">12</span> …
</div>
<div class="m3e-time-picker__face" data-face="minute" role="radiogroup">…</div>
<span class="m3e-time-picker__handle"></span>
<span class="m3e-time-picker__track"></span>
<span class="m3e-time-picker__center"></span>
<div class="m3e-time-picker__highlight" aria-hidden="true"> (the faces again, on-primary,
<div class="m3e-time-picker__face" data-face="hour" data-active> clipped to the handle; each
<span class="m3e-time-picker__label" data-label="12"></span> … label's text is generated
</div> … from data-label)
</div>
</div>
</div>
<div class="m3e-time-input [m3e-time-input--vibrant] [m3e-time-input--uncontained]">
<div class="m3e-time-input__fields">
<div class="m3e-time-input__field" [data-selected] [data-invalid]>
<input class="m3e-time-input__input" inputmode="numeric" role="spinbutton" value="07">
<span class="m3e-time-input__supporting">Hour</span> (baseline, or invalid)
</div>
<span class="m3e-time-input__separator" aria-hidden="true">:</span>
<div class="m3e-time-input__field">…</div>
</div>
<div class="m3e-time-picker__period">…</div> or, uncontained, <div class="m3e-time-picker__side">
</div> AM, PM and the mode toggle </div>
<div class="m3e-time-scroll [m3e-time-scroll--uncontained]">
<div class="m3e-time-scroll__fields">
<div class="m3e-time-scroll__field" role="spinbutton" tabindex="0">
<span class="m3e-time-scroll__item" [data-selected]
style="--_m3e-time-scroll-offset: -1">06</span> …
</div>
<span class="m3e-time-scroll__separator" aria-hidden="true">:</span> …
</div>
<div class="m3e-time-picker__period">…</div> | <div class="m3e-time-picker__side">…</div>
</div>
<dialog class="m3e-dialog m3e-dialog--custom"> … <div class="m3e-dialog__container">
<div class="m3e-time-picker-dialog [m3e-time-picker-dialog--vibrant]">
<div class="m3e-time-picker-dialog__title">Select time</div> (baseline)
<div class="m3e-time-picker-dialog__content">a picker</div>
<div class="m3e-time-picker-dialog__actions">[mode toggle] <span class="m3e-time-picker-dialog__spacer"></span>
Cancel OK</div>
</div>
</div></dialog>
140 custom properties it reads
| Custom property | Default |
|---|---|
--m3e-comp-minimum-interactive-component-size
|
48px |
--m3e-comp-small-icon-button-container-height
|
40px |
--m3e-comp-small-icon-button-icon-size
|
24px |
--m3e-comp-time-input-period-selector-container-height
|
72px |
--m3e-comp-time-input-period-selector-container-width
|
52px |
--m3e-comp-time-input-time-field-container-height
|
72px |
--m3e-comp-time-input-time-field-container-shape
|
var(--m3e-sys-shape-corner-small) |
--m3e-comp-time-input-time-field-container-width
|
96px |
--m3e-comp-time-input-time-field-label-text-font
|
var(--m3e-sys-typescale-display-medium-font) |
--m3e-comp-time-input-time-field-label-text-size
|
var(--m3e-sys-typescale-display-medium-size) |
--m3e-comp-time-input-time-field-label-text-tracking
|
var(--m3e-sys-typescale-display-medium-tracking) |
--m3e-comp-time-input-time-field-label-text-weight
|
var(--m3e-sys-typescale-display-medium-weight) |
--m3e-comp-time-input-time-field-separator-color
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-time-input-time-field-supporting-text-color
|
var(--m3e-sys-color-on-surface-variant) |
--m3e-comp-time-input-time-field-supporting-text-font
|
var(--m3e-sys-typescale-body-small-font) |
--m3e-comp-time-input-time-field-supporting-text-line-height
|
var(--m3e-sys-typescale-body-small-line-height) |
--m3e-comp-time-input-time-field-supporting-text-size
|
var(--m3e-sys-typescale-body-small-size) |
--m3e-comp-time-input-time-field-supporting-text-tracking
|
var(--m3e-sys-typescale-body-small-tracking) |
--m3e-comp-time-input-time-field-supporting-text-weight
|
var(--m3e-sys-typescale-body-small-weight) |
--m3e-comp-time-picker-clock-dial-color
|
var(--m3e-sys-color-surface-container-highest) |
--m3e-comp-time-picker-clock-dial-container-size
|
256px |
--m3e-comp-time-picker-clock-dial-label-text-color-selected
|
var(--m3e-sys-color-on-primary) |
--m3e-comp-time-picker-clock-dial-label-text-color-unselected
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-time-picker-clock-dial-label-text-font
|
var(--m3e-sys-typescale-body-large-font) |
--m3e-comp-time-picker-clock-dial-label-text-line-height
|
var(--m3e-sys-typescale-body-large-line-height) |
--m3e-comp-time-picker-clock-dial-label-text-size
|
var(--m3e-sys-typescale-body-large-size) |
--m3e-comp-time-picker-clock-dial-label-text-tracking
|
var(--m3e-sys-typescale-body-large-tracking) |
--m3e-comp-time-picker-clock-dial-label-text-weight
|
var(--m3e-sys-typescale-body-large-weight) |
--m3e-comp-time-picker-clock-dial-selector-center-container-color
|
var(--m3e-sys-color-primary) |
--m3e-comp-time-picker-clock-dial-selector-center-container-shape
|
var(--m3e-sys-shape-corner-full) |
--m3e-comp-time-picker-clock-dial-selector-center-container-size
|
8px |
--m3e-comp-time-picker-clock-dial-selector-handle-container-color
|
var(--m3e-sys-color-primary) |
--m3e-comp-time-picker-clock-dial-selector-handle-container-shape
|
var(--m3e-sys-shape-corner-full) |
--m3e-comp-time-picker-clock-dial-selector-track-container-color
|
var(--m3e-sys-color-primary) |
--m3e-comp-time-picker-clock-dial-selector-track-container-width
|
2px |
--m3e-comp-time-picker-clock-dial-shape
|
var(--m3e-sys-shape-corner-full) |
--m3e-comp-time-picker-dialog-actions-gap
|
8px |
--m3e-comp-time-picker-dialog-container-color
|
var(--m3e-sys-color-surface-container-high) |
--m3e-comp-time-picker-dialog-container-shape
|
var(--m3e-sys-shape-corner-extra-large) |
--m3e-comp-time-picker-dialog-landscape-actions-bottom
|
8px |
--m3e-comp-time-picker-dialog-landscape-content-actions
|
4px |
--m3e-comp-time-picker-dialog-landscape-content-top
|
16px |
--m3e-comp-time-picker-dialog-padding
|
24px |
--m3e-comp-time-picker-dialog-padding-bottom
|
24px |
--m3e-comp-time-picker-dialog-padding-top
|
24px |
--m3e-comp-time-picker-dialog-title-font
|
var(--m3e-sys-typescale-label-medium-font) |
--m3e-comp-time-picker-dialog-title-line-height
|
var(--m3e-sys-typescale-label-medium-line-height) |
--m3e-comp-time-picker-dialog-title-padding-bottom
|
20px |
--m3e-comp-time-picker-dialog-title-size
|
var(--m3e-sys-typescale-label-medium-size) |
--m3e-comp-time-picker-dialog-title-tracking
|
var(--m3e-sys-typescale-label-medium-tracking) |
--m3e-comp-time-picker-dialog-title-weight
|
var(--m3e-sys-typescale-label-medium-weight) |
--m3e-comp-time-picker-display-gap
|
36px |
--m3e-comp-time-picker-face-bottom-margin
|
24px |
--m3e-comp-time-picker-face-duration
|
var(--m3e-sys-motion-spring-default-effects-duration) |
--m3e-comp-time-picker-face-easing
|
var(--m3e-sys-motion-spring-default-effects-easing) |
--m3e-comp-time-picker-inner-radius
|
69px |
--m3e-comp-time-picker-input-caret-color
|
var(--m3e-sys-color-primary) |
--m3e-comp-time-picker-input-container-color-error
|
var(--m3e-sys-color-error-container) |
--m3e-comp-time-picker-input-label-color-error
|
var(--m3e-sys-color-error) |
--m3e-comp-time-picker-input-outline-color
|
var(--m3e-sys-color-outline) |
--m3e-comp-time-picker-input-outline-color-error
|
var(--m3e-sys-color-error) |
--m3e-comp-time-picker-input-outline-color-focused
|
var(--m3e-sys-color-outline) |
--m3e-comp-time-picker-input-outline-width
|
1px |
--m3e-comp-time-picker-input-outline-width-focused
|
2px |
--m3e-comp-time-picker-minimum-interactive-size
|
48px |
--m3e-comp-time-picker-outer-radius
|
101px |
--m3e-comp-time-picker-period-container-color
|
var(--m3e-sys-color-surface-container-lowest) |
--m3e-comp-time-picker-period-gap
|
4px |
--m3e-comp-time-picker-period-label-weight-selected
|
700 |
--m3e-comp-time-picker-period-selector-label-text-font
|
var(--m3e-sys-typescale-title-medium-font) |
--m3e-comp-time-picker-period-selector-label-text-line-height
|
var(--m3e-sys-typescale-title-medium-line-height) |
--m3e-comp-time-picker-period-selector-label-text-size
|
var(--m3e-sys-typescale-title-medium-size) |
--m3e-comp-time-picker-period-selector-label-text-tracking
|
var(--m3e-sys-typescale-title-medium-tracking) |
--m3e-comp-time-picker-period-selector-label-text-weight
|
var(--m3e-sys-typescale-title-medium-weight) |
--m3e-comp-time-picker-period-shape-pressed
|
12px |
--m3e-comp-time-picker-period-shape-selected
|
12px |
--m3e-comp-time-picker-scroll-field-container-color
|
var(--m3e-sys-color-surface-container-lowest) |
--m3e-comp-time-picker-scroll-field-height
|
200px |
--m3e-comp-time-picker-scroll-field-label-color
|
var(--m3e-sys-color-outline) |
--m3e-comp-time-picker-scroll-field-label-color-selected
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-time-picker-scroll-field-pages
|
3 |
--m3e-comp-time-picker-scroll-field-shape
|
var(--m3e-sys-shape-corner-large) |
--m3e-comp-time-picker-scroll-field-width
|
100px |
--m3e-comp-time-picker-scroll-item-duration
|
var(--m3e-sys-motion-spring-fast-spatial-duration) |
--m3e-comp-time-picker-scroll-item-easing
|
var(--m3e-sys-motion-spring-fast-spatial-easing) |
--m3e-comp-time-picker-scroll-item-font
|
var(--m3e-sys-typescale-display-medium-font) |
--m3e-comp-time-picker-scroll-item-line-height
|
var(--m3e-sys-typescale-display-medium-line-height) |
--m3e-comp-time-picker-scroll-item-selected-font
|
var(--m3e-sys-typescale-display-large-emphasized-font) |
--m3e-comp-time-picker-scroll-item-selected-line-height
|
var(--m3e-sys-typescale-display-large-emphasized-line-height) |
--m3e-comp-time-picker-scroll-item-selected-size
|
var(--m3e-sys-typescale-display-large-emphasized-size) |
--m3e-comp-time-picker-scroll-item-selected-tracking
|
var(--m3e-sys-typescale-display-large-emphasized-tracking) |
--m3e-comp-time-picker-scroll-item-selected-weight
|
var(--m3e-sys-typescale-display-large-emphasized-weight) |
--m3e-comp-time-picker-scroll-item-size
|
var(--m3e-sys-typescale-display-medium-size) |
--m3e-comp-time-picker-scroll-item-tracking
|
var(--m3e-sys-typescale-display-medium-tracking) |
--m3e-comp-time-picker-scroll-item-weight
|
var(--m3e-sys-typescale-display-medium-weight) |
--m3e-comp-time-picker-separator-offset
|
-4px |
--m3e-comp-time-picker-separator-width
|
24px |
--m3e-comp-time-picker-side-item-height
|
40px |
--m3e-comp-time-picker-side-item-min-width
|
40px |
--m3e-comp-time-picker-side-item-padding
|
4px |
--m3e-comp-time-picker-side-period-gap
|
8px |
--m3e-comp-time-picker-side-toggle-gap
|
16px |
--m3e-comp-time-picker-side-toggle-top-24h
|
84px |
--m3e-comp-time-picker-supporting-text-color-error
|
var(--m3e-sys-color-error) |
--m3e-comp-time-picker-supporting-text-top
|
7px |
--m3e-comp-time-picker-time-selector-container-height
|
80px |
--m3e-comp-time-picker-time-selector-container-shape
|
var(--m3e-sys-shape-corner-small) |
--m3e-comp-time-picker-time-selector-container-width
|
96px |
--m3e-comp-time-picker-time-selector-label-color-error
|
var(--m3e-sys-color-on-error-container) |
--m3e-comp-time-picker-time-selector-label-text-font
|
var(--m3e-sys-typescale-display-large-font) |
--m3e-comp-time-picker-time-selector-label-text-line-height
|
var(--m3e-sys-typescale-display-large-line-height) |
--m3e-comp-time-picker-time-selector-label-text-size
|
var(--m3e-sys-typescale-display-large-size) |
--m3e-comp-time-picker-time-selector-label-text-tracking
|
var(--m3e-sys-typescale-display-large-tracking) |
--m3e-comp-time-picker-time-selector-label-text-weight
|
var(--m3e-sys-typescale-display-large-weight) |
--m3e-comp-time-picker-uncontained-time-field-height
|
136px |
--m3e-comp-time-picker-uncontained-toggle-height
|
140px |
--m3e-comp-time-picker-vibrant-clock-dial-color
|
var(--m3e-sys-color-surface-container-lowest) |
--m3e-comp-time-picker-vibrant-dialog-container-color
|
var(--m3e-sys-color-surface-container) |
--m3e-comp-time-picker-vibrant-dialog-landscape-padding
|
24px |
--m3e-comp-time-picker-vibrant-dialog-padding
|
12px |
--m3e-comp-time-picker-vibrant-horizontal-gap
|
52px |
--m3e-comp-time-picker-vibrant-input-label-color
|
var(--m3e-sys-color-on-surface) |
--m3e-comp-time-picker-vibrant-input-label-color-focused
|
var(--m3e-sys-color-primary) |
--m3e-comp-time-picker-vibrant-input-outline-color
|
transparent |
--m3e-comp-time-picker-vibrant-input-outline-color-error
|
var(--m3e-sys-color-error) |
--m3e-comp-time-picker-vibrant-period-container-color
|
var(--m3e-sys-color-surface-container-lowest) |
--m3e-comp-time-picker-vibrant-period-height
|
120px |
--m3e-comp-time-picker-vibrant-period-horizontal-height
|
40px |
--m3e-comp-time-picker-vibrant-period-large-padding
|
16px |
--m3e-comp-time-picker-vibrant-period-padding
|
8px |
--m3e-comp-time-picker-vibrant-period-width
|
56px |
--m3e-comp-time-picker-vibrant-selector-outline-color
|
var(--m3e-sys-color-primary) |
--m3e-comp-time-picker-vibrant-selector-outline-width
|
2px |
--m3e-comp-time-picker-vibrant-separator-width
|
16px |
--m3e-comp-time-picker-vibrant-time-field-height
|
120px |
--m3e-comp-time-picker-vibrant-time-field-label-text-font
|
var(--m3e-sys-typescale-display-large-font) |
--m3e-comp-time-picker-vibrant-time-field-label-text-size
|
var(--m3e-sys-typescale-display-large-size) |
--m3e-comp-time-picker-vibrant-time-field-shape
|
var(--m3e-sys-shape-corner-large) |
--m3e-comp-time-picker-vibrant-time-field-width
|
100px |
--m3e-comp-time-picker-vibrant-vertical-gap
|
12px |
Sources
Values come from androidx Compose Material3 1.5.0-beta01 at 19991f2c and from
m3.material.io, as cited below from COMPONENT_SPEC §28 and the m3e-css sources.
TimePickerTokens.kt / TimeInputTokens.kt@19991f2c (generated time-picker.css / time-input.css) plus components/time-picker-defaults.css (time-picker-defaults.ts, PARITY.md lines): dial 256dp, labels 48dp on the 101dp / 69dp radii (OuterCircleSizeRadius, InnerCircleRadius), handle 48dp, track 2dp, center 8dp, MaxDistance 74dp; display fields 96 × 80dp (24-hour vertical 114dp), separator 24dp; vibrant fields 100 × 120dp (132dp portrait), uncontained 136dp, separator 16dp, field shape and display-large type (VibrantTimeField*); input fields 96 × 72dp, outline 1dp / 2dp focused in primary; scroll wheels 100dp wide, as tall as the fields, three pages to 200dp (ScrollField), the selected row display-large-emphasized (ScrollFieldDefaults.Item); dialog padding 24dp (landscape 16 / 4 / 8dp, vibrant 12dp), title label-medium, min height for the dial 300dp. AM/PM (owner decision M10 T3): ComposeMaterial3Flags.isUpdatedTimepickerToggleEnabled is on by default, so the period is two toggle buttons (primary-container and bold when selected, surface-container-lowest otherwise, a circle that morphs to 12dp, 4dp apart, no outline), not the tokens' outlined tertiary-container segments. Error colors from the vibrant input colors; the side column (SideControlColumn): AM, PM 8dp below, the mode toggle's icon 16dp below PM (24-hour: the toggle alone at 84dp).
time-picker.css sources
(compose/material3 @ 19991f2c; values in time-picker.css, time-input.css and time-picker-defaults.css, lines in PARITY.md):
- TimePickerTokens / TimeInputTokens through TimePicker.kt's defaults, with ComposeMaterial3Flags.isUpdatedTimepickerToggleEnabled on (owner decision M10 T3): the AM/PM toggle is two ToggleButtons (CircleShape → 12dp pressed / checked on the fast spatial spring, primary-container checked, surface-container-lowest unchecked, bold when checked, 4dp apart, no outline), not the tokens' outlined tertiary segmented control.
- VerticalTimePicker / HorizontalTimePicker (L1958–2013): the display (fields + AM/PM) then the 256dp dial, below (vertical) or beside (horizontal); vibrant gaps and sizes (L1454–1479). The layout type is Compose's screen orientation (TimePicker.android.kt), resolved by the layouts (owner decision M10 T6); the dial does not shrink in short windows (gap, T6).
- ClockFace / drawSelector / CircularLayout (L3270–3480, L3935–3972): labels on a 101dp circle (24-hour: 0, 13–23 on a 69dp one), the 48dp primary handle at the selector's angle with the labels under it in on-primary (Compose's Clear / Xor / DstOver blending), a 2dp track from the center and an 8dp center dot; the faces cross-fade on DefaultEffects.
- TimeSelector (L3060–3128): a RadioButton surface; vibrant: a 2dp primary border when selected.
- TimeInputImpl / TimePickerTextField (L2033–2227, L3789–3928): an unselected field is a TimeSelector; the selected one an outlined text field (1dp / 2dp outline borders, error container and border when invalid); "Hour" / "Minute" or the error below (baseline, invalid).
- TimeScrollImpl / ScrollField (L2230–2380; ScrollField.kt): two infinite wheels of ScrollFieldHeight / 3 rows, the centered row selected (display large emphasized, on-surface) and the others display medium in outline, interpolated on the fast spatial spring.
- SideControlColumn (L2693–2767): the uncontained variants' AM, PM and mode toggle column.
- TimePickerDialog.kt: the dialogs' surfaces and their portrait / landscape layouts.
- Touch targets ≥ 48dp (::after), :focus-visible rings, forced colors, reduced motion (the selector and the wheels jump).