homebound-team / truss

A TypeScript DSL for writing utility CSS in React/JSX
32 stars 0 forks source link

Generate matching utility class names #109

Open stephenh opened 1 year ago

stephenh commented 1 year ago

It seems neat if <div css={Css.black.df.mt2.mbPx(12).$}> ended up in the HTML as <div className="black df mt2 mbPx12"> vs. the current <div className="emotion-FooComponent-123">.

I.e. we would leverage the fact that Truss already has a unique set of methods/abbreviations, the TypeScript methods, and we could map those to CSS class names.

Obviously this is basically exactly like what Tailwinds DOM output looks like, but with Truss's ease of use "just whatever CSS-in-JS runtime" integration + custom Tachyons methods + etc.

Fela almost has a way to do this, with their monolithic plugin (which is meant to create Emotion-style "class name per set of unique rules" afaiu) which has a className "hint" for the desired class name:

https://github.com/robinweser/fela/tree/master/packages/fela-monolithic

This is basically what we want, sans monolithic output, plus it's unclear how we'd transfer the hints from Truss into Fela, i.e. today Css.black.mt2.$ becomes { color: "black", margin-top: "8px" } in an object literal, and it's not clear how we'd tell Fela the original black and mt2 names to use.

We might have to retrofit Css to ask for the class names as we go, i.e. Css.black.mt2.$ would have .black ask Fela (with a plugin) "give me a class name (named 'black') for { color: black }", then go on to .mt2 would again ask Fela "give me a class name (named 'mt2') for { ... }". Something like:

// returns "black" with the rule cached/planned for injection
const cn = renderer.renderRule({ color: black, className: black });

We would probably need to store this still as a hash within the Css instance, like { color: 'black' }, so that any ... object merges with other colors would overwrite the original color class name.

We could also very easily cache these, especially for static methods, i.e.:

const cache = {};

class Css {
  get black() {
    const cn = (cache[black] ??= renderer.renderRule({ color: black; className: black });
    return this.newCss({ color: cn });
  }
}

Which seems neat from a perf perspective.

We'd have to leave media/complex selectors alone, which is fine.

stephenh commented 1 year ago

Step one in the prototype would be a Fela plugin that could do this:

// returns "black" with the rule cached/planned for injection
const cn = renderer.renderRule({ color: black, className: black });