/* ============================================================================= * Calendar Component Styles * * A compound component for date selection. * Uses BEM naming convention: calendar, calendar__element, calendar--modifier * ============================================================================= */ /* ----------------------------------------------------------------------------- * Base Calendar Styles * -------------------------------------------------------------------------- */ .calendar { @apply w-63 max-w-63; container-type: inline-size; } /* Week view – visibleDuration={{ weeks: n }} */ .calendar--week-view { .calendar__cell { /* Keep cells circular on the 7-column grid; only filled cells take space. */ @apply aspect-square size-auto w-full place-self-center; } } /* Day view – visibleDuration={{ days: n }} */ .calendar--day-view { .calendar__cell { @apply aspect-square size-auto w-full place-self-center; } /* * Isolate header and body into separate 7-column grids. With display:contents * on the root grid, fewer than 7 weekday headers flow into the same row as dates. */ .calendar__grid { display: flex; flex-direction: column; } .calendar__grid-header { display: grid; grid-template-columns: repeat(7, 1fr); @apply w-full; & > tr { display: contents; } } .calendar__grid-body { display: grid; grid-template-columns: repeat(7, 1fr); @apply mt-1 w-full; & > tr { display: contents; } & > tr:first-child > td { @apply mt-0; } } } /* ----------------------------------------------------------------------------- * Calendar Header * -------------------------------------------------------------------------- */ .calendar__header { @apply flex items-center justify-between px-0.5 pb-4; &:has(.calendar-year-picker__trigger[data-open="true"]) { .calendar__nav-button { @apply pointer-events-none opacity-0; } } } .calendar__heading { @apply flex-1 text-sm font-medium; } .calendar__nav-button { @apply flex size-6 items-center justify-center rounded-2xl text-accent-soft-foreground; will-change: scale; /** * Transitions * CRITICAL: motion-reduce must be AFTER transition for correct override specificity */ transition: transform 250ms var(--ease-out), background-color 100ms var(--ease-out), box-shadow 100ms var(--ease-out), opacity 150ms var(--ease-out); @apply transform-gpu motion-reduce:transition-none; cursor: var(--cursor-interactive); /* Hover state – only on devices that support hover (avoids false triggers on touch) */ @media (hover: hover) { &:hover, &[data-hovered="true"] { @apply bg-default text-accent-soft-foreground; } } /* Pressed state */ &:active, &[data-pressed="true"] { transform: scale(0.95); } /* Focus state */ &:focus-visible, &[data-focus-visible="true"] { @apply status-focused; } /* Disabled state */ &:disabled, &[data-disabled="true"] { @apply status-disabled; } } .calendar__nav-button-icon { @apply size-4; } /* ----------------------------------------------------------------------------- * Calendar Grid * -------------------------------------------------------------------------- */ .calendar__grid { /* Override table display with CSS Grid for easier customization */ display: grid; grid-template-columns: repeat(7, 1fr); @apply w-full; &[aria-readonly="true"] { .calendar__cell { @apply pointer-events-none; } } } .calendar__grid-header { /* Grid header (thead) styles */ display: contents; & > tr { display: contents; } } .calendar__grid-body { /* Grid body (tbody) styles */ display: contents; & > tr { display: contents; } /* Add top spacing between header and first body row */ & > tr:first-child > td { @apply mt-1; } } .calendar__grid-row { /* Use 'contents' so cells participate directly in grid */ /* Note: This class can be used for custom row styling if needed */ display: contents; } .calendar__header-cell { /* Header cell (th - weekday names) styles */ @apply flex items-center justify-center pb-2 text-xs font-medium text-muted; } /* ----------------------------------------------------------------------------- * Calendar Cells * -------------------------------------------------------------------------- */ .calendar__cell { /* Cell (td) wrapper styles - margins now work with grid layout */ @apply relative flex aspect-square size-full items-center justify-center rounded-3xl text-center text-sm font-medium outline-none no-highlight; will-change: scale; /** * Transitions * CRITICAL: motion-reduce must be AFTER transition for correct override specificity */ transition: transform 250ms var(--ease-out), box-shadow 100ms var(--ease-out); @apply transform-gpu motion-reduce:transition-none; cursor: var(--cursor-interactive); /* Focus state */ &:focus-visible:not(:focus), &[data-focus-visible="true"] { @apply status-focused; } /* Today indicator */ &[data-today="true"] { @apply bg-accent-soft text-accent-soft-foreground; @media (hover: hover) { &:hover:not([data-selected="true"]), &[data-hovered="true"]:not([data-selected="true"]) { @apply bg-accent-soft-hover; } } } /* Selected state */ &[data-selected="true"] { @apply bg-accent text-accent-foreground; } /* Pressed state */ &:active, &[data-pressed="true"] { @apply bg-default; transform: scale(0.95); &[data-selected="true"] { @apply bg-accent-hover; } } /* Hover state – only on devices that support hover (avoids false triggers on touch) */ @media (hover: hover) { &:hover:not([data-selected="true"]), &[data-hovered="true"]:not([data-selected="true"]) { @apply bg-default; } } /* Outside month (previous/next month days) */ &[data-outside-month="true"] { @apply text-muted opacity-50; } &[data-selected="true"][data-outside-month="true"] { @apply bg-default; } /* Unavailable dates */ &[data-unavailable="true"] { @apply status-disabled; } /* Disabled state */ &:disabled:not([data-outside-month="true"]), &[data-disabled="true"]:not([data-outside-month="true"]) { @apply status-disabled; text-decoration: line-through; } } /* Cell indicator - small dot at bottom center of cell */ .calendar__cell-indicator { @apply absolute bottom-1 left-1/2 size-[3px] -translate-x-1/2 rounded-xs bg-muted; /* Indicator color changes when cell is selected */ [data-selected="true"] > & { @apply bg-accent-foreground; } }