https://github.com/yume-chan/yangshi
Object-style zero-runtime atomic CSS-in-JS library
https://github.com/yume-chan/yangshi
css css-in-js zero-runtime
Last synced: 23 days ago
JSON representation
Object-style zero-runtime atomic CSS-in-JS library
- Host: GitHub
- URL: https://github.com/yume-chan/yangshi
- Owner: yume-chan
- License: mit
- Created: 2026-04-12T14:59:14.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-04-12T17:14:34.000Z (about 1 month ago)
- Last Synced: 2026-04-12T19:06:35.429Z (about 1 month ago)
- Topics: css, css-in-js, zero-runtime
- Language: TypeScript
- Homepage:
- Size: 42 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# 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
```sh
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
```ts
import { css } from "@yume-chan/yangshi";
const className = css({
color: "red",
});
```
Output:
```js
const className = `a-a`;
```
```css
.a-a {
color: red;
}
```
### Pseudo classes/elements and parent selector
Use `&` (self selector) with nested objects:
```ts
import { css } from "@yume-chan/yangshi";
const className = css({
color: "red",
"&:hover": {
color: "blue",
},
".dark &": {
color: "cyan",
},
});
```
Output:
```js
const className = `a-a b-b c-c`;
```
```css
.a-a {
color: red;
}
.b-b:hover {
color: blue;
}
.dark .c-c {
color: cyan;
}
```
### Media queries and other at-rules
Just what you think:
```ts
import { css } from "@yume-chan/yangshi";
css({
color: "red",
"@media (prefers-color-scheme: dark)": {
color: "blue",
},
});
```
Output:
```js
const className = `a-a b-b`;
```
```css
.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:
```ts
import { css } from "@yume-chan/yangshi";
const className = css({
animationName: {
from: {
color: "blue",
},
to: {
color: "red",
},
},
animationIterationCount: "infinite",
animationDuration: "500ms",
});
```
Output:
```js
const className = `a-a b-b c-c`;
```
```css
.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:
```ts
import { atomic, css } from "@yume-chan/yangshi";
const className = css({
...atomic.border("1px", "solid", "red"),
});
```
Output:
```js
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`;
```
```css
.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:
```ts
import { css } from "@yume-chan/yangshi";
const PrimaryColor = "blue";
const className = css({ color: PrimaryColor });
```
Output:
```js
const className = `a-a`;
```
```css
.a-a {
color: blue;
}
```
However, all values must be constant at bundling time. For example, local function arguments are not allowed:
```tsx
function MyComponent(props: { color: string }) {
// NO!
return
;
}
```
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!
```ts
import { css, cx } from "@yume-chan/yangshi";
const isBlue = true;
const className = cx(
css({
color: "red",
}),
isBlue && css({ color: "blue" }),
);
```
Output:
```js
const className = cx(`a-a`, Math.random() > 0.5 && `a-b`);
```
```css
.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.