Skip to content

Guía

En esta sección, instalaremos EffCSS y veremos cómo usarlo.

Instalar

Escribe en tu terminal:

sh
# npm
npm i effcss

# pnpm
pnpm add effcss

# yarn
yarn add effcss

Usar

Para crear estilos con EffCSS, solo necesitas llamar a las utilidades. Afortunadamente, son muy pocas y su función se entiende fácilmente por su nombre.

Entre todas las utilidades, cabe destacar classNames y attributes. Estas requieren especificar un contrato en forma de un tipo TypeScript con el que se implementarán los selectores. Esto te permitirá controlar tanto la creación como el uso de los estilos. Un tipo de contrato es un objeto con cualquier nivel de anidamiento de propiedades.

ts
/**
 * 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;
};

Otras utilidades derivan los tipos de sus argumentos. Analicemos con más detalle el uso de cada una de ellas.

classNames

className crea una única regla CSS con el contenido especificado y devuelve el selector de clase como una cadena:

tsx
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 crea una hoja de estilos y devuelve una función para derivar nombres de clases:

tsx
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 se diferencia de classNames en que ejecuta la función pasada y crea una hoja de estilo después de que los selectores se derivan por primera vez:

tsx
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 crea una única regla CSS con el contenido especificado y devuelve el selector de atributo como un objeto:

tsx
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 crea una hoja de estilo y devuelve una función para derivar atributos:

tsx
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 se diferencia de attributes en que ejecuta la función pasada y crea una hoja de estilo después de que los selectores se derivan por primera vez:

tsx
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 crea una hoja de estilos sin selectores derivados:

tsx
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 se diferencia de customStyles en que ejecuta la función pasada y crea una hoja de estilos después de la primera llamada al resultado:

tsx
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 crea una única regla CSS @property, variables crea varias a la vez:

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

Puedes obtener y establecer el valor inicial de una variable global utilizando los métodos correspondientes:

ts
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 crea una única regla CSS @keyframes, animations crea varias a la vez:

ts
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 crea una única regla CSS @layer, layers crea varias a la vez:

ts
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 crea una única regla CSS @container, containers crea varias a la vez:

ts
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 crea una única regla CSS @font-face, fonts crea varias a la vez:

ts
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 actualiza el valor inicial de la(s) variable(s) global(es):

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

Utilice el método set de la variable siempre que sea posible, ya que es más explícito.

stylesheet

stylesheet devuelve la hoja de estilos creada:

ts
const custom = customStyles(() => {
    return {
        '.custom': {
            padding: '1rem'
        },
    };
});
// specified stylesheet
const customStylesheet = stylesheet(custom);

También existen utilidades que devuelven hojas de estilo especiales:

  • layersStylesheet devuelve una hoja de estilo para capas globales,
  • variablesStylesheet devuelve una hoja de estilo para variables globales,
  • animationsStylesheet devuelve una hoja de estilo para animaciones globales,
  • fontsStylesheet devuelve una hoja de estilo para fuentes globales,
  • sharedStylesheet devuelve una hoja de estilo con reglas globales (creadas mediante className y attribute).

configure

configure afecta a la generación de estilos si se llama antes de que se cree la primera hoja de estilos:

ts
configure({
    // custom prefix for ids
    prefix: 'custom',
    // disable minification
    minify: false,
    // emulate server-side mode
    emulate: true
});

serialize

serialize convierte su argumento o todas las hojas de estilo creadas en una cadena HTML:

ts
const custom = customStyles(() => {
    return {
        '.custom': {
            padding: '1rem'
        },
    };
});
// specified stylesheet
const customHTML = serialize(custom);

// all created stylesheets
const fullHTML = serialize();

serializeMeta serializa los metadatos de su argumento o los metadatos de todas las hojas de estilo creadas en una cadena HTML:

ts
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();

De esta forma, los estilos y metadatos se pueden calcular en el servidor y reutilizar en el cliente. Esto resulta especialmente útil para SSG/SSR.

Publicado bajo la Licencia Apache 2.0