Skip to content

BEM类型

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

建议使用类型时不要严格遵循特定的方法论。更多信息请参见CSS-in-TS部分。

风格描述

使用 EffCSS 开发样式的第一步是将提供的功能声明为一种类型,该类型在 BEM 方法论的三个层级上描述您的样式。

  • 积木(1 层),
  • 元素(第二级),
  • 修饰符(3 级)。

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

WARNING

名称中不要使用点号和下划线,因为这些字符用于输出 EffCSS 的选择器和属性。

INFO

您不必使用全部三个层级。如果您不想使用 BEM 描述样式,只需在单个代码块层级描述您的选择器即可。

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

代码块不必包含元素。元素不必包含修饰符。

请注意,块修饰符也在带有 "" 键的元素内的 3 级中进行描述。带有 "" 键的块被视为默认块或主块。有时,样式会描述一些不与特定块和/或元素关联的实用程序(例如在 Tailwind 中),这时以下描述就派上用场了。

ts
export type TCustomMaker = {
    '': {
        '': {
            /**
             * Width 
             */
            w: 's' | 'm' | 'l';
            /**
             * Height 
             */
            h: 's' | 'm' | 'l';
        }
    };
};

样式实现

使用 EffCSS 开发样式的第二步是在 StyleSheet maker 函数中实现所述功能。EffCSS 使用 bem 工具生成 BEM 选择器。由于 bem 是一个通用工具,它还会检查已实现的 CSS 选择器的有效性。

每个选择器都是通过用点连接块、元素、修饰符和修饰符值(如果不是布尔值)的名称而形成的。

ts
import { TStyleSheetMaker } from 'effcss';

export type TCustomMaker = {...};

const maker: TStyleSheetMaker = ({
  bem,
}) => {
  return {
    // the default block selector
    [bem<TCustomMaker>('')]: {...},
    // selector for an element inside the default block
    [bem<TCustomMaker>('.logo')]: {...},
    // element boolean modifier selector
    [bem<TCustomMaker>('.logo.spin')]: {}
    // selector for a block modifier with a specific value
    [bem<TCustomMaker>('panel..max.vw')]: {}
  }
};

export default maker;

样式的使用

可以通过将相应的 StyleSheet maker 函数传递给 Style provider 对象的 use 方法来使用现成的 EffCSS 样式。其用法与 bem 工具类似——系统也会检查输入的参数是否符合 TCustomMaker 类型。

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

const getStyle = (use: IStyleProvider['use']) => {
    // EffCSS v3 returns a resolver function,
    // and EffCSS v4 returns an array of resolvers
    const [css] = use(customStyle);
    return {
        block: css<TCustomMaker>(''),
        logo: css<TCustomMaker>('.logo'),
        spinLogo: css<TCustomMaker>('.logo.spin'),
        footer: css<TCustomMaker>('.footer'),
        body: css<TCustomMaker>('.body.p.m'),
    };
};

您还可以传递一个对象来一次性生成多个选择器。

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

const getStyle = (use: IStyleProvider['use']) => {
    // EffCSS v3 returns a resolver function,
    // and EffCSS v4 returns an array of resolvers
    const [css] = use(customStyle);
    return css<TCustomMaker>({
        '': {
            logo: {
                spin: '',
                scale: 2
            };
        }
    });
};

或者你可以传递一个字符串数组。

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

const getStyle = (use: IStyleProvider['use']) => {
    // EffCSS v3 returns a resolver function,
    // and EffCSS v4 returns an array of resolvers
    const [css] = use(customStyle);
    return css<TCustomMaker>(['.logo.spin', '.logo.scale.2']);
};

或者,您可以先获取 MonoResolver 对象,然后根据需要对其进行细化。

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

const getStyle = (use: IStyleProvider['use']) => {
    // EffCSS v3 returns a resolver function,
    // and EffCSS v4 returns an array of resolvers
    const [css] = use(customStyle);
    const block = css<TCustomMaker>();
    const logo = block.e('logo');
    const logoSpin = logo.m({
        spin: ''
    });
    return {
        block: block.$,
        logo: logo.$,
        spinLogo: logoSpin.$,
        footer: block.e('footer').$,
        body: block.e('body').m({
            p: 'm'
        }).$,
  };
};

使用你最喜欢的方式。

属性生成模式

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

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

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

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

益处

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

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

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

根据 Apache-2.0 许可证发布。