2021-03-05

A hack for missing @at-root for Tailwind CSS Plugin

tailwind, tailwindcss, hack, SASS

banner

Image by Peter H from Pixabay

Introduction

I was creating a Tailwind CSS ("TW") plugin, @downwindcss/debug. for a utility outline-{n} to apply outline.

The utility should apply to a whole page when a user adds the utility class anywhere in the DOM tree.
Basically @at-root in SASS.

Problems and Attempts

When you add a utility, you can use a CSS-in-JS syntax, to refer to the parent with & (ampersand).

Attempt #1

My attempt was to do was to apply to descendants of <body /> (line #5, "body *, &").

1const numberedOutlines = Object.entries(values)2  .filter(([key]) => key !== "DEFAULT")3  .map(([key, value]) => ({4    [`.${e(`outline-${key}`)}`]: {5      "body > *": {6        outline: `${value} solid red`,7      },8    },9  }));

But TW appended .outline to the output.

1.outline body > * {2  outline: 1px solid red;3}

That's not the selector I wanted. I wanted a simple body > * to apply the outline to the whole page!

Attempt #2

As & refers to the parent selector, .outline, I was adding & either to the left or to the right separated by ,.

1"body > *, &": {2    outline: `${value} solid red`,3},4
5// or6
7"&, body > *": {8    outline: `${value} solid red`,9},

But both of them still appeneded .outline in front of body like,

1.outline body > *, .outline2// or3.outline, .outline body > *;

Working Code

Ok, frustrated I was, I decided to hack it by excluding the current & and including it again (body * > :not(&), &).

1const numberedOutlines = Object.entries(values)2  .filter(([key]) => key !== "DEFAULT")3  .map(([key, value]) => ({4    [`.${e(`outline-${key}`)}`]: {5      "body * > :not(&), &": {6        outline: `${value} solid red`,7      },8    },9  }));

You can see that outline-5 was applied somewhere in the DOM tree and outline was applied to the whole page.
(disregard the fact that it's showing outline-4 cuz it's WIP 😅)

working!


Image by Peter H from Pixabay