Создание тем с помощью переменных: глобальные и локальные

Создание тем с помощью переменных: глобальные и локальные

От автора: иногда сложно устанавливать CSS переменные для изменения системы дизайна: если область видимости переменных слишком мала, система потеряет однообразие. Если переменные слишком глобальные, теряется детализация.

Возможно, мы можем решить обе проблемы. Я попробовал бы свести переменные системы дизайна до двух типов: глобальные и компонентные. Глобальные переменные приносят единообразие в компоненты. Компонентные переменные дают детализацию и изоляцию. Позвольте показать вам, как этого достичь, взяв в качестве примера простой компонент. Осторожно, я буду использовать в статье CSS переменные, но концепция применима и к препроцессорным переменным.

Глобальные переменные

Системные переменные представляют собой общее понятие, призванное сохранить единообразие среди компонентов.

Начнем с .alert в качестве примера. Скажем, мы хотим сохранить единообразие для всех отступов в margin и padding. Сначала можно задать глобальные значения отступов:

:root { --spacer-sm: .5rem; --spacer-md: 1rem; --spacer-lg: 2rem;
}

И далее использовать их в компонентах:

/* Defines the btn component */
.btn { padding: var(--spacer-sm) var(--spacer-md);
} /* Defines the alert component */
.alert { padding: var(--spacer-sm) var(--spacer-md);
}

Основные преимущества такого подхода:

Генерируется единый источник правды для отступов и одна точка для автора, использующего нашу систему для настройки.

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

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

Но появляются и проблемы:

Система утрачивает модульность из-за генерации дерева зависимостей. Компоненты зависят от глобальных переменных, поэтому они больше не изолированы.

Авторы не могут настроить один компонент, не переписав CSS. Например, чтобы изменить padding для alert, не сдвинув ничего сильно в системе, необходимо переписать компонент alert:

.alert { padding-left: 1rem; padding-right: 1rem;
}

Chris Coyier объясняет идею создания тем с помощью глобальных переменных на пользовательских переменных в этой статье.

Компонентные переменные

Robin Rendle объясняет в своей статье, что компонентные переменные ограничены до модуля. Если сгенерировать alert с этими переменными, мы получим:

.alert { --alert-color: #222; color: var(--alert-color); border-color: var(--alert-color);
}

Основные преимущества:

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

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

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

Давайте узнаем, как можно взять лучшее от двух методов!

Двухуровневая система создания тем

Решение – двухслойная система тем, в которой глобальные переменные всегда информируют компонентные переменные. Каждый слой следует набору определенных правил.

Первый уровень: глобальные переменные

Главная причина иметь глобальные переменные – поддержание единообразия, и эти переменные следуют этим правилам:

У глобальных переменных есть префикс global, они следуют формуле —global—concept—modifier—state—PropertyCamelCase

Concept — что-то типа spacer или main-title

State — что-то типа hover или expanded

Modifier — что-то типа sm или lg

PropertyCamelCase — что-то типа BackgroundColor или FontSize

Это концепции, не привязанные к элементу или компоненту

Это неправильно: —global-h1-font-size

Это правильно: —global—main-title—FontSize

Например, настройка глобальных переменных будет выглядеть так:

:root { /* --global--concept--size */ --global--spacer--sm: .5rem; --global--spacer--md: 1rem; --global--spacer--lg: 2rem; /* --global--concept--PropertyCamelCase */ --global--main-title--FontSize: 2rem; --global--secondary-title--FontSize: 1.8rem; --global--body--FontSize: 1rem; /* --global--state--PropertyCamelCase */ --global--hover--BackgroundColor: #ccc;
}

Второй уровень: компонентные переменные

Второй слой ограничен свойствами компонента, подверженными изменениям. Этот слой соблюдает следующие правила:

Предположим, что мы пишем BEM, тогда переменные следуют формуле —block__element—modifier—state—PropertyCamelCase

block__element—modifier – имя селектора, как alert__actions или alert—primary

state — что-то типа hover или active

если не писать имена классов в стиле BEM, то применяется тот же принцип. Просто замените block__element—modifier на свой classname

значения компонентных переменных всегда определяются глобальными переменными

у компонентной переменной всегда есть значение по умолчанию в качестве фолбека на случай, если компонент не связан с глобальными переменными

Например:

.alert { /* Component scoped variables are always defined by global variables */ --alert--Padding: var(--global--spacer--md); --alert--primary--BackgroundColor: var(--global--primary-color); --alert__title--FontSize: var(--global--secondary-title--FontSize); /* --block--PropertyCamelCase */ padding: var(--alert--Padding, 1rem); /* Sets the fallback to 1rem. */
} /* --block--state--PropertyCamelCase */
.alert--primary { background-color: var(--alert--primary--BackgroundColor, #ccc);
} /* --block__element--PropertyCamelCase */
.alert__title { font-size: var(--alert__title--FontSize, 1.8rem);
}

Обратите внимание, что мы определяем локальные переменные через глобальные. Это ключевой момент для работы системы, так как он позволяет авторам создавать темы системы. Например, если необходимо поменять главный цвет во всех компонентах, нужно лишь заново определить —global—primary-color.

С другой стороны, у всех компонентных переменных есть значение по умолчанию. То есть компонент может быть сам по себе, он не зависит от чего-либо. Авторы могут использовать их изолированно.

Такая настройка поддерживает единообразие среди компонентов и генерирует общий язык между дизайнерами и разработчиками, так как можно установить такие же глобальные переменные в Sketch как бамперы для дизайнеров, что дает детальный контроль авторам.

Почему эта система работает?

В идеальном мире мы, как создатели системы дизайна, ожидаем, что «авторы» или пользователи наших систем будут использовать их без модификаций. Но мир, конечно же, не идеален, и такого не бывает.

Если мы дадим авторам возможность легко менять тему системы без необходимости переписывать CSS, мы не только сделаем их жизнь легче, но также снизим риск поломки модулей. В конце концов, обслуживаемая система – хороша система.

Двухуровневая система создания тем генерирует модульные и изолированные компоненты, а авторы могут их настраивать на глобальном и компонентом уровне. Например:

:root { /* Changes the secondary title size across the system */ --global--secondary-title--FontSize: 2rem;
} .alert { /* Changes the padding on the alert only */ --alert--Padding: 3rem;
}

Какие значения нужно перенести в переменные?

CSS переменные открывают окна в код. Чем шире мы открываем их для авторов, тем уязвимее система становится из-за проблем с реализацией.

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

В следующей версии PatternFly (open source система дизайна, над которой я работую) мы разрешим кастомизацию почти всего, что не связано с макетом: цвета, отступы, типографика, тени и т.д.

Собираем все воедино

Чтобы показать концепцию в действии, я создал проект на CodePen:

Глобальные переменные расположены в _global-variables.scss. Это основа поддержания единообразия в системе, они позволят авторам вносить глобальные изменения.

Есть 2 компонента: alert и button. Это изолированные и модульные единицы с ограниченными переменными, которые позволяют авторам тонко настраивать компоненты.

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

Например, если автор хочет:

изменить primary цвет на розовый во всей системе;

изменить danger цвет на оранжевый только для кнопок;

и изменить padding left на 2.3rem только для alert…

… то это можно сделать следующим образом:

:root { // Changes the primary color on both the alert and the button --global--primary--Color: hotpink;
} .button { // Changes the danger color on the button only without affecting the alert --button--danger--BackgroundColor: orange; --button--danger--hover--BorderColor: darkgoldenrod;
} .alert { // Changes the padding left on the alert only without affecting the button --alert--PaddingLeft: 2.3rem;
}

Код системы дизайна цел, и это просто лучшая зависимость.

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

Автор: Andrés Galante

Источник: https://css-tricks.com/

Редакция: Команда webformyself.