Пре­фикс или по­стх­ак

Перевод «Prefix or posthack»

Перевод Вадим Макеев

В то время, как поддержка CSS в браузерах улучшается с каждым днём — включая впечатляющие успехи команды разработчиков IE9 — всё больше и больше авторов увлекаются CSS3. По этой причине им приходится сталкиваться с браузерными префиксами — свойствами вида -*-, вроде -moz-border-radius, -webkit-animation и так далее.

И конечно же, по поводу этих префиксов слышится ворчание. Звучат призывы совсем отказаться от них или объединить все специфичные браузерные префиксы в один вида -beta-. Главная причина всего этого шума — никто, на самом деле, не хочет писать одно и то же свойство четыре или пять раз подряд только для того, чтобы, скажем, скруглить уголки.

Даже если это ворчание и можно понять, оно всё равно несправедливо. На самом деле, мы должны благодарить производителей браузеров за использование префиксов и безусловно поощрять их развитие. Более того, я уверен, что префиксы должны стать основой процесса развития стандарта CSS. И я говорю это не от большой любви к повторению правил, а, напротив — от большого желания видеть последовательное развитие CSS. Я верю, что префиксы на самом деле могут ускорить развитие и совершенствование CSS.

Вспоминая ужасы Скопировать ссылку

Для понимания того, почему браузерные префиксы в принципе существуют, очень поучительно будет вспомнить историю с блочной моделью, которая чуть не убила CSS на пороге второго тысячелетия. Противоречивые реализации блочной модели в браузерах привели тогда к серьёзному кризису. Для того, чтобы обезопасить себя от такой ситуации в будущем, мы должны придумать новый механизм на основе существующих возможностей языка и изобрести принципиально новый тип хаков.

Для наших самых маленьких читателей, пропустивших всё веселье — история произошла следующая: среди первых браузеров, поддерживающих CSS, Netscape внедрил блочную модель, найденную в CSS-спецификации. Это означало, что свойства width и height соотносились с шириной и высотой границ содержимого блока. А Internet Explorer внедрил интуитивную блочную модель, в которой свойства width и height обозначали размеры по внешней границе блока.

Какая бы из реализаций ни казалась удачнее, факт оставался фактом — в тот момент на рынке существовало два браузера принципиально несовместимых друг с другом, и у каждого из них была большая пользовательская база. Дело было в конце 90-х, когда мы боролись как проклятые, чтобы вырваться из трясины предупреждений «этот сайт лучше всего отображается в…», и обычной была ситуация, когда одна и та же вёрстка отлично работала в одном браузере, но напрочь разваливалась в другом.

Суть проблемы состояла в том, что каждый из браузеров не мог изменить своего поведения до зеркального соответствия другому. Представим на секунду, что команда разработчиков IE решает изменить поддержку CSS для большего соответствия спецификации. Подобный поступок привёл бы к тому, что десятки или даже сотни тысяч сайтов, работавших в IE, не просто где-то там сломаются, а буквально развалятся на части в определённых версиях браузера. И пока всё сообщество веб-стандартистов будет рукоплескать этому поступку, весь остальной мир откажется от браузера по причине полной его бесполезности. И даже если Рабочая группа CSS (CSS Working Group) решит изменить спецификацию для соответствия поведению IE, то Netscape столкнётся ровно с теми же трудностями.

Таким образом и был придуман механизм переключения <!DOCTYPE>. Вся эта история со «стандартным» (standards mode) и «хитрым» (quirks mode) режимами выросла как раз из этой ситуации. Механизм переключения <!DOCTYPE> решал многие проблемы, но его появление спровоцировали именно сложности с блочной моделью. Вдумайтесь: из-за того, что два браузера вели себя по-разному, сегодняшние браузеры вынуждены поддерживать два основных режима обработки страниц и выбирать нужный на основании SGML-декларации, в которой вообще ничего не сказано про обработку страниц.

Более того, все CSS-хаки первой волны были посвящены именно этой проблеме. Название классического представителя хаков той поры говорит само за себя: «хак для блочной модели» (The Box Model Hack). Фактически, сам хак был основан на ошибке в синтаксической обработке значения свойства voice-family, но почему-то никто ни разу не назвал его «voice-family-хак».

Самое смешное, что это был не единственный случай, в котором непоследовательность внедрения спецификации привела к проблемам. Спустя некоторое время после появления механизма переключения <!DOCTYPE>, спасшего CSS, команда разработчиков IE внедрила некоторые CSS-свойства позиционирования. Одним из внедрённых свойств было clip. Наученные горьким опытом шумихи с блочной моделью, инженеры Microsoft с большим вниманием отнеслись к спецификации и сделали всё в точности так, как там было сказано.

Вскоре после публичного релиза браузера, Рабочая группа CSS серьёзно поменяла принцип работы свойства clip. Синтаксис выглядел точно так же, однако приводил совсем к другим результатам.

В очередной раз спецификация вошла в противоречие с публично доступным браузером — или, если хотите, наоборот. Окончательным решением стало возвращение к прежнему поведению и полный отказ от нового. Фактически, это свойство стало бесполезным для элементов с непредсказуемой высотой и шириной — т.е. для всех незамещаемых элементов в нормальном потоке, вроде элементов <div> и <p>. Подробнее о типах элементов (replaced, non-replaced) можно прочитать в документации W3C. Несмотря на то, что были предложены и другие варианты решения, они так и не были реализованы, и свойство clip утратило часть своей полезности.

Представим себе другой исход Скопировать ссылку

Предположим, что вместо внедрения свойства clip разработчики IE внедрили бы свойство -ms-clip. В этом случае было бы не так сложно справиться с последующим изменением поведения в спецификации. Из-за того, что префикс обозначает для свойства статус «в разработке», в дальнейшем производителю браузера будет гораздо проще пересмотреть работу свойства. Таким образом, разработчики IE могли бы поменять поведение свойства -ms-clip, в следующем релизе и объяснить разработчикам, что они обновили экспериментальное свойство для соответствия спецификации.

И даже если бы они решили, что сделать это невозможно, вся опасность «неправильного» внедрения была бы изолирована в рамках префиксной версии свойства. Другие производители браузеров могли бы внедрить новую версию свойства clip (со своим префиксом) и это никак бы не повлияло на то, что сделали разработчики IE. Один производитель никак не смог бы навредить своими действиями спецификации и другим производителям.

В этом состоит польза префиксов: это способ показать, что свойство находится «в разработке» и не обязательно будет вести себя так же в будущих релизах; это решение для разработчиков, которым необходимо внести изменения в работу свойства; это защита от неудачной или преждевременной реализации, которая вполне может случиться при первом внедрении. Префиксы придают столь необходимую гибкость процессу развития CSS.

Мы конечно можем заявить: «Когда браузер неправ по отношению к спецификации, то он должен изменить реализацию, даже если это сломает вёрстку сайтов». Благодаря предупреждению о рабочем статусе свойства, которое подразумевают префиксы, сделать это становится гораздо проще. Без использования префиксов это крайне сложно или, зачастую, просто невозможно. Microsoft так и не изменила способ расчёта width и height для устаревших сайтов — вместо этого она использовала объявление <!DOCTYPE> для включения другого поведения для новых (теоретически, более совместимых со стандартами) сайтов. Это был очень полезный и необходимый трюк, но один из тех, что срабатывают только один раз.

И даже сейчас мы страдаем Скопировать ссылку

И если вы думаете, что все эти глупости стали частью истории, вот вам два примера несовместимости, существующие прямо сейчас:

Браузеры на движках Mozilla и WebKit обрабатывают размытие для свойства box-shadow по-разному, и ни одна из реализаций полностью не соответствует спецификации. И пока я пишу эти строки, длинные и жаркие дебаты кипят в рассылке www-style. По меньшей мере одной, а, возможно, и обеим реализациям размытия тени придётся измениться для достижения совместимости. То же самое справедливо и для реализаций Opera и Microsoft.

Браузеры на движках Mozilla и WebKit поддерживают градиенты, используя категорически разный синтаксис для достижения простых результатов. А теперь представьте ситуацию, в которой производители браузеров внедрили бы градиенты без префиксов. В ней мы имеем три варианта:

  1. Выбрать, какой браузер получит градиенты, а какой нет;
  2. использовать CSS-хаки или фильтрацию браузеров для выдачи разных стилей разным браузерам;
  3. полностью отказаться от использования градиентов.

И мы имеем здесь целых три варианта только потому, что градиенты, реализованные в этих браузерах, имеют очень разный синтаксис, что открывает дорогу для первого варианта. В случае, когда обе реализации имели бы схожий синтаксис значения, но различную реализацию — как в истории со свойством clip — у нас были бы только два последних варианта: хакать и фильтровать для выдачи разных стилей, либо просто отказаться от этой затеи.

Мы уже вдоволь находились по этим граблям за всю историю развития CSS. Нет смысла наступать на них ещё раз, если первая дюжина попыток закончилась плохо.

Пре-фикс или пост-хак Скопировать ссылку

Но настолько ли хороши префиксы? В конце концов, было же сказано, что префиксы — это всего лишь новые CSS-хаки. Как сказал Аарон Густавсон в недавней статье, это:

-moz-border-radius: 10px 5px;
-webkit-border-top-left-radius: 10px;
-webkit-border-top-right-radius: 5px;
-webkit-border-bottom-right-radius: 10px;
-webkit-border-bottom-left-radius: 5px;
border-radius: 10px 5px;

…слишком напоминает вот эту историю:

padding: 10px;
width: 200px;
w\idth: 180px;
height: 200px;
heigh\t: 180px;

С точки зрения чрезмерного повторения и раздражения — да, они более чем похожи. Но, с другой стороны, они принципиально различаются: префиксы дают нам контроль над судьбой наших хаков. В прошлом нам приходилось выискивать кучу ошибок парсера только для того, чтобы заставить противоречивые реализации работать одинаково, как только мы выясняли, что они работают противоречиво. Таким образом, мы реагировали на имеющуюся проблему. Использование префиксов — это упреждающий подход.

Более того, префиксы — это временный хак. Со временем, когда реализация свойств станет более совместимой, браузеры просто откажутся от префиксов. И тогда авторы смогут писать всего одну строку вместо шести с чем-то для свойства border-radius. Без префиксов мы будем вынуждены ждать очередных сомнительных реализаций и год от года поддерживать их при помощи хаков.

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

Иногда мне кажется, что так же история обстоит и с методами предварительной обработки кода для работы с префиксами — серверными (при помощи Less) или клиентскими (куча JS-фреймворков). Использование этих инструментов позволяет просто писать правило border-radius и поручать им разворачивание этой строки в список необходимых правил с префиксами. С одной стороны, это уменьшает количество необходимого кода и повышает его чистоту и понятность. С другой стороны, этот подход мало чем отличается от ситуации с единым префиксом или вообще без них — всего одна неудачная реализация, и ваша вёрстка развалится.

Преимущество этого способа состоит в том, что если что-то пойдёт не так, то любой автор сможет вернуться, отключить препроцессинг и написать все префиксы вручную. Или сами препроцессоры кода могут обновиться для решения проблемы. В любом случае, это некоторый перебор сложности для авторов, хотя и не такой большой.

Обратная сторона этого способа более философская, но не менее важная: пряча свойства с префиксами за обработчиком, авторы могут забыть о том, что используют экспериментальные свойства, которые могут измениться. Неосознанно они могут начать думать, что используют что-то устоявшееся и стабильное, хотя это может быть совсем не так.

Делаем префиксы действительно важными Скопировать ссылку

Я настолько твёрдо уверен, что префиксы — это хорошая вещь, что готов перейти к следующему логическому заключению: префиксы должны занять центральное место в развитии стандартов. Они должны быть обязательными для только что реализованных свойств, и должны стать механизмом, декларирующим совместимость.

Вот что я имею в виду: предположим, что кто-то изобрёл новое свойство под названием text-curl. Тотчас же трое производителей реализуют его. Каждый из них будет обязан добавить префикс к своей реализации. Таким образом, получается следующая картина:

h1 {
    -webkit-text-curl: minor;
    -moz-text-curl: minor;
    -o-text-curl: minor;
    text-curl: minor;
}

Со временем производители совершенствуют свои реализации в ответ на сообщения об ошибках и уточнения от Рабочей группы CSS. В какой-то момент Рабочая группа решает, что две из трёх реализаций вполне совместимы со спецификацией. Тогда две эти реализации начинают поддерживать оригинальное свойство. Третья — нет.

В этот момент авторы могут решить упростить свои стили до следующих:

h1 {
    -webkit-text-curl: minor;
    text-curl: minor;
}

В отличие от ситуации с хаками, которые только разрастаются со временем, мы просто отбрасываем ненужное. В итоге, всё, что остаётся — это одна строчка с text-curl.

И что же происходит, когда дебютирует очередная реализация? В первом своём релизе она также использует префикс, вне зависимости от количества уже существующих удачных реализаций. Значит, нам придётся вернуться к нашему CSS и поменять его следующим образом:

h1 {
    -ms-text-curl: minor;
    text-curl: minor;
}

Как только Рабочая группа сочтёт реализацию свойства -ms-text-curl полной, префикс сможет быть отброшен в следующей версии IE. После чего CSS снова может быть уменьшен до единственной беспрефиксной строчки. И снова — количество хаков только уменьшается со временем.

Конечно, каждый из производителей продолжит поддерживать свойства с префиксами, так что даже если мы и не удалим эти правила, каждый из поддерживаемых браузеров распознает и использует свойство без префикса, если конечно оно будет следовать после правила с префиксом. Для любого браузера, который внедрил свойство с префиксом и не позаботился о том, чтобы привести его в беспрефиксное состояние, это свойство по-прежнему будет работать. Даже если CSS-код останется нетронутым, он всё равно продолжит функционировать.

Вернёмся к тому моменту, когда Рабочая группа называет две реализации полностью совместимыми и даёт им право избавиться от префиксов. Эта ситуация приводит к двум результатам. Во-первых, как я уже говорил, это говорит о том, что свойство имеет достаточный уровень совместимости, и это позволяет развивать процесс стандартизации дальше.

Но, с другой стороны — что, вероятно, более важно — это заставляет производителей и Рабочую группу сотрудничать в разработке тестов, необходимых для определения условий совместимости. В дальнейшем, эти тесты могут помочь остальным производителям достичь статуса совместимости гораздо раньше. Они могут выпустить версию с префиксом в одной публичной бете и отказаться от префикса буквально в следующем же бета-релизе.

Это полностью меняет существующий сейчас порядок вещей. Так уж пошло, что когда CSS-модуль достигает статуса возможной рекомендации (CR), производители начинают отказываться от префиксов для свойств из этого модуля. Но такая ситуация снова приводит к возможности появления некачественных реализаций и новых хаков для решения возникших проблем.

Как предлагалось выше, модулю должно быть позволено достигнуть статуса возможной рекомендации только в случае, когда все его свойства имеют, как минимум, две реализации без префиксов, работающие в реальных условиях. Любые подобные свойства, появившиеся после этого, должны иметь префикс, от которого они смогут избавиться, только доказав на практике полную совместимость с уже существующими реализациями без префикса. Вместо того чтобы внушать сомнения, свойства с отброшенными префиксами могут стать гарантами стабильности, вместе с уже давно известными свойствами.

Заключение Скопировать ссылку

Если история развития веб-стандартов и научила нас чему-то, то скорее тому, что хаки будут необходимы всегда. Подставляя хаки при помощи префиксов и используя их в процессе стандартизации, мы действительно можем справиться с потенциальными проблемами и ускорить разработку CSS.

И когда в следующий раз вы начнёте ворчать по поводу указания одних и тех же правил четыре раза, по одному на каждый из браузеров, вспомните, что это временная проблема. Это немного напоминает вакцинацию — укол побаливает, это правда, но всё не настолько плохо по сравнению с болезнью, которую он предотвращает. И в нашем случае вы вакцинируетесь от многолетней возни с хаками, от фильтрации браузеров, и от других скверных вещей. Мы уже однажды пережили эту эпидемию. При должном использовании префиксы позволят надолго сдержать очередную вспышку этой напасти.