<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://chameth.com/">
    <title>Chameth.com - posts like apple-google-aligned-incentives, modern-css-is-fun, why-you-should-be-using-https but not tailscale-docker-gotcha</title>
    <subtitle>Personal homepage of Chris Smith</subtitle>
    <link href="https://chameth.com/feeds/posts/like/apple-google-aligned-incentives,modern-css-is-fun,why-you-should-be-using-https/unlike/tailscale-docker-gotcha/" rel="self"/>
    <link href="https://chameth.com/"/>
    <icon>https://chameth.com/favicon.png</icon>
    <updated>2026-04-01T00:00:00Z</updated>
    <id>https://chameth.com/</id>
    <author>
        <name>Chris Smith</name>
    </author>
    <entry>
        <title>Monthly Meanderings: March 2026</title>
        <link href="https://chameth.com/monthly-meanderings-2026-03/"/>
        <updated>2026-04-01T00:00:00Z</updated>
        <id>https://chameth.com/monthly-meanderings-2026-03/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Since &lt;a href=&#34;https://chameth.com/monthly-meanderings-2026-02/&#34;&gt;last month’s update&lt;/a&gt; I’ve been unpleasantly reminded that I’m middle aged, through the medium of a dodgy knee. There’s nothing quite like not being able to stand up without groaning to underscore that you’re not young any more. The ongoing game of “will this seemingly mundane activity make my knee go funny again?” is a &lt;em&gt;delight&lt;/em&gt; to play, too. I’m great at it.&lt;/p&gt;
&lt;h3 id=&#34;website-updates&#34;&gt;Website updates&lt;/h3&gt;
&lt;p&gt;Two new blog posts this month: &lt;a href=&#34;https://chameth.com/the-longest-way-to-represent-a-date/&#34;&gt;the longest way to represent a date&lt;/a&gt; is a short thought experiment on absurd date formats, and &lt;a href=&#34;https://chameth.com/modern-css-is-fun/&#34;&gt;modern CSS is fun&lt;/a&gt; goes through some neat new CSS features I’ve used recently for this site.&lt;/p&gt;
&lt;p&gt;I did some design tweaks this month: headers now have a blue background behind them&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, and a lot of components have been redesigned to have a consistent style. You can see that component in action in the new “now playing” widget I’ve added:&lt;/p&gt;
&lt;div class=&#34;now-playing raised-box&#34; data-title=&#34;Now playing&#34;&gt;
    &lt;img src=&#34;https://chameth.com/music/albums/16/cover.jpg&#34; alt=&#34;7&#34; loading=&#34;lazy&#34;/&gt;
    &lt;div class=&#34;now-playing-info&#34;&gt;
        &lt;span class=&#34;artist-track&#34;&gt;Lil Nas X — Old Town Road (remix)&lt;/span&gt;
        &lt;span class=&#34;album-name&#34;&gt;7&lt;/span&gt;
        &lt;span class=&#34;play-status&#34;&gt;Scrobbled 42m ago&lt;/span&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Instead of each component defining its own styles (which were &lt;em&gt;mostly&lt;/em&gt; copied and pasted), there’s now a single class that handles the drop shadow, border, background colour, and the optional title. It also applies a consistent format to elements that are clickable: they use the blue accent colour for their borders, and have a glow effect shown on hover.&lt;/p&gt;
&lt;p&gt;The now playing widget is part of a larger bit of work I did on importing some music stats. I’m pulling in the data from my &lt;a href=&#34;https://www.navidrome.org/&#34;&gt;Navidrome&lt;/a&gt; instance. A new &lt;a href=&#34;https://chameth.com/music/&#34;&gt;music page&lt;/a&gt; shows my most listened albums and artists. Navidrome doesn’t store a complete play history, just the last play and the count, so I can’t do “what I listened to last month” stats until next month.&lt;/p&gt;
&lt;h3 id=&#34;other-projects&#34;&gt;Other projects&lt;/h3&gt;
&lt;p&gt;Other than a minor bug-fix update to &lt;a href=&#34;https://github.com/csmith/contempt&#34;&gt;contempt&lt;/a&gt;, my Dockerfile templating/updating tool, I’ve not done much on my other open source projects. I’ve got a bit more work to do finish migrating all my repositories from GitHub to my private &lt;a href=&#34;https://forgejo.org/&#34;&gt;Forgejo&lt;/a&gt; instance. I’ve also been thinking about mirroring my public repositories to &lt;a href=&#34;https://codeberg.org/&#34;&gt;Codeberg&lt;/a&gt; so there’s a non-GitHub way to access them.&lt;/p&gt;
&lt;h3 id=&#34;entertainment&#34;&gt;Entertainment&lt;/h3&gt;
&lt;p&gt;I apparently only watched a single film in March:&lt;/p&gt;
&lt;div class=&#34;film-review-parent&#34;&gt;
  &lt;section class=&#34;film-review raised-box&#34;&gt;
    &lt;img src=&#34;https://chameth.com/films/253/poster.jpg&#34; alt=&#34;Poster for The Substance&#34; loading=&#34;lazy&#34;/&gt;
    &lt;header&gt;
      &lt;h3 class=&#34;plain-header&#34;&gt;&lt;a href=&#34;https://chameth.com/films/the-substance-2024/&#34;&gt;The Substance&lt;/a&gt;&lt;/h3&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div title=&#34;5/10&#34;&gt;
&lt;span class=&#34;star-rating&#34;&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-half.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Half star&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-empty.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Empty star&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-empty.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Empty star&#34;/&gt;&lt;/span&gt;
&lt;/div&gt;
      &lt;time&gt;2026-03-18&lt;/time&gt;
    &lt;/header&gt;
    &lt;div&gt;&lt;p&gt;I really didn’t get on with this. It’s too long for what it is, and over-the-top in a way that just didn’t work for me.&lt;/p&gt;
&lt;p&gt;Demi Moore and Margaret Qualley were great, but nothing else really was. The cinematography and sound design were too affected, the plot was pretty shallow and obvious, most of the body horror fell into the uncanny CGI valley for me.&lt;/p&gt;
&lt;p&gt;It’s not completely without merit: there were around 40 minutes in the middle where I was really into it, but that’s not really enough given it’s closing in on a 2.5 hour runtime.&lt;/p&gt;
&lt;p&gt;There’s something to be said for the message it’s presenting about women in Hollywood, too, but it was very heavy handed. Dennis Quaid’s character is even called Harvey; it’s not so much a subtle nod as a sledgehammer to the face.&lt;/p&gt;
&lt;/div&gt;
  &lt;/section&gt;
&lt;/div&gt;
&lt;p&gt;Instead of films, I’ve been watching a bunch of TV. After devouring &lt;a href=&#34;https://www.themoviedb.org/tv/250307-the-pitt&#34;&gt;The Pitt&lt;/a&gt; last month, I struggled to find something to fill the hole it left. I settled on catching up on &lt;a href=&#34;https://www.themoviedb.org/tv/44006-chicago-fire&#34;&gt;Chicago Fire&lt;/a&gt; and &lt;a href=&#34;https://www.themoviedb.org/tv/58841-chicago-p-d&#34;&gt;Chicago P.D.&lt;/a&gt;. They’re both mostly about getting things done without personal drama becoming the primary focus. They’re not quite as grounded as The Pitt, but at least they’ve not completely gone off the deep end like &lt;a href=&#34;https://www.themoviedb.org/tv/75219-9-1-1&#34;&gt;9-1-1&lt;/a&gt; where the first responders end up in space somehow?&lt;/p&gt;
&lt;p&gt;I’ve also watched the first two episodes of &lt;a href=&#34;https://www.themoviedb.org/tv/288670-saturday-night-live-uk&#34;&gt;SNL UK&lt;/a&gt;. I didn’t have very high expectations, and it gave me a pleasant surprise. Both episodes so far have some hilarious sketches, and are obviously written by British comics with the typical darker, more sardonic humour that separates us from the USA.&lt;/p&gt;
&lt;p&gt;Last but not least, board games! I spent a weekend at a tabletop/LAN event with friends, so got to physically play some games for a change. I also kept up with some turn-based games on &lt;a href=&#34;https://boardgamearena.com/&#34;&gt;Board Game Arena&lt;/a&gt;, as usual.&lt;/p&gt;
&lt;ul class=&#34;played-boardgames&#34;&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Just One (2018)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/254640/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/254640/image.jpg&#34; alt=&#34;Box art of Just One&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;6 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;7 Wonders Dice (2025)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/446231/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/446231/image.jpg&#34; alt=&#34;Box art of 7 Wonders Dice&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;4 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Can&amp;#39;t Stop (1980)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/41/image.png&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/41/image.png&#34; alt=&#34;Box art of Can&amp;#39;t Stop&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;4 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Martian Dice (2011)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/99875/image.png&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/99875/image.png&#34; alt=&#34;Box art of Martian Dice&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;4 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Cartographers (2019)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/263918/image.png&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/263918/image.png&#34; alt=&#34;Box art of Cartographers&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;3 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Coffee Rush (2023)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/377061/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/377061/image.jpg&#34; alt=&#34;Box art of Coffee Rush&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;3 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Super Mega Lucky Box (2021)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/341530/image.png&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/341530/image.png&#34; alt=&#34;Box art of Super Mega Lucky Box&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;3 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Cthulhu Fluxx (2012)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/122159/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/122159/image.jpg&#34; alt=&#34;Box art of Cthulhu Fluxx&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;2 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Obsession (2018)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/231733/image.png&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/231733/image.png&#34; alt=&#34;Box art of Obsession&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;2 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Plant-Based Riot (2022)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/342940/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/342940/image.jpg&#34; alt=&#34;Box art of Plant-Based Riot&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;2 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Space Base (2018)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/242302/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/242302/image.jpg&#34; alt=&#34;Box art of Space Base&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;2 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Terraforming Mars (2016)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/167791/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/167791/image.jpg&#34; alt=&#34;Box art of Terraforming Mars&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;2 plays&lt;/p&gt;
  &lt;/li&gt;
  &lt;li class=&#34;raised-box&#34;&gt;
    &lt;div class=&#34;image-container&#34; title=&#34;Next Station: Tokyo (2023)&#34;&gt;
      &lt;img class=&#34;background&#34; src=&#34;https://chameth.com/boardgames/380165/image.jpg&#34; aria-hidden=&#34;true&#34; loading=&#34;lazy&#34;/&gt;
      &lt;img class=&#34;foreground&#34; src=&#34;https://chameth.com/boardgames/380165/image.jpg&#34; alt=&#34;Box art of Next Station: Tokyo&#34; loading=&#34;lazy&#34;/&gt;
    &lt;/div&gt;
    &lt;p&gt;1 play&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;around-the-web&#34;&gt;Around the web&lt;/h3&gt;
&lt;h4 id=&#34;warranty-void-if-regeneratedhttpsnearzerosoftwarepwarranty-void-if-regenerated&#34;&gt;&lt;a href=&#34;https://nearzero.software/p/warranty-void-if-regenerated&#34;&gt;Warranty Void if Regenerated&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Some really cool speculative fiction about a future where all software is written by LLMs, and the social effects that may have. Unfortunately the story itself is written with an LLM, and that becomes painfully obvious about a third of the way in. By that point I was already hooked, though.&lt;/p&gt;
&lt;h4 id=&#34;my-2-step-process-for-ai-free-blogginghttpsmanuelmorealecomthoughtsmy-2-step-process-for-ai-free-blogging&#34;&gt;&lt;a href=&#34;https://manuelmoreale.com/thoughts/my-2-step-process-for-ai-free-blogging&#34;&gt;My 2-step process for AI-free blogging&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Speaking of LLM-generated text, this short piece by Manuel Moreale made me laugh. I’m by no means anti-LLM, but I really hate people publishing the output like it’s their own work. It breaks the social assumption that the writer put more effort in than the reader, and makes me wonder how much is hallucinated.&lt;/p&gt;
&lt;h4 id=&#34;25-years-of-eggshttpswwwjohn-rushcompostseggs-25-years-20260219html&#34;&gt;&lt;a href=&#34;https://www.john-rush.com/posts/eggs-25-years-20260219.html&#34;&gt;25 Years of Eggs&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An interesting applied use of coding agents. I’m a sucker for personal statistics. I have no explicit desire to keep my receipts, or generate graphs of my egg consumption, but seeing the graphs makes me question that. The journey to get there is a great read, too.&lt;/p&gt;
&lt;h4 id=&#34;i-made-a-one-page-notebookhttpsjoelchronoxyzblogi-made-a-one-page-notebook&#34;&gt;&lt;a href=&#34;https://joelchrono.xyz/blog/i-made-a-one-page-notebook/&#34;&gt;I made a one-page notebook&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The clever paper folding is interesting, but I really like some of the small touches on the page. There’s a nice little “in reply to” callout providing context at the top of the page, you can click to rotate the image further down, and there’s a details element containing a textual version of it. It has that great ‘small web’ vibe of someone who really cares about what they’re doing.&lt;/p&gt;
&lt;h4 id=&#34;workers-who-love-synergizing-paradigms-might-be-bad-at-their-jobshttpsnewscornelledustories202603workers-who-love-synergizing-paradigms-might-be-bad-their-jobs&#34;&gt;&lt;a href=&#34;https://news.cornell.edu/stories/2026/03/workers-who-love-synergizing-paradigms-might-be-bad-their-jobs&#34;&gt;Workers who love ‘synergizing paradigms’ might be bad at their jobs&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I haven’t read the full paper, but the summary given by the article is equal parts interesting and amusing. They developed a “Corporate Bullshit Receptivity Scale”, what more is there to say?&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;It’s meant to resemble painter’s tape, but I’m not sure quite how well it works. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>Modern CSS is fun</title>
        <link href="https://chameth.com/modern-css-is-fun/"/>
        <updated>2026-03-17T00:00:00Z</updated>
        <id>https://chameth.com/modern-css-is-fun/</id>
        <content xml:lang="en" type="html">&lt;p&gt;I’ve been doing a bunch of CSS tweaking recently, and keep being surprised by how nice modern CSS is to work with. As someone grey-haired enough to remember writing HTML &lt;em&gt;without&lt;/em&gt; CSS, it’s amazing to think how far along web technology has come&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. I wanted to demonstrate some of the handy bits and pieces I’ve used recently.&lt;/p&gt;
&lt;h3 id=&#34;has&#34;&gt;:has&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;:has&lt;/code&gt; selector allows you to effectively query for child elements. While &lt;code&gt;a span&lt;/code&gt; will match a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; within an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, &lt;code&gt;a:has(span)&lt;/code&gt; will match an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; that contains a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;. This really shines when combined with more complex selectors, for example:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nt&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;border-radius&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;border-radius&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:has&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(+&lt;/span&gt; &lt;span class=&#34;chroma-nc&#34;&gt;.results&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:not&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:empty&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;border-bottom-left-radius&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;border-bottom-right-radius&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is part of the styling for my film search box. When the search box is presented initially it has fully rounded corners. But when it is followed by a non-empty results element, it removes the rounding on the bottom corners so that the border continues in a straight line down into the results. You can see it in action below; just enter a few characters (like “the”) to get some results:&lt;/p&gt;
&lt;div data-form-search=&#34;&#34;&gt;
    &lt;noscript&gt;JavaScript required for film search&lt;/noscript&gt;
&lt;/div&gt;
&lt;p&gt;You could achieve the same effect by having JavaScript add a class to the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element, but I’ll take a CSS solution over a JavaScript solution any day.&lt;/p&gt;
&lt;h3 id=&#34;nested-rules&#34;&gt;Nested rules&lt;/h3&gt;
&lt;p&gt;You probably spotted this in the example above. It’s what finally made me switch from &lt;a href=&#34;https://sass-lang.com/&#34;&gt;SCSS&lt;/a&gt; to plain CSS. If you have a rule for &lt;code&gt;.foo&lt;/code&gt; and a rule for &lt;code&gt;.foo .bar&lt;/code&gt; you can just nest them. Not only does it save repeating yourself an awful lot, it keeps everything organised nicely. For example, the CSS for my film list embeds looks like this:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nt&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;chroma-nc&#34;&gt;.film-list&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;display&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;grid&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;header images&amp;#34;&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;description images&amp;#34;&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;meta images&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;fr&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;fr&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;row-gap&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;small&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;space&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-k&#34;&gt;@media&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;width&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;800&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;grid-template-areas&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;header&amp;#34;&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;images&amp;#34;&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;meta&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;fr&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;grid-template-rows&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;margin-top&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;medium&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;space&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-nt&#34;&gt;h3&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;font-size&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;font-size-xxlarge&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-nc&#34;&gt;.description&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-cm&#34;&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Everything is wrapped up in a single bundle, including the &lt;code&gt;@media&lt;/code&gt; rules for changing the layout on smaller screens, the lovely little &lt;code&gt;&amp;amp; + &amp;amp;&lt;/code&gt; rule that adds some extra margin if there are two lists in a row, etc. The &lt;code&gt;&amp;amp;&lt;/code&gt; syntax refers to the parent selector, so &lt;code&gt;&amp;amp; + &amp;amp;&lt;/code&gt; in this case is the same as &lt;code&gt;a.film-list + a.film-list&lt;/code&gt;: it’s very handy! The best part about this is that it’s the exact same syntax as SCSS, so converting is easy&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:2&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. The styled element looks like this:&lt;/p&gt;
&lt;a class=&#34;film-list raised-box plain-link&#34; data-title=&#34;Film list&#34; href=&#34;https://chameth.com/films/lists/ranking/&#34;&gt;
  &lt;h3 class=&#34;plain-header&#34;&gt;Watched films ranked&lt;/h3&gt;
  &lt;div class=&#34;poster-stack&#34;&gt;
    &lt;img src=&#34;https://chameth.com/films/265/poster.jpg&#34; alt=&#34;Poster of Midsommar&#34;/&gt;&lt;img src=&#34;https://chameth.com/films/4/poster.jpg&#34; alt=&#34;Poster of Easy A&#34;/&gt;&lt;img src=&#34;https://chameth.com/films/97/poster.jpg&#34; alt=&#34;Poster of Hackers&#34;/&gt;&lt;img src=&#34;https://chameth.com/films/25/poster.jpg&#34; alt=&#34;Poster of The Matrix&#34;/&gt;&lt;img src=&#34;https://chameth.com/films/202/poster.jpg&#34; alt=&#34;Poster of Sinners&#34;/&gt;
  &lt;/div&gt;
  &lt;div class=&#34;description&#34;&gt;&lt;p&gt;Every film I’ve watched since I started logging, ranked.&lt;/p&gt;
&lt;p&gt;Obviously super subjective, and subject to change often and arbitrarily.&lt;/p&gt;
&lt;/div&gt;
  &lt;p class=&#34;count&#34;&gt;264 films&lt;/p&gt;
&lt;/a&gt;
&lt;h3 id=&#34;media-range-syntax&#34;&gt;Media range syntax&lt;/h3&gt;
&lt;p&gt;Once again, you might have spotted this in the previous snippet. I’m not deliberately teasing things, I promise! Back in the day, you did media queries like so:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;@media&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;min-width&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-na&#34;&gt;max-width&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;2000&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-cm&#34;&gt;/* some rules that work for screens at least 1000px wide and at most 2000px wide */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I always hated this syntax. I always got muddled up as to whether I wanted “min” or “max”, and whether they were inclusive or not. It’s easy to reason through,
but it never came naturally. Fortunately you can now just use ranges:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;@media&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;width&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;2000&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-cm&#34;&gt;/* some rules that work for screens at least 1000px wide and at most 2000px wide */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I find this style so much easier to write and understand.&lt;/p&gt;
&lt;h3 id=&#34;anchor-positioning&#34;&gt;Anchor positioning&lt;/h3&gt;
&lt;p&gt;Trying to dynamically position one element next to another used to exclusively fall within the purview of JavaScript. Fortunately anchor positioning
fixes all this. I use this in the film search field I showed above:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nc&#34;&gt;.film-search&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-nt&#34;&gt;input&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;anchor-name&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;film-search-box&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-nc&#34;&gt;.results&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;position&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;absolute&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;position-anchor&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;film-search-box&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;position-area&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;bottom&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;position-visibility&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;always&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;anchor-size&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This does lots of fun things. The &lt;code&gt;.results&lt;/code&gt; element is anchored to the &lt;code&gt;input&lt;/code&gt; field (via the &lt;code&gt;--film-search-box&lt;/code&gt; name), and it’s positioned on the bottom,
directly under the anchor. The &lt;code&gt;position-area&lt;/code&gt; rule describes a 3x3 grid, with the anchor at the center, so &lt;code&gt;top left&lt;/code&gt; would put it diagonally adjacent to
the anchor. Next, &lt;code&gt;position-visibility&lt;/code&gt; keeps the &lt;code&gt;.results&lt;/code&gt; element visible when the anchor &lt;em&gt;isn’t&lt;/em&gt;; otherwise when you scroll the anchor off the screen
the results would immediately vanish. Finally, the special &lt;code&gt;anchor-size()&lt;/code&gt; function makes the &lt;code&gt;.results&lt;/code&gt; element take up the same exact width as the anchor.&lt;/p&gt;
&lt;p&gt;All-in-all, this puts the results exactly where I want them, without having to deal with any JavaScript at all. I can see how it’d also be amazingly useful
trying to do tooltips or other forms of ‘floating’ content too. You can even leave the positioning up to the browser, giving it hints about which order to
try, or what property to optimise for (e.g. you can say “anchor to the left or right, whichever has more horizontal space”).&lt;/p&gt;
&lt;h3 id=&#34;attr-function&#34;&gt;attr() function&lt;/h3&gt;
&lt;p&gt;This one blew my mind a little. In some of the elements on the site, I’ve added a little label to the top. It looks something like this:&lt;/p&gt;
&lt;figure class=&#34;rating-distribution raised-box&#34; data-title=&#34;Film rating distribution&#34;&gt;
 &lt;div class=&#34;chart-container&#34;&gt;
  &lt;span class=&#34;left-label&#34;&gt;
&lt;span class=&#34;star-rating&#34;&gt;&lt;img src=&#34;https://chameth.com/star-half.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Half star&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-empty.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Empty star&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-empty.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Empty star&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-empty.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Empty star&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-empty.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Empty star&#34;/&gt;&lt;/span&gt;
&lt;/span&gt;
  &lt;svg width=&#34;200&#34; height=&#34;50&#34; viewBox=&#34;0 0 200 50&#34; xmlns=&#34;http://www.w3.org/2000/svg&#34; role=&#34;img&#34; aria-label=&#34;Film rating distribution chart&#34;&gt;
   &lt;rect x=&#34;1&#34; y=&#34;37&#34; width=&#34;18&#34; height=&#34;13&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;0.5 stars: 11 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;21&#34; y=&#34;29&#34; width=&#34;18&#34; height=&#34;21&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;1.0 stars: 18 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;41&#34; y=&#34;33&#34; width=&#34;18&#34; height=&#34;17&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;1.5 stars: 14 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;61&#34; y=&#34;31&#34; width=&#34;18&#34; height=&#34;19&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;2.0 stars: 16 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;81&#34; y=&#34;12&#34; width=&#34;18&#34; height=&#34;38&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;2.5 stars: 32 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;101&#34; y=&#34;13&#34; width=&#34;18&#34; height=&#34;37&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;3.0 stars: 31 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;121&#34; y=&#34;7&#34; width=&#34;18&#34; height=&#34;43&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;3.5 stars: 36 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;141&#34; y=&#34;0&#34; width=&#34;18&#34; height=&#34;50&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;4.0 stars: 42 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;161&#34; y=&#34;19&#34; width=&#34;18&#34; height=&#34;31&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;4.5 stars: 26 films&lt;/title&gt;
   &lt;/rect&gt;&lt;rect x=&#34;181&#34; y=&#34;4&#34; width=&#34;18&#34; height=&#34;46&#34; fill=&#34;var(--accent-colour)&#34; class=&#34;rating-bar&#34;&gt;
    &lt;title&gt;5.0 stars: 39 films&lt;/title&gt;
   &lt;/rect&gt;
  &lt;/svg&gt;
  &lt;span class=&#34;right-label&#34;&gt;
&lt;span class=&#34;star-rating&#34;&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;/span&gt;
&lt;/span&gt;
 &lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;It’s meant to just be a visual flourish, not a semantic title or anything, so I initially just added a &lt;code&gt;::before&lt;/code&gt; selector to each element, customising the
&lt;code&gt;content&lt;/code&gt; to have the right value. I didn’t like the duplication, though. Now, instead, I do this:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;::before&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;attr&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;data-title&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each component defines its title in a data attribute: &lt;code&gt;&amp;lt;div class=&amp;#34;raised-box&amp;#34; data-title=&amp;#34;an example&amp;#34;&amp;gt;&lt;/code&gt;, and the &lt;code&gt;attr&lt;/code&gt; function plucks it out and
puts it in the content rule. It’s worth noting that this is only widely supported for the &lt;code&gt;content&lt;/code&gt; rule, you can’t yet use it for colours or dimensions
or other things. It still feels a bit magical, though. Like you’re making a reusable, customisable component with just HTML and CSS.&lt;/p&gt;
&lt;h3 id=&#34;layers&#34;&gt;layers&lt;/h3&gt;
&lt;p&gt;I’d come across layers a few times, but I never found a need for them. It seemed like something you’d only really need with complicated design systems,
or something. Then I had a problem and layers were the perfect solution!&lt;/p&gt;
&lt;p&gt;The headings on this site have a whole bunch of CSS attached to them. Currently they look a bit like blue painter’s tape with handwriting on them.
This style is applied to all headers, and then the various places that &lt;em&gt;don’t&lt;/em&gt; want it had to manually reset everything. I got fed up with that, so
decided to add a &lt;code&gt;plain-header&lt;/code&gt; class which would “disable” the extra styling:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nt&#34;&gt;h2&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nt&#34;&gt;h3&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nt&#34;&gt;h4&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nt&#34;&gt;h5&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nt&#34;&gt;h6&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-cm&#34;&gt;/* common styles for all headers, regardless */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;font-family&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-s2&#34;&gt;&amp;#34;Chris Hand&amp;#34;&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;sans-serif&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;font-size&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;font-size-xxlarge&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;highlight-colour&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;margin&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;medium&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;chroma-ni&#34;&gt;space&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;line-height&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:not&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nc&#34;&gt;.plain-header&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-cm&#34;&gt;/* do fancy stuff if it _doesn&amp;#39;t_ have the .plain-header class */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;transform&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;rotate&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-mf&#34;&gt;.15&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;deg&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;padding&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;smedium-space&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;font-weight&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;800&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-na&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;drop-shadow&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;rgb&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-nt&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;            &lt;span class=&#34;chroma-na&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;none&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;            &lt;span class=&#34;chroma-na&#34;&gt;text-decoration-skip-ink&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-ni&#34;&gt;auto&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This might seem a bit backwards but I want the default to be styled, as those are far more common. I added this change,
sprinkled around some “plain-header” classes, tidied up a lot of duplicate CSS, and things mostly worked. Mostly.
Some things stopped working. In a few places the headers had slight tweaks, and they stopped working entirely.&lt;/p&gt;
&lt;p&gt;It turns out in moving the rules from a plain &lt;code&gt;h2&lt;/code&gt; selector to a &lt;code&gt;h2:not(.plain-header)&lt;/code&gt;, I’d made them more specific.
Previously a selector like &lt;code&gt;h2.special&lt;/code&gt; would have been more specific, so its properties would override those from the
less specific &lt;code&gt;h2&lt;/code&gt; selector. My first reaction was to try and hack around it. Changing all the overrides to
&lt;code&gt;body h2.special&lt;/code&gt; would make them more specific again, but I didn’t want to have to remember to do that forever more.&lt;/p&gt;
&lt;p&gt;Instead, I defined some custom layers, and put the header definitions in one:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;@layer&lt;/span&gt; &lt;span class=&#34;chroma-nt&#34;&gt;reset&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-nt&#34;&gt;links&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;chroma-nt&#34;&gt;headings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-o&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;chroma-nt&#34;&gt;layer&lt;/span&gt; &lt;span class=&#34;chroma-nt&#34;&gt;headings&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-nt&#34;&gt;h2&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-nt&#34;&gt;h3&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;        &lt;span class=&#34;chroma-cm&#34;&gt;/* etc */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first &lt;code&gt;@layer&lt;/code&gt; rule defines some layers that will be handled in a specific order: first the “reset” layer, then
the “links” layer, then the “headings” layer, and then anything not in a layer after that. Rules in one layer don’t
have to worry about beating the specificity of rules in another layer, because they’re handled separately.&lt;/p&gt;
&lt;p&gt;Note that I couldn’t just put the headings in a layer and call it a day: their rules have to come after the CSS reset,
and after the normal link styles, as headers have a bit of extra styling for nested links. If the CSS reset weren’t in
a layer, then the &lt;code&gt;* { margin: 0; padding: 0; }&lt;/code&gt; type reset would apply over the rules from the headings layer.&lt;/p&gt;
&lt;p&gt;Using layers is definitely a bit fiddly, and is not necessary for a lot of sites, but it’s a much cleaner alternative
to ugly specificity hacking.&lt;/p&gt;
&lt;h3 id=&#34;things-im-looking-forward-to&#34;&gt;Things I’m looking forward to&lt;/h3&gt;
&lt;p&gt;There are a few things that aren’t (widely) available yet, that particularly interest me:&lt;/p&gt;
&lt;h4 id=&#34;sibling-index-and-sibling-count&#34;&gt;sibling-index() and sibling-count()&lt;/h4&gt;
&lt;p&gt;These new functions are available in WebKit and Blink based browsers, but not Firefox. They give you the index of
the element within its siblings, or the count of siblings. For the film list component above, I currently have
this abomination for the overlapping posters:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:nth-child&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:nth-child&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nt&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;calc&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;133&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;overlap&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:nth-child&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nt&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;calc&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;133&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;overlap&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:nth-child&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nt&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;calc&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;133&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;overlap&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt;:nth-child&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nt&#34;&gt;5&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;calc&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;133&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;overlap&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These could be replaced with something like:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-na&#34;&gt;z-index&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;calc&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;sibling-count&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;sibling-index&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-na&#34;&gt;left&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;calc&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-mi&#34;&gt;133&lt;/span&gt;&lt;span class=&#34;chroma-kt&#34;&gt;px&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-nf&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;--&lt;/span&gt;&lt;span class=&#34;chroma-n&#34;&gt;overlap&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt;sibling-index&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;chroma-o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;chroma-mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As an added bonus, it would scale to any number of posters. &lt;code&gt;index&lt;/code&gt; being 1-based not 0-based is a
bit unfortunate as it makes using it in calculations awkward (see &lt;code&gt;1&lt;/code&gt; offset in both of those rules!),
but it’s leagues better than writing 5 separate rules.&lt;/p&gt;
&lt;h4 id=&#34;random&#34;&gt;random()&lt;/h4&gt;
&lt;p&gt;Currently only available in Safari. There are a few places where I’d like to have slight random variations
of the style. Things like elements that are rotated slightly for aesthetics; they look a bit silly if
they’re all identically positioned. I also randomise the icons and positions of my rating stars to break
up the visual monotony. Here are a few examples:&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;star-rating&#34;&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;star-rating&#34;&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&#34;star-rating&#34;&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-1&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Currently I do the stars by hardcoding a bunch of classes and having the backend randomly apply one when
it generates the markup for stars. Being able to do it in CSS would be great, though.&lt;/p&gt;
&lt;h4 id=&#34;mixins&#34;&gt;Mixins&lt;/h4&gt;
&lt;p&gt;This one’s so far off it’s not even listed on &lt;a href=&#34;https://caniuse.com/&#34;&gt;caniuse.com&lt;/a&gt; yet. Mixins are another handy feature of
SCSS, that allow you to define reusable blocks of rules, then import them when needed:&lt;/p&gt;
&lt;pre class=&#34;chroma-chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-k&#34;&gt;@mixin&lt;/span&gt;&lt;span class=&#34;chroma-nf&#34;&gt; fancy-background&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-na&#34;&gt;background&lt;/span&gt;&lt;span class=&#34;chroma-o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;chroma-cm&#34;&gt;/*...&lt;/span&gt;&lt;span class=&#34;chroma-c&#34;&gt;*/&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nc&#34;&gt;.some-element&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-k&#34;&gt;@include&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt; fancy-background&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-nn&#34;&gt;#other-element&lt;/span&gt; &lt;span class=&#34;chroma-p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;    &lt;span class=&#34;chroma-k&#34;&gt;@include&lt;/span&gt;&lt;span class=&#34;chroma-nd&#34;&gt; fancy-background&lt;/span&gt;&lt;span class=&#34;chroma-p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;chroma-line&#34;&gt;&lt;span class=&#34;chroma-cl&#34;&gt;&lt;span class=&#34;chroma-p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would be particularly useful if you can’t easily control the markup to add classes to everything. For example, my
footnotes are rendered by a markdown plugin, and don’t easily have a way to add extra classes to them. To style it the
same as another element, I currently duplicate a bunch of rules between them both. Mixins would allow me to define those
rules once, and then import them in both places.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Aside from JavaScript, which seems to just endlessly reinvent new frameworks and ways to make the most sprawling dependency tree possible. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;It also means that if your website’s syntax highlighting library doesn’t seem to understand nested rules, you can just pretend your perfectly valid CSS is actually SCSS and it’ll magically work. Grumble, grumble. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:2&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>How tech companies failed to build the Star Trek computer</title>
        <link href="https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/"/>
        <updated>2025-07-16T00:00:00Z</updated>
        <id>https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image right&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/enterprise-computer-room.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/enterprise-computer-room.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/how-tech-companies-failed-to-build-the-star-trek-computer/enterprise-computer-room.jpg&#34; alt=&#34;Still from an episode of Star Trek: The Next Generation, with various characters stood around in a computer core room&#34; loading=&#34;lazy&#34; width=&#34;500&#34; height=&#34;376&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;A computer core room on the Enterprise-D&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In most Star Trek series, the ship or station computer is ever-present in the
background, waiting to be called on by the main characters&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. It nearly
always does exactly the right thing, and there’s little limit to the functions
it can perform. Take this mundane example from DS9:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;KIRA: Computer, establish link with the Bajoran Medical Index for the Northwestern District. &lt;br/&gt;
COMPUTER: Link established. &lt;br/&gt;
KIRA: Access all information on Doctor Surmak Ren. &lt;br/&gt;
COMPUTER: There are no records matching that name. &lt;br/&gt;
KIRA: Try the Northeastern District, same search. &lt;br/&gt;
COMPUTER: Doctor Surmak Ren, currently serving as Chief Administrator of the Ilvian Medical Complex. &lt;br/&gt;
KIRA: Computer, open a channel to the Ilvian Medical Complex. Administrator’s office.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The computer is doing some kind of networking to a database only identified by
name. It does a search and summarises the lack of results. It then repeats the
process with another database, and succinctly announces the results. Finally,
it opens a communication channel to a specific room in a facility, based only
on its name.&lt;/p&gt;
&lt;p&gt;This whole interaction is remarkably boring&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:2&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. Kira doesn’t have to know
any URLs or API endpoints, or what protocol she wants to use. She doesn’t have
to open a specific app and then login and then try the query again. She just
says what she wants and the computer does it.&lt;/p&gt;
&lt;p&gt;It seems like this should be one of the most easily obtainable bits of sci-fi
wizardry with our current technology. We have multiple massive companies
throwing lots of money at digital assistants, LLMs that are improving at an
insane rate, but we’re somehow not even close to the usability or usefulness of
the Trek computers. What gives?&lt;/p&gt;
&lt;h3 id=&#34;boring-is-well-boring&#34;&gt;Boring is, well, boring.&lt;/h3&gt;
&lt;p&gt;Larry Page once said something that might help explain it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Star Trek computer doesn’t seem that interesting. They ask it random
questions, it thinks for a while. I think we can do better than that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the same Larry Page that founded Google, whose mission statement is
“to organize the world’s information and make it universally accessible and
useful”. Of all people, surely he should find an omnipresent computer that can
answer ‘random questions’ interesting?! It seems like it should be the epitome
of Google’s mission!&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Google’s “better than that” seems to have been to stuff LLMs into every product
they can, even when you don’t want them there. Even when they’re worse than the
normal content they displace. These things look &lt;em&gt;exciting&lt;/em&gt; when they’re part of
a scripted demo at Google I/O, but they fall flat and just get in the way when
they’re exposed to the reality of day-to-day use.&lt;/p&gt;
&lt;p&gt;The Star Trek computer is the opposite: it isn’t snazzy, but it is genuinely
useful. That means it’s not an attractive target for the company execs who want
marketing opportunities, and it’s not appealing for engineers who need to
demonstrate “impact”. But even if Google did try to make the Trek computer,
there are other problems…&lt;/p&gt;
&lt;h3 id=&#34;assistants-need-to-be-free&#34;&gt;Assistants need to be free&lt;/h3&gt;
&lt;p&gt;A significant amount of tech companies’ business models currently revolves
around trapping users in walled gardens. They want you using &lt;em&gt;their&lt;/em&gt; ecosystem;
that way they get more data from you, and you’re more likely to spend more money
on their other offerings that work together. There’s barely any incentive to
allow any kind of interoperability with other platforms outside carefully
contracted integrations.&lt;/p&gt;
&lt;p&gt;I remember trying to help a family member move their photos from iCloud to
Google Photos. At one point they turned around and said, exasperated, “why is
this so hard? Aren’t they both in the cloud?!”. It’s easy to dismiss that as
someone who hasn’t quite grasped the fundamental idea that “the cloud” is just
someone else’s computers, but that’s not the whole story. There’s no reason why
there shouldn’t be a quick and easy transfer: both services already allow
uploading and downloading, there’s just no incentive for the companies involved
to make it so&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:3&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;These kinds of misaligned incentives and walled garden business models cause
even more problems when it comes to digital assistants. Siri is basically never
going to be able to interact with, say, your Google Drive; &lt;del&gt;Bard&lt;/del&gt; Gemini
is never going to be able to send a message via iMessage. Even when there are
appropriately blessed interactions, they’re so clunky. Can you imagine Captain
Picard saying “Computer, ask the turbolift skill to take me to deck 5”?&lt;/p&gt;
&lt;h3 id=&#34;someone-elses-computer&#34;&gt;Someone else’s computer&lt;/h3&gt;
&lt;p&gt;Software issues aside, there’s still a key difference between the Star Trek
computers and our current batch of digital assistants: where they run. The
Trek computers are all housed within the ship or station they serve; they can
connect elsewhere to gather information, but they run entirely independently.
If they go wrong, a local engineer can go in and fix things. While some of our
assistants may have physical hardware in your home, they don’t work without
a vast cloud apparatus behind them. If your Internet connection fails, they
become paperweights. If the company running them decide to remove some
functionality you depend on, you have no recourse.&lt;/p&gt;
&lt;p&gt;That kind of helplessness isn’t limited to assistants, either. There’s a rapidly
growing trend of being unable to modify or repair hardware you fully own and
control. Part of this is just that they’re becoming more complex: it’s a lot
harder to replace a microchip than a gear, but companies are also going out
of their way to make it more difficult for users through draconian DRM
regimes&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:4&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt; and aggressive intellectual property enforcement. If the US Navy
can’t repair their own equipment because a corporation says so, what hope do
consumers have?&lt;/p&gt;
&lt;p&gt;We’re approaching a point where you don’t actually own anything. Software
is cloud and subscription based, hardware is unrepairable. Even cars can
be remotely updated and have features added or removed. The Federation wouldn’t
allow a third party control over their ships&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:5&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;, so why are we so happy to
put up with it in everything we consume?&lt;/p&gt;
&lt;h3 id=&#34;a-small-ray-of-hope&#34;&gt;A small ray of hope?&lt;/h3&gt;
&lt;p&gt;The most promising way of tackling all of these problems is through legislation.
The EU’s &lt;a href=&#34;https://digital-markets-act.ec.europa.eu/index_en&#34;&gt;Digital Market Act&lt;/a&gt;
is an attempt to force ‘gatekeepers’ like Google, Apple and Meta, to allow
third-party access to their services. It seems like a pretty reasonable
approach, but the tech companies are unsurprisingly resisting it. Apple in
particular have gone out of their way to refuse to comply, and when forced to
do so have limited the functionality to people in Europe.
Still, the DMA is a promising start, and if similar legislation is introduced
(and robustly enforced) elsewhere it might start forcing companies to behave a
bit better.&lt;/p&gt;
&lt;p&gt;There are also smaller companies that actually do the right thing.
&lt;a href=&#34;https://frame.work/gb/en&#34;&gt;Framework&lt;/a&gt; make laptops that are user-serviceable;
&lt;a href=&#34;https://www.fairphone.com/&#34;&gt;Fairphone&lt;/a&gt; do the same for mobile phones. Smaller
software companies provide useful, open APIs. The average person on the street
will probably have never heard of these, unfortunately, but they do still
exist. Maybe as the bigger tech companies tighten the screws more, people will
turn to alternatives like this? Or maybe we’ll just keep accepting that our
computers work for everyone but us?&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Unless, of course, the computer is playing the role of the episode’s
MacGuffin and has contracted space-computer-COVID or something, then it’s a lot
less in-the-background. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;It’s almost like it only exists to move the plot along. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:2&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;You can generally export your data, thanks to a combination of legislation
and efforts like Google’s “Data Liberation Front”, but I’ve never seen an export
format that could then just be imported into an equivalent commercial product. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:3&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;Oh, you’ve changed the screen on your iPhone? Better hope it can do the
secret handshake with the Apple hardware. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:4&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:5&#34;&gt;
&lt;p&gt;I think there might actually have been an episode where that did in fact
happen. We’ll just ignore that as a plot contrivance. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:5&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>Apple, Google and aligned incentives</title>
        <link href="https://chameth.com/apple-google-aligned-incentives/"/>
        <updated>2020-10-17T00:00:00Z</updated>
        <id>https://chameth.com/apple-google-aligned-incentives/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image right&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/apple-google-aligned-incentives/htc-dream.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/apple-google-aligned-incentives/htc-dream.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/apple-google-aligned-incentives/htc-dream.jpg&#34; alt=&#34;White HTC Dream mobile phone&#34; loading=&#34;lazy&#34; width=&#34;300&#34; height=&#34;225&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The HTC Dream, the first phone released running Android.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For the past decade I’ve exclusively used Android phones. I got the HTC Dream (aka the T-Mobile G1)
shortly after it came out, and dutifully upgraded every 1-2 years. In that timespan I used Android
as the basis for my Master’s Thesis, took a job on the Android team at Google, and eventually
became a contractor specialising in Android app development. So when I switched to using an iPhone
earlier this year a few people were surprised&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:1&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id=&#34;the-good-old-days&#34;&gt;The good old days&lt;/h3&gt;
&lt;p&gt;When Android was announced in 2007 – alongside the formation of the Open Handset Alliance – it
was positioned as a bastion of openness: it would be built on open standards and the operating
system would be open source. At the time iPhones were strongly coupled to iTunes and Apple was
exercising strict control over what app developers could do.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;When the HTC Dream was released it lived up to expectations. You could write apps for it without
shelling out for a Mac! You could get root access, and it was running Linux under the hood! A
whole ecosystem of custom firmwares and bootloaders started to appear, thanks to the open source
nature of the OS. It shipped with some Google apps, but they were just normal apps that served
as examples of what could be done.&lt;/p&gt;
&lt;p&gt;After the Dream came a line of Nexus devices. These were Android’s flagship devices, designed to
show off what a good Android phone should look like. Both the hardware and software releases
tended to feature interesting, useful upgrades. Some of the original open source apps were
replaced with closed source, Google proprietary ones, but that was OK - the open source
versions lived on in the open source project as examples of what you &lt;em&gt;could&lt;/em&gt; do. The devices
allowed flashing custom firmware, and the OS source was always released… eventually.&lt;/p&gt;
&lt;h3 id=&#34;the-downfall&#34;&gt;The downfall&lt;/h3&gt;
&lt;figure class=&#34;image left&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/apple-google-aligned-incentives/pixels.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/apple-google-aligned-incentives/pixels.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/apple-google-aligned-incentives/pixels.jpg&#34; alt=&#34;Pixel and Pixel XL phones&#34; loading=&#34;lazy&#34; width=&#34;400&#34; height=&#34;363&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The Pixel and Pixel XL&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Over time, Android has got less and less free. The Nexus line of phones gave way to the Pixel
range, which got rid of the clearly demarcated border where Android ended and Google began.
&lt;a href=&#34;https://www.android.com/android-11/&#34;&gt;The Android 11 highlights&lt;/a&gt; list is dotted with sections
that say “On Pixel devices…”, and every single one is a software feature that could be
implemented on any device, but Google have decided to keep it proprietary instead of releasing
it as part of the open source platform.&lt;/p&gt;
&lt;p&gt;At the same time more and more functionality has been added to Google Play Services. This was
originally a shared location for Google specific services - in 2012 it merely handled some
Google+ functionality and dealing with OAuth for Google accounts. These days it contains a
huge swathe of Google services, as well as platform functionality such as push notifications,
barcode scanning, geolocation, and so on. Google Play Services isn’t part of the Android
Open Source Project, and is only available under license from Google. You can’t really have
a device without these functions, so as a manufacturer you have the choice between agreeing
to whatever terms Google requires&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a class=&#34;footnote-ref&#34; href=&#34;#fn:2&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; or spending an awful lot of development time creating an
alternative.&lt;/p&gt;
&lt;p&gt;While these are fairly abstract arguments about what a free platform should look like,
during the same period there has been a marked restriction in how you can actually use
Android devices – both as an end-user and as a developer. iOS has always had very stark
restrictions on what apps can do in the background; Android started its life allowing
pretty much anything, like any good general purpose computing device. This inevitably
lead to lots of apps doing lots of things that ranged from stupid to mildly suboptimal,
creating a &lt;a href=&#34;https://en.wikipedia.org/wiki/Tragedy_of_the_commons&#34;&gt;tragedy of the commons&lt;/a&gt;
amongst apps. The victim was the phone’s battery life, and Google’s solution was a
progressive series of restrictions on what apps can do and when. This includes limits
on when push notifications are delivered to a device, how frequently apps can wake up,
and so forth. Some of these can be bypassed by the end-user, but not all, and the
process is fairly cumbersome.&lt;/p&gt;
&lt;h3 id=&#34;aligned-incentives&#34;&gt;Aligned incentives&lt;/h3&gt;
&lt;p&gt;From my point of view, Android and iOS are now pretty much in a similar place.
They’re not general purpose computers, but
&lt;a href=&#34;https://daringfireball.net/linked/2020/08/14/orland-epic-game-consoles&#34;&gt;app consoles&lt;/a&gt;:
much like games consoles they consist of hardware and software that the user lacks
control over but accepts in order to access the library of apps/games.&lt;/p&gt;
&lt;p&gt;Given there’s no clear winner between them in terms of hardware and software, my
decision came down to a more holistic question: how aligned are their
incentives to my own? Apple is a product company: they make their money by
producing shiny things that people want to purchase; Google is an advertising
company: they make their money by using my personal information to show me
targeted adverts.&lt;/p&gt;
&lt;p&gt;For Google, Android was originally a strategic move to ensure that
Apple couldn’t dominate the mobile web, and by extension the revenue from ads.
As a company, there’s nothing to really push them forward in any particular
direction other than one that facilitates advertising. Of course, Google employs
tonnes of good people who want to do good things which counterbalances this, but
I’d still prefer to deal with a company that has an intrinsic motivation to
do things I want them to do, rather than one forced to by regulation, custom,
or the good intent of their employees.&lt;/p&gt;
&lt;p&gt;So the decision became obvious: if I have no strong opinions about the software
and hardware, Apple is the clear winner because their incentives are a lot
better aligned to mine.&lt;/p&gt;
&lt;hr/&gt;
&lt;h3 id=&#34;image-credits&#34;&gt;Image credits&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Photo&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Licence&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTC Dream&lt;/td&gt;
&lt;td&gt;Akela NDE&lt;/td&gt;
&lt;td&gt;CC BY-SA 3.0&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;https://commons.wikimedia.org/w/index.php?curid=6680413&#34;&gt;Wikimedia&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pixel and Pixel XL&lt;/td&gt;
&lt;td&gt;Maurizio Pesce from Milan, Italia&lt;/td&gt;
&lt;td&gt;CC BY 2.0&lt;/td&gt;
&lt;td&gt;&lt;a href=&#34;https://commons.wikimedia.org/w/index.php?curid=52110138&#34;&gt;Wikimedia&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Or, at least, politely feigned surprise. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:1&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;And Google’s terms were particularly onerous: as well as requiring Chrome and Google
Search to be preinstalled, they prevented manufacturers from selling any devices
powered by alternative versions of Android - e.g., Samsung wouldn’t be allowed to
sell a refrigerator that ran Amazon’s FireOS. The European Union handed Google a
$5,000,000,000 fine for this anti-competitive behaviour, which Google are still in
the process of contesting. &lt;a class=&#34;footnote-backref&#34; href=&#34;#fnref:2&#34; role=&#34;doc-backlink&#34;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </entry>
    <entry>
        <title>Why you should be using HTTPS</title>
        <link href="https://chameth.com/why-you-should-be-using-https/"/>
        <updated>2016-06-17T00:00:00Z</updated>
        <id>https://chameth.com/why-you-should-be-using-https/</id>
        <content xml:lang="en" type="html">&lt;figure class=&#34;image left&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/why-you-should-be-using-https/https-everywhere.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/why-you-should-be-using-https/https-everywhere.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/why-you-should-be-using-https/https-everywhere.jpg&#34; alt=&#34;The EFF&amp;#39;s HTTPS Everywhere logo&#34; loading=&#34;lazy&#34; width=&#34;300&#34; height=&#34;260&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The EFF’s HTTPS Everywhere logo&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One of my favourite hobbyhorses recently has been the use of HTTPS, or lack thereof. HTTPS is the
thing that makes the little padlock appear in your browser, and has existed for over 20 years.
In the past, that little padlock was the exclusive preserve of banks and other ‘high security’
establishments; over time its use has gradually expanded to most (but not all) websites
that handle user information, and the time is now right for it to become ubiquitous.&lt;/p&gt;
&lt;h3 id=&#34;why-use-https&#34;&gt;Why use HTTPS?&lt;/h3&gt;
&lt;p&gt;There are numerous advantages to using HTTPS, both for the users of a website and for the
operator:&lt;/p&gt;
&lt;h4 id=&#34;privacy&#34;&gt;Privacy&lt;/h4&gt;
&lt;p&gt;The most obvious advantage is that HTTPS gives your users additional privacy. An insecure (HTTP)
request can potentially be read by anyone on the same network, or the network operators, or anyone
who happens to operate a network along the path between the user and the server.&lt;/p&gt;
&lt;p&gt;Users on shared WiFi networks (such as those in coffee shops, hotels, or offices) are particularly
vulnerable to passive sniffing by anyone else on that network. If the network is open (as is
frequently the case) then anyone in radio range can see exactly what the user is up to.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h4 id=&#34;integrity&#34;&gt;Integrity&lt;/h4&gt;
&lt;p&gt;HTTPS also helps to maintain the integrity of your site. With a plain HTTP request, there’s nothing
to stop anyone in between the server and the user from modifying the content of the request or the
response. This is a frequent tactic used by annoying WiFi gateways (such as the ones &lt;a href=&#34;http://justinsomnia.org/2012/04/hotel-wifi-javascript-injection/&#34;&gt;you’d find in
a hotel&lt;/a&gt;), dubious ISPs who want
to serve you extra adverts, or just plain old nefarious attackers.&lt;/p&gt;
&lt;p&gt;If you’re trying to convey some kind of information to users (and if you aren’t, why exactly are
you running a website again?) it seems beneficial to both you and them if the information arrives
as you intended, rather than in a modified form due to someone or something tampering with it.&lt;/p&gt;
&lt;h4 id=&#34;security&#34;&gt;Security&lt;/h4&gt;
&lt;p&gt;If your website has any kind of authentication, or session identifiers, it becomes extremely
vulnerable to an attacker monitoring the traffic and stealing the credentials. This was
starkly demonstrated in 2010 when &lt;a href=&#34;https://en.wikipedia.org/wiki/Firesheep&#34;&gt;Firesheep&lt;/a&gt; was
released. This tool allowed anyone to quickly and automatically hijack social media accounts of
anyone on the same network who was using HTTP to access them.&lt;/p&gt;
&lt;p&gt;Even if your login pages are served over HTTPS, if you send a single session ID cookie over HTTP
(such as a page you decided wasn’t particularly ‘important’) then an attacker can probably spoof
the user’s session and gain full access to their account.  Again, in the case of open WiFi networks
that could be anyone in radio range.&lt;/p&gt;
&lt;h4 id=&#34;search-engine-rankings&#34;&gt;Search engine rankings&lt;/h4&gt;
&lt;p&gt;Some search engines use HTTPS as a signal in their ranking algorithms. &lt;a href=&#34;https://security.googleblog.com/2014/08/https-as-ranking-signal_6.html&#34;&gt;Google announced in
2004&lt;/a&gt; that it was using
the presence of HTTPS as a small positive signal, but that it may strengthen that signal over time
as more and more websites switch to using a secure transport. It’s not unthinkable that at some
point in the future there will be HTTPS-only search engines.&lt;/p&gt;
&lt;h3 id=&#34;but-but-but&#34;&gt;But… But… But…&lt;/h3&gt;
&lt;p&gt;There are lots of excuses for not implementing HTTPS. Most of them are either misguided or outdated.&lt;/p&gt;
&lt;h4 id=&#34;its-too-expensive-andor-complicated&#34;&gt;It’s too expensive and/or complicated&lt;/h4&gt;
&lt;p&gt;In the past, getting HTTPS certificates was a pain. A number of free suppliers have existed for
a while but the process for getting their certificates wasn’t particularly straight forward, and
many imposed arbitrary restrictions on the certificate parameters. Even once you had the
certificate, you had to fiddle about with your HTTP server configuration to make it work, remember
to manually get a new certificate when the old one expired, and lots of other annoying busywork.&lt;/p&gt;
&lt;p&gt;With the arrival of &lt;a href=&#34;https://letsencrypt.org/&#34;&gt;Let’s Encrypt&lt;/a&gt;, all that changed. You can retrieve
and deploy a free HTTPS certificate with two or three commands. Renewal can be handled completely
automatically with a single command executed by cron.&lt;/p&gt;
&lt;h4 id=&#34;theres-no-point-nothing-on-my-site-is-sensitive&#34;&gt;There’s no point; nothing on my site is sensitive&lt;/h4&gt;
&lt;p&gt;You might not think your content warrants privacy, but can you speak for everyone who accesses it?
Even content that seems mundane to you — such as travel advice, or technical writing — could be
used to build up a profile of a user. If an attacker is monitoring traffic in a coffee shop and
sees a user looking at travel advice and weather forecasts for a foreign country, he could use that
information to plan a burglary knowing that the user will be away. Similarly, some content which
is perfectly mundane to you may actually be very sensitive in other countries with repressive
governments. HTTPS makes it much harder for these people to snoop on traffic.&lt;/p&gt;
&lt;p&gt;From another angle, if you’re offering any kind of information, instructions, or especially file
downloads, there’s a severe risk to users if the content is modified on its way to them. An evil
sysadmin could rewrite your travel advice to suggest visiting the local drug dealer’s hangout, or
replace your download with a malware-infested version.&lt;/p&gt;
&lt;h4 id=&#34;https-is-slower-uses-more-resources-etc&#34;&gt;HTTPS is slower, uses more resources, etc&lt;/h4&gt;
&lt;p&gt;Back in 1995 this might have been a valid argument. Enabling HTTPS on a modern server will make
an almost negligible difference to performance. If you also enable HTTP/2 (which most
implementations only support over HTTPS), it’s likely to actually use fewer resources, and result
in a faster, smoother experience for your users. HTTP/2 was designed to work with HTTPS, and
designed with modern requirements and networking techniques in mind.&lt;/p&gt;
&lt;p&gt;CloudFlare have an &lt;a href=&#34;https://www.cloudflare.com/http2/&#34;&gt;excellent demonstration&lt;/a&gt; of the benefits of
HTTP/2, and it can show speed improvements of 2-3x in a typical environment. On top of being faster,
HTTP/2 uses fewer connections which results in less resource overhead on both the server and the
client.&lt;/p&gt;
&lt;h3 id=&#34;so-what-are-you-waiting-for&#34;&gt;So what are you waiting for?&lt;/h3&gt;
&lt;p&gt;If you run a website and aren’t using HTTPS, &lt;a href=&#34;https://certbot.eff.org/&#34;&gt;give it a try&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
</feed>
