Why CSS Can’t Target Parents
CSS selectors travel from the root down the DOM tree. A selector can match an element only if it can be reached by following child or sibling relationships from the root. This design intentionally avoids back‑references to ancestors, keeping the cascade predictable and performant.
Because of this, a selector like li>a.active can’t be written to style the li based on its child’s state. The language simply doesn’t expose a parent‑matching syntax in Level 2 or Level 3 specifications.
Modern Solution: :has() Pseudo‑Class
The CSS Selectors Level 4 draft introduces the relational pseudo‑class :has(), which allows ancestor matching. In browsers that support it, you can write:
``` li:has(> a.active) { background: #f0f0f0; } ```
This selector matches any <li> that contains a direct child <a> with the class active. As of 2026, :has() is supported in Chrome, Edge, and Safari, but Firefox still lacks implementation. Use a feature‑test or a polyfill if you need cross‑browser coverage.
Practical Workaround with Sibling Selectors
When :has() isn’t available, you can restructure the markup or rely on the fact that the <a> is the only child of the <li>. The following trick uses the adjacent sibling combinator and the :first-child pseudo‑class to apply styles to the <li> when its child <a> is active:
``` li > a.active { /* style the link itself */ } li > a.active:first-child { /* this rule targets the <a>, not the <li> */ } li > a.active:first-child { /* cannot style <li> directly */ } ```
Because CSS can’t affect the parent, the most reliable method is to move the active class to the <li> in the markup or to add a wrapper element that can be styled. If you can’t change the CMS output, consider adding a data‑attribute via a server‑side template or a small JavaScript snippet that toggles the class on the <li>.
When JavaScript Is Needed
If you must keep the active class on the <a> and need to style its parent, a tiny script is the simplest solution:
``` document.querySelectorAll('a.active').forEach(a => { a.parentElement.classList.add('active'); }); ```
This code runs once on page load and mirrors the active state onto the <li>. It’s lightweight, works in all browsers, and keeps the CMS untouched.
Conclusion
CSS alone cannot target a parent element based on a child’s state in current stable specifications. The :has() pseudo‑class offers a future‑proof solution where supported, while server‑side tweaks or JavaScript remain the practical workarounds today.
Takeaway: Use :has() for future support, otherwise move the active class to the <li> or use a small script.
People also ask
Does :has() affect performance?
It can add a minor cost, but modern engines optimize it well. Test in your target browsers.
Is there a pure CSS way to style the <li> without changing markup?
Not with current standards; you must alter the markup or use JavaScript.
Inspired by a public discussion on Stack Overflow. This article is an original explanation for learners.