Calendar
canonMonth grid with navigation and presets, single or range selection.
Calendar PAGE chrome (acrm !3942, AD 27 Sep 2026): month label = SectionTopBarChip opening the jump popover (the page title stays „Kalendář“); year ←→ = IconButton sm; the 12-month grid reuses the day cell `.dp-day` with `.sel` + aria-current — one „selected“ look for date picking, no button variant as state. DELIBERATE EXCEPTION: the nav trio ‹ ● › (previous · today · next) keeps its own look per the owner’s ACRM-91/97/103 (arrows without a hover surface that only darken, „Dnes“ a small grey circle-dot) — real <button>s with aria-label and a focus ring, tokens only; do not convert to IconButton. The day-panel ear (.cal-ear) is a second exception (surface chrome, tokens only).
Used in 7 files · 6 areas · 7× — components/FilterBar, components/callwrapup, components/mail, components/projects, components/tasks, components/timeline
Default
export const Default = () => {
const [value, setValue] = useState<string | null>('2026-07-26');
return (
<div className="dp-inline">
<Calendar mode="single" value={value} onChange={(v) => setValue(v)} today={TODAY} />
</div>
);
};Variants
export const Variants = () => {
const [single, setSingle] = useState<string | null>('2026-07-26');
const [range, setRange] = useRange({ from: '2026-07-14', to: '2026-07-21' });
const [narrow, setNarrow] = useRange({ from: '2026-07-14', to: '2026-07-21' });
return (
<div style={row}>
<Labeled label="single — singleDatePresets">
<div className="dp-inline">
<Calendar
mode="single"
value={single}
onChange={(v) => setSingle(v)}
today={TODAY}
presets={singleDatePresets(TODAY)}
/>
</div>
</Labeled>
<Labeled label="range, two months — pastRangePresets">
<div className="dp-inline" style={{ width: 540 }}>
<Calendar
mode="range"
months={2}
value={range}
onChange={(v) => setRange(v)}
today={TODAY}
presets={pastRangePresets(TODAY)}
/>
</div>
</Labeled>
<Labeled label="range, one month — filter chip width, no .range frame">
<div className="dp-inline">
<Calendar mode="range" value={narrow} onChange={(v) => setNarrow(v)} today={TODAY} />
</div>
</Labeled>
</div>
);
};Sizes
export const Sizes = () => {
const [one, setOne] = useState<string | null>('2026-07-26');
const [two, setTwo] = useRange({ from: '2026-07-14', to: '2026-08-04' });
return (
<div style={row}>
<Labeled label="months 1 — .dp-pop 264px">
<div className="dp-inline">
<Calendar mode="single" value={one} onChange={(v) => setOne(v)} today={TODAY} />
</div>
</Labeled>
<Labeled label="months 2 — .dp-pop.range 540px">
<div className="dp-inline" style={{ width: 540 }}>
<Calendar mode="range" months={2} value={two} onChange={(v) => setTwo(v)} today={TODAY} />
</div>
</Labeled>
</div>
);
};States
export const States = () => {
const [none, setNone] = useState<string | null>(null);
const [openEnd, setOpenEnd] = useRange({ from: '2026-07-20', to: null });
const [openStart, setOpenStart] = useRange({ from: null, to: '2026-07-10' });
const [bounded, setBounded] = useState<string | null>('2026-07-27');
return (
<div style={grid}>
<div style={row}>
<Labeled label="no selection — today 23. 7. as accent digit">
<div className="dp-inline">
<Calendar mode="single" value={none} onChange={(v) => setNone(v)} today={TODAY} />
</div>
</Labeled>
<Labeled label="open range — od 20. 7. (band continues, stop only at the start)">
<div className="dp-inline">
<Calendar mode="range" value={openEnd} onChange={(v) => setOpenEnd(v)} today={TODAY} />
</div>
</Labeled>
<Labeled label="open range — do 10. 7.">
<div className="dp-inline">
<Calendar
mode="range"
value={openStart}
onChange={(v) => setOpenStart(v)}
today={TODAY}
/>
</div>
</Labeled>
<Labeled label="min/max — days outside 20. 7. – 31. 7. disabled">
<div className="dp-inline">
<Calendar
mode="single"
value={bounded}
onChange={(v) => setBounded(v)}
today={TODAY}
min="2026-07-20"
max="2026-07-31"
/>
</div>
</Labeled>
</div>
<span style={{ color: 'var(--fg-muted)', font: 'var(--small)' }}>
Focus: the focused day (roving tabindex) draws the accent ring; hover fills with
--hover-surface; a range in progress shows `.dp-day.preview` between the first click and the
pointer.
</span>
</div>
);
};Composition
export const Composition = () => {
const [range, setRange] = useRange({ from: '2026-07-14', to: '2026-07-21' });
return (
<div style={grid}>
<Labeled label="range picker body — head + presets + two months + footer">
<div className="dp-inline" style={{ width: 540 }}>
<Calendar
mode="range"
months={2}
value={range}
onChange={(v) => setRange(v)}
today={TODAY}
presets={pastRangePresets(TODAY)}
head={
<div className="dp-entry pair">
<Icon name="calendar" />
<input aria-label="Od" placeholder="od" readOnly value="14. 7." />
<span className="dash">–</span>
<input aria-label="Do" placeholder="do" readOnly value="21. 7." />
<span className="count">8 dní</span>
</div>
}
/>
<div className="dp-foot">
<div className="dp-rule" />
<button
type="button"
className="dp-clear"
onClick={() => setRange({ from: null, to: null })}
>
<Icon name="x" />
Vymazat
</button>
</div>
</div>
</Labeled>
</div>
);
};