Уроки 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. Это тоже может быть интересно: