You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
308 lines
11 KiB
308 lines
11 KiB
/* TimePicker — visual layer.
|
|
*
|
|
* Reuses time-field tokens for the field-shaped portion (Input + Segments)
|
|
* and the calendar/popover surface tokens for the popover Clock body.
|
|
*/
|
|
|
|
[data-time-picker] {
|
|
--_time-field-height: var(--time-field-height-md);
|
|
--_time-field-px: var(--time-field-px-md);
|
|
--_time-field-gap: var(--time-field-gap-md);
|
|
--_time-field-segment-px: var(--time-field-segment-px-md);
|
|
--_time-field-font-size: var(--time-field-font-size-md);
|
|
--_time-field-accent-border: var(--color-primary-border);
|
|
--_time-field-accent-track: var(--color-primary-track);
|
|
--_time-field-accent-text: var(--color-primary-text);
|
|
|
|
display: inline-flex;
|
|
flex-direction: column;
|
|
gap: var(--time-field-stack-gap);
|
|
inline-size: 100%;
|
|
min-inline-size: 0;
|
|
color: var(--time-field-color);
|
|
font-family: var(--time-field-font-family);
|
|
font-size: var(--_time-field-font-size);
|
|
line-height: var(--time-field-line-height);
|
|
}
|
|
|
|
[data-time-picker][data-size='xs'] {
|
|
--_time-field-height: var(--time-field-height-xs);
|
|
--_time-field-px: var(--time-field-px-xs);
|
|
--_time-field-gap: var(--time-field-gap-xs);
|
|
--_time-field-segment-px: var(--time-field-segment-px-xs);
|
|
--_time-field-font-size: var(--time-field-font-size-xs);
|
|
}
|
|
|
|
[data-time-picker][data-size='sm'] {
|
|
--_time-field-height: var(--time-field-height-sm);
|
|
--_time-field-px: var(--time-field-px-sm);
|
|
--_time-field-gap: var(--time-field-gap-sm);
|
|
--_time-field-segment-px: var(--time-field-segment-px-sm);
|
|
--_time-field-font-size: var(--time-field-font-size-sm);
|
|
}
|
|
|
|
[data-time-picker][data-size='lg'] {
|
|
--_time-field-height: var(--time-field-height-lg);
|
|
--_time-field-px: var(--time-field-px-lg);
|
|
--_time-field-gap: var(--time-field-gap-lg);
|
|
--_time-field-segment-px: var(--time-field-segment-px-lg);
|
|
--_time-field-font-size: var(--time-field-font-size-lg);
|
|
}
|
|
|
|
[data-time-picker][data-color='secondary'] {
|
|
--_time-field-accent-border: var(--color-secondary-border);
|
|
--_time-field-accent-track: var(--color-secondary-track);
|
|
--_time-field-accent-text: var(--color-secondary-text);
|
|
}
|
|
[data-time-picker][data-color='neutral'] {
|
|
--_time-field-accent-border: var(--color-neutral-border);
|
|
--_time-field-accent-track: var(--color-neutral-track);
|
|
--_time-field-accent-text: var(--color-neutral-text);
|
|
}
|
|
[data-time-picker][data-color='affirm'] {
|
|
--_time-field-accent-border: var(--color-affirm-border);
|
|
--_time-field-accent-track: var(--color-affirm-track);
|
|
--_time-field-accent-text: var(--color-affirm-text);
|
|
}
|
|
[data-time-picker][data-color='fulfill'] {
|
|
--_time-field-accent-border: var(--color-fulfill-border);
|
|
--_time-field-accent-track: var(--color-fulfill-track);
|
|
--_time-field-accent-text: var(--color-fulfill-text);
|
|
}
|
|
[data-time-picker][data-color='risk'] {
|
|
--_time-field-accent-border: var(--color-risk-border);
|
|
--_time-field-accent-track: var(--color-risk-track);
|
|
--_time-field-accent-text: var(--color-risk-text);
|
|
}
|
|
[data-time-picker][data-color='threat'] {
|
|
--_time-field-accent-border: var(--color-threat-border);
|
|
--_time-field-accent-track: var(--color-threat-track);
|
|
--_time-field-accent-text: var(--color-threat-text);
|
|
}
|
|
[data-time-picker][data-color='loss'] {
|
|
--_time-field-accent-border: var(--color-loss-border);
|
|
--_time-field-accent-track: var(--color-loss-track);
|
|
--_time-field-accent-text: var(--color-loss-text);
|
|
}
|
|
|
|
/* ── Trigger ─────────────────────────────────────────────────────────────── */
|
|
|
|
[data-time-picker-trigger] {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex: 0 0 auto;
|
|
inline-size: var(--_time-field-height);
|
|
block-size: var(--_time-field-height);
|
|
padding: 0;
|
|
border: var(--time-field-border-width) solid var(--time-field-border);
|
|
border-radius: var(--time-field-radius);
|
|
background: var(--time-field-bg);
|
|
color: inherit;
|
|
cursor: pointer;
|
|
transition:
|
|
background var(--time-field-transition-duration) var(--time-field-transition-ease),
|
|
border-color var(--time-field-transition-duration) var(--time-field-transition-ease),
|
|
box-shadow var(--time-field-transition-duration) var(--time-field-transition-ease),
|
|
color var(--time-field-transition-duration) var(--time-field-transition-ease);
|
|
}
|
|
|
|
[data-time-picker-trigger]:hover {
|
|
border-color: var(--_time-field-accent-border);
|
|
background: var(--_time-field-accent-track);
|
|
color: var(--_time-field-accent-text);
|
|
}
|
|
|
|
[data-time-picker-trigger]:focus-visible {
|
|
border-color: var(--_time-field-accent-border);
|
|
/* Focus ring as a flush outline (no offset → no gap) — survives forced-colors. */
|
|
outline: var(--focus-ring-width) solid var(--focus-ring-color);
|
|
outline-offset: 0;
|
|
}
|
|
|
|
[data-time-picker-trigger][data-disabled],
|
|
[data-time-picker-trigger]:disabled {
|
|
cursor: not-allowed;
|
|
opacity: var(--time-field-disabled-opacity);
|
|
}
|
|
|
|
/* Embedded trigger inside the input — flush affordance like date-picker. */
|
|
[data-time-field-input] [data-time-picker-trigger] {
|
|
flex: 0 0 auto;
|
|
/* `--time-field-segment-height` is scoped to `[data-time-field]` (not rendered
|
|
here) → undefined, box collapsed. Use the picker-local `--_time-field-height`
|
|
(scales per size). `font: inherit` so the `1em` icon tracks the field font. */
|
|
inline-size: calc(var(--_time-field-height) - var(--space-2));
|
|
block-size: calc(var(--_time-field-height) - var(--space-2));
|
|
margin-inline-start: auto;
|
|
padding: 0;
|
|
border: none;
|
|
border-radius: var(--time-field-segment-radius);
|
|
background: transparent;
|
|
color: var(--time-field-segment-color);
|
|
box-shadow: none;
|
|
font: inherit;
|
|
}
|
|
|
|
[data-time-field-input] [data-time-picker-trigger]:hover {
|
|
border: none;
|
|
background: var(--_time-field-accent-track);
|
|
color: var(--_time-field-accent-text);
|
|
}
|
|
|
|
[data-time-field-input] [data-time-picker-trigger]:focus-visible {
|
|
outline: none;
|
|
border: none;
|
|
background: var(--field-segment-active-bg);
|
|
color: var(--field-segment-active-text);
|
|
}
|
|
|
|
/* ── Popover content / Clock surface ─────────────────────────────────────── */
|
|
|
|
[data-popover-content]:has([data-time-picker-clock]) {
|
|
--_popover-content-width-override: max-content;
|
|
--_popover-content-min-width-override: max-content;
|
|
--_popover-content-max-width-override: calc(100vw - var(--space-4));
|
|
}
|
|
|
|
[data-time-picker-clock] {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-2);
|
|
padding: var(--space-2-5);
|
|
min-inline-size: 14rem;
|
|
color: var(--color-content-primary);
|
|
font-family: var(--font-ui);
|
|
font-size: var(--font-size-sm);
|
|
line-height: var(--leading-ui);
|
|
}
|
|
|
|
[data-time-picker-clock] [data-slider] {
|
|
inline-size: 100%;
|
|
}
|
|
|
|
/* ── Labelled scale (ruler) above hour slider, below minute/second ───────── */
|
|
|
|
.time-picker-slider {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
inline-size: 100%;
|
|
}
|
|
|
|
/* Channel caption naming each slider (Hour / Minute / Second). Visual
|
|
only — the soma Slider already carries the same text as `aria-label`,
|
|
so the caption is `aria-hidden`. */
|
|
.time-picker-slider-label {
|
|
margin-block-end: var(--space-1);
|
|
color: var(--color-content-secondary);
|
|
font-family: var(--font-ui);
|
|
font-size: var(--font-size-xxs);
|
|
font-weight: var(--font-weight-medium);
|
|
letter-spacing: var(--tracking-caps);
|
|
line-height: 1;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.time-picker-scale {
|
|
position: relative;
|
|
block-size: 0.875rem;
|
|
inline-size: 100%;
|
|
/* Inner inset matches the slider's thumb radius so endpoint labels align
|
|
with the actual track endpoints, not the bounding box. */
|
|
padding-inline: calc(var(--_slider-thumb-size, 1rem) / 2);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.time-picker-scale-tick {
|
|
position: absolute;
|
|
inset-block: 0;
|
|
transform: translateX(-50%);
|
|
display: inline-flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 1px;
|
|
color: var(--color-content-muted);
|
|
font-family: var(--font-mono, var(--font-ui));
|
|
/* Smallest typographic step in the system token set — keeps the ruler
|
|
readable but visually subordinate to the slider thumb / value. */
|
|
font-size: var(--font-size-xxs);
|
|
line-height: 1;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.time-picker-scale-mark {
|
|
display: block;
|
|
inline-size: 1px;
|
|
block-size: 4px;
|
|
background: var(--color-border-default);
|
|
}
|
|
|
|
.time-picker-scale-label {
|
|
display: block;
|
|
}
|
|
|
|
/* Below variant: marks rise to the top of the scale row so the label is
|
|
underneath the track. */
|
|
.time-picker-scale-below .time-picker-scale-tick {
|
|
flex-direction: column-reverse;
|
|
}
|
|
|
|
/* ── Per-channel accent palette ───────────────────────────────────────────
|
|
Differentiates the three sliders visually: hour uses the picker's
|
|
primary accent, minute uses secondary (cool counterpart), second uses
|
|
a muted tone so it visually subordinates. All three remain
|
|
semantically equivalent — the differentiation is purely visual to
|
|
help the user track which channel each slider drives. */
|
|
|
|
/* Hour = primary, Minute = secondary, Second = neutral. Tokens are
|
|
resolved at the clock root using global CSS vars (not the picker's
|
|
inherited `--_time-field-accent-*`) because the Popover content is
|
|
portaled outside the picker scope — inherited custom properties do
|
|
not cross portal boundaries. */
|
|
[data-time-picker-clock] [data-time-picker-hour-slider] {
|
|
--_time-picker-slider-accent: var(--color-primary-border);
|
|
--_time-picker-slider-track: var(--color-primary-track);
|
|
}
|
|
|
|
[data-time-picker-clock] [data-time-picker-minute-slider] {
|
|
--_time-picker-slider-accent: var(--color-secondary-border);
|
|
--_time-picker-slider-track: var(--color-secondary-track);
|
|
}
|
|
|
|
[data-time-picker-clock] [data-time-picker-second-slider] {
|
|
--_time-picker-slider-accent: var(--color-neutral-border);
|
|
--_time-picker-slider-track: var(--color-neutral-track);
|
|
}
|
|
|
|
/* ── Slider thumb / range reinforcement inside the clock ──────────────────
|
|
The base slider recipe sets thumb color from --color-surface-default
|
|
over the primary range; inside the popover this can blend with the
|
|
popover surface. Bump emphasis with the per-channel accent. */
|
|
|
|
[data-time-picker-clock] [data-slider-thumb] {
|
|
border-color: var(--_time-picker-slider-accent, var(--_time-field-accent-border));
|
|
box-shadow:
|
|
var(--slider-thumb-shadow),
|
|
0 0 0 2px var(--color-surface-default);
|
|
}
|
|
|
|
[data-time-picker-clock] [data-slider-thumb]:focus-visible {
|
|
box-shadow:
|
|
var(--slider-thumb-shadow),
|
|
0 0 0 2px var(--color-surface-default);
|
|
outline: var(--focus-ring-width) solid var(--focus-ring-color);
|
|
outline-offset: 0;
|
|
}
|
|
|
|
[data-time-picker-clock] [data-slider-range] {
|
|
background: var(--_time-picker-slider-accent, var(--_time-field-accent-border));
|
|
}
|
|
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
[data-time-picker-trigger] {
|
|
transition: none;
|
|
}
|
|
}
|