Introduction
@tito10047/vanilla-js-timepicker is a lightweight, dependency-free time picker component for the web. It attaches to any <input> element and provides a dropdown UI for selecting hours, minutes, and (optionally) seconds.
The problem
A native <input type="time"> exists, but its appearance is browser-controlled and virtually unthemable. Cross-browser behaviour is inconsistent: Chrome renders a spinner, Firefox renders an inline clock, and Safari renders a plain text box in some versions. Rolling your own time input means:
<!-- You get the browser's design. No control over layout, colours, or interaction. -->
<input type="time" />Common workarounds — splitting into separate hour/minute fields, using a full-blown date library, or pulling in a date-range picker — all add either UX friction or multi-megabyte dependencies.
The solution
import { Timepicker } from '@tito10047/vanilla-js-timepicker'
import '@tito10047/vanilla-js-timepicker/dist/timepicker.css'
new Timepicker('#departure', {
format: 'HH:mm',
minuteStep: 15,
showNowButton: true,
onChange: (value) => console.log('Selected:', value),
})What you get with one constructor call:
- A spinner-based picker column for each time unit.
- A grid view for directly tapping an hour, minute, or second.
- Keyboard navigation (Arrow keys, Page Up/Down, Home, End, Escape, Enter).
- ARIA roles that screen readers understand.
- Light and dark themes driven by CSS custom properties.
- An optional "Now" button, "Clear" button, and explicit "Confirm" button.
- Async validation with human-readable error messages.
- Strict min/max clamping.
Design principles
- No framework coupling. The library ships ES module, CommonJS, and UMD builds. It works in React, Vue, Svelte, Angular, or a bare HTML page without any adapter.
- Opt-in complexity. Defaults are sensible. You do not touch
validate,onBeforeOpen, orparseStrategyunless you need them. - CSS variables, not inline styles. Every colour, radius, shadow, and font is a CSS custom property. Override a single property anywhere in your stylesheet — no
!important, no class fighting. - Events on the element. Every lifecycle event fires both as a callback option (e.g.
onChange) and as a nativeCustomEventon the input element. Frameworks that listen to DOM events work out of the box. - Async-friendly lifecycle.
open(),close(), andsetValue()are all async. Guards (onBeforeOpen,onBeforeChange,validate) accept Promises — call an API, check a server, return a result.
What this library is not
- It is not a date picker. It picks time only. For calendar integration, use a separate date picker and combine the values in your application logic.
- It is not a full UI framework component. There is no React component export, no Vue plugin. It is a plain class you
new-up on a DOM element. - It is not server-side-renderable out of the box. The dropdown is created by JavaScript at open time. The
<input>itself renders fine on the server; the picker UI requires a browser.
Next: Getting Started.