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

5

Комментарии

  1. Лев Солнцев

    Лев обещал написать про свои SVG-трюки отдельную статью, так что я пока ограничусь самыми базовыми…

    Практически всё описал, даже не знаю что добавить, разве что расписать подробности. Только времени, как обычно, не хватает. Может на майских…

    1. SelenIT

      SelenIT (Автор записи)

      Было бы здорово! От подробностей зависит очень многое, особенно в «битве» за каждый байт)

  2. Мария

    Плюс этого батла для меня, в том, что можно попробовать «не продакшн» свойства цсс, какая-никакая разминка для ума.

    1. SelenIT

      SelenIT (Автор записи)

      Согласен, я примерно это же пытался сказать в п.6:). Причем не только совсем новые свойства, но и малоизвестные или редко используемые (как недостаточно кроссбраузерные) возможности давно знакомых свойств – тот же синтаксис градиентов с двумя границами и «подсказками» или редкие значения background-repeat…

  3. Anton

    We need it in English. I demand!

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Ваш E-mail не будет опубликован

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.