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

https://github.com/ymzuiku/cssin

Ease use css-in-js
https://github.com/ymzuiku/cssin

Last synced: 5 months ago
JSON representation

Ease use css-in-js

Awesome Lists containing this project

README

        

我们先看看我们最终的目标, 我们可以如何描述代码:

```js
import React from 'react';

export default () => 我是一个按钮;
```

在这个文件中,我们 "似乎没有引入任何库",就好像写内联样式一样,把样式描述、媒体查询、伪类都实现了, 并且可自定义样式名,如 bg、radius。

# cssin

cssin 是一个高度可定制的低级 CSS-In-JS 框架,它为您提供构建定制设计所需的所有构建模块,而无需任何令人讨厌的样式,你可以使用内联样式的所有语法,和其他扩展语法。

大多数 CSS 框架都做得太多了。
它们带有各种预先设计的组件,如按钮,卡片和警报,这些组件可能会帮助您最初快速移动,但是当您的网站使用自定义设计脱颖而出时,会导致更多的痛苦。

cssin 与众不同。

cssin 提供了低级实用程序类,而不是固定的预先设计的组件,使您可以构建完全自定义的设计而无需离开 JS。

cssin 生成的每个相同的样式值可以被重复引用,而不是重新创建。

### 理念

我们在使用 cssin 之前做了非常多的尝试,css\less\scss, tailwindCSS, styled-components 和其他 css-in-js 方案。其中 tailwindCSS 是最符合生产需要的,我们从中学到许多东西和理念;可是这些样式方案对于作者来说并没能真正解决问题:

**简短高效的描述我的样式,并且不离开 js;当然也不放弃 css 的任何一个特性**

css-in 就是为了解决此类问题而存在

### 旨在定制

cssin 所有样式都是通过定制而得,cssin 允许您自定义它。这包括颜色,边框大小,字体粗细,间距实用程序,断点,阴影和任何 css 样式。

cssin 采用纯 Typescript 编写,并且无需对项目框架进行配置,这意味着您可以轻松获得真正编程语言的全部功能。

cssin 相当于在内敛样式上扩展了伪类和媒体查询,并且支持自定义属性名和设定组件。

cssin 不仅仅是一个 CSS-IN-JS 框架,它还是一个创建设计系统的引擎。

### 轻巧

- 仅有 2kb (gzip)
- 每条样式会被缓存, 以更高的性能进行样式处理
- 可以在任何框架中使用,如你喜欢的 React、Vue、Stencil

### 安装

```sh
$ npm i cssin --save
```

# 先看看展现形式

example: [navar.workos.top](http://navar.workos.top)

在没有进行任何配置之前,cssin 的语法和内敛样式是一致的

```js
import React from 'react';
import cssin from 'cssin';

// 设置一个全局的 css-value
document.body.style.setProperty('--button-color', '#fff');

export default () => {
return (


Button

);
};
```

看起来还不错,有点像内联样式,但是又有些许不同,似乎直接描述了伪类和媒体查询,而且代码不够精简。

好的,我们最后会通过简单的配置的让样式描述变成这样:

```js
import React from 'react';
import cssin from 'cssin';

export default () => {
return

Button
;
};
```

或者极限简洁:

```js
import React from 'react';
import cssin from 'cssin';

export default () => {
return

Button
;
};
```

更加极限极限简洁, 连 cssin 的包裹都省略掉:

```js
import React from 'react';

export default () => {
return

Button
;
};
```

我们会一步步来达到最后的步骤。

# 或许一段话就可以描述清楚 cssin

我们先回顾刚开始的代码块:

```js
export default () => {
return (


Button

);
};
```

上述代码有点像内联样式,但是又有一些不同,因为它可以实现伪类及更好的自定义,我们逐步分析:

- 和编写内联样式一样的编写 css 样式, 如: `background-color: #f66; padding: 4px;`
- 直接使用伪类, 伪类在属性名之前,使用`:`分割如: `hover:background-color=#f33`
- 可以直接描述媒体查询等功能, 媒体查询对象使用`@`开头, 如: `@md:border-radius=4px`

其他规则:

- 如果只有属性名,那么它将是一个组件, 如 `button;`
- 如果值是一个**单一**的 css 变量, 如 `color:--button-color`; 等效于 `color:var(--button-color);`
- 使用`!`表示`!important`, 如 `color: #f00!`; 等效于 `color: #f00 !important`
- 如果只有属性名,并且以 `.` 开头, 那么就是对原生 css 样式的引用, 如 `.button;`
- 如果包含 `{}`, 表示这是一个纯 css, 它会被插入至全局样式中, 如 `body { margin:0px; }`

以上就是 cssin 的所有规则

下面是完整属性的表达式:
@[媒体查询]:[伪类名]:[属性名]:[属性值];

下面这句完整的语法描述:

```js
// 当媒体查询大于 760px 时、鼠标移入时、描边等于 #f00;
cssin`@md:hover:border:1px solid #f00;`;
```

### 为什么不直接编写 style 内联样式?

1. style 样式无法完全描述 css 的功能,如媒体查询、伪类等等 style;
2. 样式无法自定义更简短的样式集、样式集的组合、嵌套;
3. 内联样式无法直接引用 className,这样我们通常需要编写 css 文件,设置 className 和 style;
4. 并且默认优先级比 css 高,css 和 内联样式混合使用需要注意优先级;

cssin 最后生成的还是 css 样式,所以不会有以上的问题

# 如果更喜欢编写 style 属性

有的朋友更喜欢编写 style 属性,但是 style 中的一个痛点是无法实现伪类或媒体查询。

cssin 足够轻量,我们也可以仅仅使用它的伪类或媒体查询特性,来配合 style 属性进行项目样式的编写.

不过我们要注意,style 中编写的属性权重默认高于 className 中的样式,所以需要添加 `!important`:

```js
import React from 'react';
import cssin from 'cssin';

export default () => {
return (


Button

);
};
```

由于这个模式很常见,所以在 cssin 中,它可以使用 `!` 直接表示 `!important`:

```js
...
export default () => {
return (


Button

);
};Z
```

# 订制自定义样式

和众多 css 框架一样,cssin 允许你自定义样式集,这样可以用更简短的声明来描述样式

cssin 有一个 addSheets 属性用来添加样式映射表

我们现在达成刚刚的约定,将:

`background-color:#f66; hover:background-color:#f33; padding:4px; color:--button-color; border:2px solid #f33; @md:border-radius:8px;`

变成:

`btn:#f33, 4px; hover:bg:#f33; @md:radius:8px;`

```js
import React from 'react';
import cssin, { addSheets } from 'cssin';

// 添加自定义样式集
addSheets({
bg: (v) => `{ background-color: ${v}; }`,
radius: (v) => `{ border-radius: ${v}; }`,
btn: (v) => {
const values = v.split(';');
return {
`{ background-color: ${values[0]}; padding:${values[1]}; color:var(--button-color); }`
}
},
});

// 使用自定义的样式
export default () => {
return

Button
;
};
```

由于使用 cssin , 我们不会需要有 css 代码,所以可以降低项目首屏的资源请求。

自定义样式除了可以简化开发,还可以减少 js 代码量,从而最终达到相对更少的打包资源。

# 订制媒体查询

cssin 默认配置了 4 个尺寸级别的媒体查询,和基于设备媒体查询,我们可以覆盖它或者创建新的规则

注意,我们约定,只有以 `@` 开头的才是媒体查询对象

```js
// 默认的媒体查询
addSheets({
'@sm': (v: string) => `@media (min-width: 640px) {${v}}`,
'@md': (v: string) => `@media (min-width: 768px) {${v}}`,
'@lg': (v: string) => `@media (min-width: 1024px) {${v}}`,
'@xl': (v: string) => `@media (min-width: 1280px) {${v}}`,
'@ios': (v: string) => `@media (min-width: ${device.isIos ? '0px' : '9999px'}) {${v}}`,
'@android': (v: string) => `@media (min-width: ${device.isAndroid ? '0px' : '9999px'}) {${v}}`,
'@native': (v: string) => `@media (min-width: ${device.isNative ? '0px' : '9999px'}) {${v}}`,
'@pc': (v: string) => `@media (min-width: ${device.isPc ? '0px' : '9999px'}) {${v}}`,
});
// 我们覆盖 @md 以及创建一个 @xxl
addSheets({
'@md': v => `@media (min-width: 800px) {${v}}`,
'@xxl': v => `@media (min-width: 1920px) {${v}}`,
});
```

使用媒体查询,以下例子是屏幕宽度大于 800px,button 宽度为 200px,并且在 native 端隐藏

```js
import React from 'react';
// 最终只需要包裹一个单词的声明
export default () => {
return

Button
;
};
```

# 订制组件

我们希望把刚刚的代码简写成更精巧的组件, 组件其实是一组样式集

设置自定义组件, 因为 sheets 是一个简单的对象表,请注意不要和其他自定义样式重名导致覆盖

它和自定义样式或媒体查询的区别是它的值是一个单纯的字符串:

```js
import React from 'react';
import cssin, { addSheets } from 'cssin';

addSheets({
// 区别于自定义样式,组件的值是一个字符串,它遵循 cssin 语法,可以调用其他组件和自定义样式
button: 'bgc:#f66; hover:bgc:#f22; padding:8px; color:--button-color;',
});

// 最终只需要包裹一个单词的声明
export default () => {
return

Button
;
};
```

注意,组件不可以和伪类或者媒体查询进行组合,因为组件内部就已经包含了伪类或媒体查询

# 覆盖 setAttribute

这里涉及一些魔法,请辩证的使用。

作者在编写代码的时候不希望每次都引用 cssin,这对作者来说太过繁琐了,如果你也有这种感觉,可以使用 cssin 的 `coverAttribute`

index.js

```js
import React from 'react';
import { coverAttribute } from 'cssin';

// 这里我们设置css-in属性,它会模拟 className={cssin`...`}
coverAttribute('css-in');

// 请确保 coverAttribute 在 ReactDOM.render 之前执行
ReactDOM.render(, document.getElementById('root'));
```

App.js

```js
import React from 'react';

// 最终只需要一个单词的声明,就像原生声明一样
export const App = () => {
return (


Button


);
};
```

inlint 可以和 className 一起使用,前提是 className 必须在 css-in 之前声明

```js
import React from 'react';

// 最终只需要一个单词的声明,就像原生声明一样
export const App = () => {
return (



Button


);
};
```

我们需要注意,覆盖 setAttribute 并不是非常正确的做法,因为它带来了其他开发人员的不友好,其他人员并不知道我们做了这些黑魔法的前提下,会非常困惑。

另外,请不要覆盖 className 等常用属性,这样会让其他组件库失效

# 使用 css 原生功能在 javascript 中

### 使用 `{}` 编写单纯的 css 片段

有时候,我们会需要编写单纯的 css 片段,我们约定若字符串中包含 `{}`

此时传入的字符串只会被当成单纯的 css 样式进行注入至 html 中

```js
import cssin from 'cssin';

cssin`
body {
margin: 0px;
background-color: #f5f5f5;
}

@media (min-width: 640px) {
.box {
background: #f00;
}
}
`;
```

### 使用 `.` 引用原生的 css

其他地方定义的原生的 css 可以和 cssin 混合使用,只需要在属性名前面增加 `.`:

```js
import React from 'react';
import cssin from 'cssin';

// 使用 .box 引用 css 样式
export default () => {
return

Button
;
};
```

# 使用预设自定义样式、组件、 css-values

cssin 提供了一整套预设的自定义样式集合及 css-value 集合,它精心设计、开箱即用,亦可以作为一个自定义样式集合的参照标本

默认情况下 cssin 并未配置它,如果我们需要可以如下配置:

```js
import 'cssin/commonSheets'; // 引入 sheets集合
import 'cssin/commonCSSValues'; // 引入 css-value 集合
```

commonSheets 中的内容:

| 自定义样式名 | 映射样式 | 使用 |
| ------------ | ------------------------------------------------- | --------------------------- |
| dis | display | dis: flex; |
| items | align-items | items: 20px; |
| justify | justify-content | justify: start; |
| self | align-self | self: center; |
| content | align-content | content: end; |
| z | z-index | z: 10; |
| p | padding | p: 5rem; |
| px | pading-left, padding-right | px: 5rem; |
| py | padding-top, padding-bottom | py: 5rem; |
| pl | padding-left | pl: 5rem; |
| pt | padding-top | pt: 5rem; |
| pr | padding-right | pr: 5rem; |
| pb | padding-bottom | pb: 5rem; |
| m | margin | m: 5rem; |
| mx | margin-left, margin-right | mx: 5rem; |
| my | margin-top, margin-bottom | my: 5rem; |
| ml | margin-left | ml: 5rem; |
| mt | margin-top | mt: 5rem; |
| mr | margin-right | mr: 5rem; |
| mb | margin-bottom | mb: 5rem; |
| w | width | w: 5rem; |
| w-min | min-width | w-min: 5rem; |
| w-max | max-width | w-max: 5rem; |
| w-min-max | min-width, max-width | w-min-max: 5rem; |
| h | height | h: 5rem; |
| h-min | min-height | h-min: 5rem; |
| h-max | max-height | h-max: 5rem; |
| h-min-max | min-height, max-height | h-min-max: 5rem; |
| b | border: \${v} solid; | b: 5rem; |
| bl | border-left: \${v} solid; | bl: 5rem; |
| bt | border-top: \${v} solid; | bt: 5rem; |
| br | border-right: \${v} solid; | br: 5rem; |
| bb | border-bottom: \${v} solid; | bb: 5rem; |
| bc | border-color | bc: #f00; |
| radius | border-radius | radius: 2rem; |
| font | font-size | font: 1.25rem; |
| bg | background | background: #f00; |
| bgc | background-color | bgc: #f00; |
| linear | transition: all \${v} linear; | linear: 0.3s; |
| ease | transition: all \${v} ease; | ease: 0.3s; |
| ease-in | transition: all \${v} ease-in; | ease-in: 0.3s; |
| ease-out | transition: all \${v} ease-out; | ease-out: 0.3s; |
| ease-in-out | transition: all \${v} ease-in-out; | ease-in-out: 0.3s; |
| move-x | transform: translateX(\${v}); | move-x: 50%; |
| move-y | transform: translateY(\${v}); | move-y: 50%; |
| move-z | transform: translateZ(\${v}); | move-z: 50%; |
| rotate | transform: rotate(\${v}deg); | rotate: 180; |
| scale | transform: scale(\${v}, \${v}); | scale: 0.7; |
| shadow | shadow: shadowFN(size, r, g, b, a); | shadow: lg, 0, 0, 255, 0.7; |
| 以下均为组件 | | 组件不需要设置值 |
| scroll | overflow:auto; -webkit-overflow-scrolling: touch; | scroll; |
| col | dis:flex; flex-direction:column; | col; |
| row | dis:flex; flex-direction:row; | row; |
| center | col; justify:center; items:center; | center; |
| fixed | position:fixed; | fixed; |
| static | position:static; | static; |
| absolute | position:absolute; | absolute; |
| relative | position:relative; | relative; |
| sticky | position:sticky; | sticky; |
| left | left:0px; | left; |
| top | top:0px; | top; |
| right | right:0px; | right; |
| bottom | bottom:0px; | bottom; |
| bold | font-weight: bold; | bold; |

commonCSSValues 设置了一些 css-value, 其中的颜色、尺寸分类、投影均取自于 tailwindCSS 的配置:

![](https://github.com/ymzuiku/cssin/blob/master/imgs/1.png)
![](https://github.com/ymzuiku/cssin/blob/master/imgs/2.png)
![](https://github.com/ymzuiku/cssin/blob/master/imgs/3.png)
![](https://github.com/ymzuiku/cssin/blob/master/imgs/4.png)

使用预设的示例:

```js
import React from 'react';
import cssin from 'cssin';

// 使用预设的自定义样式和 css-value 配合使用
export default () => {
return (


Button

);
};
```

我们可以查看这两个文件,它们只是使用 cssin API 的简单配置,也欢迎有朋友提供更好的自定义样式及组件:

[commonSheets.ts](https://github.com/ymzuiku/cssin/blob/master/lib/commonSheets.ts)

[commonCSSValues.ts](https://github.com/ymzuiku/cssin/blob/master/lib/commonCSSValues.ts)

如果我们要对其进行修改,可以下载该资源进行修改:

# 性能开销

cssin 虽然是运行时创建 css 样式,但是它有着极低的性能开销。

我们可以看到,创建重复执行 500 次,每次大约创建 20 条样式,只消耗了 `1.6ms`, 这是因为 cssin 会对整体属性做缓存,还会对子属性创建 css 样式做缓存:

```js
console.time(t);
for (let i = 0; i < 500; i++) {
cssin(
`transition:all 0.1s ease-in; box-shadow:--shadow-1lg; hover:box-shadow:--shadow-1md; active:box-shadow:--shadow-sm1;`
);
cssin(
`transition:all 0.2s ease-in; box-shadow:--shadow-2lg; hover:box-shadow:--shadow-2md; active:box-shadow:--shadow-sm2;`
);
cssin(
`transition:all 0.3s ease-in; box-shadow:--shadow-3lg; hover:box-shadow:--shadow-3md; active:box-shadow:--shadow-sm3;`
);
cssin(
`transition:all 0.4s ease-in; box-shadow:--shadow-4lg; hover:box-shadow:--shadow-4md; active:box-shadow:--shadow-sm4;`
);
}
console.timeEnd(t); // 1.60009765625ms
```

### 现在开始使用它:

```sh
$ npm i cssin --save
```

盼望 Star 或提出贡献,仓库地址:

[github.com/ymzuiku/cssin](https://github.com/ymzuiku/cssin)