Алло, мы с нижнего этажа, у вас стили протекают

Никита Дубко, HR Tech Яндекса

Алло, мы с нижнего этажа,
у вас стили протекают

Никита Дубко, HR Tech Яндекса

Никита
Дубко

2013 год
Нужно быстро сверстать страничку!
Руководитель
Мы потом обязательно переделаем нормально.
Руководитель
Перекрась ссылки в навигации, плиз.
Руководитель
.nav-link {
    color: lightsteelblue;
}

😕 Не работает

.navbar-inverse .navbar-nav > li > .nav-link {
    color: lightsteelblue;
}

😐 Работает

.nav-link {
    color: lightsteelblue !important;
}

😬 Тоже работает

😢 И так везде?

.navbar-inverse .navbar-nav > li > a {
    color: #9d9d9d;
}

.link { color: red; }

.nav-link { color: lightsteelblue; }
a:-webkit-any-link {
    color: -webkit-link;
    cursor: pointer;
    text-decoration: underline;
}

⬇️ Сверху вниз

Селектор Свойство Значение
.navbar-inverse .navbar-nav > li > a color #9d9d9d
.link color red
.nav-link color lightsteelblue
a:-webkit-any-link color -webkit-link
a:-webkit-any-link cursor pointer
a:-webkit-any-link text-decoration underline
ID :pseudo,
.class,
[attr]
::pseudo,
element
Source
.navbar-inverse .navbar-nav > li > a 0 2 2 Dev
.link 0 1 0 Dev
.nav-link 0 1 0 Dev
a:-webkit-any-link 0 1 1 Browser

⬇️ Сверху вниз

Селектор Свойство Значение Вес
.navbar-inverse .navbar-nav > li > a color #9d9d9d 0,2,2
.link color red 0,1,0
.nav-link color lightsteelblue 0,1,0
a:-webkit-any-link color -webkit-link 0,1,1
a:-webkit-any-link cursor pointer 0,1,1
a:-webkit-any-link text-decoration underline 0,1,1

⬆️ Снизу вверх

Селектор Свойство Значение Вес
.nav-link color lightsteelblue 0,1,0
.link color red 0,1,0
.navbar-inverse .navbar-nav > li > a color #9d9d9d 0,2,2
a:-webkit-any-link color -webkit-link 0,1,1
a:-webkit-any-link cursor pointer 0,1,1
a:-webkit-any-link text-decoration underline 0,1,1

🔀 Сортируем

Селектор Свойство Значение Вес
.navbar-inverse .navbar-nav > li > a color #9d9d9d 0,2,2
.nav-link color lightsteelblue 0,1,0
.link color red 0,1,0
a:-webkit-any-link color -webkit-link 0,1,1
a:-webkit-any-link cursor pointer 0,1,1
a:-webkit-any-link text-decoration underline 0,1,1
<nav class="navbar-inverse">
    <ul class="navbar-nav">
        <li><a href="#" class="nav-link">Link 1</a></li>
        <li><a href="#" class="nav-link">Link 2</a></li>
    </ul>
</nav>
.navbar-inverse .navbar-nav > li > a ✅ color #9d9d9d
.nav-link ❌ color lightsteelblue
.link ❌ color red
a:-webkit-any-link ❌ color -webkit-link
a:-webkit-any-link ✅ cursor pointer
a:-webkit-any-link ✅ text-decoration underline
WWW-аппликация / Никита Дубко

Нужно учитывать

Никто не знает CSS: специфичность — не каскад

Как сделать хорошо?

1. Избавляемся от веса

Единая специфичность (0, 1, 0)

.one {
    /* ... */
}

.two {
    /* ... */
}

:hover, :focus?

(0, 2, 0)

.one {
    /* ... */
}

.one:hover {
    /* ... */
}

А с вложенностью что?

<nav class="b-nav">
    <ul class="b-list">
        <li class="b-list-item">
            <a href="#" class="b-link">Ссылка</a>
        </li>
    </ul>
</nav>

Абсолютно
независимые
блоки

Вёрстка независимыми блоками

😭 Придумать уникальное имя

💡 Правила именования

<nav class="nav">
    <ul class="nav__list">
        <li class="nav__list-item">
            <a href="#" class="nav__link">
                Ссылка 1
            </a>
        </li>
        <li class="nav__list-item">
            <a href="#" class="nav__link nav__link--active">
                Ссылка 2
            </a>
        </li>
    </ul>
</nav>

💡 Правила именования файлов

/* nav.css */

.nav { /* блок */ }
.nav__list { /* элемент */ }
.nav__list-item { /* элемент */ }
.nav__link { /* элемент */ }
.nav__link--active { /* модификатор */ }

А если вложенные блоки?

<nav class="nav">
    <ul class="nav__list list">
        <li class="list__item">
            <a href="#" class="list__item-link link">
                <span class="link__text">Ссылка</span>
            </a>
        </li>
    </ul>
</nav>

Ещё одна договорённость

👑 Наследуемые свойства?
font-size, color?

Ещё одна договорённость

🧸 БЭМ

БЭМ

БЭМ, OOCSS, SMACSS...

Дизайнер

Верстальщик


Бэкенд-разработчик

Дизайн-блоки

Компоненты


Приложение

✅ Профит!

✅ Плюсы БЭМ & Co

❌ Минусы БЭМ & Co

Большой бандл?

.card {
    box-sizing: border-box;
    padding: var(--token-padding-s);

    display: flex;
    flex-direction: column;
}

.list {
    display: flex;
    flex-direction: column;
}

Нашёл в Поиске

🗜️ Но есть же gzip!

Yes, But Official

💡 Блоки-утилиты

🧐 Хочу текст
размером 16px

.fs-16 {
    font-size: 16px;
}

🧐 Хочу стилизовать ::before и :hover

.fs-16\:\:b,
.fs-16\:h {
    font-size: 16px;
}

⚛️ AtomicCSS

Основные идеи

😐 HTML же раздуется!

header__list-item link link--active

fs-16 c-red с-blue:h m-0 mb-12 p-0

🤔 А как рефакторить?

// Notification.jsx

function Notification({ imageUrl, imageAlt, title, message }) {
    return (
        <div className="p-6 max-w-sm mx-auto rounded-xl flex items-center space-x-4">
            <div className="shrink-0">
                <img className="h-12 w-12" src={imageUrl.src} alt={imageAlt}>
            </div>
            <div>
                <div className="text-xl font-medium text-black">{title}</div>
                <p className="text-slate-500">{message}</p>
            </div>
        </div>
    )
}
Reusing Styles

🤔 Но я просто хочу одинаковые кнопки

@tailwind base;
@tailwind components;
@tailwind utilities;

.btn-primary {
    @apply py-2 px-4 bg-blue-500 text-white font-semibold
        rounded-lg shadow-md hover:bg-blue-700
        focus:outline-none focus:ring-2
        focus:ring-blue-400 focus:ring-opacity-75;
}

♒️ TailwindCSS

🥴 Новый выдуманный синтаксис

🧐 Хочу новые фичи CSS!

⏳ Хоти

Tailwind CSS v3.2

🥹 Всегда новое будет появляться быстро?

В браузерах — да 😁

Во фреймворках — не факт 🤷‍♂️

💎 Редкие свойства?

Из коробки — нет

👍 Но можно добавить
свой класс

🥹 Хочу собрать демку для дебага на codepen

⏳ Хоти

Chrome DevTools — спрятанные полезности / Никита Дубко

✅ Плюсы TailwindCSS

❌ Минусы TailwindCSS

Classic rock, Mario Kart, and why we can't agree on Tailwind
Давай подключим карусельку!
Заказчик
И дейтпикер!
Заказчик
И Material, у гугла видел!
Заказчик

😐 Внешний мир ничего вам не должен

Hacker News

А если я разработчик библиотеки?

🤔 Как обычно подключают библиотеку?

Нужна изоляция

А если на JS?

import styled from 'styled-components'

const Container = styled.div`
    background-color: #2b2b2b;
    border-radius: 5px;
`
const Title = styled.h1`
    font-weight: 300;
`
export const SimpleComponent = () => (
    <Container>
        <Title>Styled Component</Title>
    </Container>
)
{
    "color": {
        "primary": "#ff0000",
        "secondary": "#cc6600"
    },
    "typography": {
        "fontSize": {
            "sm": "12px",
            "m": "14px",
            "l": "16px"
        }
    }
}

🔥 Удобно!

style="width: NaNpx; color: undefined"
class="undefined"

CSS

CSS-in-JS

💡 Автоматические уникальные классы

import styles from './App.css';

class App extends Component {
    render() {
        return (
            <div className={ styles.app }>
                <div className={ styles.header } />
                <p className={ styles.intro }>
                    Hello!
                </p>
            </div>
        );
    }
}

⚙️ CSS Modules

CSS Modules

✅ Плюсы CSS Modules

❌ Минусы CSS Modules

Какой процесс я хочу

🔍 Инструментов полно!

Какой процесс я хочу

А что там в CSS?

😒 Нет контроля

Что изменилось?

🏹 Сквозное API

--color-primary

:root {
    --base-text-primary: #2F3337;
}
.component {
    color: var(--base-text-primary);
}
Токены в дизайн-системах / Юрий Ветров
Houdini — великий разоблачитель, Никита Дубко

🪄 @property

@property
window.CSS.registerProperty({
    name: "--my-color",
    syntax: "<color>",
    inherits: false,
    initialValue: "#c0ffee",
});
@property --my-color {
    syntax: "<color>";
    inherits: false;
    initial-value: #c0ffee;
}

😒 Оно же не работает

🗓️ 24 октября 2023

😈 Ломаем наследование

@property --color-text {
    syntax: "<color>";
    inherits: false;
    initial-value: red;
}

* {
    color: var(--color-text);
}

.text {
    --color-text: yellow;
}
<div class="card">
    Parent Card
    <p class="text">
        Text of P
        <span>Span inside of P</span>
    </p>
</div>

А что там с каскадом?

@layer

@layer reset, bootstrap, utilities;
@import url('bootstrap.css') layer(bootstrap);

@layer utilities {
    .sr-only { /* ... */ }
}

@layer reset {
    *, *::before, *::after {
        box-sizing: border-box;
    }
}

p { font-size: 18px; }
<p id="id" class="class">Text</p>
@layer first, second;
@layer second {
    p { color: yellow; }
    #id { color: red; }
}
@layer first {
    .class { color: blue; }
}
p { color: green; }

⬇️ Сверху вниз

Слой Селектор Свойство Вес
second p color: yellow 0,0,1
second #id color: red 1,0,0
first .class color: blue 0,1,0
undefined p color: green 0,0,1

🔀 Сортируем слои

Слой Селектор Свойство Вес
undefined p color: green 0,0,1
first .class color: blue 0,1,0
second p color: yellow 0,0,1
second #id color: red 1,0,0

🔀 Сортируем внутри слоёв

Слой Селектор Свойство Вес
undefined p color: green 0,0,1
first .class color: blue 0,1,0
second #id color: red 1,0,0
second p color: yellow 0,0,1

⏺️ Ищем победителя

Слой Селектор Свойство Вес
undefined p color: green 0,0,1
first .class color: blue 0,1,0
second #id color: red 1,0,0
second p color: yellow 0,0,1
@layer default {
    a { color: maroon; }
}

@layer theme {
    a {
        color: var(--brand-primary, purple);
    }

    .no-theme {
        color: revert-layer;
    }
}
@import url('bootstrap.css') layer(bootstrap);

button {
    font-size: 18px;
}
@layer utilities {
    @layer common, important;

    @layer important { /* ... */ }

    @layer common { /* ... */ }
}
@layer utilities.common {
    /* ... */
}

😒 Сказки какие-то...

🤩 Уже можно

A Complete Guide to CSS Cascade Layers
CSS @layer visualizer

А что там с изоляцией?

@scope

<div class="card">

    <style scoped>
        h2 { font-weight: 300; }
    </style>

    <h2>Title</h2>

    <img src="image.jpg" alt=""/>
</div>
<div className="card">

    <style scoped>
        h2 { font-weight: 300; }
    </style>

    <h2>Title</h2>

    <img src="image.jpg" alt=""/>
</div>
<div class="red">
    <div class="green">
        <p>Text</p>
    </div>
</div>
.green p {
    color: green;
}

.red p {
    color: red;
}

RED

<div class="red">
    <div class="green">
        <p>Text</p>
    </div>
</div>
@scope(.green) {
    p { color: green; }
}

@scope(.red) {
    p { color: red; }
}

GREEN

A quick introduction to CSS @scope
@scope(.library) {
    .btn {
        background-color: green;
    }
}

.btn {
    background-color: red;
}
@scope (.card) to (.content) {
    p {
        color: var(--color-card);
    }
}
<div class="card">
    <p>Some Text</p>
    <div class="content">
        <h3>Title</h3>
        <p>Content</p>
    </div>
</div>
<div className="card">

    <style scoped>
        h2 { font-weight: 300; }
    </style>

    <h2>Title</h2>

    <img src="image.jpg" alt=""/>
</div>
<div className="card">
    <style>
        @scope {
            h2 { font-weight: 300; }
        }
    </style>
    <h2>Title</h2>

    <img src="image.jpg" alt=""/>
</div>

🥹 И это тоже везде работает?

😭 Обманул

WebKit Standards Positions

🤷‍♂️ Firefox

Scope Proposal & Explainer

Подытожим

Контроль

💪 CSS ещё могёт!

Берегите себя!

mefody.dev/talks/css-isolation/
@dark_mefody
t.me/mefody_dev QR-код на голосование