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

от автора

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

Допустим, мы используем 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-right: 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; /* Наш победитель */ }

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


ссылка на оригинал статьи https://habr.com/ru/post/652047/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *