Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.17.0 · main

Calendar

tier 2 — classes

The 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.

Positions are yours, as custom properties A pill says which days it covers with 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.

Mon Tue Wed Thu Fri Sat Sun
31
1
2
3
4
5
6
21
22
23
24
25
26
27
28
29
30
1
2
3
4
<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.

Week Mon Tue Wed Thu Fri Sat Sun
31
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
2
3
4
<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.

Mon Tue Wed Thu Fri Sat Sun
21
22
23
24
25
26
27
28
29
30
1
2
3
4
<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.

Mon 14 Tue 15 Wed 16 Thu 17 Fri 18 Sat 19 Sun 20
<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.

Mon 14 Tue 15 Wed 16 Thu 17 Fri 18 Sat 19 Sun 20
<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.

Wednesday 16 September
<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.

Mon Tue Wed Thu Fri Sat Sun
5 Autumn bank holiday
6
7
8
9 Harvest festival
10
11

October 2026

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 31
<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

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

October 2026

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 31

November 2026

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
<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.

Resource Mo 14 Tu 15 We 16 Th 17 Fr 18 Sa 19 Su 20 Mo 21 Tu 22 We 23 Th 24 Fr 25 Sa 26 Su 27
<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.

2026 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 31
<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.

September 2026
Week 36
Week 37
Week 38
Week 39
Week 40
<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">&ndash;</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.

2020–2029
<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&ndash;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>