<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jane Ori</title>
    <description>The latest articles on Forem by Jane Ori (@janeori).</description>
    <link>https://forem.com/janeori</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F790708%2Fe61d3abe-e086-407e-8424-21249ea063ad.png</url>
      <title>Forem: Jane Ori</title>
      <link>https://forem.com/janeori</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/janeori"/>
    <language>en</language>
    <item>
      <title>Customizing the MacOS Terminal with help from AI</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Fri, 06 Mar 2026 14:36:27 +0000</pubDate>
      <link>https://forem.com/janeori/customizing-the-macos-terminal-with-help-from-ai-3f91</link>
      <guid>https://forem.com/janeori/customizing-the-macos-terminal-with-help-from-ai-3f91</guid>
      <description>&lt;p&gt;After spending a year traveling the world, I've acquired a new Macbook Air and had to set up my terminal interface again.&lt;/p&gt;

&lt;p&gt;I do not normally enjoy this process but I loved it this time.&lt;br&gt;
A continuous conversation with AI (Gemini specifically) lead to every random idea I had becoming realized in my &lt;code&gt;.zshrc&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;There are some goodies in here I suspect you'll enjoy too.&lt;/p&gt;
&lt;h2&gt;
  
  
  Basic settings and imports
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# import things we need #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="c"&gt;# load 'live' hooks to execute things every call,&lt;/span&gt;
&lt;span class="c"&gt;# load TAB completion&lt;/span&gt;
autoload &lt;span class="nt"&gt;-Uz&lt;/span&gt; add-zsh-hook compinit

&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# Enable Tab Completion #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="c"&gt;# 1. Prevent duplicate entries in paths&lt;/span&gt;
&lt;span class="nb"&gt;typeset&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; FPATH PATH
&lt;span class="c"&gt;# 2. Add Homebrew to FPATH for even better tab completion (if it's not present already)&lt;/span&gt;
&lt;span class="nv"&gt;FPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/homebrew/share/zsh/site-functions:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FPATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# 3. Enable TAB completion&lt;/span&gt;
compinit

&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# Toggling Misc options #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="c"&gt;# make ls pretty&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CLICOLOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Basic aliases
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# make the current terminal tab aware of things just installed or changed&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;reload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'source ~/.zshrc'&lt;/span&gt;

&lt;span class="c"&gt;# shortcut to go to my main projects folder&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;pj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cd ~/Desktop/PropJockey"&lt;/span&gt;

&lt;span class="c"&gt;# Launch Gemini in a frameless chrome window&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gemini&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/Applications/Google&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="s2"&gt;Chrome.app/Contents/MacOS/Google&lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="s2"&gt;Chrome --app=https://gemini.google.com/app"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Whenever you modify the .zshrc file, which I did about a hundred times during this, you either have to open a new tab or run the command I've aliased with a simple "reload".&lt;/p&gt;

&lt;p&gt;I wound up installing Gemini using Chrome's built in "Install as App" feature which had the added benefit of getting Gemini in my dock. 🤙 Sweet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0gaxoryrpyff60bj3qx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0gaxoryrpyff60bj3qx.png" alt="Chrome menu → " width="800" height="1253"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Lock Function
&lt;/h2&gt;

&lt;p&gt;One of the first things I do when setting up a mac is turn sleep into hibernate. When I close the lid, I'm done for the night, no reason to sleep.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo pmset -a hibernatemode 25&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, no matter how you've configured your lock screen settings, after only a minute idle on the lock screen, the mac gets bored and falls asleep, which in my case hibernates it and I have to sit through memory reloading from the harddrive to log back in.&lt;/p&gt;

&lt;p&gt;The fix:&lt;/p&gt;

&lt;p&gt;First create a shortcut using the Shortcuts app that ships with mac. It's &lt;em&gt;great&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add a new shortcut, search for the lock screen action, add it, name it "LockMac", and you're ready for the script&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvm4jk0mj35b4mjb4rqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvm4jk0mj35b4mjb4rqc.png" alt="Shortcuts app screenshot of the lock mac shortcut" width="800" height="338"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Lock the screen without sleeping the system and keep it awake until ctrl + c to quit&lt;/span&gt;
&lt;span class="c"&gt;# alias lock='shortcuts run "LockMac" &amp;amp;&amp;amp; caffeinate -i -d -t 3600'&lt;/span&gt;
&lt;span class="c"&gt;# ^ great but this is better because I don't have to ctrl + c!&lt;/span&gt;
lock&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# Start caffeinate in the background, disown it, hide its output, and save its Process ID (PID)&lt;/span&gt;
  caffeinate &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 3600 &amp;amp;&amp;gt;/dev/null &amp;amp;!
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;caff_pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;

  shortcuts run &lt;span class="s2"&gt;"LockMac"&lt;/span&gt;

  &lt;span class="c"&gt;# Give macOS a bit to register the locked state&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;3

  &lt;span class="c"&gt;# The Magic Loop: Poll the Mac's hardware registry. &lt;/span&gt;
  &lt;span class="c"&gt;# This specific string ONLY exists while the screen is actively locked.&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;ioreg &lt;span class="nt"&gt;-n&lt;/span&gt; Root &lt;span class="nt"&gt;-d1&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"CGSSessionScreenIsLocked"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;2
  &lt;span class="k"&gt;done&lt;/span&gt;

  &lt;span class="c"&gt;# You're back! Kill the caffeinate process silently&lt;/span&gt;
  &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nv"&gt;$caff_pid&lt;/span&gt; 2&amp;gt;/dev/null

  &lt;span class="c"&gt;# Print a Bifrost-styled welcome back message&lt;/span&gt;
  &lt;span class="c"&gt;# print -P "%F{cyan}🛸 Welcome back. Sleep prevention disabled.%f"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I always have at least one terminal open so I just type &lt;code&gt;lock&lt;/code&gt; and I'm off to the bathroom in my hostel. The mac stays wide awake the whole time so I can simply type my password and continue.&lt;/p&gt;

&lt;p&gt;I use this &lt;em&gt;often&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhdgmkey7xska9rzrrsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhdgmkey7xska9rzrrsp.png" alt="screenshot of a terminal tab with the lock command ran many times" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic Node &amp;amp; NPM version mismatch warning
&lt;/h2&gt;

&lt;p&gt;One of the things any seasoned node developer has done time and time again is accidentally run their project with the wrong version, or worse, installed packages.&lt;/p&gt;

&lt;p&gt;I wanted to prevent that as best as I could so I requested a function that alerts the node version and the version specified in &lt;code&gt;.nvmrc&lt;/code&gt; or &lt;code&gt;package.json&lt;/code&gt; with pass/fail indicators based on a comparison of the values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Checks current versions against .nvmrc or package.json requirements&lt;/span&gt;
nvi&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# 1. Ensure Node is actually installed&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; node &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{red}✘ Node.js is not installed or not in PATH.%f"&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;current_node&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;current_npm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;npm &lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;req_node&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;req_npm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

  &lt;span class="c"&gt;# 2. Look for project requirements (.nvmrc takes priority for node)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;".nvmrc"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;req_node&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .nvmrc | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\r'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Fallback to package.json engines if it exists&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"package.json"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_node&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="c"&gt;# Safely extract engines.node using Node itself&lt;/span&gt;
      &lt;span class="nv"&gt;req_node&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"try { console.log(require('./package.json').engines.node || '') } catch(e) {}"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
    &lt;span class="c"&gt;# Extract engines.npm&lt;/span&gt;
    &lt;span class="nv"&gt;req_npm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"try { console.log(require('./package.json').engines.npm || '') } catch(e) {}"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# 3. Print Current Versions&lt;/span&gt;
  print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{magenta}Current Node:%f %F{cyan}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;current_node&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f"&lt;/span&gt;
  print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{magenta}Current NPM:%f  %F{cyan}v&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;current_npm&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f"&lt;/span&gt;

  &lt;span class="c"&gt;# 4. Evaluate and Print Requirements (if they exist)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_node&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{242}Project Requirements:%f"&lt;/span&gt;

    &lt;span class="c"&gt;# Node Requirement Check&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_node&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
      &lt;span class="c"&gt;# Extract just the major version number for a reliable comparison&lt;/span&gt;
      &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;clean_current&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$current_node&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'[0-9]+'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;clean_req&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_node&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'[0-9]+'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$clean_current&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$clean_req&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$current_node&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_node&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{green}✔ Node:%f &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;req_node&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;else
        &lt;/span&gt;print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{red}✘ Node:%f &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;req_node&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (Mismatch detected)"&lt;/span&gt;
      &lt;span class="k"&gt;fi
    fi&lt;/span&gt;

    &lt;span class="c"&gt;# NPM Requirement Check&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;clean_current_npm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$current_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'[0-9]+'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;clean_req_npm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'[0-9]+'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$clean_current_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$clean_req_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$current_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$req_npm&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{green}✔ NPM:%f  &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;req_npm&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="k"&gt;else
        &lt;/span&gt;print &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s2"&gt;"%F{red}✘ NPM:%f  &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;req_npm&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (Mismatch detected)"&lt;/span&gt;
      &lt;span class="k"&gt;fi
    fi
  fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just run &lt;code&gt;nvi&lt;/code&gt; (Node Version Information) and we're golden...&lt;/p&gt;

&lt;p&gt;...but we could be platinum if we called this &lt;strong&gt;automatically&lt;/strong&gt; when you change directory into one with node expected!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# Run nvi automatically #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

auto_check_node_env&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# Check if we are in a folder with Node files&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"package.json"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;".nvmrc"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="c"&gt;# Add a blank line for visual breathing room&lt;/span&gt;
    nvi
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Attach it to the 'Change Directory' hook&lt;/span&gt;
add-zsh-hook chpwd auto_check_node_env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🤌 Great success.&lt;/p&gt;

&lt;p&gt;Now every time I cd into the root of one of my node projects, it runs this command so I'll be shown without needing to remember to check. I stopped short of letting it &lt;code&gt;nvm&lt;/code&gt; into the "right" version automatically hah&lt;/p&gt;

&lt;h2&gt;
  
  
  The Beautifully Colorful Path Highlighting and Git Info
&lt;/h2&gt;

&lt;p&gt;First, I installed this color theme called Bifrost:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lysyi3m/macos-terminal-themes?tab=readme-ov-file#bifrost-download" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9a7zg6xyxqg6ruffq8zf.png" alt="A terminal color theme pallet display with corresponding color codes" width="640" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, for the current directory path, I wanted to grey out the roots of the path, highlight the project name by detecting if it's in a git repository, then highlight everything after the project name in a different color.&lt;/p&gt;

&lt;p&gt;The end result is the most significant parts being easy to distinguish and effortlessly identified at a glance. I. Love. It.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42dqg1axfaofi6wl8qfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42dqg1axfaofi6wl8qfe.png" alt="Screenshot of my terminal showing the current path dissected into colors" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's all kinds of other stuff in here like basic git status indicators, the branch, separate counts of staged and unstaged files, etc.&lt;/p&gt;

&lt;p&gt;The first part of the prompt is something that is slightly redundant indicating complete vs a failed reason, followed by a completion timestamp of the previous command AND the number of seconds the previous command took (if it was more than one).&lt;/p&gt;

&lt;p&gt;A couple line breaks after that and the typical PROMPT shows up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# Meta info of last cmd #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="c"&gt;# 1. Capture the start time of a command&lt;/span&gt;
preexec&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;timer&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$SECONDS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# 2. Calculate the difference and format it&lt;/span&gt;
calculate_timer&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$timer&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;timer_show&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$SECONDS&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;$timer&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$timer_show&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 1 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ELAPSED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{yellow}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;timer_show&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s %f"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ELAPSED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;fi
    &lt;/span&gt;&lt;span class="nb"&gt;unset &lt;/span&gt;timer
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# THE FIX: If no command was run, clear the old time!&lt;/span&gt;
    &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ELAPSED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
add-zsh-hook precmd calculate_timer


&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# Best git aware prompt #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="c"&gt;# look inside the variables every time the PROMPT is printed&lt;/span&gt;
setopt PROMPT_SUBST

&lt;span class="c"&gt;# Fetch the Mac Display Name&lt;/span&gt;
&lt;span class="nv"&gt;DISPLAY_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Define the raw escape codes for Dim and Reset&lt;/span&gt;
&lt;span class="nv"&gt;DIM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s1"&gt;[2m'&lt;/span&gt;
&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\e&lt;/span&gt;&lt;span class="s1"&gt;[22m'&lt;/span&gt;
&lt;span class="c"&gt;# %{${DIM}%}%F{yellow}%~%f%{${RESET}%}&lt;/span&gt;

set_surgical_path&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;exit_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="c"&gt;# capture exit code of previous command&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;repo_root
  &lt;span class="c"&gt;# Get the absolute path to the repo root&lt;/span&gt;
  &lt;span class="nv"&gt;repo_root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="c"&gt;# Start with a blank slate&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$repo_root&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;full_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# 1. Everything BEFORE the repo root&lt;/span&gt;
    &lt;span class="c"&gt;# We take the directory of the repo_root (its parent)&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;parent_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$repo_root&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;parent_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
    &lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;/#&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/~&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# Clean up Home path&lt;/span&gt;

    &lt;span class="c"&gt;# 2. The Repo folder itself&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;repo_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$repo_root&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# 3. Everything AFTER the repo root&lt;/span&gt;
    &lt;span class="c"&gt;# We remove the repo_root path from the full_path&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;full_path&lt;/span&gt;&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;$repo_root&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# Assemble: Dim Prefix + Bright Repo + Dim Suffix&lt;/span&gt;
    &lt;span class="nv"&gt;DYNAMIC_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DIM&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%}%F{white}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%}"&lt;/span&gt;
    DYNAMIC_PATH+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{magenta}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f"&lt;/span&gt;
    DYNAMIC_PATH+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{blue}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f"&lt;/span&gt;
    &lt;span class="c"&gt;# Get the branch name, fallback to short hash if in detached HEAD&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;git_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--show-current&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; git rev-parse &lt;span class="nt"&gt;--short&lt;/span&gt; HEAD 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Check for Action States (Merging / Rebasing)&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;git_action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$repo_root&lt;/span&gt;&lt;span class="s2"&gt;/.git/MERGE_HEAD"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nv"&gt;git_action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{yellow}(merge)%f "&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$repo_root&lt;/span&gt;&lt;span class="s2"&gt;/.git/rebase-merge"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$repo_root&lt;/span&gt;&lt;span class="s2"&gt;/.git/rebase-apply"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nv"&gt;git_action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{yellow}(rebase)%f "&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Check Clean/Dirty Status&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;git_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;git_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$git_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nv"&gt;git_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{green}✔%f"&lt;/span&gt; &lt;span class="c"&gt;# Perfectly clean&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c"&gt;# Check for staged changes (+) and unstaged/untracked files (*)# Count lines matching staged (column 1) and unstaged/untracked (column 2)&lt;/span&gt;
      &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;staged_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$git_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'^[AMRCD]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;unstaged_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$git_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'^.[MD?]'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

      &lt;span class="c"&gt;# If there are staged files, add the yellow count&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$staged_count&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;git_state+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{yellow}+&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;staged_count&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f"&lt;/span&gt;
      &lt;span class="k"&gt;fi&lt;/span&gt;

      &lt;span class="c"&gt;# If there are unstaged/untracked files, add the red count&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$unstaged_count&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Add a space for readability if we also have staged files (e.g., +2 *3)&lt;/span&gt;
        &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$git_state&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git_state+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;
        git_state+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{red}*&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;unstaged_count&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f"&lt;/span&gt;
      &lt;span class="k"&gt;fi
    fi&lt;/span&gt;

    &lt;span class="c"&gt;# Assemble the final Git string&lt;/span&gt;
    &lt;span class="nv"&gt;GIT_INFO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;" on %F{cyan}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;git_branch&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;git_action&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;git_state&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# Not in a repo: Show the whole path dimmed&lt;/span&gt;
    &lt;span class="nv"&gt;DYNAMIC_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DIM&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%}%F{magenta}%~%f%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%}"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Define the Prompt&lt;/span&gt;
  &lt;span class="c"&gt;# %D{%H:%M:%S} = Hour:Minute:Second&lt;/span&gt;
  &lt;span class="c"&gt;# %n = username&lt;/span&gt;
  &lt;span class="c"&gt;# %~ = current directory (shortened)&lt;/span&gt;
  &lt;span class="c"&gt;# %# = % for users, # for root (I replaced this with: %(!.#.$))&lt;/span&gt;
  &lt;span class="c"&gt;# %(!.#.$) = # for root, $ for users&lt;/span&gt;
  &lt;span class="c"&gt;# The %(?.X.Y) syntax means: If last exit code was 0, show X, else show Y ## %(?.%F{green}Completed.%F{red}Stopped)%f&lt;/span&gt;

  &lt;span class="nb"&gt;local nl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="c"&gt;# ${nl}&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;status_line&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$HISTCMD&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; &lt;span class="nv"&gt;$LAST_HISTCMD&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

    &lt;span class="c"&gt;# 2. The Exit Code Skyscraper&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$exit_code&lt;/span&gt; &lt;span class="k"&gt;in
      &lt;/span&gt;0&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{green}Completed"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;

      &lt;span class="c"&gt;# The "Oh, I meant to do that" code&lt;/span&gt;
      130&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{yellow}Stopped"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="c"&gt;# SIGINT (Ctrl+C)&lt;/span&gt;

      &lt;span class="c"&gt;# The "Wait, what just happened?" codes&lt;/span&gt;
      126&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{magenta}Denied (126)"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="c"&gt;# Permission denied / Not executable&lt;/span&gt;
      127&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{magenta}Not Found (127)"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="c"&gt;# Typo in command name&lt;/span&gt;

      &lt;span class="c"&gt;# The "Violent Crash" codes&lt;/span&gt;
      137&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{red}Killed / Out of Memory (137)"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="c"&gt;# SIGKILL (Often the Out-Of-Memory killer)&lt;/span&gt;
      139&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{red}Segfault (139)"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="c"&gt;# SIGSEGV (Memory access violation)&lt;/span&gt;
      143&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{yellow}Terminated (143)"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="c"&gt;# SIGTERM (Graceful kill command)&lt;/span&gt;

      &lt;span class="c"&gt;# The Catch-All for standard script errors&lt;/span&gt;
      &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{red}Exit code: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;exit_code&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;

    &lt;span class="nv"&gt;status_line&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;nl&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DIM&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;status_text&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%} %F{242}at %D{%H:%M:%S}%f &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ELAPSED&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;nl&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;nl&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nv"&gt;LAST_HISTCMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HISTCMD&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;name_path_git&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%F{magenta}&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DISPLAY_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%f in &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DYNAMIC_PATH&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;GIT_INFO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;input_indicator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;nl&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DIM&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%}%F{yellow}%(!.#.&lt;/span&gt;&lt;span class="nv"&gt;$)&lt;/span&gt;&lt;span class="s2"&gt;%f%{&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESET&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%}"&lt;/span&gt;

  &lt;span class="nv"&gt;PROMPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;status_line&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;name_path_git&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;input_indicator&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# 4. Tell the surgical path function to run before every prompt&lt;/span&gt;
add-zsh-hook precmd set_surgical_path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The final touch
&lt;/h2&gt;

&lt;p&gt;Place a little 👽 Extra Terrestrial emoji in right prompt so I can scroll up the wall of text and find my inputs a little more easily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# A little splash of me #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="nv"&gt;RPROMPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'👽'&lt;/span&gt;


&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# This was a triumph. ♫ #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="c"&gt;# I'm making a note here: Huge success.&lt;/span&gt;


&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;
&lt;span class="c"&gt;# Things added by tools #&lt;/span&gt;
&lt;span class="c"&gt;# # # # # # # # # # # # #&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NVM_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.nvm"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NVM_DIR&lt;/span&gt;&lt;span class="s2"&gt;/nvm.sh"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NVM_DIR&lt;/span&gt;&lt;span class="s2"&gt;/nvm.sh"&lt;/span&gt;  &lt;span class="c"&gt;# This loads nvm&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NVM_DIR&lt;/span&gt;&lt;span class="s2"&gt;/bash_completion"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NVM_DIR&lt;/span&gt;&lt;span class="s2"&gt;/bash_completion"&lt;/span&gt;  &lt;span class="c"&gt;# This loads nvm bash_completion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Open Contact 👽
&lt;/h2&gt;

&lt;p&gt;Please do reach out if you need help with any of this, have feature requests, or want to share what you've created!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;small&gt;PropJockey&lt;/small&gt;&lt;/th&gt;
&lt;th&gt;&lt;small&gt;CodePen&lt;/small&gt;&lt;/th&gt;
&lt;th&gt;&lt;small&gt;DEV Blog&lt;/small&gt;&lt;/th&gt;
&lt;th&gt;&lt;small&gt;GitHub&lt;/small&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://propjockey.io" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/propjockey-lines.svg" alt="PropJockey.io"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/codepen.svg" alt="CodePen"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/janeori"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/dev.svg" alt="DEV Blog"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/propjockey" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/github.svg" alt="GitHub"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;small&gt;Mastodon&lt;/small&gt;&lt;/th&gt;
&lt;th&gt;&lt;small&gt;LinkedIn&lt;/small&gt;&lt;/th&gt;
&lt;th&gt;&lt;small&gt;X&lt;/small&gt;&lt;/th&gt;
&lt;th&gt;&lt;small&gt;Bluesky&lt;/small&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://front-end.social/@JaneOri" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/mastodon.svg" alt="Mastodon"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.linkedin.com/in/janeori/" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/linkedin.svg" alt="LinkedIn"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://x.com/jane0ri" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/x.svg" alt="X"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://bsky.app/profile/janeori.propjockey.io" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/external-social/100px/bluesky.svg" alt="Bluesky"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  My heart is open to receive abundance in all forms, flowing to me in many expected and unexpected ways.
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PayPal&lt;/th&gt;
&lt;th&gt;Ko-Fi&lt;/th&gt;
&lt;th&gt;Venmo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.paypal.com/donate/?cmd=_s-xclick&amp;amp;hosted_button_id=9Z925L3SJJ8BS&amp;amp;source=qr&amp;amp;ssrt=1772865628068" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/QR-Codes/svg/200px/paypal.svg" alt="PayPal"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ko-fi.com/janeori" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/QR-Codes/svg/200px/ko-fi.svg" alt="Ko-fi"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://account.venmo.com/u/JaneOri" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/QR-Codes/svg/200px/venmo.svg" alt="Venmo"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;BTC&lt;/th&gt;
&lt;th&gt;XRP&lt;/th&gt;
&lt;th&gt;ETH&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://app.ens.domains/janeori.eth" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/QR-Codes/svg/200px/btc.svg" alt="BTC bc1qe2ss8hvmskcxpmk046msrjpmy9qults2yusgn9"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/QR-Codes/svg/200px/xrp.svg" alt="XRP rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg : 459777128"&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://app.ens.domains/janeori.eth" rel="noopener noreferrer"&gt;&lt;img src="//raw.githubusercontent.com/propjockey/propjockey-brand/main/QR-Codes/svg/200px/eth.svg" alt="ETH 0x674D4191dEBf9793e743D21a4B8c4cf1cC3beF54"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bc1qe...usgn9&lt;/td&gt;
&lt;td&gt;rw2ci...nWKQg&lt;br&gt;: 459777128&lt;/td&gt;
&lt;td&gt;0x674...beF54&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>cli</category>
      <category>zshrc</category>
      <category>shell</category>
    </item>
    <item>
      <title>100% CSS: Fetch and Exfiltrate 512 bits of Server-Generated Data Embedded in an Animated SVG</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Fri, 17 Jan 2025 21:41:08 +0000</pubDate>
      <link>https://forem.com/janeori/100-css-fetch-and-exfiltrate-512-bits-of-server-generated-data-embedded-in-an-animated-svg-5aad</link>
      <guid>https://forem.com/janeori/100-css-fetch-and-exfiltrate-512-bits-of-server-generated-data-embedded-in-an-animated-svg-5aad</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is a direct followup to to &lt;a href="https://dev.to/janeori/getting-your-ip-address-with-css-and-other-32-bit-api-responses-without-javascript-402h"&gt;Getting 32 bit API Response Data in CSS&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In CSS, &lt;code&gt;16 bits&lt;/code&gt; of response data, placed both in the intrinsic width and in the intrinsic height, was a huge improvement from not being able to get API response data at all (without JavaScript)...&lt;/p&gt;

&lt;p&gt;However, in the days after I figured it out, my mind leaned in one direction:&lt;br&gt;
&lt;strong&gt;It would be way more fun if it was &lt;code&gt;16 bits&lt;/code&gt; &lt;em&gt;32 times&lt;/em&gt; instead of just twice.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feogate0l0up6img1r2ze.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feogate0l0up6img1r2ze.gif" alt="Gif recording of a harsh terminal app setting several integer values to an array of variables one at a time" width="726" height="341"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Packing 512 bits into an SVG for Exfiltration
&lt;/h2&gt;

&lt;p&gt;I was meditating before bed and got struck with another inspired thought -&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What if it was possible for the image document &lt;em&gt;itself&lt;/em&gt; to animate its &lt;em&gt;own&lt;/em&gt; intrinsic size?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The usual chain of realizations after an inspired thought each flashed with understanding on how it could be accomplished... I just needed to figure out if such an image type existed.&lt;/p&gt;

&lt;p&gt;I grabbed my phone and and searched across all the animated image formats I knew of, seeing if any of them were capable of it. &lt;strong&gt;svg&lt;/strong&gt;, &lt;strong&gt;webp&lt;/strong&gt;, &lt;strong&gt;apng&lt;/strong&gt;, &lt;strong&gt;gif&lt;/strong&gt;, &lt;em&gt;maybe weird video formats&lt;/em&gt;? I couldn't find any.&lt;/p&gt;

&lt;p&gt;In the morning, something in me said to &lt;em&gt;keep digging at SVG&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I tried embedded CSS, &lt;a href="https://www.sitepoint.com/using-svg-with-media-queries/" rel="noopener noreferrer"&gt;media queries&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs#example" rel="noopener noreferrer"&gt;use'd defs&lt;/a&gt;, more &lt;a href="https://css-tricks.com/guide-svg-animations-smil/#specifying-the-target-of-the-animation-with-xlinkhref" rel="noopener noreferrer"&gt;use'd defs&lt;/a&gt;, diving into the numerous &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate" rel="noopener noreferrer"&gt;SVG animate docs&lt;/a&gt;, &lt;a href="https://stackoverflow.com/questions/78736461/is-it-possible-to-disable-svgs-animate-element-for-prefers-reduced-motion-media#:~:text=Disable%20animations%20via" rel="noopener noreferrer"&gt;trying to trick them&lt;/a&gt;, and read up on &lt;a href="https://github.com/w3c/fxtf-drafts/issues/7" rel="noopener noreferrer"&gt;other ideas&lt;/a&gt; and animation related properties - nothing could let me set height or width.&lt;/p&gt;

&lt;p&gt;But that last link made me think...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...what about &lt;code&gt;viewBox&lt;/code&gt;? I'd have some other hurdles to tackle but... Is &lt;em&gt;that&lt;/em&gt; possible to animate?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;vvv&lt;br&gt;
&lt;a href="https://stackoverflow.com/a/33821641" rel="noopener noreferrer"&gt;IT IS!!&lt;/a&gt;&lt;br&gt;
^^^&lt;/p&gt;
&lt;h3&gt;
  
  
  Sorting out the solution space
&lt;/h3&gt;

&lt;p&gt;Now the problem is, if you don't set &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on the root &lt;code&gt;svg&lt;/code&gt; element then try to use the svg as &lt;code&gt;content&lt;/code&gt; on a &lt;code&gt;pseudo&lt;/code&gt;, it renders &lt;code&gt;0px&lt;/code&gt; x &lt;code&gt;0px&lt;/code&gt; because it's a vector document and no longer has an &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Intrinsic_Size" rel="noopener noreferrer"&gt;intrinsic size&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I searched and added &lt;a href="https://unmatchedstyle.com/news/svg-and-the-preserveaspectratio-property.php#:~:text=preserveAspectRatio-,Example%205,-is%20an%20interactive" rel="noopener noreferrer"&gt;preserveAspectRatio&lt;/a&gt; to it... Still &lt;code&gt;0x0&lt;/code&gt;... but then in my CSS, I pinned the width to &lt;code&gt;10px&lt;/code&gt; and let the preserved aspect ratio of the &lt;code&gt;viewBox&lt;/code&gt; determine the height (which I can change with an animation embedded in the SVG) aaand... the html element containing it grew to the expected height.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;:3&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If there was only one frame, this took my original 32 bits and cut it in half; since only one dimension can be exfiltrated while the other is static.&lt;/p&gt;

&lt;p&gt;BUT! Now, my 2nd dimension was &lt;strong&gt;time&lt;/strong&gt; and the first is at the mercy of time, so there's more than enough data to be had.&lt;/p&gt;

&lt;p&gt;How exciting!&lt;/p&gt;

&lt;p&gt;I learned all I could about how to &lt;a href="https://oak.is/thinking/animated-svgs/" rel="noopener noreferrer"&gt;control the animation in SVGs&lt;/a&gt; and whipped up a server side script to generate my first animated SVG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-type: image/svg+xml'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;175&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$datalen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'0 0 10 '&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;$frames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$datalen&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nv"&gt;$dur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$datalen&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.33&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// total seconds&lt;/span&gt;

  &lt;span class="nv"&gt;$keytimeStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$datalen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// uniform portion per frame&lt;/span&gt;

  &lt;span class="nv"&gt;$keytimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"; "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$keytimeStep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$keytimeStep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$datalen&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

  &lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"; "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$frames&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg"&amp;gt;
    &amp;lt;animate
      attributeName="viewBox"
      dur="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$dur&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'s"
      fill="freeze"
      begin="0.1s;"
      values="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'" 
      keytimes="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$keytimes&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'"
      repeatCount="indefinite"
      calcMode="discrete"
    /&amp;gt;
  &amp;lt;/svg&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;(why php?! - because I already had a server I've been paying for for years, set up to run php out the gate.... And even though I've earned a wonderful living knowing JavaScript and node really well, sometimes &lt;em&gt;it's fun&lt;/em&gt; to look up every single function, operator, and syntax to progress through something you know you &lt;em&gt;can&lt;/em&gt; do without knowing specifics. lol)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let's fork my first CodePen from the &lt;a href="https://dev.to/janeori/getting-your-ip-address-with-css-and-other-32-bit-api-responses-without-javascript-402h"&gt;previous article&lt;/a&gt; to see CSS responding to and changing size --vars as the SVG ticks along:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/9e6d471ea957785ac3d2157bd7a52549?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Confirmed! We &lt;em&gt;can&lt;/em&gt; read the size change. Like in the previous article, in the end, it'll be using the &lt;code&gt;view-timeline&lt;/code&gt; measurement technique instead this &lt;a href="https://dev.to/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j"&gt;tan(atan2())&lt;/a&gt; technique.&lt;/p&gt;

&lt;p&gt;It cooks my cpu so we'll want to remove it from &lt;code&gt;content&lt;/code&gt; when the exfiltration completes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conceptually, how to procedurally exfiltrate 1D + time
&lt;/h3&gt;

&lt;p&gt;The demo above isn't very useful on its own. It reports a copy of the height whenever it's there but we need to save it... and what good is a bunch of 16 bit values if you don't know and can't trust the order?&lt;/p&gt;

&lt;p&gt;I know I can accumulate values in CSS with &lt;a href="https://dev.to/janeori/expert-css-the-cpu-hack-4ddj"&gt;the CPU Hack&lt;/a&gt; and mathematically determine which --var gets incoming values updated instead of just holding its previous value, so I won't worry about CSS specifically. How, in general, could we exfiltrate 1D over time?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Sentinel_value" rel="noopener noreferrer"&gt;Sentinel values&lt;/a&gt; to the rescue!&lt;/p&gt;

&lt;p&gt;The size of the measuring area we use doesn't HAVE to be limited to 16 bits even if I want to limit the data packets themselves to 16 bits. So we can pack some sentinels in there too. &lt;a href="https://www.npmjs.com/package/css-api-fetch" rel="noopener noreferrer"&gt;css-api-fetch&lt;/a&gt; ships with the ability to handle values up to &lt;code&gt;99999&lt;/code&gt;, which is well above &lt;code&gt;65535&lt;/code&gt; (the 16 bit ceiling).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So what do we need to know?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What problems might we run into?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If &lt;strong&gt;two values in our data are the same back-to-back&lt;/strong&gt;, we need an interruption to know it's two distinct packets. I already decided we'll be aiming for 512 bits, so we need the SVG's animation to have a maximum of 32 16-bit data frames, with sentinel frames in between...&lt;/p&gt;

&lt;p&gt;If the CPU is feeling heavy, &lt;strong&gt;the SVG animation may appear to skip discrete steps entirely&lt;/strong&gt;. That means we need some way of always knowing what step we're on. So rather than a single "between data frames" sentinel, let's use the data index (1 based like CSS nth-* selectors) as the sentinel value, making it its own discrete step before the discrete step showing the data for that index.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Sentinel index -&amp;gt; data -&amp;gt; sentinel index -&amp;gt; data ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That lets us know when it loops too, potentially when we hit sentinel &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But how do we know that it didn't skip to a different data frame and &lt;strong&gt;accidentally have us record it in the wrong slot&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;We need to let it loop and &lt;em&gt;keep going until it's right&lt;/em&gt;, and the best way to know if data is correct is &lt;a href="https://en.wikipedia.org/wiki/Checksum" rel="noopener noreferrer"&gt;a checksum&lt;/a&gt;! So we need another data frame, and a sentinel for that value.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating the Checksum Algorithm
&lt;/h4&gt;

&lt;p&gt;I could use &lt;a href="https://propjockey.github.io/css-bin-bits/#logic-docs" rel="noopener noreferrer"&gt;&lt;code&gt;css-bin-bits&lt;/code&gt; to XOR&lt;/a&gt; all the data, but it's quite heavy and isn't needed anywhere else - let's settle on an alternative that's simple to do in CSS.&lt;/p&gt;

&lt;p&gt;Mathematically, if you take a 16 bit value, divide it by 256 (floor to integer), and take the 16 bit value again modulo by 256, you get the high and low bytes. Add those 8 bit values together and you're at 9 bits. This feels like a reasonable checksum approach, let's circle back to this though.&lt;/p&gt;

&lt;p&gt;We don't have to stay in 16 bit range to &lt;em&gt;compute&lt;/em&gt; the checksum as long as the &lt;em&gt;final checksum is 16 bits&lt;/em&gt;, so let's just sum all (up-to) 32 values.&lt;/p&gt;

&lt;p&gt;We've got to be careful about incorrect storage writes due to skipped frames though, so let's add the even index values &lt;em&gt;twice&lt;/em&gt; so there's &lt;em&gt;some semblance of order&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That sum, &lt;code&gt;16 bit values&lt;/code&gt;, &lt;code&gt;32 times&lt;/code&gt;, plus an additional &lt;code&gt;16 times&lt;/code&gt;, is about &lt;code&gt;22 bits&lt;/code&gt;. Divide and module &lt;code&gt;11 bits&lt;/code&gt; on each side circling back to the earlier thought, then add those together, giving &lt;code&gt;12 bits&lt;/code&gt; as our checksum response.&lt;/p&gt;

&lt;p&gt;Seems reasonable... It's not completely error proof but the SVG would have to be skipping several steps to mess it up in a way to MAYBE generate the same checksum now... In any case, let's also send back the &lt;code&gt;data length&lt;/code&gt; and &lt;em&gt;include that in the checksum as well&lt;/em&gt;, just by adding it as the last step of our checksum. The max data length (number of 16 bit values we want to handle) is only &lt;code&gt;32&lt;/code&gt;, so adding the length value to the 12 bits doesn't come anywhere near pushing us over 16 bits. Yay!&lt;/p&gt;

&lt;p&gt;spoiler: this &lt;em&gt;is&lt;/em&gt; what I did but CSS became lossy somewhere around 21 bits so I split it up and effectively did the same algorithm but in smaller chunks at a time. Server side uses the alg exactly as described.&lt;/p&gt;

&lt;p&gt;Technically with the setup we've described, it doesn't matter what the order is in the &lt;em&gt;animation&lt;/em&gt; as long as each sentinel tells you what index the next frame is supposed to be in the &lt;em&gt;data&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;One more thing, let's put the &lt;code&gt;data length value&lt;/code&gt; first in the response and add a sentinel for that too (sentinel in the SVG animation before the value, as with the rest of the data).&lt;/p&gt;

&lt;p&gt;That's &lt;em&gt;34 sentinels&lt;/em&gt;. The SVG &lt;code&gt;viewBox&lt;/code&gt; height can't be &lt;code&gt;0&lt;/code&gt; and CSS will benefit from allowing &lt;code&gt;0&lt;/code&gt; to represent no data internally, so let's say we have &lt;strong&gt;35 sentinels&lt;/strong&gt; with &lt;code&gt;0&lt;/code&gt; deliberately unused.&lt;/p&gt;

&lt;p&gt;All data frames now get embedded in the SVG with &lt;code&gt;35&lt;/code&gt; &lt;strong&gt;added to their value&lt;/strong&gt;. &lt;code&gt;Length&lt;/code&gt; and &lt;code&gt;checksum&lt;/code&gt; data values ALSO get &lt;code&gt;35&lt;/code&gt; added to the viewbox value. &lt;code&gt;viewBox&lt;/code&gt; heights in the SVG Animation representing the sentinels will have values &lt;em&gt;0 to 34&lt;/em&gt; (skipping 0) and each tell us exactly what the next frame in the SVG Animation represents.&lt;/p&gt;

&lt;p&gt;CSS side, we just check if &lt;em&gt;the raw measurement&lt;/em&gt; is greater than 34, it's &lt;code&gt;data&lt;/code&gt; so subtract &lt;code&gt;35&lt;/code&gt; from it, if it's less than &lt;code&gt;35&lt;/code&gt;, it's a &lt;code&gt;sentinel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvffqw4huzstc7c0at6by.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvffqw4huzstc7c0at6by.png" alt="A meme picture of Charlie Day from It's Always Sunny in Philadelphia with a crazed look standing in front of a board covered in paper with red lines connecting them hectically" width="600" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Beginning to Exfiltrate the 512 bits with CSS
&lt;/h2&gt;

&lt;p&gt;After I finished the PHP side to generate the SVG animation as detailed, I thought on specific ways to begin the CSS for this exfiltration process.&lt;/p&gt;

&lt;p&gt;
  Here's the PHP code!
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-type: image/svg+xml'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;175&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$datalen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'0 0 10 '&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// add 35 to all the values so we can use 0 to 34 for sentinels. 0 = CSS-side sentinel, 1-32 = data frames, 33 = length, 34 = checksum&lt;/span&gt;
  &lt;span class="nv"&gt;$frames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'; '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$val&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$datalen&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 1 up to 32 = indicator that next frame is the value(+35) for that index(1-based)&lt;/span&gt;

  &lt;span class="c1"&gt;// no matter how many are in the array, '33' indicates the next frame is data length, which is used in the checksum too&lt;/span&gt;
  &lt;span class="nb"&gt;array_unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$frames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'33; '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$datalen&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt; &lt;span class="c1"&gt;// + 35 b/c data&lt;/span&gt;
  &lt;span class="c1"&gt;// unshift so the length is (hopefully) the first value read and a sense of progress can be reported&lt;/span&gt;

  &lt;span class="nv"&gt;$fullsum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$datalen&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// double the odd ones so there's some semblance of order accounted for&lt;/span&gt;
    &lt;span class="c1"&gt;// the odd ones with 0 based index is the even ones on the CSS side&lt;/span&gt;
    &lt;span class="nv"&gt;$fullsum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nv"&gt;$checksum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fullsum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fullsum&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$datalen&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// + 35 because it's data&lt;/span&gt;

  &lt;span class="c1"&gt;// no matter how many are in the array, '34' indicates the next frame is checksum&lt;/span&gt;
  &lt;span class="nb"&gt;array_push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$frames&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'34; '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$viewBoxXYWidth&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$checksum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$actualNumItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$frames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;$dur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$actualNumItems&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.33&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// total seconds&lt;/span&gt;

  &lt;span class="nv"&gt;$keytimeStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$actualNumItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// uniform portion per frame&lt;/span&gt;

  &lt;span class="nv"&gt;$keytimes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"; "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$keytimeStep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$keytimeStep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$actualNumItems&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

  &lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"; "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$frames&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 

  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;svg viewBox="0 0 10 100" preserveAspectRatio="xMinYMin meet" xmlns="http://www.w3.org/2000/svg"&amp;gt;
    &amp;lt;animate
      attributeName="viewBox"
      dur="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$dur&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'s"
      fill="freeze"
      begin="0.1s;"
      values="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$values&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'" 
      keytimes="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$keytimes&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'"
      repeatCount="indefinite"
      calcMode="discrete"
    /&amp;gt;
  &amp;lt;/svg&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;There are a few ways to accomplish this in CSS and potentially many more coming with &lt;a href="https://www.bram.us/2024/12/18/the-future-of-css-construct-custom-idents-and-dashed-idents-with-ident/" rel="noopener noreferrer"&gt;recent spec additions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My first approach is the easiest conceptually - using a view-timeline for every piece of data and doing the same thing over and over. It was working but I groaned through my progress in displeasure with how gross it was. That'll be nearly 40 animations on &lt;code&gt;:root&lt;/code&gt; if I continued.&lt;/p&gt;

&lt;p&gt;So I went to sleep.&lt;/p&gt;

&lt;p&gt;When I woke up, I laid there several moments looking out the window smiling with that just-woke-up-or-meditated buzzy feeling, then a firehose of thoughts rushed into my head. I rolled over, grabbed my notebook and the closest pen, sat up in bed, and started writing down the algorithm to exfiltrate it with just 6 CSS animations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwafy0ni6wwzmf3qzn6r0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwafy0ni6wwzmf3qzn6r0.png" alt="my chicken scratch handwriting on a single piece of lined notebook paper with arrows pointing to a couple of nested boxes detailing the method described below" width="525" height="934"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Literally solved on paper; This is &lt;em&gt;EXACTLY&lt;/em&gt; how it's implemented.&lt;/p&gt;

&lt;p&gt;I got up, got on my computer, ignored my previous work, and opened a new CodePen.&lt;/p&gt;

&lt;p&gt;I set up the 4 html elements indicated among the chicken scratch there, then flooded the CSS panel with notes around 4 empty class selectors corresponding to them. It won't be on &lt;code&gt;:root&lt;/code&gt; now but we can place anything that relies on it inside.&lt;/p&gt;

&lt;p&gt;Not a single piece of functionality was added until the notes were copied from the paper and written in more specific detail in CodePen.&lt;/p&gt;

&lt;p&gt;When I was done, I just read the notes and started implementing what they said, all the way to the final working result.&lt;/p&gt;

&lt;p&gt;(I wrote "20" instead of "35" because I was going to test with 256 bits)&lt;/p&gt;

&lt;p&gt;I'll dive into how it works. Because of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/view-timeline" rel="noopener noreferrer"&gt;view-timeline&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/timeline-scope" rel="noopener noreferrer"&gt;timeline-scope&lt;/a&gt;, you can set data up to flow in in kind of a Klein Bottle shape if you can picture the surface animating and getting sucked into the "hole", back up to the narrow top to poor down over the surface again, gaining size and complexity through dom layers, and then loop back up through the blackhole into the higher consciousness (:root).&lt;/p&gt;

&lt;p&gt;It's mostly vertically cyclic (rather than &lt;a href="https://dev.to/janeori/expert-css-the-cpu-hack-4ddj"&gt;mostly horizontally cyclic&lt;/a&gt; or &lt;a href="https://kizu.dev/indirect-cyclic-conditions/" rel="noopener noreferrer"&gt;statically cyclic&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/MYgEexp?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Exfiltrating the 512 bits with CSS
&lt;/h2&gt;

&lt;p&gt;The notes we slightly tweaked and made more clear:&lt;/p&gt;

&lt;p&gt;256 test -&amp;gt; 512 final&lt;/p&gt;

&lt;p&gt;and I added a presentation node inside which is great because I can show the internals too as the algorithm executes.&lt;/p&gt;

&lt;p&gt;but here is the final notes without all the implementation and presentation noise. This exactly describes how it all works.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/d7f701e4bd136e2264e9264913d73808?height=600&amp;amp;default-tab=css&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It may not be in good form for an article to have this many details embedded externally but I will be breaking down each chunk to show &lt;em&gt;how&lt;/em&gt; it's implemented.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main Controller
&lt;/h3&gt;

&lt;p&gt;At the top of this structure is 4 timeline values and their animations. So let's just plop those in.&lt;/p&gt;

&lt;p&gt;The key piece of data flow that this enables, is it gives us the ability to lift data nested deep in the DOM back up to a host (timeline scope). It's not efficient, so we want to limit how often we do this. Each registered property and it's animation can vertically host a single piece of data. The data's value is determined by the &lt;code&gt;inline&lt;/code&gt; or &lt;code&gt;block&lt;/code&gt; &lt;code&gt;view&lt;/code&gt; position of an element somewhere deep in the structure - we'll get to that part later.&lt;/p&gt;

&lt;p&gt;(see the looping example previously embedded above for a clearer picture of the data flow)&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/b6845b2d74877f3da004611b46da9655?height=600&amp;amp;default-tab=css&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The four pieces of data we're lifting here are:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--xfl-cpu-phase&lt;/code&gt; - this is a numeric value 0 to 4 that signals what phase of the CPU Hack is currently being executed. (a single 'frame' of the CPU Hack is 4 to 5 CSS render frames, one loop of the phases 'ticks' the CPU Hack) I will demonstrate this more specifically later in this article.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--xfl-raw-data&lt;/code&gt; - this hosts the height of the SVG wherever the SVG is in its animation cycle. Our raw data. As said before, if this value is less than 35, this discrete step of the SVG animation is a sentinel value. If it's greater than 34, this discrete step of the SVG animation is our 16 bit value &lt;code&gt;+ 35&lt;/code&gt;, which corresponds to what the previous sentinel indicated.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--xfl-data-type&lt;/code&gt; - this is the most recent sentinel value. This value does not change until the next sentinel is encountered. There is a 1 CSS frame delay from setting &lt;code&gt;--xfl-raw-data&lt;/code&gt; to setting this value.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--xfl-data-value&lt;/code&gt; - this is the current data value after &lt;code&gt;35&lt;/code&gt; was subtracted from the raw value, or &lt;code&gt;0&lt;/code&gt; if we haven't reached this step of the sequence yet. There is a 1 CSS frame delay from setting &lt;code&gt;--xfl-data-type&lt;/code&gt; to setting this value.&lt;/p&gt;

&lt;p&gt;I've also preemptively wrapped &lt;code&gt;svg-animation-current-state-reporter&lt;/code&gt; in a condition that only has functionality and only loads the animated SVG while the process is incomplete. (so all the internals are removed from memory and the heavy animated svg is removed from rendering when we finish)&lt;/p&gt;

&lt;p&gt;The keyframe values go from a &lt;code&gt;max&lt;/code&gt; value for that piece of data to 0. I'll explain later why these are backwards - look for the image of Uno Reverse cards.&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU Exfiltrator
&lt;/h3&gt;

&lt;p&gt;Next we set up the basic boilerplate for a &lt;a href="https://dev.to/janeori/expert-css-the-cpu-hack-4ddj"&gt;CPU Hack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CPU Hack boilerplate is just following a variable name pattern to set up &lt;code&gt;capture&lt;/code&gt; and &lt;code&gt;hoist&lt;/code&gt; animations.&lt;/p&gt;

&lt;p&gt;If we have 1 integer &lt;code&gt;--xfl\\1&lt;/code&gt; that we want to cycle horizontally (over time), we register it and we set up capture and hoist animations like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;capture&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;--xfl\\1&lt;/span&gt;&lt;span class="py"&gt;-captured&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--xfl&lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;hoist&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;--xfl\\1&lt;/span&gt;&lt;span class="py"&gt;-hoist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--xfl&lt;/span&gt;&lt;span class="err"&gt;\\&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;-captured&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then complete the cyclic assignment on the &lt;code&gt;.cpu-exfiltrator&lt;/code&gt; element where the two CPU animations are hosted. I'll do it for just one of the values for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;--xfl&lt;/span&gt;&lt;span class="err"&gt;\\1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl&lt;/span&gt;&lt;span class="err"&gt;\\1&lt;/span&gt;&lt;span class="nt"&gt;-hoist&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;
  &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Chrome, they won't statically cycle (become the &lt;code&gt;initial&lt;/code&gt; value) unless both animations are &lt;code&gt;running&lt;/code&gt; at the same time. Which is a FANTASTIC side effect of what's likely an optimization of paused animations setting numeric properties.&lt;/p&gt;

&lt;p&gt;Finally, since we're using a new automatic version of the CPU Hack (you don't have to :hover to cycle the phases like in the original hack), we wire in the &lt;code&gt;--xfl-cpu-phase&lt;/code&gt; var from previous (hosted on the parent element here, so we can use style queries to respond to it) and control the play state of our animations.&lt;/p&gt;

&lt;p&gt;We also output &lt;code&gt;--cpu-next-phase&lt;/code&gt; which will later be lifted back to the top and set the next value for &lt;code&gt;--xfl-cpu-phase&lt;/code&gt; using its view position and timeline scope.&lt;/p&gt;

&lt;p&gt;I've added an additional phase to keep the CPU Hack paused until the SVG Animation measurement has successfully locked in the next &lt;code&gt;--xfl-data-type&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--xfl-cpu-phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--cpu-next-phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--xfl-data-type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(as it is now, data-type is still always 0 so once next phase is wired in, this will already be looping the CPU Hack. Once we have a data-type sentinel it won't loop until we've cleared it deliberately with the &lt;code&gt;0&lt;/code&gt; sentinel)&lt;/p&gt;

&lt;p&gt;Later, we'll also add the noted condition to prevent CPU phase 1 from starting until data is all in place. That will ensure that between data type (sentinel) being locked in and data value (raw - 35) being locked in, we want to leave the CPU Hack in its capture phase. So it's "ready to be ready" as Abraham Hicks might say.&lt;/p&gt;

&lt;p&gt;I'll go ahead and register all the 32 values plus checksum and length that we expect the SVG Animation to report.&lt;/p&gt;

&lt;p&gt;Since registration of &lt;code&gt;--xfl\\1&lt;/code&gt; to &lt;code&gt;--xfl\\32&lt;/code&gt; is a big block and the CPU animations are just boilerplate too, I'll move all those to the bottom of the hack setup to be ignored moving forward.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/3f8a2de1185abe7ac4852d65b0a3e805?height=600&amp;amp;default-tab=css&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic CPU Hack
&lt;/h3&gt;

&lt;p&gt;This wires the next cpu phase up to the &lt;code&gt;--xfl-cpu-phase&lt;/code&gt; value&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.cpu-phase-cycle-request0r&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;hotpink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;&amp;amp;::before&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;25px&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--cpu-next-phase&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;view-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--xfl-cpu-phase&lt;/span&gt; &lt;span class="nb"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's some CSS boilerplate here to make the element be a scroll container and put it off screen. The important part is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;view-timeline: --xfl-cpu-phase inline;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;which says where the right edge of this pseudo element falls in its 100px wide parent, wire it up as a "progress" from the left to our animation that moves from 0 to 4... So 25px is 25% complete, which maps to 1 when 25% is between 0 and 4.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrdeyocs40lpilceihqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrdeyocs40lpilceihqg.png" alt="picture of two 'reverse' cards from Uno" width="800" height="450"&gt;&lt;/a&gt; image sourced from a google search leading to &lt;a href="https://x.com/HawksRunning/status/1580160214426603520" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TECHNICALLY the animation is 4 to 0 and TECHNICALLY it's measuring from the right edge of the pseudo as view progress towards the right. So 25px wide pseudo is 75% from the right of its 100px wide scroll parent and maps to a value of 1 when 75% is between 4 and 0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's easier to understand if you don't cognitively process the reverse reverse math and just accept the end result is a simple progress 0 to 4 because the max value in the animation is 4 (again ignoring that the animation &lt;em&gt;starts&lt;/em&gt; at 4).&lt;/p&gt;

&lt;p&gt;Let's also write the ready state that holds the CPU in phase 0 until the data is ready. The note is on line 64 of our demos:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Data ready = data-type &amp;gt; 0 &amp;amp;&amp;amp; raw-frame-data - 35 === data-value&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;--xfl-data-is-ready&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nt"&gt;min&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-data-type&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;min&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-raw-data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;35&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-data-value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
      &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-data-value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-raw-data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;35&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;)))&lt;/span&gt;
  &lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--xfl-cpu-phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--cpu-next-phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--xfl-data-is-ready&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Wait, === in CSS?
&lt;/h4&gt;

&lt;p&gt;These are quite outdated now and I'd do them differently today, written before &lt;code&gt;clamp()&lt;/code&gt; was baseline, but I always open this old codepen to mindlessly copy paste numeric comparators when I need them. It'd be a good article to update these and explain them but here you go in the meantime: &lt;a href="https://codepen.io/propjockey/pen/YzZMNaz" rel="noopener noreferrer"&gt;https://codepen.io/propjockey/pen/YzZMNaz&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reading the SVG Animation
&lt;/h3&gt;

&lt;p&gt;This is initially &lt;em&gt;VERY&lt;/em&gt; similar to the &lt;code&gt;CPU Exfiltrator&lt;/code&gt; section because it's the same technique, measuring and moving data from here up the DOM to where it's hosted (scope).&lt;/p&gt;

&lt;p&gt;We'll be measuring and reporting the last 3 values for the base element we set up initially.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;::before&lt;/code&gt; we'll render the SVG and set &lt;code&gt;--xfl-raw-data&lt;/code&gt; using &lt;code&gt;block&lt;/code&gt; view position which is the measurement of the animated SVG's height. (remember, we'll pin the width to &lt;code&gt;10px&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;::after&lt;/code&gt; we'll set &lt;code&gt;--xfl-data-type&lt;/code&gt; inline (sentinel values 0 to 34) and &lt;code&gt;--xfl-data-value&lt;/code&gt; block (16 bit values).&lt;/p&gt;

&lt;p&gt;The parent will need to be wide enough to render the SVG (at least 10px) and accurately provide measurements for the sentinel values (0 to 34).&lt;/p&gt;

&lt;p&gt;The parent will also need to be tall enough to measure 16 bit values (+ 35). Since we set a max value in the first step of 100k, we'll just use that even though it's about 30% bigger than we need.&lt;/p&gt;

&lt;p&gt;And move it off screen to the top and left so it doesn't cause scrollbars.&lt;/p&gt;

&lt;p&gt;Therefore,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.svg-animation-current-state-reporter&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;gets&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;absolute&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;rgba&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;255&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;34&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* --xfl-data-type keyframes are 34 to 0 b/c max data-type = 34 */&lt;/span&gt;
  &lt;span class="nt"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-max&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;bottom&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-max&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="nt"&gt;vh&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-100vw&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;overflow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;hidden&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;font-size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;important&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;line-height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nt"&gt;important&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and before becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('http://css-api.propjockey.io/api.svg.php?')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;lime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;--height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2135px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="py"&gt;view-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--xfl-raw-data&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;::after&lt;/code&gt; gets&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;position&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;absolute&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;width&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;height&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--TODO_SENTINEL_VALUE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;
  &lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;top&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--TODO_DATA_VALUE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;
  &lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;background&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;black&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;view-timeline&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;--xfl-data-type&lt;/span&gt; &lt;span class="nt"&gt;inline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;--xfl-data-value&lt;/span&gt; &lt;span class="nt"&gt;block&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially the storage medium here for these after values is the view position of a 1px square pseudo against its parent scroll container. We subtract 1px in the left and top calcs because the pseudo itself is 1x1 and we want it to report 0 when the corresponding value is 0.&lt;/p&gt;

&lt;p&gt;This is all very similar to what was done previously.&lt;/p&gt;

&lt;p&gt;There are several complexities to how we calculate what these values should be, as the note indicates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* there's 4 stages here
// 1) cpu tells us to reset to 0s
// 2) cpu is in phase 0 (capture running) and stays until these stages finish
//   a. if svg animation frame (based on raw) is type, set type else use prev
//   3. if svg animation frame (based on raw) is data, set data else use prev
// 4) CPU is executing, our job is to hold prev values and ignore the SVG ani
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key to understanding how to solve this is that any comparator value is set to either 0 or 1. 1 if it's true, 0 if it's false. Then multiply it by some value. If it's false, the result stays 0, otherwise it becomes whatever the value is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://css-tricks.com/dry-switching-with-css-variables-the-difference-of-one-declaration/" rel="noopener noreferrer"&gt;Ana Tudor goes into great depth of how this idea works here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, if we do that comparison twice, with a different or opposite comparison for the second value, and add them together (Ensuring at most one of the comparators is 1) then the addition of two of them is just saying "else if".&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if not ready&lt;/code&gt; * &lt;code&gt;use the old value&lt;/code&gt; + &lt;code&gt;else&lt;/code&gt;&lt;br&gt;
&lt;code&gt;if is ready&lt;/code&gt; * &lt;code&gt;use this new value&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is how we hold onto the sentinel value through the duration of the SVG Animation's discrete step for the value after the type has already been reported.&lt;/p&gt;

&lt;p&gt;The CSS code implementing it begins on line 191 here, just above the big block of &lt;code&gt;--xfl\\...&lt;/code&gt; property registrations we put towards the bottom&lt;br&gt;
&lt;code&gt;@property --xfl\\1 { syntax: "&amp;lt;integer&amp;gt;"; initial-value: 0; inherits: true; }&lt;/code&gt;&lt;br&gt;
...&lt;br&gt;
and it contains additional notes:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/a99afb3c6e4d4d6397e62ce24528bc7c?height=600&amp;amp;default-tab=css&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting specific CSS --var values (addressed assignments)
&lt;/h3&gt;

&lt;p&gt;The logic we just touched on is exactly the same concept we use for all of the &lt;code&gt;--xfl\\1&lt;/code&gt;, 2, 32 values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;--xfl-set&lt;/span&gt;&lt;span class="err"&gt;\\1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;min&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="err"&gt;1&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-data-type&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
      &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-data-type&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;
    &lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-data-is-ready&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nt"&gt;--xfl&lt;/span&gt;&lt;span class="err"&gt;\\1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-set&lt;/span&gt;&lt;span class="err"&gt;\\1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-data-value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;1&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl-set&lt;/span&gt;&lt;span class="err"&gt;\\1&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;var&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--xfl&lt;/span&gt;&lt;span class="err"&gt;\\1&lt;/span&gt;&lt;span class="nt"&gt;-hoist&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You read &lt;code&gt;--xfl-set\\1&lt;/code&gt; as &lt;code&gt;if --xfl-data-type gte 1&lt;/code&gt; &lt;code&gt;use --xfl-data-is-ready&lt;/code&gt; with an implied &lt;code&gt;else 0&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;--xfl-data-is-ready&lt;/code&gt; was established earlier as a flag holding us in phase 0 until it's time to flip to phase 1.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means our condition is &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; logic. Both flags must be 1 to pass.&lt;/p&gt;

&lt;p&gt;Then you continue to read &lt;code&gt;--xfl\\1&lt;/code&gt; as &lt;code&gt;if --xfl-set\\1&lt;/code&gt; &lt;code&gt;use --xfl-data-value&lt;/code&gt; (the current SVG Animation value) else &lt;code&gt;if NOT --xfl-set\\1&lt;/code&gt; &lt;code&gt;use --xfl\\1-hoist&lt;/code&gt; (the previous value the CPU hack was holding for --xfl\1)&lt;/p&gt;

&lt;p&gt;This is highly repetitive, and describes almost the entirety of the rest of this exfiltration.&lt;/p&gt;

&lt;p&gt;The final steps are running basic &lt;code&gt;calc()&lt;/code&gt; and &lt;code&gt;mod()&lt;/code&gt; math to build the checksum as described earlier, then adding if the computed checksum === the checksum embedded in the SVG Animation to the CPU Hack so we know when it's complete. All more of the same.&lt;/p&gt;

&lt;p&gt;So now it's time. :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Presenting: The CSS Animated SVG Exfiltration Hack
&lt;/h2&gt;

&lt;p&gt;Because I wanted to show each and every piece of this hack one value per element, the presentation for this is obnoxiously heavy. Over 2000 lines of HTML and over 400 lines of CSS. Plus I'm using &lt;a href="https://propjockey.github.io/css-bin-bits/" rel="noopener noreferrer"&gt;css-bin-bits&lt;/a&gt; to convert each register to binary, etc.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/ee2e273bc44529cefa21f7e97dd82a6c?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;(Click rerun in the bottom right of the codepen frame to see it happen in real time!)&lt;/p&gt;

&lt;p&gt;No JavaScript!&lt;/p&gt;




&lt;h2&gt;
  
  
  Open Contact 👽
&lt;/h2&gt;

&lt;p&gt;Please do reach out if you think this is neat and wish to learn more! Always happy to answer questions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PropJockey.io&lt;/th&gt;
&lt;th&gt;CodePen&lt;/th&gt;
&lt;th&gt;DEV Blog&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;Mastodon&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://propjockey.io" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fpropjockey-lines.svg" alt="PropJockey.io" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fcodepen.svg" alt="CodePen" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/janeori"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fdev.svg" alt="DEV Blog" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fgithub.svg" alt="GitHub" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://front-end.social/@JaneOri" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fmastodon.svg" alt="Mastodon" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://bsky.app/profile/janeori.propjockey.io" rel="noopener noreferrer"&gt;🦋@JaneOri.PropJockey.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/jane0ri" rel="noopener noreferrer"&gt;𝕏@Jane0ri&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>hacking</category>
      <category>svg</category>
    </item>
    <item>
      <title>100% CSS: &lt;fieldset&gt;'s border &lt;legend&gt; knockout behavior for any element !</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Wed, 08 Jan 2025 06:31:03 +0000</pubDate>
      <link>https://forem.com/janeori/100-css-s-border-knockout-behavior-for-any-element--4b7b</link>
      <guid>https://forem.com/janeori/100-css-s-border-knockout-behavior-for-any-element--4b7b</guid>
      <description>&lt;p&gt;Legend has it, there will come a day when the beautiful fieldset border knockout effect will be possible with any html element.&lt;/p&gt;

&lt;p&gt;And I have great news: In Chrome, that day is today!&lt;/p&gt;

&lt;h2&gt;
  
  
  Fieldset? Legend?
&lt;/h2&gt;

&lt;p&gt;If you're unfamiliar, here's the border knockout effect on a fieldset with a legend:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv85906qevz3g7uh9wtxz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv85906qevz3g7uh9wtxz.png" alt="a picture of two simple fieldset elements with their legend (titles) leaving a gap the width of their text in the fieldset's border" width="800" height="380"&gt;&lt;/a&gt;fieldset legend examples in a slightly modified screenshot from &lt;a href="https://vanillahtml.fandeytech.com/" rel="noopener noreferrer"&gt;VanillaHTML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The width of the &lt;code&gt;legend&lt;/code&gt; (title) is clipping a hole in the border of the &lt;code&gt;fieldset&lt;/code&gt; automatically.&lt;/p&gt;

&lt;p&gt;While technically possible to just use &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt; anywhere, it's generally not recommended to use a &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt; outside of a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; and without any &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements inside of it, as the primary purpose of a fieldset is to &lt;em&gt;semantically group related form inputs&lt;/em&gt;. Using it in unrelated contexts goes against its intended meaning and can negatively impact accessibility for screen readers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fieldset border legend knockout behavior anywhere
&lt;/h2&gt;

&lt;p&gt;This is not trivial to accomplish so I hid every bit of the complexity that I could to make it as close to trivial to replicate:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/9d6539d75d50c4f9e60e142641c78c3a?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;@import&lt;/code&gt; the &lt;a href="https://www.npmjs.com/package/fieldset-legend" rel="noopener noreferrer"&gt;fieldset-legend&lt;/a&gt; utility in your css.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;fieldset-legend&lt;/code&gt; class to the wrapper&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;--fl-left&lt;/code&gt; property to any &lt;code&gt;&amp;lt;length-percentage&amp;gt;&lt;/code&gt; value (even negative values if you wish)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the library will position the &lt;code&gt;:first-child&lt;/code&gt; so it's vertically centered with the top of the element and knock out the stuff behind it!&lt;/p&gt;

&lt;p&gt;To create a gap around the title, add padding to the &lt;code&gt;:first-child&lt;/code&gt; however you want to.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The biggest gotcha here is you can't put plain text nodes directly in the &lt;code&gt;fieldset-legend&lt;/code&gt; wrapper, text has to be nested inside of their own elements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, &lt;em&gt;technically&lt;/em&gt; &lt;code&gt;fieldset&lt;/code&gt;'s border knockout doesn't &lt;em&gt;sink&lt;/em&gt; to the bottom of the &lt;code&gt;legend&lt;/code&gt; element and clip the background too, like it does above.&lt;/p&gt;

&lt;p&gt;If you prefer the knockout to only &lt;em&gt;sink&lt;/em&gt; through the border, you can provide an additional &lt;code&gt;&amp;lt;length&amp;gt;&lt;/code&gt; property, &lt;code&gt;--fl-sink&lt;/code&gt; and set it equal to your border width:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/fe26154ad3d7a12af48d301ecf357f54?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it do anything else?
&lt;/h2&gt;

&lt;p&gt;It does!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;--fl-left&lt;/code&gt; alternatives
&lt;/h3&gt;

&lt;p&gt;Instead of &lt;code&gt;--fl-left&lt;/code&gt;, you could instead use &lt;code&gt;--fl-center&lt;/code&gt;.&lt;br&gt;
If you set &lt;code&gt;--fl-center&lt;/code&gt; to &lt;code&gt;0px&lt;/code&gt;, the title will be centered horizontally along the top edge.&lt;br&gt;
If you set it to &lt;code&gt;-10px&lt;/code&gt;, it will be offset to the left of center by &lt;code&gt;10px&lt;/code&gt;.&lt;br&gt;
Set it to &lt;code&gt;15px&lt;/code&gt; and it will shift right of center by &lt;code&gt;15px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead of &lt;code&gt;--fl-left&lt;/code&gt; or &lt;code&gt;--fl-center&lt;/code&gt;, you could also use &lt;code&gt;--fl-right&lt;/code&gt; with the expected behavior.&lt;/p&gt;

&lt;p&gt;All 3 of these are &lt;code&gt;&amp;lt;length-percentage&amp;gt;&lt;/code&gt; and can be positive, 0px, or negative.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;--fl-top&lt;/code&gt; option
&lt;/h3&gt;

&lt;p&gt;By default, the title element is vertically centered with the top edge of the outer element. Setting &lt;code&gt;--fl-top&lt;/code&gt; to a negative &lt;code&gt;&amp;lt;length&amp;gt;&lt;/code&gt; lifts it, and to a positive length drops it.&lt;/p&gt;

&lt;p&gt;By default, the bottom edge of the title is how far down the knockout sinks into the element, so remember you can control that as well with &lt;code&gt;--fl-sink&lt;/code&gt; described previously!&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/ByBYjEy?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fieldset-legend&lt;/code&gt; container also automatically applies a &lt;code&gt;margin-top&lt;/code&gt; to cover the height of the title poking above the element. The rule setting this has 0 specificity so it can easily be overwritten if you prefer another value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Title as &lt;code&gt;:first-child&lt;/code&gt; alternatives
&lt;/h3&gt;

&lt;p&gt;It may be important for you to place other elements, such as screenreader-only page jumps, prior to the title inside the &lt;code&gt;fieldset-legend&lt;/code&gt; container.&lt;/p&gt;

&lt;p&gt;Place a class &lt;code&gt;fieldset-legend-title&lt;/code&gt; on any ONE of the direct descendants of your &lt;code&gt;fieldset-legend&lt;/code&gt; element and the library will lift that element to the same desired position at the top, leaving &lt;code&gt;:first-child&lt;/code&gt; alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;fieldset-legend&lt;/code&gt; uses the &lt;code&gt;::before&lt;/code&gt; pseudo
&lt;/h3&gt;

&lt;p&gt;You can make it use the &lt;code&gt;::after&lt;/code&gt; pseudo instead, just change the class name from &lt;code&gt;fieldset-legend&lt;/code&gt; to &lt;code&gt;fieldset-legend-after&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;fieldset-legend&lt;/code&gt; no pseudo?
&lt;/h3&gt;

&lt;p&gt;This is advanced usage but...&lt;/p&gt;

&lt;p&gt;You can &lt;code&gt;inset: 0px;&lt;/code&gt; an element inside the wrapper, customize it however you want, and use &lt;code&gt;fieldset-legend-custom&lt;/code&gt; instead of &lt;code&gt;fieldset-legend&lt;/code&gt; or &lt;code&gt;fieldset-legend-after&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This drops all of the library's clipping and gives you a custom &lt;code&gt;mask&lt;/code&gt; to use anywhere inside of the &lt;code&gt;fieldset-legend-custom&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;For example, if you wanted to use this with your favorite sci-fi shaping library, &lt;a href="https://augmented-ui.com/" rel="noopener noreferrer"&gt;augmented-ui&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/6c7c6383c874218951fa4870411f7585?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Fallback behavior
&lt;/h2&gt;

&lt;p&gt;The limiting feature support required to use this utility is &lt;a href="https://caniuse.com/mdn-css_properties_timeline-scope" rel="noopener noreferrer"&gt;&lt;code&gt;timeline-scope&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Container &lt;a href="https://caniuse.com/css-container-queries-style" rel="noopener noreferrer"&gt;style queries&lt;/a&gt; are also required.&lt;/p&gt;

&lt;p&gt;This is what the first demo in this article looks like in non-Chrome browsers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxliyd2qhvvvknolrjbi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxliyd2qhvvvknolrjbi.png" alt="picture of the fallback behavior as described below" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It applies the same mechanical styling to minimize differences, such as a non-static position and &lt;code&gt;isolation: isolate;&lt;/code&gt; but most notably, it moves the title back inline and does two &lt;code&gt;!important&lt;/code&gt; things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The title &lt;code&gt;color&lt;/code&gt; becomes &lt;code&gt;currentColor&lt;/code&gt; - the content behind the title switches from the body to the inside of your &lt;code&gt;fieldset-legend&lt;/code&gt; container, which may have a very different background. Using &lt;code&gt;currentColor&lt;/code&gt; ensures the content is readable because the rest of the content in your not-a-fieldset is likely already set appropriately.&lt;/li&gt;
&lt;li&gt;Along the same lines, I can't know if your title element already had its own background (though if it did, you don't need this utility to position it over the border) so the background is forced to &lt;code&gt;transparent&lt;/code&gt;, ensuring &lt;code&gt;currentColor&lt;/code&gt; on the &lt;code&gt;fieldset-legend&lt;/code&gt;'s background, which in most cases will already be readable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To determine specific fallback behavior, you can set&lt;br&gt;
&lt;code&gt;--fl-fallback-title-color&lt;/code&gt; and &lt;code&gt;--fl-fallback-title-background&lt;/code&gt; which will be used in place of &lt;code&gt;currentColor&lt;/code&gt; or &lt;code&gt;transparent&lt;/code&gt; in the event it is rendered somewhere without support.&lt;/p&gt;

&lt;p&gt;And this is what the custom demo above looks like in browsers that don't support &lt;code&gt;fieldset-legend&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsuu8ule5m9xzw2biiii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmsuu8ule5m9xzw2biiii.png" alt="picture of the fallback behaviors as described above" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Further fallback support
&lt;/h3&gt;

&lt;p&gt;If you know how to use my old &lt;a href="https://github.com/propjockey/css-sweeper?tab=readme-ov-file#css-is-a-programming-language-thanks-to-the-space-toggle-trick" rel="noopener noreferrer"&gt;Space Toggle&lt;/a&gt; technique, the library also provides:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--fl-supported&lt;/code&gt;, which will be a space when supported and &lt;code&gt;initial&lt;/code&gt; when not supported&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--fl-not-supported&lt;/code&gt;, which is the opposite.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open Contact 👽
&lt;/h2&gt;

&lt;p&gt;Please do reach out if you need help with any of this, have feature requests, or want to share what you've created!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PropJockey.io&lt;/th&gt;
&lt;th&gt;CodePen&lt;/th&gt;
&lt;th&gt;DEV Blog&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;Mastodon&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://propjockey.io" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fpropjockey-lines.svg" alt="PropJockey.io" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fcodepen.svg" alt="CodePen" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/janeori"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fdev.svg" alt="DEV Blog" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fgithub.svg" alt="GitHub" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://front-end.social/@JaneOri" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fmastodon.svg" alt="Mastodon" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://bsky.app/profile/janeori.propjockey.io" rel="noopener noreferrer"&gt;🦋@JaneOri.PropJockey.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/jane0ri" rel="noopener noreferrer"&gt;𝕏@Jane0ri&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>GETting your IP Address with CSS - and other 32 bit API responses - without JavaScript!</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Mon, 06 Jan 2025 18:30:13 +0000</pubDate>
      <link>https://forem.com/janeori/getting-your-ip-address-with-css-and-other-32-bit-api-responses-without-javascript-402h</link>
      <guid>https://forem.com/janeori/getting-your-ip-address-with-css-and-other-32-bit-api-responses-without-javascript-402h</guid>
      <description>&lt;p&gt;Just over a week ago while scrolling YouTube shorts after work on a Friday, looking for something to want, this 35 second video stopped me and took 100% of my attention&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/WzlTtn8NCZI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;After one and a half playthroughs, I paused it, tilted my head back and stared off into space as ideas rushed into my mind.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I could make Chess in CSS with the CPU Hack."&lt;/p&gt;

&lt;p&gt;"Could I connect it to that tablebase without JS?"&lt;/p&gt;

&lt;p&gt;"The only way CSS can reach out to a server is with background ima---!!!&lt;/p&gt;

&lt;p&gt;It can also use &lt;code&gt;url()&lt;/code&gt; as &lt;code&gt;content&lt;/code&gt; on a pseudo element, which can resize the parent, then &lt;code&gt;inset: 0px&lt;/code&gt; an absolute position &lt;code&gt;container&lt;/code&gt; inside of it and I can measure that with &lt;a href="https://dev.to/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j"&gt;tan(atan2())&lt;/a&gt; aaand that's my GET request's response data...&lt;/p&gt;

&lt;p&gt;If the element resizes based on that content, it would be useful to have it off screen to avoid causing scrollbars - plus better DX for anyone using would if it didn't need a weird one-off setup that you had to build &lt;em&gt;inside&lt;/em&gt; of... So how do I get the data out --&lt;/p&gt;

&lt;p&gt;I've known how to lift data to &lt;code&gt;:root&lt;/code&gt; since the &lt;a href="https://dev.to/janeori/expert-css-the-cpu-hack-4ddj"&gt;CPU Hack&lt;/a&gt; since it already does that on a much smaller scale but never had a reason to try..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then I paused as my eyes darted around without any awareness of my physical surroundings, looking for edge cases among the components I've assembled - "What can't be done here?"&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I'd have to &lt;code&gt;@container style(--x: N)&lt;/code&gt; wrap my urls so gamestate can choose what ones to use but I'd only have a hardcoded list of possible endpoints until we get &lt;code&gt;url()&lt;/code&gt; &lt;a href="https://drafts.csswg.org/css-link-params/" rel="noopener noreferrer"&gt;param()&lt;/a&gt;...&lt;br&gt;
AH WAIT! I could also generate the necessary request url and prompt the player to &lt;a href="https://dev.to/janeori/the-css-smear-hack-4fd1"&gt;SMEAR&lt;/a&gt; it into the page, shrouded as the UI to confirm their move..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I leaned back and splayed my fingers and palms, panning my eyes across the room slowly as excitement built inside me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"16 bits on the width and 16 bits on the height is huge but well within possible..."&lt;/p&gt;

&lt;p&gt;"I think that's it! I can do it!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;
  The rest of this plot exposition isn't directly relevant, but I'm enjoying the moment so I'll leave the writing here for anyone reading along
  &lt;p&gt;So I started building my chess board in &lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;CodePen, where all my ideas start their journey into realization&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I guess it's time to learn Chess so I know how to build the API request and to determine if ~32 bits was enough response data for it to communicate the CPU move..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;I played Chess briefly back in my teenage IRC-loving, game hacking days and quit at the end of a losing streak with hysterical laughter when I devised a way to cheat every move of a match and still lost. (I made my friend go first then used the move he wrote in IRC against the "expert" computer player in the chess game I downloaded, then communicated to him what the CPU did as my move.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was COMPLETELY oblivious to how much of Chess I knew absolutely nothing about.&lt;/p&gt;

&lt;p&gt;I searched for existing APIs I could just wrap, assuming I'll generate an image on my server use the height/width as the response data. I quickly found &lt;a href="https://chess-api.com/" rel="noopener noreferrer"&gt;chess-api.com&lt;/a&gt; which can respond with a next move given "FEN" as input.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation" rel="noopener noreferrer"&gt;learned FEN&lt;/a&gt;, which lead to even more discovery of how much I didn't know about Chess. I knew I could generate the SMEAR Hack to build the gamestate in FEN format, and most importantly, I measured that I could indeed bitpack the important response data &lt;code&gt;move: "b7b8q"&lt;/code&gt; into 32 bits. (It's an 8x8 board and I know where everything is, not much fundamentally has to be provided in the response.)&lt;/p&gt;

&lt;p&gt;Now, I know with more certainty I can program Chess against a CPU Player in 100% CSS.&lt;/p&gt;

&lt;p&gt;I also knew at that moment, I should definitely rewatch The Queen's Gambit because I absolutely loved it and can't wait to pick up on new details.&lt;/p&gt;



&lt;/p&gt;

&lt;p&gt;The universe is talking to me and I'm putting the excitement into action to keep the loop flowing.&lt;/p&gt;

&lt;p&gt;Time to test the CSS API URL GET ALL CAPS TRAIN idea and see if it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demoing the basics
&lt;/h2&gt;

&lt;p&gt;First, establish appropriate background noise for this endeavor, &lt;a href="https://www.netflix.com/title/80234304" rel="noopener noreferrer"&gt;The Queen's Gambit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, confirm measuring an image.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/ab9685602b2041007a7f85177a1bc635?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Easy enough! Just make sure all the content inside stays absolute so it doesn't change the size of the container holding the image.&lt;/p&gt;

&lt;p&gt;Throw in a test for changing what URL we GET...&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/19e9ede4136dbb7310e6b0b0119c35f7?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Proof of concept confirmed, next leg of this journey was lifting the response data to &lt;code&gt;:root&lt;/code&gt; so I can make all the setup invisible and at least 65535px off screen to the top and left.&lt;/p&gt;

&lt;p&gt;I knew this was going to be heavy, esoteric CSS+HTML and &lt;a href="https://github.com/propjockey/propjockey.io/blame/main/src/components/Jane.astro#L76" rel="noopener noreferrer"&gt;I make things that help people make things&lt;/a&gt;, so right from the start, I built this with the intention of making it an easy-to-use library.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/propjockey/css-api-fetch/tree/52abadea53c09048ccabe082ec3384b7e271aa8e" rel="noopener noreferrer"&gt;version 1&lt;/a&gt;, &lt;a href="https://github.com/propjockey/css-api-fetch/blob/52abadea53c09048ccabe082ec3384b7e271aa8e/html-templates-root.md" rel="noopener noreferrer"&gt;Copy plop some specific (bulky) HTML&lt;/a&gt; in your page with a handful of options built in to make the requests and do the &lt;code&gt;:root&lt;/code&gt; lift for you.&lt;/p&gt;

&lt;p&gt;Import the library and specify the API endpoint from your CSS,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="sx"&gt;url(https://unpkg.com/css-api-fetch@1/api-fetch.css)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--api-id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.api-fetch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--api-fetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url(https://css-api.propjockey.io/ip-address.php)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the data will be on &lt;code&gt;:root&lt;/code&gt; for the rest of your app once it's ready.&lt;/p&gt;

&lt;p&gt;But it gets better, fast!&lt;/p&gt;

&lt;h2&gt;
  
  
  First Public Demonstration, Chrome Only, :root
&lt;/h2&gt;

&lt;p&gt;GETting a user's IP Address with CSS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/propjockey/pen/pvzrWyG" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flk2jqiwuo4pfbzlr0rnk.png" alt="Screenshot of the initial state of the codepen linked" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm not going to go into super specific implementation details in this post of how exactly that CSS+HTML lifts the data to &lt;code&gt;:root&lt;/code&gt; in that original public demo because a couple of CSS loving friends pointed me in a much easier direction I hadn't learned about!&lt;/p&gt;

&lt;p&gt;
  but I will put this quick summary here for anyone interested
  &lt;p&gt;&lt;code&gt;:root:has(.specific-element:hover) { ..state... }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the specific-element descendant knows it's &lt;code&gt;:hover&lt;/code&gt;, it can change to put a different element on top of where it currently is which causes the next element to be hovered, then &lt;code&gt;:root:has(...:hover)&lt;/code&gt; changes its state based on that element too, in a loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/janeori/expert-css-the-cpu-hack-4ddj"&gt;The CPU Hack&lt;/a&gt; lets us accumulate the state as different elements are &lt;code&gt;:hover&lt;/code&gt;'d.&lt;/p&gt;

&lt;p&gt;So if the data we want to lift is '512', we can sequence 'thousands', 'hundreds', 'tens', and 'ones', telling the child to present an element 0 to 9 representing the value it's holding in that position.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.api-transfer-value-0:hover&lt;/code&gt;, &lt;code&gt;...5:hover&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Child knows what numeric position we're asking for because state cascades, &lt;code&gt;:has()&lt;/code&gt; lifts the value because the child presented the corresponding digit for us to &lt;code&gt;:hover&lt;/code&gt; and read at &lt;code&gt;:root&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The CPU Hack accumulates it into one value then tells the child to stop reporting after the ones position is saved.&lt;/p&gt;

&lt;p&gt;The HTML in the demo linked above reveals a bit of what setup is required for that. The CSS code for it is here in &lt;a href="https://unpkg.com/css-api-fetch@1/api-fetch.css" rel="noopener noreferrer"&gt;v1 of css-api-fetch&lt;/a&gt;.&lt;/p&gt;



&lt;/p&gt;

&lt;h2&gt;
  
  
  Second Public Demonstration - Cross Browser, no :root
&lt;/h2&gt;

&lt;p&gt;The only difference here is that I don't lift it to :root and instead leave it in the container, DX requires you to wrap all of your content that needs access to the data in an absolutely positioned element inside of the hack's context.&lt;/p&gt;

&lt;p&gt;In my original exposition I mentioned that I wanted to avoid this scenario, but it's cross browser and it has its uses too. Plus, it's not like you &lt;em&gt;can't&lt;/em&gt; build your whole app inside the specific html setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/propjockey/pen/EaYvdey" rel="noopener noreferrer"&gt;You can see in the HTML tab&lt;/a&gt; just how much less effort is involved in there.&lt;/p&gt;

&lt;p&gt;Just &lt;a href="https://github.com/propjockey/css-api-fetch/blob/0289b936fd583b71234728d30393a89f9c6abe25/html-templates-compat.md" rel="noopener noreferrer"&gt;4 specific nested html elements&lt;/a&gt; are required in the cross-browser version, then you build anything you want using your response data inside of that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Third Public Demonstration - Chrome Only, :root, trivial
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://front-end.social/@kizu/113732033335417449" rel="noopener noreferrer"&gt;Kizu mentioned it might be possible to simplify&lt;/a&gt; so I went digging, then after not quite putting the dots together, &lt;a href="https://front-end.social/@css/113732421808915352" rel="noopener noreferrer"&gt;T. Afif showed a possible way in that direction&lt;/a&gt;, I learned a ton from that article but was unsatisfied with the division and precision loss so I dove into the articles and &lt;a href="https://scroll-driven-animations.style/tools/view-timeline/ranges/" rel="noopener noreferrer"&gt;amazing tools&lt;/a&gt; &lt;a href="https://front-end.social/@bramus" rel="noopener noreferrer"&gt;Bramus&lt;/a&gt; made and came up with a variation that satisfied my use cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/propjockey/pen/JoPyxrK" rel="noopener noreferrer"&gt;As you can see in the CodePen's HTML&lt;/a&gt; the setup is so simple it's literally &lt;a href="https://github.com/propjockey/css-api-fetch/tree/0289b936fd583b71234728d30393a89f9c6abe25?tab=readme-ov-file#adding-the-html" rel="noopener noreferrer"&gt;one line of HTML&lt;/a&gt; to fetch and your response data is on &lt;code&gt;:root&lt;/code&gt;. In this case I used a second element to present the data too. AMAZING!&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://www.npmjs.com/package/css-api-fetch" rel="noopener noreferrer"&gt;packaged it up for a v3 release&lt;/a&gt; making all the handoff mindless, putting the measuring stuff off screen already set up since it's not important.&lt;/p&gt;

&lt;p&gt;I posted about the release, then &lt;a href="https://front-end.social/@css/113736016084195154" rel="noopener noreferrer"&gt;T. Afif linked a different article demonstrating a similar view-timeline configuration for measuring&lt;/a&gt; that I worked out for this - without the division! :)&lt;/p&gt;

&lt;p&gt;I highly recommend reading all the work I've linked above, there's some exciting, talented CSS people sharing their ideas -- and knowing something is possible opens entire worlds of cool ideas!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;css-api-fetch&lt;/code&gt; doing?
&lt;/h2&gt;

&lt;p&gt;Importing &lt;a href="https://www.npmjs.com/package/css-api-fetch" rel="noopener noreferrer"&gt;css-api-fetch&lt;/a&gt; and adding a single line of html, you can fairly easily do API requests in your CSS and not have to consider the complexity of view-timeline yourself. I made it so you don't have to think about it, but for the curious, here's the concept and setup the library uses internally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Two animations setting a --ints &lt;code&gt;from&lt;/code&gt; the max values your API will respond with &lt;code&gt;to&lt;/code&gt; 0&lt;/li&gt;
&lt;li&gt;host (scope) the animations and their timelines on &lt;code&gt;:root&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;create a measuring element sized to the max values your API will respond with, with non-static position, overflow hidden, etc to make it suitable for reading view/scroll positions.&lt;/li&gt;
&lt;li&gt;Throw it absolute offscreen to the top left so it doesn't cause scrollbars on your app.&lt;/li&gt;
&lt;li&gt;load the image &lt;code&gt;url()&lt;/code&gt; inside of a nested &lt;code&gt;size&lt;/code&gt; &lt;code&gt;container&lt;/code&gt;'s pseudo element's &lt;code&gt;content&lt;/code&gt; and set &lt;code&gt;view-timeline&lt;/code&gt;s for both &lt;code&gt;inline&lt;/code&gt; and &lt;code&gt;block&lt;/code&gt; corresponding to the animations in step 1&amp;amp;2.&lt;/li&gt;
&lt;li&gt;The animations now report the size of the image to the whole document&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a demonstration of all 6 of the steps above, step 4 is commented out so you can see it. We'll use &lt;a href="https://picsum.photos/" rel="noopener noreferrer"&gt;Lorem Picsum&lt;/a&gt; to emulate specific height &amp;amp; width api responses easily.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/a2f529ef98795bf645ce489bc2e8ffef?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;That's the fundamentals but the library sets up &lt;a href="https://github.com/propjockey/css-api-fetch/tree/main?tab=readme-ov-file#customize-api-endpoints-in-the-css-root-version" rel="noopener noreferrer"&gt;4 separate API request/responses&lt;/a&gt; that you can use simultaneously without knowing how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's &lt;code&gt;css-bin-bits&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;All 3 of the public releases have demonstrations that use a library I made ages ago, &lt;a href="https://propjockey.github.io/css-bin-bits/" rel="noopener noreferrer"&gt;css-bin-bits&lt;/a&gt; that makes it super easy to do bitwise operations on 16 bit integers in 100% CSS and you don't usually have to write any calc() yourself - the library takes care of it all. For the 32 bit IP Address, using &lt;code&gt;css-bin-bits&lt;/code&gt; I was able to limit the size of the image to exactly 16 bits on a single dimension.&lt;/p&gt;

&lt;p&gt;Without bitpacking the 32 bit IP Address into two 16 bit values, &lt;a href="https://front-end.social/@css/113736545957058158" rel="noopener noreferrer"&gt;T. Afif demonstrates&lt;/a&gt; you could use a &lt;code&gt;255255px by 255255px&lt;/code&gt; measuring container and split the response data out with decimal math - skipping &lt;code&gt;css-bin-bits&lt;/code&gt;. &lt;code&gt;css-api-fetch&lt;/code&gt; supports you setting any maximum, shipping with &lt;code&gt;99999&lt;/code&gt; as the default maximum, much more than 16 bits (65535), but not quite &lt;code&gt;255255&lt;/code&gt; lol.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you set up an API to respond with data in the image?
&lt;/h2&gt;

&lt;p&gt;The lightest and most trivial way to pack data into the width and height of an image is to make the server respond with a bare-minimum SVG.&lt;/p&gt;

&lt;p&gt;Here's how the IP address is packed and served in PHP, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-type: image/svg+xml'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nv"&gt;$adr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s1"&gt;'255.255.255.255'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;$hex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;str_pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dechex'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;preg_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/[^\d\.]/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$adr&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;STR_PAD_LEFT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="cm"&gt;/* rx returns first 4 hex chars, removing up to 3 leading 0's */&lt;/span&gt;
  &lt;span class="nv"&gt;$width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;hexdec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;preg_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/(^0{0,3})|(.{4}$)/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="cm"&gt;/* rx returns the last 4 hex chars, removing up to 3 leading 0's */&lt;/span&gt;
  &lt;span class="nv"&gt;$height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;hexdec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;preg_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/^.{4}0{0,3}/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$hex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s1"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;svg xmlns="http://www.w3.org/2000/svg" width="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'px" height="'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$height&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'px"&amp;gt;&amp;lt;/svg&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API requests you set up could easily accomplish a handful of low hanging fruit that CSS can't otherwise do without JavaScript&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RNG &lt;/li&gt;
&lt;li&gt;Current Time&lt;/li&gt;
&lt;li&gt;TimeZone &lt;/li&gt;
&lt;li&gt;Country Code&lt;/li&gt;
&lt;li&gt;Operating System&lt;/li&gt;
&lt;li&gt;or connection to any other api if you wrap it - such as &lt;a href="https://chess-api.com/" rel="noopener noreferrer"&gt;chess-api&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What comes next?
&lt;/h2&gt;

&lt;p&gt;Chess?!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5n35loivqlijw3anuk27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5n35loivqlijw3anuk27.png" alt="Screenshot of an early build of an unreleased Chess game with a bit of code on the left indicating specific openings such as king's pawn and queen's pawn. The board shows an opening move has been played by white, it's the Reti Opening, Nf3" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not yet! Plus, Lilian has already made Chess in CSS!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1838643820428165565-495" src="https://platform.twitter.com/embed/Tweet.html?id=1838643820428165565"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1838643820428165565-495');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1838643820428165565&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I'm not sure how it works or if there's a publicly available demo, but I'm sure she'd answer any questions! :)&lt;/p&gt;




&lt;p&gt;For now, my excitement is focused elsewhere......&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I want more than 32 bits per request.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I want 512 bits per request.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyvict2cu9jb742mui6b.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyvict2cu9jb742mui6b.gif" alt="Gif showing a bunch of integer data being set one at a time in a harsh terminal appearance" width="726" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No JavaScript.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open Contact 👽
&lt;/h2&gt;

&lt;p&gt;Please do reach out if you need help with any of this, have feature requests, want to share what you've created, or wish to learn more.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PropJockey.io&lt;/th&gt;
&lt;th&gt;CodePen&lt;/th&gt;
&lt;th&gt;DEV Blog&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;Mastodon&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://propjockey.io" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fpropjockey-lines.svg" alt="PropJockey.io" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fcodepen.svg" alt="CodePen" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/janeori"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fdev.svg" alt="DEV Blog" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fgithub.svg" alt="GitHub" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://front-end.social/@JaneOri" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fmastodon.svg" alt="Mastodon" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://bsky.app/profile/janeori.propjockey.io" rel="noopener noreferrer"&gt;🦋@JaneOri.PropJockey.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/jane0ri" rel="noopener noreferrer"&gt;𝕏@Jane0ri&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>hacking</category>
      <category>api</category>
    </item>
    <item>
      <title>CSS Type Casting to Numeric: tan(atan2()) Scalars</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Mon, 09 Oct 2023 05:48:34 +0000</pubDate>
      <link>https://forem.com/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j</link>
      <guid>https://forem.com/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j</guid>
      <description>&lt;p&gt;In current browser implementations of CSS, you cannot divide by length types; &lt;code&gt;calc(100vw / 5px)&lt;/code&gt; does not work. It will eventually because &lt;a href="https://www.w3.org/TR/css-values-4/#calc-type-checking" rel="noopener noreferrer"&gt;it's in the spec&lt;/a&gt;, and &lt;a href="https://github.com/web-platform-tests/interop/issues/513" rel="noopener noreferrer"&gt;with enough support it may happen soon&lt;/a&gt;. But for now, no way to produce scalars based on size.&lt;/p&gt;

&lt;p&gt;With, technically, one up-and-coming exception, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/atan2" rel="noopener noreferrer"&gt;&lt;code&gt;atan2()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://caniuse.com/mdn-css_types_atan2" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F417b276cdr1mkrvyvq21.png" alt="screenshot of caniuse data for atan2 showing 87.25% support" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;atan2()&lt;/code&gt; accepts 2 params, effectively the slope, rise over run, y divided by x, &lt;code&gt;atan2(height, width)&lt;/code&gt; and returns the angle from the bottom of the slope looking to the top.&lt;/p&gt;

&lt;p&gt;Then, did you know? &lt;code&gt;tan(atan2(height, width))&lt;/code&gt; is the scalar &lt;code&gt;&amp;lt;number&amp;gt;&lt;/code&gt; between the two dimensions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No, I did not know that; is that true?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's right... One could scale all kinds of dimensions, using simple trigonometry functions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Really...?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If one were so &lt;em&gt;inclined&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  tan(atan2()) is just a scalar
&lt;/h2&gt;

&lt;p&gt;No need to dive into normal trig things; pretty graphs, maps, and geometric art, etc.&lt;/p&gt;

&lt;p&gt;Fundamentally, &lt;code&gt;tan(atan2())&lt;/code&gt; is just a scalar between two dimensions.&lt;/p&gt;

&lt;p&gt;For this article I'll focus on identity scaling as a means to typecast into a numeric value but there are many ways to use this and compare any two dimensions now.&lt;/p&gt;




&lt;h3&gt;
  
  
  Screensize
&lt;/h3&gt;

&lt;p&gt;One thing many people want is the ability to have the numeric pixel width (or height) of the viewport to find the aspect-ratio or do other calcs down the line.&lt;/p&gt;

&lt;p&gt;I solved screensize in 100% CSS previously with a binary search using &lt;a href="https://dev.to/janeori/expert-css-the-cpu-hack-4ddj"&gt;The CPU Hack&lt;/a&gt; but I realized today it's so much easier and faster with &lt;code&gt;tan(atan2())&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the numeric pixel value of 100vw?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;should be as easy as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--px-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but atan2 is super buggy in browsers right now. (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/atan2#parameters" rel="noopener noreferrer"&gt;mixing vw and px was intended to work&lt;/a&gt;)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Chrome returns &lt;code&gt;100&lt;/code&gt; in this case, &lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1491662" rel="noopener noreferrer"&gt;which is strange/undesirable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Firefox returns &lt;code&gt;0&lt;/code&gt; in this case because of the mixed units failing. If both are &lt;code&gt;vw&lt;/code&gt; or both are &lt;code&gt;px&lt;/code&gt;, Firefox correctly returns &lt;code&gt;100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Safari seems to &lt;em&gt;always&lt;/em&gt; return &lt;code&gt;0&lt;/code&gt; from &lt;code&gt;tan(atan2(Y, X))&lt;/code&gt;  for &lt;em&gt;any&lt;/em&gt; &lt;code&gt;Y&lt;/code&gt; and &lt;em&gt;any&lt;/em&gt; &lt;code&gt;X&lt;/code&gt;. With or without units, mixed or matching. (edit: &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=263000" rel="noopener noreferrer"&gt;Unless you wrap it in calc()&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So for now we first need &lt;code&gt;100vw&lt;/code&gt; as &lt;code&gt;NNNpx&lt;/code&gt;, which is easy in CSS because that's what happens automatically if you register a var as &lt;code&gt;&amp;lt;length&amp;gt;&lt;/code&gt; then set it to a value like &lt;code&gt;--100vw: 100vw&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="n"&gt;--100vw&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"&amp;lt;length&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;initial-value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this works in Chrome exactly as hoped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--100vw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--px-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--100vw&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--px-width&lt;/code&gt; is the width of the screen as a, usually integer, number&lt;/p&gt;

&lt;p&gt;here it is live including one for &lt;code&gt;100vh&lt;/code&gt; too:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/6a0d83e6c3dd0eacdfa35cd736a26d83?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h3&gt;
  
  
  get font-size and more
&lt;/h3&gt;

&lt;p&gt;You can use this same idea for any container query units, calc sizes containing mixed units, find out the px size of rem, anything you want. Just register a length, set its value, and convert to numeric px with &lt;code&gt;tan(atan2())&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/58bff61364b7fbdd344bd0ac6a71dd45?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Easy peasy! Potentially super useful.&lt;/p&gt;

&lt;p&gt;Works with &lt;code&gt;&amp;lt;time&amp;gt;&lt;/code&gt; too&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;12s&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nl"&gt;counter-reset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--ms&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Numeric ms value of 12s + 1ms is: "&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c"&gt;/* counter prints 12001 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all numeric typecasting identity scalars, and it's fun to think about all the ways you can mix calcs now, but there's even more cool stuff tan-atan2 can-a-can-do, perhaps for a future article ~&lt;/p&gt;




&lt;h2&gt;
  
  
  The Trig (update)
&lt;/h2&gt;

&lt;p&gt;The trig behind this isn't necessary to know to use it, but I had a couple questions come up in private about it:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tan( angle )&lt;/code&gt; is a &lt;a href="https://en.wikipedia.org/wiki/Trigonometric_functions#Right-angled_triangle_definitions" rel="noopener noreferrer"&gt;function&lt;/a&gt; that takes an angle then produces a result that's equal to "opposite over adjacent" - the height divided by the width of a right triangle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdoia1yad6ya0wpp4a3c3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdoia1yad6ya0wpp4a3c3.png" alt="a right triangle with a shorter height than width, the hypotenuse bisects the image from the top left to the bottom right, the left side is labeled " width="757" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;atan( ratio )&lt;/code&gt; is a &lt;a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions" rel="noopener noreferrer"&gt;function&lt;/a&gt; that takes the value of height divided by width and returns the angle; the inverse of &lt;code&gt;tan()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;atan2( Y, X )&lt;/code&gt; is a &lt;a href="https://en.wikipedia.org/wiki/Atan2" rel="noopener noreferrer"&gt;programming language's adaptation of atan()&lt;/a&gt; that takes the height (Y) and width (X) as separate arguments instead of dividing them before passing, then returns the same thing atan would have.&lt;/p&gt;

&lt;p&gt;So what this trick is doing is kind of silly in most worlds (and probably why nobody pointed it out sooner) because we're using two trig functions instead of division that &lt;code&gt;calc()&lt;/code&gt; implementations can't do yet.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;atan2( Height, Width )&lt;/code&gt; = &lt;code&gt;angle&lt;/code&gt; from the picture above&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tan( angle )&lt;/code&gt; = &lt;code&gt;Height / Width&lt;/code&gt; from the picture above&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tan( atan2( Height, Width ) )&lt;/code&gt; = &lt;code&gt;Height / Width&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="n"&gt;--MyFullInlineSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"&amp;lt;length&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;initial-value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="n"&gt;--MyEm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"&amp;lt;length&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;initial-value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.in-a-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--MyFullInlineSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="n"&gt;cqi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--MyEm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--MyNumEmsWide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--MyFullInlineSize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--MyEm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="py"&gt;--MyNumPxWide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--MyFullInlineSize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="m"&gt;1px&lt;/span&gt;
  &lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The End
&lt;/h2&gt;

&lt;p&gt;If you think this is useful, fun, or interesting, it's the kind of thing I do in my free time! So please do consider following me around the web:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PropJockey.io&lt;/th&gt;
&lt;th&gt;CodePen&lt;/th&gt;
&lt;th&gt;DEV Blog&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;Mastodon&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://propjockey.io" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fpropjockey-lines.svg" alt="PropJockey.io" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fcodepen.svg" alt="CodePen" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/janeori"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fdev.svg" alt="DEV Blog" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fgithub.svg" alt="GitHub" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://front-end.social/@JaneOri" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fmastodon.svg%3F" alt="Mastodon" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;and &lt;a href="https://x.com/jane0ri" rel="noopener noreferrer"&gt;𝕏@Jane0ri&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👽💜&lt;br&gt;
// Jane Ori&lt;/p&gt;




&lt;p&gt;PS: I've been laid off recently and am looking for a job!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linkedin.com/in/JaneOri" rel="noopener noreferrer"&gt;https://linkedin.com/in/JaneOri&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over 13 years of full stack (mostly JS) engineering work and consulting, ready for the right opportunity!&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>hacking</category>
      <category>howto</category>
    </item>
    <item>
      <title>[CSS Fix] When justify-content: space-evenly overflows, un-center the content</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Tue, 26 Sep 2023 14:42:27 +0000</pubDate>
      <link>https://forem.com/janeori/css-fix-when-justify-content-space-evenly-overflows-un-center-the-content-4l50</link>
      <guid>https://forem.com/janeori/css-fix-when-justify-content-space-evenly-overflows-un-center-the-content-4l50</guid>
      <description>&lt;p&gt;Bramus has shown us a clever scroll animation based &lt;a href="https://github.com/propjockey/css-sweeper#basics-of-space-toggle" rel="noopener noreferrer"&gt;Space Toggle&lt;/a&gt; that allows our styles to respond based on weather the element can scroll or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bram.us/2023/09/16/solved-by-css-scroll-driven-animations-detect-if-an-element-can-scroll-or-not/" rel="noopener noreferrer"&gt;https://www.bram.us/2023/09/16/solved-by-css-scroll-driven-animations-detect-if-an-element-can-scroll-or-not/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pop over there to see how it works, continue here to see how I've used it ~&lt;/p&gt;




&lt;h2&gt;
  
  
  When we use space-evenly
&lt;/h2&gt;

&lt;p&gt;When content spans the full width of your page in columns, like itineraries, house banners, etc,&lt;br&gt;
&lt;code&gt;justify-content: space-evenly;&lt;/code&gt; is subjectively the best looking layout for it.&lt;/p&gt;

&lt;p&gt;(that's .&lt;code&gt;justify-evenly&lt;/code&gt; if you tailwind)&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/2efa1cfd97260f464e111f1104ee239d?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What space-evenly breaks
&lt;/h2&gt;

&lt;p&gt;If a column section is set to space-evenly but the code allows more columns to be added and scrolled horizontally, you'd probably expect &lt;code&gt;overflow-x: auto;&lt;/code&gt; to do it well.&lt;/p&gt;

&lt;p&gt;Here's 7 columns with &lt;code&gt;overflow-x: auto;&lt;/code&gt; and the same code as shown previously:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/2a57fee9c51d8d2a1711b0a6dadeb1ac?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;At least 1.7 columns are totally inaccessible, scrolled off to the left in the unreachable negative scroll positions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why space-evenly breaks on overflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/67111711/how-not-to-overflow-on-the-left-when-using-justify-content-space-evenly/67115137#67115137" rel="noopener noreferrer"&gt;This answer on stackoverflow does a great job&lt;/a&gt; explaining the 'why':&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I found the &lt;a href="https://www.w3.org/TR/css-align-3/#distribution-values" rel="noopener noreferrer"&gt;CSS specs&lt;/a&gt; describe a "fallback alignment" in case the spacing cannot be applied. For space-between this is start whereas for space-evenly this is center, unfortunately. This explains the observed overlap difference.&lt;/p&gt;

&lt;p&gt;Nevertheless, the spec also mentions&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A future level of this module may allow the fallback alignment to be specified explicitly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;so perhaps with time we may be able to set the fallback to start.&lt;/p&gt;

&lt;p&gt;Until that time I've found a workaround: set min-width on the container. To avoid excessive width, wrap the container in a scrolling parent element. &lt;a href="https://jsfiddle.net/oL7yke6g/" rel="noopener noreferrer"&gt;Demo here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;author: &lt;a href="https://stackoverflow.com/users/2290493/paul" rel="noopener noreferrer"&gt;Paul&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where is the CSS-Only fix to space-evenly overflows?!
&lt;/h2&gt;

&lt;p&gt;It starts &lt;a href="https://www.bram.us/2023/09/16/solved-by-css-scroll-driven-animations-detect-if-an-element-can-scroll-or-not/" rel="noopener noreferrer"&gt;in this article from Bramus&lt;/a&gt;, then adapting his Space Toggle into our solution, we create a class to replace &lt;code&gt;.justify-evenly&lt;/code&gt; here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;detect-scroll&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--can-scroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.justify-evenly-until-scroll&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--start-if-can-scroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--can-scroll&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;--start-if-can-scroll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;space-evenly&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;detect-scroll&lt;/span&gt; &lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;animation-timeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt; &lt;span class="nb"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making a note here, huge success - and we added &lt;code&gt;inline&lt;/code&gt; to the &lt;code&gt;animation-timeline: scroll(self inline)&lt;/code&gt; to make it respond to horizontal scroll instead.&lt;/p&gt;

&lt;p&gt;Swap &lt;code&gt;.justify-evenly&lt;/code&gt; in the html with our new &lt;code&gt;.justify-evenly-until-scroll&lt;/code&gt; and you get:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/9b20183c3a6daa1c426c568777a50d3a?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Hooray!&lt;/p&gt;

&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;If you think this is useful, fun, or interesting, it's the kind of thing I do in my free time! So please do consider following me here, &lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;on CodePen&lt;/a&gt;, and &lt;a href="https://twitter.com/Jane0ri" rel="noopener noreferrer"&gt;on X&lt;/a&gt; as well!&lt;/p&gt;

&lt;p&gt;👽💜&lt;br&gt;
// Jane Ori&lt;/p&gt;



&lt;p&gt;PS: I've been laid off recently and am looking for a job!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linkedin.com/in/JaneOri" rel="noopener noreferrer"&gt;https://linkedin.com/in/JaneOri&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over 13 years of full stack (mostly JS) engineering work and consulting, ready for the right opportunity!&lt;/p&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PropJockey.io&lt;/th&gt;
&lt;th&gt;CodePen&lt;/th&gt;
&lt;th&gt;DEV Blog&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;X&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://propjockey.io" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fpropjockey-lines.svg" alt="PropJockey.io" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fcodepen.svg" alt="CodePen" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/janeori"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fdev.svg" alt="DEV Blog" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/propjockey" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fgithub.svg" alt="GitHub" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://x.com/jane0ri" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpropjockey%2Fpropjockey-brand%2Fmain%2Fexternal-social%2F100px%2Fx.svg" alt="X" width="100" height="100"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;p&gt;UPDATE:&lt;/p&gt;


&lt;h2&gt;
  
  
  Wait? Nope! Continue, but, heads up:
&lt;/h2&gt;

&lt;p&gt;Bramus (and someone privately) informed me of a new feature with a slight superset of browser support over animation scroll():&lt;/p&gt;

&lt;p&gt;justify-content's &lt;code&gt;safe&lt;/code&gt; keyword&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drafts.csswg.org/css-align-3/#overflow-values" rel="noopener noreferrer"&gt;https://drafts.csswg.org/css-align-3/#overflow-values&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It currently works with &lt;code&gt;justify-content: center safe;&lt;/code&gt; but if you specifically want &lt;code&gt;space-evenly&lt;/code&gt; (I do!), this hack is currently the only working solution.&lt;/p&gt;

&lt;p&gt;All the previous demos use grid, here it is with flex, first using &lt;code&gt;space-evenly safe&lt;/code&gt; which doesn't do anything yet. Then, further down, same hack as used on the grid demos above, also works on flex.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/bGOMgjy?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Thank you!&lt;/p&gt;

</description>
      <category>css</category>
      <category>howtofix</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Expert CSS: The CPU Hack</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Sun, 17 Sep 2023 03:33:01 +0000</pubDate>
      <link>https://forem.com/janeori/expert-css-the-cpu-hack-4ddj</link>
      <guid>https://forem.com/janeori/expert-css-the-cpu-hack-4ddj</guid>
      <description>&lt;p&gt;A "CPU Hack" implies unlocking the ability for continuous crunching of data and re-evaluation of state.&lt;/p&gt;

&lt;p&gt;For example, if cyclic vars didn't automatically fail to invalid (&lt;code&gt;initial&lt;/code&gt;) state in CSS, this would continuously increment the value of &lt;code&gt;--frame-count&lt;/code&gt; here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--input-frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--frame-count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--frame-count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--input-frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spoiler alert: You actually &lt;em&gt;can&lt;/em&gt; do this in CSS, without ever touching JS, I'll show you how!&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 Observables
&lt;/h2&gt;

&lt;p&gt;First, let's establish a handful of observations of advanced CSS animation usage so the final demonstration isn't entirely unexpected.&lt;/p&gt;

&lt;p&gt;Not directly related to "&lt;a href="https://www.youtube.com/watch?v=sstBSuXIW0I" rel="noopener noreferrer"&gt;The 5 Observables&lt;/a&gt;"&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Animation State Rules Over All (almost)
&lt;/h3&gt;

&lt;p&gt;The property assignments set by Animation State trump all Selector State property assignments.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;body background is always &lt;code&gt;hotpink&lt;/code&gt; in this example:&lt;/p&gt;


&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;hotpink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;This is (partially) why Animation State is not allowed to alter properties that control animations. As in, you cannot&lt;sup&gt;[1]&lt;/sup&gt; animate the value of &lt;code&gt;animation-play-state&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(otherwise, once started, a self-setting animation could only be stopped by JS removal of the element it lives on since the animation could set its own &lt;code&gt;animation&lt;/code&gt; value and stay alive no matter what other selector states tried to stop it.&lt;sup&gt;[2]&lt;/sup&gt;)&lt;/p&gt;

&lt;p&gt;Paused animations are no exception; whatever the value is when it's paused still trumps other states.&lt;/p&gt;

&lt;p&gt;[1] Technically there's an unrelated hack that allows this, dealing with inheritance and invalid compute states, but the animation-frame timing of that hack is unreliable for CPU ticking.&lt;/p&gt;

&lt;p&gt;[2] This is what went wrong with Ultron.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Property Assignments in a Keyframe Can Use var()
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the background color is &lt;code&gt;blue&lt;/code&gt; by default, &lt;code&gt;green&lt;/code&gt; if hovering, or &lt;code&gt;red&lt;/code&gt; if hovering specifically within a div. The color changes as the user interacts.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. --var Assignments on Keyframe Results are Cached
&lt;/h3&gt;

&lt;p&gt;We can test this by adding a little indirection to the background color assignment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No matter what, the background is always &lt;code&gt;blue&lt;/code&gt; because it first evaluated as &lt;code&gt;blue&lt;/code&gt; and changes to &lt;code&gt;--color&lt;/code&gt; are not re-computed.&lt;/p&gt;

&lt;p&gt;Even if the animation is &lt;code&gt;paused&lt;/code&gt;, the cached value does not change when states in the background change.&lt;/p&gt;

&lt;p&gt;(paused animations use the cached value)&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Changing An Animation Property Breaks The Cache
&lt;/h3&gt;

&lt;p&gt;By altering the &lt;code&gt;animation-duration&lt;/code&gt; while the user :hovers, the animation cache is recomputed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The end result here is exactly the same as in #2 above; the background color is &lt;code&gt;blue&lt;/code&gt; by default, &lt;code&gt;green&lt;/code&gt; if hovering, or &lt;code&gt;red&lt;/code&gt; if hovering specifically within a div.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Safari has a bug where it does NOT recompute the cache when an animation property changes, so we've entered Chrome-only territory (firefox can't animate --vars yet)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we "changed" the &lt;code&gt;animation-duration&lt;/code&gt; to &lt;code&gt;1s&lt;/code&gt;, it would not technically change, and the cache does not recompute.&lt;/p&gt;

&lt;p&gt;You begin to get interesting behavior if both :hover states use the same value that's different from the default state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's show this one live:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/6fcc512d52cd936640f44dfbdd17cda1?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;depending on where your mouse enters the screen (from the top vs bottom), you'll get different colors that "lock" to one or the other until you mouse out.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Two Animations
&lt;/h3&gt;

&lt;p&gt;What if instead of pseudo selector state changing the value of &lt;code&gt;--color&lt;/code&gt;, we made another animation that changed it?&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;example&lt;/code&gt; animation still sets &lt;code&gt;--bg&lt;/code&gt; based on &lt;code&gt;--color&lt;/code&gt;, so we can expect it to still have the same caching behavior.&lt;/p&gt;

&lt;p&gt;Changing an animation property of our &lt;code&gt;example&lt;/code&gt; animation should also, still, cause it to recompute its cache.&lt;/p&gt;

&lt;p&gt;So, finally, the &lt;code&gt;example&lt;/code&gt; animation should accept whatever the current &lt;code&gt;--color&lt;/code&gt; value is from another animation and cache it with its state.&lt;/p&gt;

&lt;p&gt;Here's what that would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="m"&gt;3s&lt;/span&gt; &lt;span class="n"&gt;step-end&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"color preview"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;33&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;33&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;67&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;67&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Even though we're pausing the &lt;code&gt;example&lt;/code&gt; animation on :hover, that still constitutes a change from the default &lt;code&gt;running&lt;/code&gt; state so it re-computes and pauses in the same CSS paint frame.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and here's what it feels like:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/3f8b84767d12d3eea5308864ae6ddab6?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The bg locks to whatever it was when you entered, then re-computes and re-locks to whatever it is when you leave.&lt;/p&gt;

&lt;p&gt;Persistence! NEAT!&lt;/p&gt;




&lt;h2&gt;
  
  
  The CPU Hack Begins
&lt;/h2&gt;

&lt;p&gt;The previous information implies something extremely interesting; grabbing the cached &lt;em&gt;value&lt;/em&gt; from an animation does not recompute it, so it shouldn't cause an invalid cyclic state if the source of the cached value is one step removed.&lt;/p&gt;

&lt;p&gt;Double capture, compute once, manage the timing... Should be possible.&lt;/p&gt;

&lt;p&gt;We have the &lt;code&gt;example&lt;/code&gt; animation conditionally capturing the value from either normal selector states or from another animation.&lt;/p&gt;

&lt;p&gt;Let's imagine it's capturing a number instead of a color, like the &lt;code&gt;--frame-count&lt;/code&gt; from the opening of this article.&lt;/p&gt;

&lt;p&gt;And we'll rename it from &lt;code&gt;example&lt;/code&gt; to &lt;code&gt;capture&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;capture&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--input-frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--frame-count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--input-frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;capture&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--frame-captured&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--frame-count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wouldn't it be great if we could set &lt;code&gt;--input-frame&lt;/code&gt; to that &lt;code&gt;--frame-captured&lt;/code&gt; value?&lt;/p&gt;

&lt;p&gt;We know that doing it directly would be cyclic because all 3 of these assignments exist in the same frame:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--input-frame&lt;/code&gt; = &lt;code&gt;--frame-captured&lt;/code&gt;&lt;br&gt;
&lt;code&gt;--frame-count&lt;/code&gt; = &lt;code&gt;--input-frame&lt;/code&gt; + 1&lt;br&gt;
&lt;code&gt;--frame-captured&lt;/code&gt; = &lt;code&gt;--frame-count&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If we capture the captured value though and make sure both captures aren't running at the same time, the capture-capture could hoist that value back to &lt;code&gt;--input-frame&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;Let's try. We'll call the captured capture &lt;code&gt;hoist&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, since we don't want them ever running at the same time (because that would definitely be cyclic), let's start them off &lt;code&gt;paused&lt;/code&gt; to be safe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hoist&lt;/span&gt; &lt;span class="m"&gt;1ms&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capture&lt;/span&gt; &lt;span class="m"&gt;1ms&lt;/span&gt; &lt;span class="n"&gt;infinite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="py"&gt;--input-frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--frame-hoist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--frame-count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--input-frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;counter-reset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--frame-count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"--frame-count: "&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;hoist&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--frame-hoist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--frame-captured&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;capture&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;%,&lt;/span&gt; &lt;span class="err"&gt;100&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--frame-captured&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--frame-count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in order to test this, we also want to set up some dom that we can hover in a specific order to trigger the &lt;code&gt;animation-play-state&lt;/code&gt; in the right order. No gaps between the elements and we'll give them classes &lt;code&gt;phase-0&lt;/code&gt;, etc&lt;/p&gt;

&lt;p&gt;The first phase is definitely capturing the original output. So we'll keep &lt;code&gt;hoist&lt;/code&gt; paused and let our old friend &lt;code&gt;capture&lt;/code&gt; run first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.phase-0&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can stop hovering that element to pause both, which will capture &lt;code&gt;--frame-count&lt;/code&gt;, or we can go ahead and set up another element to explicitly do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.phase-1&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, finally? The moment of truth, test if we can run &lt;code&gt;hoist&lt;/code&gt; while &lt;code&gt;capture&lt;/code&gt; is paused, which should give us enough room to avoid the cyclic dependency and plop that output back that the top as input... which should give us our first &lt;code&gt;2&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.phase-2&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here it is live, :hover the cursor from top to bottom to complete a loop:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/86fc64e923093e4ce9e865bd0c9b8da7?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The CPU Hack
&lt;/h2&gt;

&lt;p&gt;Eureka!&lt;/p&gt;

&lt;p&gt;We could make the user pet the dom with their cursor all day orrrr we could move the dom under the cursor the moment it needs to be to automatically trigger :hover&lt;/p&gt;

&lt;p&gt;Let's figure that out!&lt;/p&gt;

&lt;p&gt;We'll need hovering &lt;code&gt;.phase-0&lt;/code&gt; to automatically "goto" &lt;code&gt;.phase-1&lt;/code&gt;, then hovering that will "goto" &lt;code&gt;.phase-2&lt;/code&gt;...&lt;/p&gt;

&lt;p&gt;And then hovering &lt;code&gt;.phase-2&lt;/code&gt; needs to return to a &lt;code&gt;paused, paused&lt;/code&gt; state to avoid any single frame running a compute for both animations at the same time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember: playing or pausing an animation causes it to re-compute on that frame, so changing from &lt;code&gt;running, paused&lt;/code&gt; straight to &lt;code&gt;paused, running&lt;/code&gt; is actually, for one frame, running both at the same time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So we need to "goto" a state that will then "goto" &lt;code&gt;.phase-0&lt;/code&gt;. Since &lt;code&gt;.phase-1&lt;/code&gt; is &lt;code&gt;paused, paused&lt;/code&gt; and "goto" 2, we'll duplicate it and make the new &lt;code&gt;.phase-3&lt;/code&gt; also pause both but "goto" 0 instead.&lt;/p&gt;

&lt;p&gt;Let's add this CSS to what we had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.phase-3&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;animation-play-state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;paused&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we'll use this for the HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ol&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cpu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"phase-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"phase-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"phase-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"phase-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  Here's a recap of each phase if interested
  &lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.phase-0&lt;/code&gt; (&lt;code&gt;hoist&lt;/code&gt; paused, &lt;code&gt;capture&lt;/code&gt; running)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hoist Value is Frozen&lt;/li&gt;
&lt;li&gt;Assign Captured = Output Value&lt;/li&gt;
&lt;li&gt;GOTO &lt;code&gt;.phase-1&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.phase-1&lt;/code&gt; (&lt;code&gt;hoist&lt;/code&gt; paused, &lt;code&gt;capture&lt;/code&gt; paused)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hoist Value is Frozen&lt;/li&gt;
&lt;li&gt;Assign Captured Value = Output Value&lt;/li&gt;
&lt;li&gt;Freeze Captured (at the end of this css paint frame)&lt;/li&gt;
&lt;li&gt;GOTO &lt;code&gt;.phase-2&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.phase-2&lt;/code&gt; (&lt;code&gt;hoist&lt;/code&gt; running, &lt;code&gt;capture&lt;/code&gt; paused)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Captured Value is Frozen&lt;/li&gt;
&lt;li&gt;Assign Hoist Value = Captured Value&lt;/li&gt;
&lt;li&gt;GOTO &lt;code&gt;.phase-3&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;.phase-3&lt;/code&gt; (&lt;code&gt;hoist&lt;/code&gt; paused, &lt;code&gt;capture&lt;/code&gt; paused)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Captured Value is Frozen&lt;/li&gt;
&lt;li&gt;Assign Hoist Value = Captured Value&lt;/li&gt;
&lt;li&gt;Freeze Hoist (at the end of this css paint frame)&lt;/li&gt;
&lt;li&gt;GOTO &lt;code&gt;.phase-0&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;



&lt;/p&gt;

&lt;p&gt;Next, we'll style this &lt;code&gt;.cpu&lt;/code&gt; element so each of its children take up its whole area when their width becomes &lt;code&gt;100%&lt;/code&gt;, z-stacked on top of each other in dom order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.cpu&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.cpu&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;inset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.cpu&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.phase-0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.cpu&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.phase-0&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.phase-1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.cpu&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.phase-1&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.phase-2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.cpu&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.phase-2&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;.phase-3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be the final piece; each phase triggers the next and they'll only happen for one CSS Paint Frame each. Let's see it live!&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/9fbc3990ce135b77c2af8267dbd14a0c?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: we also needed to register the output var (&lt;code&gt;--frame-count&lt;/code&gt;) or else it will suddenly stop working at 100 because of &lt;code&gt;calc()&lt;/code&gt; technically becoming nested each iteration. Casting it to integer prevents this and is much more efficient. The demo above has the &lt;code&gt;@property&lt;/code&gt; code included.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, technically, one small cleanup you could make:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drop the &lt;code&gt;--input-frame&lt;/code&gt; var, just &lt;code&gt;--frame-hoist&lt;/code&gt; directly, it's cleaner.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Rest of the Owl
&lt;/h2&gt;

&lt;p&gt;So you have a CPU in CSS. What can you do with it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/propjockey/pen/ExGXjaE" rel="noopener noreferrer"&gt;100% CSS Compute Integer --width and --height of the Screen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/propjockey/pen/qBQjYxd" rel="noopener noreferrer"&gt;100% CSS Image-Mouse-Coordinate Zoom on Hover&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/propjockey/pen/NWEYdjY" rel="noopener noreferrer"&gt;100% CSS Conway's Game of Life Simulator - Infinite Generations, 42x42&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;100% CSS Breakout, play it here:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/preview/dywNyBQ?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  The End!
&lt;/h2&gt;

&lt;p&gt;If you think this is useful, fun, or interesting, it's the kind of thing I do in my free time! So please do consider following me here, &lt;a href="https://codepen.io/propjockey" rel="noopener noreferrer"&gt;on CodePen&lt;/a&gt;, and &lt;a href="https://twitter.com/Jane0ri" rel="noopener noreferrer"&gt;on X&lt;/a&gt; as well!&lt;/p&gt;

&lt;p&gt;👽💜&lt;br&gt;
// Jane Ori&lt;/p&gt;




&lt;p&gt;PS: I've been laid off recently and am looking for a job!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linkedin.com/in/JaneOri" rel="noopener noreferrer"&gt;https://linkedin.com/in/JaneOri&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over 13 years of full stack (mostly JS) engineering work and consulting, ready for the right opportunity!&lt;/p&gt;

</description>
      <category>css</category>
      <category>howto</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CSS Light/Dark Mode Implementation WITHOUT Duplicating Vars</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Wed, 06 Sep 2023 18:14:52 +0000</pubDate>
      <link>https://forem.com/janeori/css-lightdark-mode-implementation-without-duplicating-vars-22c9</link>
      <guid>https://forem.com/janeori/css-lightdark-mode-implementation-without-duplicating-vars-22c9</guid>
      <description>&lt;p&gt;A common perspective on the challenges behind implementing light mode and dark mode toggles in CSS is that it seemingly requires duplicating your --var declarations in order to set up the classes.&lt;/p&gt;

&lt;p&gt;It doesn't - and it's an easy pattern to implement!&lt;/p&gt;




&lt;h2&gt;
  
  
  A brief history
&lt;/h2&gt;

&lt;p&gt;As an example, &lt;a href="https://www.bram.us/2022/05/25/dark-mode-toggles-should-be-a-browser-feature/#code-duplication-as-a-red-flag" rel="noopener noreferrer"&gt;this article from Bramus&lt;/a&gt; in May 2022 states the common perspective outright:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8ii2oprwdmzvr8dghn5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8ii2oprwdmzvr8dghn5.png" alt="Screenshot of an excerpt from the article linked above showing a warning from the author that duplication is necessary, plus the CSS code to demonstrate it" width="800" height="688"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Though it was broadly accepted as truth back when that article was written, there were ways around duplicating&lt;sup&gt;[1]&lt;/sup&gt; the actual theme color value, which is the bulk of the concern. But... those approaches also left room for improvement.&lt;/p&gt;

&lt;p&gt;That improvement was found, and worked in all browsers&lt;sup&gt;[2]&lt;/sup&gt; - except Safari - &lt;a href="https://twitter.com/Jane0ri/status/1640570291087720448" rel="noopener noreferrer"&gt;until Safari 16.4, around March 2023&lt;/a&gt; when it began working everywhere!&lt;/p&gt;

&lt;p&gt;PS: &lt;a href="https://twitter.com/bramus" rel="noopener noreferrer"&gt;Bramus is great, follow him!&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Desired API
&lt;/h2&gt;

&lt;p&gt;First, the biggest ask from the community is that we can do this all from an actual media preference, without a checkbox hack or making JS toggle a class on load to flip the state, and &lt;a href="https://twitter.com/intenttoship/status/1699185089718984930" rel="noopener noreferrer"&gt;that work is underway now&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1699185089718984930-250" src="https://platform.twitter.com/embed/Tweet.html?id=1699185089718984930"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1699185089718984930-250');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1699185089718984930&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;In the meantime, and even after that's implemented, it's also really nice to have the flexibility of classes that manage it in addition to the preference.&lt;/p&gt;

&lt;p&gt;Here is the api:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dark by default&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@media (prefers-color-scheme: light)&lt;/code&gt; light on root and descendants by preference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.theme-preferred&lt;/code&gt; render this node (&amp;amp; desc.) based on the default root preference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.theme-not-preferred&lt;/code&gt; render this node (&amp;amp; desc.) as the opposite of the default root preference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.theme-dark&lt;/code&gt; render this node (&amp;amp; desc.) in dark&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.theme-light&lt;/code&gt; render this node (&amp;amp; desc.) in light&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, additionally, mix and match that api. Any section or element, no matter the parents or preference, you can toggle by flipping preference or by managing light/dark directly.&lt;/p&gt;

&lt;p&gt;For example, this would render exactly as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;render dark&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;render light&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as would this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-preferred"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  preferred theme here even if body or html changed it
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;light&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;dark&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-not-preferred"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    render the opposite of the preference
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-preferred"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;render the preference&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;light&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;dark&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The DRY Implementation
&lt;/h2&gt;

&lt;p&gt;If you are unfamiliar with Space Toggles, this setup can be copy pasted without any technical knowledge; you don't have to use them after initial setup.&lt;/p&gt;

&lt;p&gt;Just following the pattern will enable you to take full advantage of it!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you ARE interested, &lt;a href="https://github.com/propjockey/css-sweeper#css-is-a-programming-language-thanks-to-the-space-toggle-trick" rel="noopener noreferrer"&gt;you can read about space toggles here from when I first invented the idea&lt;/a&gt; or google it for the many articles and talks that have come out about them since then!&lt;/p&gt;

&lt;p&gt;In short, you're adding a light switch to other values. If the switch is a space (on), the value next to it is used, if it was &lt;code&gt;initial&lt;/code&gt; (off) then the value isn't used and the fallback takes its place when it's referenced.&lt;/p&gt;

&lt;p&gt;(And yep, it is spec!)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Boilerplate
&lt;/h3&gt;

&lt;p&gt;First, copy paste this exactly as it is. No matter how many variables you need to toggle between themes, this engine never changes and does all the work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.theme-not-preferred&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.theme-light&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.theme-preferred&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.theme-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.theme-dark&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.theme-not-preferred&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="nc"&gt;.theme-preferred&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.theme-not-preferred&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up a &lt;code&gt;--media-prefers-light&lt;/code&gt; space toggle that flips in accordance with our desired API described previously. (Media settings and space toggles were made for each other; check out &lt;a href="https://github.com/propjockey/css-media-vars#example-of-what-your-mobile-first-css-would-look-like" rel="noopener noreferrer"&gt;css-media-vars&lt;/a&gt; for a whole collection of them and the huge improvements they offer!)&lt;/p&gt;

&lt;h3&gt;
  
  
  Your theme values
&lt;/h3&gt;

&lt;p&gt;Next, we'll set up just 3 theme-dependent variables to establish the pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.theme-preferred&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.theme-not-preferred&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.theme-light&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.theme-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* const maybe_light_val = lightTog &amp;amp;&amp;amp; YourLightValue: */&lt;/span&gt;
  &lt;span class="py"&gt;--theme_0_light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--theme_1_light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--theme_2_light&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;hotpink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--theme_scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--media-prefers-light&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c"&gt;/* const theme-var = maybe_light_val || YourDarkValue: */&lt;/span&gt;
  &lt;span class="py"&gt;--theme-0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--theme_0_light&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--theme-1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--theme_1_light&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0%&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--theme-2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--theme_2_light&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rebeccapurple&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--theme_shceme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You won't use the light vars like &lt;code&gt;--theme_1_light&lt;/code&gt; directly anywhere else, they're only meant as internal vars (hence underscore naming convention) for setting up the vars you actually want to use everywhere, &lt;code&gt;--theme-1&lt;/code&gt; etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;p&gt;Finally, all you have to do is use the theme variables wherever you want in your CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.my-info-box-component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--theme-0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--theme-1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--theme-2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it works in your HTML in all the api scenarios as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-info-box-component"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  My border is:&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
  - `hotpink` with light mode preference&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
  - `rebeccapurple` with dark mode preference.
&lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-info-box-component theme-not-preferred"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  My border is:&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
  - `rebeccapurple` with light mode preference&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
  - `hotpink` with dark mode preference.
&lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-not-preferred"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-info-box-component"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    My border is:&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    - `rebeccapurple` with light mode preference&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    - `hotpink` with dark mode preference.
  &lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-info-box-component theme-preferred"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    My border is:&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    - `hotpink` with light mode preference&lt;span class="nt"&gt;&amp;lt;br&amp;gt;&lt;/span&gt;
    - `rebeccapurple` with dark mode preference.
  &lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and of course these too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-info-box-component theme-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  My border is `rebeccapurple`
&lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-dark"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
      ...
      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
        ...
        &lt;span class="nt"&gt;&amp;lt;aside&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-info-box-component"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          My border is `rebeccapurple`
        &lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
        ...
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Scenario Tests / Demo CodePen
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/RwVNybQ?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  The End!
&lt;/h2&gt;

&lt;p&gt;If you think this is useful, it's the kind of thing I do for fun all the time! So please do consider following me here &lt;a href="https://twitter.com/Jane0ri" rel="noopener noreferrer"&gt;and on X&lt;/a&gt; as well!&lt;/p&gt;

&lt;p&gt;👽💜&lt;br&gt;
// Jane Ori&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: I've been laid off recently and am looking for a job!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linkedin.com/in/JaneOri" rel="noopener noreferrer"&gt;https://linkedin.com/in/JaneOri&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over 13 years of full stack (mostly JS) engineering work and consulting, ready for the right opportunity!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;[1] "ways around duplicating" - You did have to duplicate some variable work if you want a &lt;a href="https://github.com/propjockey/css-sweeper#css-is-a-programming-language-thanks-to-the-space-toggle-trick" rel="noopener noreferrer"&gt;Space Toggle&lt;/a&gt; powered API like the one demonstrated in this article, but the primary concern could be avoided; no duplication of the actual theme color values is necessary. &lt;a href="https://codepen.io/propjockey/pen/MWmYQvr?editors=1100" rel="noopener noreferrer"&gt;DRY Theme color values, version that works in all browsers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] improved version that "worked in all browsers except Safari" - The method described in this article worked in FireFox and Chrome for many years BUT because Safari technically had it right according to the CSS spec, FF &amp;amp; Chrome "fixed it" in coordination with each other (so all 3 major browsers were in harmony), then in response &lt;a href="https://github.com/w3c/csswg-drafts/issues/5370" rel="noopener noreferrer"&gt;I filed an issue&lt;/a&gt; to change the spec, successfully got all parties to agree (and break harmony again), the CSS Spec changed to describe the previous behavior as the new spec, FF and Chrome reverted to the previous behavior in step, aaand Safari updated later, resulting in a new, improved, harmony!&lt;/p&gt;

</description>
      <category>howto</category>
      <category>css</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The New Tailwind Plugin You'll Want On Every Responsive Project</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Mon, 24 Apr 2023 03:49:47 +0000</pubDate>
      <link>https://forem.com/janeori/the-new-tailwind-plugin-youll-want-on-every-responsive-project-29a6</link>
      <guid>https://forem.com/janeori/the-new-tailwind-plugin-youll-want-on-every-responsive-project-29a6</guid>
      <description>&lt;p&gt;Web designs are often polished &amp;amp; delivered, targeting just your largest mobile, tablet, and desktop view widths.&lt;/p&gt;

&lt;p&gt;In the gap between design handoff and dev implementation, there's a lot of squishing that has to happen to the desktop design until it breaks into the smaller tablet layout.&lt;/p&gt;

&lt;p&gt;Headers wrap, layout stretches vertically, designers weep, developers can't reasonably do much to help.&lt;/p&gt;

&lt;p&gt;Until now...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0z4zscvixlvw3e421ipj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0z4zscvixlvw3e421ipj.gif" alt="fluid-text-plugin demonstration, a screen resizes and the layout changes at specific breakpoints but the headers smoothly transition to a perfect size every step of the way" width="600" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fluid-text-plugin&lt;/code&gt; was published earlier today on &lt;a href="https://github.com/bitovi/fluid-text" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/fluid-text-plugin?v0.0.2" rel="noopener noreferrer"&gt;NPM&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just one Tailwind* utility class and you can specify fluid font sizes that maintain well designed headers no matter what size the screen is at. The &lt;code&gt;fluid-text-plugin&lt;/code&gt; even uses your custom breakpoints automatically with zero additional setup.&lt;/p&gt;

&lt;p&gt;* also works with UnoCSS and as a standalone utility function when no build system is available.&lt;/p&gt;




&lt;p&gt;I suspect this one will get a lot of traction as it makes a well known issue, encountered on almost every project, near-trivial to fix.&lt;/p&gt;

&lt;p&gt;Not bad for a little weekend project 🎉&lt;br&gt;
Hope it helps you!&lt;/p&gt;

&lt;p&gt;Will you be using it? 💜&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tailwindcss</category>
      <category>css</category>
      <category>design</category>
    </item>
    <item>
      <title>CSS-Only Type Grinding: Casting Tokens (sm|md|etc) into Useful CSS Values</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Mon, 03 Oct 2022 21:19:29 +0000</pubDate>
      <link>https://forem.com/janeori/css-only-type-grinding-casting-tokens-smmdetc-into-useful-css-values-28e0</link>
      <guid>https://forem.com/janeori/css-only-type-grinding-casting-tokens-smmdetc-into-useful-css-values-28e0</guid>
      <description>&lt;p&gt;Ready for brand new CSS magic?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqgfe4oj3fqube2vqrtq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqgfe4oj3fqube2vqrtq.gif" alt="Gif of a person wiggling their fingers, looking right at the camera, and saying the word " width="218" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're writing a sharable component or library and want to provide your dev users an API that has CSS options like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--size: sm;&lt;/code&gt; | &lt;code&gt;--size: md;&lt;/code&gt; | &lt;code&gt;--size: lg;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;it's actually possible to do in CSS, without involving JS, a build system, or Style/Container Queries thanks to a new technique I've named "Type Grinding."&lt;/p&gt;

&lt;p&gt;Type Grinding allows your design tokens (keywords, or “&lt;a href="https://www.w3.org/TR/css-values-4/#custom-idents" rel="noopener noreferrer"&gt;&amp;lt;custom-ident&amp;gt;&lt;/a&gt;” values) written in your CSS to be transformed into any other values - like width, padding, color, etc - without relying on anything outside of CSS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6uiu25s1zos33c0rt2x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6uiu25s1zos33c0rt2x.gif" alt="gif of css token values changing other properties without js" width="1580" height="799"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current global user support is 72.77% &lt;a href="https://caniuse.com/mdn-css_at-rules_property_syntax" rel="noopener noreferrer"&gt;according to caniuse&lt;/a&gt; - specifically chromium based browsers.&lt;/p&gt;




&lt;h2&gt;
  
  
  How does it work?!
&lt;/h2&gt;

&lt;p&gt;The company I work for, Bitovi, is super supportive and allowed me to drop what I was working on to write an article about this instead! So over &lt;a href="https://www.bitovi.com/blog/css-only-type-grinding-casting-tokens-into-useful-values" rel="noopener noreferrer"&gt;on the company blog&lt;/a&gt;, I'll be destructuring the CSS involved here to teach how it works, bring awareness to what's possible, and hopefully empower you to do it yourself!&lt;/p&gt;

&lt;p&gt;Please take a look - I would LOVE to see what you do with it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bitovi.com/blog/css-only-type-grinding-casting-tokens-into-useful-values" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5dzo34kv8r89qa03ru9.png" alt="Screenshot of the Bitovi Article's Header" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to answer questions below, &lt;a href="https://twitter.com/Jane0ri" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt;, or in the comments at the full blog post!&lt;/p&gt;

&lt;p&gt;Thank you! 👽💜&lt;/p&gt;

</description>
      <category>css</category>
      <category>programming</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Share your ideas that never made it to production</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Sun, 17 Jul 2022 19:57:41 +0000</pubDate>
      <link>https://forem.com/janeori/share-your-ideas-that-never-made-it-to-production-1ckl</link>
      <guid>https://forem.com/janeori/share-your-ideas-that-never-made-it-to-production-1ckl</guid>
      <description>&lt;p&gt;Got any ideas buried in your git history that never made it to production?&lt;/p&gt;

&lt;p&gt;Give it a chance in the spotlight; Throw it in a codepen or just talk about it in the comments below!&lt;/p&gt;




&lt;p&gt;Here's my contribution:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prior to the launch of augmented-ui v1.0.0 in 2019, early builds of the website's introductory animation also featured this 3D CSS animation behind it. The background animation was removed for performance reasons but still exists in the commit history. 💜&lt;/p&gt;

&lt;p&gt;Hopefully &lt;a href="https://codepen.io/propjockey/full/VwXPGMP" rel="noopener noreferrer"&gt;this codepen&lt;/a&gt; helps give life to the idea that couldn't make it to production ~&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/propjockey/full/VwXPGMP" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6vd5lp7h0ldcng4a6v7f.gif" alt="Gif Capture of pre v1 augmented-ui intro animation featuring a 3D CSS animation in the background" width="560" height="275"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;// &lt;a href="https://twitter.com/jane0ri" rel="noopener noreferrer"&gt;Jane Ori&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>javascript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>The CSS SMEAR hack</title>
      <dc:creator>Jane Ori</dc:creator>
      <pubDate>Tue, 12 Jul 2022 12:57:26 +0000</pubDate>
      <link>https://forem.com/janeori/the-css-smear-hack-4fd1</link>
      <guid>https://forem.com/janeori/the-css-smear-hack-4fd1</guid>
      <description>&lt;p&gt;The SMEAR technique enables infinite generations of simulations, games, or other CSS experiments to happen without any JS and without revealing the sauce to the User.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeqhh697cjefcmexfvqi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeqhh697cjefcmexfvqi.gif" alt="gif of SMEAR being used" width="1022" height="1052"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SMEAR stands for Shrouded Markup Editing And Regeneration - a description of what happens to advance the application state to the next generation without JS. The smearing gesture itself has to be more deliberate and slower than a swipe, and is where the name SMEAR actually comes from. Forcing SMEAR into an acronym happened after the fact as a terrible joke.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/7QrBqCifZKs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The SMEAR hack is somewhat involved in the details, so to navigate that complexity, let's walk backwards from the checkers demo and reveal the work one layer at a time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3dfnqginskz34qs80rr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3dfnqginskz34qs80rr.png" alt="Image of an onion being peeled" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;!But first! Go hands-on with the SMEAR if you'd like (Chrome and Safari - desktop only) by selecting a front-row checker, then select a direction to move it in, then SMEAR to confirm:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/JjLKqGa?height=600&amp;amp;default-tab=result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Shrouded? What is the SMEAR hack doing?
&lt;/h2&gt;

&lt;p&gt;The big reveal is right here in our first layer:&lt;/p&gt;

&lt;p&gt;SMEAR is a drag-drop.&lt;/p&gt;

&lt;p&gt;Code (CSS in this case) is on the right, and the user drags it into a contenteditable element (Style tag display block, contenteditable plaintext-only in this case) on the left.&lt;/p&gt;

&lt;p&gt;We make the CSS and the style tag invisible to the user and put a fake call-to-action pseudo element in place for the user to seemingly-but-not-really interact with.&lt;/p&gt;

&lt;p&gt;The last key at this layer is that the element with our code (shrouded markup) has &lt;code&gt;user-select: all;&lt;/code&gt; so any interaction selects all of the code automatically and the drag action begins immediately.&lt;/p&gt;

&lt;p&gt;Here's an unshrouded bare bones demo of what's happening:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/a6ed72d9d120789a8db8b625c25a5761?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  user-select: almost-all; How to change the code each generation
&lt;/h2&gt;

&lt;p&gt;In order for the patch to be regenerated from our current state, we have to include markup that can programmatically represent any possible patch.&lt;/p&gt;

&lt;p&gt;If we could somehow allow users to select pseudo element content, it would be much easier to generate the next generation's state from CSS. As it is now though, we have to omit the elements that don't apply to the next generation from a superset.&lt;/p&gt;

&lt;p&gt;There are a few ways to omit text from the &lt;code&gt;user-select: all;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;user-select: none;&lt;/code&gt; is surprisingly not one of them because it still copies the seemingly unselected text. Probably a bug.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;display: none;&lt;/code&gt; works but if your patch state is derived from an animation, you cannot set &lt;code&gt;display&lt;/code&gt; to &lt;code&gt;none&lt;/code&gt;. (Spoiler: we need to change our generated code from an animation hack)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;visibility: hidden;&lt;/code&gt; works and can be set from an animation. Yay!&lt;/p&gt;

&lt;p&gt;The state of our patched code will come down to math - like showing the win screen when the opposite player's total sum/count of pieces on the board is 0. So we need to cast an integer from a &lt;code&gt;calc()&lt;/code&gt; to &lt;code&gt;visibility: hidden;&lt;/code&gt;, which requires an animation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@keyframes markupToggler { from { visibility: hidden; } }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;code&gt;animation: markupToggler 1ms linear both var(--condition, 0);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;--condition&lt;/code&gt; is 0, animation applies the &lt;code&gt;from&lt;/code&gt; state, if &lt;code&gt;--condition&lt;/code&gt; is 1, the animation stops applying the &lt;code&gt;from&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in action: note the content of our CSS patch regenerates every time we SMEAR and shows the opposite state.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/d384e548657fe8239584baf48c26d9c2?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This animation can be reused on any element, just set the &lt;code&gt;--condition&lt;/code&gt; variable to a calc (or min/max/clamp/etc) that becomes 0 or 1 and you're good to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forcing new code to the bottom of the style element
&lt;/h3&gt;

&lt;p&gt;There are a number of ways to prevent the user from dragging code into the middle of existing code (something which you may have done while interacting with the previous demo?) but the most reliable I've used (which could be trimmed down or done differently) is just adding this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="nc"&gt;.shrouded-markup&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-999999vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;style&lt;/span&gt;&lt;span class="nc"&gt;.shrouded-markup&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"\A\A\A\A"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Shrouding Reality
&lt;/h2&gt;

&lt;p&gt;The only important gotcha here is that you'll want your fake call-to-action element to be on the ::before pseudo specifically, not ::after.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/propjockey/embed/06da280a735dc6846e21cb884fbe9ab0?height=600&amp;amp;default-tab=css,result&amp;amp;theme-id=40148&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Maybe a follow up article if there's interest!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to &lt;code&gt;user-select: all;&lt;/code&gt; on contenteditable elements (you can!)
and&lt;/li&gt;
&lt;li&gt;using a textarea and reset button (JS not required) to empty cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The End!
&lt;/h2&gt;

&lt;p&gt;If you think this idea/trick is neat, it's the kind of thing I do all the time! So please do consider following me here and &lt;a href="https://twitter.com/Jane0ri" rel="noopener noreferrer"&gt;on twitter&lt;/a&gt; as well!&lt;/p&gt;

&lt;p&gt;💜&lt;br&gt;
// Jane Ori&lt;/p&gt;

</description>
      <category>css</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
