项目文件夹

文件
2026-07-13 12:12:04 +08:00

160 行
5.5 KiB
CSS

/* =============================================================================
* 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;
}
}