Главная » Статьи » Убрать var CSS из :root может оказаться хорошей идеей

Убрать var CSS из :root может оказаться хорошей идеей

Убрать var CSS из :root может оказаться хорошей идеей

От автора: пользовательские свойства CSS уже давно стали горячей темой с множеством замечательных статей о них, от прекрасных пособий о том, как они работают, до креативных руководств о том, что реально можно создать с их помощью. Если вы прочитали более одной или двух статей по этой теме, то я уверен, что вы заметили, что примерно в 99% случаев они начинают с настройки CSS root var.

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

Почему мы ставим переменные для :root в начало.

Почему глобальный охват не подходит для всего.

Как преодолеть влияние классов с помощью локальных свойств

Как обстоят дела с пользовательскими свойствами и :root?

Прежде чем мы перейдем к рассмотрению глобальной области действия, я думаю, что стоит рассмотреть, почему все в начале устанавливают пользовательские свойства для :root. Я объявлял пользовательские свойства для :root даже не задумываясь. Практически все это делают, даже не упоминая почему, включая официальную спецификацию.

Субъект :root упоминается, как то же самое, что html, но с более высокой специфичностью, и это все. Но имеет ли значение эта более высокая специфичность? На самом деле, нет. Все, что он делает, это выбирает html с более высокой специфичностью, точно так же, как селектор класса имеет более высокую специфичность, чем селектор элемента при выборе div.

:root { --color: red;
} html { --color: blue;
} .example { background: var(--color); /* Будет красным, потому что :root имеет более высокую специфичность */
}

Основная причина этого заключается в том, что CSS используется не только для оформления HTML-документов. Он также используется для файлов XML и SVG.

В случае XML и SVG файлов :root выбирает не элемент html, а его корень (например, тег svg в файле SVG). Из-за этого наилучшей практикой для настраиваемого свойства в глобальном масштабе является :root. Но если вы создаете сайт, вы можете добавить его в селектор html и не заметить разницы.

Тем не менее, с каждым использованием :root, он быстро стал «стандартом». Он также помогает отделить переменные для последующего использования от селекторов, которые активно оформляют документ.

Почему глобальный охват не подходит для всего

С помощью препроцессоров CSS, таких как Sass и Less, большинство из нас хранят переменные в выделенном для них раздел. Это прекрасно работает, так почему мы должны внезапно рассматривать локальные переменные? Одна из причин заключается в том, что некоторые люди могут найти что-то подобное.

:root { --clr-light: #ededed; --clr-dark: #333; --clr-accent: #EFF; --ff-heading: 'Roboto', sans-serif; --ff-body: 'Merriweather', serif; --fw-heading: 700; --fw-body: 300; --fs-h1: 5rem; --fs-h2: 3.25rem; --fs-h3: 2.75rem; --fs-h4: 1.75rem; --fs-body: 1.125rem; --line-height: 1.55; --font-color: var(--clr-light); --navbar-bg-color: var(--clr-dark); --navbar-logo-color: var(--clr-accent); --navbar-border: thin var(--clr-accent) solid; --navbar-font-size: .8rem; --header-color: var(--clr-accent); --header-shadow: 2px 3px 4px rgba(200,200,0,.25); --pullquote-border: 5px solid var(--clr-light); --link-fg: var(--clr-dark); --link-bg: var(--clr-light); --link-fg-hover: var(--clr-dark); --link-bg-hover: var(--clr-accent); --transition: 250ms ease-out; --shadow: 2px 5px 20px rgba(0, 0, 0, .2); --gradient: linear-gradient(60deg, red, green, blue, yellow); --button-small: .75rem; --button-default: 1rem; --button-large: 1.5rem;
}

Конечно, это дает нам место, из которого мы можем управлять стилями с помощью пользовательских свойств. Но зачем нам определять —header-color или —header-shadow для :root? Это не глобальные свойства, я явно использую их в заголовке и нигде больше. Если это не глобальное свойство, зачем определять его глобально? Вот где в игру вступает локальная область видимости.

Свойства в локальной области действия

Допустим, у нас есть список стилей, но наш сайт использует систему иконок — скажем, Font Awesome для простоты. Мы не хотим использовать disc для маркеров ul — нам нужна собственная иконка! Если я хочу отключить маркеры неупорядоченного списка для иконок Font Awesome, мы можем сделать что-то вроде этого:

ul { list-style: none;
} li::before { content: "\f14a"; /* checkbox */ font-family: "Font Awesome Free 5"; font-weight: 900; float: left; margin-left: -1.5em;
}

Хотя это очень просто сделать, одной из проблем является то, что иконка становится абстрактной. Если мы не будем использовать Font Awesome реально много, мы можем не знать, что значит f14, не говоря уже о том, чтобы быть в состоянии идентифицировать ее, как иконку флажка. Это семантически бессмысленно. Мы можем уточнить вещи с помощью пользовательского свойства.

ul { --checkbox-icon: "\f14a"; list-style: none;
}

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

<ul class="icon-list checkbox-list"> ... </ul>
<ul class="icon-list star-list"> ... </ul>
<ul class="icon-list bolt-list"> ... </ul>

Затем в CSS мы можем создать пользовательские свойства для разных иконок:

.icon-list { --checkbox: "\f14a"; --star: "\f005"; --bolt: "\f0e7"; list-style: none;
}

Реальная сила локальной области видимости пользовательских свойств проявляется, когда мы фактически хотим применить иконки. Мы можем установить для списка пунктов content: var(—icon):

.icon-list li::before { content: var(--icon); font-family: "Font Awesome Free 5"; font-weight: 900; float: left; margin-left: -1.5em;
}

Затем мы можем определить эту иконку для каждого из списков с более осмысленными именами:

.checkbox-list { --icon: var(--checkbox);
} .star-list { --icon: var(--star);
} .bolt-list { --icon: var(--bolt);
}

Мы можем сделать это на ступеньку выше, добавив цвета:

.icon-list li::before { content: var(--icon); color: var(--icon-color); /* Other styles */
}

Перемещение иконок в глобальную область видимости

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

Наличие иконок в :root не означает, что мы по-прежнему не можем использовать преимущества пользовательских свойств локальной области!

:root { --checkbox: "\f14a"; --star: "\f005"; --bolt: "\f0e7"; --clr-success: rgb(64, 209, 91); --clr-error: rgb(219, 138, 52); --clr-warning: rgb(206, 41, 26);
} .icon-list li::before { content: var(--icon); color: var(--icon-color); /* Other styles */
} .checkbox-list { --icon: var(--checkbox); --icon-color: var(--clr-success);
} .star-list { --icon: var(--star); --icon-color: var(--clr-warning);
} .bolt-list { --icon: var(--bolt); --icon-color: var(--clr-error);
}

Добавление резервных вариантов

Мы можем либо добавить иконку по умолчанию, установив ее как резервный вариант (например var(—icon, «/f1cb»)), либо, поскольку мы используем свойство content, мы можем даже добавить сообщение об ошибке var(—icon, «no icon set»).

Используя переменные —icon и —icon-color локально, мы значительно увеличили читаемость кода. Если кто-то новый придет в проект, им будет намного легче понять, как он работает.

Конечно, это не ограничивается Font Awesome. Локальная область видимости пользовательских свойств также отлично подходит для системы иконок SVG:

:root { --checkbox: url(../assets/img/checkbox.svg); --star: url(../assets/img/star.svg); --baby: url(../assets/img/baby.svg);
} .icon-list { list-style-image: var(--icon);
} .checkbox-list { --icon: checkbox; }
.star-list { --icon: star; }
.baby-list { --icon: baby; }

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

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

Некоторым людям нравится CSS как есть; другие ненавидят работать с глобальным охватом каскада. Я здесь не буду обсуждать CSS-in-JS (достаточно умных людей уже пишут об этом), но пользовательские локальные свойства дают нам фантастическое промежуточное решение.

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

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

<div class="card"> <h2 class="title">This is a card</h2> <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Libero, totam.</p> <button class="button">More info</button>
</div> <div class="cta"> <h2 class="title">This is a call to action</h2> <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Aliquid eveniet fugiat ratione repellendus ex optio, ipsum modi praesentium, saepe, quibusdam rem quaerat! Accusamus, saepe beatae!</p> <button class="button">Buy now</button>
</div>

Если создать стиль для класса .title, он будет стилизовать одновременно и элементы содержащие .card, и классы .cta. Мы можем использовать составной селектор (то есть .card .title), но это повышает специфичность, что может ухудшить поддерживаемость. Или мы можем принять подход BEM и переименовать класс .title в .card__title и .cta__title, чтобы больше изолировать эти элементы.

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

.title { color: var(--title-clr); font-size: var(--title-fs);
} .button { background: var(--button-bg); border: var(--button-border); color: var(--button-text);
}

Затем мы можем контролировать все, что нам нужно, в родительских селекторах соответственно:

.card { --title-clr: #345; --title-fs: 1.25rem; --button-border: 0; --button-bg: #333; --button-text: white;
} .cta { --title-clr: #f30; --title-fs: 2.5rem; --button-border: 0; --button-bg: #333; --button-text: white;
}

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

.button { /* Пользовательские переменные со значениями по умолчанию */ border: var(--button-border, 0); /* По умолчанию: 0 */ background: var(--button-bg, #333); /* По умолчанию: #333 */ color: var(--button-text, white); /* По умолчанию: white */ /* Общие стили, которые имеет любая кнопка */ padding: .5em 1.25em; text-transform: uppercase; letter-spacing: 1px;
}

Мы могли бы даже использовать calc(), чтобы добавить для кнопки масштаб, что потенциально может помочь избавиться от таких классов, как .btn-sm, btn-lg (или эти классы могут собираться в зависимости от ситуации).

.button { font-size: calc(var(--button-scale) * 1rem); /* Умножаем `--button-scale` на `1rem`, чтобы добавить единицу*/
} .cta { --button-scale: 1.5;
}

Вот более детальный обзор всего этого в действии:

Обратите внимание, что в приведенном выше примере я использовал некоторые общие классы, такие как .title и .button, которые имеют стили с локальными областями действия (с помощью резервных вариантов). Настроив для них пользовательские свойства, я могу определить их локально в родительском селекторе, эффективно назначая каждому свой стиль без необходимости дополнительного селектора.

Я также настроил карты прайсов с классами модификаторов для них. Используя универсальный класс .pricing, я все настроил, а затем, используя классы модификаторов, я переопределил некоторые свойства, такие как —text, и —background, не беспокоясь об использовании составных селекторов или дополнительных классов.

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

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

Автор: Kevin Powell

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

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