/eszett

Primary LanguageRust

ß – eszett

Explicit css scopes for react – by using a unique class name per component generated at build time.

TODO

  • hash relative filenames (maybe add an option to keep relative filenames – but as of now the filenames leak way too much information)
  • auto rewrite classNames (I liked the simplicity of just adding the helpers – but the ergonomics of tagging every class are not so nice, especially html tags without classes are a bit strange to handle)
import sz, { scopeName } from "eszett";

function Header() {
  return (
    <header className={sz`header`}>
      <h2 className={sz`title`}>Hello World</h2>
      <p>Subtitle</p>

      <style href={scopeName}>{`
        .${scopeName} {
          &.header {
            background: blue;
          }

          &.title {
            color: white;
          }

          &.header p {
            color: grey;
          }
        }
      `}</style>
    </header>
  );
}

Install

npm install eszett

eszett is an swc plugin – so it should work wherever swc works.

With NextJs

In nextjs you can add it to your next.config.js:

// next.config.js
module.exports = {
  
  experimental: {
    swcPlugins: [["eszett/swc", {}]]
  }
}

Usage

How it Works

eszett generates a unique id for each react component and gives you two helper methods to use it:

sz tagged template literal

// this input
import sz from "eszett";
<div className={sz`header`} />;

// will be tranformed to:
<div className={"23u00ds-1 " + `header`} />;

scopeName variable

// this input
import { scopeName } from "eszett";
console.log(scopeName);

// will be transfomed to to:
console.log("23u00ds-1");

The scopeName is generated by hashing the file path of the component and incrementing a counter for each top level function in each file

Together with support for <style> tags in react 19 and css nesting the two helpers is all we need to encapsulate our styles inside our components.

Without modern css

If you need to suport older Browsers you could use something like postcss-preset-env or you can just write classic css:

import sz, { scopeName } from "eszett";

function Header() {
  return (
    <header className={sz`header`}>
      <h2 className={sz`title`}>Hello World</h2>
      <p>Subtitle</p>

      <style href={scopeName}>{`
        ${scopeName}.header {
          background: blue;
        }

        ${scopeName}.title {
          color: white;
        }

        ${scopeName}.header p {
          color: grey;
        }
      `}</style>
    </header>
  );
}

Styling children

Since you explictly need to add the scope to all the classes, you can set global styles simply by omitting the sz helpers.

But if you want to pass the scoped classed down you can also do that:

import sz, { scopeName } from "eszett";

function PassClassNameToChildren() {
  return (
    <>
      <Link className={sz`link`} href="/home">
        Home
      </Link>
      <style href={scopeName}>{`
        ${scopeName}.link {
          color: red;
        }
      `}</style>
    </>
  );
}