Войти с помощью github
Форум /

Блоки и темы оформления.

Уже давно пытаюсь избавиться от боли модификатора _theme у блоков. У нас 2 темы блока button - публичная и админка, около 2K строк стилей в сумме.

Имхо, блок должен быть максимально реиспользуемым и не тянуть ненужный код. Сейчас под тему запихиваются ВСЕ комбинации модификаторов блока, даже если они не нужны на странице. Это боль в поддержке.

Очень близки идеи http://whitepaper.tools/. В целом работаю тоже в этом направлении.

На данный момент пришёл к использованию 2-х видов переменных.

1. Глобальные/Контекстные (что реализовано в whitepaper)

Задаются через блок theme и модификаторы theme_color_* theme_size_* и т.д. Провайдят контекст переменных вниз по дереву.

.Theme_color_light {
  --color-bg: #f7f7f7;
  --coloron-bg: #222; 

  --color-primary: #222;
  --coloron-primary: #fff;

  --color-secondary: #777;
  --coloron-secondary: #ddd;

  --color-surface: #fff;
  --coloron-surface: #222;

  --color-error: #d00;
  --coloron-error: #fff;

  --color-success: #090;
  --coloron-success: #fff;
}

2. Локальные (Уровень блока)

Задаются с указанием префикса имени блока. Такой способ явно ограничивает пространство имён переменных, чтобы случайно не сломать что-то внутри дерева. И позволяет легко изменять их через модификаторы.

Если спроецировать на мир React, то контекстные переменные - это пропсы, локальные - стейт :)

.Button {
  --Button-bg: var(--color-bg);
  --Button-border: var(--color-secondary);
  --Button-color: var(--color-primary);
  --Button-shadow: var(--color-bg);
  --Button-fontSize: 1rem;

  background: var(--Button-bg);
  border: 1px solid var(--Button-bg);
  color: var(--Button-color);
  padding: 1em 2em;
  font-size: var(--Button-fontSize);
  transition: .3s;
  border-radius: 3px;
}

.Button:hover {
  background: var(--Button-color);
  color: var(--Button-bg);
  cursor: pointer;
  box-shadow: 0 10px 20px var(--Button-shadow);
}

.Button_disabled,
.Button_disabled:hover {
  background: #ddd;
  border-color: #ddd;
  color: #999;
  cursor: default;
  box-shadow: none;
}

Блок получает переменные из контекста и преобразует их в локальные для дальнейшего использования в своих стилях.

3. Модификаторы

Дальше через модификаторы жонглируем уже локальными переменными блока.

.Button_color_action {
  --Button-bg: var(--color-action, #00a);
  --Button-color: var(--coloron-action, #fff);
}

.Button_color_error {
  --Button-bg: var(--color-error, #f00);
  --Button-color: var(--coloron-error, #fff);
}

.Button_color_success {
  --Button-bg: var(--color-success, #090);
  --Button-color: var(--coloron-success, #fff);
}

Данный подход позволяет избавиться от длинных селекторов в css.

Например если мы не хотим использовать локальные переменные, то нужно писать классы таким образом

.Button_color_success {
  background: var(--coloron-success, #fff);
  color: var(--color-success, #090);
}

.Button_color_success.Button:hover {
  background: var(--color-success, #090);
  color: var(--coloron-success, #fff);
}

И это только 2 состояние, а там ещё N состояний, включая вложенные элементы...

Локальные переменные, конечно не серебренная пуля и каскады писать придётся. Но как решение вполне может помочь добиться лаконичных селекторов.

4. Именование цветов

После долгих ресёрчей толком не нашёл способа лаконично и предсказуемо описывать переменные. Наиболее лаконичным стилем нашёл подход из Material Design https://material.io/design/color/

В кратце, есть какой-то цвет и для него создаётся рекомендуемый контрастный цвет.

:root {
    --color-primary: #000;
    --coloron-primary: #fff;
}

В качестве дополнительного задания контраста есть идеи использовать доп. параметр -low, -hight

:root {
    --color-primary: #000;
    --coloron-primary: #dddddd ; /* например для текста на фоне с цветом primary */
    --coloron-primary-hight: #ffffff; /* сильноконтрастный цвет */
    --coloron-primary-low: #777777; /* слабоконтрастный цвет */
}

В целом такое именование позволяет добиться предсказуемых результатов при создании темы.

Правда это тоже не является серебренной пулей, т.к. где-то могут не использоваться рекомендуемые переменные.

.Block {
   background: var(--color-surface);
   color: var(--color-error);
}

И не известно на сколько в новой теме surface и error будут контрастны друг к другу.

upd Пересмотрел ещё раз whitepapper https://github.com/whitepapertools/whitepaper-portal/tree/a6bb170522b470560e3572856cf11b1929e1469a/common.blocks/theme

Идёт разбитие цветовой темы на 2 вида. 1 базовые цвета страницы, 2 контролы (кнопки, чекбоксы, инпуты и т.д.).

Данный подход позволяет наиболее однозначно разрабатывать цвета для темы и тестировать их в "вакуме", что очень удобно. Тема не рассчитывается на все цвета радуги. Если нужно много вариаций, то стоит сделать несколько тем, что и плюс и минус.

Плюсы:

  • Однозначность использования цветов
  • Возможность разрабатывать тему "в вакуме"

Минусы:

  • Нужно использовать несколько миксов темы на странице для нескольких комбинаций цветовых решений.

5. Старые браузеры

Любимый IE... Хоть поддержка по caniuse уже > 90% https://caniuse.com/#search=css-variables он ещё живёт...

Как один из подходов решения этого вопроса есть компромиссный вариант - делать версию с одной темой. Все переменные темы дублировать в :root и через postcss ставить статичные значения.

Этот сегмент получит сильно урезанный вариант оформления... Если это сильно критично то от локальных переменных придётся отказаться и делать стилизацию через каскады.

Либо использовать подходы с css-in-js... Но они не лишены недостатков.

6. Дизайнеры, разработчики и система

Тут конечно есть конфликт интересов... Но имхо, у дизайнера должна быть система, по которой он работает и она должна быть логичной (например http://whitepaper.tools/)

П.Н. Небольшая демка "на коленке" Буду благодарен, если поделитесь своей системой стилизации блоков и именованием переменных.