Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Добавляет статью про Псевдоприватные кастомные свойства #5577

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Базовое решение — Псевдоприватные кастомные свойства — Дока</title>

<style>
* {
box-sizing: border-box;
font-family: sans-serif;
}

.product-list {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;

.product-list__item {
width: 100%;
aspect-ratio: 4/5;

text-align: center;
}
}

.product-card {
display: flex;
justify-content: center;
align-items: center;

background-color: #46ad8e;
border-radius: 12px;
box-shadow: 4px 5px 5px 1px #398f75;

font-size: 48px;
color: #FFFFFF;

&.new {
background-color: #45b9bb;
box-shadow: 4px 5px 5px 1px #3ba0a2;

color: #000000;
}

&.top {
background-color: #ffd700;
box-shadow: 4px 5px 5px 1px #dcbb02;

color: #000000;
}
}
</style>
</head>
<body>
<section class="product-list">
<div class="product-list__item product-card">Карточка 1</div>
<div class="product-list__item product-card new">Карточка 2</div>
<div class="product-list__item product-card top">Карточка 3</div>
</section>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Решение с псевдоприватными свойствами — Псевдоприватные кастомные свойства — Дока</title>

<style>
* {
box-sizing: border-box;
font-family: sans-serif;
}

.product-list {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;

.product-list__item {
width: 100%;
aspect-ratio: 4/5;

text-align: center;
}
}

.product-card {
--_background-color: var(--background-color, #46ad8e);

--_lighter-color: hsl(from var(--_background-color) h s calc(l * 1.2));
--_darker-color: hsl(from var(--_background-color) h s calc(l * 0.8));
--_text-color: hsl(
from var(--_background-color) 0 0 calc((50 - l) * 100)
);

display: flex;
justify-content: center;
align-items: center;

background-color: var(--_background-color);
border-radius: 12px;
box-shadow: 2px 5px 5px 0 var(--_darker-color);

font-size: 48px;
color: var(--_text-color);

&.new {
--background-color: #45b9bb;
}
&.top {
--background-color: #ffd700;
}
}
</style>
</head>
<body>
<section class="product-list">
<div class="product-list__item product-card">Карточка 1</div>
<div class="product-list__item product-card new">Карточка 2</div>
<div class="product-list__item product-card top">Карточка 3</div>
</section>
</body>
</html>
248 changes: 248 additions & 0 deletions css/pseudo-private-custom-property/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
---
title: "Псевдоприватные кастомные свойства"
descriptioin: "Удобный способ работать с кастомными свойствами и использовать их как аргументы миксинов у препроцессоров."
authors:
- alex-andr-19
related:
- tools/preprocessors
- css/layer
- css/cascade
- css/custom-properties
- css/var
tags:
- article
---

Может возникнуть такая ситуация, когда дизайнеры создали классный дизайн для карточки продуктов. Но основной цвет может отличаться в зависимости от типа товара (скидки, популярное, новое и так далее).

В классическом варианте можно добавить уникальные классы каждой карточке:

```html
<section class="product-list">
<div class="product-list__item product-card">Карточка 1</div>
<div class="product-list__item product-card new">Карточка 2</div>
<div class="product-list__item product-card top">Карточка 3</div>
</section>
```

И затем задать стили этим уникальным классам:

```css
.product-list {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;

.product-list__item {
width: 100%;
aspect-ratio: 4/5;
}
}

.product-card {
display: flex;
justify-content: center;
align-items: center;

background-color: #46ad8e;
border-radius: 12px;
box-shadow: 2px 5px 5px 1px #398f75;

font-size: 48px;
color: #FFFFFF;

&.new {
background-color: #45b9bb;
box-shadow: 4px 5px 5px 1px #3ba0a2;

color: #000000;
}

&.top {
background-color: #ffd700;
box-shadow: 4px 5px 5px 1px #dcbb02;

color: #000000;
}
}
```

<iframe title="Красивая карточка" src="demos/products-demo/default.html" height="300"></iframe>

Можно увидеть, что основные цвета (`#46ad8e`, `#45b9bb`, `#ffd700`) влияют на бОльшую часть стилизации карточки: цвет фона, падающей тени и текста.
Каждый раз подбирать нужный цвет тени и определять нужную степень контраста текста будет сложно, особенно когда появится необходимость расширить набор карточек подобного рода.

## Возможное решение

Классическим решением такой задачи может стать использование [кастомных свойств](/css/custom-properties/) и CSS-функции. Недостатки такого подхода станут очевидны после его реализации.

```css
:root {
--card-default: #46ad8e;
--card-new: #45b9bb;
--card-top: #ffd700;
}

.product-card {
display: flex;
justify-content: center;
align-items: center;

background-color: var(--card-default);
border-radius: 12px;
box-shadow: 2px 5px 5px 1px hsl(from var(--card-default) h s calc(l * 0.8));

font-size: 48px;
color: hsl(from var(--card-default) 0 0 calc((50 - l) * 100));

&.new {
background-color: var(--card-new);
box-shadow: 4px 5px 5px 1px hsl(from var(--card-new) h s calc(l * 0.8));

color: hsl(from var(--card-new) 0 0 calc((50 - l) * 100));
}

&.top {
background-color: var(--card-top);
box-shadow: 4px 5px 5px 1px hsl(from var(--card-top) h s calc(l * 0.8));

color: hsl(from var(--card-top) 0 0 calc((50 - l) * 100));
}
}
```

При таком подходе объём кода увеличился и читаемость стала значительно ниже. Также прямое использование кастомных свойств не дало возможности увеличить количество классов.
В таком случае очень хочется применить миксины или функции из препроцессоров.

Приносить в проект препроцессоры для решения задачи перекраски карточек не выглядит как разумное решение.

## Что такое псевдоприватность?

В языках программирования, которые не имеют в своём движке инкапсуляцию полей и методов, принято использовать псевдоприватность, которая основывается на нейминге.
Например, в JavaScript и Python принято писать нижнее подчёркивание перед самим именем поля или метода:

```js
const someObj = {
publicValue: 42,
_privateValue: "42"
}
```
Также существует вариант с подчеркиванием в конце имени.

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

## Псевдоприватность в CSS

Кастомные свойства CSS наследуются внутрь всех дочерних элементов по умолчанию.

Так же нет возможности инкапсулировать свойство для конкреного класса:

```html
<div class="first second"></div>
```

Все кастомные свойства, которые определены в классе `first`, будут также доступны для чтения и изменения в классе `second`.

В этот момент и возникает необходимость псевдоприватности кастомных свойств.

```html
<div class="product-card new">
Карточка 1
</div>
```

```css
.product-card {
--_background-color: #46ad8e;
--_border-radius: 12px;

background-color: var(--_background-color);
border-radius: var(--_border-radius);
box-shadow: 4px 5px 5px 1px hsl(from var(--_background-color) h s calc(l * 0.8));
}

.new {
--background-color: #45b9bb;

box-shadow: 4px 5px 5px 1px hsl(from var(--background-color) h s calc(l * 0.8));
}
```

В данном случае мы видим, что в разных классах существуют незвисимые кастомные свойства. В классе `product-card` свойства «закрыты» псевдоприватностью.
При всём этом цвет тени напрямую зависит от цвета фона в каждом из классов.
Фактически значение [`box-shadow`](/css/box-shadow/) зависит только от кастомного свойства.

## Взаимодействие кастомных свойств с разной приватностью

Так как `--background-color` в классе `new` не «защищено» псевдоприватностью, можно использовать его в смежных классах. Например, в `.product-card`.

```css
.product-card {
--_background-color: var(--background-color);
--_border-radius: 12px;

background-color: var(--_background-color);
border-radius: var(--_border-radius);
box-shadow: 4px 5px 5px 1px hsl(from var(--_background-color) h s calc(l * 0.8));
}

.new {
--background-color: #45b9bb;
}
```

Таким образом мы передали значение цвета фона в `.product-card` из `.new`.
Но обязательно нужно предохраниться от ситуации, в которой переменная `--background-color` не была создана в смежных классах:

```css
.product-card {
--_background-color: var(--background-color, #45b9bb);
...
}
```

## Как псевдоприватность может заменить миксины препроцессоров?

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

Аналогия с миксинами:

1. Имя миксина — имя атомарного класса.
1. Стили миксина — стили атомарного класса.
1. Аргумент — кастомное свойство, принимаемое псевдоприватным.
1. Внутренняя переменная — псевдоприватное кастомное свойство.

## Решение поставленной задачи

```css
.product-card {
/* Псевдоприватное кастомное свойство */
--_background-color: var(--background-color, #46ad8e);
/* ---------------------------------- */

display: flex;
justify-content: center;
align-items: center;

background-color: var(--_background-color);
border-radius: 12px;
box-shadow: 2px 5px 5px 0 hsl(from var(--_background-color) h s calc(l * 0.8));

font-size: 48px;
color: hsl(from var(--_background-color) 0 0 calc((50 - l) * 100));

&.new {
/* Значение псевдоприватного кастомного свойства */
--background-color: #45b9bb;
/* --------------------------------------------- */
}
&.top {
/* Значение псевдоприватного кастомного свойства */
--background-color: #ffd700;
/* --------------------------------------------- */
}
}
```

<iframe title="Красивая карточка" src="demos/products-demo/" height="300"></iframe>
6 changes: 6 additions & 0 deletions people/alex-andr-19/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
name: 'Алекс'
url: https://github.com/Alex-Andr-19
badges:
- first-contribution-small
---
Loading