Calendar
tier 2 — classesThe surfaces a scheduling view is drawn on — a month, a week or a day on a time axis, a year — and a date picker built on the same grid. The library paints the grid; every date, span, overlap and recurrence is your app's to work out.
grid-column, and a block says when it starts
and how long it runs with --cal-start and --cal-span. Nothing is
measured, so a server render is already right and a re-render has nothing to fight.
A month
One .cal-row per week: seven .cal-days, and over them
.cal-row-events in the same seven columns, where each .cal-event takes
its days with grid-column and a lane with grid-row.
--cal-lanes is how many lanes a day shows; what does not fit is a
.cal-more, which sits in the row under them. A span that runs into the next week is
two pills, --continues-end then --continues-start. Today is
aria-current="date" on the day; --outside and --weekend
are the days you mark. Pills take the badge families — --go, --warn,
--danger, --info, --cyan, --orange,
--teal.
<div class="cal-month" style="--cal-lanes: 2">
<div class="cal-weekdays">
<span class="cal-weekday">Mon</span>
<span class="cal-weekday">Tue</span>
<span class="cal-weekday">Wed</span>
<span class="cal-weekday">Thu</span>
<span class="cal-weekday">Fri</span>
<span class="cal-weekday">Sat</span>
<span class="cal-weekday">Sun</span>
</div>
<div class="cal-row">
<div class="cal-day cal-day--outside"><span class="cal-day-num">31</span></div>
<div class="cal-day"><span class="cal-day-num">1</span></div>
<div class="cal-day"><span class="cal-day-num">2</span></div>
<div class="cal-day"><span class="cal-day-num">3</span></div>
<div class="cal-day"><span class="cal-day-num">4</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">5</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">6</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--teal" href="#" style="grid-column: 5 / span 3; grid-row: 1">Autumn stocktake</a>
</div>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">7</span></div>
<div class="cal-day"><span class="cal-day-num">8</span></div>
<div class="cal-day"><span class="cal-day-num">9</span></div>
<div class="cal-day"><span class="cal-day-num">10</span></div>
<div class="cal-day"><span class="cal-day-num">11</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">12</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">13</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--info" href="#" style="grid-column: 1; grid-row: 1"><span class="cal-event-time">09:00</span> Carrier review</a>
<a class="cal-event cal-event--warn" href="#" style="grid-column: 3 / span 2; grid-row: 2">EU-West dock closed</a>
<a class="cal-event cal-event--continues-end cal-event--cyan" href="#" style="grid-column: 6 / span 2; grid-row: 1">Peak rehearsal</a>
</div>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">14</span></div>
<div class="cal-day"><span class="cal-day-num">15</span></div>
<div class="cal-day" aria-current="date"><span class="cal-day-num">16</span></div>
<div class="cal-day"><span class="cal-day-num">17</span></div>
<div class="cal-day"><span class="cal-day-num">18</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">19</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">20</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--continues-start cal-event--cyan" href="#" style="grid-column: 1 / span 2; grid-row: 1">Peak rehearsal</a>
<a class="cal-event cal-event--go" href="#" style="grid-column: 3; grid-row: 1"><span class="cal-event-time">14:00</span> Dispatch ORD-4209</a>
<a class="cal-event" href="#" style="grid-column: 3; grid-row: 2"><span class="cal-event-time">15:30</span> Handover</a>
<button class="cal-more" type="button" style="grid-column: 3">+2 more</button>
</div>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">21</span></div>
<div class="cal-day"><span class="cal-day-num">22</span></div>
<div class="cal-day"><span class="cal-day-num">23</span></div>
<div class="cal-day"><span class="cal-day-num">24</span></div>
<div class="cal-day"><span class="cal-day-num">25</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">26</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">27</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--danger" href="#" style="grid-column: 4; grid-row: 1">Audit deadline</a>
</div>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">28</span></div>
<div class="cal-day"><span class="cal-day-num">29</span></div>
<div class="cal-day"><span class="cal-day-num">30</span></div>
<div class="cal-day cal-day--outside"><span class="cal-day-num">1</span></div>
<div class="cal-day cal-day--outside"><span class="cal-day-num">2</span></div>
<div class="cal-day cal-day--outside cal-day--weekend"><span class="cal-day-num">3</span></div>
<div class="cal-day cal-day--outside cal-day--weekend"><span class="cal-day-num">4</span></div>
</div>
</div>
Week numbers
.cal-month--weeknums adds a column before the days, headed by an empty
.cal-weekday, and each row starts with a .cal-weeknum.
The number is yours — ISO 8601 or your reader's locale rule — because a stylesheet
cannot count weeks. Make it a <button> when choosing the week opens something,
and give it a visually hidden word, since a bare "38" is not a week to a screen reader. Pills and
.cal-more still take grid-column 1 to 7: the lanes cover only the days.
The same column works in the date picker.
<div class="cal-month cal-month--weeknums" style="--cal-lanes: 2">
<div class="cal-weekdays">
<span class="cal-weekday"><span class="visually-hidden">Week</span></span>
<span class="cal-weekday">Mon</span>
<span class="cal-weekday">Tue</span>
<span class="cal-weekday">Wed</span>
<span class="cal-weekday">Thu</span>
<span class="cal-weekday">Fri</span>
<span class="cal-weekday">Sat</span>
<span class="cal-weekday">Sun</span>
</div>
<div class="cal-row">
<button class="cal-weeknum" type="button"><span class="visually-hidden">Show week </span>36</button>
<div class="cal-day cal-day--outside"><span class="cal-day-num">31</span></div>
<div class="cal-day"><span class="cal-day-num">1</span></div>
<div class="cal-day"><span class="cal-day-num">2</span></div>
<div class="cal-day"><span class="cal-day-num">3</span></div>
<div class="cal-day"><span class="cal-day-num">4</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">5</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">6</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--teal" href="#" style="grid-column: 5 / span 3; grid-row: 1">Autumn stocktake</a>
</div>
</div>
<div class="cal-row">
<button class="cal-weeknum" type="button"><span class="visually-hidden">Show week </span>37</button>
<div class="cal-day"><span class="cal-day-num">7</span></div>
<div class="cal-day"><span class="cal-day-num">8</span></div>
<div class="cal-day"><span class="cal-day-num">9</span></div>
<div class="cal-day"><span class="cal-day-num">10</span></div>
<div class="cal-day"><span class="cal-day-num">11</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">12</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">13</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--info" href="#" style="grid-column: 1; grid-row: 1"><span class="cal-event-time">09:00</span> Carrier review</a>
<a class="cal-event cal-event--warn" href="#" style="grid-column: 3 / span 2; grid-row: 2">EU-West dock closed</a>
</div>
</div>
<div class="cal-row">
<button class="cal-weeknum" type="button"><span class="visually-hidden">Show week </span>38</button>
<div class="cal-day"><span class="cal-day-num">14</span></div>
<div class="cal-day"><span class="cal-day-num">15</span></div>
<div class="cal-day" aria-current="date"><span class="cal-day-num">16</span></div>
<div class="cal-day"><span class="cal-day-num">17</span></div>
<div class="cal-day"><span class="cal-day-num">18</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">19</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">20</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--go" href="#" style="grid-column: 3; grid-row: 1"><span class="cal-event-time">14:00</span> Dispatch ORD-4209</a>
<a class="cal-event cal-event--cyan" href="#" style="grid-column: 4 / span 2; grid-row: 1">Peak rehearsal</a>
</div>
</div>
<div class="cal-row">
<button class="cal-weeknum" type="button"><span class="visually-hidden">Show week </span>39</button>
<div class="cal-day"><span class="cal-day-num">21</span></div>
<div class="cal-day"><span class="cal-day-num">22</span></div>
<div class="cal-day"><span class="cal-day-num">23</span></div>
<div class="cal-day"><span class="cal-day-num">24</span></div>
<div class="cal-day"><span class="cal-day-num">25</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">26</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">27</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--danger" href="#" style="grid-column: 4; grid-row: 1">Audit deadline</a>
</div>
</div>
<div class="cal-row">
<button class="cal-weeknum" type="button"><span class="visually-hidden">Show week </span>40</button>
<div class="cal-day"><span class="cal-day-num">28</span></div>
<div class="cal-day"><span class="cal-day-num">29</span></div>
<div class="cal-day"><span class="cal-day-num">30</span></div>
<div class="cal-day cal-day--outside"><span class="cal-day-num">1</span></div>
<div class="cal-day cal-day--outside"><span class="cal-day-num">2</span></div>
<div class="cal-day cal-day--outside cal-day--weekend"><span class="cal-day-num">3</span></div>
<div class="cal-day cal-day--outside cal-day--weekend"><span class="cal-day-num">4</span></div>
</div>
</div>
Rows that grow
.cal-month--grow is the other choice for a whole calendar: a week is as tall as its
last lane, so every event is on screen and there is no .cal-more. Use it where a
busy day must not hide anything — a dispatch plan, a rota — and the fixed rows where the month has
to fit a screen. --cal-lanes is then the height a quiet week keeps. The lanes and the
grid-row of each pill are still yours to work out.
<div class="cal-month cal-month--grow" style="--cal-lanes: 1">
<div class="cal-weekdays">
<span class="cal-weekday">Mon</span>
<span class="cal-weekday">Tue</span>
<span class="cal-weekday">Wed</span>
<span class="cal-weekday">Thu</span>
<span class="cal-weekday">Fri</span>
<span class="cal-weekday">Sat</span>
<span class="cal-weekday">Sun</span>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">14</span></div>
<div class="cal-day"><span class="cal-day-num">15</span></div>
<div class="cal-day" aria-current="date"><span class="cal-day-num">16</span></div>
<div class="cal-day"><span class="cal-day-num">17</span></div>
<div class="cal-day"><span class="cal-day-num">18</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">19</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">20</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--cyan" href="#" style="grid-column: 1 / span 2; grid-row: 1">Peak rehearsal</a>
<a class="cal-event cal-event--info" href="#" style="grid-column: 3; grid-row: 1"><span class="cal-event-time">08:30</span> Stand-up</a>
<a class="cal-event" href="#" style="grid-column: 3; grid-row: 2"><span class="cal-event-time">10:00</span> Interview</a>
<a class="cal-event cal-event--go" href="#" style="grid-column: 3; grid-row: 3"><span class="cal-event-time">14:00</span> Dispatch ORD-4209</a>
<a class="cal-event" href="#" style="grid-column: 3; grid-row: 4"><span class="cal-event-time">15:30</span> Handover</a>
<a class="cal-event cal-event--warn" href="#" style="grid-column: 3 / span 3; grid-row: 5">Racking inspection</a>
<a class="cal-event cal-event--teal" href="#" style="grid-column: 5; grid-row: 1"><span class="cal-event-time">11:00</span> Carrier review</a>
</div>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">21</span></div>
<div class="cal-day"><span class="cal-day-num">22</span></div>
<div class="cal-day"><span class="cal-day-num">23</span></div>
<div class="cal-day"><span class="cal-day-num">24</span></div>
<div class="cal-day"><span class="cal-day-num">25</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">26</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">27</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--danger" href="#" style="grid-column: 4; grid-row: 1">Audit deadline</a>
</div>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">28</span></div>
<div class="cal-day"><span class="cal-day-num">29</span></div>
<div class="cal-day"><span class="cal-day-num">30</span></div>
<div class="cal-day cal-day--outside"><span class="cal-day-num">1</span></div>
<div class="cal-day cal-day--outside"><span class="cal-day-num">2</span></div>
<div class="cal-day cal-day--outside cal-day--weekend"><span class="cal-day-num">3</span></div>
<div class="cal-day cal-day--outside cal-day--weekend"><span class="cal-day-num">4</span></div>
</div>
</div>
A week on a time axis
.cal-week puts a .cal-hours gutter beside one .cal-day-col
per day. --cal-hours is how many hour rows the axis has and
--cal-hour-height how tall each is. A .cal-block's
--cal-start and --cal-span are in hours from the top of the axis, so
09:30 on an axis starting at 07:00 is 2.5; two blocks that overlap go side by side
with --cal-lane and --cal-lanes, which you work out.
.cal-now is the line for the current time, at --cal-at. A block's text
stops at the last whole line that fits, so a short block shows its time and the start of its
title — put the full title in the link's label or a data-tip if it matters.
<div class="cal-week" style="--cal-hours: 11; --cal-hour-height: 40px">
<div class="cal-week-head">
<span></span>
<span class="cal-weekday">Mon 14</span>
<span class="cal-weekday">Tue 15</span>
<span class="cal-weekday" aria-current="date">Wed 16</span>
<span class="cal-weekday">Thu 17</span>
<span class="cal-weekday">Fri 18</span>
<span class="cal-weekday">Sat 19</span>
<span class="cal-weekday">Sun 20</span>
</div>
<div class="cal-week-body sedna-scroll">
<div class="cal-hours">
<span class="cal-hour">07:00</span>
<span class="cal-hour">08:00</span>
<span class="cal-hour">09:00</span>
<span class="cal-hour">10:00</span>
<span class="cal-hour">11:00</span>
<span class="cal-hour">12:00</span>
<span class="cal-hour">13:00</span>
<span class="cal-hour">14:00</span>
<span class="cal-hour">15:00</span>
<span class="cal-hour">16:00</span>
<span class="cal-hour">17:00</span>
</div>
<div class="cal-day-col">
<a class="cal-block" href="#" style="--cal-start: 1; --cal-span: 0.5"><span class="cal-event-time">08:00</span> Stand-up</a>
<a class="cal-block cal-event--teal" href="#" style="--cal-start: 3; --cal-span: 2"><span class="cal-event-time">10:00</span> Carrier review</a>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--warn" href="#" style="--cal-start: 2; --cal-span: 3; --cal-lane: 0; --cal-lanes: 2"><span class="cal-event-time">09:00</span> Dock closed</a>
<a class="cal-block" href="#" style="--cal-start: 2.5; --cal-span: 1; --cal-lane: 1; --cal-lanes: 2"><span class="cal-event-time">09:30</span> Handover</a>
</div>
<div class="cal-day-col" aria-current="date">
<a class="cal-block cal-event--go" href="#" style="--cal-start: 7; --cal-span: 1"><span class="cal-event-time">14:00</span> Dispatch ORD-4209</a>
<div class="cal-now" style="--cal-at: 4.4"></div>
</div>
<div class="cal-day-col"></div>
<div class="cal-day-col">
<a class="cal-block cal-event--danger" href="#" style="--cal-start: 5; --cal-span: 1.5"><span class="cal-event-time">12:00</span> Audit</a>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--orange" href="#" style="--cal-start: 2; --cal-span: 2"><span class="cal-event-time">09:00</span> Weekend picking</a>
</div>
<div class="cal-day-col"></div>
</div>
</div>
Room for busy days
.cal-week--by-lanes shares the width by how busy each day is. CSS cannot tell which
blocks overlap, so you pass the count: the most blocks side by side on that day, as
--cal-lanes on its .cal-day-col and the same number on its
.cal-weekday, which keeps the heading over the column. A day you leave alone counts
as one lane. --cal-day-min is the narrowest any day may get, on this modifier or
without it; the week's own minimum width follows from it, so put the week in a
.sedna-scroll-x, which scrolls it sideways on a phone. Leave the modifier off and
every day is the same width again.
<div class="sedna-scroll-x">
<div class="cal-week cal-week--by-lanes" style="--cal-hours: 7; --cal-hour-height: 40px; --cal-day-min: 72px">
<div class="cal-week-head">
<span></span>
<span class="cal-weekday">Mon 14</span>
<span class="cal-weekday" style="--cal-lanes: 3">Tue 15</span>
<span class="cal-weekday" style="--cal-lanes: 2" aria-current="date">Wed 16</span>
<span class="cal-weekday">Thu 17</span>
<span class="cal-weekday">Fri 18</span>
<span class="cal-weekday">Sat 19</span>
<span class="cal-weekday">Sun 20</span>
</div>
<div class="cal-week-body sedna-scroll">
<div class="cal-hours">
<span class="cal-hour">08:00</span>
<span class="cal-hour">09:00</span>
<span class="cal-hour">10:00</span>
<span class="cal-hour">11:00</span>
<span class="cal-hour">12:00</span>
<span class="cal-hour">13:00</span>
<span class="cal-hour">14:00</span>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--teal" href="#" style="--cal-start: 2; --cal-span: 2"><span class="cal-event-time">10:00</span> Carrier review</a>
</div>
<div class="cal-day-col" style="--cal-lanes: 3">
<a class="cal-block cal-event--warn" href="#" style="--cal-start: 1; --cal-span: 2; --cal-lane: 0; --cal-lanes: 3"><span class="cal-event-time">09:00</span> Dock closed</a>
<a class="cal-block" href="#" style="--cal-start: 1.5; --cal-span: 1; --cal-lane: 1; --cal-lanes: 3"><span class="cal-event-time">09:30</span> Interview</a>
<a class="cal-block cal-event--info" href="#" style="--cal-start: 2; --cal-span: 2; --cal-lane: 2; --cal-lanes: 3"><span class="cal-event-time">10:00</span> Supplier call</a>
<a class="cal-block cal-event--go" href="#" style="--cal-start: 5; --cal-span: 1"><span class="cal-event-time">13:00</span> Dispatch</a>
</div>
<div class="cal-day-col" style="--cal-lanes: 2" aria-current="date">
<a class="cal-block" href="#" style="--cal-start: 0.5; --cal-span: 0.5; --cal-lane: 0; --cal-lanes: 2"><span class="cal-event-time">08:30</span> Stand-up</a>
<a class="cal-block cal-event--orange" href="#" style="--cal-start: 0.5; --cal-span: 3; --cal-lane: 1; --cal-lanes: 2"><span class="cal-event-time">08:30</span> Weekend rota</a>
<div class="cal-now" style="--cal-at: 2.4"></div>
</div>
<div class="cal-day-col"></div>
<div class="cal-day-col">
<a class="cal-block cal-event--danger" href="#" style="--cal-start: 4; --cal-span: 1.5"><span class="cal-event-time">12:00</span> Audit</a>
</div>
<div class="cal-day-col"></div>
<div class="cal-day-col"></div>
</div>
</div>
</div>
A day
The same surface with --cal-days: 1. Nothing else changes, so a week view and a day
view share one set of rules and one layout of blocks.
<div class="cal-week" style="--cal-days: 1; --cal-hours: 6; --cal-hour-height: 44px; max-width: 420px">
<div class="cal-week-head">
<span></span>
<span class="cal-weekday" aria-current="date">Wednesday 16 September</span>
</div>
<div class="cal-week-body">
<div class="cal-hours">
<span class="cal-hour">12:00</span>
<span class="cal-hour">13:00</span>
<span class="cal-hour">14:00</span>
<span class="cal-hour">15:00</span>
<span class="cal-hour">16:00</span>
<span class="cal-hour">17:00</span>
</div>
<div class="cal-day-col" aria-current="date">
<a class="cal-block cal-event--go" href="#" style="--cal-start: 2; --cal-span: 1"><span class="cal-event-time">14:00–15:00</span> Dispatch ORD-4209</a>
<a class="cal-block" href="#" style="--cal-start: 3.5; --cal-span: 1.5"><span class="cal-event-time">15:30–17:00</span> Handover to Priya Nair</a>
<div class="cal-now" style="--cal-at: 1.4"></div>
</div>
</div>
</div>
Event status as a fill
Tinted is the default. .cal-event--filled is for what is settled or happening now,
--outline for what may be skipped, and --tentative — a dashed edge on a
faint hatch — for an invitation nobody has accepted yet. Each combines with every hue,
--go to --teal, on a pill and on a block. Say the status in words
too, as the time line does here: a dashed border is not something a screen reader or a
printout can pass on.
<div class="sedna-col">
<div class="cal-month cal-month--grow" style="--cal-lanes: 1">
<div class="cal-weekdays">
<span class="cal-weekday">Mon</span>
<span class="cal-weekday">Tue</span>
<span class="cal-weekday">Wed</span>
<span class="cal-weekday">Thu</span>
<span class="cal-weekday">Fri</span>
<span class="cal-weekday">Sat</span>
<span class="cal-weekday">Sun</span>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">14</span></div>
<div class="cal-day"><span class="cal-day-num">15</span></div>
<div class="cal-day" aria-current="date"><span class="cal-day-num">16</span></div>
<div class="cal-day"><span class="cal-day-num">17</span></div>
<div class="cal-day"><span class="cal-day-num">18</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">19</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">20</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--filled cal-event--go" href="#" style="grid-column: 1 / span 2; grid-row: 1">Peak rehearsal · confirmed</a>
<a class="cal-event cal-event--teal" href="#" style="grid-column: 3; grid-row: 1"><span class="cal-event-time">11:00</span> Carrier review</a>
<a class="cal-event cal-event--outline cal-event--info" href="#" style="grid-column: 3 / span 2; grid-row: 2">Optional: Q4 read-out</a>
<a class="cal-event cal-event--tentative cal-event--warn" href="#" style="grid-column: 4 / span 2; grid-row: 1"><span class="cal-event-time">Tentative</span> Racking inspection</a>
<a class="cal-event cal-event--filled cal-event--danger" href="#" style="grid-column: 5; grid-row: 2">Audit deadline</a>
</div>
</div>
</div>
<div class="cal-week" style="--cal-days: 5; --cal-hours: 5; --cal-hour-height: 48px">
<div class="cal-week-head">
<span></span>
<span class="cal-weekday">Mon 14</span>
<span class="cal-weekday">Tue 15</span>
<span class="cal-weekday" aria-current="date">Wed 16</span>
<span class="cal-weekday">Thu 17</span>
<span class="cal-weekday">Fri 18</span>
</div>
<div class="cal-week-body">
<div class="cal-hours">
<span class="cal-hour">09:00</span>
<span class="cal-hour">10:00</span>
<span class="cal-hour">11:00</span>
<span class="cal-hour">12:00</span>
<span class="cal-hour">13:00</span>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--filled cal-event--go" href="#" style="--cal-start: 0; --cal-span: 2"><span class="cal-event-time">09:00 · Confirmed</span> Site visit</a>
<a class="cal-block cal-event--teal" href="#" style="--cal-start: 2.5; --cal-span: 1.5"><span class="cal-event-time">11:30</span> Carrier review</a>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--outline" href="#" style="--cal-start: 1; --cal-span: 1"><span class="cal-event-time">10:00 · Optional</span> Q4 read-out</a>
<a class="cal-block cal-event--tentative cal-event--warn" href="#" style="--cal-start: 3; --cal-span: 1.5"><span class="cal-event-time">12:00 · Tentative</span> Supplier call</a>
</div>
<div class="cal-day-col" aria-current="date">
<a class="cal-block cal-event--tentative cal-event--cyan" href="#" style="--cal-start: 0; --cal-span: 1"><span class="cal-event-time">09:00 · Awaiting reply</span> Interview</a>
<a class="cal-block cal-event--filled cal-event--danger" href="#" style="--cal-start: 2; --cal-span: 1.5"><span class="cal-event-time">11:00 · Confirmed</span> Audit</a>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--outline cal-event--orange" href="#" style="--cal-start: 0.5; --cal-span: 1.5"><span class="cal-event-time">09:30 · Optional</span> Rota planning</a>
<a class="cal-block cal-event--filled" href="#" style="--cal-start: 2.5; --cal-span: 1"><span class="cal-event-time">11:30 · Confirmed</span> Handover</a>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--filled cal-event--teal" href="#" style="--cal-start: 1; --cal-span: 1"><span class="cal-event-time">10:00 · Confirmed</span> Carrier call</a>
<a class="cal-block cal-event--tentative" href="#" style="--cal-start: 3; --cal-span: 1"><span class="cal-event-time">12:00 · Tentative</span> Lunch and learn</a>
</div>
</div>
</div>
</div>
Type, state and people
Before the title, a .cal-event-icon for the kind of event, a .cal-event-dot,
or .cal-event-dot--live for one in progress; all three take the event's hue. After it,
the people on it, as an avatar group with .cal-event-avatars,
which shrinks the avatars to the pill. On a pill, wrap the title in .cal-event-title
so it truncates instead of pushing the avatars out; on a block the avatars sit in the top corner
and the text wraps round them. The avatars and the dot are pictures: put the names and the word
"Live" in text as well.
<div class="sedna-col">
<div class="cal-month cal-month--grow" style="--cal-lanes: 1">
<div class="cal-weekdays">
<span class="cal-weekday">Mon</span>
<span class="cal-weekday">Tue</span>
<span class="cal-weekday">Wed</span>
<span class="cal-weekday">Thu</span>
<span class="cal-weekday">Fri</span>
<span class="cal-weekday">Sat</span>
<span class="cal-weekday">Sun</span>
</div>
<div class="cal-row">
<div class="cal-day"><span class="cal-day-num">14</span></div>
<div class="cal-day"><span class="cal-day-num">15</span></div>
<div class="cal-day" aria-current="date"><span class="cal-day-num">16</span></div>
<div class="cal-day"><span class="cal-day-num">17</span></div>
<div class="cal-day"><span class="cal-day-num">18</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">19</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">20</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--teal" href="#" style="grid-column: 1 / span 2; grid-row: 1">
<i class="ri-truck-line cal-event-icon" aria-hidden="true"></i>
<span class="cal-event-title">Carrier onboarding</span>
<span class="avatar-group cal-event-avatars" aria-hidden="true">
<span class="avatar avatar--viz-2">AF</span>
<span class="avatar avatar--viz-3">PN</span>
</span>
<span class="visually-hidden">with Alex Fischer and Priya Nair</span>
</a>
<a class="cal-event cal-event--filled cal-event--go" href="#" style="grid-column: 3; grid-row: 1">
<span class="cal-event-dot cal-event-dot--live"></span>
<span class="visually-hidden">Live:</span>
<span class="cal-event-time">14:00</span>
<span class="cal-event-title">Dispatch ORD-4209</span>
</a>
<a class="cal-event cal-event--info" href="#" style="grid-column: 4; grid-row: 1">
<span class="cal-event-dot"></span>
<span class="cal-event-time">09:00</span>
<span class="cal-event-title">Interview</span>
<span class="avatar-group cal-event-avatars" aria-hidden="true">
<span class="avatar avatar--viz-4">JW</span>
</span>
<span class="visually-hidden">with Jordan Weiss</span>
</a>
<a class="cal-event cal-event--tentative cal-event--warn" href="#" style="grid-column: 5 / span 2; grid-row: 1">
<i class="ri-tools-line cal-event-icon" aria-hidden="true"></i>
<span class="cal-event-title">Tentative: racking inspection</span>
<span class="avatar-group cal-event-avatars" aria-hidden="true">
<span class="avatar avatar--viz-2">AF</span>
<span class="avatar avatar--viz-4">JW</span>
<span class="avatar-more">+2</span>
</span>
<span class="visually-hidden">with Alex Fischer, Jordan Weiss and two more</span>
</a>
</div>
</div>
</div>
<div class="cal-week" style="--cal-days: 2; --cal-hours: 4; --cal-hour-height: 52px">
<div class="cal-week-head">
<span></span>
<span class="cal-weekday" aria-current="date">Wed 16</span>
<span class="cal-weekday">Thu 17</span>
</div>
<div class="cal-week-body">
<div class="cal-hours">
<span class="cal-hour">13:00</span>
<span class="cal-hour">14:00</span>
<span class="cal-hour">15:00</span>
<span class="cal-hour">16:00</span>
</div>
<div class="cal-day-col" aria-current="date">
<a class="cal-block cal-event--filled cal-event--go" href="#" style="--cal-start: 1; --cal-span: 1.5">
<span class="avatar-group cal-event-avatars" aria-hidden="true">
<span class="avatar avatar--viz-3">PN</span>
<span class="avatar avatar--viz-4">JW</span>
</span>
<span class="cal-event-time"><span class="cal-event-dot cal-event-dot--live"></span>Live · 14:00–15:30</span>
<span class="cal-event-title">Dispatch ORD-4209</span>
<span class="visually-hidden">with Priya Nair and Jordan Weiss</span>
</a>
<div class="cal-now" style="--cal-at: 1.4"></div>
</div>
<div class="cal-day-col">
<a class="cal-block cal-event--cyan" href="#" style="--cal-start: 0.5; --cal-span: 2">
<span class="avatar-group cal-event-avatars" aria-hidden="true">
<span class="avatar avatar--viz-2">AF</span>
</span>
<span class="cal-event-time">13:30–15:30</span>
<i class="ri-user-voice-line cal-event-icon" aria-hidden="true"></i><span class="cal-event-title">Customer workshop</span>
<span class="visually-hidden">with Alex Fischer</span>
</a>
</div>
</div>
</div>
</div>
Holidays
.cal-day--holiday shades a day nobody works, and a .cal-holiday beside the
day number names it with an icon. A name that does not fit is cut short, and in a very narrow cell
it drops out and the icon stays; either way its data-tip carries the name — the hint
only appears while the whole name is not on screen, so it never repeats it. The
aria-label is what a screen reader hears. A small month has no room for the marker:
shade the day and put the data-tip on it.
October 2026
<div class="sedna-col">
<div class="cal-month" style="--cal-lanes: 1">
<div class="cal-weekdays">
<span class="cal-weekday">Mon</span>
<span class="cal-weekday">Tue</span>
<span class="cal-weekday">Wed</span>
<span class="cal-weekday">Thu</span>
<span class="cal-weekday">Fri</span>
<span class="cal-weekday">Sat</span>
<span class="cal-weekday">Sun</span>
</div>
<div class="cal-row">
<div class="cal-day cal-day--holiday">
<span class="cal-day-num">5</span>
<span class="cal-holiday" role="img" aria-label="Holiday: Autumn bank holiday" data-tip="Autumn bank holiday">
<i class="ri-flag-2-line"></i><span class="cal-holiday-name">Autumn bank holiday</span>
</span>
</div>
<div class="cal-day"><span class="cal-day-num">6</span></div>
<div class="cal-day"><span class="cal-day-num">7</span></div>
<div class="cal-day"><span class="cal-day-num">8</span></div>
<div class="cal-day cal-day--holiday">
<span class="cal-day-num">9</span>
<span class="cal-holiday" role="img" aria-label="Holiday: Harvest festival" data-tip="Harvest festival">
<i class="ri-plant-line"></i><span class="cal-holiday-name">Harvest festival</span>
</span>
</div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">10</span></div>
<div class="cal-day cal-day--weekend"><span class="cal-day-num">11</span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--outline cal-event--warn" href="#" style="grid-column: 2 / span 3; grid-row: 1">Short week: dispatch cut-off moved</a>
</div>
</div>
</div>
<section class="cal-year-month" style="max-width: 260px">
<h3 class="cal-year-title">October 2026</h3>
<div class="cal-month cal-month--mini">
<div class="cal-weekdays" aria-hidden="true">
<span class="cal-weekday">Mo</span>
<span class="cal-weekday">Tu</span>
<span class="cal-weekday">We</span>
<span class="cal-weekday">Th</span>
<span class="cal-weekday">Fr</span>
<span class="cal-weekday">Sa</span>
<span class="cal-weekday">Su</span>
</div>
<div class="cal-row">
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day">1</span>
<span class="cal-day">2</span>
<span class="cal-day">3</span>
<span class="cal-day">4</span>
</div>
<div class="cal-row">
<span class="cal-day cal-day--holiday" data-tip="Autumn bank holiday">5</span>
<span class="cal-day">6</span>
<span class="cal-day">7</span>
<span class="cal-day">8</span>
<span class="cal-day cal-day--holiday" data-tip="Harvest festival">9</span>
<span class="cal-day">10</span>
<span class="cal-day">11</span>
</div>
<div class="cal-row">
<span class="cal-day">12</span>
<span class="cal-day">13</span>
<span class="cal-day">14</span>
<span class="cal-day">15</span>
<span class="cal-day">16</span>
<span class="cal-day">17</span>
<span class="cal-day">18</span>
</div>
<div class="cal-row">
<span class="cal-day">19</span>
<span class="cal-day">20</span>
<span class="cal-day">21</span>
<span class="cal-day">22</span>
<span class="cal-day">23</span>
<span class="cal-day">24</span>
<span class="cal-day">25</span>
</div>
<div class="cal-row">
<span class="cal-day">26</span>
<span class="cal-day">27</span>
<span class="cal-day">28</span>
<span class="cal-day">29</span>
<span class="cal-day">30</span>
<span class="cal-day">31</span>
<span class="cal-day cal-day--outside"></span>
</div>
</div>
</section>
</div>
A year
.cal-year lays out small months — .cal-month--mini, numbers only and
square days — and wraps them to the width, so twelve fill a page and three fit a panel. A day
with something on it is .cal-day--marked. Hide the weekday initials from a screen
reader with aria-hidden, as here: the date is in the day's own label or context.
September 2026
October 2026
November 2026
<div class="cal-year">
<section class="cal-year-month">
<h3 class="cal-year-title">September 2026</h3>
<div class="cal-month cal-month--mini">
<div class="cal-weekdays" aria-hidden="true">
<span class="cal-weekday">Mo</span>
<span class="cal-weekday">Tu</span>
<span class="cal-weekday">We</span>
<span class="cal-weekday">Th</span>
<span class="cal-weekday">Fr</span>
<span class="cal-weekday">Sa</span>
<span class="cal-weekday">Su</span>
</div>
<div class="cal-row">
<span class="cal-day cal-day--outside"></span>
<span class="cal-day">1</span>
<span class="cal-day">2</span>
<span class="cal-day">3</span>
<span class="cal-day cal-day--marked">4</span>
<span class="cal-day cal-day--marked">5</span>
<span class="cal-day">6</span>
</div>
<div class="cal-row">
<span class="cal-day">7</span>
<span class="cal-day cal-day--marked">8</span>
<span class="cal-day cal-day--marked">9</span>
<span class="cal-day">10</span>
<span class="cal-day">11</span>
<span class="cal-day">12</span>
<span class="cal-day">13</span>
</div>
<div class="cal-row">
<span class="cal-day">14</span>
<span class="cal-day">15</span>
<span class="cal-day cal-day--marked" aria-current="date">16</span>
<span class="cal-day">17</span>
<span class="cal-day">18</span>
<span class="cal-day">19</span>
<span class="cal-day">20</span>
</div>
<div class="cal-row">
<span class="cal-day">21</span>
<span class="cal-day">22</span>
<span class="cal-day">23</span>
<span class="cal-day cal-day--marked">24</span>
<span class="cal-day">25</span>
<span class="cal-day">26</span>
<span class="cal-day">27</span>
</div>
<div class="cal-row">
<span class="cal-day">28</span>
<span class="cal-day">29</span>
<span class="cal-day">30</span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
</div>
</div>
</section>
<section class="cal-year-month">
<h3 class="cal-year-title">October 2026</h3>
<div class="cal-month cal-month--mini">
<div class="cal-weekdays" aria-hidden="true">
<span class="cal-weekday">Mo</span>
<span class="cal-weekday">Tu</span>
<span class="cal-weekday">We</span>
<span class="cal-weekday">Th</span>
<span class="cal-weekday">Fr</span>
<span class="cal-weekday">Sa</span>
<span class="cal-weekday">Su</span>
</div>
<div class="cal-row">
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--marked">1</span>
<span class="cal-day">2</span>
<span class="cal-day">3</span>
<span class="cal-day">4</span>
</div>
<div class="cal-row">
<span class="cal-day">5</span>
<span class="cal-day">6</span>
<span class="cal-day">7</span>
<span class="cal-day">8</span>
<span class="cal-day">9</span>
<span class="cal-day">10</span>
<span class="cal-day">11</span>
</div>
<div class="cal-row">
<span class="cal-day cal-day--marked">12</span>
<span class="cal-day cal-day--marked">13</span>
<span class="cal-day">14</span>
<span class="cal-day">15</span>
<span class="cal-day">16</span>
<span class="cal-day">17</span>
<span class="cal-day">18</span>
</div>
<div class="cal-row">
<span class="cal-day">19</span>
<span class="cal-day">20</span>
<span class="cal-day">21</span>
<span class="cal-day">22</span>
<span class="cal-day">23</span>
<span class="cal-day">24</span>
<span class="cal-day">25</span>
</div>
<div class="cal-row">
<span class="cal-day">26</span>
<span class="cal-day">27</span>
<span class="cal-day">28</span>
<span class="cal-day">29</span>
<span class="cal-day cal-day--marked">30</span>
<span class="cal-day">31</span>
<span class="cal-day cal-day--outside"></span>
</div>
</div>
</section>
<section class="cal-year-month">
<h3 class="cal-year-title">November 2026</h3>
<div class="cal-month cal-month--mini">
<div class="cal-weekdays" aria-hidden="true">
<span class="cal-weekday">Mo</span>
<span class="cal-weekday">Tu</span>
<span class="cal-weekday">We</span>
<span class="cal-weekday">Th</span>
<span class="cal-weekday">Fr</span>
<span class="cal-weekday">Sa</span>
<span class="cal-weekday">Su</span>
</div>
<div class="cal-row">
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day">1</span>
</div>
<div class="cal-row">
<span class="cal-day">2</span>
<span class="cal-day">3</span>
<span class="cal-day">4</span>
<span class="cal-day">5</span>
<span class="cal-day cal-day--marked">6</span>
<span class="cal-day">7</span>
<span class="cal-day">8</span>
</div>
<div class="cal-row">
<span class="cal-day">9</span>
<span class="cal-day">10</span>
<span class="cal-day">11</span>
<span class="cal-day">12</span>
<span class="cal-day">13</span>
<span class="cal-day">14</span>
<span class="cal-day">15</span>
</div>
<div class="cal-row">
<span class="cal-day">16</span>
<span class="cal-day">17</span>
<span class="cal-day">18</span>
<span class="cal-day cal-day--marked">19</span>
<span class="cal-day">20</span>
<span class="cal-day">21</span>
<span class="cal-day">22</span>
</div>
<div class="cal-row">
<span class="cal-day">23</span>
<span class="cal-day">24</span>
<span class="cal-day">25</span>
<span class="cal-day">26</span>
<span class="cal-day">27</span>
<span class="cal-day">28</span>
<span class="cal-day">29</span>
</div>
<div class="cal-row">
<span class="cal-day">30</span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
</div>
</div>
</section>
</div>
A resource timeline
One row per room, vehicle or person, with the days or hours across. .cal-timeline
takes the column count as --cal-columns — yours to write, because a stylesheet
cannot count days — and each .cal-timeline-row is a
.cal-timeline-label, pinned while the columns scroll, a
.cal-timeline-cells behind it for the shading, and the month's own
.cal-row-events: the same pills, lanes, .cal-more, fills and
--continues-*. A column that is a day takes the day's modifiers, and
.cal-now is a vertical rule at --cal-at columns. Keep it in a
.sedna-scroll-x: a column never gets narrower than --cal-column-min.
<div class="sedna-scroll-x">
<div class="cal-timeline" style="--cal-columns: 14; --cal-lanes: 2">
<div class="cal-timeline-head">
<span class="cal-timeline-corner">Resource</span>
<span class="cal-timeline-col">Mo 14</span>
<span class="cal-timeline-col">Tu 15</span>
<span class="cal-timeline-col" aria-current="date">We 16</span>
<span class="cal-timeline-col">Th 17</span>
<span class="cal-timeline-col">Fr 18</span>
<span class="cal-timeline-col cal-day--weekend">Sa 19</span>
<span class="cal-timeline-col cal-day--weekend">Su 20</span>
<span class="cal-timeline-col">Mo 21</span>
<span class="cal-timeline-col">Tu 22</span>
<span class="cal-timeline-col">We 23</span>
<span class="cal-timeline-col">Th 24</span>
<span class="cal-timeline-col">Fr 25</span>
<span class="cal-timeline-col cal-day--weekend">Sa 26</span>
<span class="cal-timeline-col cal-day--weekend">Su 27</span>
</div>
<div class="cal-timeline-row">
<span class="cal-timeline-label"><span>Dock 1</span></span>
<div class="cal-timeline-cells" aria-hidden="true"><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--go" href="#" style="grid-column: 2 / span 3; grid-row: 1">Inbound, Northwind Retail</a>
<a class="cal-event cal-event--warn" href="#" style="grid-column: 6 / span 1; grid-row: 2">Racking inspection</a>
<a class="cal-event" href="#" style="grid-column: 9 / span 2; grid-row: 1">Stocktake</a>
</div>
</div>
<div class="cal-timeline-row">
<span class="cal-timeline-label"><span>Dock 2</span></span>
<div class="cal-timeline-cells" aria-hidden="true"><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--info" href="#" style="grid-column: 1 / span 5; grid-row: 1">Outbound, weekly carrier</a>
<a class="cal-event cal-event--filled cal-event--danger" href="#" style="grid-column: 3 / span 2; grid-row: 2">Door repair</a>
<button class="cal-more" type="button" style="grid-column: 4">+1 more</button>
</div>
</div>
<div class="cal-timeline-row">
<span class="cal-timeline-label"><span>Dock 3</span></span>
<div class="cal-timeline-cells" aria-hidden="true"><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--tentative cal-event--cyan" href="#" style="grid-column: 8 / span 4; grid-row: 1">Returns batch, tentative</a>
<a class="cal-event cal-event--continues-end cal-event--orange" href="#" style="grid-column: 12 / span 3; grid-row: 1">Seasonal overflow</a>
</div>
</div>
<div class="cal-timeline-row">
<span class="cal-timeline-label"><span>Yard</span></span>
<div class="cal-timeline-cells" aria-hidden="true"><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--teal" href="#" style="grid-column: 4 / span 1; grid-row: 1">Trailer swap</a>
<a class="cal-event cal-event--teal" href="#" style="grid-column: 11 / span 1; grid-row: 1">Trailer swap</a>
</div>
</div>
<div class="cal-now" style="--cal-at: 2.6" role="img" aria-label="Now, Wednesday 16 September, 14:24"></div>
</div>
</div>
A year as a timeline
The same layout with months down the side and days across: a column past the end of a short
month is .cal-day--outside, and a holiday is .cal-day--holiday.
.cal-timeline--grow grows each row to fit its lanes instead of summarising the rest,
as .cal-month--grow does, so --cal-lanes becomes the height an empty row
still keeps. --cal-label-width and --cal-column-min fit the label and
the day numbers.
<div class="sedna-scroll-x">
<div class="cal-timeline cal-timeline--grow" style="--cal-columns: 31; --cal-lanes: 1; --cal-label-width: 104px; --cal-column-min: 28px">
<div class="cal-timeline-head">
<span class="cal-timeline-corner">2026</span>
<span class="cal-timeline-col">1</span>
<span class="cal-timeline-col">2</span>
<span class="cal-timeline-col">3</span>
<span class="cal-timeline-col">4</span>
<span class="cal-timeline-col">5</span>
<span class="cal-timeline-col">6</span>
<span class="cal-timeline-col">7</span>
<span class="cal-timeline-col">8</span>
<span class="cal-timeline-col">9</span>
<span class="cal-timeline-col">10</span>
<span class="cal-timeline-col">11</span>
<span class="cal-timeline-col">12</span>
<span class="cal-timeline-col">13</span>
<span class="cal-timeline-col">14</span>
<span class="cal-timeline-col">15</span>
<span class="cal-timeline-col">16</span>
<span class="cal-timeline-col">17</span>
<span class="cal-timeline-col">18</span>
<span class="cal-timeline-col">19</span>
<span class="cal-timeline-col">20</span>
<span class="cal-timeline-col">21</span>
<span class="cal-timeline-col">22</span>
<span class="cal-timeline-col">23</span>
<span class="cal-timeline-col">24</span>
<span class="cal-timeline-col">25</span>
<span class="cal-timeline-col">26</span>
<span class="cal-timeline-col">27</span>
<span class="cal-timeline-col">28</span>
<span class="cal-timeline-col">29</span>
<span class="cal-timeline-col">30</span>
<span class="cal-timeline-col">31</span>
</div>
<div class="cal-timeline-row">
<span class="cal-timeline-label"><span>October</span></span>
<div class="cal-timeline-cells" aria-hidden="true"><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--info" href="#" style="grid-column: 1 / span 2; grid-row: 1">Q4 planning</a>
<a class="cal-event cal-event--warn" href="#" style="grid-column: 12 / span 12; grid-row: 1">Peak preparation</a>
<a class="cal-event" href="#" style="grid-column: 20 / span 3; grid-row: 2">Audit</a>
</div>
</div>
<div class="cal-timeline-row">
<span class="cal-timeline-label"><span>November</span></span>
<div class="cal-timeline-cells" aria-hidden="true"><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span class="cal-day--outside"></span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--orange" href="#" style="grid-column: 2 / span 26; grid-row: 1">Peak season</a>
<a class="cal-event cal-event--danger" href="#" style="grid-column: 27 / span 2; grid-row: 2">Sale weekend</a>
<a class="cal-event cal-event--go" href="#" style="grid-column: 9 / span 2; grid-row: 2">Carrier review</a>
<a class="cal-event" href="#" style="grid-column: 16 / span 2; grid-row: 3">Stocktake</a>
</div>
</div>
<div class="cal-timeline-row">
<span class="cal-timeline-label"><span>December</span></span>
<div class="cal-timeline-cells" aria-hidden="true"><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span></span><span class="cal-day--weekend"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span><span class="cal-day--holiday"></span><span class="cal-day--holiday"></span><span class="cal-day--weekend"></span><span></span><span></span><span></span><span></span></div>
<div class="cal-row-events">
<a class="cal-event cal-event--continues-end cal-event--teal" href="#" style="grid-column: 21 / span 11; grid-row: 1">Holiday rota</a>
<a class="cal-event cal-event--filled" href="#" style="grid-column: 28 / span 4; grid-row: 2">Year-end close</a>
</div>
</div>
</div>
</div>
A date picker
Live — open it. For a date that has to look the same in every engine, or a range,
or days that cannot be chosen. .datepicker is a popover, so
the anchoring, light dismiss and Escape are the platform's; its body is the small
month grid. Each day is a <button> named with its full date —
“21” alone is not a date to a screen reader — and aria-pressed="true"
when chosen. A range is --range-start, --in-range and
--range-end on the days you work out; an unavailable day is a disabled button.
The keys beyond Tab are yours to add. The week numbers are
.cal-month--weeknums, as on a full month. For a single date the native
date input is still the better default.
<button class="form-input" type="button" popovertarget="shutdown-range" style="max-width:260px; text-align:start">
<i class="ri-calendar-line"></i> 21–25 Sep 2026
</button>
<div class="popover datepicker" id="shutdown-range" popover>
<div class="datepicker-head">
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Previous month"><i class="ri-arrow-left-s-line"></i></button>
<span class="datepicker-title">September 2026</span>
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Next month"><i class="ri-arrow-right-s-line"></i></button>
</div>
<div class="cal-month cal-month--mini cal-month--weeknums">
<div class="cal-weekdays" aria-hidden="true">
<span class="cal-weekday">Wk</span>
<span class="cal-weekday">Mo</span>
<span class="cal-weekday">Tu</span>
<span class="cal-weekday">We</span>
<span class="cal-weekday">Th</span>
<span class="cal-weekday">Fr</span>
<span class="cal-weekday">Sa</span>
<span class="cal-weekday">Su</span>
</div>
<div class="cal-row">
<span class="cal-weeknum"><span class="visually-hidden">Week </span>36</span>
<span class="cal-day cal-day--outside"></span>
<button class="cal-day" type="button" aria-label="Tuesday 1 September 2026" aria-pressed="false" disabled>1</button>
<button class="cal-day" type="button" aria-label="Wednesday 2 September 2026" aria-pressed="false" disabled>2</button>
<button class="cal-day" type="button" aria-label="Thursday 3 September 2026" aria-pressed="false" disabled>3</button>
<button class="cal-day" type="button" aria-label="Friday 4 September 2026" aria-pressed="false">4</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 5 September 2026" aria-pressed="false">5</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 6 September 2026" aria-pressed="false">6</button>
</div>
<div class="cal-row">
<span class="cal-weeknum"><span class="visually-hidden">Week </span>37</span>
<button class="cal-day" type="button" aria-label="Monday 7 September 2026" aria-pressed="false">7</button>
<button class="cal-day" type="button" aria-label="Tuesday 8 September 2026" aria-pressed="false">8</button>
<button class="cal-day" type="button" aria-label="Wednesday 9 September 2026" aria-pressed="false">9</button>
<button class="cal-day" type="button" aria-label="Thursday 10 September 2026" aria-pressed="false">10</button>
<button class="cal-day" type="button" aria-label="Friday 11 September 2026" aria-pressed="false">11</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 12 September 2026" aria-pressed="false">12</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 13 September 2026" aria-pressed="false">13</button>
</div>
<div class="cal-row">
<span class="cal-weeknum"><span class="visually-hidden">Week </span>38</span>
<button class="cal-day" type="button" aria-label="Monday 14 September 2026" aria-pressed="false">14</button>
<button class="cal-day" type="button" aria-label="Tuesday 15 September 2026" aria-pressed="false">15</button>
<button class="cal-day" type="button" aria-label="Wednesday 16 September 2026" aria-pressed="false" aria-current="date">16</button>
<button class="cal-day" type="button" aria-label="Thursday 17 September 2026" aria-pressed="false">17</button>
<button class="cal-day" type="button" aria-label="Friday 18 September 2026" aria-pressed="false">18</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 19 September 2026" aria-pressed="false">19</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 20 September 2026" aria-pressed="false">20</button>
</div>
<div class="cal-row">
<span class="cal-weeknum"><span class="visually-hidden">Week </span>39</span>
<button class="cal-day cal-day--range-start" type="button" aria-label="Monday 21 September 2026" aria-pressed="true">21</button>
<button class="cal-day cal-day--in-range" type="button" aria-label="Tuesday 22 September 2026" aria-pressed="true">22</button>
<button class="cal-day cal-day--in-range" type="button" aria-label="Wednesday 23 September 2026" aria-pressed="true">23</button>
<button class="cal-day cal-day--in-range" type="button" aria-label="Thursday 24 September 2026" aria-pressed="true">24</button>
<button class="cal-day cal-day--range-end" type="button" aria-label="Friday 25 September 2026" aria-pressed="true">25</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 26 September 2026" aria-pressed="false">26</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 27 September 2026" aria-pressed="false">27</button>
</div>
<div class="cal-row">
<span class="cal-weeknum"><span class="visually-hidden">Week </span>40</span>
<button class="cal-day" type="button" aria-label="Monday 28 September 2026" aria-pressed="false">28</button>
<button class="cal-day" type="button" aria-label="Tuesday 29 September 2026" aria-pressed="false">29</button>
<button class="cal-day" type="button" aria-label="Wednesday 30 September 2026" aria-pressed="false">30</button>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
</div>
</div>
<div class="datepicker-actions">
<button class="btn btn-sm btn-ghost" type="button">Today</button>
<button class="btn btn-sm" type="button" popovertarget="shutdown-range" popovertargetaction="hide">Cancel</button>
<button class="btn btn-sm btn-primary" type="button" popovertarget="shutdown-range" popovertargetaction="hide">Apply</button>
</div>
</div>
A date with a time
Live — open it. A .datepicker-time row under the grid holds the
platform's own <input type="time"> as a .form-input-sm — one
for a moment, two for a slot — so the clock, the 12- or 24-hour form and the keys are the
reader's. Give each input a label, even when the second one is only read by a screen reader.
The actions under it share its divider.
<button class="form-input" type="button" popovertarget="carrier-slot" style="max-width:260px; text-align:start">
<i class="ri-calendar-schedule-line"></i> Thu 24 Sep 2026, 14:00
</button>
<div class="popover datepicker" id="carrier-slot" popover>
<div class="datepicker-head">
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Previous month"><i class="ri-arrow-left-s-line"></i></button>
<button class="datepicker-title" type="button" aria-label="September 2026, choose a month">September 2026 <i class="ri-arrow-down-s-line"></i></button>
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Next month"><i class="ri-arrow-right-s-line"></i></button>
</div>
<div class="cal-month cal-month--mini">
<div class="cal-weekdays" aria-hidden="true">
<span class="cal-weekday">Mo</span>
<span class="cal-weekday">Tu</span>
<span class="cal-weekday">We</span>
<span class="cal-weekday">Th</span>
<span class="cal-weekday">Fr</span>
<span class="cal-weekday">Sa</span>
<span class="cal-weekday">Su</span>
</div>
<div class="cal-row">
<span class="cal-day cal-day--outside"></span>
<button class="cal-day" type="button" aria-label="Tuesday 1 September 2026" aria-pressed="false">1</button>
<button class="cal-day" type="button" aria-label="Wednesday 2 September 2026" aria-pressed="false">2</button>
<button class="cal-day" type="button" aria-label="Thursday 3 September 2026" aria-pressed="false">3</button>
<button class="cal-day" type="button" aria-label="Friday 4 September 2026" aria-pressed="false">4</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 5 September 2026" aria-pressed="false">5</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 6 September 2026" aria-pressed="false">6</button>
</div>
<div class="cal-row">
<button class="cal-day" type="button" aria-label="Monday 7 September 2026" aria-pressed="false">7</button>
<button class="cal-day" type="button" aria-label="Tuesday 8 September 2026" aria-pressed="false">8</button>
<button class="cal-day" type="button" aria-label="Wednesday 9 September 2026" aria-pressed="false">9</button>
<button class="cal-day" type="button" aria-label="Thursday 10 September 2026" aria-pressed="false">10</button>
<button class="cal-day" type="button" aria-label="Friday 11 September 2026" aria-pressed="false">11</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 12 September 2026" aria-pressed="false">12</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 13 September 2026" aria-pressed="false">13</button>
</div>
<div class="cal-row">
<button class="cal-day" type="button" aria-label="Monday 14 September 2026" aria-pressed="false">14</button>
<button class="cal-day" type="button" aria-label="Tuesday 15 September 2026" aria-pressed="false">15</button>
<button class="cal-day" type="button" aria-label="Wednesday 16 September 2026" aria-pressed="false" aria-current="date">16</button>
<button class="cal-day" type="button" aria-label="Thursday 17 September 2026" aria-pressed="false">17</button>
<button class="cal-day" type="button" aria-label="Friday 18 September 2026" aria-pressed="false">18</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 19 September 2026" aria-pressed="false">19</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 20 September 2026" aria-pressed="false">20</button>
</div>
<div class="cal-row">
<button class="cal-day" type="button" aria-label="Monday 21 September 2026" aria-pressed="false">21</button>
<button class="cal-day" type="button" aria-label="Tuesday 22 September 2026" aria-pressed="false">22</button>
<button class="cal-day" type="button" aria-label="Wednesday 23 September 2026" aria-pressed="false">23</button>
<button class="cal-day" type="button" aria-label="Thursday 24 September 2026" aria-pressed="true">24</button>
<button class="cal-day" type="button" aria-label="Friday 25 September 2026" aria-pressed="false">25</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Saturday 26 September 2026" aria-pressed="false">26</button>
<button class="cal-day cal-day--weekend" type="button" aria-label="Sunday 27 September 2026" aria-pressed="false">27</button>
</div>
<div class="cal-row">
<button class="cal-day" type="button" aria-label="Monday 28 September 2026" aria-pressed="false">28</button>
<button class="cal-day" type="button" aria-label="Tuesday 29 September 2026" aria-pressed="false">29</button>
<button class="cal-day" type="button" aria-label="Wednesday 30 September 2026" aria-pressed="false">30</button>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
<span class="cal-day cal-day--outside"></span>
</div>
</div>
<div class="datepicker-time">
<label for="carrier-slot-from">From</label>
<input class="form-input form-input-sm" id="carrier-slot-from" type="time" value="14:00" />
<span aria-hidden="true">–</span>
<label class="visually-hidden" for="carrier-slot-to">Until</label>
<input class="form-input form-input-sm" id="carrier-slot-to" type="time" value="15:30" />
</div>
<div class="datepicker-actions">
<button class="btn btn-ghost btn-sm" type="button">Clear</button>
<button class="btn btn-sm" type="button" popovertarget="carrier-slot" popovertargetaction="hide">Cancel</button>
<button class="btn btn-primary btn-sm" type="button" popovertarget="carrier-slot" popovertargetaction="hide">Set slot</button>
</div>
</div>
Months and years
Live — open both. To go further than a month at a time, make the
.datepicker-title a <button> that swaps the panel's body for the
level above: .cal-months, then .cal-years, each a grid of
.cal-pick buttons with the day grid's states — aria-pressed="true"
chosen, aria-current now, disabled out of bounds, and
.cal-pick--outside for the years of the neighbouring decade. Which level shows and
what a pick does are yours; name the title button with what it opens.
<div class="sedna-row-wrap sedna-gap-3">
<button class="form-input" type="button" popovertarget="audit-month" style="max-width:200px; text-align:start">
<i class="ri-calendar-line"></i> September 2026
</button>
<button class="form-input" type="button" popovertarget="audit-year" style="max-width:200px; text-align:start">
<i class="ri-calendar-line"></i> 2026
</button>
</div>
<div class="popover datepicker" id="audit-month" popover>
<div class="datepicker-head">
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Previous year"><i class="ri-arrow-left-s-line"></i></button>
<button class="datepicker-title" type="button" aria-label="2026, choose a year">2026 <i class="ri-arrow-down-s-line"></i></button>
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Next year"><i class="ri-arrow-right-s-line"></i></button>
</div>
<div class="cal-months">
<button class="cal-pick" type="button" aria-label="January 2026" aria-pressed="false" disabled>Jan</button>
<button class="cal-pick" type="button" aria-label="February 2026" aria-pressed="false" disabled>Feb</button>
<button class="cal-pick" type="button" aria-label="March 2026" aria-pressed="false" disabled>Mar</button>
<button class="cal-pick" type="button" aria-label="April 2026" aria-pressed="false" disabled>Apr</button>
<button class="cal-pick" type="button" aria-label="May 2026" aria-pressed="false" disabled>May</button>
<button class="cal-pick" type="button" aria-label="June 2026" aria-pressed="false" disabled>Jun</button>
<button class="cal-pick" type="button" aria-label="July 2026" aria-pressed="false">Jul</button>
<button class="cal-pick" type="button" aria-label="August 2026" aria-pressed="false">Aug</button>
<button class="cal-pick" type="button" aria-label="September 2026" aria-pressed="true" aria-current="date">Sep</button>
<button class="cal-pick" type="button" aria-label="October 2026" aria-pressed="false">Oct</button>
<button class="cal-pick" type="button" aria-label="November 2026" aria-pressed="false">Nov</button>
<button class="cal-pick" type="button" aria-label="December 2026" aria-pressed="false">Dec</button>
</div>
</div>
<div class="popover datepicker" id="audit-year" popover>
<div class="datepicker-head">
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Previous decade"><i class="ri-arrow-left-s-line"></i></button>
<span class="datepicker-title">2020–2029</span>
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-label="Next decade"><i class="ri-arrow-right-s-line"></i></button>
</div>
<div class="cal-years">
<button class="cal-pick cal-pick--outside" type="button" aria-pressed="false" disabled>2019</button>
<button class="cal-pick" type="button" aria-pressed="false" disabled>2020</button>
<button class="cal-pick" type="button" aria-pressed="false" disabled>2021</button>
<button class="cal-pick" type="button" aria-pressed="false" disabled>2022</button>
<button class="cal-pick" type="button" aria-pressed="false" disabled>2023</button>
<button class="cal-pick" type="button" aria-pressed="false">2024</button>
<button class="cal-pick" type="button" aria-pressed="false">2025</button>
<button class="cal-pick" type="button" aria-pressed="true" aria-current="date">2026</button>
<button class="cal-pick" type="button" aria-pressed="false">2027</button>
<button class="cal-pick" type="button" aria-pressed="false">2028</button>
<button class="cal-pick" type="button" aria-pressed="false">2029</button>
<button class="cal-pick cal-pick--outside" type="button" aria-pressed="false">2030</button>
</div>
</div>