Skip to content

Guide

In this section, we'll install EffCSS and see how to use it.

Install

Type in your terminal:

sh
# npm
npm i effcss

# pnpm
pnpm add effcss

# yarn
yarn add effcss

Use

To create styles with EffCSS, you just need to call the utilities. Fortunately, there are very few, and by their name you can understand what they do.

EffCSS creates separate stylesheets using classNames, attributes and customStyles utilities. The first two require specifying a Typescript type contract by which selectors will be implemented. This will allow you to control both the creation of styles and their use. The contract type is an object with any level of nesting properties:

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

Let's take a closer look at the usage of each utility.

classNames

classNames creates a stylesheet and returns a function for deriving classnames:

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

// apply
export const Component = () => {
    const cls = card({
        card: {
            rounded: true
        },
        w: 's'
    });
    return <div className={cls}>
        Card
    </div>
};

attributes

attributes creates stylesheet and returns a function for deriving attributes:

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

// apply
export const Component = () => {
    const attrs = card({
        card: {
            rounded: true
        },
        w: 's'
    });
    return <div {...attrs}>
        Card
    </div>
};

customStyles

customStyles creates stylesheet without derived selectors:

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

variables

variable creates a single CSS @property rule, variables creates several at once:

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

animations

animation creates a single CSS @keyframes rule, animations creates several at once:

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 creates a single CSS @layer rule, layers creates several at once:

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 creates a single CSS @container rule, containers creates several at once:

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

update

update refreshes initial value of global variable/variables:

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

stylesheet

stylesheet returns the created stylesheet:

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

configure

configure affects style generation if it is called before the first stylesheet is created:

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

serialize

serialize converts its argument or all created stylesheets to an HTML string:

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

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

The resulting string can be used with SSG/SSR.

Released under the Apache-2.0 License.