Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Yangshi

Another object-style zero-runtime atomic CSS-in-JS library based on wyw-in-js.

Features

  • CSS-in-JS: Write CSS code inside JS files.
  • Object Style: Write CSS properties as objects, if this is what you prefer.
  • Atomic CSS: Only longhand properties are supported. Merging styles is straightforward and intuitive.
  • Zero runtime (except for cx): Styles are extracted to CSS files at bundling time. No runtime overhead added.
    • Wide bundler support: Including Bun, esbuild, Next.js, Parcel, Rollup, Rspack, Svelte, Vite, Webpack...
    • Code evaluation: Constants, function calls, and even imports (but not local function arguments!) are allowed inside CSS objects. They get evaluated at bundling time.

Install

npm i @yume-chan/yangshi

Config

See https://wyw-in-js.dev/bundlers to config your bundler.

Note: because we are using Atomic CSS, you might want to generate a single CSS file (otherwise there will be many duplicate rules). For example for Vite, this can be done by setting build.cssCodeSplit to false.

Usage

Basics

import { css } from "@yume-chan/yangshi";

const className = css({
  color: "red",
});

Output:

const className = `a-a`;
.a-a {
  color: red;
}

Pseudo classes/elements and parent selector

Use & (self selector) with nested objects:

import { css } from "@yume-chan/yangshi";

const className = css({
  color: "red",

  "&:hover": {
    color: "blue",
  },

  ".dark &": {
    color: "cyan",
  },
});

Output:

const className = `a-a b-b c-c`;
.a-a {
  color: red;
}
.b-b:hover {
  color: blue;
}
.dark .c-c {
  color: cyan;
}

Media queries and other at-rules

Just what you think:

import { css } from "@yume-chan/yangshi";

css({
  color: "red",

  "@media (prefers-color-scheme: dark)": {
    color: "blue",
  },
});

Output:

const className = `a-a b-b`;
.a-a {
  color: red;
}
@media (prefers-color-scheme: dark) {
  .b-b.b-b {
    color: #00f;
  }
}

Note: at-rules use double class selector (.b-b.b-b) to increase its priority, so they always override normal rules no matter the ordering.

Animation

Put the animation keyframes in animationName property:

import { css } from "@yume-chan/yangshi";

const className = css({
  animationName: {
    from: {
      color: "blue",
    },
    to: {
      color: "red",
    },
  },
  animationIterationCount: "infinite",
  animationDuration: "500ms",
});

Output:

const className = `a-a b-b c-c`;
.a-a {
  animation-name: a;
}
.b-b {
  animation-iteration-count: infinite;
}
.c-c {
  animation-duration: 0.5s;
}
@keyframes a {
  0% {
    color: blue;
  }
  to {
    color: red;
  }
}

Shorthand properties

Only atomic properties are allowed in css.

Some shorthand helpers are provided in atomic object:

import { atomic, css } from "@yume-chan/yangshi";

const className = css({
  ...atomic.border("1px", "solid", "red"),
});

Output:

const className = `a-a b-b c-c d-a e-b f-c g-a h-b i-c j-a k-b l-c`;
.a-a {
  border-block-start-width: 1px;
}
.b-b {
  border-block-start-style: solid;
}
.c-c {
  border-block-start-color: red;
}
.d-a {
  border-inline-end-width: 1px;
}
.e-b {
  border-inline-end-style: solid;
}
.f-c {
  border-inline-end-color: red;
}
.g-a {
  border-block-end-width: 1px;
}
.h-b {
  border-block-end-style: solid;
}
.i-c {
  border-block-end-color: red;
}
.j-a {
  border-inline-start-width: 1px;
}
.k-b {
  border-inline-start-style: solid;
}
.l-c {
  border-inline-start-color: red;
}

Code evaluation

Like atomic functions, you can include constants, function calls, and even imports inside CSS objects:

import { css } from "@yume-chan/yangshi";

const PrimaryColor = "blue";

const className = css({ color: PrimaryColor });

Output:

const className = `a-a`;
.a-a {
  color: blue;
}

However, all values must be constant at bundling time. For example, local function arguments are not allowed:

function MyComponent(props: { color: string }) {
  // NO!
  return <div className={css({ color: props.color })} />;
}

You can only use style property, or CSS custom properties (variables) for that.

Merge class names

The cx function is a classNames-style function that merges class names at runtime.

cx ensures property values specified later will always override values before, no matter what order they appeared in the final CSS output. In another word, you must always use cx to merge class names generated by this library!

import { css, cx } from "@yume-chan/yangshi";

const isBlue = true;

const className = cx(
  css({
    color: "red",
  }),
  isBlue && css({ color: "blue" }),
);

Output:

const className = cx(`a-a`, Math.random() > 0.5 && `a-b`);
.a-a {
  color: red;
}
.a-b {
  color: blue;
}

Stable class names

No. It currently doesn't generate stable class names.

It chooses class names from alphabets as new properties/values appears in processing.

This might change in future.

About

Object-style zero-runtime atomic CSS-in-JS library

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages