// Datepicker // // Styles the `data-vc-*` markup that Vanilla Calendar Pro renders, the same way // Bootstrap 6 does: the calendar grid is one set of tokens (`--calendar-*`), // the popup surface the datepicker adds around it is another (`--datepicker-*`). // An inline calendar renders unadorned and takes the surface of its container. @use '../config' as *; // stylelint-disable selector-max-attribute, selector-no-qualifying-type -- the plugin marks everything with data attributes // // Calendar grid // [data-vc='calendar'] { --calendar-padding: #{$datepicker-calendar-padding}; --calendar-color: var(--body-color); --calendar-font-size: #{$datepicker-calendar-font-size}; --calendar-header-font-weight: #{$datepicker-calendar-header-font-weight}; --calendar-weekday-color: var(--secondary); --calendar-week-number-color: var(--secondary); --calendar-week-number-border-color: var(--border-color); --calendar-day-hover-bg: var(--hover-bg); --calendar-day-selected-bg: var(--primary); --calendar-day-selected-color: var(--primary-fg); --calendar-day-today-bg: #{color-transparent(var(--body-color), 6%)}; --calendar-day-today-color: var(--primary); --calendar-day-disabled-color: var(--disabled-color); --calendar-day-size: #{$datepicker-calendar-day-size}; --calendar-day-font-size: #{$datepicker-calendar-day-font-size}; --calendar-border-radius: var(--border-radius); position: relative; box-sizing: border-box; display: flex; flex-direction: column; width: fit-content; padding: var(--calendar-padding); font-family: inherit; font-size: var(--calendar-font-size); color: var(--calendar-color); button { font-family: inherit; @include focus-ring-transition(); } button:focus-visible { position: relative; z-index: 1; @include focus-ring(true); } } // Month navigation [data-vc-arrow] { position: relative; display: block; width: 2rem; height: 2rem; color: var(--calendar-color); pointer-events: auto; cursor: pointer; background-color: transparent; border: 0; @include border-radius(var(--calendar-border-radius)); &::before { position: absolute; inset: 0.25rem; content: ''; background-color: currentColor; @include mask-icon(url("data:image/svg+xml,"), $size: 1rem); } &:hover { background-color: var(--calendar-day-hover-bg); } } [data-vc-arrow='prev']::before { transform: rotate(90deg); } [data-vc-arrow='next']::before { transform: rotate(-90deg); } // The controls overlay the header, so they repeat the calendar's own inset to // keep the arrows aligned with the content below. [data-vc='controls'] { position: absolute; top: 0; right: 0; left: 0; z-index: 20; display: flex; align-items: center; justify-content: space-between; padding-block-start: var(--calendar-padding); padding-inline: var(--calendar-padding); pointer-events: none; } [data-vc='grid'] { display: flex; flex-grow: 1; flex-wrap: wrap; gap: 1.75rem; } [data-vc='column'] { display: flex; flex-grow: 1; flex-direction: column; min-width: 240px; } // Header: month and year [data-vc='header'] { position: relative; display: flex; align-items: center; margin-bottom: 0.75rem; } [data-vc-header='content'] { display: inline-flex; flex-grow: 1; align-items: center; justify-content: center; white-space: pre-wrap; } [data-vc='month'], [data-vc='year'] { padding: 0.25rem 0.5rem; margin-inline: -0.125rem; font-size: var(--font-size-base, 1rem); font-weight: var(--calendar-header-font-weight); color: var(--calendar-color); background-color: transparent; border: 0; @include border-radius(var(--calendar-border-radius)); &:disabled { color: var(--calendar-day-disabled-color); pointer-events: none; } &:hover:not(:disabled) { background-color: var(--calendar-day-hover-bg); } } // Holds the week numbers column, when enabled, next to the month [data-vc='wrapper'] { display: flex; flex-grow: 1; } [data-vc='content'] { display: flex; flex-grow: 1; flex-direction: column; } // Month and year views. The plugin wraps each set in role="row" and // role="gridcell" elements, so the column grid goes on the row. [data-vc='months'], [data-vc='years'] { display: flex; flex-grow: 1; flex-direction: column; row-gap: 1rem; } [data-vc-months='row'], [data-vc-years='row'] { display: grid; grid-template-columns: repeat(var(--calendar-columns, 4), minmax(0, 1fr)); column-gap: 0.25rem; align-items: center; } [data-vc='years'] { --calendar-columns: 5; } [data-vc-months='cell'], [data-vc-years='cell'] { display: flex; } [data-vc-months-month], [data-vc-years-year] { display: flex; align-items: center; justify-content: center; width: 100%; height: 2.5rem; padding: 0.25rem; font-size: var(--calendar-day-font-size); font-weight: var(--calendar-header-font-weight); line-height: 1rem; color: var(--calendar-weekday-color); text-align: center; word-break: break-all; cursor: pointer; background-color: transparent; border: 0; @include border-radius(var(--calendar-border-radius)); &:disabled { color: var(--calendar-day-disabled-color); pointer-events: none; } &:hover:not(:disabled) { background-color: var(--calendar-day-hover-bg); } &[data-vc-months-month-selected], &[data-vc-years-year-selected] { color: var(--calendar-day-selected-color); background-color: var(--calendar-day-selected-bg); &:hover { color: var(--calendar-day-selected-color); background-color: var(--calendar-day-selected-bg); } } } // Weekday header [data-vc='week'] { display: grid; grid-template-columns: repeat(7, 1fr); justify-items: center; margin-bottom: 0.5rem; } [data-vc-week-day] { display: flex; align-items: center; justify-content: center; width: 100%; min-width: var(--calendar-day-size); padding: 0; margin: 0; font-size: var(--calendar-day-font-size); font-weight: var(--calendar-header-font-weight); line-height: 1rem; color: var(--calendar-weekday-color); background-color: transparent; border: 0; } button[data-vc-week-day] { cursor: pointer; } // Week numbers: a column in front of the month, with a title that lines up // with the weekday header and one number for each row of dates. [data-vc-week='numbers'] { display: flex; flex-direction: column; padding-inline-end: 0.5rem; margin-inline-end: 0.5rem; border-inline-end: var(--border-width) solid var(--calendar-week-number-border-color); } [data-vc-week-numbers='title'] { display: flex; align-items: center; justify-content: center; margin-bottom: 0.5rem; font-size: var(--calendar-day-font-size); font-weight: var(--calendar-header-font-weight); line-height: 1rem; color: var(--calendar-week-number-color); } [data-vc-week-numbers='content'] { display: grid; grid-auto-rows: calc(var(--calendar-day-size) + 0.25rem); align-items: center; justify-items: center; } [data-vc-week-number] { display: flex; align-items: center; justify-content: center; width: 100%; min-width: var(--calendar-day-size); font-size: var(--calendar-day-font-size); font-weight: $font-weight-normal; line-height: 1rem; color: var(--calendar-week-number-color); } // Dates grid [data-vc='dates'] { pointer-events: none; } [data-vc-dates='row'] { display: grid; grid-template-columns: repeat(7, 1fr); align-items: center; justify-items: center; width: 100%; } [data-vc-date] { position: relative; display: flex; align-items: center; justify-content: center; width: 100%; padding-top: 0.125rem; padding-bottom: 0.125rem; pointer-events: auto; &:not(:has([data-vc-date-btn])), &[data-vc-date-disabled], &[data-vc-date-disabled] [data-vc-date-btn] { pointer-events: none; } } // A day paints its background from two variables: the colour a state picks // and the opacity it gets. States change the variables, never `opacity`, so // the date stays readable on a faded background. [data-vc-date-btn] { --calendar-day-bg: transparent; --calendar-day-bg-opacity: 100%; display: flex; align-items: center; justify-content: center; width: 100%; min-width: var(--calendar-day-size); height: 100%; min-height: var(--calendar-day-size); padding: 0; font-size: var(--calendar-day-font-size); font-weight: $font-weight-normal; line-height: 1rem; color: var(--calendar-color); cursor: pointer; background-color: color-transparent(var(--calendar-day-bg), var(--calendar-day-bg-opacity)); border: 0; border-radius: var(--calendar-border-radius); @include focus-ring-transition(color $transition-time, background-color $transition-time); &:hover { --calendar-day-bg: var(--calendar-day-hover-bg); } } // Today [data-vc-date-today] [data-vc-date-btn] { font-weight: $font-weight-bold; --calendar-day-bg: var(--calendar-day-today-bg); color: var(--calendar-day-today-color); } // Days of the previous and next month [data-vc-date-month='next'] [data-vc-date-btn], [data-vc-date-month='prev'] [data-vc-date-btn] { --calendar-day-bg-opacity: 50%; color: var(--calendar-weekday-color); } [data-vc-date-disabled] [data-vc-date-btn] { color: var(--calendar-day-disabled-color); } // Range selection: the hovered and selected runs join into one bar, so only the // outer corners keep their radius. Logical corners follow the writing direction. [data-vc-date-hover] [data-vc-date-btn] { --calendar-day-bg: var(--calendar-day-hover-bg); border-radius: 0; } [data-vc-date-hover='first'] [data-vc-date-btn] { border-start-start-radius: var(--calendar-border-radius); border-end-start-radius: var(--calendar-border-radius); } [data-vc-date-hover='last'] [data-vc-date-btn] { border-start-end-radius: var(--calendar-border-radius); border-end-end-radius: var(--calendar-border-radius); } [data-vc-date-hover='first-and-last'] [data-vc-date-btn] { border-radius: var(--calendar-border-radius); } [data-vc-date-selected] [data-vc-date-btn] { --calendar-day-bg: var(--calendar-day-selected-bg); color: var(--calendar-day-selected-color); } // Inside a range only the two ends are solid; the days between keep their own // text colour on a faint tint of the selected colour. [data-vc-date-selected='middle'] [data-vc-date-btn] { --calendar-day-bg-opacity: #{$datepicker-calendar-range-bg-opacity}; color: var(--calendar-color); border-radius: 0; } [data-vc-date-selected='first'] [data-vc-date-btn] { border-start-start-radius: var(--calendar-border-radius); border-start-end-radius: 0; border-end-start-radius: var(--calendar-border-radius); border-end-end-radius: 0; } [data-vc-date-selected='last'] [data-vc-date-btn] { border-start-start-radius: 0; border-start-end-radius: var(--calendar-border-radius); border-end-start-radius: 0; border-end-end-radius: var(--calendar-border-radius); } [data-vc-date-selected='first-and-last'] [data-vc-date-btn] { border-radius: var(--calendar-border-radius); } // // Popup surface // // The plugin adds `[data-vc-input]` only when it owns a popup, so inline // calendars keep the bare grid styling above. [data-vc='calendar'][data-vc-input] { --datepicker-padding: #{$datepicker-padding}; --datepicker-bg: #{$datepicker-bg}; --datepicker-border-radius: #{$datepicker-border-radius}; --datepicker-box-shadow: #{$datepicker-box-shadow}; --datepicker-min-width: #{$datepicker-min-width}; --datepicker-zindex: #{$zindex-dropdown}; --calendar-padding: var(--datepicker-padding); position: absolute; z-index: var(--datepicker-zindex); min-width: var(--datepicker-min-width); // Same surface as `.dropdown-menu`: the edge is the 1px ring inside the // dropdown shadow, so the popup has no border of its own. background-color: var(--datepicker-bg); box-shadow: var(--datepicker-box-shadow); @include border-radius(var(--datepicker-border-radius)); // The plugin creates the popup at 0/0 with this attribute, positions it on // show, and puts the attribute back on hide. The selector repeats the popup // one so it wins on specificity; otherwise the popup flashes in the corner // and never disappears. &[data-vc-calendar-hidden] { pointer-events: none; opacity: 0; } } [data-vc-position='bottom'] { margin-block-start: 0.25rem; } [data-vc-position='top'] { margin-block-end: -0.25rem; } // stylelint-enable selector-max-attribute, selector-no-qualifying-type