Guide
In this section, we'll install EffCSS and see how to use it.
Install
Type in your terminal:
# npm
npm i effcss
# pnpm
pnpm add effcss
# yarn
yarn add effcssUse
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:
/**
* 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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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.