CSS-in-TS
The StyleSheet maker functions can describe the generated styles using special types, so when using them, TypeScript will prompt the available CSS selectors. This feature uses TypeScript generics, hiding the implementation of styles behind the external interface.
Description of styles
The first step in developing styles using EffCSS is to capture the provided functionality as a TypeScript type. Since version v4.6.0, there is no limit to the nesting levels of an object, and at any level you can define properties with a string or numeric value:
For all levels, it is recommended to use meaningful names written in the lowerCamelCase style.
export type TCustomMaker = {
/**
* Width utility
*/
w: 's' | 'm' | 'l';
/**
* Logo
*/
logo: {
/**
* Spin animation
*/
spin: '';
/**
* Logo scale
*/
scale: 1 | 2;
};
/**
* Body
*/
body: {
/**
* Padding size
*/
p: 's' | 'm' | 'l';
};
/**
* Footer
*/
footer: {
/**
* Is footer hidden
*/
hidden: '';
};
/**
* Panel block
*/
panel: {
/**
* Is panel modal
*/
modal: '';
/**
* Max panel width
*/
max: 'vw' | 'parent';
/**
* Close button
*/
closeBtn: Record<string, never>;
}
};Implementation of styles
The second step in developing styles using EffCSS is to implement the described functionality within the framework of the StyleSheet maker function. To generate unique selectors with the style sheet prefix in EffCSS, the 'select` utility is used. This utility also checks the validity of implemented CSS selectors.
Each selector is formed by connecting using a path point to the desired property and adding a sign : before its value, if it is not an object:
import { TStyleSheetMaker } from 'effcss';
export type TCustomMaker = {...};
const maker: TStyleSheetMaker = ({
select,
}) => {
const selector = select<TCustomMaker>;
return {
[selector('logo')]: {
// ...
},
[selector('logo.scale:1')]: {
// ...
},
[selector('logo.spin:')]: {
// ...
},
[selector('panel.closeBtn')]: {
// ...
}
}
};
export default maker;Usage of styles
To get the selector resolver, you need to pass the corresponding StyleSheet maker function to the use method of the Style provider object. The resolver has two methods, list and obj, and they are used to output HTML attributes with the necessary selectors:
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';
const getStyle = (use: IStyleProvider['use']) => {
const [css] = use(customStyle);
return {
firstElemAttrs: css.obj<TCustomMaker>({
w: 's',
panel: {
max: 'parent'
}
}), // {[attributeName]: 'selectors_string'}
secondElemAttrs: css.list<TCustomMaker>('logo.spin:', 'body.p:m') // {[attributeName]: 'selectors_string'}
};
};Attribute generation modes
The Style provider component can influence the generation of selectors using 2 attributes:
mode- can have the valuea(uses thedata-*attributes) andc(uses theclassattribute),minis a Boolean attribute that allows you to minify BEM selectors.
CSS class generation is more predictable, and data attribute generation allows you to visually separate different style sheets. Use what you like best.
Minification retains a unique prefix in the selector, so the styles of different tables will remain isolated, while the length of the selectors will be significantly reduced.
If the a generation mode is used and the framework used supports this syntax, the resulting attributes can be passed directly to the layout:
<div {...firstElemAttrs}>...</div>If the c generation mode is used, you can get the classes from the object and pass them to the layout:
<div className={firstElemAttrs.class}>...</div>Explicit generation of classnames
Starting from version 4.10.0, you can directly get the CSS class selectors you need using the cx utility. This utility ignores mode attribute of Style provider and returns a string with classNames:
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';
const getStyle = (provider: IStyleProvider) => {
return {
firstElemAttrs: provider.cx<TCustomMaker>(customStyle, {
w: 's',
panel: {
max: 'parent'
}
}),
secondElemAttrs: provider.cx<TCustomMaker>(customStyle, ['logo.spin:', 'body.p:m'])
};
};Explicit generation of data attributes
Starting from version 4.10.0, you can directly get the CSS data attribute selectors you need using the dx utility. This utility ignores mode attribute of Style provider and returns a object with data attributes:
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';
const getStyle = (provider: IStyleProvider) => {
return {
firstElemAttrs: provider.dx<TCustomMaker>(customStyle, {
w: 's',
panel: {
max: 'parent'
}
}),
secondElemAttrs: provider.dx<TCustomMaker>(customStyle, ['logo.spin:', 'body.p:m'])
};
};WARNING
Each StyleSheet maker creates only one stylesheet, so you need to use either the cx or dx utility with one maker, but not both.
Benefit
The described approach allows you to separate the contract from the implementation. During development, you initially know which CSS selectors should be provided, and any third-party developer can import your TCustomMaker type and get the necessary styles without looking into your StyleSheet maker function. Considering that JS files are usually minified during assembly, the proposed approach saves a lot of time.
In addition, EffCSS generates its own unique selectors for each stylesheet, which eliminates the risk of redefining them.
Thus, EffCSS allows you to step beyond the usual creation of CSS, approaching the role of CSS-in-TS.