Как стать автором
Обновить

Практический пример использования CSS Layer

Время на прочтение3 мин
Количество просмотров6.9K

Допустим, мы разрабатываем библиотеку компонентов.

Допустим, мы используем React.

Допустим, в ней есть компонент кнопки.

Очень условно он будет выглядеть так:

// Button.js

import './Button.css'

const Button = ({ children, className = '' }) => {
	const cls = 'Button' + className;
	return <button className={cls}>{children}</button>
}

А стили будут выглядеть так:

/* Button.css */

.Button {
	background: blue;
	padding: 4px 10px;
	border: none;
}

Тут к нам приходят разрабы и говорят: "кнопка классная, но нам нужна ссылка в виде кнопки"

Не вопрос! Добавим свойство Component:

// Button.js

import './Button.css'

const Button = ({ children, Component = 'button', className = '' }) => {
	const cls = 'Button' + className;
	return <Component className={cls}>{children}</Component>
}

Пример использования:

const linkButton = (
	<Button Component="a" href="https://vk.com">
		ВКонакте
	</Button>
);

Только ой, a ж является инлайновым элементом. Что такого, спросите вы? Кнопка вроде как выглядела, так и выглядит. А вот нет. Представьте, что пользователи в месте встраивания кнопки хотят добавить ей отступ сверху:

// App.js

<Button className="myButton">Click it!</Button>
/* App.css */

.myButton {
	margin-top: 10px;
}

Инлайновый элемент проигнорирует этот margin.

Ну ничего, и не с такими сложностями мы справлялись. Внесем изменение в стиль кнопки:

/* Button.css */

.Button {
	background: blue;
	padding: 4px 10px;
	border: none;
	display: inline-block; /* Зафиксируем display */
}

Но теперь возникла другая беда, куда более подлая и трудноизлечимая. Следите за руками. Представьте себе такой способ использования кнопки в проекте. Допустим, там есть какой-то стейт loading, который если true, то кнопку нужно скрыть:

// App.js

<Button className={loading ? 'hidden' : ''} />
/* App.css */

.hidden {
	display: none;
}

Вспоминаем, как работает каскад в CSS. Если вдруг забыли, то вот отличная статья, поясняющая за все уровни каскадов. Прочитали? Возвращаемся к нашей беде.

Беда заключается в том, что у .hidden и .Button одинаковый вес, при этом оба они претендуют на свойство display. А значит CSS-парсеру придется выяснять победителя исходя из порядка. Порядок — это последний уровень каскада. Когда ваши CSS-файлы разбиты по модулям, рассчитывать на их определенный порядок появления в итоговом бандле очень некомфортно. В итоге это будет приводить к ситуациям, когда побеждает то один селектор, то другой. Что же делоть...

Layer спешит на помощь

Новый уровень каскада Layer разместился аккурат перед специфичностью и порядком:

  1. Importance

  2. Context

  3. Layer (приветик)

  4. Specificity

  5. Order

Layer позволяет гибко задать разные уровни собственных стилей. В нашем примере таких уровней может быть три (а может быть и больше):

  1. уровень сброса стилей;

  2. уровень стилей библиотеки;

  3. уровень стилей приложения.

Давайте эти уровни зададим.

@layer reset, library; /* Деклалируем приоритет слоёв */

/* Button.css */

@layer reset {
	.Button {
		display: inline-block;
	}
}

@layer library {
	.Button {
		background: blue;
		padding: 4px 10px;
		border: none;
	}
}

Но постойте, мы вроде хотели три уровня задать, а задали два. Это специфика каскада layer. Всё, что определено за пределами какого-либо уровня, автоматически становится самым приоритетным. На самом деле и library тут не нужен, но я его оставил для наглядности.

Прикольно, что все стили вне уровней побеждают, так как пользователям не нужно свои перебивающие стили обрамлять в какой-нибудь @layer app.

То есть вот этот стиль в итоге перебьёт тот, что находится в @layer reset.

/* App.css */

.hidden {
	display: none; /* Наш победитель */
}

Удобно? По-моему это просто фантастика. Во многом потому, что в браузерах этого каскада ещё нет :( пока.

Теги:
Хабы:
+6
Комментарии18

Публикации

Изменить настройки темы

Истории

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн