Skip to content

CSS-in-TS

StyleSheet maker 函数可以使用特殊类型描述生成的样式,因此在使用它们时,TypeScript 会提示可用的 CSS 选择器。此功能利用了 TypeScript 泛型,将样式的实现隐藏在外部接口之后。

风格描述

使用 EffCSS 开发样式的第一步是将提供的功能捕获为 TypeScript 类型。自 v4.6.0 版本起,对象的嵌套层级不再有限制,并且可以在任何层级定义字符串或数值类型的属性。

对于所有级别,建议使用以 lowerCamelCase 格式编写的有意义的名称。

ts
export type TCustomMaker = {
    /**
     * Width utility
     */
    w: 's' | 'm' | 'l';
    /**
     * Logo
     */
    logo: {
        /**
         * Spin animation
         */
        spin: '';
        /**
         * Logo scale
         */
        scale: 1 | 2;
    };
    /**
     * Body
     */
    body: {
        /**
         * Padding size
         */
        p: 's' | 'm' | 'l';
    };
    /**
     * Footer
     */
    footer: {
        /**
         * Is footer hidden
         */
        hidden: '';
    };
    /**
     * Panel block
     */
    panel: {
        /**
         * Is panel modal
         */
        modal: '';
        /**
         * Max panel width
         */
        max: 'vw' | 'parent';
        /**
         * Close button
         */
        closeBtn: Record<string, never>;
    }
};

样式实现

使用 EffCSS 开发样式的第二步是在 StyleSheet maker 函数框架内实现所述功能。为了在 EffCSS 中生成带有样式表前缀的唯一选择器,需要使用 select 工具。该工具还会检查已实现的 CSS 选择器的有效性。

每个选择器都是通过路径点连接到所需的属性,并在其值(如果不是对象)前添加冒号 : 来形成的:

ts
import { TStyleSheetMaker } from 'effcss';

export type TCustomMaker = {...};

const maker: TStyleSheetMaker = ({
  select,
}) => {
    const selector = select<TCustomMaker>;
    return {
        [selector('logo')]: {
            // ...
        },
        [selector('logo.scale:1')]: {
            // ...
        },
        [selector('logo.spin:')]: {
            // ...
        },
        [selector('panel.closeBtn')]: {
            // ...
        }
    }
};

export default maker;

样式的使用

要获取选择器解析器,需要将相应的 StyleSheet maker 函数传递给 Style provider 对象的 use 方法。解析器有两个方法:listobj,它们用于输出带有必要选择器的 HTML 属性。

ts
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';

const getStyle = (use: IStyleProvider['use']) => {
    const [css] = use(customStyle);
    return {
        firstElemAttrs: css.obj<TCustomMaker>({
            w: 's',
            panel: {
                max: 'parent'
            }
        }), // {[attributeName]: 'selectors_string'}
        secondElemAttrs: css.list<TCustomMaker>('logo.spin:', 'body.p:m') // {[attributeName]: 'selectors_string'}
    };
};

属性生成模式

Style provider 组件可以通过 2 个属性影响选择器的生成:

  • mode 可以取值 a(使用 data-* 属性)和 c(使用 class 属性),
  • min 是一个布尔属性,允许您压缩 BEM 选择器。

CSS 类生成更具可预测性,而数据属性生成则允许你在视觉上区分不同的样式表。选择你最喜欢的方式即可。

代码压缩会在选择器中保留唯一的前缀,因此不同表格的样式将保持隔离,同时选择器的长度将显著减少。

如果使用 a 生成模式,并且所使用的框架支持此语法,则生成的属性可以直接传递给布局:

tsx
<div {...firstElemAttrs}>...</div>

如果使用 c 生成模式,您可以从对象中获取类并将其传递给布局:

tsx
<div className={firstElemAttrs.class}>...</div>

显式生成类名

从 4.10.0 版本开始,您可以使用 cx 工具直接获取所需的 CSS 类选择器。此工具会忽略 Style providermode 属性,并返回一个包含类名的字符串:

ts
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';

const getStyle = (provider: IStyleProvider) => {
    return {
        firstElemAttrs: provider.cx<TCustomMaker>(customStyle, {
            w: 's',
            panel: {
                max: 'parent'
            }
        }),
        secondElemAttrs: provider.cx<TCustomMaker>(customStyle, ['logo.spin:', 'body.p:m'])
    };
};

显式生成数据属性

从 4.10.0 版本开始,您可以使用 dx 工具直接获取所需的 CSS 数据属性选择器。此工具会忽略 Style providermode 属性,并返回一个包含数据属性的对象:

ts
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';

const getStyle = (provider: IStyleProvider) => {
    return {
        firstElemAttrs: provider.dx<TCustomMaker>(customStyle, {
            w: 's',
            panel: {
                max: 'parent'
            }
        }),
        secondElemAttrs: provider.dx<TCustomMaker>(customStyle, ['logo.spin:', 'body.p:m'])
    };
};

WARNING

每个 StyleSheet maker 只创建一个样式表,因此您需要将 cxdx 实用程序与一个 StyleSheet maker 一起使用,但不能同时使用两个实用程序。

益处

所描述的方法可以将契约与实现分离。在开发过程中,您一开始就知道应该提供哪些 CSS 选择器,任何第三方开发人员都可以导入您的 TCustomMaker 类型并获取所需的样式,而无需查看您的 StyleSheet maker 函数。考虑到 JS 文件通常会在编译过程中被压缩,这种方法可以节省大量时间。

此外,EffCSS 会为每个样式表生成自己独特的样式选择器,从而消除了重新定义样式选择器的风险。

因此,EffCSS 允许您超越通常的 CSS 创建,接近 CSS-in-TS 的角色。

根据 Apache-2.0 许可证发布。