README.md

effcss

EffCSS

license npm latest package Coverage Status npm package minimized gzipped size install size NPM Downloads GitHub Repo stars

EffCSS is a self-confident CSS-in-TS library based only on the browser APIs. Use the full power of JS and TS to create styles.

Some features

  • zero-dependency,
  • framework agnostic,
  • small bundle size,
  • lazy mode support,
  • selectors isolation and minification out of the box,
  • TypeScript contract-based autocompletion,
  • compatible with any rendering (CSR, SSR, SSG).

Links

Examples

Installation

Type in your terminal:

# npm
npm i effcss

# pnpm
pnpm add effcss

# yarn
yarn add effcss

CSS types & TS autocompletion

By default EffCSS is permissive: styles accept any object. To enable strict CSS-typing (autocompletion + validation of invalid values), define EffCSS.Properties once in your project via global namespace augmentation.

For example, you can install csstype

npm i -D csstype

Then combine with EffCSS

// env.d.ts
import type { Properties as CSSProperties } from 'csstype';

declare global {
    namespace EffCSS {
        interface Properties extends CSSProperties {}
    }
}

export {};

Now every style rule gets full TS autocompletion for CSS properties.

Usage

Just declare stylesheet contract, implement it and apply ready selectors:

App.tsx

import { className, classNames, attribute, attributes, variable, variables, animation, animations } from 'effcss';

// you can create independent rules

// returns classname string
const bgCls = className({
    background: 'white',
    '&:hover': {
        borderRadius: '1em'
    }
});

// returns object with data-attribute
const fontAttr = attribute({
    fontWeight: 'bold'
});

// single global variable
// shadowColor() => var(--unique-variable-name)
const shadowColor = variable('#58666d');

// multiple global variables
// widthVars.s() => var(--unique-variable-name)
const widthVars = variables({
    s: '12px',
    m: '16px',
    l: '20px'
});

// single global animation
// spinAnimation = unique-animation-name
const spinAnimation = animation({
    from: {
        transform: 'rotate(0deg)'
    },
    to: {
        transform: 'rotate(360deg)'
    }
});

// also you can create isolated stylesheets

// 1. declare

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

// 2. implement

// creates a stylesheet with attribute selectors
// styleComponents({rounded: true}) => {[data-unique-key]: 'some values'}
const styleComponents = attributes<Components>((selectors) => {
    const { rounded, card, spinner } = selectors;

    // multiple local variables
    const colors = variables({
        primary: {
            inherits: false,
            initialValue: '#2192a7'
        },
        secondary: {
            inherits: false,
            initialValue: '#425158'
        }
    });

    return {
        [rounded.true]: {
            borderRadius: '50%'
        },
        [spinner]: {
            animation: `${spinAnimation} infinite 6s linear`
        },
        [card]: {
            display: 'flex',
            justifyContent: 'center',
            '&:hover': {
                filter: `drop-shadow(0 0 2em ${shadowColor()})`
            }
        },
        [card.bg.primary]: {
            background: colors.primary()
        },
        [card.bg.secondary]: {
            // with fallback value
            background: colors.secondary('cyan')
        },
        '@media(prefers-color-scheme: dark)': {
            [card + ':hover']: {
                filter: `drop-shadow(0 0 2em #ffffff)`
            }
        }
        // ... and so on
    };
});

// creates a stylesheet with class selectors
// styleUtils({w: 's'}) => 'string with classnames'
const styleUtils = classNames<Utils>((selectors) => {
    const { w, spacing, blink } = selectors;

    // local animations
    const blinkAnimations = animations({
        simple: {
            '50%': {
                visibility: 'hidden'
            }
        },
        smooth: {
            '0%': {
                opacity: 1
            },
            '50%': {
                opacity: 0
            },
            '100%': {
                opacity: 1
            }
        }
    });

    return {
        [blink.true]: {
            animation: `${blinkAnimations.smooth} 2s infinite`
        },
        [w.s]: {
            width: widthVars.s()
        },
        [w.m]: {
            width: widthVars.m()
        }
        // ... and so on
    };
});

// 3. apply

// returns an object as it is created using `attributes`
const cardAttrs = styleComponents({
    card: {
        bg: 'primary',
        disabled: true
    }
});

// returns a string as it is created using `classNames`
const utilsCls = styleUtils({
    w: 'm'
});

export const App = () => {
    return (
        <div {...cardAttrs} className={utilsCls}>
            <p {...fontAttr} className={bgCls}>
                ...
            </p>
        </div>
    );
};

That’s all. Enjoy simplicity.

Описание
Self-confident CSS-in-JS
Конвейеры
0 успешных
0 с ошибкой
Разработчики