CALENDAR DISPLAY: Events month grid (display-only, not a date picker)
VARIANTS: Full month | Empty month | Compact (container-query collapse)
"Browse by month" view for the Events vertical. A real <table> (caption +
th[scope=col]) — no role="grid", no roving tabindex: event entries are
ordinary links in the tab order. Zero JS — month prev/next is server-driven navigation (here:
anchor links between the two demo months; a disabled button marks a range edge). Event chips are
tone-named (-info / -warning / -accent) and bound to meaning
by the legend. Below ~560px of container width, chips collapse to dots + a count with a
screen-reader-only category breakdown.
Full month: August 2026 (Monday start), today marked on the 12th — filled navy
circle, aria-current="date" and a screen-reader-only "(today)" suffix, never colour
alone. Out-of-month days are muted and their date numbers carry a screen-reader-only month
suffix. Saturday the 15th shows the .calendar-display-more "+2 more" overflow link
(a link to the day's list view — no popup). "Previous" is a real disabled button
(range edge); "Next" is a real link to the September calendar below.
| Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday |
|---|---|---|---|---|---|---|
27 July |
28 July |
29 July |
30 July |
31 July |
1 |
2 |
3 |
5 |
7 |
8 |
9 |
||
10 |
11 |
12 (today)
2 events: 1 deadline, 1 open day |
13 |
14 |
15
4 events: 1 open day, 2 taster events, 1 deadline |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
|
24 |
26 |
27 |
29 |
30 |
||
31 |
1 September |
2 September |
3 September |
4 September |
5 September |
6 September |
<div class="calendar-display"> <table class="calendar-display-table"> ... <a class="calendar-display-event calendar-display-event-info" href="...">Open Day — ...</a>
Empty month: September 2026 with no events — the grid renders normally (5 week
rows here; the row count is markup-driven, never padded to 6), the legend is omitted because
there is nothing to key, and a short note under the table tells the parent why it's quiet.
"Next" is the disabled range edge; "Previous" links back up to August.
This scenario deliberately does not compose .empty-state: the grid isn't
empty — every day cell still renders, still shows its date, and is still browsable — so a
centred icon + title + action block would cover working content to announce the absence of a
subset of it. .empty-state belongs on the Events list view, where the
region really is empty.
| Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday |
|---|---|---|---|---|---|---|
31 August |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
1 October |
2 October |
3 October |
4 October |
No events scheduled in September yet — providers usually publish autumn open days after enrolment closes.
<td class="calendar-display-outside">...</td> — muted out-of-month cell; row count is markup-driven (5 or 6 weeks)
Compact: the same August markup inside a ~340px column. The collapse is a
container query on .calendar-display (threshold 560px), so it also fires
when the calendar sits in a narrow column beside a filter rail at desktop widths — and on any
small viewport. Weekday headers drop to single letters (full names stay for screen readers),
and each day's chips are replaced by category dots + a visible count backed by a
screen-reader-only breakdown ("2 events: 1 deadline, 1 open day"). The hidden chip links leave
the tab order entirely — no sub-24px targets on small screens.
| Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday |
|---|---|---|---|---|---|---|
27 July |
28 July |
29 July |
30 July |
31 July |
1 |
2 |
3 |
5 |
7 |
8 |
9 |
||
10 |
11 |
12 (today)
2 events: 1 deadline, 1 open day |
13 |
14 |
15
4 events: 1 open day, 2 taster events, 1 deadline |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
|
24 |
26 |
27 |
29 |
30 |
||
31 |
1 September |
2 September |
3 September |
4 September |
5 September |
6 September |
<p class="calendar-display-summary"><span class="calendar-display-dot calendar-display-dot-info" aria-hidden="true"></span><span class="calendar-display-count" aria-hidden="true">2</span><span class="tw:sr-only">2 events: ...</span></p>
Quick Reference
Shell: .calendar-display > .calendar-display-header (.calendar-display-title + .calendar-display-nav) + .calendar-display-table + .calendar-display-legend
Grid: table > caption(sr-only) + th.calendar-display-weekday[scope=col] + td[.calendar-display-outside] > .calendar-display-day
Day: .calendar-display-date [.calendar-display-date-today + aria-current="date" on the td] + .calendar-display-events > .calendar-display-event-{info|warning|accent} + .calendar-display-more
Compact (@container ≤560px): .calendar-display-summary > .calendar-display-dot-{info|warning|accent} + .calendar-display-count + sr-only breakdown