<main>
<h1>CSS ::selection inheritance demo</h1>
<p>Instructions: Select some of the text in the following demos. Try this in Chrome 131+.</p>
<div class="warning"><p>⚠️ You are not using a Chromium-based browser that is at least Chromium 131. As a result, the expected behavior described in this demo will most likely make no sense.</p></div>
<h2>Demo1</h2>
<div class="demo" id="demo1">
<p>This box has the following snippet applied to it:</p>
<pre><code>#demo1::selection {
color: green;
}</code></pre>
<p>It sets the <code>::selection</code> style for the entire <code>#demo1</code> element and its descendants.</p>
<div><p>This is paragraph here is wrapped in an extra div which has a dotted outline. Its contents are also green when highlighted, because it inherits the highlight styles through <code>#demo1::selection</code></p></div>
<p>Before Chrome 131 none of the paragraphs in this box would be <code>green</code> when highlighted, because they are not the <code>#demo1</code> element but descendants of it.</p>
</div>
<h2>Demo2</h2>
<div class="demo" id="demo2">
<p>This box has the following snippet applied to it:</p>
<pre><code>#demo2 {
--bg: lightcoral;
}
#demo2::selection {
color: green;
background: var(--bg);
}</code></pre>
<p>All selected text in this box is therefore <code>green</code> with a <code>lightcoral</code> background.</p>
<p>The following CSS is also present:</p>
<pre><code>#demo2 p {
--bg: hotpink;
}</code></pre>
<p>It, however, has no effect on the paragraphs because the <code>::selection</code> styles are defined through <code>#demo2::selection</code> which gets its value for <code>--bg</code> from the originating element <code>#demo2</code>.</p>
</div>
<h2>Demo3</h2>
<div class="demo" id="demo3">
<p>This box has the following snippet applied to it:</p>
<pre><code>#demo3 {
--bg: lightcoral;
}
#demo3::selection {
color: green;
}
#demo3 ::selection {
background: var(--bg);
}</code></pre>
<p>Just like in <code>#demo2</code> highlighted text is <code>green</code> with a <code>lightcoral</code> background.</p>
<p>But by having <code>#demo3 ::selection</code> in there–note the space–it allows every child of <code>#demo3</code> to declare its own <code>--bg</code>.</p>
<div><p>For example, this paragraph is wrapped in a <code>div</code>. Because of the following CSS, its background color is <code>yellow</code>.</div>
<pre><code>div {
--bg: yellow;
}</code></pre>
<p>This is because custom properties for highlight pseudos are inherited through the element tree.</p>
<p class="special">This paragraph with the class of <code>.special</code> is styled as follows:</p>
<pre><code>p.special {
--bg: hotpink;
}
p.special code {
--bg: lime;
}</code></pre>
<p class="special">Note that the <code>--bg</code> in <code>code::selection</code> gets ignored. The lookup for custom props in <code>::selection</code> <b>ALWAYS</b> happens through the element chain, so the following has no effect:</p>
<pre><code>p.special code::selection {
--bg: blue; /* This gets ignored. Instead, the --bg from the originating element is used instead. */
}</code></pre>
</div>
<h2>Putting it all together</h2>
<div class="demo" id="demo4">
<p>Practically this means you need to write your styles as follows:</p>
<pre><code>#demo4 {
--bg: yellow;
code {
--bg: lightblue;
}
/* Regular properties = prefix with element */
&::selection {
color: red;
}
/* Custom properties = no prefix */
::selection {
background: var(--bg, transparent);
}
}</code></pre>
<p>If you want backwards compatibility with the old model, you can try duplicating the ::selection style on all children of the element you are targeting:</p>
<pre><code>#demo4 {
--bg: yellow;
code {
--bg: lightblue;
}
/* Regular properties = prefix with element */
&::selection {
color: red;
}
/* Backwards compatibility */
& *::selection {
color: red;
}
/* Custom properties = no prefix */
::selection {
background: var(--bg, transparent);
}
}</code></pre>
<div><p>This also works on this paragraph which is wrapped in a <code>div</code> element.</p></div>
</div>
</main>
:root {
--color: hotpink;
}
#demo1 {
&::selection {
color: green;
}
}
#demo2 {
--bg: lightcoral;
&::selection {
color: green;
background: var(--bg);
}
p {
--bg: hotpink;
}
}
#demo3 {
--bg: lightcoral;
&::selection {
color: green;
}
*::selection {
background: var(--bg);
}
div {
--bg: yellow;
}
p.special {
--bg: hotpink;
}
p.special code {
--bg: lime;
}
p.special code::selection {
--bg: blue;
}
}
#demo4 {
--bg: yellow;
/* Regular props = prefix with element */
&::selection {
color: red;
}
/* Backwards compatibility */
& *::selection {
color: red;
}
/* Custom props = no prefix */
::selection {
background: var(--bg, transparent);
}
code {
--bg: lightblue;
}
}
.demo {
border: 1px solid black;
padding: 2em;
background: aliceblue;
margin-bottom: 2em;
> :first-child {
margin-top: 0;
}
> :last-child {
margin-bottom: 0;
}
div {
outline: 1px dashed #ccc;
outline-offset: 10px;
}
}
main {
max-width: 40em;
margin: 0 auto;
}
@layer baselayout {
* {
box-sizing: border-box;
}
html {
margin: auto;
line-height: 1.5;
font-size: 24px;
font-family: "Syne", sans-serif;
height: 100%;
background: white;
}
body {
margin: 0;
min-height: 100dvh;
padding: 2em;
}
footer {
text-align: center;
font-style: italic;
margin-top: 2rem;
}
h1,
h2,
summary {
font-family: "Anybody", sans-serif;
text-decoration: underline;
text-decoration-color: hsl(156deg 100% 50% / 50%);
text-decoration-thickness: 0.2rem;
text-decoration-style: wavy;
text-decoration-skip-ink: none;
}
h2 {
margin: 2em 0 0.5em 0;
text-decoration-color: hsl(240deg 100% 50% / 50%);
text-wrap: balance;
}
a {
color: inherit;
}
button,
input,
textarea {
font-family: inherit;
font-size: inherit;
}
}
@layer code {
pre {
border: 1px solid #dedede;
padding: 0.5em;
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);
padding: 0.1rem 0.3rem;
margin: 0.1rem 0;
border-radius: 0.2rem;
/* display: inline-block; */
-webkit-box-decoration-break: clone;
white-space: pre-wrap;
}
}
@layer utilities {
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
}
@layer warning {
.warning {
display: none;
box-sizing: border-box;
padding: 1em;
margin: 1em 0;
border: 1px solid #ccc;
background: rgba(255 255 205 / 0.8);
}
.warning > :first-child {
margin-top: 0;
}
.warning > :last-child {
margin-bottom: 0;
}
.warning a {
color: blue;
}
.warning--info {
border: 1px solid #123456;
background: rgb(205 230 255 / 0.8);
}
.warning--alarm {
border: 1px solid red;
background: #ff000010;
}
}
}
const getChromiumVersion = (brands) => {
const chromium = brands.filter(b => b.brand == 'Chromium');
return chromium[0]?.version ?? 0;
}
const isChromium = (brands) => {
return brands.filter(b => b.brand == 'Chromium').length;
}
if (!navigator.userAgentData || !isChromium(navigator.userAgentData.brands) || getChromiumVersion(navigator.userAgentData.brands) < 131) {
document.querySelector('.warning').style.display = 'block';
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.