Skip to content

Tipos BEM

Las funciones del generador de hojas de estilo pueden describir los estilos generados mediante tipos especiales, de modo que al usarlas, TypeScript mostrará los selectores CSS disponibles. Esta característica combina la metodología de BEM y los genéricos de TypeScript, ocultando la implementación de los estilos tras la interfaz externa.

Se recomienda usar tipos sin una referencia estricta a una metodología específica. Para más información, consulte la sección CSS-in-TS.

Descripción de estilos

El primer paso para desarrollar estilos con EffCSS es declarar la funcionalidad proporcionada como un tipo que describa tus estilos en tres niveles de la metodología BEM:

  • bloques (1.er nivel),
  • elementos (2.º nivel),
  • modificadores (3.er nivel).

Para todos los niveles, se recomienda usar nombres descriptivos escritos en formato lowerCamelCase.

WARNING

No utilice puntos ni guiones bajos en los nombres, ya que estos caracteres se utilizan para mostrar selectores y atributos de EffCSS.

INFO

No es necesario utilizar los tres niveles. Si no desea describir los estilos en términos de BEM, simplemente describa sus selectores a nivel de bloques individuales.

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: {};
    }
};

El bloque no tiene por qué contener elementos. El elemento no tiene por qué contener modificadores.

Tenga en cuenta que los modificadores de bloque también se describen en el nivel 3 dentro del elemento con la clave "". El bloque con la clave "" se considera el bloque predeterminado o principal. También puede darse el caso de que los estilos describan algunas utilidades que no estén vinculadas a un bloque o elemento específico (como en Tailwind); en ese caso, la siguiente descripción resulta útil.

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

Implementación de estilos

El segundo paso para desarrollar estilos con EffCSS consiste en implementar la funcionalidad descrita dentro de la función StyleSheet Maker. La utilidad bem se utiliza en EffCSS para generar selectores BEM. Esta utilidad también verifica la validez de los selectores CSS implementados, ya que es genérica.

Cada selector se forma uniendo los nombres del bloque, el elemento, el modificador y el valor del modificador (si no es booleano) mediante un punto.

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;

Uso de estilos

Los estilos predefinidos de EffCSS se pueden usar pasando la función StyleSheet maker correspondiente al método use del objeto Style provider. Su uso es similar al de la utilidad bem: los argumentos introducidos también se comprobarán para verificar su compatibilidad con el tipo 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
    // you can also 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'),
    };
};

También puedes pasar un objeto para generar varios selectores a la vez.

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
            };
        }
    });
};

O bien, puedes pasar una matriz de cadenas

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']);
};

Alternativamente, puede obtener primero el objeto MonoResolver y luego refinarlo según sea necesario

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'
        }).$,
  };
};

Utiliza el método que prefieras.

Modos de generación de atributos

El componente Proveedor de estilos puede influir en la generación de selectores BEM mediante dos atributos:

  • mode: puede tener el valor a (utiliza los atributos data-*) o c (utiliza el atributo class).

  • min: es un atributo booleano que permite minimizar los selectores BEM.

La generación de clases CSS es más predecible, y la generación de atributos de datos permite separar visualmente las distintas hojas de estilo. Utiliza el método que prefieras.

La minimización conserva un prefijo único en el selector, por lo que los estilos de las distintas tablas permanecerán aislados, a la vez que la longitud de los selectores se reduce significativamente.

Beneficio

Este enfoque permite separar el contrato de la implementación. Durante el desarrollo, se sabe de antemano qué selectores CSS deben proporcionarse, y cualquier desarrollador externo puede importar tu tipo TCustomMaker y obtener los estilos necesarios sin tener que consultar tu función Creador de hojas de estilo. Dado que los archivos JS suelen minificarse durante el ensamblaje, el enfoque propuesto ahorra mucho tiempo.

Además, EffCSS genera sus propios selectores BEM únicos para cada hoja de estilo, lo que elimina el riesgo de tener que redefinirlos.

De este modo, EffCSS permite ir más allá de la creación habitual de CSS, acercándose al rol de CSS integrado en TypeScript.

Publicado bajo la Licencia Apache 2.0