<h1>Creating new Selectors with <code>:has()</code>:<br />A <code>:nth-child(n of S)</code> polyfill</em></h1>
<div class="no-support" data-support="css-nth-child-of-s" data-level="warn"><p>⚠️ Your browser does not support the <code>:nth-child(1 of S)</code>/<code>:nth-last-child(1 of S)</code> selectors but that’s no biggie. Thanks to <code>:not()</code> and <code>:has()</code> we can polyfill it.</p></div>
<div class="has-support" data-support="css-nth-child-of-s"><p>✅ Your browser supports the <code>:nth-child(1 of S)</code>/<code>:nth-last-child(1 of S)</code> selectors. However, they are not used because this demo demonstrates how <code>:not()</code> and <code>:has()</code> can polyfill them.</p></div>
<div class="no-support" data-support="css-has-basic"><p>🚨 Your browser does not support CSS <code>:has()</code>, so this demo will not work correctly.</p></div>
<div class="no-support" data-support="css-has-relative"><p>🚨 Your browser does not support relative selectors in CSS <code>:has()</code>, so this demo will not work correctly.</p></div>
<h2>What?</h2>
<p>This page is a demo for <a href="https://brm.us/nth-child-polyfill" target="_top">https://brm.us/nth-child-polyfill</a>. Please read the post to get the full details and explanation.</p>
<h2>The Rules</h2>
<ul>
<li><code>:nth-child()</code>: The <code>.special</code> children, from first to last, have rainbow colors: <code>red</code>, <code>orange</code>, <code>yellow</code>, <code>green</code>, <code>blue</code>, <code>indigo</code>, and <code>violet</code>.</li>
<li><code>:nth-last-child()</code>: The <code>.special</code> children, from last to first, have a different border thickness.</li>
</ul>
<p>Typically you’d use <code>:nth-child(n of S)</code> for this, but thanks to <code>:has()</code> we can polyfill it.</p>
<h2>Demo</h2>
<div>
<p><em>TIP: You can click each paragraph to toggle the <code>.special</code> class</em></p>
</div>
<div class="demo">
<p class="special">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p class="special">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p class="special">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p class="special">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p class="special">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
<p class="special">Lorem ipsum dolor sit, amet consectetur adipisicing elit. Eaque in, enim rem recusandae mollitia error, natus ab atque dignissimos culpa veniam exercitationem sequi voluptates, commodi corrupti ullam sit. Debitis, a!</p>
</div>
:where(.demo > *) {
margin: 0;
padding: 0.5em 1em;
border: 1px solid transparent; /* We set a transparent border on all sides to prevent a layout jump when actually painting the border */
cursor: pointer;
transition: all 0.5s ease-in-out;
}
/* Basic .special styling */
.special {
background: rgb(0 0 0 / 0.1);
border-left: 1px solid currentcolor;
}
/* :nth-child(1 of .special) */
.special:not(.special ~ .special) {
color: red;
}
/* :nth-child(2 of .special) */
.special ~ .special:not(.special ~ .special ~ .special) {
color: orange;
}
/* :nth-child(3 of .special) */
.special ~ .special ~ .special:not(.special ~ .special ~ .special ~ .special) {
color: yellow;
}
/* :nth-child(4 of .special) */
.special ~ .special ~ .special ~ .special:not(.special ~ .special ~ .special ~ .special ~ .special) {
color: green;
}
/* :nth-child(5 of .special) */
.special ~ .special ~ .special ~ .special ~ .special:not(.special ~ .special ~ .special ~ .special ~ .special ~ .special) {
color: blue;
}
/* :nth-child(6 of .special) */
.special ~ .special ~ .special ~ .special ~ .special ~ .special:not(.special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special) {
color: indigo;
}
/* :nth-child(7 of .special) */
.special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special:not(.special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special) {
color: violet;
}
/* :nth-last-child(1 of .special) */
.special:not(:has(~ .special)) {
border-left-width: 50px;
}
/* :nth-last-child(2 of .special) */
.special:not(:has(~ .special ~ .special)):not(.special:not(:has(~ .special))) {
border-left-width: 45px;
}
/* :nth-last-child(3 of .special) */
.special:not(:has(~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special))) {
border-left-width: 40px;
}
/* :nth-last-child(4 of .special) */
.special:not(:has(~ .special ~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special ~ .special))) {
border-left-width: 35px;
}
/* :nth-last-child(5 of .special) */
.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special ~ .special ~ .special))) {
border-left-width: 30px;
}
/* :nth-last-child(6 of .special) */
.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special))) {
border-left-width: 25px;
}
/* :nth-last-child(7 of .special) */
.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special))) {
border-left-width: 20px;
}
/* :nth-last-child(8 of .special) */
.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special))) {
border-left-width: 15px;
}
/* :nth-last-child(9 of .special) */
.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special))) {
border-left-width: 10px;
}
/* :nth-last-child(10 of .special) */
.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special)):not(.special:not(:has(~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special ~ .special))) {
border-left-width: 5px;
}
/* 💡 See https://codepen.io/bramus/pen/rNKErPe to generate more selectors … */
/* Non-demo styles below */
@layer base {
@layer layout {
html {
max-width: 84ch;
padding: 3rem 2rem;
margin: auto;
line-height: 1.5;
font-size: 1.25rem;
}
body {
margin: 0;
padding: 0;
}
a,
a:visited {
color: blue;
}
h2 {
margin-top: 2em;
}
h1 {
display: none;
}
}
@layer code {
pre {
border: 1px solid #dedede;
padding: 1em;
background: #f7f7f7;
font-family: "Courier 10 Pitch", Courier, monospace;
overflow-x: auto;
border-left: 0.4em solid cornflowerblue;
tab-size: 4;
}
code:not(pre code) {
background: #f7f7f7;
border: 1px solid rgb(0 0 0 / 0.2);
}
}
@layer support {
.no-support,
.has-support {
margin: 1em 0;
padding: 1em;
border: 1px solid #ccc;
}
.no-support {
background-color: #ff00002b;
display: block;
}
.no-support[data-level="warn"] {
background-color: #ffff002b;
}
.has-support {
background-color: #00ff002b;
display: none;
}
:is(.has-support, .no-support) > :first-child {
margin-top: 0;
}
:is(.has-support, .no-support) > :last-child {
margin-bottom: 0;
}
@supports selector(:has(*)) {
.no-support[data-support="css-has-basic"] {
display: none;
}
.has-support[data-support="css-has-basic"] {
display: block;
}
}
@supports selector(:has(+ *)) {
.no-support[data-support="css-has-relative"] {
display: none;
}
.has-support[data-support="css-has-relative"] {
display: block;
}
}
@supports selector(:nth-child(1 of .foo)) {
.no-support[data-support="css-nth-child-of-s"] {
display: none;
}
.has-support[data-support="css-nth-child-of-s"] {
display: block;
}
}
}
}
@layer reset {
}
document.querySelectorAll('.demo p').forEach($p => {
$p.addEventListener('click', (e) => {
e.target.classList.toggle('special');
});
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.