https://k9l.io/badcss k9l.io/badcss

Hello 👋

My name is Keith Cirkel

And I am bad at CSS

👈

👆

👈

To be bad at CSS, you must first understand CSS.

You have to know where the bodies are buried.

You too, can be bad at CSS!


            div {

              width: var(--width, 10px);

            }
          

🚨 Parsing theory tangent! 🚨

Raw Text


            h2 { width: var(--width,10px) }
          

Tokenization

  1. h2
  2.  
  3. {
  4.  
  5. width
  6. :
  7.  
  8. var(
  9. --width
  10. ,
  11. 10px
  12. )
  13.  
  14. }

Tokenization

  1. h2
  2.  
  3. {
  4.  
  5. width
  6. :
  7.  
  8. var(
  9. --width
  10. ,
  11. 10px
  12. )
  13.  
  14. }

AST "Component Values"

  1. h2
  2.  
    1. {
    2. width
    3. :
    4.  
      1. var(
      2. --width
      3. ,
      4. 10px
      5. )
    5. }

HIR (Higher-Order Intermediate Representation)

    1. h2
    1. {
      1. width
      2. :
      3.  
        1. var(
        2. --width
        3. ,
        4. 10px
        5. )
    2. }

CSS Syntax 3

<ident-token>

-- - a-z A-Z _ or non-ASCII escape a-z A-Z 0-9 _ - or non-ASCII escape
  • 4px
  • px4
  • -4px
  • -four-px
  • -4-px
  • --4px
  • ⁃⁃foo
  • -⁸px
  • --⁂⁂⁂
  • -̶̡̰̤̰̣̫̗̻̦̘͔̺̪̰̙͕̋͗̈́͗ͅ-̷̢̫̜̻͚̩̭̫̔̋̎̑͜͠z̴̧͓̪̱͈̯̗̣̖̪̼̣̱͇͓͑̓̒̽ả̴̛͓̻̖̾̑̐͂̾̈́̋͂̒̐̆̈́̄̕l̸̨̧̢͇͍̦̱͉̖̮̫̳͕̖̖̝̽̒̇͒͗́͋͐͝͝g̴̳̻̼̳̭͉͔͖͖̜̼̻̳̹͍̈́̈́̅͗̈́͋̽̀̅̑̚͜͜o̸̯̭̖̫̦̭̬̲̞̳̗̗̫͔̍͗͛̒͊͋̒̄̓̓̇̈́̈̐̚̕
  • p x
  • \000070 \000078

CSS escaping rules means all of these parse the same

  • h3 { color: red }
  • \h3 { color: red }
  • \68 3 { color: red }
  • \68 3 { color: red }
  • \68 \33 { color: red }
  • \000068 \000033 { color: red }

<number-token>

+ - digit . digit digit . digit e E + - digit
  • four
  • 4
  • 4/2
  • -2.0
  • 2.8.3
  • 2e4
  • 2e4e5
  • +2.0000e+0002

CSS escaping rules means all of these parse the same

  • h2 { width: 200px }
  • h2 { width: 200.000000000px }
  • h2 { width: 2e2px }
  • h2 { width: +000000000000002e+00000000000002px }
  • h2 { width: +000000000000002e+00000000000002\000070 \000078 }
  • h2 { width: +000.00020e+000000006\000070 \000078 }

👐🏻 Demo Time! 👐🏻


            npx csskit@latest expand --escape-idents -c "h2{width:200px;}"
          

              h2{width:200px;}
            

...becomes...


              \000068 \000032 {
                \000077 \000069 \000064 \000074 \000068
                :+2.00000000000000e+0000000002\000070 \000078 ;
              }
            

Demo page:


              data:text/html,<h2 style="border:solid">Hello NN1</h2><style>\000068 \000032 { \000077 \000069 \000064 \000074 \000068 :+2.00000000000000e+0000000002\000070 \000078 ; }
            
❝But Keith that's stupid, I'm never going to use it❞
You, probably

            <label class="block">
              <span class="after:content-['*'] after:ml-0.5 after:text-red-500 block text-sm font-medium text-slate-700">
                Email
              </span>
              <input type="email" name="email" class="mt-1 px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 focus:outline-none focus:border-sky-500 focus:ring-sky-500 block w-full rounded-md sm:text-sm focus:ring-1" placeholder="you@example.com" />
            </label>
          

            .after\:content-\[\'\*\'\]::after {
              --tw-content:"*";
              content:var(--tw-content)
            }
            .after\:ml-0\.5::after {
              margin-left: .125rem;
            }
            .after\:text-red-500 {
              color: rgb(239 68 68 / var(--tw-text-opacity, 1));
            }
          

But, okay, let's ignore characters, focus on tokens

AST "Component Values"

  1. div
  2.  
    1. {
    2. width
    3. :
    4.  
      1. var(
      2. --width
      3. ,
      4. 10px
      5. )
    5. }
  1. --white
  2. :
      1. rgba(
      2. 255
      3.  
      4. 255
      5.  
      6. 255
      7.  
      8. /
      9. 1.0
      10. )
  1. color
  2. :
      1. var(
      2. --foo
      3. )
      1. rgba(
      2. 255
      3.  
      4. 255
      5.  
      6. 255
      7.  
      8. /
      9. 1.0
      10. )
  1. --rg
  2. :
    1. 255
    2. ,
    3. 255
  1. --b
  2. :
    1. ,
    2. 255
  1. color
  2. :
    1. rgb(
      1. var(
      2. --rg
      3. )
      1. var(
      2. --b
      3. )
    2. )
    1. rgb(
      1. 255
      2. ,
      3. 255
      4. ,
      5. 255
    2. )

CSS "Booleans"


            :root {
              --condition: 0; /* or 1 */
            }

            .box {
              /* This would be 100px */
              width: calc( 1 * 100px );

              /* This would be 0px */
              width: calc( 0 * 100px );

              /* This would be either 0px or 100px */
              width: calc( var(--condition, 0) * 100px );
            }
          

            if (myCondition) {
              el.style.setProperty('--condition', '1');
            } else {
              el.style.setProperty('--condition', '0');
            }
          

Boolean toggle in action


            .notification {
              --unread: 0;

              /* Badge only visible when --unread is > 0 */
              &::after {
                counter-reset: variable var(--unread);
                content: counter(variable);
                width: calc(min(var(--unread), 1) * 22px);
                height: calc(min(var(--unread), 1) * 22px);
                background: deepskyblue;
                border-radius: 100%;
                overflow: hidden;
              }
            }
          
--unread: 0
Inbox
--unread: 1
Inbox
--unread: 4
Inbox

Fallback Chaining


            .card {
              /* How it works */
              --fallbacks: var(--the-thing, the-fallback);

              /* Check multiple variables, use first valid one */
              background: var(--card-bg,
                          var(--surface-bg,
                          var(--default-bg, white)));

              /* Component → Theme → System → Hardcoded */
              color: var(--card-text,
                     var(--theme-text,
                     var(--system-text, black)));
            }
          

The "Space Toggle" Hack


            button {
              --OFF: ;        /* Just a space - valid token! */
              --ON: initial; /* "initial" = forces fallback */
              --is-raised: var(--OFF);
              border: 1px solid var(--is-raised, rgb(0 0 0 / .1));
              background: var(--is-raised,
                /*         ON value                :  OFF value */
                linear-gradient(hsl(0 0% 100% / .3), transparent));
              box-shadow: var(--is-raised,
                /*         ON value            :  OFF value     */
                0 1px hsl(0 0% 100% / .8) inset, 0 .1em .1em -.1em rgb(0 0 0 / .2));
            }
            button:hover { --is-raised: var(--ON); }
          
Button

... it's kind of useless


            button {
              border: 1px solid transparent;
              background: hsl(200 100% 50%);
              box-shadow: 0 .1em .1em -.1em rgb(0 0 0 / .2);
            }
            button:hover {
              border-color: rgb(0 0 0 / .1);
              background: linear-gradient(hsl(0 0% 100% / .3)) hsl(200 100% 50%);
              box-shadow: 0 1px hsl(0 0% 100% / .8) inset;
            }
          
Button

Now that we know variables are powerful...

Let's abuse them for color math!


            <label style="--r: 102; --g: 51; --b: 153">Purple!</label>
            <label style="--r: 218; --g: 165; --b: 32">Yellow!</label>
          

            label { background-color: rgb(var(--r) var(--g) var(--b)) }
          

            label {
              --color: rgb(var(--r) var(--g) var(--b));
              background-color: var(--color);
              color: contrast-color(var(--color));
            }
          

            label {
              /* Rec709 Luma coefficient */
              --luma: calc(
                (0.2126 * var(--r)) +
                (0.7152 * var(--g)) +
                (0.0722 * var(--b))
              );
              --threshold: 128;
              /* Convert threshold: either 0 (black) or 255 (white) */
              --x: calc(
                255 * (1 - clamp(0, calc((var(--luma) - var(--threshold)) / 1), 1))
              );
              background-color: rgb(var(--r) var(--g) var(--b));
              color: rgb(var(--x) var(--x) var(--x))
            }
          
❝But Keith that's stupid, I'm never going to use it❞
You, probably

Let's do more colo(u)r math(s)!

But this time with 100% less usefulness!


            /* Linearize sRGB to Linear-RGB */
            --rt: clamp(0, sign(calc(var(--rs) - 0.04045)), 1);
            --gt: clamp(0, sign(calc(var(--gs) - 0.04045)), 1);
            --bt: clamp(0, sign(calc(var(--bs) - 0.04045)), 1);
            --rl: calc(
              (1 - var(--rt)) * (var(--rs) / 12.92) +
              var(--rt) * pow(calc((var(--rs) + 0.055) / 1.055), 2.4)
            );
            --gl: calc(
              (1 - var(--gt)) * (var(--gs) / 12.92) +
              var(--gt) * pow(calc((var(--gs) + 0.055) / 1.055), 2.4)
            );
            --bl: calc(
              (1 - var(--bt)) * (var(--bs) / 12.92) +
              var(--bt) * pow(calc((var(--bs) + 0.055) / 1.055), 2.4)
            );
          

            /* Convert LinearRGB to a generic LMS (Long, Medium, Short) color space  */
            --l: calc(0.4122214708 * var(--rl) + 0.5363325363 * var(--gl) + 0.0514459929 * var(--bl));
            --m: calc(0.2119034982 * var(--rl) + 0.6806995451 * var(--gl) + 0.1073969566 * var(--bl));
            --s: calc(0.0883024619 * var(--rl) + 0.2817188376 * var(--gl) + 0.6299787005 * var(--bl));
            --lp: pow(var(--l), 0.3333333333333333);
            --mp: pow(var(--m), 0.3333333333333333);
            --sp: pow(var(--s), 0.3333333333333333);
          

            /* Convert LMS to Oklab (L, a, b) */
            --okl: calc(0.2104542553 * var(--lp) + 0.7936177850 * var(--mp) - 0.0040720468 * var(--sp));
            --oka: calc(1.9779984951 * var(--lp) - 2.4285922050 * var(--mp) + 0.4505937099 * var(--sp));
            --okb: calc(0.0259040371 * var(--lp) + 0.7827717662 * var(--mp) - 0.8086757660 * var(--sp));

            /* Oklab to Oklch: C = sqrt(a^2+b^2), h = atan2(b, a) */
            --okc: sqrt(calc(var(--oka) * var(--oka) + var(--okb) * var(--okb)));
            --okh: atan2(var(--okb), var(--oka));

            /* Final Oklch color declaration */
            --oklch: oklch(var(--okl) var(--okc) var(--okh));
          

            /* BOOLEAN: determine if the Luminance Value was light or not */
            --is-light: clamp(0, calc((var(--okl) - 0.5) * 1000), 1);
            --dir: calc(1 - 2 * var(--is-light));

            /* Calculate the alternate Lightness for Oklch */
            --okl-alt: clamp(0, calc(var(--okl) + var(--dir) * 0.55), 1);
            --oklch-alt: oklch(var(--okl-alt) var(--okc) var(--okh));
          

            /* Giant mound of CSS */
            label {
              background:var(--oklch);
              color:var(--oklch-alt)
            }
          
❝But Keith that's stupid, I'm never going to use it❞
You, probably

Thank you for listening

Questions welcome


Keith Cirkel

🦋 @keithamus.social

🐘 @keithamus.indieweb.social

🐙 github/keithamus

▶️ youtube/keithamus