CSS-in-TS
Функции StyleSheet maker могут описывать генерируемые стили с помощью специальных типов, так что при их использовании TypeScript подскажет доступные CSS-селекторы. Эта возможность объединяет в себе методологию БЭМ и дженерики TypeScript, скрывая реализацию стилей за внешним интерфейсом.
Описание стилей
Первый шаг в разработке стилей с помощью EffCSS - это зафиксировать предоставляемую функциональность в виде типа, которые описывает ваши стили на трех уровнях методологии БЭМ
- блоках (1 уровень),
- элементах (2 уровень),
- модификаторах (3 уровень).
Для всех уровней рекомендуется использовать осмысленные названия, написаные в стиле lowerCamelCase.
WARNING
Не используйте в названиях символы точки и нижнего подчеркивания, так как эти символы используются для вывода селекторов и аттрибутов EffCSS
INFO
Вам не обязательно использовать все три уровня. Если вам не хочется описывать стили в терминах БЭМ, просто описывайте свои селекторы на уровне отдельных блоков.
export type TCustomMaker = {
/**
* Default block
*/
'': {
/**
* Logo element
*/
logo: {
/**
* Spin animation
*/
spin: '';
/**
* Logo scale
*/
scale: 1 | 2;
};
/**
* Body element
*/
body: {
/**
* Padding size
*/
p: 's' | 'm' | 'l';
};
/**
* Footer element
*/
footer: {
/**
* Is footer hidden
*/
hidden: '';
};
};
/**
* Panel block
*/
panel: {
/**
* Default block
*/
'': {
/**
* Is panel modal
*/
modal: '';
/**
* Max panel width
*/
max: 'vw' | 'parent';
};
/**
* Close button block
*/
closeBtn: {};
}
};Блок не обязательно должен содержать элементы. Элемент не обязательно должен содержать модификаторы.
Обратите внимание, что модификаторы блока также описываются на 3 уровне внутри элемента с ключом ''. Блок с ключом '' считается дефолтным или основным. Возможна также ситуация, когда стили описывают некоторые утилиты, не привязанные к конкретному блоку и/или элементу (как у Tailwind) - тогда на помощь приходит следующее описание
export type TCustomMaker = {
'': {
'': {
/**
* Width
*/
w: 's' | 'm' | 'l';
/**
* Height
*/
h: 's' | 'm' | 'l';
}
};
};Реализация стилей
Второй шаг в разработке стилей с помощью EffCSS - это реализовать описанную функциональность в рамках функции StyleSheet maker. Для генерации БЭМ-селекторов в EffCSS используется утилита bem. Эта утилита также проверяет допустимость реализуемых CSS-селекторов, поскольку является дженериком.
Каждый селектор образуется путем соединения с помощью точки названий блока, элемента, модификатора и значения модификатора (если он не является булевым).
import { TStyleSheetMaker } from 'effcss';
export type TCustomMaker = {...};
const maker: TStyleSheetMaker = ({
bem,
}) => {
return {
// селектор корневого блока
[bem<TCustomMaker>('')]: {...},
// селектор элемента внутри корневого блока
[bem<TCustomMaker>('.logo')]: {...},
// селектор булева модификатора элемента
[bem<TCustomMaker>('.logo.spin')]: {}
// селектор модификатора блока с конкретным значением
[bem<TCustomMaker>('panel..max.vw')]: {}
}
};
export default maker;Использование стилей
Готовые стили EffCSS можно использовать, передавая соответствующую функцию StyleSheet maker в метод use объекта Style consumer. Использование аналогично утилите bem - введенные аргументы также будут проверены на соответствие типу TCustomMaker.
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';
const getStyle = (use: IStyleProvider['use']) => {
// EffCSS v3 возвращает функцию-резолвер,
// а EffCSS v4 - массив резолверов
const [css] = use(customStyle);
return {
block: css<TCustomMaker>(''),
logo: css<TCustomMaker>('.logo'),
spinLogo: css<TCustomMaker>('.logo.spin'),
footer: css<TCustomMaker>('.footer'),
body: css<TCustomMaker>('.body.p.m'),
};
};Вы также можете передать объект для генерации сразу множества селекторов
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';
const getStyle = (use: IStyleProvider['use']) => {
// EffCSS v3 возвращает функцию-резолвер,
// а EffCSS v4 - массив резолверов
const [css] = use(customStyle);
return css<TCustomMaker>({
'': {
logo: {
spin: '',
scale: 2
};
}
});
};Или вы можете передать массив строк
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';
const getStyle = (use: IStyleProvider['use']) => {
// EffCSS v3 возвращает функцию-резолвер,
// а EffCSS v4 - массив резолверов
const [css] = use(customStyle);
return css<TCustomMaker>(['.logo.spin', '.logo.scale.2']);
};Кроме того вы можете сначала получить объект MonoResolver, а затем доуточнять его по мере необходимости
import { IStyleProvider } from 'effcss';
import { default as customStyle, TCustomMaker } from './styles/custom';
const getStyle = (use: IStyleProvider['use']) => {
// EffCSS v3 возвращает функцию-резолвер,
// а EffCSS v4 - массив резолверов
const [css] = use(customStyle);
const block = css<TCustomMaker>();
const logo = block.e('logo');
const logoSpin = logo.m({
spin: ''
});
return {
block: block.$,
logo: logo.$,
spinLogo: logoSpin.$,
footer: block.e('footer').$,
body: block.e('body').m({
p: 'm'
}).$,
};
};Используйте наиболее понравившийся способ.
Режимы генерации атрибутов
Компонент Style provider может влиять на генерацию БЭМ-селекторов с помощью 2 атрибутов:
mode- может иметь значениеa(используетdata-*-атрибуты) иc(использует атрибутclass),min- булев атрибут, который позволяет минифицировать БЭМ-селекторы.
Генерация CSS-классов более предсказуема, а генерация data-атрибутов позволяет визуально отделить селекторы разных таблиц. Используйте то, что вам больше по душе.
Минификация сохраняет уникальный префикс в составе селектора, поэтому стили разных таблиц останутся изолированными, при этом длина селекторов существенно сократится.
Польза
Описанный подход позволяет отделить контракт от реализации - при разработке вы изначально знаете, какие CSS-селекторы должны предоставить, а любой сторонний разработчик сможет импортировать ваш тип TCustomMaker и получить нужные стили, не заглядывая в вашу функцию StyleSheet maker. Учитывая, что в при сборке JS-файлы обычно подвергаются минификации, предложенный подход здорово экономит время.
Кроме того EffCSS генерирует для каждой таблицы стилей свои уникальные БЭМ-селекторы, что исключает риск их переопределения.
Таким образом, EffCSS позволяет шагнуть дальше обычного создания CSS, приближаясь к роли CSS-in-TS.