Building a Date Range Selector with CSS :nth-child and a Touch of JavaScript

From Wwwspill, the free encyclopedia of technology

Selecting a date range is a common feature in booking systems, event planners, and dashboards. While JavaScript usually handles the interactivity, CSS can do the heavy lifting for styling the selected range. One powerful tool is the :nth-child(An+B of selector) syntax, which lets you target elements based on both their order and a specific class or attribute. This Q&A explains how to combine that CSS selector with minimal JavaScript to create a clean, intuitive date range picker.

What is a date range selector and where is it used?

A date range selector lets users pick a time frame between a start and end date. You see it on travel booking sites like Airbnb, in filtering tools for analytics dashboards, and in scheduling apps. Instead of selecting one date, you pick two – a start and an end – to define a period. For example, choosing check-in and check-out dates for a hotel stay or selecting a week for a report. The interface usually highlights all dates between the two selected ones, making it clear what period you've chosen. In the example we're building, the styling of that highlighted range is mostly done with CSS, using the :nth-child selector's special of syntax to efficiently target the selected start and end dates among a list of date elements.

Building a Date Range Selector with CSS :nth-child and a Touch of JavaScript
Source: css-tricks.com

What is the “n of selector” syntax in CSS?

The :nth-child(An+B of selector) syntax is an extension of the regular :nth-child() pseudo-class. Normally, :nth-child(2) selects the second child element of its parent, regardless of type or class. But when you add of selector, it first filters all children that match the given selector (like .accent), and then counts among those filtered elements. For instance, if you have a list of paragraphs where some have class accent, :nth-child(2 of .accent) will select the second paragraph that has the class .accent, ignoring any non-accent paragraphs in between. This is perfect for date range pickers because you often want to apply styles to the second selected date (the end date) without being thrown off by non-date elements like day labels.

How do you create the month layout in CSS for the date picker?

The month layout is built with a simple unordered list and CSS Grid. Each day is an <li> – first the weekday headers (Mon, Tue, etc.) with class day, then the actual date numbers with class date. Inside each date <li>, there's a hidden checkbox input for JavaScript to track selection. The grid is defined with display: grid and grid-template-columns: repeat(7, 1fr) to create seven columns for the days of the week. That's it – just a few lines of CSS. The dates automatically flow into the correct positions because the list order matches the calendar days. The month can start on any weekday; the empty cells before the first date are simply not present in the HTML, so the grid starts at the correct column.

What role does JavaScript play in the date range selector?

CSS cannot change the checked state of a checkbox, so JavaScript is needed to handle user clicks and update the selection. The script listens for a change event on the calendar container. When a checkbox is toggled, it checks how many dates are currently selected. If exactly two dates are checked, it adds a CSS class to the calendar for styling the range. The logic for updating the range when a third date is clicked is custom – in this example, if the third date comes before the current start date, it becomes the new start; if it comes after the current end date, it becomes the new end; otherwise, it replaces the end date. This keeps the range always between two selected dates. The JavaScript is minimal, focusing only on checkbox management, while the visual highlighting of the range relies on CSS selectors applied via the dynamic class.

Building a Date Range Selector with CSS :nth-child and a Touch of JavaScript
Source: css-tricks.com

How do you highlight the selected date range using CSS?

Once JavaScript toggles a class (e.g., range-active) on the calendar container when exactly two dates are checked, CSS takes over. You can use the :nth-child(1 of .date:checked) and :nth-child(2 of .date:checked) selectors to target the first and second checked date (the start and end). Then, using the general sibling combinator (~) or :has() (if browser support is available), you can style all date elements between them. For example, #calendar.range-active .date:checked:first-of-type ~ .date:checked is one approach, but the of syntax makes it cleaner: you can style the start and end date with a different background, and using nth-child ranges you can fill the dates in between. The exact implementation depends on how you structure the HTML, but the principle is to let CSS handle the visual logic after JavaScript sets the state.

What happens when the user clicks a third date?

When a third date is selected, the range needs to adjust. The JavaScript logic determines which of the two previously selected dates to replace. The approach shown in the example works like this: if the newly clicked date is earlier than the current start date, it becomes the new start date (the old start is unselected). If it is later than the current end date, it becomes the new end date (the old end is unselected). If the new date falls between the start and end, then it replaces the end date (since it's closer to the end). This ensures that there are always exactly two selected dates defining a continuous range. After the update, the CSS class reapplies to highlight the new range. This logic is consistent with many booking interfaces where you can adjust either the departure or return date by tapping a new date.

What are the benefits of using CSS for most of the range styling?

Letting CSS handle the visual highlighting keeps your JavaScript lean and easier to maintain. CSS selectors, especially the n of selector syntax, are declarative and performant – the browser applies styles without extra loops or DOM manipulations. This separation of concerns improves readability: the JavaScript code only manages state (which dates are checked), while the CSS defines how the range looks. Additionally, if you later need to change the appearance (colors, animations, hover effects), you only update the stylesheet, not the logic. The technique also gracefully degrades: if JavaScript fails, the checkboxes still work for form submission, and no range styling is applied – a clean fallback.