指导
在本节中,我们将安装 EffCSS 并了解如何使用它。
安装
在终端中输入:
# npm
npm i effcss
# pnpm
pnpm add effcss
# yarn
yarn add effcss使用
要使用 EffCSS 创建样式,只需调用相应的工具即可。幸运的是,工具数量很少,而且从名称上就能看出它们的功能。
在所有实用工具中,你需要重点关注 classNames 和 attributes。它们需要以 TypeScript 类型的形式指定一个契约,选择器将根据该契约实现。这样你就可以控制样式的创建和使用。契约类型是一个可以包含任意层级嵌套属性的对象:
/**
* Components stylesheet
*/
type Components = {
/**
* Is rounded
*/
rounded: true;
/**
* Height
*/
h: 'full' | 'half';
/**
* Card
*/
card: {
/**
* Card background
*/
bg: 'primary' | 'secondary';
/**
* Is card disabled
*/
disabled: boolean;
};
/**
* Spinner component
*/
spinner: {};
};
/**
* Utils stylesheet
*/
type Utils = {
/**
* Width
*/
w: 's' | 'm' | 'l';
/**
* Spacing
*/
spacing: 0 | 1 | 2;
/**
* Blink animation
*/
blink: true;
};其他实用程序会根据其参数推导出类型。让我们仔细看看它们各自的用法。
classNames
className 会创建一个具有指定内容的 CSS 规则,并将类选择器作为字符串返回:
import { className } from 'effcss';
// create
const cls = className({
margin: 'auto',
'&:hover': {
outline: '2px solid black',
'.child': {
background: 'grey'
}
}
});
// apply
export const Component = () => {
return <div className={cls}>
Card
</div>
};classNames 创建一个样式表并返回一个用于派生类名的函数:
import { classNames } from 'effcss';
// declare
type Card = {
w: 's' | 'm' | 'l';
blur: true;
card: {
variant: 1 | 2;
rounded: true;
};
}
// implement
const card = classNames<Card>((selectors) => {
const {w, card, blur} = selectors;
return {
[w.s]: {
width: '12px'
},
[w.m]: {
width: '24px'
},
[w.l]: {
width: '26px'
},
[blur.true]: {
filter: 'blur(5px)'
},
[card]: {
background: 'white',
border: 'none'
},
[card.variant[1]]: {
width: 'auto',
display: 'block',
padding: '12px',
'&:hover': {
cursor: 'pointer'
}
},
[card.variant[2]]: {
width: 'auto',
display: 'flex',
flexDirection: 'column',
padding: '16px',
'&:hover': {
outline: '2px solid black'
}
},
[card.rounded.true]: {
borderRadius: '1rem'
}
}
});
const cls = card({
card: {
rounded: true
},
w: 's'
});
// apply
export const Component = () => {
return <div className={cls}>
Card
</div>
};lazyClassNames 与 classNames 的区别在于,它会在首次派生选择器之后执行传递的函数并创建样式表:
import { lazyClassNames } from 'effcss';
// declare
type Card = {/* the same */};
// implement
const card = lazyClassNames<Card>(/* the same */);
// the stylesheet has not been created yet
const cls = card({
card: {
rounded: true
},
w: 's'
});
// the stylesheet has been created
// apply
export const Component = () => {
return <div className={cls}>
Card
</div>
};attributes
attribute 会创建一个包含指定内容的 CSS 规则,并将属性选择器作为对象返回:
import { attribute } from 'effcss';
// create
const attr = attribute({
margin: 'auto',
'&:hover': {
outline: '2px solid black',
'.child': {
background: 'grey'
}
}
});
// apply
export const Component = () => {
return <div {...attr}>
Card
</div>
};attributes 创建一个样式表并返回一个用于派生属性的函数:
import { attributes } from 'effcss';
// declare
type Card = {
w: 's' | 'm' | 'l';
blur: true;
card: {
variant: 1 | 2;
rounded: true;
};
}
// implement
const card = attributes<Card>((selectors) => {
const {w, card, blur} = selectors;
return {
[w.s]: {
width: '12px'
},
[w.m]: {
width: '24px'
},
[w.l]: {
width: '26px'
},
[blur.true]: {
filter: 'blur(5px)'
},
[card]: {
background: 'white',
border: 'none'
},
[card.variant[1]]: {
width: 'auto',
display: 'block',
padding: '12px',
'&:hover': {
cursor: 'pointer'
}
},
[card.variant[2]]: {
width: 'auto',
display: 'flex',
flexDirection: 'column',
padding: '16px',
'&:hover': {
outline: '2px solid black'
}
},
[card.rounded.true]: {
borderRadius: '1rem'
}
}
});
const attrs = card({
card: {
rounded: true
},
w: 's'
});
// apply
export const Component = () => {
return <div {...attrs}>
Card
</div>
};lazyAttributes 与 attributes 的区别在于,它会在首次派生选择器之后执行传递的函数并创建样式表:
import { lazyAttributes } from 'effcss';
// declare
type Card = {/* the same */};
// implement
const card = lazyAttributes<Card>(/* the same */);
// the stylesheet has not been created yet
const attrs = card({
card: {
rounded: true
},
w: 's'
});
// the stylesheet has been created
// apply
export const Component = () => {
return <div {...attrs}>
Card
</div>
};customStyles
customStyles 创建的样式表不包含派生选择器:
import { customStyles } from 'effcss';
// implement
customStyles(() => ({
'.custom': {
background: 'transparent',
width: '100%',
'&:hover': {
outline: '2px solid black'
}
},
'@media screen and (max-width: 768px)': {
'.custom': {
width: '50%'
}
}
}));
// apply
export const Component = () => {
return <div className='custom'>
Card
</div>
};lazyCustomStyles 与 customStyles 的区别在于,它会在第一次调用结果后执行传入的函数并创建样式表:
import { lazyCustomStyles } from 'effcss';
// implement
const applyStyles = lazyCustomStyles(/* the same */);
// the stylesheet has not been created yet
applyStyles();
// the stylesheet has been created
// apply
export const Component = () => {
return <div className='custom'>
Card
</div>
};variables
variable 创建一个 CSS @property 规则,variables 一次创建多个规则:
import { customStyles, variable, variables } from 'effcss';
// global
const offset = variable('10px');
const colors = variable({
primary: {
syntax: 'color',
inherits: false,
initialValue: '#2192a7'
},
secondary: '#425158'
});
customStyles(() => {
// local
const localOffset = variable({
inherits: true,
initialValue: '12px'
});
const localColors = variables({
primary: '#2192a7',
secondary: '#425158'
});
return {
'.global': {
background: colors.primary(),
// with fallback value
padding: offset('8px'),
},
'.local': {
// with fallback value
background: localColors.primary('grey'),
padding: localOffset()
},
'.override': {
[localColors.primary]: 'grey'
}
};
});您可以使用相应的方法获取和设置全局变量的初始值:
const offset = variable('10px');
const colors = variable({
primary: {
syntax: 'color',
inherits: false,
initialValue: '#2192a7'
},
secondary: '#425158'
});
offset.set('14px');
colors.primary.set('grey');
colors.secondary.set('green');
const actualOffsetValue = offset.get();
const actualPrimaryColorValue = colors.primary.get();animations
animation 创建一个 CSS @keyframes 规则,animations 一次创建多个规则:
import { customStyles, animation, animations } from 'effcss';
// global
const spin = animation({
from: {
transform: 'rotate(0deg)',
},
to: {
transform: 'rotate(360deg)',
},
});
const blink = animations({
simple: {
'50%': {
visibility: 'hidden'
}
},
smooth: {
'0%': {
opacity: 1
},
'50%': {
opacity: 0
},
'100%': {
opacity: 1
}
}
});
customStyles(() => {
// local
const localSpin = animation(/* the same */);
const localBlink = animations(/* the same */);
return {
'.global-spin': {
animation: `${spin} 6s infinite`
},
'.global-blink': {
animation: `${blink.smooth} 2s infinite`
},
'.local-spin': {
animation: `${localSpin} 6s infinite`
},
'.local-blink': {
animation: `${localBlink.smooth} 2s infinite`
},
};
});layers
layer 创建一个 CSS @layer 规则,layers 一次创建多个规则:
import { customStyles, layer, layers } from 'effcss';
// global
const single = layer();
const list = layers(['theme', 'layout', 'utilities']);
customStyles(() => {
// local
const localSingle = layer();
const localList = layers(['theme', 'layout', 'utilities']);
return {
[single]: {
'.global-layer': {
background: 'transparent'
}
},
[list.theme]: {
'.global-layer': {
background: '#425158'
}
},
[localSingle]: {
'.local-layer': {
background: 'white'
}
},
[localList.theme]: {
'.local-layer': {
background: 'grey'
}
}
};
});containers
container 创建一个 CSS @container 规则,containers 一次创建多个规则:
import { customStyles, container, containers } from 'effcss';
// global
const single = container();
const multiple = containers({
normal: '',
inline: 'inline-size',
scrollState: 'size scroll-state'
});
customStyles(() => {
// local
const localSingle = container();
const localMultiple = containers({
normal: '',
inline: 'inline-size',
scrollState: 'size scroll-state'
});
return {
'.global-container': {
container: single()
},
[single + ' not scroll-state(scrollable: none)']: {
'.inside-global-container': {
width: '100%'
}
},
'.local-container': {
container: localMultiple.inline()
},
[localMultiple.inline + ' (max-width: 768px)']: {
'.inside-local-container': {
width: '100%'
}
},
};
});fonts
font 创建一个 CSS @font-face 规则,fonts 一次创建多个规则:
import { customStyles, font, fonts } from 'effcss';
// global
const single = font({
src: `url("https://mdn.github.io/shared-assets/fonts/FiraSans-Regular.woff2")`,
genericName: 'sans-serif'
});
const multiple = fonts({
primary: {
src: `url("/fonts/roboto-regular.woff2") format("woff2"), url("/fonts/roboto-regular.woff") format("woff")`,
weight: 400,
style: 'normal',
display: 'swap'
},
secondary: {
src: `url("https://mdn.github.io/shared-assets/fonts/FiraSans-Regular.woff2")`
}
});
customStyles(() => {
// local
const localSingle = font(/* the same */);
const localMultiple = fonts(/* the same */);
return {
'.global-font': {
fontFamily: single()
},
'.global-primary-font': {
// `single` as fallback
fontFamily: multiple.primary(single)
},
'.local': {
fontFamily: localSingle()
},
'.local-primary-font': {
// `localSingle` as fallback
fontFamily: localMultiple.primary(localSingle)
}
};
});update
update 会刷新全局变量的初始值:
const offset = variable('10px');
const colors = variable({
primary: {
syntax: 'color',
inherits: false,
initialValue: '#2192a7'
},
secondary: '#425158'
});
update(offset, '14px');
update(colors, {
primary: 'grey',
secondary: 'green'
});尽可能使用变量 set 方法,因为它更明确。
stylesheet
stylesheet 返回创建的样式表:
const custom = customStyles(() => {
return {
'.custom': {
padding: '1rem'
},
};
});
// specified stylesheet
const customStylesheet = stylesheet(custom);还有一些实用工具可以返回特殊的样式表:
layersStylesheet返回全局图层的样式表,variablesStylesheet返回全局变量的样式表,animationsStylesheet返回全局动画的样式表,fontsStylesheet返回全局字体的样式表,sharedStylesheet返回全局规则的样式表(使用className和attribute创建)。
configure
在创建第一个样式表之前调用 configure 会影响样式生成:
configure({
// custom prefix for ids
prefix: 'custom',
// disable minification
minify: false,
// emulate server-side mode
emulate: true
});serialize
serialize 函数将其参数或所有已创建的样式表转换为 HTML 字符串:
const custom = customStyles(() => {
return {
'.custom': {
padding: '1rem'
},
};
});
// specified stylesheet
const customHTML = serialize(custom);
// all created stylesheets
const fullHTML = serialize();serializeMeta 将其参数的元数据或所有已创建的样式表的元数据序列化为 HTML 字符串:
import { classNames } from 'effcss';
type Card = {/* the same */};
const card = classNames<Card>(/* the same */);
// specified stylesheet metadata
const cardMetaHTML = serializeMeta(card);
// all created stylesheets metadata
const fullMetaHTML = serialize();这样,样式和元数据可以在服务器端计算,并在客户端重用。这对于静态站点生成/服务端渲染尤其有用。