- BrainTools - https://www.braintools.ru -
Представьте, что вы — разумный человек. Вы знаете, что CSS — это язык стилей. Cascading Style Sheets. Для оформления. Не для логики. Не для программирования. Просто цвета, шрифты, отступы.
А потом вы заходите на CodePen.
И там кто-то сделал полностью рабочий калькулятор. На чистом CSS. Без JavaScript. Кто-то другой запрограммировал игру в крестики-нолики. С искусственным интеллектом [1]. На CSS. Третий создал 3D-лабиринт с first-person камерой. Управление клавиатурой. Опять же — на CSS.
И вы думаете: “Что за чёрт? Как это вообще работает?”
Добро пожаловать в мир CSS-программирования. Это место, где люди делают вещи не потому, что они нужны, а потому что это технически возможно. Где checkbox становится ячейкой памяти [2]. Где :checked — это ваш Boolean. Где общее количество строк кода больше, чем если бы вы просто написали на JavaScript. Но зато — без JavaScript!
Сейчас я покажу вам, как работает эта магия. И почему это одновременно гениально и абсурдно.
Вся эта “магия” держится на одном хитром приёме, который называется Checkbox Hack. Это краеугольный камень всей CSS-логики. Без него ничего бы не работало.
Как это работает:
Берёте <input type="checkbox"> (или <input type="radio">)
Скрываете его визуально через CSS: position: absolute; left: -9999px;
Рядом ставите <label>, привязанный к чекбоксу через атрибут for
Пользователь кликает на label — чекбокс меняет состояние
Используете псевдокласс :checked и селекторы (+, ~) для изменения стилей
Переводя на человеческий:
<input type="checkbox" id="toggle" class="hidden">
<label for="toggle">Кликни меня</label>
<div class="content">Этот контент появится/исчезнет</div>
.hidden {
position: absolute;
left: -9999px;
}
.content {
display: none;
}
#toggle:checked ~ .content {
display: block;
}
Бац! У вас есть интерактивность. Без JavaScript. Чекбокс — это ваша переменная Boolean. Он может быть true (checked) или false (unchecked). Это 1 бит памяти.
И тут начинается цирк.
Потому что если у вас есть 1 бит памяти, вы можете сделать 8 бит. А если у вас 8 бит, вы можете сделать 64. А если у вас 64 бита — у вас уже есть всё, что нужно для калькулятора.
Давайте посмотрим, что люди создали, используя этот простой трюк.
Это стандартное применение. Нормальное. Даже полезное.
#tab1:checked ~ .tab-content-1,
#tab2:checked ~ .tab-content-2 {
display: block;
}
Кликнул на таб — открылся контент. Ничего сверхъестественного. Любой разумный разработчик скажет: “Окей, это можно использовать для простых случаев”. Chris Coyier с CSS-Tricks даже написал статью об этом ещё в 2012 году, и она актуальна до сих пор.
Вердикт: Полезно. Легкое. Работает.
Опять же, вполне разумно. Дефолтные чекбоксы уродливы. Хочется красиво. Скрываете оригинальный чекбокс, рисуете свой через ::before и ::after. Используете :checked для изменения стилей.
input[type="checkbox"] {
position: absolute;
opacity: 0;
}
label::before {
content: '';
width: 50px;
height: 25px;
background: #ccc;
border-radius: 25px;
transition: 0.3s;
}
input:checked + label::before {
background: #4CAF50;
}
Результат: Красивые переключатели в стиле iOS, Android, или любой другой дизайн-системы.
Вердикт: Совершенно нормально. Нет вопросов.
Тоже вполне адекватное использование. Особенно для мобильных меню. Скрытый чекбокс — label в виде “гамбургера” — меню выезжает сбоку при клике.
#menu-toggle:checked ~ nav {
left: 0;
/* Меню выезжает из-за экрана */
}
Проблема одна: если пользователь кликнет вне меню, оно не закроется. Потому что у чекбокса нет события “клик вне элемента”. Для этого нужен JavaScript. Но для 80% случаев — работает отлично.
Вердикт: Разумное применение. Легковесное решение.
И вот тут начинается безумие.
Да. Полностью рабочий калькулятор. Вы можете складывать, вычитать, умножать, делить. Есть дробные числа. Есть дисплей, который показывает результат.
Как это вообще работает?
Ключевая фича: CSS counters. Это механизм для автоматической нумерации элементов (как в упорядоченных списках). Но оказывается, counters можно использовать для вычислений.
body {
counter-reset: result 0;
}
.set-number {
counter-increment: result 5;
/* Увеличиваем счётчик на 5 */
}
.display::after {
content: counter(result);
/* Выводим значение счётчика */
}
Проблема: counters могут хранить только целые числа. Для дробных чисел приходится разделять число на целую и дробную части, хранить их отдельно, а потом склеивать через content.
Второй прикол: вся логика [3] калькулятора реализована через тысячи селекторов CSS. Буквально. Для каждой комбинации “цифра + операция + цифра” написан отдельный селектор.
#num1:checked ~ #num2:checked ~ #add:checked ~ .result {
counter-increment: result 3;
/* 1 + 2 = 3 */
}
Вердикт: Технически впечатляющее. Практически бесполезное. Но доказывает, что CSS может в арифметику.
Это уже хардкор. Люди создали:
Крестики-нолики (с AI-противником на основе захардкоженных ходов)
Арканоид (отбивание шарика платформой)
Лабиринты (управление клавишами)
Whack-a-Mole (бей крота)
Стрелялки (типа Duck Hunt)
Тетрис (вращение фигур и стакинг)
Шахматы (полностью рабочие, но без проверки правил)
Принцип работы:
Каждая клетка игрового поля — это radio button
Клик по label перемещает “персонажа” (меняет состояние radio button)
:checked селектор показывает/скрывает элементы в зависимости от текущей позиции
CSS animations создают движение (враги, объекты)
Таймер игры — CSS animation-delay (например, “game over” появляется через 100 секунд)
Пример из игры “Kill the Birds”:
.game-over {
height: 0;
opacity: 0;
animation: curtain 0.6s ease-in;
animation-delay: 100s;
/* Появляется через 100 секунд */
animation-fill-mode: forwards;
}
@keyframes curtain {
to {
height: 100vh;
opacity: 1;
}
}
Счётчик времени:
.timer::before {
content: '9';
animation: countdown 10s step-end;
/* Прыгает с 9 до 0 */
}
@keyframes countdown {
0% { content: '9'; }
10% { content: '8'; }
20% { content: '7'; }
/* ... и так далее */
90% { content: '0'; }
}
Вердикт: Это уже не CSS. Это какой-то боевой язык ассемблера, замаскированный под стили.
Да, вы правильно поняли. Люди создали полноценные 3D-сцены на CSS. С перспективой. С глубиной. С анимацией.
Основные инструменты:
perspective: 1000px; — устанавливает “камеру”
transform-style: preserve-3d; — сохраняет 3D-пространство для дочерних элементов
transform: rotateX() rotateY() rotateZ(); — вращение в 3D
transform: translateZ(); — движение по оси Z (вглубь/на зрителя)
Пример 3D-куба:
.cube {
width: 200px;
height: 200px;
position: relative;
transform-style: preserve-3d;
animation: rotateCube 10s infinite linear;
}
.face {
position: absolute;
width: 200px;
height: 200px;
background: rgba(255, 0, 0, 0.7);
}
.front { transform: translateZ(100px); }
.back { transform: rotateY(180deg) translateZ(100px); }
.right { transform: rotateY(90deg) translateZ(100px); }
.left { transform: rotateY(-90deg) translateZ(100px); }
.top { transform: rotateX(90deg) translateZ(100px); }
.bottom { transform: rotateX(-90deg) translateZ(100px); }
@keyframes rotateCube {
to { transform: rotateX(360deg) rotateY(360deg); }
}
Но это ещё не всё. Кто-то создал первое лицо 3D-мир на CSS. С комнатами. С дверями. С навигацией клавишами. Через :target селектор (который срабатывает при изменении URL хеша).
#room1:target ~ .scene {
transform: translate3d(0, 0, 0);
}
#room2:target ~ .scene {
transform: translate3d(-2000px, 0, 0);
}
Вердикт: CSS превратился в движок рендеринга 3D-графики. Страшно представить, что будет дальше.
Давайте разберёмся, почему CSS вообще способен на это.
Чекбокс имеет два состояния: checked и unchecked. Это 1 бит информации. Если у вас есть 10 чекбоксов, у вас 10 бит. Если 100 — 100 бит.
Но тут есть нюанс. В классическом программировании вы можете менять значение переменной когда угодно. В CSS — только через взаимодействие пользователя. Вы не можете написать:
/* Это не работает */
#checkbox1:checked {
#checkbox2: checked;
}
Вы можете только показать/скрыть элементы в зависимости от состояния чекбокса.
#toggle:checked ~ .element {
display: block;
}
Это эквивалент:
if (toggle.checked) {
element.style.display = 'block';
}
Чем больше комбинаций чекбоксов, тем сложнее “логика”:
#a:checked ~ #b:checked ~ #c:not(:checked) ~ .result {
/* Если A и B включены, но C выключен */
background: red;
}
Это if (a && b && !c).
counter-reset: total 0;
counter-increment: total 5;
content: counter(total);
Это переменная + операция инкремента. Можно складывать, вычитать (через отрицательные инкременты), умножать (через несколько инкрементов подряд).
Проблема: нельзя делать деление и дробные числа напрямую. Приходится изобретать костыли.
animation: event 5s forwards;
animation-delay: 10s;
Это setTimeout(event, 10000). Анимация запускается через 10 секунд, выполняется 5 секунд, и остаётся в конечном состоянии.
#page1:target ~ .content-1 { display: block; }
#page2:target ~ .content-2 { display: block; }
Это SPA (Single Page Application) без JavaScript. URL меняется — контент меняется.
1. Нет зависимости от JavaScript
Если пользователь отключил JS (или у него старый браузер, или он использует Tor), ваше приложение всё равно работает. Accessibility на максимум.
2. Нет runtime-ошибок
CSS не крашится. Если селектор неправильный — элемент просто не изменится. Нет “undefined is not a function”. Нет “Cannot read property of null”.
3. Hardware acceleration
CSS transforms и animations обрабатываются GPU. Это значит 60 FPS даже на мобильных устройствах. JavaScript animations часто тормозят.
4. Это охренеть как весело
Серьёзно. Создать калькулятор на CSS — это как решать головоломку. Это челлендж. Это доказательство, что вы можете.
1. Нечитаемый код
Попробуйте прочитать 5000 строк CSS-селекторов для калькулятора. Это ад. Отладка невозможна. Поддержка невозможна.
2. Огромный размер файла
CSS-калькулятор весит больше, чем JavaScript-версия. CSS-игра “The Mine” — 10,000+ строк SCSS. После компиляции — 20,000+ строк CSS.
3. Отсутствие переменных и циклов
В нормальных языках вы пишете:
for (let i = 0; i < 100; i++) {
createEnemy(i);
}
В CSS вы пишете 100 отдельных правил вручную. Или генерируете их препроцессором (что уже не “чистый CSS”).
4. Невозможность динамической логики
Вы не можете изменить поведение [4] в runtime. Всё должно быть заранее прописано в CSS. Для игры это значит: все возможные ходы и состояния должны быть захардкожены.
5. Семантика HTML ломается
Вы используете <input type="checkbox"> не как чекбокс, а как переменную Boolean. Это нарушает семантику. Screen readers для слепых пользователей будут в шоке: “Why are there 50 checkboxes on this page?!”
6. Это медленно
Да, CSS animations — hardware accelerated. Но если у вас 100 чекбоксов и тысячи селекторов, браузер начинает тупить. Потому что каждый клик пересчитывает все селекторы для всех элементов.
Давайте посмотрим на конкретные проекты, которые реально существуют.
Полностью рабочая игра “Камень-ножницы-бумага”. Выбираете оружие — компьютер выбирает случайно — результат показывается.
Хитрость: “случайность” сделана через CSS animations с разной длительностью. Вы не можете предсказать, в какой момент кликнете, поэтому выбор компьютера кажется случайным.
Крестики-нолики, где компьютер делает оптимальные ходы.
Хитрость: все возможные комбинации ходов заранее прописаны. Если вы поставили X в центр, компьютер ставит O в угол. Если вы пошли в угол — компьютер идёт в центр. Это не AI. Это гигантская таблица if-else.
Полноценная приключенческая игра. Вы ходите по лабиринту, собираете ключи, открываете двери, избегаете ловушек.
Хитрость: 10,000+ строк SCSS. Каждая клетка лабиринта — radio button. Каждый переход — селектор типа:
#cell1:checked ~ #cell2:checked ~ .player {
transform: translate(100px, 0);
}
Вердикт: Это безумие. Восхитительное, но безумие.
Калькулятор с поддержкой дробных чисел. Сложение, вычитание, умножение, деление.
Хитрость: Число разделено на целую и дробную части. Каждая хранится в отдельном counter. При выводе они склеиваются через content: counter(integer) '.' counter(decimal).
Проблема: округление. CSS counters не умеют в дробную математику [5]. Поэтому все вычисления делаются в фиксированной точке (умножаем всё на 100, вычисляем, делим обратно).
Первое лицо 3D-мир с комнатами, дверями, текстурами. Управление клавишами (через :target).
Хитрость: Вся сцена — набор div-ов с transform: translate3d(). При переходе в другую комнату меняется URL хеш, срабатывает :target, применяется новый transform.
Проблема: Нет коллизий. Вы можете пройти сквозь стену, если знаете хеш. Нет физики. Нет освещения (всё запечено в градиентах).
Мнение разумных людей:
“Это интересный эксперимент, но не используйте это в продакшене. Семантика ломается, accessibility страдает, код нечитаем.”
Мнение энтузиастов:
“Это доказывает, что CSS — Тьюринг-полный язык! Мы можем сделать что угодно!”
Правда:
CSS НЕ Тьюринг-полный. Потому что:
Нет полноценных переменных (только counters)
Нет циклов (только анимации)
Нет способа изменить состояние без взаимодействия пользователя
Но CSS + HTML + User Interaction в сумме Тьюринг-полные. Потому что пользователь может кликать в любой последовательности, а чекбоксы — это память.
Окей, хватит троллить. Давайте честно: когда checkbox hack действительно полезен?
Аккордеоны, табы, dropdown-меню — это норм. Если вам не нужна сложная логика (например, закрытие меню при клике вне), checkbox hack отлично работает.
Если у вас статичный сайт без сложной интерактивности, checkbox hack может заменить 90% JavaScript. Меньше зависимостей, быстрее загрузка.
Если JavaScript не загрузился или отключён, CSS-версия продолжит работать. Это хороший паттерн: делаете базовую функциональность на CSS, улучшаете через JS.
Навигация на мобильных часто делается через checkbox hack. Это легковесно, работает везде, не требует библиотек.
Красивые чекбоксы, radio buttons, toggle switches — всё это лучше делать через CSS, чем тащить jQuery UI.
1. Любая сложная логика
Если вам нужно больше, чем “показать/скрыть”, используйте JavaScript. Не пытайтесь воссоздать Spring Framework на CSS.
2. Динамические данные
CSS не может обрабатывать API-запросы, парсить JSON, обновлять данные в реальном времени. Для этого нужен JS.
3. Production-приложения
Не делайте калькулятор на CSS для банковского приложения. Просто не делайте.
4. Accessibility-critical интерфейсы
Если ваш сайт используют люди с ограниченными возможностями, не ломайте семантику HTML ради CSS-трюков.
5. Когда у вас есть дедлайн
Написать toggle на JavaScript — 5 минут. Написать на CSS checkbox hack — 5 минут. Написать калькулятор на CSS — 5 дней. Написать на JS — 2 часа.
CSS никогда не задумывался как язык программирования. Он создавался для стилизации документов. Но разработчики — изобретательные ребята. Если есть хоть малейшая возможность что-то сломать и заставить работать по-другому, они это сделают.
Checkbox hack — гениальный хак. Он использует стандартное поведение [6] HTML (связь label и input) + стандартные возможности CSS (псевдоклассы и селекторы) для создания интерактивности. Это не баг. Это не exploit. Это легитимная техника, описанная в спецификациях.
Но это не значит, что нужно писать всё на CSS. Калькулятор на CSS — это как забить гвоздь микроскопом. Технически возможно. Впечатляюще. Но нахрена?
Правда в том, что большинство CSS-игр и приложений созданы для челленджа. Для доказательства концепта. Для фана. Авторы прекрасно понимают, что это не production-код. Это эксперимент. Это исследование границ возможного.
И в этом вся прелесть. CSS-программирование — это как code golf. Цель не в практичности. Цель — сделать невозможное возможным.
P.S. Если вы сейчас думаете: “Окей, это прикольно, попробую сделать свою игру на CSS” — отлично! Но помните: это кроличья нора. Вы начнёте с простого toggle. Потом сделаете accordion. Потом табы. Потом dropdown. Потом анимированное меню. Потом 3D-карточку. Потом 3D-куб. Потом 3D-мир. Потом игру. Потом калькулятор.
И в один прекрасный день вы проснётесь с мыслью: “А можно ли сделать операционную систему на CSS?” Ответ: нет, но вы всё равно попробуете.
P.P.S. Спасибо всем CSS-энтузиастам, которые делятся своими безумными проектами на CodePen. Особенно Chris Coyier (CSS-Tricks), Elad Shechter (автор Pure CSS Games), и всем остальным, кто доказывает, что ограничения — это вызов, а не препятствие.
P.P.P.S. Я CSS-программист. И я не извиняюсь за это.
Автор: meeliname
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/21535
URLs in this post:
[1] интеллектом: http://www.braintools.ru/article/7605
[2] памяти: http://www.braintools.ru/article/4140
[3] логика: http://www.braintools.ru/article/7640
[4] поведение: http://www.braintools.ru/article/9372
[5] математику: http://www.braintools.ru/article/7620
[6] поведение: http://www.braintools.ru/article/5593
[7] Источник: https://habr.com/ru/articles/963368/?utm_source=habrahabr&utm_medium=rss&utm_campaign=963368
Нажмите здесь для печати.