An open API service indexing awesome lists of open source software.

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

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.