Time Picker <mv-time-picker>
区切られた時刻フィールド(時、分、オプションの秒。AM/PM の並びは Intl によりロケールに従います)と、指定した間隔の時刻リストを表示するポップオーバー。送信値は常に 24 時間制の HH:MM で、フォームに関連付けられます。
一時停止 再生
カテゴリー フォーム タイプ Web Component(<mv-time-picker>) ステータス 安定版 Keywords time-picker, timepicker, time, clock, spinbutton, segmented, listbox, popover, intl, form-associated
When to use
A booking form offers appointment slots every 15 or 30 minutes within opening hours Users type an exact time quickly, like a departure or a reminder, with arrow keys to adjust One time field must read as 2:30 PM in the US and 14:30 in Europe while storing the same value
Avoid when
The field picks a calendar day, or a day and a time together → use Date Picker instead A touch-first flow picks a time by flicking iOS-style drums → use Wheel Picker instead The value is a duration like 1 h 30 min rather than a time of day → use Number Field instead
インストール
node scripts/add.mjs time-picker --out ./src/marvelous
Marvelous UI の MCP サーバーを使う AI エージェント:install_components({ slugs: ["time-picker"], target_dir: "<absolute path>/src/marvelous", framework: "react" })。
コピーされるファイル(依存関係を含む):tokens/tokens.css, core/base.css, core/dismiss.js, core/dom.js, core/element.js, core/focus.js, core/form.js, core/position.js, components/time-picker/time-picker.js, components/time-picker/time-picker.css。
使い方
基本となるマークアップです。ここから属性、data-*、CSS 変数でカスタマイズしてください:
<form id="time-picker-demo" style="display:flex;flex-wrap:wrap;gap:1.5rem 2rem;justify-content:center;align-items:flex-start;max-width:48rem">
<div style="display:grid;gap:.4rem;width:14rem">
<label for="time-picker-demo-slot" style="font-size:.875rem;font-weight:500">Appointment</label>
<mv-time-picker id="time-picker-demo-slot" name="slot" step="30" min="09:00" max="17:30" value="10:30" required aria-describedby="time-picker-demo-slot-hint" style="--mv-time-picker-width:100%;--mv-time-picker-list-height:11rem"></mv-time-picker>
<p id="time-picker-demo-slot-hint" style="margin:0;font-size:.75rem;color:var(--mv-fg-muted)">9:00 AM to 5:30 PM, every 30 min</p>
</div>
<div style="display:grid;gap:.4rem;width:14rem">
<label for="time-picker-demo-start" style="font-size:.875rem;font-weight:500">Race start (24-hour)</label>
<mv-time-picker id="time-picker-demo-start" name="start" locale="en-GB" seconds value="14:05:30" style="--mv-time-picker-width:100%"></mv-time-picker>
<p style="margin:0;font-size:.75rem;color:var(--mv-fg-muted)">Value: <output id="time-picker-demo-start-out" style="font-family:var(--mv-font-mono)">14:05:30</output></p>
</div>
<div style="display:grid;gap:.4rem;width:14rem">
<label for="time-picker-demo-alarm" style="font-size:.875rem;font-weight:500">Wake-up alarm</label>
<mv-time-picker id="time-picker-demo-alarm" name="alarm" locale="en-US" hour-cycle="12" style="--mv-time-picker-width:100%"></mv-time-picker>
<p style="margin:0;font-size:.75rem;color:var(--mv-fg-muted)">Value: <output id="time-picker-demo-alarm-out" style="font-family:var(--mv-font-mono)">empty</output></p>
</div>
</form>
<script type="module">
const form = document.getElementById("time-picker-demo");
for (const id of ["start", "alarm"]) {
const field = form.querySelector(`#time-picker-demo-${id}`);
const out = form.querySelector(`#time-picker-demo-${id}-out`);
field.addEventListener("mv-input", (e) => { out.textContent = e.detail.value || "empty"; });
field.addEventListener("mv-change", (e) => { out.textContent = e.detail.value || "empty"; });
}
</script>
API
Attributes
Name タイプ Default Description valuestring 24-hour value, “HH:MM” or “HH:MM:SS”, whatever the display. Restored on form reset. namestring Form field name (submits the 24-hour value). min / maxstring Earliest and latest time (“09:00”, “17:30”): bounds the list and sets rangeUnderflow / rangeOverflow. stepnumber 15Minutes between times in the list. When set explicitly, times off the grid get stepMismatch and minute arrows follow it (30: :00, :30). secondsboolean Adds a seconds segment; the value becomes “HH:MM:SS”. hour-cycle12 | 24 Forces a 12-hour (AM/PM) or 24-hour display; by default the locale decides. localestring en-USSegment order, separators, AM/PM labels, digits and list labels (Intl.DateTimeFormat). requiredboolean Native validation (valueMissing); a partly filled time is badInput. disabledboolean Disables the field (a disabled fieldset does too). invalidboolean Forces the error style and aria-invalid. labelstring Accessible name without a <label for>. placementbottom-start | top-start… bottom-startList popover placement (flipped when there is no room). data-sizesm | lg Field height.
Properties
Name タイプ Description valuestring 24-hour value, “” while empty or incomplete. valueAsNumbernumber Milliseconds since midnight, NaN when empty (like a native time input). stringsobject Overrides the default labels and messages: hour, minute, second, dayPeriod, empty, toggle, list, noTimes, missing, incomplete, underflow ({min}), overflow ({max}), step ({before}, {after}). hourCycleResolvedh11 | h12 | h23 Hour cycle in use after the locale and hour-cycle are applied. isOpenboolean Whether the list is open.
Methods
Name Description open() / close({ focus })Opens or closes the list of times. clear()Empties every segment. focus()Focuses the first empty segment (or the first one). checkValidity() / reportValidity()Native validation.
Events
Name Description mv-inputThe value changed (every edit that completes or clears a time). detail: { value, hours, minutes, seconds }. mv-changeThe value was committed: a time picked in the list, or focus leaving the field. Same detail. mv-open / mv-closeList opened and closed.
CSS classes
Name Description mv-time-picker-field / -segments / -segment / -literal / -toggle / -popup / -list / -optionGenerated parts; segments carry data-type (hour, minute, second, dayPeriod) and data-placeholder when empty.
CSS variables
Name Default Description --mv-time-picker-widthautoField width. --mv-time-picker-radiusvar(--mv-radius-md)Field radius. --mv-time-picker-list-height15remMaximum height of the list before it scrolls.
Accessibility
The field is a role=group named by the <label for> (or label). Each segment is a role=spinbutton with aria-label, aria-valuemin/max/now and aria-valuetext (“Empty” when blank), all in the tab order: ↑/↓ step (wrapping), Page ↑/↓ jump, Home/End go to the bounds, digits fill and move to the next segment, ←/→ move, Backspace clears then steps back, a/p set AM/PM. Segments are contenteditable with inputmode=numeric so phones show a number pad. The clock button (aria-haspopup=listbox, aria-expanded) or Alt+↓ opens a listbox with aria-activedescendant: ↑/↓, Page ↑/↓, Home/End, typed digits jump to a time, Enter picks, Escape closes and returns focus. Forced colors use system Highlight colors. In the open list, Tab closes it and moves on to the next field, Shift+Tab closes it and goes back to the clock button. Known limits: validation messages go through the form (validationMessage, reportValidity, the browser bubble); the component shows the invalid state but no error text of its own, so place one next to it (aria-describedby is passed to every segment); the list offers times at step minutes only (no seconds), up to 1,440 entries; the picker has no time zone: the value is a wall-clock time.