A lightweight, customizable web component timepicker
Restrict selectable time ranges using start-time and end-time attributes.
Select a time in either picker to dynamically restrict the other's range.
const startPicker = document.getElementById('start-picker');
const endPicker = document.getElementById('end-picker');
startPicker.addEventListener('change', (e) => {
if (e.detail.value) endPicker.setAttribute('start-time', e.detail.value);
else endPicker.removeAttribute('start-time');
});
endPicker.addEventListener('change', (e) => {
if (e.detail.value) startPicker.setAttribute('end-time', e.detail.value);
else startPicker.removeAttribute('end-time');
});
Apply common attributes globally using JavaScript. Notice how the last picker overrides the
interval.
<!-- HTML -->
<time-picker class="common-time-demo"></time-picker>
<time-picker class="common-time-demo"></time-picker>
<time-picker class="common-time-demo" interval="30"></time-picker>
<!-- JavaScript -->
const defaultOptions = {
'use-ampm': 'true',
'interval': '15',
'hour-label': 'Hour',
'min-label': 'Min',
'ampm-label': 'AM/PM'
};
document.querySelectorAll('.common-time-demo').forEach(picker => {
Object.entries(defaultOptions).forEach(([key, value]) => {
if (!picker.hasAttribute(key)) {
picker.setAttribute(key, value);
}
});
});
Customize the appearance using CSS Custom Properties.
.custom-styled {
--ampm-primary-color: #6c5ce7;
--ampm-border-radius: 20px;
}
.custom-styled::part(container) {
background-color: #f0f0ff;
}
/* Dark mode override */
[data-theme="dark"] .custom-styled::part(container) {
background-color: #1a1a2e;
}
Customize internal elements directly using the ::part() selector and attribute selectors.
<time-picker class="neon-styled"
dropdown-class="neon-dropdown">
/* Host styling (::part) */
.neon-styled {
--ampm-primary-color: #ff5e00;
--ampm-border-radius: 20px;
}
.neon-styled::part(container) {
background-color: #fff; /* light mode: white bg */
border: 2px solid #ff5e00;
box-shadow: 0 4px 10px rgba(255, 94, 0, 0.3);
}
.neon-styled::part(input) { color: #ff5e00; }
.neon-styled::part(toggle-btn) {
background-color: #ff5e00; color: #111;
}
/* Dropdown styling */
.neon-dropdown {
background-color: #fff; /* light mode: white bg */
border: 2px solid #ff5e00;
color: #ff5e00;
}
.neon-dropdown .ampm-header {
background-color: #f5f5f5; /* light mode: light header */
border-bottom: 1px solid #ff5e00;
}
.neon-dropdown .ampm-item.ampm-active {
background-color: #ff5e00; color: #111;
}
/* Dark mode: black bg + stronger glow */
[data-theme="dark"] .neon-styled::part(container) {
background-color: #000;
box-shadow: 0 0 20px rgba(255,94,0,0.8),
inset 0 0 10px rgba(255,94,0,0.1);
}
[data-theme="dark"] .neon-dropdown {
background-color: #000;
box-shadow: 0 0 25px rgba(255,94,0,0.6);
}
.state-styled[disabled]::part(container) {
background-color: #ffeaa7;
border-color: #fdcb6e;
}
This component supports ElementInternals, allowing it to work seamlessly with native HTML
forms.
Modify attributes via JavaScript and see the component react instantly. Try changing these while the dropdown is open!