Files
2026-07-13 12:12:04 +08:00

160 lines
5.5 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* Calendar Year Picker Component Styles
*
* Internal component used inside Calendar / RangeCalendar to provide a
* toggleable year selector. Uses CSS :has() on the parent .calendar to
* drive crossfade transitions.
*
* The year grid is absolutely positioned and sized via JS to match the
* day grid's exact area. This keeps the calendar's original block layout
* completely untouched.
*
* BEM naming: calendar-year-picker__element, calendar-year-picker--modifier
* ============================================================================= */
/* -----------------------------------------------------------------------------
* Positioning Context
* -------------------------------------------------------------------------- */
.calendar:has(.calendar-year-picker__year-grid),
.range-calendar:has(.calendar-year-picker__year-grid) {
position: relative;
}
/* -----------------------------------------------------------------------------
* Crossfade + Blur Transitions
*
* The crossfade is staggered to avoid both grids being semi-transparent at
* the same time (ghosting). When opening, the day grid fades out first,
* then the year grid fades in. When closing, the year grid fades out first,
* then the day grid fades in (via transition-delay).
*
* -------------------------------------------------------------------------- */
/* Day grid default visible */
.calendar:has(.calendar-year-picker__year-grid) > [data-slot="calendar-grid"],
.range-calendar:has(.calendar-year-picker__year-grid) > [data-slot="range-calendar-grid"] {
will-change: opacity;
transition:
opacity 150ms var(--ease-out),
visibility 0ms linear;
@apply motion-reduce:transition-none;
}
/* Day grid hidden when year picker is open (fade + blur out) */
.calendar:has(.calendar-year-picker__year-grid[data-open="true"]) > [data-slot="calendar-grid"],
.range-calendar:has(.calendar-year-picker__year-grid[data-open="true"])
> [data-slot="range-calendar-grid"] {
@apply pointer-events-none opacity-0;
visibility: hidden;
transition:
opacity 150ms var(--ease-out),
visibility 0ms linear 150ms;
@apply motion-reduce:transition-none;
}
/* -----------------------------------------------------------------------------
* Trigger Button
* -------------------------------------------------------------------------- */
.calendar-year-picker__trigger {
@apply flex flex-1 items-center justify-start gap-1 rounded-lg outline-none;
cursor: var(--cursor-interactive);
touch-action: manipulation;
/* Focus state */
&:focus-visible {
@apply status-focused;
}
}
.calendar-year-picker__trigger-heading {
@apply text-sm font-medium;
transition: color 150ms var(--ease-out);
@apply motion-reduce:transition-none;
}
.calendar-year-picker__trigger-indicator {
@apply text-xs text-accent-soft-foreground;
transition: transform 150ms var(--ease-out);
@apply motion-reduce:transition-none;
}
/* Rotate chevron when open */
.calendar-year-picker__trigger[data-open="true"] .calendar-year-picker__trigger-indicator {
transform: rotate(90deg);
}
.calendar-year-picker__trigger[data-open="true"] .calendar-year-picker__trigger-heading {
@apply text-accent-soft-foreground;
}
/* -----------------------------------------------------------------------------
* Year Grid Container
*
* Absolutely positioned over the day grid area. The exact `top` and `height`
* are set via JS to match the sibling day grid element.
* -------------------------------------------------------------------------- */
.calendar-year-picker__year-grid {
@apply pointer-events-none absolute right-0 left-0 grid content-start gap-1 overflow-y-auto p-1 opacity-0 scrollbar;
grid-template-columns: repeat(3, 1fr);
/* When closing: instant (no transition) */
will-change: opacity;
}
.calendar-year-picker__year-grid[data-open="true"] {
@apply pointer-events-auto opacity-100;
/* When opening: slight delay so the day grid starts fading out first */
transition: opacity 200ms var(--ease-out) 50ms;
@apply motion-reduce:transition-none;
}
/* -----------------------------------------------------------------------------
* Year Cell
* -------------------------------------------------------------------------- */
.calendar-year-picker__year-cell {
@apply relative inline-flex h-8 items-center justify-center rounded-3xl px-2.5 text-sm font-medium outline-none select-none no-highlight;
touch-action: manipulation;
/**
* Transitions same as .tag
* CRITICAL: motion-reduce must be AFTER transition for correct override specificity
*/
transition:
color 100ms var(--ease-smooth),
scale 100ms var(--ease-smooth),
opacity 100ms var(--ease-smooth),
background-color 100ms var(--ease-smooth),
box-shadow 100ms var(--ease-out);
@apply origin-center transform-gpu motion-reduce:transition-none;
cursor: var(--cursor-interactive);
/* Hover state (when not selected) only on devices that support hover */
@media (hover: hover) and (pointer: fine) {
&:is(:hover, [data-hovered="true"]):not([data-selected="true"]) {
@apply bg-default text-default-foreground;
}
}
/* Selected state (current year) same as .tag[data-selected] */
&[data-selected="true"],
&[aria-selected="true"] {
@apply bg-accent text-accent-foreground;
@media (hover: hover) and (pointer: fine) {
&:is(:hover, [data-hovered="true"]) {
@apply bg-accent-hover;
}
}
}
/* Focus state */
&:focus-visible {
@apply status-focused;
}
}