Calendar

Selector de dată, în două trepte: .calendar — ancorat de un câmp, într-un popover — și .calendar.lg — de sine stătător, cu controale obișnuite în antet. Selecția de interval e trei stări pe .calendar-day, pe aceeași grilă.

Markup canonic · components/calendar.json

sursa = JSON · generat de scripts/build-catalog.py · treapta implicită
L M M J V S D

Mărimi · .calendar și .calendar.lg

aceeași lună, două trepte — diferă antetul, nu doar densitatea
.calendar · 34px
L M M J V S D
.calendar.lg · 44px
LU MA MI JO VI SÂ DU

Treptele diferă prin antet. Implicit: .calendar-select — select nativ fără bordură și fără fundal — plus navigare .btn.ghost.sm.icon-only; luni abreviate. Pe lg: .select pentru lună și an, nume întregi, navigare .btn.outline.icon-only. Treapta mare nu adaugă nicio clasă nouă — e compoziție.

TreaptăLună / anÎnainte / înapoiCelulăCând
.calendar
implicit
.calendar-select — fără bordură, 30px, luni abreviate (Aug).btn.ghost.sm.icon-only 30px34×34 · 13pxAncorat de un declanșator: câmp de dată, filtru, buton de perioadă. Obligatoriu înăuntrul unui .popover.
.calendar.lg.select 40px, nume întregi (August).btn.outline.icon-only 40px44×44 · 15pxCalendarul e conținutul: ecran de rezervare, panou de filtre, agendă. 44px e și ținta de atins pe touch.

În popover, lângă un câmp

treapta implicită în contextul pentru care există
L M M J V S D

.popover .calendar pune --cal-pad: 0 și scoate rama și fundalul: chrome-ul e al popoverului, padding-ul vine din .popover.content. Declanșatorul e .select-anchor.block + .select-trigger (components/select.html) — până la v8.0 era un .input readonly într-un div cu position:relative scris inline. Înăuntru, luna și anul rămân <select> native: un popover în popover n-are poveste de închidere.

Scurtături · .calendar-presets

rând opțional sub grilă · .btn.outline.xs — cea mai mică treaptă din DS
.calendar · scurtături
L M M J V S D
.calendar.lg · scurtături
LU MA MI JO VI SÂ DU

Model shadcn: rând sub grilă, despărțit de o linie, butoane outline pe cea mai mică treaptă, flex: 1. La ei Button variant="outline" size="sm" în CardFooter cu border-t; aici cea mai mică treaptă din DS e .btn.xs (26px).

Reguli: date relative unice pe un calendar de o zi; scurtături de interval („Ultimele 7 zile") doar unde se aplică și stările de interval. Presetul mută și luna afișată. Rândul e opțional și contextual, 3–5 bucăți; niciodată .btn.primary.

Interval · .is-range-start · .in-range · .is-range-end

trei stări pe .calendar-day — aceeași grilă, nicio clasă pe rădăcină
.calendar · 10–21 aug
L M M J V S D

O zi, sau două clickuri pentru interval.

.calendar.lg · peste hotarul lunii
LU MA MI JO VI SÂ DU

Trei stări pe .calendar-day, aceeași grilă, nicio clasă pe rădăcină — deci identic pe ambele trepte. Capetele: --c-accent plin, text alb. Interiorul: bandă --c-accent-tint.

Banda stă pe ::before și se întinde cu jumătate de gap în ambele direcții — două jumătăți acoperă gapul grilei, fără aritmetică pe :nth-child. Pictare: fundal buton → ::before (bandă) → ::after (discul capătului) → cifră. isolation: isolate pe celulă e obligatoriu: altfel z-index: -1 iese sub fundalul alb al calendarului și banda dispare.

SituațieClasăTratament
capătul de început / sfârșit.is-range-start · .is-range-end--c-accent plin pe ::after, text alb, 600 · banda pleacă spre interior de la 50%
zi din interior.in-rangebandă --c-accent-tint, text --c-ink (16.41:1)
zi din altă lună, în interval.is-muted.in-rangetext --c-accent-deep — 4.58:1 pe tint; mist ar da 2.08, fog 3.88
azi, în interval.is-today.in-rangedoar greutatea 600 — inelul e un inset shadow, s-ar picta sub bandă
hover în interior—inel --c-accent 1px: banda ocupă fundalul, iar un click acolo începe un interval nou
interval de o zi.is-selectedcând capetele coincid nu e interval — e o zi selectată

Contractul de interacțiune (starea o ține ecranul, DS-ul dă clasele): primul click pune începutul, al doilea îl închide; capetele inversate se normalizează; un click în interiorul unui interval închis începe unul nou; pe hover se aplică aceleași clase, deci previzualizarea și rezultatul sunt identice; un preset de interval scrie ambele capete și mută luna. O zi :disabled nu poate fi în interval — ecranul refuză al doilea capăt.

.calendar-foot — opțional, recomandat cu interval: .t13 la stânga, .btn.link de golire la dreapta.

Stari

StareClasăTratament
zi normală—ink pe transparent, hover bej-1
altă lună.is-muted--c-mist
azi.is-todayinel circular --c-input-border + weight 600
selectată.is-selecteddisc --c-accent plin, text alb
indisponibilă:disabled--c-mist, cursor not-allowed — și niciodată .in-range

Stările sunt aceleași pe ambele trepte — se schimbă doar mărimea celulei. Stările de interval sunt în blocul Interval. Select-ul fără bordură are hover bej-1 și focus pe outline fog (n-are bordură care să poarte halo-ul, ca .input).

Metrici

.calendar            padding 14px · alb · 1px --c-rule-card · --r-lg · 280px lat
.calendar-grid       7 coloane de 34px · gap 2px
.calendar-day        34x34 · cerc (border-radius 50%) · 13px tabular
.calendar-dow        11px/500 · 0.06em · uppercase · --c-fog · o literă
.calendar-picker     grupul lună+an, flex:1, gap 12px
.calendar-select     30px · fără bordură · 14px/600 · săgeata de la .select
luna                 abreviată (Ian, Feb, …)
navigarea lunii      .btn.ghost.sm.icon-only 30px

.calendar.lg         padding 20px · --r-xl · 374px lat
.calendar.lg grid    7 coloane de 44px · gap 4px
.calendar.lg day     44x44 · 15px tabular (44px = ținta de atins pe touch)
.calendar.lg dow     12px · două litere (LU MA MI JO VI SÂ DU)
luna                 nume întreg
lună / an            .select 40px — flex 1 1 auto + width auto
navigarea lunii      .btn.outline.icon-only 40px

.calendar-presets    rând opțional sub grilă · linie --c-rule-soft ·
                     .btn.outline.xs (26px) · flex-wrap, flex:1 pe buton
                     gap 6px / 12px sus — pe lg 8px / 16px

.calendar-foot       rând opțional · .t13 (max 62%) + .btn.link · aceeași linie

interval             .is-range-start / .in-range / .is-range-end pe .calendar-day
banda                ::before, întinsă cu var(--cal-gap)/2 în ambele direcții
discul capătului     ::after, --c-accent, cerc — pictat peste bandă
celula               position:relative + isolation:isolate (obligatoriu)

treapta = 3 valori   --cal-cell · --cal-gap · --cal-pad
lățimea              calc(7 × cell + 6 × gap + 2 × pad + 2px) — o dă grila
.popover .calendar   --cal-pad: 0 · fără ramă și fără fundal — chrome-ul e al popoverului