惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

CSS-Tricks

Stack Overflow: When We Stop Asking | CSS-Tricks Cross-Document View Transitions: The Gotchas Nobody Mentions | CSS-Tricks What’s !important #11: 3D Voxel Scenes, Flying Focus, CSS Syntaxes, and More | CSS-Tricks Computing and Displaying Discounted Prices in CSS | CSS-Tricks rotateX() | CSS-Tricks rotateY() | CSS-Tricks rotateZ() | CSS-Tricks rotate() | CSS-Tricks Soon We Can Finally Banish JavaScript to the ShadowRealm | CSS-Tricks Using CSS corner-shape For Folded Corners | CSS-Tricks A Scrollytelling Gift for Mum on Mother’s Day 2026 | CSS-Tricks Google’s Prompt API | CSS-Tricks Making Zigzag CSS Layouts With a Grid + Transform Trick | CSS-Tricks Fixed-Height Cards: More Fragile Than They Look | CSS-Tricks What’s !important #10: HTML-in-Canvas, Hex Maps, E-ink Optimization, and More | CSS-Tricks The Importance of Native Randomness in CSS | CSS-Tricks contrast() | CSS-Tricks contrast-color() | CSS-Tricks Let’s Use the Nonexistent ::nth-letter Selector Now | CSS-Tricks Recreating Apple’s Vision Pro Animation in CSS | CSS-Tricks Enhancing Astro With a Markdown Component | CSS-Tricks Markdown + Astro = ❤️ | CSS-Tricks What’s !important #9: clip-path Jigsaws, View Transitions Toolkit, Name-only Containers, and More | CSS-Tricks A Well-Designed JavaScript Module System is Your First Architecture Decision | CSS-Tricks hypot() | CSS-Tricks The Radio State Machine | CSS-Tricks 7 View Transitions Recipes to Try | CSS-Tricks saturate() | CSS-Tricks justify-self | CSS-Tricks Alternatives to the !important Keyword | CSS-Tricks New CSS Multi-Column Layout Features in Chrome | CSS-Tricks Making Complex CSS Shapes Using shape() | CSS-Tricks Front-End Fools: Top 10 April Fools’ UI Pranks of All Time | CSS-Tricks Sniffing Out the CSS Olfactive API | CSS-Tricks What’s !important #8: Light/Dark Favicons, @mixin, object-view-box, and More | CSS-Tricks Form Automation Tips for Happier User and Clients | CSS-Tricks Generative UI Notes | CSS-Tricks Experimenting With Scroll-Driven corner-shape Animations | CSS-Tricks JavaScript for Everyone: Destructuring | CSS-Tricks What’s !important #7: random(), Folded Corners, Anchored Container Queries, and More | CSS-Tricks 4 Reasons That Make Tailwind Great for Building Layouts | CSS-Tricks Abusing Customizable Selects | CSS-Tricks The Value of z-index | CSS-Tricks
Selecting a Date Range in CSS | CSS-Tricks
2026-04-09 · via CSS-Tricks

A date range selector lets users pick a time frame between a start and end date, which is useful in booking trips, sorting info by date blocks, picking time slots, and planning schedules.

A calendar month layout with the dates 8 and 18 selected with black backgrounds.
Example pulled from Airbnb

I’m going to show you an example where, even though JavaScript is involved, the bulk of the work is handled by the “n of selector(s)” syntax of the CSS :nth-child selector, making it easy to build the range selection.

The “n of selector” syntax

This syntax of the :nth-child selector filters elements by a given selector first among all the child elements, before selecting them by a counting order.

<p>The reclamation of land...</p>
<p>The first reclamations can be traced...</p>
<p class="accent">By 1996, a total of...</p>
<p>Much reclamation has taken...</p>
<p class="accent">Hong Kong legislators...</p>
.accent {
  color: red;
}
.accent:nth-child(2) {
  font-weight: bold; /* does not work */
}
:nth-child(2 of .accent){
  text-decoration: underline;
}

There are two .accent-ed paragraphs with red text. As we try to target the second accented paragraph, .accent:nth-child(2) fails to select it because it’s trying to find an .accent element that’s the second child of its parent.

Whereas, :nth-child(2 of .accent) succeeds in selecting and styling the second accented paragraph because it’s only looking for the second element among the **.accent** elements rather than the second of all of the children.

The Layout

Moving onto our main example, let’s put together a month layout. It only takes a few lines of CSS.

<ul id="calendar">
  <li class="day">Mon</li>
  <li class="day">Tue</li>
  <!-- up to Sat -->
  <li class="date">01<input type="checkbox" value="01"></li>
  <li class="date">02<input type="checkbox" value="02"></li>
  <!-- up to 31  -->
</ul>
#calendar {
  display: grid;
  grid-template-columns: repeat(7, 1fr); /* 7 for no. of days in a week */
}

Choose Only Two Dates

Now is when we reach for JavaScript since we can’t check/uncheck a control in CSS. But even here the “n of selector” syntax can be very useful.

When we pick two dates to create a range, clicking on a third date will update the range and remove one of the earlier dates.

You can set up the range re-adjustment logic in any way you like. I’m using this approach: If the third date is either earlier or later than the last return date, it becomes the new return date, and the old one is unselected. If the third date is earlier than the last onward date, it becomes the new onward date, and the old one is unselected.

const CAL = document.getElementById('calendar');
const DT = Array.from(CAL.getElementsByClassName('date')); 

CAL.addEventListener('change', e => {
  if (!CAL.querySelector(':checked')) return;
  
  /* When there are two checked boxes, calendar gets 'isRangeSelected' class  */
  CAL.className = CAL.querySelector(':nth-child(2 of :has(:checked))') ? 'isRangeSelected':'';

  /* When there are three checked boxes */
  if (CAL.querySelector(':nth-child(3 of :has(:checked))')) {

    switch (DT.indexOf(e.target.parentElement)) {

      /* If the newly checked date is first among the checked ones, 
          the second checked is unchecked. Onward date moved earlier. */
      case DT.indexOf(CAL.querySelector(':nth-child(1 of :has(:checked))')):
      CAL.querySelector(':nth-child(2 of :has(:checked)) input').checked = 0; 
      break;

      /* If the newly checked date is second among the checked ones, 
          the third checked is unchecked. Return date moved earlier. */
      case DT.indexOf(CAL.querySelector(':nth-child(2 of :has(:checked))')):
      CAL.querySelector(':nth-child(3 of :has(:checked)) input').checked = 0; 
      break;

      /* If the newly checked date is third among the checked ones, 
          the second checked is unchecked. Return date moved later. */
      case DT.indexOf(CAL.querySelector(':nth-child(3 of :has(:checked))')):
      CAL.querySelector(':nth-child(2 of :has(:checked)) input').checked = 0; 
      break;

    }
  }
});

First, we get the index of the current checked date (DT.indexOf(e.target.parentElement)), then we see if that’s the same as the first checked among all the checked ones (:nth-child(1 of :has(:checked))), second (:nth-child(2 of :has(:checked))), or third (:nth-child(3 of :has(:checked))). Given that, we then uncheck the relevant box to revise the date range.

You’ll notice that by using the “n of selector” syntax, targeting the :checked box we want by its position among all checked ones is made much simpler — instead of indexing through a list of checked dates in JavaScript for this, we can directly select it.

Styling the range is even easier than this.

Styling the Range

/* When two dates are selected */
.isRangeSelected { 
  /* Dates following the first but not the second of selected */
  :nth-child(1 of :has(:checked)) ~ :not(:nth-child(2 of :has(:checked)) ~ .date) {
    /* Range color */
    background-color: rgb(228 239 253); 
  }
}

When there are two dates chosen, the dates between the first (1 of :has(:checked)) and second (2 of :has(:checked)) are colored pale blue, creating a visual range for that block of dates in the month.

A calendar month layout with the dates 9-29 selected. 9 and 19 have a dark blue background and the dates between are light blue.

The color is declared inside a compound selector that selects dates (.date) following the first of all checked date (:nth-child(1 of :has(:checked))), but not the second of all checked date (:not(:nth-child(2 of :has(:checked))).

Here’s the full example once again: