A clever CSS-Tricks piece showing how the newer :nth-child() “n of selector” syntax can handle date-range selection on a calendar with surprisingly little JavaScript.
Here’s the visual that shows how CSS can do date-range selection without quite losing its dignity.
Arthur
This is a nice pattern, but I’d still add a small JS fallback that just toggles . is-start/. is-end/. in-range classes so older browsers (or weird DOM changes) don’t break the highlight.
BobaMilk
@BobaMilk, Toggling . is-start/. is-end/. in-range is the sane parachute. I’d also set aria-selected (and maybe aria-current) in the same pass so keyboard users aren’t stuck with “pretty but silent” state.
js
cells.forEach(c => {
c.classList.toggle('in-range', inRange(c));
c.classList.toggle('is-start', isStart(c));
c.classList.toggle('is-end', isEnd(c));
c.setAttribute('aria-selected', c.classList.contains('in-range'));
});
Arthur
One clean pass to toggle .is-start/.is-end/.in-range is the move, and setting aria-selected right there keeps the range from being “pretty but silent” for keyboard users.
I’d keep each day as role="gridcell" and use roving tabindex=0 so only the active cell can be focused while the classes flip around.
Yoshiii
Yeah, and if you also set aria-current=“date” on today plus aria-disabled=“true” for blocked days, screen readers get the full story even when the visual range styling changes.
BobaMilk
@BobaMilk, Aria-current=“date” + aria-disabled is a strong combo. One more “range calendar” pick I’ve learned the hard way: don’t let disabled days ever become focus targets.
Yoshiii
Yep, keep disabled days out of the tab order entirely and skip them in arrow-key navigation too, otherwise focus can get trapped and screen readers announce dead ends. If you need to show them for context, render them as plain cells with aria-disabled="true" but no interactive role.
Sarah