Введение
СкопированоНачнём с простого. Что такое псевдоприватность?
В языках программирования, которые не имеют в своём движке инкапсуляцию, для защиты полей и методов принято использовать соглашение о нейминге.
Например, в JavaScript и Python принято писать нижнее подчёркивание перед самим именем поля или метода:
const someObj = { publicValue: 42, _privateValue: "42"}
const someObj = { publicValue: 42, _privateValue: "42" }
Также существует вариант с подчёркиванием в конце имени.
Псевдоприватность в CSS
СкопированоКастомные свойства CSS наследуются внутрь всех дочерних элементов по умолчанию. Определив кастомное свойство в родительском элементе, мы сможем переиспользовать его в любом дочернем.
Также нет возможности инкапсулировать свойство для конкретного класса:
<div class="first second"></div>
<div class="first second"></div>
Все кастомные свойства, которые определены в классе first
, будут также доступны для чтения и изменения в классе second
и наоборот.
Чтобы защитить свойства от чтения и записи, возникает необходимость использовать псевдоприватность кастомных свойств.
.first { --_background-color: darkgreen; background-color: var(--_background-color); color: var(--color);}.second { --background-color: blue; --color: white;}
.first { --_background-color: darkgreen; background-color: var(--_background-color); color: var(--color); } .second { --background-color: blue; --color: white; }
Здесь цвет фона блока не будет изменён из класса second
, а останется таким, каким его определил first
.
Применение
СкопированоПсевдоприватность позволяет делать защищённые утилитарные классы и оставлять «снаружи» только «аргументы».
Например, утилитарный класс rect
описывает параметры квадратного блока, а «снаружи» через класс logo
можно контролировать только его размер:
<img src="/img/logo.svg" class="logo rect-size"> />
<img src="/img/logo.svg" class="logo rect-size"> />
.rect-size { --_size: var(--size, 24px); min-width: var(--_size); min-height: var(--_size); width: var(--_size); height: var(--_size); max-width: var(--_size); max-height: var(--_size); aspect-ratio: 1;}.logo { --size: 32px;}
.rect-size { --_size: var(--size, 24px); min-width: var(--_size); min-height: var(--_size); width: var(--_size); height: var(--_size); max-width: var(--_size); max-height: var(--_size); aspect-ratio: 1; } .logo { --size: 32px; }
Или ограничим количество отображаемых строк, оставив возможность указать нужное через «аргумент»:
<p class="description clamp-text-lines"> И восстали машины из пепла ядерного огня, и пошла война на уничтожение человечества. И шла она десятилетия, но последнее сражение состоится не в будущем, оно состоится здесь, в наше время, сегодня ночью.</p>
<p class="description clamp-text-lines"> И восстали машины из пепла ядерного огня, и пошла война на уничтожение человечества. И шла она десятилетия, но последнее сражение состоится не в будущем, оно состоится здесь, в наше время, сегодня ночью. </p>
.clamp-text-lines { --_lines-count: var(--lines-count, 3); display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--_lines-count); overflow: hidden;}.description { --lines-count: 2;}
.clamp-text-lines { --_lines-count: var(--lines-count, 3); display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--_lines-count); overflow: hidden; } .description { --lines-count: 2; }
Перечисленные «утилитарные» классы можно расположить в одном файле, например, utilities
и сразу подключить его к index
. Так, просто применив класс к DOM-элементу, мы получим ожидаемое поведение, которое каждый раз прописывать руками было бы неприятно.
Применение псевдоприватности даёт:
Скопировано- Изоляцию стилей на уровне CSS, без экспериментальных технологий.
- Гибкость в использовании.
- Возможность переписать миксины препроцессоров на нативный CSS.
Реальная задача
СкопированоМожет возникнуть такая ситуация, что дизайнеры создали классный дизайн для карточек продуктов, у которых цвет отличается в зависимости от типа товара (скидки, популярное, новое и т. д.).
В классическом варианте решения можно добавить уникальные классы каждой карточке:
<section class="product-list"> <div class="product-list__item product-card">Глубина шторма</div> <div class="product-list__item product-card new">Электро-лазурь</div> <div class="product-list__item product-card top">Сахарная вата</div></section>
<section class="product-list"> <div class="product-list__item product-card">Глубина шторма</div> <div class="product-list__item product-card new">Электро-лазурь</div> <div class="product-list__item product-card top">Сахарная вата</div> </section>
И затем задать стили этим уникальным классам:
.product-card { display: flex; justify-content: center; align-items: center; background-color: #123E66; box-shadow: 10px 10px 0 0 #0D2B47; font-size: 18px; color: #FFFFFF; &.new { background-color: #2E9AFF; box-shadow: 10px 10px 0 0 #006DD3; color: #000000; } &.top { background-color: #F498AD; box-shadow: 10px 10px 0 0 #E92D58; color: #000000; }}
.product-card { display: flex; justify-content: center; align-items: center; background-color: #123E66; box-shadow: 10px 10px 0 0 #0D2B47; font-size: 18px; color: #FFFFFF; &.new { background-color: #2E9AFF; box-shadow: 10px 10px 0 0 #006DD3; color: #000000; } &.top { background-color: #F498AD; box-shadow: 10px 10px 0 0 #E92D58; color: #000000; } }
Можно увидеть, что основные цвета (#123
, #2
, #
) влияют на большую часть стилизации карточки: цвет фона, падающей тени и текста.
Каждый раз подбирать нужный цвет тени и определять нужную степень контраста текста будет сложно, особенно если появится необходимость расширить набор карточек подобного рода.
Возможное решение
СкопированоРешением такой задачи может стать использование кастомных свойств и CSS-функций. Недостатки такого подхода станут очевидны после его реализации:
:root { --card-default: #123E66; --card-new: #2E9AFF; --card-top: #F498AD;}.product-card { display: flex; justify-content: center; align-items: center; background-color: var(--card-default); box-shadow: 10px 10px 0 0 hsl(from var(--card-default) h s calc(l * 0.7)); font-size: 18px; color: hsl(from var(--card-default) 0 0 calc((50 - l) * 100)); &.new { background-color: var(--card-new); box-shadow: 10px 10px 0 0 hsl(from var(--card-new) h s calc(l * 0.7)); color: hsl(from var(--card-new) 0 0 calc((50 - l) * 100)); } &.top { background-color: var(--card-top); box-shadow: 10px 10px 0 0 hsl(from var(--card-top) h s calc(l * 0.7)); color: hsl(from var(--card-top) 0 0 calc((50 - l) * 100)); }}
:root { --card-default: #123E66; --card-new: #2E9AFF; --card-top: #F498AD; } .product-card { display: flex; justify-content: center; align-items: center; background-color: var(--card-default); box-shadow: 10px 10px 0 0 hsl(from var(--card-default) h s calc(l * 0.7)); font-size: 18px; color: hsl(from var(--card-default) 0 0 calc((50 - l) * 100)); &.new { background-color: var(--card-new); box-shadow: 10px 10px 0 0 hsl(from var(--card-new) h s calc(l * 0.7)); color: hsl(from var(--card-new) 0 0 calc((50 - l) * 100)); } &.top { background-color: var(--card-top); box-shadow: 10px 10px 0 0 hsl(from var(--card-top) h s calc(l * 0.7)); color: hsl(from var(--card-top) 0 0 calc((50 - l) * 100)); } }
Объём кода увеличился и читаемость стала значительно ниже. Также прямое использование кастомных свойств не дало возможности увеличить количество классов.
В таком случае очень хочется применить миксины или функции из препроцессоров, но приносить в проект препроцессоры для решения задачи перекраски карточек не выглядит как разумное решение.
Решаем задачу при помощи псевдоприватности
Скопировано.product-card { --_background-color: #123E66; background-color: var(--_background-color); box-shadow: 10px 10px 0 0 hsl(from var(--_background-color) h s calc(l * 0.7));}.new { --background-color: #2E9AFF; box-shadow: 10px 10px 0 0 hsl(from var(--background-color) h s calc(l * 0.7));}
.product-card { --_background-color: #123E66; background-color: var(--_background-color); box-shadow: 10px 10px 0 0 hsl(from var(--_background-color) h s calc(l * 0.7)); } .new { --background-color: #2E9AFF; box-shadow: 10px 10px 0 0 hsl(from var(--background-color) h s calc(l * 0.7)); }
В данном случае в разных классах существуют независимые кастомные свойства -
и -
. При этом в классе product
свойство «закрыто» псевдоприватностью и не может быть изменено из класса new
.
При всём этом цвет тени напрямую зависит от цвета фона в каждом из классов. Фактически значение box
зависит только от кастомного свойства.
Взаимодействие кастомных свойств с разной приватностью
СкопированоТак как -
в классе new
не «защищено» псевдоприватностью, можно использовать его в смежных классах. Например, в .product
:
.product-card { --_background-color: var(--background-color); background-color: var(--_background-color); box-shadow: 10px 10px 0 0 hsl(from var(--_background-color) h s calc(l * 0.7));}.new { --background-color: #2E9AFF;}
.product-card { --_background-color: var(--background-color); background-color: var(--_background-color); box-shadow: 10px 10px 0 0 hsl(from var(--_background-color) h s calc(l * 0.7)); } .new { --background-color: #2E9AFF; }
Таким образом мы передали значение цвета фона в .product
из .new
.
Но обязательно нужно предусмотреть ситуацию, в которой переменная -
не была создана в смежных классах и задать значение по умолчанию:
.product-card { --_background-color: var(--background-color, #123E66); ...}
.product-card { --_background-color: var(--background-color, #123E66); ... }
Как псевдоприватность может заменить миксины препроцессоров?
СкопированоВыше описан способ «передачи» кастомных свойств в другие между смежными классами. Это можно сравнить с передачей аргументов функции или, в контексте стилей, миксину препроцессора.
Аналогия с миксинами:
- Имя миксина — имя атомарного класса.
- Стили миксина — стили атомарного класса.
- Аргумент — кастомное свойство, принимаемое псевдоприватным.
- Внутренняя переменная — псевдоприватное кастомное свойство.
Решение поставленной задачи
Скопировано.product-card { /* Псевдоприватное кастомное свойство */ --_background-color: var(--background-color, #123E66); /* ---------------------------------- */ display: flex; justify-content: center; align-items: center; background-color: var(--_background-color); box-shadow: 2px 5px 5px 0 hsl(from var(--_background-color) h s calc(l * 0.7)); font-size: 48px; color: hsl(from var(--_background-color) 0 0 calc((50 - l) * 100)); &.new { /* Значение псевдоприватного кастомного свойства */ --background-color: #2E9AFF; /* --------------------------------------------- */ } &.top { /* Значение псевдоприватного кастомного свойства */ --background-color: #F498AD; /* --------------------------------------------- */ }}
.product-card { /* Псевдоприватное кастомное свойство */ --_background-color: var(--background-color, #123E66); /* ---------------------------------- */ display: flex; justify-content: center; align-items: center; background-color: var(--_background-color); box-shadow: 2px 5px 5px 0 hsl(from var(--_background-color) h s calc(l * 0.7)); font-size: 48px; color: hsl(from var(--_background-color) 0 0 calc((50 - l) * 100)); &.new { /* Значение псевдоприватного кастомного свойства */ --background-color: #2E9AFF; /* --------------------------------------------- */ } &.top { /* Значение псевдоприватного кастомного свойства */ --background-color: #F498AD; /* --------------------------------------------- */ } }