<main>
	<h1>CSS ::selection inheritance demo</h1>
	
	<p>Instructions: Select some of the text in the following demos. Try this in Chrome 134+.</p>
	
	<div class="warning"><p>⚠️ You are not using a Chromium-based browser that is at least Chromium 134. 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>I am a paragraph in a box. For clarity a dotted outline is added around the paragraph.</p>
		<p>The enclosing 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 <b>and its descendants</b>.</p>

		<p>Before Chrome 134, the selection styles would only be set on the <code>#demo</code> element itself. As a result 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>
		
		Text like this, which is placed directly inside <code>#demo</code> without any enclosing <code>&lt;p&gt;</code>, does get a highlight color in Chrome &lt; 134, because it is matched by <code>#demo1::selection</code>.
		
		<p>(In the text above though, in Chrome &lt; 134, you end up in this weird situation in which the <code>&lt;code&gt;</code> elements don’t get the highlight color)</p>
		
		<p>As of Chrome 134, the regular properties of the <code>#demo1::selection</code> selection styles also inherit onto children of <code>#demo1</code>.</p>
		
		<div><p>That means that even paragraph like this one, which is wrapped in an extra div (also with a dotted outline) or this <code>&lt;code&gt;</code> element is also green when highlighted.</p></div>
		
	</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 from the originating element.</p>
	</div>
	
	<h2>Demo4</h2>
	<div class="demo" id="demo4">
		<p>This box has the following snippet applied to it, which is the same as <code>#demo3</code>:</p>
		<pre><code>#demo4 {
	--bg: lightcoral;
}
		
#demo4::selection {
	color: green;
}

#demo4 ::selection {
	background: var(--bg);
}</code></pre>
		
		<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::selection {
	--bg: lime;
}</code></pre>
		<p class="special">The interesting thing here is that the <code>--bg</code> is set to <code>lime</code> for <code>p.special::selection</code>. This custom property only affects the selection styles of <code>p.special</code> and does not inherit onto child <code>::selection</code> styles. You can see this on the nested <code>&lt;code&gt;</code> elements: they are <code>hotpink</code>, not <code>lime</code>.</p>
		<p>Again, just like in <code>#demo3</code>, this is because <b>custom properties for highlight pseudos are inherited from the originating element</b> – they don’t inherit through the highlight chain.</p>
		<p>This behavior is compatible with Chrome &lt; 134.</p>
	</div>
	
	<h2>In summary</h2>
	<div class="demo" id="demo5">
		<p>This thing you have to remember is that:</p>
		
		<ul>
			<li>Regular properties in highlight pseudos inherit through the highlight chain.</li>
			<li>Custom properties in highlight pseudos inherit from their originating element.</li>
		</ul>
		<p>For backwards compatibility don’t only set things like <code>color</code> on <code>element::selection</code> but also on <code>element ::selection</code>. Custom Properties don’t need a fixup, as their behavior did not change.</p>
		<pre><code>#demo5 {
  --bg: yellow;

  code {
    --bg: #CBC3E3;
  }

  /* 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;
	}
}

#demo4 {
	--bg: lightcoral;
	&::selection {
		color: green;
	}
	*::selection {
		background: var(--bg);
	}
	
	p.special {
		--bg: hotpink;
	}
	p.special::selection {
		--bg: lime;
	}
	
}

#demo5 {
	--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: #CBC3E3;
	}
}

.demo {
	border: 1px solid black;
	padding: 2em;
	background: aliceblue;
	margin-bottom: 2em;
	
	> :first-child {
		margin-top: 0;
	}
	> :last-child {
		margin-bottom: 0;
	}
	
	p, div {
		outline: 1px dashed #ccc;
		padding: 0.25em;
	}
		
	div p {
		margin: 0;
	}
}

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) < 134) {
	document.querySelector('.warning').style.display = 'block';
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.