<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';
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.