Уроки CSSbattle
В начале апреля появилась затягивающая и познавательная онлайн-игра для верстальщиков — CSSbattle.dev. Вашего покорного слугу угораздило «влипнуть» в нее практически с самого начала (и даже пару раз какое-то время побыть в самом топе:). Это был интересный и поучительный опыт, которым хочется поделиться.
Придумали и реализовали эту игру два друга-тёзки из Индии, фронтендер Кушагра Гур и дизайнер Кушагра Агарвал. Жанр таких развлекательных состязаний называют «code golf»: как в гольфе надо закатить мяч в лунку минимальным числом ударов, так здесь надо решить задачу минимальным количеством кода. Задача — воспроизвести 12 несложных геометрических рисунков, а код — любой (CSS, HTML…), кроме скриптов и ссылок на внешние ресурсы. Кроссбраузерность, адаптивность и тем более валидность не нужны: только Хром, только фиксированный размер 400×300. Поэтому многие «запретные» в обычной жизни приемы типа отсутствия доктайпа, незакрытых тегов/кавычек, нестандартных свойств и т.п. допустимы и часто помогают. Игроки не видят код друг друга, только результаты, и угадывание, какая хитрость помогла более успешному сопернику выгадать еще символ-другой, добавляет игрового азарта.
Так выглядит главная страница игры
Авторы гордо называют свою игру первым в мире таким состязанием для CSS, и это очень похоже на правду (да, был когда-то давно css1k, но то был скорее «творческий конкурс»). А среди участников замечены такие звезды, как Лия Веру и сам Эрик Мейер!
Я не буду много говорить о хитростях прохождения конкретных заданий («targets», т.е. «целей» или «мишеней») игры: об этом уже немало написано (прежде всего на авторском форуме), некоторые участники и вовсе выложили свои решения в опенсорс, но не буду «спойлерить». Мне больше хочется поделиться общими, «высокоуровневыми» соображениями.
1. Неоптимальное, но работающее лучше оптимального, но «почти работающего»
Алгоритм оценки результатов в игре в первую очередь оценивает совпадение результата с заданием, и лишь затем — краткость решения. Самый компактный код не наберет больше 600 баллов, если расхождение с образцом превышает 0.1% (как именно оценивается это расхождение — по числу пикселей, по отличию их цвета и т.д. — авторы пока не раскрывают). На мой взгляд, это полезное напоминание: даже там, где цель в оптимизации, не стоит гнаться за оптимизацией преждевременно. Сперва нужно добиться, чтобы заработало.
Авторы явно придерживались этого же правила и в разработке самой игры: много улучшений было добавлено уже после запуска, и не только «косметических» — в частности, доработан сам алгоритм сравнения (поначалу он был излишне придирчивым, так что ряд заданий был почти «непроходимым»). А улучшать работающее решение можно бесконечно, потому что…
2. Нет предела совершенству
После очередного рекордного результата часто кажется, что вот он, абсолютный предел, дальше которого продвинуться невозможно. Тем не менее, рекорды в игре до сих пор постоянно обновляются. «Тупик» на конкретном пути решения не значит, что нет других путей, возможно, намного более эффективных.
Для меня особенно ярко это проявилось в задании № 9 («Тессеракт»): я долго втайне гордился своим решением в 145 знаков (ради него мне даже пришлось «расчехлить секретное оружие» времен IE6:), но у Вячеслава Попова, нынешнего чемпиона, нашелся вариант в 144, и мне пришлось немало поломать голову, чтобы, поменяв пару свойств в решении, ужать его до 143 (попутно еще и выиграв в гибкости). Но буквально через день некто Джеймс Уайтхед запросто сократил результат до <130 знаков, а Клэр Ларсен (есть подозрение, что они с Джеймсом в одной команде) довела его аж до 124, на целых 19 знаков лучше моего «идеального» решения. Правда, предыдущие рекорды этой компании натолкнули меня на кое-какие догадки, я повыкидывал из своего решения всё лишнее и смог повторить и даже еще чуть улучшить рекорд… но уже не обольщаюсь, что он простоит долго. Так что…
3. Важно вовремя остановиться:)
Признаюсь, этот урок дался мне тяжелее всего! Я как-то пропустил момент, когда игра перешла из ненавязчивого способа скрасить ожидание в дороге и т.п. (с телефона, благо много текста вводить не надо) в один из значимых приоритетов, «отжирающий» немалую долю свободного времени (в т.ч. от сна). Авторы честно предупреждают: игра затягивает и может вызвать зависимость. Конечно, полезные знания в процессе тоже приобретаются (лично я ощутимо «прокачался» в SVG), но… это всего лишь игра.
Собственно, этой статьёй я пытаюсь подвести итог и… выполнить этот пункт, наконец:) Это трудно, ведь всегда есть куда улучшать свои достижения (см. п. 2). Вот даже сейчас меня терзает соблазн проверить всего одну многообещающую догадку насчет наложения цветов… Но я справлюсь! Зря я, что ли, бывший чемпион?:)
4. От людей зависит гораздо больше, чем от технологий
Хотя изначально правила игры не запрещали использовать SVG, неявно предполагалось (начиная с названия), что это всё же в первую очередь состязание в HTML/CSS. Поэтому, когда в игру буквально ворвался Лев Солнцев, мейнтейнер утилиты SVGO, и поставил целую серию рекордов почти без единой строчки CSS, многие были в шоке. Но даже приблизиться к этим рекордам, наивно используя SVG «в лоб», оказалось не так-то просто! Например, в 12-м задании явно напрашивается нарисовать волнистую линию одним элементом <path>
, но одни лишь атрибуты fill
, stroke
, stroke-width
и stroke-linecap
вместе с их значениями уже тянут на сотню байт — куда же впихнуть саму кривую? Так что и в SVG не обошлось без маленьких хитростей, а поиск оптимального применения этих хитростей оказался не менее творческой задачей, чем поиск применений CSS-трюкам!
Лев обещал написать про свои SVG-трюки отдельную статью, так что я пока ограничусь самыми базовыми своими наблюдениями и подсказками, почерпнутыми от других участников:
- Атрибут
viewBox
— это сила! Он в корне меняет практически всё — от масштаба и положения элементов до поведения самой SVGшки на странице. Используйте эту мощь с умом! - Благодаря дефолтным значениям некоторые атрибуты можно не писать вообще.
- Один
<path>
с более длиннымd
, как правило, короче нескольких отдельных тегов для фигур. - Внутри
d
масса мест, откуда можно выбросить пробелы и запятые: перед минусом, после флагов в эллиптической дуге… - Однозначные целые числа — ваши друзья. Избежать двузначных и отрицательных чисел часто помогает правильный выбор между относительными и абсолютными координатами для сегмента контура (
v
илиV
,a
илиA
,c
илиС
…) - Одну и ту же (ну, или очень похожую) кривую можно нарисовать разными командами в
d
. Не зацикливайтесь на одной, пробуйте разные!
Но несмотря на все эти хитрости, для некоторых заданий SVG так и не смог побороть старого доброго CSS. Так что правильный выбор технологии для конкретной задачи — сам по себе еще один секрет успеха.
Примечание: пока я пишу эту статью, авторы игры готовятся вот-вот запретить SVG-решения. Но опыт оптимизации SVG лишним не будет: это как раз то, что можно смело применять в «боевом» коде (в отличие от большинства «сугубо спортивных» трюков с CSS).
5. Сильные стороны HTML/CSS действительно сильны
Не так давно мы переводили статью Кита Гранта про сильные стороны CSS: устойчивость, декларативность, контекстность и всё такое. На мой взгляд, CSSbattle.dev — отличная их иллюстрация.
Конечно, в реальной работе мы не будем оставлять незакрытыми скобки и вообще обрывать код «по живому», как порой вынуждала «спортивная» экономия. Даже необязательные теги большинство из нас всё-таки ставит (и правильно делает: явное лучше неявного). Но оборванный код может случиться и помимо нашей воли. Связь, особенно мобильная, может сбойнуть в любой момент по тысяче причин. И CSS (и HTML!) всегда будет делать всё возможное, чтобы пользователь мог увидеть хоть что-то — как бы над этим кодом ни издевались обстоятельства. Их специально такими придумали.
В любой HTML-странице — даже абсолютно пустой — всегда будут 3 DOM-элемента: html
, head
и body
. Все их можно стилизовать. Правда, чтобы стилизовать head
, нужно поменять его display
(по умолчанию он none
), потратив драгоценные байты. Но и два элемента — отличное начало. А если этого мало, необязательные теги могут выручить снова: <p><p><p>
создаст три абзаца подряд, каждый следующий неявно закрывает предыдущий.
И «глобальная» природа CSS, которую мы по привычке ругаем, в этих задачах оборачивается большим плюсом. Во многих заданиях меня выручал один подход: выделить что-то общее, применимое ко всему подряд, а потом лишь чуть-чуть «подшлифовать» какую-нибудь мелочь у отдельной части. Чем больше общая часть и чем меньше приходится «шлифовать», тем короче решение. К тому же универсальный селектор — самый короткий. И из него можно строить краткие, но выразительные комбинации: например, в структуре html > body > p
селектор *>*
выберет всё, кроме html
, а *+*
(«лоботомированная сова», с лёгкой руки Хейдона Пикеринга) — только body
(как следующего соседа после head
).
Даже дефолтные браузерные стили, которые нас порой так бесят, что мы готовы не глядя «прихлопнуть» их первым попавшимся под руку ресетом-нормалайзом, иногда очень кстати! Например, в одном из кратчайших решений первого задания (простой квадрат 200×200 в углу), эти 200px получаются из 2in
(т.е. 192px, поскольку в современном CSS пиксели и др. абсолютные единицы связаны константной пропорцией) и дефолтных 8-пиксельных margin
-ов от body
.
6. Лишних знаний в CSS не бывает
В повседневной практике мы используем малую часть CSS, а про остальное в лучшем случае знаем, что оно есть (совсем в идеале — можем быстро найти на MDN). По крайней мере, у меня так. Но упражнения вроде CSSbattle помогают «инвентаризации» этого багажа знаний: вдруг что-то, до чего раньше никак не доходили руки, окажется полезным?
Например, единицы измерения CSS. Обычно мы используем px
да em
, изредка vw
/vh
. А ведь этих единиц целая куча, и иногда можно выиграть байт-другой, просто заменив одну на другую (Алекс Заворски так увлекся этим, что не поленился написать целый скрипт-калькулятор таких замен). А экзотическая единица q («четверть миллиметра») вообще чудо — она единственная однобуквенная. И при этом очень близка к пикселю. Справедливости ради, еще короче запись пикселей безразмерным числом, что допустимо для нескольких старых свойств по одному из стандартов для нестандартного режима (но это не точно, и не пытайтесь повторить это в продакшне!). И проценты: тоже всего один символ, да еще и пробел после него не нужен.
То же самое с цветами: вы знали, что прозрачный цвет теперь можно задать 5 символами — #0000
?
Еще в повседневной практике мы часто недооцениваем сокращенные свойства. Например, background
: можете сходу вспомнить без шпаргалки все его подсвойства, и в каком порядке они записываются? Я вот не могу. А ведь в одном таком сокращении можно задавать и фоновую картинку (включая градиент), и ее размер, и способ ее размещения, включая отступы вокруг… и в одном свойстве может быть сколько угодно таких сокращений! Конечно, для разработки удобнее всё-таки писать подсвойства отдельно — но ничто не мешает объединять их в сокращения при минификации, при сборке. Пользователи и поисковики скажут спасибо за сэкономленный трафик.
Кстати, у градиентов тоже хватает малоизвестных секретов:
- кроме привычных линейных и радиальных, они бывают еще и конические;
- форму радиальным можно задавать не только ключевым словом
circle
/ellipse
, но и указанием радиуса; - для одной цветовой зоны теперь можно задать сразу две контрольные точки — начало и конец;
- можно управлять «плавностью» цветового перехода, явно указывая положение его середины…
- …и это только то, что я сам успел узнать за время игры:). А судя по последнему рекорду Венсана де Оливейры (непревзойденного знатока инлайнового контекста, кстати), узнал я далеко-о не всё (вообще полезно иногда напоминать себе, что я тоже не знаю CSS).
А моё «секретное оружие» из п.2 оказалось не таким уж секретным. Я говорил про нестандартное свойство zoom
: старожилы помнят его по укрощению IE6-7, но оно давно работает и в WebKit/Blink. Обычно одно свойство с составными значениями, вроде box-shadow
или того же background
, выигрывает у нескольких отдельных свойств, но очень короткие свойства бывают исключениями из этого правила: zoom:.5;
короче, чем добавочный scale(.5)
в transform
. Активный участник Расмус Флоэ высказал на форуме занятную идею, что zoom
может стать своего рода заменой viewBox
для HTML, и тоже экономить байты, сокращая размеры и т.п. до однозначных целых. Меня самого посещала эта мысль, но я было отбросил ее как бесперспективную… а зря, как оказалось:)
Так что не бойтесь узнавать новое и вспоминать старое! Потому что, в конце концов…
7. CSS — это весело и увлекательно!
Надеюсь, впрочем, что постоянные читатели нашего сайта никогда в этом и не сомневались:)
Пускай же ваше CSS-«оружие» никогда не «заржавеет» и всегда будет готово приносить победы над задачами любой сложности. И желаю вам изящного компактного кода, решающего задачу на 100%!
P.S. Это тоже может быть интересно:
Практически всё описал, даже не знаю что добавить, разве что расписать подробности. Только времени, как обычно, не хватает. Может на майских…
Было бы здорово! От подробностей зависит очень многое, особенно в «битве» за каждый байт)
Плюс этого батла для меня, в том, что можно попробовать «не продакшн» свойства цсс, какая-никакая разминка для ума.
Согласен, я примерно это же пытался сказать в п.6:). Причем не только совсем новые свойства, но и малоизвестные или редко используемые (как недостаточно кроссбраузерные) возможности давно знакомых свойств – тот же синтаксис градиентов с двумя границами и «подсказками» или редкие значения background-repeat…
We need it in English. I demand!