<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>css &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/css/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Fri, 13 Mar 2026 15:18:22 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>css &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[An odd font rendering bug in Firefox and Safari]]></title>
		<link>https://shkspr.mobi/blog/2026/03/an-odd-font-rendering-bug-in-firefox-and-safari/</link>
					<comments>https://shkspr.mobi/blog/2026/03/an-odd-font-rendering-bug-in-firefox-and-safari/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 13 Mar 2026 12:34:09 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[font]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=68692</guid>

					<description><![CDATA[First up, you should go and watch The Importance of Being Earnest with Ncuti Gatwa. It is a brilliant set of performances and a joy to see.  While perusing the programme on the National Theatre website I stumbled upon a little bug.  The incredible Ronkẹ Adékọluẹ́jọ́ has her name rendered in a most unusual style:    It rendered just fine in Chrome - but both Firefox and Safari misrendered some of t…]]></description>
										<content:encoded><![CDATA[<p>First up, you should go and watch <a href="https://www.youtube.com/watch?v=obX-HGs-PS8">The Importance of Being Earnest</a> with Ncuti Gatwa. It is a brilliant set of performances and a joy to see.</p>

<p>While perusing the <a href="https://www.nationaltheatre.org.uk/productions/the-importance-of-being-earnest/#cast">programme on the National Theatre website</a> I stumbled upon a little bug.  The incredible Ronkẹ Adékọluẹ́jọ́ has her name rendered in a most unusual style:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2026/03/ronke.webp" alt="Screenshot of a website. Contains a phone of a black woman next to her name. Any characters with accents in her name are rendered without boldface." width="2953" height="1798" class="aligncenter size-full wp-image-68694">

<p>It rendered just fine in Chrome - but both Firefox and Safari misrendered <em>some</em> of the accented characters.</p>

<p>Here's a minimum viable demo to show what's happening:</p>

<iframe height="300" style="width: 100%;" scrolling="no" title="FF Font Rendering Issue?" src="https://codepen.io/edent/embed/qEaRyrz?default-tab=html%2Cresult" frameborder="no" loading="lazy" allowtransparency="true">
  See the Pen <a href="https://codepen.io/edent/pen/qEaRyrz">
  FF Font Rendering Issue?</a> by Terence Eden (<a href="https://codepen.io/edent">@edent</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

<h2 id="fonts-are-hard-ok"><a href="https://shkspr.mobi/blog/2026/03/an-odd-font-rendering-bug-in-firefox-and-safari/#fonts-are-hard-ok">Fonts are hard, OK?!?!</a></h2>

<p>Broadly speaking<sup id="fnref:complicated"><a href="https://shkspr.mobi/blog/2026/03/an-odd-font-rendering-bug-in-firefox-and-safari/#fn:complicated" class="footnote-ref" title="It is a lot more complicated than that." role="doc-noteref">0</a></sup>, accented characters can be made in two way.</p>

<ol>
<li>Pre-composed. There is a separate code for the character <code>é</code></li>
<li>Combining. The plain letter <code>e</code> is immediately followed by the <em>combining</em> character <code>◌́</code> and the computer smushes them together.</li>
</ol>

<p>Similarly, a font file can have separate little drawings for each accented character or it can have separate accents.</p>

<p>In this case, the National Theatre is using the font "Helvetica Now Display W04".</p>

<p>The web font contains <code>é</code> (U+00E9) and both <code>◌́</code> (U+0301) &amp; <code>̣◌</code> (U+0323).</p>

<p>But doesn't include <code>ẹ</code> (U+1EB9) or <code>ọ</code> (U+1ECD).</p>

<p>So the ẹ́  and ọ́  have to be made by combining characters in the font.</p>

<p>On Chrome this works. On Firefox and Safari, it seems to break when the CSS is set to <code>font-weight: normal;</code>. This causes the browser to render those characters in the default fallback font - hence the slightly weird look.</p>

<h2 id="next-steps"><a href="https://shkspr.mobi/blog/2026/03/an-odd-font-rendering-bug-in-firefox-and-safari/#next-steps">Next Steps</a></h2>

<p>I've raised <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=2023126">a bug with Firefox</a> and one with <a href="https://bugs.webkit.org/show_bug.cgi?id=309889">WebKit</a>.</p>

<p>Of course, it might be that they're doing the right thing and Chrome is in the wrong - but I think that's unlikely.</p>

<p>Now, time to fix the font I use on this website to prevent any rendering errors!</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:complicated">
<p>It is a <em>lot</em> more <a href="https://www.youtube.com/watch?v=5NPBIwQyPWE">complicated</a> than that.&nbsp;<a href="https://shkspr.mobi/blog/2026/03/an-odd-font-rendering-bug-in-firefox-and-safari/#fnref:complicated" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=68692&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/03/an-odd-font-rendering-bug-in-firefox-and-safari/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Getting started with simple CSS View Transitions]]></title>
		<link>https://shkspr.mobi/blog/2025/10/getting-started-with-simple-css-view-transitions/</link>
					<comments>https://shkspr.mobi/blog/2025/10/getting-started-with-simple-css-view-transitions/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 21 Oct 2025 11:34:07 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[webdev]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=64009</guid>

					<description><![CDATA[There&#039;s (yet another) new piece of CSS to learn! Hurrah!  Way back in 2011, jQuery mobile introduced the web to page-change animations. Clicking on a link would make your high-tech Nokia display a cool page-flip as you navigated from one page of a website to another. Just like an app!!!!  A decade-and-a-half later, and CSS has caught up (mostly). No more JavaScript, just spec-compliant CSS. Well, …]]></description>
										<content:encoded><![CDATA[<p>There's (yet another) new piece of CSS to learn! Hurrah!</p>

<p>Way back in 2011, <a href="https://demos.jquerymobile.com/1.1.0/docs/pages/page-transitions.html">jQuery mobile introduced the web to page-change animations</a>. Clicking on a link would make your high-tech Nokia display a cool page-flip as you navigated from one page of a website to another. Just like an app!!!!</p>

<p>A decade-and-a-half later, and CSS has caught up (mostly). No more JavaScript, just spec-compliant CSS. Well, as long as you're using Chrome or Safari.  Here's a quick quick MVP which will add some fancy animations as people browse your website.</p>

<p>Every page which wants animations has to "opt in". That means you need this:</p>

<pre><code class="language-css">@view-transition {
    navigation: auto;
}
</code></pre>

<p>Next, you'll probably want to define some animations. Here are two I use:</p>

<pre><code class="language-css">@keyframes slide-in {
    from {
        translate: 100vw 0;
    }
}

@keyframes fade-out {
    to {
        opacity: 0;
    }
}
</code></pre>

<p>Any standard CSS animation will work. Get creative!</p>

<p>Which elements do you want to animate? I'm just going to do the whole page.</p>

<pre><code class="language-css">html {
    view-transition-name: page;
}
</code></pre>

<p>If you have a fancy app-like site, you might only want to animate specific parts of it.</p>

<p>While the page is transitioning, you can have something in the background to prevent things looking odd.</p>

<pre><code class="language-css">::view-transition {
    background: black;
}
</code></pre>

<p>That's optional, but rather useful.</p>

<p>Next, we have to assign the animations to specific events. Here, I have the old page fade out and the new page slide in.</p>

<pre><code class="language-css">::view-transition-old(page) {
    animation-name: fade-out;
    animation-duration: 1s;
}

::view-transition-new(page) {
    animation-name: slide-in;
    animation-duration: 1s;
}
</code></pre>

<p>You can set the duration to whatever makes sense for your page and animation style.</p>

<p>Finally, and this is <strong>important</strong>, some people find animations painful or discombobulating. Make sure the animations are turned off for those who don't like them.</p>

<pre><code class="language-css">@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(page) {
        animation-duration: 0s;
    }
}
</code></pre>

<p>And that's it! A couple of dozen lines of CSS and you've got started with view transitions.</p>

<p>For more information, you can <a href="https://view-transitions.chrome.dev/">see the Chrome Devs' demo page</a>, or take a read of <a href="https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API">the MDN documentation</a>. There's also a <a href="https://drafts.csswg.org/css-view-transitions-2/">full spec document</a> if you like that sort of thing.</p>

<p>Right, I'm off to create some delightful animations!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=64009&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/10/getting-started-with-simple-css-view-transitions/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Quick and dirty bar-charts using HTML's meter element]]></title>
		<link>https://shkspr.mobi/blog/2025/10/quick-and-dirty-bar-charts-using-htmls-meter-element/</link>
					<comments>https://shkspr.mobi/blog/2025/10/quick-and-dirty-bar-charts-using-htmls-meter-element/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 11 Oct 2025 11:34:57 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=63220</guid>

					<description><![CDATA[&#34;If it&#039;s stupid but it works, it&#039;s not stupid.&#34;  I want to draw some vertical bar charts. I don&#039;t want to use a 3rd party library, or bundle someone else&#039;s CSS, or learn how to build SVGs.  HTML contains a &#60;meter&#62; element. It is used like this:  &#60;meter min=&#34;0&#34; max=&#34;4000&#34; value=&#34;1234&#34;&#62;1234&#60;/meter&#62;   Which looks like this: 1234  There isn&#039;t much you can do to style it. Browser manufacturers seem to …]]></description>
										<content:encoded><![CDATA[<p>"If it's stupid but it works, it's not stupid."</p>

<p>I want to draw some vertical bar charts. I don't want to use a 3rd party library, or bundle someone else's CSS, or learn how to build SVGs.</p>

<p>HTML contains a <code>&lt;meter&gt;</code> element. It is used like this:</p>

<pre><code class="language-html">&lt;meter min="0" max="4000" value="1234"&gt;1234&lt;/meter&gt;
</code></pre>

<p>Which looks like this: <meter min="0" max="4000" value="1234" style="border-radius:0 !important;">1234</meter></p>

<p>There isn't <em>much</em> you can do to style it. Browser manufacturers seem to have forgotten it exists and the CSS standard kind of ignores it.</p>

<p>It <em>is</em> possible to use CSS to rotate it using:</p>

<pre><code class="language-css">meter {
   transform: rotate(-90deg);
}
</code></pre>

<p>But then you have to mess about with origins and the box model gets a bit confused.</p>

<p>See what <meter min="0" max="4000" value="1234" style="transform: rotate(-90deg);">1234</meter> I mean?</p>

<p>You can hack your way around that with <code>&lt;div&gt;</code>s and bludgeoning your layout into submission.</p>

<p>But that is a bit tedious.</p>

<p>Luckily, there's another way.  As suggested by <a href="https://mastodon.social/@gundersen/115168958609140525">Marius Gundersen</a>, it's possible to set the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">writing direction</a> of the element to be vertical.</p>

<p>That means you can have them "written" vertically, while having them laid out horizontally. Giving a nice(ish) bar-chart effect.</p>

<p><meter min="0" max="4000" value="1000" style="writing-mode:vertical-lr;border-radius:0 !important;">1000</meter><meter min="0" max="4000" value="2000" style="writing-mode: vertical-lr;border-radius:0 !important;">2000</meter><meter min="0" max="4000" value="3000" style="writing-mode: vertical-lr;border-radius:0 !important;">3000</meter><meter min="0" max="4000" value="4000" style="writing-mode: vertical-lr;border-radius:0 !important;">4000</meter></p>

<p>As well as the normal sort of CSS spacing, there is basic colour support for values which are inside a specific range:</p>

<p><meter min="0" max="4000" value="1000" low="1000" high="400" style="writing-mode:vertical-lr;border-radius:0 !important;">1000</meter>
<meter min="0" max="4000" value="2000" low="2000" high="400" style="writing-mode:vertical-lr;border-radius:0 !important;">2000</meter>
<meter min="0" max="4000" value="3000" style="writing-mode:vertical-lr;border-radius:0 !important;">3000</meter>
<meter min="0" max="4000" value="4000" high="4000" style="writing-mode:vertical-lr;border-radius:0 !important;">4000</meter></p>

<p>The background colour can also be set.</p>

<p><meter min="0" max="4000" value="1000" style="writing-mode:vertical-lr;border-radius:0 !important;background:red;">1000</meter></p>

<p>I dare say they're slightly more accessible than a raster image - even with good alt text. They can be targetted with JS, if you want to do fancy things with them.</p>

<p>Or, if you just want a quick and dirty bar-chart, they're basically fine.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63220&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/10/quick-and-dirty-bar-charts-using-htmls-meter-element/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Drunk CSS]]></title>
		<link>https://shkspr.mobi/blog/2025/09/drunk-css/</link>
					<comments>https://shkspr.mobi/blog/2025/09/drunk-css/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 27 Sep 2025 11:34:51 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[drunk]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[ui]]></category>
		<category><![CDATA[ux]]></category>
		<category><![CDATA[webdev]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=62987</guid>

					<description><![CDATA[A decade ago, I was writing about how you should test your user interface on drunk people. It was a semi-serious idea.  Some of your users will be drunk when using your app or website. If it is easy for them to use, then it should be easy for sober people to use.  Of course, necking a few shots every time you update your website isn&#039;t great for your health - so is there another way?  Click the &#34;🥴 …]]></description>
										<content:encoded><![CDATA[<p>A decade ago, I was writing about how you should <a href="https://shkspr.mobi/blog/2014/01/ui-for-drunks/">test your user interface on drunk people</a>. It was a semi-serious idea.  Some of your users <em>will</em> be drunk when using your app or website. If it is easy for them to use, then it should be easy for sober people to use.</p>

<p>Of course, necking a few shots every time you update your website isn't <em>great</em> for your health - so is there another way?</p>

<p>Click the "🥴 Drunk" button at <a href="https://shkspr.mobi/blog/2025/09/drunk-css/#theme">the top of the page</a> and see what happens!</p>

<p>These are a relatively simple set of CSS rules which you can apply to any site in order to <em>simulate</em> inebriation.</p>

<p>(I may have changed these since writing the post. Check the source for the latest version.)</p>

<p>First, monkey around with the fonts. This sets all the lower-case vowels to be rendered in a different font - as discussed in "<a href="https://shkspr.mobi/blog/2025/09/targetting-specific-characters-with-css-rules/">targetting specific characters with CSS rules</a>":</p>

<pre><code class="language-css">/* Drunk */
@font-face {
    font-family: "Drunk";
    src: url("/blog/wp-content/themes/edent-wordpress-theme/assets/fonts/CommitMonoV143-Edent.woff2") format("woff2");
    /* Lower-Case Vowels */
    unicode-range: U+61, U+65, U+69, U+6F, U+75 ;
    size-adjust: 105%;
}
</code></pre>

<p>The rest of the characters will be rendered in the system's default Cursive font. Characters will also be slanted. The first character of every paragraph will be shrunk:</p>

<pre><code class="language-css">:root:has(input#drunk:checked) * {
    font-family: "Drunk", cursive;
    font-style: oblique -12deg;
    text-align: end;
}
:root:has(input#drunk:checked) p::first-letter {
    font-size: .5em;
}
</code></pre>

<p>Next, use the child selectors to rotate and skew various elements. While we wait for <a href="https://webkit.org/blog/17285/rolling-the-dice-with-css-random/">CSS randomness to come to all browsers</a> this is a simple way to select various elements:</p>

<pre><code class="language-css">:root:has(input#drunk:checked) *:nth-child(3n) {
    transform: rotate(2deg);
}
:root:has(input#drunk:checked) *:nth-child(5n) {
    transform: skew(5deg, 5deg);
}
:root:has(input#drunk:checked) *:nth-child(7n) {
    transform: rotate(-3deg);
}
</code></pre>

<p>Make the entire page blurred and saturate the colours:</p>

<pre><code class="language-css">:root:has(input#drunk:checked) body {
    filter: blur(1px) saturate(2.5);
}
</code></pre>

<p>Make any hyperlink harder to click by having it gently bounce up and down:</p>

<pre><code class="language-css">:root:has(input#drunk:checked) a  {
    animation-name: bounce;
    animation-duration: 4s;
    animation-direction: alternate;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
}
@keyframes bounce {
    0%   { margin-top:  0px; }
    25%  { margin-top:-10px; }
    50%  { margin-top:  0px; }
    75%  { margin-top: 10px; }
    100% { margin-top:  0px; }
}
</code></pre>

<p>Does this <em>really</em> simulate drunkenness? No. It is a pale simulacrum. What it is, however, is deliberately inaccessible to the majority of people.</p>

<p>How does it make you feel using the site in Drunk-Mode? Does it frustrate you? Do your eyes hurt due to the garish colour scheme? Do you keep missing the thing that you try and click on? Are the words so hard to read that it takes you extra time to do anything useful? Will you recommend this experience to your friends and family?</p>

<p>I've written before about <a href="https://shkspr.mobi/blog/2019/07/i-feel-hopeless-rejected-and-a-burden-on-society-one-week-of-empathy-training/">cosplaying as being disabled</a>. Strapping on a pair of <a href="https://www.lowvisionsimulators.com/products/glaucoma-rp-simulators">Glaucoma Goggles</a> will give you an idea of what a visual impairment is like. But it won't give you the experience of living that way for months or years.</p>

<p>You should test your stuff with people who have cognitive impairments or physical disabilities. Find out how usable your site is for someone lacking fine motor control or for those with learning disabilities. Pay disable people to take part in usability studies. Integrate their feedback.</p>

<p>Faffing around with CSS will only get you so far.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=62987&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/09/drunk-css/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Targetting specific characters with CSS rules]]></title>
		<link>https://shkspr.mobi/blog/2025/09/targetting-specific-characters-with-css-rules/</link>
					<comments>https://shkspr.mobi/blog/2025/09/targetting-specific-characters-with-css-rules/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 23 Sep 2025 11:34:09 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=62760</guid>

					<description><![CDATA[You can&#039;t. There is no way to use CSS to apply a style to every letter &#34;E&#34;. It simply can&#039;t be done.  At least, that&#039;s what they want you to think…  What if I told you there was a secret and forbidden way to target specific characters in text and apply some styles to them?  As part of my experiments in creating a &#34;drunk&#34; CSS theme, I thought it would be useful to change the presentation of s…]]></description>
										<content:encoded><![CDATA[<p>You can't. There is no way to use CSS to apply a style to every letter "E". It simply can't be done.</p>

<p>At least, that's what <em>they</em> want you to think…</p>

<p>What if I told you there was a secret and forbidden way to target specific characters in text and apply <em>some</em> styles to them?</p>

<p>As part of my experiments in creating a "drunk" CSS theme, I thought it would be useful to change the presentation of specific characters. Wouldn't it be <em>fun</em> to have every letter "a" look slightly different to the rest of the text?!</p>

<p>So here's how you can apply <em>limited</em> CSS styles to certain characters while leaving the rest of the text unchanged, and without having to wrap characters in extra markup.</p>

<pre><code class="language-css">@font-face {
    font-family: "Different";
    src: url("whatever.woff2") format("woff2");
    /* Lower-Case Vowels */
    unicode-range: U+61, U+65, U+69, U+6F, U+75 ;
}
body {
    font-family: "Different", sans;
}
</code></pre>

<p>This creates a new font-family called "Different". It loads a unique font. It is applied to <em>specific</em> Unicode characters - in this case: a, e, i , o, and u.</p>

<p>The body places this font-family <em>first</em> and then defaults to a different family.  This means all the lower-case vowels will use one font, and every other character will use something else.</p>

<p>That's… OK. I guess? Having certain characters as Garamond and the others as Times New Roman isn't exactly exciting, is it?</p>

<p>Sadly, there only other thing we can do in CSS to spice things up is to monkey around with <code>size-adjust</code> which lets the text be scaled up or down.</p>

<p>But modern fonts are pretty magic, you know!</p>

<p>The WOFF2 format has a new(ish) <a href="https://learn.microsoft.com/en-us/typography/opentype/spec/colr">COLR table</a> which allows you to create multi-coloured fonts. That means it is possible to target specific characters and have them display in living colour.</p>

<p>For example, using this <a href="https://fontstruct.com/fontstructions/show/2469233/street-fighter-ii-large-1">colourful pixel font</a> by <a href="https://www.splintered.co.uk/">Patrick H. Lauke</a> (<a href="http://creativecommons.org/licenses/by/3.0/">CC BY</a>), I can target the Unicode Range of upper-case characters.</p>

<style style="display:block;white-space: break-spaces;font-family: mono;">
@font-face {
 font-family: "colrWOFF2";
 src: url("/blog/wp-content/uploads/2025/09/street-fighter-ii-large-colour.colr.ttf.woff2") format("woff2");
 unicode-range: U+0041-005A ;
}
.colrW{
 font-family:"colrWOFF2", monospace;
}</style>

<p><span class="colrW">The above CSS only changes the appearance of UPPER Case characters!</span></p>

<p>To wrap things up - yes, you can target specific characters with CSS rules. Sadly, you're pretty much limited to fiddling around with their fonts.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=62760&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/09/targetting-specific-characters-with-css-rules/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Class Warfare! Can I eliminate CSS classes from my HTML?]]></title>
		<link>https://shkspr.mobi/blog/2025/09/class-warfare-can-i-eliminate-css-classes-from-my-html/</link>
					<comments>https://shkspr.mobi/blog/2025/09/class-warfare-can-i-eliminate-css-classes-from-my-html/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 19 Sep 2025 11:34:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[schema.org]]></category>
		<category><![CDATA[semantic web]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=63392</guid>

					<description><![CDATA[I recently read a brilliantly provocative blog post called &#34;This website has no class&#34;. In it, Adam Stoddard makes the case that you might not need CSS classes on a modern website:  I think constraints lead to interesting, creative solutions […]. Instead of relying on built in elements a bit more, I decided to banish classes from my website completely.  Long time readers will know that I&#039;m a big f…]]></description>
										<content:encoded><![CDATA[<p>I recently read a brilliantly provocative blog post called "<a href="https://aaadaaam.com/notes/no-class/">This website has no class</a>". In it, Adam Stoddard makes the case that you might not need CSS classes on a modern website:</p>

<blockquote><p>I think constraints lead to interesting, creative solutions […]. Instead of relying on built in elements a bit more, I decided to banish classes from my website completely.</p></blockquote>

<p>Long time readers will know that I'm a big fan of using semantic HTML where possible. If you peek beneath the curtain of this website you'll only see a handful of <code>&lt;div&gt;</code> elements (mostly because WordPress hardcodes them) - all the other blocks are fully semantic. Regrettably, there are rather too many <code>&lt;span&gt;</code> elements for my liking - normally for accessibility or for supplementing the metadata.</p>

<p>Overall, my CSS contained about 134 rules which selected based on class. Is that a lot? It <em>feels</em> like a lot.</p>

<p>On the one hand, classes are an easy way of splitting and grouping elements. Some <code>&lt;img&gt;</code>s should be displayed one way, the rest another. There's no semantic way to say "This is a hero image and should take up the full width, but this is an icon and should float discretely to the right."</p>

<p>But, on the other hand, <em>why</em> do we need classes?  Keith Cirkel's excellent post "<a href="https://www.keithcirkel.co.uk/css-classes-considered-harmful/">CSS Classes considered harmful</a>" goes through their history and brings together some proposed solutions for replacing them. I think his idea of using <code>data-</code> attributes is a neat hack - but ultimately isn't much different from using classes. It's still a scrap of metadata to be tied into a style-sheet.</p>

<p>Classes are great for when you <em>reuse</em> something.  I have multiple <code>&lt;section&gt;</code> elements but most don't share anything in common with the others. So they probably oughtn't have classes.</p>

<p>Removing classes has some advantages. It makes the HTML fractionally smaller, sure, but it also forces the author to think about the logical structure of their page and the semantics behind it.</p>

<p>Looking through my HTML, lots of classes exist because of laziness. If I want to position all the <code>&lt;time&gt;</code> elements which are within a comment, I don't <em>need</em> to write <code>&lt;time class="whatever"&gt;</code> and to pair it with <code>.whatever { … }</code>. Instead, I can use modern CSS selectors and say <code>#comments time { … }</code>.</p>

<p>But this leads me on to another existential question.</p>

<h2 id="are-ids-necessary-in-modern-html"><a href="https://shkspr.mobi/blog/2025/09/class-warfare-can-i-eliminate-css-classes-from-my-html/#are-ids-necessary-in-modern-html">Are IDs necessary in modern HTML?</a></h2>

<p>Mayyyyybe? I only have one <code>&lt;main&gt;</code> element, so an ID on there is unnecessary. <code>&lt;input&gt;</code> elements need IDs in order to be properly targetted by <code>&lt;label&gt;</code>s - but the label can wrap around the input. I have multiple <code>&lt;aside&gt;</code> elements because there's no semantic <code>&lt;widget&gt;</code> element, so they need unique IDs.</p>

<p>In theory, as suggested by Adam above, I could use an <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements">autonomous custom element</a> like <code>&lt;my-widget&gt;</code> - but that has none of the semantics and, frankly, feels like a bit of a cheat.</p>

<h2 id="trimming-the-fat"><a href="https://shkspr.mobi/blog/2025/09/class-warfare-can-i-eliminate-css-classes-from-my-html/#trimming-the-fat">Trimming the fat</a></h2>

<p>Any day where I can delete some code is a good day. This was an excellent exercise in going through (years) of HTML and CSS to see what cruft had built up.</p>

<p>The first CSS rule I changed was, as mentioned above:</p>

<pre><code class="language-css">time.commentmetadata {
    float: right;
}
</code></pre>

<p>Which became:</p>

<pre><code class="language-CSS">#comments time { 
    float: right;
}
</code></pre>

<p>Classless and slightly more brief. Is it more readable? Having the fact it was about the metadata in a class could have been slightly useful - but if I thought it would be confusing, I could stick a <code>/* comment */</code> in there.</p>

<p>Next, I found <code>&lt;nav class="navigation posts-navigation"&gt;</code> - what a tautology! I have multiple <code>&lt;nav&gt;</code> elements, it is true. But none of them have the same style. So this swiftly became <code>&lt;nav id="posts-navigation"&gt;</code> with an accompanying CSS rewrite.</p>

<p>My theme switcher had a bunch of <code>&lt;label class=button&gt;</code>s. They were all within a container with a unique ID, so could they be changed? Yes. But seeing the class name in the HTML is a good reminder to the author of <em>how</em> they are meant to display. Does that co-mingle content and presentation too much?</p>

<p>Some of the WordPress default classes are ridiculous. The <code>body_class()</code> function injected this into every <code>&lt;body&gt;</code></p>

<p><code>"wp-singular post-template-default single single-post postid-62959 single-format-standard wp-theme-edent-wordpress-theme"</code></p>

<p>Most of that is redundant - what's the difference between single and single-post? For my purposes, nothing! So they were all yeeted into the sun.</p>

<p>Rather than targetting IDs or classes, I targetted the presence or absence of Schema.org microdata.</p>

<p>For example:</p>

<pre><code class="language-css">main[itemprop="blogPost"] { … }
main:not([itemprop="blogPost"]) { … }
</code></pre>

<p>This can go to the extreme. I have lots of comments, each one has an author, the author's details are wrapped in <code>&lt;div class="authordetails"&gt;…&lt;/div&gt;</code></p>

<p>That can be replaced with:</p>

<pre><code class="language-css">/* Comment Author */
li[itemtype="https://schema.org/Comment"] &gt; article &gt; div[itemprop="https://schema.org/author"] {
    margin-bottom: 0;
}
</code></pre>

<p>Is that <em>sensible</em>? It is more semantic, but feels a bit brittle.</p>

<p>Parent selector are also now a thing. If I want a paragraph to have centred text but <em>only</em> when there's a submit button inside it:</p>

<pre><code class="language-css">p:has(input#submit) {
  text-align: center;
}
</code></pre>

<p>Again, am I sure that my button will always be inside a paragraph?</p>

<p>Similarly, <a href="https://css-tricks.com/child-and-sibling-selectors/">sibling selectors</a> are sometimes superior - but they do suppose that your layout never changes.</p>

<h2 id="what-remains"><a href="https://shkspr.mobi/blog/2025/09/class-warfare-can-i-eliminate-css-classes-from-my-html/#what-remains">What remains?</a></h2>

<p>There are some bits of this site which are reusable and do need classes. The code-highlighting you see above requires text to be wrapped in spans with specific classes.</p>

<p>Image alignment was also heavily class based.</p>

<p>There are some accessibility things which are either hidden or exposed using classes.</p>

<p>A bunch of WordPress defaults use classes and, even if they are redundant, it's hard to exorcise them.</p>

<p>As much as I would have liked to get rid of all my IDs, many needed to stay for linking as well as CSS targetting.</p>

<p>All told, the changes I made were:</p>

<ul>
<li>134 class selectors down to about 65.</li>
<li>35 ID selectors up to about 50.</li>
<li>5 attribute selectors up to to about 20.</li>
<li>Deleted or combined a lot of redundant CSS and tidied up my markup considerably.</li>
</ul>

<p>I have around 250 CSS rules, so now the majority target semantics rather than classes or IDs.</p>

<h2 id="is-this-really-necessary"><a href="https://shkspr.mobi/blog/2025/09/class-warfare-can-i-eliminate-css-classes-from-my-html/#is-this-really-necessary">Is this really necessary?</a></h2>

<p>No, of course not. This is an exercise in minimalism, creativity, and constraint. Feel free to litter your HTML with whatever attributes you want!</p>

<p>As I went through, it increasingly became apparent that I was fitting my CSS to my HTML's logical structure rather than to its <em>conceptual</em> structure.</p>

<p>Previously, my comments were targetted with a class. Now they have the slightly more tangled targetting of "divs with this schema attribute whose parent is an article and whose grandparent has this ID".</p>

<p>It is a delightful meditative exercise to go through your code and deeply consider whether something is unique, reusable, or obsolete.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63392&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/09/class-warfare-can-i-eliminate-css-classes-from-my-html/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Decorative text within HTML]]></title>
		<link>https://shkspr.mobi/blog/2025/05/decorative-text-within-html/</link>
					<comments>https://shkspr.mobi/blog/2025/05/decorative-text-within-html/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 25 May 2025 11:34:29 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=60444</guid>

					<description><![CDATA[Back in 2020, Andy Bell introduced me to the idea of grouping attribute values.  You&#039;ve probably seen something like this before:  &#60;article   class=&#34;card-section-background1-colorRed&#34; &#62;&#60;/article&#62;   A single class over-encumbered by all sorts of things.  The more modular way to write this would be:  &#60;article   class=&#34;card section box bg-base color-primary&#34; &#62;&#60;/article&#62;   That&#039;s pretty good! Each…]]></description>
										<content:encoded><![CDATA[<p>Back in 2020, Andy Bell introduced me to the idea of <a href="https://piccalil.li/blog/cube-css/#grouping">grouping attribute values</a>.</p>

<p>You've probably seen something like this before:</p>

<pre><code class="language-html">&lt;article
  class="card-section-background1-colorRed"
&gt;&lt;/article&gt;
</code></pre>

<p>A single class over-encumbered by all sorts of things.  The more modular way to write this would be:</p>

<pre><code class="language-html">&lt;article
  class="card section box bg-base color-primary"
&gt;&lt;/article&gt;
</code></pre>

<p>That's pretty good! Each one of those classes can have its own bit of CSS and everyone is happy. But… sometimes it is hard to spot the gaps. Is that a - or a spec of dirt on your screen?  Is there a way to make it more visually obvious what the groupings are?</p>

<p>Andy proposed this:</p>

<pre><code class="language-html">&lt;article
  class="[ card ] [ section box ] [ bg-base color-primary ]"
&gt;&lt;/article&gt;
</code></pre>

<p>Or, if you don't like brackets, this:</p>

<pre><code class="language-html">&lt;article
  class="card | section box | bg-base color-primary"
&gt;&lt;/article&gt;
</code></pre>

<p>The nice thing about attributes values is that they can contain <em>any</em> character. <a href="https://html.spec.whatwg.org/multipage/dom.html#attribute-text">The spec says</a>:</p>

<blockquote><p>An attribute value is a string. Except where otherwise specified, attribute values on HTML elements may be any string value, including the empty string, and there is no restriction on what text can be specified in such attribute values.</p></blockquote>

<p>Obviously there are some little gotchas. Quotes may need to be encoded, and some attributes only take specific variables. For the <code>class</code> attribute, however, <a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#set-of-space-separated-tokens">the spec says</a> they can have:</p>

<blockquote><p>A set of space-separated tokens is a string containing zero or more words (known as tokens) separated by one or more ASCII whitespace, where words consist of any string of one or more characters, none of which are ASCII whitespace.</p></blockquote>

<p>If a string isn't referenced within the CSS, it is simply ignored. So let's get creative!</p>

<h2 id="space-cowboy"><a href="https://shkspr.mobi/blog/2025/05/decorative-text-within-html/#space-cowboy">Space Cowboy</a></h2>

<p>You can space your variables however you like. These are all perfectly valid and (might) be easier for a human to read.</p>

<p>Separating out primary and secondary classes:</p>

<pre><code class="language-html">&lt;article
  class="card             section box  bg-base color-primary"
&gt;&lt;/article&gt;
</code></pre>

<p>Newline classes:</p>

<pre><code class="language-html">&lt;article
  class="card
         section
         box
         bg-base
         color-primary"
&gt;&lt;/article&gt;
</code></pre>

<p>Vertically aligned classes:</p>

<pre><code class="language-html">&lt;article
  class="card 
            section
            box
         bg-base 
            color-primary"
&gt;&lt;/article&gt;
</code></pre>

<h2 id="specific-call-outs"><a href="https://shkspr.mobi/blog/2025/05/decorative-text-within-html/#specific-call-outs">Specific call-outs</a></h2>

<p>Remember, you can have <em>any</em> text in your class names. If you need to highlight something specific to a human, you could use emoji:</p>

<pre><code class="language-html">&lt;article
  class="card ➡️ section box ⬅️ bg-base color-primary"
&gt;&lt;/article&gt;
</code></pre>

<p>Or</p>

<pre><code class="language-html">&lt;article
  class="card 👉 section box 👈 bg-base color-primary"
&gt;&lt;/article&gt;
</code></pre>

<h2 id="unicode-abuses"><a href="https://shkspr.mobi/blog/2025/05/decorative-text-within-html/#unicode-abuses">Unicode Abuses</a></h2>

<p>Unicode contains lots of <a href="https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols">mathematical symbols which <em>look</em> like letters</a> but aren't. You <em>could</em> write something like:</p>

<pre><code class="language-html">&lt;article
  class="𝐜𝐚𝐫𝐝 𝑠𝑒𝑐𝑡𝑖𝑜𝑛 𝒃𝒐𝒙 𝘣𝘨-𝘣𝘢𝘴𝘦 c𝐨l𝐨r-p𝐫i𝐦a𝐫y"
&gt;&lt;/article&gt;
</code></pre>

<p>But I wouldn't recommend it; you would need to change your CSS to target those particular values.</p>

<h2 id="commenting"><a href="https://shkspr.mobi/blog/2025/05/decorative-text-within-html/#commenting">Commenting</a></h2>

<p>All code should be self commenting. HTML allows <code>&lt;!-- comments in code --&gt;</code> but there's nothing stopping you from adding comments <em>inside</em> values.</p>

<pre><code class="language-html">&lt;article
  class="
    'Cards_updated_with_2025_setting'
     card
    //section_box_to_be_deprecated_next_year
     section box
    #Colours_set_in_primary.css
     bg-base color-primary"
&gt;&lt;/article&gt;
</code></pre>

<p>I'd suggesting using underscore spacing to keep things readable and avoid having words which are accidentally class names.</p>

<p>Or, go artstic:</p>

<pre><code class="language-html">&lt;article
  class="
     / \
    / _ \
   | / \ |
   ||   || _______
   ||   || |\     \
   ||   || ||\     \
   ||   || || \    |
   ||   || ||  \__/
   ||   || ||   ||
    \\_/ \_/ \_//
   /   _     _   \
  /               \  Don't change this
  |    0     0    |  code without first
  |   \  ___  /   |  speaking to Sam 
 /     \ \_/ /     \ in front-end.
/  -----  |  --\    \
|     \__/|\__/ \   |
\       |_|_|       /
 \_____       _____/
       \     /
       |     |
     card section box bg-base color-primary"
&gt;&lt;/article&gt;
</code></pre>

<p>Yes. That is perfectly valid HTML. It may not be <em>sensible</em>, but it won't cause any problems in the browser. <a href="https://mastodon.social/@Edent/114410839719196560">It might make people grumpy though</a>.</p>

<h2 id="caveats"><a href="https://shkspr.mobi/blog/2025/05/decorative-text-within-html/#caveats">Caveats</a></h2>

<p>There are a few things to be aware of here:</p>

<ul>
<li>Optimisers might strip spaces.</li>
<li>Pre-processes might re-order values.</li>
<li>This is unusual and humans might get confused.</li>
</ul>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=60444&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/05/decorative-text-within-html/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[HTML Oddities: Is a newline just another whitespace in attribute values?]]></title>
		<link>https://shkspr.mobi/blog/2025/04/html-oddities-is-a-newline-just-another-whitespace-in-attribute-values/</link>
					<comments>https://shkspr.mobi/blog/2025/04/html-oddities-is-a-newline-just-another-whitespace-in-attribute-values/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 27 Apr 2025 11:34:02 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=59686</guid>

					<description><![CDATA[Consider these two HTML elements:  &#60;div class=&#34;a b&#34;&#62;…&#60;/div&#62;  &#60;div class=&#34;a b&#34;&#62;…&#60;/div&#62;   Is there any semantic difference between them? Is there any way to target one but not the other? In other words, are they logically different?  I think the answer is no. On every browser I&#039;ve tested, both are the same. Whether using JS or CSS, there&#039;s no difference between them.  You could replace every \n wit…]]></description>
										<content:encoded><![CDATA[<p>Consider these two HTML elements:</p>

<pre><code class="language-html">&lt;div class="a b"&gt;…&lt;/div&gt;

&lt;div class="a
b"&gt;…&lt;/div&gt;
</code></pre>

<p>Is there any <em>semantic</em> difference between them? Is there any way to target one but not the other? In other words, are they logically different?</p>

<p>I <em>think</em> the answer is no. On every browser I've tested, both are the same. Whether using JS or CSS, there's no difference between them.  You could replace every <code>\n</code> with a <code> </code> and nothing would break.</p>

<p>But is that true for <em>every</em> attribute? Are there some attributes where a newline is *significant"?</p>

<p>For the vast majority of attributes, the answer is no.  Consider the <code>alt</code> attribute for providing alternate text on images.  This:</p>

<pre><code class="language-html">&lt;img src="" alt="First line.
Second Line.

Forth line."&gt;
</code></pre>

<p>When rendered by a browser, the newlines become spaces. See:</p>

<img src="" alt="First line.
Second Line.
Forth line.">

<p>But there's are <em>three</em> attributes where newlines <em>do</em> matter. Can you work out what they are?</p>

<h2 id="title"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-is-a-newline-just-another-whitespace-in-attribute-values/#title">Title</a></h2>

<p><span title="First line.
Second Line.

Forth line.">Hover your cursor over this text and a title will appear</span>.  It will look something like:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/04/title.webp" alt="Title text showing multiple lines." width="704" height="303" class="aligncenter size-full wp-image-59697">

<p>The HTML specification has a section on "<a href="https://infra.spec.whatwg.org/#ascii-whitespace">space-separated tokens</a>" which it defines as "<a href="https://infra.spec.whatwg.org/#ascii-whitespace">ASCII whitespace</a>":</p>

<ul>
<li>U+0009 TAB</li>
<li>U+000A LF</li>
<li>U+000C FF</li>
<li>U+000D CR</li>
<li>U+0020 SPACE</li>
</ul>

<p>So tab, any newline, and space are all equivalent when it comes to tokenisation of content.</p>

<p>However, for <code>title</code> specifically:</p>

<blockquote><p>If the title attribute's value contains U+000A LINE FEED (LF) characters, the content is split into multiple lines. Each U+000A LINE FEED (LF) character represents a line break.</p>

<p><a href="https://html.spec.whatwg.org/multipage/dom.html#attr-title">3.2.6.1 The title attribute</a></p></blockquote>

<h2 id="placeholder"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-is-a-newline-just-another-whitespace-in-attribute-values/#placeholder">Placeholder</a></h2>

<p>There's another similar case:</p>

<p><textarea rows="4" placeholder="First line.
Second Line.

Forth line."></textarea></p>

<p>The good old <code>&lt;textarea&gt;</code> element has a <code>placeholder</code> attribute. That also allows newlines - although in a subtly different way to the title element!</p>

<blockquote><p>All U+000D CARRIAGE RETURN U+000A LINE FEED character pairs (CRLF) in the hint, as well as all other U+000D CARRIAGE RETURN (CR) and U+000A LINE FEED (LF) characters in the hint, must be treated as line breaks when rendering the hint.</p>

<p><a href="https://html.spec.whatwg.org/#the-textarea-element:concept-fe-value-4">4.10.11 The textarea element</a></p></blockquote>

<p>Quite why carriage returns are allowed here, but not in <code>title</code>, I don't know!</p>

<p>Also note, the <code>textarea</code>'s placeholder is different from the <code>&lt;input&gt;</code>'s placeholder, which <a href="https://html.spec.whatwg.org/#the-placeholder-attribute"><em>doesn't</em> support newlines</a>.</p>

<h2 id="id"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-is-a-newline-just-another-whitespace-in-attribute-values/#id">ID</a></h2>

<p>I warn you though, this one is pretty nasty!</p>

<p>Consider this piece of HTML:</p>

<pre><code class="language-html">&lt;p id="test
"&gt;Hello&lt;/p&gt;
</code></pre>

<p>I know! What sort of sicko would include a newline in their ID?!  But, it turns out, that is <em>significant</em>.</p>

<p>Try to select that element using CSS like:</p>

<pre><code class="language-css">#test {
   color: red;
}
</code></pre>

<p>It won't work! The <em>literal</em> ID is <strong>not</strong> <code>test</code>.  If you run:</p>

<pre><code class="language-js">document.querySelector("p")
</code></pre>

<p>It will return <code>&lt;p id="test\n"&gt;</code> - which means you can only select it with:</p>

<pre><code class="language-js">document.getElementById("test\n")
</code></pre>

<p>Or with CSS using <a href="https://www.w3.org/TR/CSS2/syndata.html#characters">special character selectors</a>:</p>

<pre><code class="language-css">#test\a {
  color: blue;
}
</code></pre>

<p><a href="https://html.spec.whatwg.org/#global-attributes:the-id-attribute-3">The spec says</a></p>

<blockquote><p>The id attribute specifies its element's unique identifier (ID).</p>

<p>There are no other restrictions on what form an ID can take; in particular, IDs can consist of just digits, start with a digit, start with an underscore, consist of just punctuation, etc.</p></blockquote>

<p>While it doesn't specifically mention newlines, it seems clear that the attribute can contain *anything".</p>

<h2 id="any-others"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-is-a-newline-just-another-whitespace-in-attribute-values/#any-others">Any others?</a></h2>

<p>I'm pretty sure those three are the only attributes which treat newlines in their values as significant.  Think I'm wrong? Please leave a comment.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=59686&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/04/html-oddities-is-a-newline-just-another-whitespace-in-attribute-values/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Using Tempest Highlight with WordPress]]></title>
		<link>https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/</link>
					<comments>https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 26 Apr 2025 11:34:19 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=59866</guid>

					<description><![CDATA[I like to highlight bits of code on my blog. I was using GeSHi - but it has ceased to receive updates and the colours it uses aren&#039;t WCAG compliant.  After skimming through a few options, I found Tempest Highlight. It has nearly everything I want in a code highlighter:        PHP with no 3rd party dependencies.      Lots of common languages.      Modern, with regular updates.      Easy to use fun…]]></description>
										<content:encoded><![CDATA[<p>I like to highlight bits of code on my blog. I <em>was</em> using <a href="https://shkspr.mobi/blog/2025/04/a-small-php-update-to-geshi/">GeSHi</a> - but it has ceased to receive updates and the colours it uses aren't WCAG compliant.</p>

<p>After skimming through a few options, I found <a href="https://github.com/tempestphp/highlight">Tempest Highlight</a>. It has <em>nearly</em> everything I want in a code highlighter:</p>

<ul style="list-style-type: &quot;✅&quot;;">
    <li>&nbsp;PHP with no 3rd party dependencies.</li>
    <li>&nbsp;Lots of common languages.</li>
    <li>&nbsp;Modern, with regular updates.</li>
    <li>&nbsp;Easy to use functions.</li>
    <li>&nbsp;Range of difference style sheets.</li>
</ul>

<p>But, on the downside:</p>

<ul style="list-style-type: &quot;❌&quot;;">
    <li>&nbsp;No WordPress plugin.</li>
    <li>&nbsp;Not all languages supported.</li>
    <li>&nbsp;CSS embedded in HTML.</li>
</ul>

<p>I can live without some esoteric languages, but I don't really want to run <code>composer install</code> on my blog. I just want a quick WordPress plugin.  So, here's how I did it.</p>

<p></p><nav role="doc-toc"><menu><li><h2 id="table-of-contents"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#table-of-contents">Table of Contents</a></h2><menu><li><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#here-be-dragons">Here Be Dragons</a></li><li><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#the-art-of-loading-without-loading">The Art of Loading without Loading</a></li><li><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#testing">Testing</a></li><li><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#draw-the-rest-of-the-owl">Draw The Rest of the Owl</a></li><li><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#todo">ToDo</a></li><li><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#get-the-code">Get the code</a></li></menu></li></menu></nav><p></p>

<h2 id="here-be-dragons"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#here-be-dragons">Here Be Dragons</a></h2>

<p>This is a quick prototype. It has an audience of one; me. It may break in unexpected ways. Use at your own risk.</p>

<p>The file layout is relatively simple:</p>

<pre><code class="language-_">WordPress Plugins
├── Highlight_Plugin
│&nbsp;&nbsp; ├── src/
│&nbsp;&nbsp; ├── autoload.php
│&nbsp;&nbsp; ├── index.php
│&nbsp;&nbsp; └── base.css
</code></pre>

<p>The <code>src/</code> directory contains the <code>src/</code> directory from <a href="https://github.com/tempestphp/highlight">Tempest Highlight</a>.</p>

<h2 id="the-art-of-loading-without-loading"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#the-art-of-loading-without-loading">The Art of Loading without Loading</a></h2>

<p>Normally, to install a PHP package, the <code>composer</code> app creates an autoloader which will magically import everything you need into your project.  We can't do that here. Instead, we need to manually load the library.</p>

<p>Create a file in the plugin's directory called <code>autoload.php</code> - its job is to autoload everything in the <code>src/</code> directory.</p>

<pre><code class="language-php">&lt;?php
spl_autoload_register( function ( $class ) {
    //  Project-specific namespace prefix
    $prefix = "Tempest\\Highlight\\";

    //  Base directory for the namespace prefix
    $base_dir = __DIR__ . "/src/";

    //  Does the class use the namespace prefix?
    $len = strlen( $prefix );
    if ( strncmp( $prefix, $class, $len ) !== 0) {
        //  No, move to the next registered autoloader
        return;
    }

    //  Get the relative class name
    $relative_class = substr( $class, $len );

    //  Replace namespace separators with directory separators, append with .php
    $file = $base_dir . str_replace( "\\", "/", $relative_class ) . ".php";

    //  If the file exists, require it
    if ( file_exists( $file ) ) {
        require $file;
    }
});
</code></pre>

<p>I don't know if that's the <em>easiest</em> way to do it. But it works!</p>

<h2 id="testing"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#testing">Testing</a></h2>

<p>The <code>index.php</code> file can now be tested:</p>

<pre><code class="language-php">//  Load the Tempest Highlight library
require_once __DIR__ . "/autoload.php";

//  Set up the namespace
use Tempest\Highlight\Highlighter;

//  Define the theme.
$theme = new Tempest\Highlight\Themes\InlineTheme( __DIR__ . "/src/Themes/Css/light-plus.css");

//  Create the highlighter.
$highlighter = new Tempest\Highlight\Highlighter( $theme );

//  Print some formatted HTML
echo $highlighter-&gt;parse("&lt;em id='foo' class='bar'&gt;test&lt;/em&gt;", "html" );
</code></pre>

<p>All being well, that should produce this:</p>

<pre><code class="language-_">&amp;lt;&lt;span style="color: #0000ff;"&gt;em&lt;/span&gt; id='foo' class='bar'&amp;gt;test&amp;lt;/&lt;span style="color: #0000ff;"&gt;em&lt;/span&gt;&amp;gt;
</code></pre>

<p>That has the CSS embedded. Not ideal, but certainly good enough.  I picked "light-plus" because it was the only theme which seemed to meet at least WCAG AA when on a white background.</p>

<p>OK, so how do we go from printing out a scrap of HTML to extracting all the code snippets from a WordPress blog?</p>

<h2 id="draw-the-rest-of-the-owl"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#draw-the-rest-of-the-owl">Draw The Rest of the Owl</a></h2>

<p>In <em>theory</em> the code is relatively straightforward.</p>

<h3 id="find-code-snippets"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#find-code-snippets">Find code snippets</a></h3>

<p>My <a href="https://codeberg.org/edent/markdown-extra-unofficial/">Markdown plugin</a> transforms this:</p>

<pre><code class="language-_"> ```javascript
 var a = 2.0;
 ``` 
</code></pre>

<p>Into this:</p>

<pre><code class="language-html">&lt;pre&gt;&lt;code class="language-javascript"&gt;
var a = 2.0;
&lt;/code&gt;&lt;/pre&gt;
</code></pre>

<p>No need to use a regex, the new PHP 8.4 HTMLDocument gives us direct programmatic access to the HTML.</p>

<pre><code class="language-php">//  Load the content into PHP 8.4's HTML DOM.
$dom = Dom\HTMLDocument::createFromString( $content, LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED, "UTF-8" );

//  Select the code snippets.
//  `&lt;pre&gt;&lt;code class="language-*"&gt;`
$codeSnippets = $dom-&gt;querySelectorAll( "pre&gt;code[class^=language-]" );
</code></pre>

<h3 id="replace-the-snippets"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#replace-the-snippets">Replace the snippets</a></h3>

<p>From the above, I have the language and code, so it can "easily" be replaced.</p>

<pre><code class="language-php">//  Iterate through each snippet.
foreach ( $codeSnippets as $code ) {
    //  Get the HTML from within the &lt;code&gt;.
    $originalCode = $code-&gt;textContent;
    //  Replace the contents of &lt;code&gt; with the highlighted HTML.
    $code-&gt;innerHTML = $highlighter-&gt;parse( $originalCode, $language )
}
</code></pre>

<p>Replacing the code in that node manipulates the original DOM.  Which means, after looping through all the snippets, I can return the altered HTML like so:</p>

<pre><code class="language-php">return $dom-&gt;saveHTML();
</code></pre>

<h3 id="and-then"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#and-then">And then…</a></h3>

<p>Obviously, there's a bit more too it than that. It ignores RSS feeds, it adds a base CSS style to the head, some SVGs get embedded, semantic metadata is included, and it all gets a bit tangled and complicated.</p>

<h2 id="todo"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#todo">ToDo</a></h2>

<p>A few things need to happen to make this even better.</p>

<ul>
<li>Encoded comments as well and posts.</li>
<li>Add new languages.</li>
<li>Don't in-line the CSS into the HTML, but add it as a separate stylesheet.</li>
</ul>

<p>But, for now, it is running on my blog and that's good enough for me!</p>

<h2 id="get-the-code"><a href="https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/#get-the-code">Get the code</a></h2>

<p>You can <a href="https://github.com/edent/highlight">play about with the WordPress plugin</a>. Bugs reports, pull requests, and suggestions all warmly welcomed.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=59866&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/04/using-tempest-highlight-with-wordpress/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[HTML Oddities: Does the order of attribute values matter?]]></title>
		<link>https://shkspr.mobi/blog/2025/04/html-oddities-does-the-order-of-attribute-values-matter/</link>
					<comments>https://shkspr.mobi/blog/2025/04/html-oddities-does-the-order-of-attribute-values-matter/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 24 Apr 2025 11:34:56 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=59481</guid>

					<description><![CDATA[HTML elements can have attributes. For example id, class, src, alt, and many others. These attributes can contain values - an img element&#039;s src attribute has a value which is a link to an image. An id attribute&#039;s value is a single string. But some attributes can contain multiple values.  Here&#039;s a thought experiment for you. Consider these two HTML elements:  &#60;p class=&#34;alpha bravo charlie&#34;&#62;………&#60;/p&#62; …]]></description>
										<content:encoded><![CDATA[<p>HTML elements can have attributes. For example id, class, src, alt, and many others. These attributes can contain values - an img element's src attribute has a value which is a link to an image. An id attribute's value is a single string. But some attributes can contain <em>multiple</em> values.</p>

<p>Here's a thought experiment for you. Consider these two HTML elements:</p>

<pre><code class="language-html">&lt;p class="alpha bravo charlie"&gt;………&lt;/p&gt;
&lt;p class="bravo charlie alpha"&gt;………&lt;/p&gt;
</code></pre>

<p>Is there any <em>semantic</em> difference between them?  Does the ordering of the values inside the class attribute matter?</p>

<p>Both can be targetted with CSS like:</p>

<pre><code class="language-css">.bravo {
   color: red;
}
</code></pre>

<p>They can also be targetted using:</p>

<pre><code class="language-css">.charlie.bravo {
   color: green;
}
</code></pre>

<p>Or using a <a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Basic_selectors#selector_lists">Selector List</a></p>

<pre><code class="language-css">.bravo, .alpha {
   color: yellow;
}
</code></pre>

<p>So order doesn't matter, right?</p>

<h2 id="well-its-a-bit-more-complicated-than-that"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-does-the-order-of-attribute-values-matter/#well-its-a-bit-more-complicated-than-that">Well, it's a bit more complicated than that</a></h2>

<p>Consider <a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Attribute_selectors#presence_and_value_selectors">Presence Selectors</a>.</p>

<p>This CSS will <em>only</em> select the <strong>first</strong> element:</p>

<pre><code class="language-css">p[class="alpha bravo charlie"] {
  font-size: 2em;
}
</code></pre>

<p>It targets the class name in that exact order. No other.</p>

<p>Similarly, <a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Attribute_selectors#substring_matching_selectors">Substring Selectors</a> can be used in an order-specific manner.</p>

<p>This CSS will <strong>only</strong> select the <em>second</em> element:</p>

<pre><code class="language-css">p[class^="b"] {
  display: block;
}
</code></pre>

<p>It looks for a class attribute where the <em>value</em> starts with <code>b</code></p>

<h2 id="where-ordering-matters"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-does-the-order-of-attribute-values-matter/#where-ordering-matters">Where ordering matters</a></h2>

<p>The <a href="https://html.spec.whatwg.org/multipage/indices.html#attributes-3">HTML spec has a (non-normative) section on attributes</a>. Those which accept multiple values are (broadly) in three categories.</p>

<ol>
<li>A <a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#set-of-space-separated-tokens">set of space-separated tokens</a> is a string containing zero or more words (known as tokens) separated by one or more ASCII whitespace, where words consist of any string of one or more characters, none of which are ASCII whitespace.</li>
<li>An <a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#unordered-set-of-unique-space-separated-tokens">unordered set</a> of unique space-separated tokens is a set of space-separated tokens where none of the tokens are duplicated.</li>
<li>An <a href="https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#ordered-set-of-unique-space-separated-tokens">ordered set</a> of unique space-separated tokens is a set of space-separated tokens where none of the tokens are duplicated but where the order of the tokens is meaningful.</li>
</ol>

<p>The class attribute belongs to the first group. While ordering isn't meaningful, it also isn't irrelevant.</p>

<p>The only attributes which are specifically <strong>unordered</strong> are:</p>

<ul>
<li>All elements:

<ul>
<li><code>itemprop=</code>, <code>itemref=</code>, <code>itemtype=</code></li>
</ul></li>
<li><code>&lt;link&gt;</code>, <code>&lt;script&gt;</code>, <code>&lt;style&gt;</code>

<ul>
<li><code>blocking=</code></li>
</ul></li>
<li><code>&lt;output&gt;</code>

<ul>
<li><code>for=</code></li>
</ul></li>
<li><code>&lt;td&gt;</code>, <code>&lt;th&gt;</code>

<ul>
<li><code>headers=</code></li>
</ul></li>
<li><code>&lt;a&gt;</code>, <code>&lt;area&gt;</code>, <code>&lt;link&gt;</code>

<ul>
<li><code>rel=</code></li>
</ul></li>
<li><code>&lt;iframe&gt;</code>

<ul>
<li><code>sandbox=</code></li>
</ul></li>
<li><code>&lt;link&gt;</code>

<ul>
<li><code>sizes=</code></li>
</ul></li>
</ul>

<p>The <em>only</em> attribute specifically listed as "Ordered" is <a href="https://html.spec.whatwg.org/multipage/interaction.html#the-accesskey-attribute">the <code>accesskey</code> attribute</a>.</p>

<p>There are a few others which do require an order, although it is not immediately obvious.</p>

<p>Both <code>imagesrcset</code> and <code>srcset</code> require a "Comma-separated list of image candidate strings". The comma separated strings can be in any order, but the text <em>within</em> them <a href="https://html.spec.whatwg.org/multipage/images.html#image-candidate-string">has a strict ordering</a>.</p>

<p>For example:</p>

<pre><code class="language-html">&lt;img srcset="header640.png 640w, header960.png 960w, header1024.png 1024w" …
</code></pre>

<p>Similarly, the <code>type</code> attribute requires its value to be a <a href="https://mimesniff.spec.whatwg.org/#valid-mime-type">valid MIME type</a>. But <a href="https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/codecs_parameter#basic_syntax">MIME types can also include a codec</a>. So it's possible to end up with HTML like:</p>

<pre><code class="language-html">&lt;video&gt;
  &lt;source 
    src="movie.webm"
    type="video/webm; codecs='vp8, vorbis'"&gt;
</code></pre>

<p>Assuming those attributes were whitespace separated tokens would lead to nonsense!</p>

<p>The <code>autocomplete</code> type is another complex example.  The <a href="https://html.spec.whatwg.org/multipage/indices.html#attributes-3">spec</a> just says "Autofill field name and related tokens", but <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete#token-list">MDN makes it clear</a> that it requires:</p>

<blockquote><p>An ordered set of space-separated tokens consisting of autofill detail tokens preceded by optional sectioning and either billing or shipping grouping tokens. Phone numbers, email addresses, and messaging protocol tokens are preceded by a token identifying the type of recipient.</p></blockquote>

<p>For example <code>autocomplete="home email"</code> says to suggest the user's home email address whereas <code>autocomplete="work email"</code> suggests their work email.  The ordering is necessary and <code>autocomplete="email home"</code> will not be understood.</p>

<h2 id="where-else-might-ordering-matter"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-does-the-order-of-attribute-values-matter/#where-else-might-ordering-matter">Where else might ordering matter?</a></h2>

<p>Rather obviously, alt text should <em>always</em> remain in order. No one wants to read alphabetically ordered image descriptions!</p>

<p>As mentioned by <a href="https://sunny.garden/@knowler/114331801775907008">Nathan Knowler</a> some ARIA attributes require ordering for accessibility.</p>

<p>And, as <a href="https://wandering.shop/@kagan/114331745674240833">Kagan MacTane</a> pointed out, sometimes the ordering is important for a human - even if it is irrelevant for a machine.</p>

<h2 id="what-have-we-learned-today"><a href="https://shkspr.mobi/blog/2025/04/html-oddities-does-the-order-of-attribute-values-matter/#what-have-we-learned-today">What have we learned today?</a></h2>

<p>I originally asked this question on Mastodon. About two-thirds of respondents thought that attribute ordering was irrelevant.</p>

<blockquote class="mastodon-embed" data-embed-url="https://mastodon.social/@Edent/114331612009479129/embed" style="background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;"> <a href="https://mastodon.social/@Edent/114331612009479129" target="_blank" style="align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 79 75"><path d="M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z" fill="currentColor"></path></svg> <div style="color: #787588; margin-top: 16px;">Post by @Edent@mastodon.social</div> <div style="font-weight: 500;">View on Mastodon</div> </a> </blockquote>

<script data-allowed-prefixes="https://mastodon.social/" async="" src="https://mastodon.social/embed.js"></script>

<p>I hope I've demonstrated that it is slightly more complicated than it may appear at first.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=59481&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/04/html-oddities-does-the-order-of-attribute-values-matter/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Using a CSS cursor to show the external link's favicon]]></title>
		<link>https://shkspr.mobi/blog/2024/10/using-a-css-cursor-to-show-the-external-links-favicon/</link>
					<comments>https://shkspr.mobi/blog/2024/10/using-a-css-cursor-to-show-the-external-links-favicon/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 28 Oct 2024 12:34:18 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Web Development]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=53652</guid>

					<description><![CDATA[How do you know where this link goes to?  If you&#039;re on a desktop, you might notice that hovering your mouse over it displays the destination somewhere on your screen. If you&#039;re a geek, you could view the source-code of a page.  Can we improve the experience for users? Here&#039;s an attempt.  Try hovering your cursor over this link to a popular website.  This is what it should look like:    Here&#039;s how …]]></description>
										<content:encoded><![CDATA[<p>How do you know where <a href="https://google.com">this link</a> goes to?</p>

<p>If you're on a desktop, you <em>might</em> notice that hovering your mouse over it displays the destination <em>somewhere</em> on your screen. If you're a geek, you could view the source-code of a page.</p>

<p>Can we improve the experience for users? Here's an attempt.</p>

<p>Try hovering your cursor over <a href="https://google.com/" style="cursor: url(&quot;https://icons.duckduckgo.com/ip9/google.com.ico&quot;), auto;">this link to a popular website</a>.</p>

<p>This is what it should look like:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/10/hover-fs8.png" alt="A link with the Google logo hovering over it." width="500" height="46" class="aligncenter size-full wp-image-53656">

<p>Here's how it works.</p>

<h2 id="cursor-styles"><a href="https://shkspr.mobi/blog/2024/10/using-a-css-cursor-to-show-the-external-links-favicon/#cursor-styles">Cursor Styles</a></h2>

<p>CSS allows us to <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/cursor" style="cursor: url(&quot;https://icons.duckduckgo.com/ip9/developer.mozilla.org.ico&quot;), auto;">change the icon displayed by a cursor</a>.  There are dozens of built-in icons, but you can also <a href="https://drafts.csswg.org/css-ui/#cursor" style="cursor: url(&quot;https://icons.duckduckgo.com/ip9/w3.org.ico&quot;), auto;">supply your own image file</a>.</p>

<pre><code class="language-css">#link {
   cursor: url("/path/to/image.png"), auto;
}
</code></pre>

<p>Anything hovering over that link will get that .png as its cursor. Nifty!</p>

<h2 id="favicons"><a href="https://shkspr.mobi/blog/2024/10/using-a-css-cursor-to-show-the-external-links-favicon/#favicons">Favicons</a></h2>

<p>Most websites have <a href="https://en.wikipedia.org/wiki/Favicon" style="cursor: url(&quot;https://icons.duckduckgo.com/ip9/wikipedia.org.ico&quot;), auto;">a Favicon</a> - it is a little image that you see in your browser bar, or when you save a website to your favourites. It is usually found at <code>/favicon.ico</code> - but can be in a variety of places.</p>

<p>There are dozens of free and paid services which let you quickly grab a favicon from any site.</p>

<p>The one I tend to use is DuckDuckGo's service. It takes any domain name, like Google.com, and returns an icon. It looks like this <code>https://icons.duckduckgo.com/ip9/google.com.ico</code></p>

<h2 id="putting-it-all-together"><a href="https://shkspr.mobi/blog/2024/10/using-a-css-cursor-to-show-the-external-links-favicon/#putting-it-all-together">Putting it all together</a></h2>

<p>You can put the CSS anywhere - including inline with your links:</p>

<pre><code class="language-html">&lt;a
  href="https://google.com/"
  style='cursor:url("https://icons.duckduckgo.com/ip9/google.com.ico"), auto;'
&gt;Visit this website&lt;/a&gt;!
</code></pre>

<h2 id="is-this-a-good-idea"><a href="https://shkspr.mobi/blog/2024/10/using-a-css-cursor-to-show-the-external-links-favicon/#is-this-a-good-idea">Is this a good idea?</a></h2>

<p>Well… maybe? If you have lots of links to various destinations and don't want to clutter up your prose with "(Wikipedia)" or other things like that, it could be useful.</p>

<p>Not everyone will recognise the logo for every service - so it may not add anything useful.</p>

<p>It doesn't work on mobile.</p>

<p>This isn't a common UI pattern - which might be a little confusing for users.</p>

<p>Loading images from remote sites is <em>probably not</em> a security concern. But if a website is hacked, you might have unwanted images on your site.</p>

<p>A site could lie to you about its destination.</p>

<p>Automating it should be possible, but it could be a bit of a faff to maintain.</p>

<p>But it  <a href="https://disney.com/" style="cursor: url(&quot;https://icons.duckduckgo.com/ip9/disney.com.ico&quot;), auto;">looks</a>  <a href="https://openbenches.org/" style="cursor: url(&quot;https://icons.duckduckgo.com/ip9/openbenches.org.ico&quot;), auto;">so</a>  <a href="https://edent.tel/" style="cursor: url(&quot;https://icons.duckduckgo.com/ip9/edent.tel.ico&quot;), auto;">pretty</a>!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53652&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/10/using-a-css-cursor-to-show-the-external-links-favicon/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[You can use text-wrap: balance; on icons]]></title>
		<link>https://shkspr.mobi/blog/2024/10/you-can-use-text-wrap-balance-on-icons/</link>
					<comments>https://shkspr.mobi/blog/2024/10/you-can-use-text-wrap-balance-on-icons/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 20 Oct 2024 11:34:27 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=53199</guid>

					<description><![CDATA[A fun little CSS experiment!  There&#039;s a new(ish) feature in CSS which allows you to set the way text is wrapped.  Ordinarily, a long line of text might be split at an inopportune time. For example:  This very long headline ends with a single word   Having a dangling word doesn&#039;t always look great. Using text-wrap:balance would give us something like:  This very long headline ends with balanced…]]></description>
										<content:encoded><![CDATA[<p>A fun little CSS experiment!  There's a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/text-wrap#balance">new(ish) feature in CSS which allows you to set the way text is wrapped</a>.  Ordinarily, a long line of text might be split at an inopportune time. For example:</p>

<pre><code class="language-_">This very long headline ends with a single
word
</code></pre>

<p>Having a dangling word doesn't always look great. Using <code>text-wrap:balance</code> would give us something like:</p>

<pre><code class="language-_">This very long headline ends
with balanced lines
</code></pre>

<p>Hurrah!</p>

<p>But the name is, I think, slightly misleading. It doesn't <em>only</em> work on text. It will work on <em>any</em> content.  For example - I have a row of icons at the bottom of this page.  If the viewport is too narrow, a single icon might drop to the next line. That can look a bit weird.</p>

<p>Here's a video demo:</p>

<iframe title="text-wrap:balance for icons" width="560" height="315" src="https://tube.tchncs.de/videos/embed/dc43947d-855c-4eaf-a8c3-8df146b00671" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"></iframe>

<p>So feel free to use <code>text-wrap: balance;</code> on <em>any</em> items which need to be balanced over multiple lines.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53199&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/10/you-can-use-text-wrap-balance-on-icons/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Styling links based on their destination]]></title>
		<link>https://shkspr.mobi/blog/2024/10/styling-links-based-on-their-destination/</link>
					<comments>https://shkspr.mobi/blog/2024/10/styling-links-based-on-their-destination/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 03 Oct 2024 11:34:16 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=53388</guid>

					<description><![CDATA[Suppose you have lots of links on a page. You want to highlight the ones which point to example.com - is that possible in CSS without using JavaScript?  Yes!  This scrap of code will turn all those links red:  a[href^=&#34;https://example.com&#34;] {    color: red; }   Now, there are a few gotchas with this code.   It matches the string exactly. So https://example.com will not match…]]></description>
										<content:encoded><![CDATA[<p>Suppose you have lots of links on a page. You want to highlight the ones which point to <code>example.com</code> - is that possible in CSS without using JavaScript?</p>

<p>Yes!</p>

<p>This scrap of code will turn all those links red:</p>

<pre><code class="language-css">a[href^="https://example.com"] {
   color: red;
}
</code></pre>

<p>Now, there are a few gotchas with this code.</p>

<ul>
<li>It matches the string <em>exactly</em>. So <code>https://example.com</code> will <strong>not</strong> match <code>https://www.example.com</code></li>
<li>The match will occur at the <em>start</em> of the string.</li>
</ul>

<p>To match more lazily, you can use a substring match:</p>

<pre><code class="language-css">a[href*="example.com"] {
   color: red;
}
</code></pre>

<p>That will match <code>www.example.com</code> as well - but it will also match <code>https://wikipedia.org/wiki/example.com</code></p>

<h2 id="is-this-useful"><a href="https://shkspr.mobi/blog/2024/10/styling-links-based-on-their-destination/#is-this-useful">Is this useful?</a></h2>

<p>I use it to ensure all the links which point to the Internet Archive are properly labelled:</p>

<pre><code class="language-css">a[href^="https://web.archive.org"]::after {
  content: " (archived)";
  vertical-align: super;
  font-size: .9em;
}
</code></pre>

<p>For example, <a href="https://web.archive.org/web/20090122051921/https://shkspr.mobi/blog/">this link points to the earliest version of my website</a>.</p>

<p>You could also use it to indicate when a link takes you to a specific filetype (note, this may need to be <a href="https://www.w3.org/TR/selectors-4/#attribute-case">case insensitive</a>:</p>

<pre><code class="language-css">/* URl ends with .pdf or .PDF */
a[href$=".pdf" i]::after  {
  content: " (PDF)";
  vertical-align: super;
  font-size: .9em;
}
</code></pre>

<p>It is also possible to add something to show people are leaving this site:</p>

<pre><code class="language-css">a[href]:not([href^="https://shkspr.mobi" i]) {
    content: "↗️";
}
</code></pre>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53388&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/10/styling-links-based-on-their-destination/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Inline CSS - The Link "Cheat"]]></title>
		<link>https://shkspr.mobi/blog/2024/08/inline-css-the-link-cheat/</link>
					<comments>https://shkspr.mobi/blog/2024/08/inline-css-the-link-cheat/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 29 Aug 2024 11:34:42 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Web Development]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=52225</guid>

					<description><![CDATA[I am a bear of very little brains sometimes.  I had a site which, for various boring reasons, was printing a &#60;style&#62; element in the middle of the HTML&#039;s body.  Because &#60;style&#62; is a metadata element, it should only appear within the &#60;head&#62; element.  This is OK:  &#60;!doctype html&#62; &#60;html&#62;    &#60;head&#62;       &#60;style&#62; a { color: #f00; } &#60;/style&#62;    &#60;/head&#62;    &#60;body&#62;       …   This is an error:  &#60;!doctype h…]]></description>
										<content:encoded><![CDATA[<p>I am a bear of very little brains sometimes.</p>

<p>I had a site which, for various boring reasons, was printing a <code>&lt;style&gt;</code> element in the middle of the HTML's body.  Because <code>&lt;style&gt;</code> is a metadata element, <a href="https://html.spec.whatwg.org/multipage/semantics.html#the-style-element">it should only appear within the <code>&lt;head&gt;</code> element</a>.</p>

<p>This is OK:</p>

<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html&gt;
   &lt;head&gt;
      &lt;style&gt; a { color: #f00; } &lt;/style&gt;
   &lt;/head&gt;
   &lt;body&gt;
      …
</code></pre>

<p>This is an error:</p>

<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html&gt;
   &lt;head&gt;
   &lt;/head&gt;
   &lt;body&gt;
      &lt;style&gt; a { color: #f00; } &lt;/style&gt;
      …
</code></pre>

<p>Most modern browsers will parse the stylesheet and not complain. But I like doing things properly and to spec because I am a massive boring nerd who is no fun at parties.</p>

<p>There is another way to include CSS in a document - via the <code>&lt;link&gt;</code> element.</p>

<pre><code class="language-html">&lt;link rel="stylesheet" type="text/css" href="https://example.com/style.css"&gt;
</code></pre>

<p>It is <a href="https://html.spec.whatwg.org/multipage/links.html#body-ok"><em>sometimes</em> OK to have a <code>&lt;link&gt;</code> element in the <code>&lt;body&gt;</code></a> - but can you spot the problem? Yes! The <code>href</code> points to an external resource. That's no good because we want to put the CSS <em>inline</em>.</p>

<p>The <code>href</code> needs to point to a URl. But it can point to <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs">a <em>data</em> URl</a>!!!!</p>

<p>So, if the CSS is first Base64 encoded, it's possible to place inline CSS <em>within</em> the body of the HTML document!!!!!!!</p>

<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html&gt;
   &lt;head&gt;
   &lt;/head&gt;
   &lt;body&gt;
      &lt;link rel="stylesheet" type="text/css" href="data:text/css;base64,Q29uZ3JhdHVsYXRpb25zISBZb3UgZm91bmQgbXkgc2VjcmV0IG1lc3NhZ2UhIFlvdSByZWNlaXZlICs1IEludGVybmV0IHBvaW50cy4="&gt;
      …
</code></pre>

<p>This also works with URl encoding, if you prefer that, thusly:</p>

<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html&gt;
   &lt;head&gt;
   &lt;/head&gt;
   &lt;body&gt;
      &lt;link rel="stylesheet" type="text/css" href="data:text/css,%2F%2A%0AThis%20is%20my%20CSS%0A%2A%2F%0Aa%20%20%7Bfont-family%3Amonospace%3B%7D"&gt;
      …
</code></pre>

<p>Obviously, the <em>correct</em> thing to do is to change your rendering path so that the styles are all placed within the correct metadata section of the document. But, if you need to, this will work across all browsers in a standards compliant way.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=52225&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/08/inline-css-the-link-cheat/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Use CSS to boost the font size of emoji with no extra markup]]></title>
		<link>https://shkspr.mobi/blog/2024/04/use-css-to-boost-the-font-size-of-emoji-with-no-extra-markup/</link>
					<comments>https://shkspr.mobi/blog/2024/04/use-css-to-boost-the-font-size-of-emoji-with-no-extra-markup/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 14 Apr 2024 11:34:58 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[emoji]]></category>
		<category><![CDATA[font]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=50168</guid>

					<description><![CDATA[I want to make emoji bigger than the text that surrounds them. At my age and eyesight, it can be difficult to tell the difference between 😃, 😄, and 😊 when they are as small as the text.  Is there a way to use CSS to increase the font size of specific characters without having to wrap them in an extra &#60;span&#62; or similar?  Yes! Although it is a bit of a hack.    This relies on 3 CSS features: src: lo…]]></description>
										<content:encoded><![CDATA[<p>I want to make emoji bigger than the text that surrounds them. At my age and eyesight, it can be difficult to tell the difference between 😃, 😄, and 😊 when they are as small as the text.</p>

<p>Is there a way to use CSS to increase the font size of <em>specific</em> characters without having to wrap them in an extra <code>&lt;span&gt;</code> or similar?</p>

<p>Yes! Although it is a bit of a hack.</p>

<iframe width="100%" height="550" src="//jsfiddle.net/edent/531htvmn/embedded/result,html,css/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>

<p>This relies on 3 CSS features: <code>src: local()</code>, <code>unicode-range</code>,and <code>size-adjust</code>.  Let me walk you through it.</p>

<pre><code class="language-css">@font-face {
    font-family: "emoji";

    src: local('Apple Color Emoji'),
         local('Android Emoji'),
         local('Segoe UI Emoji'),
         local('Noto Color Emoji'),
         local(EmojiSymbols),
         local(Symbola);

    unicode-range: U+231A-231B, U+23E9-23EC, U+23F0, U+23F3, U+25FD-25FE, U+2614-2615, U+2648-2653, U+267F, U+2693, U+26A1, U+26AA-26AB, U+26BD-26BE, U+26C4-26C5, U+26CE, U+26D4, U+26EA, U+26F2-26F3, U+26F5, U+26FA, U+26FD, U+2705, U+270A-270B, U+2728, U+274C, U+274E, U+2753-2755, U+2757, U+2795-2797, U+27B0, U+27BF, U+2B1B-2B1C, U+2B50, U+2B55, U+FE0F, U+1F004, U+1F0CF, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201, U+1F21A, U+1F22F, U+1F232-1F236, U+1F238-1F23A, U+1F250-1F251, U+1F300-1F320, U+1F32D-1F335, U+1F337-1F393, U+1F3A0-1F3CA, U+1F3CF-1F3D3, U+1F3E0-1F3F0, U+1F3F4, U+1F3F8-1F43E, U+1F440, U+1F442-1F4FC, U+1F4FF-1F53D, U+1F54B-1F567, U+1F57A, U+1F595-1F596, U+1F5A4, U+1F5FB-1F64F, U+1F680-1F6CC, U+1F6D0-1F6D2, U+1F6D5-1F6D7, U+1F6DC-1F6DF, U+1F6EB-1F6EC, U+1F6F4-1F6FC, U+1F7E0-1F7EB, U+1F7F0, U+1F90C-1F93A, U+1F93C-1F945, U+1F947-1FA7C, U+1FA80-1FAC5, U+1FACE-1FADB, U+1FAE0-1FAE8, U+1FAF0-1FAF8;

    size-adjust: 300%;
}

body {
    font-family: "emoji", sans-serif;
}
</code></pre>

<p>Here's how it works.</p>

<p><code>@font-face</code> this tells the browser that we're defining a new font which will be referenced later.</p>

<p><code>font-family</code> this is the name we're going to be using as a reference.</p>

<p><code>src: local('Apple Color Emoji') ...</code> CSS can reference <em>local</em> fonts. We don't know what device this page is being viewed on, so we've included a number of popular fallback fonts which <em>should</em> work with all major browsers.  You can also reference a webfont if you want - although Emoji fonts tend to have a large filesize.  I've adapted this from <a href="https://gist.github.com/mfornos/9991865">Marc Fornós' CSS</a> and added a few more common default emoji fonts.</p>

<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/%40font-face/unicode-range"><code>unicode-range</code></a> this tells the browser to use this font <em>only</em> for the specific codepoints mentioned.  This is where the magic happens.</p>

<p>Emoji codepoints are complicated - especially when it comes to combining characters. You can see <a href="https://unicode.org/Public/emoji/15.1/emoji-sequences.txt">a full list of every sequence in Unicode 15.1</a>. There are currently <a href="https://en.wikipedia.org/wiki/List_of_emojis">3,782 different emoji</a>.</p>

<p>There was <a href="https://github.com/w3c/csswg-drafts/issues/7921">some talk of using named ranges</a> but that doesn't seem to have gone anywhere. So, instead, I've extracted all the Emoji codepoints and manually grouped them. It's a pretty long sequence, and I'm sure I've made a few mistakes.</p>

<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/size-adjust"><code>size-adjust</code></a> this is used to increase or decrease the apparent size of a font.</p>

<p>Finally, the <code>body { font-family: "emoji", sans-serif; }</code> tells the browser to use the Emoji font (remember, this will only work on the specified Unicode range) and then fall back to the defaults sans-serif font. Obviously, you can specify whatever fonts you like.</p>

<p><a href="https://jsfiddle.net/edent/531htvmn/">Have a play with it</a></p>

<p>This is a nifty little hack if you want to make Emoji (or any other text) bigger than its surroundings.</p>

<p>I am indebted to <a href="https://mastodon.social/@simevidas/112204970338133463">Šime Vidas</a> who demonstrated this trick to me.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=50168&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/04/use-css-to-boost-the-font-size-of-emoji-with-no-extra-markup/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[You can't photocopy this blogpost (abusing EURion in CSS)]]></title>
		<link>https://shkspr.mobi/blog/2024/03/you-cant-photocopy-this-blogpost-abusing-eurion-in-css/</link>
					<comments>https://shkspr.mobi/blog/2024/03/you-cant-photocopy-this-blogpost-abusing-eurion-in-css/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 02 Mar 2024 12:34:33 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=39312</guid>

					<description><![CDATA[Do you know about the EURion constellation?  It is a pattern which is embedded into some modern banknotes and has a curious property.  Most modern photocopiers will, if they detect the pattern, refuse to make a copy.  Try it for yourself - stick a €20 note into your nearest Xerox machine and try to print out some illicit currency - see what happens!  It goes a little further Some printers will r…]]></description>
										<content:encoded><![CDATA[<html><head><style>   body:before {position: absolute;  content: ''; height:1000px;width:100%;transform: scale(0.5); pointer-events: none; background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NTAiIGhlaWdodD0iNTUwIiBzdHlsZT0iZmlsbDojRkZGO3N0cm9rZTojMEYwO3N0cm9rZS13aWR0aDoxNyI+CjxjaXJjbGUgcj0iMjUiIGN4PSIyNjkiIGN5PSI3MyIgLz4KPGNpcmNsZSByPSIyNSIgY3g9Ijg1IiBjeT0iMTcwIiAvPgo8Y2lyY2xlIHI9IjI1IiBjeD0iMjM3IiBjeT0iMjI4IiAvPgo8Y2lyY2xlIHI9IjI1IiBjeD0iNDc1IiBjeT0iMjgwIiAvPgo8Y2lyY2xlIHI9IjI1IiBjeD0iMjYzIiBjeT0iNDg3IiAvPgo8L3N2Zz4=');opacity:.25;  print-color-adjust: exact;}
</style>

</head><body><p>Do you know about the <a href="https://en.wikipedia.org/wiki/EURion_constellation">EURion constellation</a>?  It is a pattern which is embedded into <a href="https://commons.wikimedia.org/wiki/File:EURion_%EC%98%A4%EB%A7%8C%EC%9B%90.jpg">some modern banknotes</a> and has a curious property.  Most modern photocopiers will, if they detect the pattern, refuse to make a copy.</p>

<p>Try it for yourself - stick a €20 note into your nearest Xerox machine and try to print out some illicit currency - see what happens!</p>

<p>It goes a little further Some printers will refuse to print anything which contains this pattern.  Lots of <a href="https://www.bbc.com/future/article/20150624-the-secret-codes-of-british-banknotes">image editing software will throw up dire warnings</a> if you try to open a file with it embedded.</p>

<p>Now, I know what you're thinking. Usually web browsers won't print background images. Even if you mark them as <code>!important</code>. And don't try using <code>@media print</code> because that won't work either.</p>

<p>Instead, you need to use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/print-color-adjust"><code>print-color-adjust: exact;</code></a> to tell the browser that you want to print <em>exactly</em> what is seen on screen.</p>

<p>If you can print this blog post in colour - and then photocopy it - do please let me know 😉</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=39312&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/03/you-cant-photocopy-this-blogpost-abusing-eurion-in-css/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Using date-based CSS to make old web pages *look* old]]></title>
		<link>https://shkspr.mobi/blog/2023/12/using-date-based-css-to-make-old-web-pages-look-old/</link>
					<comments>https://shkspr.mobi/blog/2023/12/using-date-based-css-to-make-old-web-pages-look-old/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 12 Dec 2023 12:34:04 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[history]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=47378</guid>

					<description><![CDATA[How do you know you&#039;re looking at an old website?  You may have found a page which has lots of interesting information, but how can you tell it&#039;s a modern and relevant result?  Some websites don&#039;t contain dates in their URls. There may not be a © date or publication date shown on the page. And the &#60;meta&#62; tags might not contain anything useful. If you&#039;re lucky, the site will look old fashioned:    …]]></description>
										<content:encoded><![CDATA[<p>How do you know you're looking at an old website?  You may have found a page which has lots of interesting information, but how can you tell it's a modern and relevant result?</p>

<p>Some websites <a href="https://shkspr.mobi/blog/2015/02/why-your-blog-urls-should-contain-dates/">don't contain dates in their URls</a>. There may not be a © date or publication date shown on the page. And the <code>&lt;meta&gt;</code> tags might not contain anything useful. If you're lucky, the site will <em>look</em> old fashioned:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/11/bbc.webp" alt="Screenshot of an early BBC news website from the 1990s. The page looks old fashioned." width="773" height="645" class="aligncenter size-full wp-image-48786">

<p>Unlike the BBC, most sites have adopted the "Eternal CSS" pattern. When fashions change, the <em>entire</em> site gets a facelift - which means ancient articles look modern. This can mislead readers into thinking old information is new.</p>

<p>Here's (one way) this can be fixed - inspired by <a href="https://moriel.tech/?era=2012">Moriel Schottlender fabulous historic CSS selector</a>.</p>

<h2 id="get-the-pages-publication-date"><a href="https://shkspr.mobi/blog/2023/12/using-date-based-css-to-make-old-web-pages-look-old/#get-the-pages-publication-date">Get the page's publication date</a></h2>

<p>A page might have a date published on it. For example:</p>

<pre><code class="language-html">Published on &lt;time id="published" datetime="2005-02-14"&gt;Valentine's Day&lt;/time&gt;
</code></pre>

<p>CSS has <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors#attrvalue_3">an attribute selector</a> which can see the year encoded in that <code>datetime</code>:</p>

<blockquote><p><code>[attr|=value]</code>
 Represents elements with an attribute name of attr whose value can be exactly value or can begin with value immediately followed by a hyphen, <code>-</code> (U+002D). It is often used for language subcode matches.</p></blockquote>

<p>That means, the element can be styled based on its year. For example, make the year 2005 green, and the year 2006 blue:</p>

<pre><code class="language-css">time[datetime|="2005"] {
  color: green;
}

time[datetime|="2006"] {
  color: blue;
}
</code></pre>

<p>That's fine for styling a specific element, but using the CSS property <code>has()</code> it is possible to set variables based on the date. For example, this code sets a variable which can be used anywhere on the page:</p>

<pre><code class="language-css">:root:has( time[datetime|="2005"] )  {
    --timeColor: green;
}

:root:has( time[datetime|="2006"] )  {
    --timeColor: blue;
}

p {
  color: var(--timeColor);
}
</code></pre>

<p>(Note that <code>has()</code> is only available in Chrome. It is coming soon to Firefox.)</p>

<h2 id="variables-from-metadata"><a href="https://shkspr.mobi/blog/2023/12/using-date-based-css-to-make-old-web-pages-look-old/#variables-from-metadata">Variables from Metadata</a></h2>

<p>If the published date is in the metadata, rather than the body, it can still be used in CSS. This is how WordPress adds the original published date to a page's <code>&lt;head&gt;</code>:</p>

<pre><code class="language-html">&lt;meta property="article:published_time" name="article:published_time" content="2007-11-16T12:34:41+00:00" /&gt;
</code></pre>

<p>The date can be accessed and used to set specific variables depending on the year:</p>

<pre><code class="language-css">:root:has( [name="article:published_time"][content|="2007"] ) {
    --font: "Old Fashioned";
    --bgColor: green;
    --fontColor: red;
    ...
}
:root:has( [name="article:published_time"][content|="2023"] ) {
    --font: "Modern";
    --bgColor: white;
    --fontColor: black;
    ...
}
</code></pre>

<p>Using this pattern, it is possible to have a single style-sheet which has a different look and feel depending on the age of the article.</p>

<p>Imagine that! You could set old pages to have a 1990's look and feel while keeping modern markup.</p>

<h2 id="maths-limitations"><a href="https://shkspr.mobi/blog/2023/12/using-date-based-css-to-make-old-web-pages-look-old/#maths-limitations">Maths limitations</a></h2>

<p>It isn't yet possible to use the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/attr"><code>attr()</code> CSS Function</a> to get the date. And it is impossible to do if/else or use <a href="https://www.bram.us/2016/10/13/css-attribute-value-less-than-greater-than-equals-selectors/">less than / greater than</a> in CSS.</p>

<p>But we can cheat!</p>

<p>A variable can be set depending on the year published:</p>

<pre><code class="language-css">:root:has( [name="article:published_time"][content|="1998"] ) {
    --publishedYear: 1997;
}
:root:has( [name="article:published_time"][content|="1999"] ) {
    --publishedYear: 1999;
}
:root:has( [name="article:published_time"][content|="2000"] ) {
    --publishedYear: 2000;
}
...
</code></pre>

<p>That pattern can be repeated as far back as your site goes - and as far forward as you think you'll still be publishing.</p>

<p>CSS has no access to the computer's datetime functions - that requires JavaScript. But if the website does expose the date it was generated, it should be possible to calculate the age of the page.</p>

<p>For example, if the <code>&lt;body&gt;</code> contains:</p>

<pre><code class="language-html">&lt;time id="RomanYear" datetime="2023"&gt;ⅯⅯⅩⅩⅠⅠⅠ&lt;/time&gt;
</code></pre>

<p>The year can be turned into a variable using a similar pattern:</p>

<pre><code class="language-css">:root:has( #RomanYear[datetime|="2023"] ) {
    --currentYear: 2023;
}
:root:has( #RomanYear[datetime|="2024"] ) {
    --currentYear: 2024;
}
:root:has( #RomanYear[datetime|="2025"] ) {
    --currentYear: 2025;
}
...
</code></pre>

<p>It is then possible to use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/calc">the <code>calc()</code> CSS function</a> to calculate the age of the page:</p>

<pre><code class="language-css">--age: calc( var(--currentYear) - var(--publishedYear) );
</code></pre>

<p>From this, it's possible to work out how old the page is as a percentage of the site's history. For example, if the earliest publication on the site is from 2001:</p>

<pre><code class="language-css">--oldestYear:    2001;
--currentYear:   2023;
--publishedYear: 2014;

--siteAge: calc( var(--currentYear) - var(--oldestYear) ); /* 16 */

--pageAge: calc( var(--currentYear) - var(--publishedYear) ); /* 9 */
</code></pre>

<p>Finally, a sepia filter can be placed over the page - with its intensity higher the older a page is.</p>

<pre><code class="language-css">--percentAge: calc( var(--pageAge) / var(--site-age) );

--filterRatio: calc( var(--percentAge) );

filter: sepia( var(--filterRatio) );
</code></pre>

<h2 id="whats-next"><a href="https://shkspr.mobi/blog/2023/12/using-date-based-css-to-make-old-web-pages-look-old/#whats-next">What's next</a></h2>

<p>What will you do with this forbidden information?</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=47378&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/12/using-date-based-css-to-make-old-web-pages-look-old/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[CSS only colour-scheme selector - no JS required]]></title>
		<link>https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/</link>
					<comments>https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 26 Oct 2023 11:34:02 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=48492</guid>

					<description><![CDATA[Yesterday I wrote about a lazy way to implement a manual dark mode chooser. Today I&#039;ll show you a slightly more sensible way to do it.  It just uses CSS, no need for JavaScript.  Here&#039;s a scrap of HTML which present a dropdown for a user to choose their colour scheme:  &#60;select id=&#34;colour-mode&#34;&#62;   &#60;option value=&#34;&#34;&#62;Theme Selector&#60;/option&#62;   &#60;option value=&#34;dark&#34;&#62;Dark Mode&#60;/option&#62;   &#60;option…]]></description>
										<content:encoded><![CDATA[<p>Yesterday I wrote about <a href="https://shkspr.mobi/blog/2023/10/laziest-possible-dark-mode-toggle-using-has-and-invert/">a lazy way to implement a manual dark mode chooser</a>. Today I'll show you a <em>slightly</em> more sensible way to do it.  It just uses CSS, no need for JavaScript.</p>

<p>Here's a scrap of HTML which present a dropdown for a user to choose their colour scheme:</p>

<pre><code class="language-html">&lt;select id="colour-mode"&gt;
  &lt;option value=""&gt;Theme Selector&lt;/option&gt;
  &lt;option value="dark"&gt;Dark Mode&lt;/option&gt;
  &lt;option value="light"&gt;Light and Bright&lt;/option&gt;
  &lt;option value="eink"&gt;eInk&lt;/option&gt;
&lt;/select&gt;
</code></pre>

<p>It will look something like this:
<select style="max-width: fit-content;">
  <option value="">Theme Selector</option>
  <option value="dark">Dark Mode</option>
  <option value="light">Light and Bright</option>
  <option value="eink">eInk</option>
</select></p>

<p>Modern CSS gives us a way to see which of those <code>option</code>s have been chosen by the user:</p>

<pre><code class="language-css">#colour-mode option:checked[value="dark"]
</code></pre>

<p>That can be combined with the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:has">new <code>:has()</code> CSS pseudo class</a>. This allows the CSS to say "If the body has a checked elements with this specific value, then apply these CSS rules" - like so:</p>

<pre><code class="language-css">body:has( &gt; #colour-mode option:checked[value="dark"] ) {
  background: ...
}
</code></pre>

<p>OK! So, depending on which option the user selects, the CSS can be made to do all sorts of weird and wonderful things. But, that will require...</p>

<h2 id="css-variables"><a href="https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/#css-variables">CSS variables</a></h2>

<p>Here's some CSS which will set various colours for light mode and dark mode. Then it sets the default colours to the light mode:</p>

<pre><code class="language-css">:root {
  /* Light mode variables */
  --light-background: beige;
  --light-text: #000;
  --light-bg: #0F0;

  /* Dark mode variables */
  --dark-background: #000;
  --dark-text: #FFF;
  --dark-bg: #FF0;

    /*  Default variables */
  --background: var(--light-background);
  --text: var(--light-text);
  --bg: var(--light-bg);
}
</code></pre>

<p>So the rest of the CSS can have things like:</p>

<pre><code class="language-css">p {
   color: var(--text);
}
</code></pre>

<p>The <code>&lt;p&gt;</code> will be set to use the colour from <code>--text</code> which, at first, is the same as <code>--light-text</code>.</p>

<p>That can be changed with both <code>:has()</code> and <code>:checked</code> like so:</p>

<pre><code class="language-css">body:has( &gt; #colour-mode option:checked[value="dark"] ) {
  --text: var(--dark-text);
}
</code></pre>

<p>That says "If the body element has a child which has the ID "colour-mode", and if "colour-mode" has a child option with the checked value of "dark", then set the <code>--text</code> variable to be the value of <code>--dark-text</code>.</p>

<p>String enough of those together and that will make a pretty capable theme switcher!</p>

<h2 id="user-preferences"><a href="https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/#user-preferences">User Preferences</a></h2>

<p>Some browsers will know whether their user has a preference for dark or light mode. Perhaps the user has set their phone to dark mode, or flipped a switch somewhere for light mode. This preference can be determined in <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme">CSS using the <code>prefers-color-scheme</code> media feature</a>.</p>

<p>A site can set the default value of the colour variables like so:</p>

<pre><code class="language-css">@media (prefers-color-scheme: light) {
  :root {
    --text: var(--light-text);
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --text: var(--dark-text);
  }
}
</code></pre>

<h2 id="demo"><a href="https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/#demo">Demo</a></h2>

<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="Less Lazy Dark / Light Mode Selector" src="https://codepen.io/edent/embed/preview/RwvbeKe?default-tabs=css%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=RwvbeKe#?secret=tZ9rB4vp1w" data-secret="tZ9rB4vp1w" scrolling="no" frameborder="0" height="300"></iframe>

<h2 id="caveats"><a href="https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/#caveats">Caveats</a></h2>

<p>A few issues:</p>

<ol>
<li>This doesn't yet work on Firefox. Even if you enable <code>layout.css.has-selector.enabled</code> in about:config. Support is coming soon™.</li>
<li>This doesn't remember your user's choice. So they'll need to toggle it on every page load.</li>
<li>Choosing a sensible colour scheme means you should test it for accessibility.</li>
</ol>

<h2 id="saving-the-selection"><a href="https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/#saving-the-selection">Saving the selection</a></h2>

<p>Sadly, this does require JS. This uses <code>localstorage</code> rather than a cookie. If a user doesn't have JS enabled, this will gracefully degrade; the theme will follow the user's preferences, switching will work but won't be remembered.</p>

<pre><code class="language-javascript">//  Get the theme switcher
var themeSelect = document.getElementById('theme');

//  If a theme has previously been selected, set it
if (localStorage.theme) {
    themeSelect.value = localStorage.theme;
}

//  Listen for any changes and save them
if(themeSelect) {
    themeSelect.addEventListener('change', function(event){
        localStorage.setItem('theme', themeSelect.value);
    });
}
</code></pre>

<h2 id="further-reading"><a href="https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/#further-reading">Further Reading</a></h2>

<ul>
<li><a href="https://sarajoy.dev/blog/color-scheme/">Do you know color-scheme?</a></li>
<li><a href="https://www.jobsity.com/blog/how-to-make-dark-mode-for-websites-using-only-css">How to Make Dark Mode for Websites Using Only CSS</a></li>
<li><a href="https://m0yng.uk/2021/07/prefers-color-scheme/">Prefers Color Scheme</a></li>
<li><a href="https://codepen.io/demilad/pen/bZRjpb">Pure CSS dark mode toggle switcher</a></li>
<li><a href="https://codyhouse.co/blog/post/store-theme-color-preferences-with-localstorage">How to store theme color preferences using the Local Storage API</a></li>
</ul>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=48492&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/10/css-only-colour-scheme-selector-no-js-required/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Laziest Possible Dark-Mode Toggle - Using :has() and invert()]]></title>
		<link>https://shkspr.mobi/blog/2023/10/laziest-possible-dark-mode-toggle-using-has-and-invert/</link>
					<comments>https://shkspr.mobi/blog/2023/10/laziest-possible-dark-mode-toggle-using-has-and-invert/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 25 Oct 2023 11:34:53 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[accessibility]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=48485</guid>

					<description><![CDATA[I&#039;m not saying this is a good way to make a dark mode website. I&#039;m not even saying it&#039;s a sensible way to do dark mode. But I&#039;m pretty sure this is the laziest way of getting dark mode on your site.  And it is all done with less than a handful of CSS rules.  It relies on the new-ish :has() CSS pseudo class and the positively ancient filter() CSS function.  Here&#039;s the code in all its glory: …]]></description>
										<content:encoded><![CDATA[<p>I'm not saying this is a <em>good</em> way to make a dark mode website. I'm not even saying it's a <em>sensible</em> way to do dark mode. But I'm pretty sure this is the <em>laziest</em> way of getting dark mode on your site.  And it is all done with less than a handful of CSS rules.</p>

<p>It relies on the new-ish <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:has"><code>:has()</code> CSS pseudo class</a> and the positively ancient <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/invert"><code>filter()</code> CSS function</a>.</p>

<p>Here's the code in all its glory:</p>

<pre><code class="language-css">body:has( &gt; #dark-mode-checkbox:checked ) {
  filter: invert(1);
  background: #000;
}

#dark-mode-checkbox:checked ~ img {
  filter: invert(1);
}
</code></pre>

<p>That does three things. If the body has an <code>&lt;input id="dark-mode-checkbox" type="checkbox"&gt;</code> which is selected, then:</p>

<ul>
<li>Invert all the colours on <em>everything</em> on the site.</li>
<li>Manually set the background of the body, because the above is only filtering the descendants of the body.</li>
<li>Find any images and invert them, so they're the right colour.</li>
</ul>

<p>Here's some minimum viable HTML:</p>

<pre><code class="language-html">&lt;body&gt;
  &lt;input id="dark-mode-checkbox" type="checkbox"&gt;
  &lt;label class="dark-mode-label" for="dark-mode-checkbox"&gt;Enable dark mode&lt;/label&gt;
  &lt;h1&gt;Heading&lt;/h1&gt;
  &lt;p&gt;Test&lt;/p&gt;
  &lt;img src="https://placecats.com/128/128" alt="A cute cat"&gt;
&lt;/body&gt;
</code></pre>

<h2 id="demo"><a href="https://shkspr.mobi/blog/2023/10/laziest-possible-dark-mode-toggle-using-has-and-invert/#demo">Demo</a></h2>

<p>You can <a href="https://codepen.io/edent/pen/NWeQZOy">play with the demo</a>.</p>

<p>It should look like this:
<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/10/lightmode-fs8.png" alt="A light website with a photo of a kitten." width="326" height="352" class="size-full wp-image-48486"><img src="https://shkspr.mobi/blog/wp-content/uploads/2023/10/darkmode-fs8.png" alt="A dark website with a photo of a kitten." width="326" height="352" class="size-full wp-image-48487"></p>

<h2 id="caveat"><a href="https://shkspr.mobi/blog/2023/10/laziest-possible-dark-mode-toggle-using-has-and-invert/#caveat">Caveat</a></h2>

<p>A few issues:</p>

<ol>
<li>This doesn't yet work on Firefox. Even if you enable <code>layout.css.has-selector.enabled</code> in about:config. Support is coming soon™.</li>
<li>Inverting colours isn't the same as a properly designed dark mode. You absolutely shouldn't use this in production.</li>
<li>There are no accessibility guarantees.  Please test this before subjecting your users to it.</li>
<li>This doesn't remember your user's choice. So they'll need to toggle it on every page load.</li>
<li>Who knows what eldritch horrors this will unleash upon humanity.</li>
</ol>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=48485&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/10/laziest-possible-dark-mode-toggle-using-has-and-invert/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[The limits of CSS styling select options]]></title>
		<link>https://shkspr.mobi/blog/2023/05/the-limits-of-css-styling-select-options/</link>
					<comments>https://shkspr.mobi/blog/2023/05/the-limits-of-css-styling-select-options/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 15 May 2023 11:34:37 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML5]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=45753</guid>

					<description><![CDATA[Sometimes you learn the most from failures!  I wanted a &#60;select multiple&#62; element where the &#60;options&#62; were laid out in a grid. I nearly got there.  It&#039;s possible to have the &#60;option&#62;s in a horizontal row - but only on Chrome and Firefox.  Here&#039;s a quick fiddle showing the results:    As you can see, it&#039;s possible to do some pretty extravagant styling of the individual &#60;options&#62; you can even…]]></description>
										<content:encoded><![CDATA[<p>Sometimes you learn the most from failures!</p>

<p>I wanted a <code>&lt;select multiple&gt;</code> element where the <code>&lt;options&gt;</code> were laid out in a grid. I <em>nearly</em> got there.</p>

<p>It's possible to have the <code>&lt;option&gt;</code>s in a horizontal row - but only on Chrome and Firefox.</p>

<p><a href="https://jsfiddle.net/edent/v2qeyfg8/">Here's a quick fiddle showing the results</a>:</p>

<iframe width="100%" height="150" src="//jsfiddle.net/edent/v2qeyfg8/embedded/result/" allowfullscreen="allowfullscreen" allowpaymentrequest="" frameborder="0"></iframe>

<p>As you can see, it's possible to do some pretty extravagant styling of the individual <code>&lt;options&gt;</code> you can even change how they look when they're selected.</p>

<p>But it's impossible to get them to wrap using a flexbox or grid approach. It seems the only layout options are a vertical or horizontal list.</p>

<p>Why, yes, I should probably have used <code>&lt;input type="checkbox"&gt;</code> instead!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=45753&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/05/the-limits-of-css-styling-select-options/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
