Привет! Предлагаю вашему вниманию перевод статьи The Languages Which Almost Became CSS автора Zack Bloom про языки, которые могли бы стать CSS, сложись история немного иначе.
На протяжении всего прошлого года неисчерпаемым источником удовольствия для меня было то, что я постоянно общался с толпами (в буквальном смысле) людей, желавших (держитесь крепче) контролировать внешний вид своих документов так, как это можно тривиальным образом делать в TeX, Microsoft Word или любой другой среде обработки текстов. Я говорил всем им: "Простите, но вам ничем не помочь".
— Марк Андриссен, 1994 год
Когда в 1991 году Тим Бернерс-Ли объявил о создании HTML, способов стилизации страниц не существовало. Способ рендеринга тегов HTML определялся браузером и на него значительно влияли пользовательские настройки. Однако хорошей идеей казалось создание стандартного инструмента, позволяющего страницам «намекать» об их предпочтительном стилистическом рендеринге.
Но до появления CSS было ещё пять лет, и ещё десять лет до его полной реализации. Это был период напряжённой работы и инноваций, приведших к созданию множества конкурирующих способов стилизации, которые вполне могли превратиться в стандарт.
Хотя эти языки, очевидно, сегодня мало где используются, мне интересно поразмышлять над тем, каким бы мог стать мир. Ещё более удивительно то, что многие из этих языков имеют функции, которые разработчики с радостью использовали бы в CSS даже сегодня.
Первый кандидат
В начале 1993 года браузер Mosaic не достиг даже версии 1.0, а все существовавшие браузеры работали только с HTML. Не было никакого способа указать стиль HTML, поэтому вы видели тег <h1>
таким, каким его решил показывать браузер.
В июне того же года Роберт Райш внёс в список рассылки www-talk предложение по созданию «легко анализируемого формата для передачи стилистической информации вместе с веб-документами», который он назвал RRP.
@BODY fo(fa=he,si=18)
Вполне простительно, если вы не понимаете, что делает этот код. В эпоху до появления gzip, когда скорости подключения обычно не превышали 14,4 кбит/с, вполне логично было сделать содержимое этого нового формата как можно более компактным. Конкретно это правило выбирает в качестве семейства шрифтов (fa
) Helvetica (he
) и задаёт размер шрифта (si
) в 18 пунктов.
Любопытно, что предложении Райша отсутствовали единицы измерения, все числа интерпретировались на основании контекста (например, все размеры шрифтов задавались в пунктах). Это связано с тем, что RRP проектировался больше как «набор советов и рекомендаций рендереру», а не как спецификация. Это считалось необходимым, потому что одна и та же таблица стилей должна была работать и в обычных браузерах текстового режима (наподобие Lynx), и в набиравших всё большую популярность графических браузерах.
Скриншот браузера Lynx
Интересно, что в RRP включён способ задания столбчатого расположения — этой возможности в CSS не было до 2011 года. Например, три столбца, каждый шириной в «80 единиц», выглядели бы так:
@P co(nu=3,wi=80)
Это немного сложно парсить, но, вероятно, не намного сложнее, чем white-space: nowrap
.
Стоит заметить, что RRP не поддерживал никакой «каскадности», с которой мы сегодня ассоциируем таблицы стилей. Любой документ мог иметь единовременно не более одной активной таблицы стилей, что вполне логично в случае стилизации документов, хоть и непривычно для нас сегодня.
Марк Андриссен (создатель Mosaic, со временем ставшего самым популярным браузером) знал о предложении RRP, но так никогда и не реализовал его в Mosaic. Вместо этого Mosaic почти сразу же двинулся по пути использования тегов HTML для стилизации (что довольно трагично), добавив такие теги, как <FONT>
и <CENTER>
.
Viola и войны прото-браузеров
«Тогда почему бы просто не реализовать одно из множества уже имеющихся предложений таблиц стилей. При правильной структуре это бы почти полностью решило проблему».
Тогда бы мне пришлось сказать людям: «Так, вам нужно изучить этот язык, чтобы написать документ, а затем изучить ещё один язык, чтобы документ выглядел так, как вы хотите». О, они были бы просто в восторге.
— Марк Андриссен, 1994 год
Вопреки распространённому мнению, Mosaic не был первым графическим браузером. Ему предшествовал ViolaWWW — графический браузер, изначально написанный Пей-Юань Веем всего за четыре дня.
Скриншот браузера Viola
Пей-Юань создал язык таблиц стилей, поддерживающий разновидность иерархической структуры, которая сегодня используется в CSS:
(BODY fontSize=normal
BGColor=white
FGColor=black
(H1 fontSize=largest
BGColor=red
FGColor=white)
)
В данном случае мы применяем цвета к телу документа (body) и, в частности, стилизуем H1
, которые находятся внутри тела. Вместо повторяющихся селекторов для управления этой вложенностью PWP использовал систему скобок, заставляющую нас вспомнить о системах отступов, применяемых в языках наподобие Stylus и SASS, которые и сегодня предпочитают некоторые разработчики вместо CSS. Потенциально это делает синтаксис PWP по крайней мере в одном аспекте лучше, чем CSS, который со временем превратился в универсальный язык веба.
Также PWP примечателен тем, что в нём появился способ ссылаться на внешние таблицы стилей, который мы используем и сегодня:
<LINK REL="STYLE" HREF="URL_to_a_stylesheet">
К сожалению, ViolaWWW была написана в основном для работы с X Window System, которая была популярна только в Unix-системах. Когда Mosaic был портирован на Windows, он быстро поверг Viola в прах.
Таблицы стилей до веба
HTML — эта такая штука, которую сможет полюбить только специалист по вычислительным машинам. Да, он выражает внутреннюю структуру документа, но документы это не только структурированные текстовые базы данных; они оказывают визуальное воздействие. HTML совершенно уничтожает любую графическую креативность, которую может иметь разработчик документа.
— Рой Смит, 1993 год
Потребность в языке, способном выражать стиль документов, намного старше самого Интернета.
Как вы, возможно, знаете, известный нам HTML изначально был основан на доинтернетном языке под названием SGML. В 1987 году Министерство обороны США решило проверить, можно ли использовать SGML для упрощения хранения и передачи его огромных объёмов документации. Как и любому хорошему правительственному проекту, первым делом ему придумали название. Коллектив изначально назвали Computer-Aided Logistics Support team, затем Computer-aided Acquisition and Logistics Support team, и затем, наконец, Continuous Acquisition and Life-cycle Support initiative. В любом случае, инициалами оставались CALS.
Работавшая над CALS команда создала язык для стилизации документов SGML под названием FOSI. Она опубликовала спецификацию языка, столько же подробную, сколь и непостижимую. В неё была включена любимая мной самая бессмысленная инфографика, когда-либо существовавшая в вебе.
Одно не имеющее исключений правило Интернета гласит: всегда удаётся сделать больше, если в процессе работы можно доказать, что кто-то неправ. В 1993 году, спустя всего четыре дня после предложения Пей-Юаня, Стивен Хини внёс предложение, что вместо «изобретения колеса» для стилизации веба лучше использовать версию FOSI.
Сам документ FOSI написан на SGML, что является довольно логичным ходом, учитывая знакомство веб-разработчиков с версией SGML под названием HTML. Пример документа выглядит вот так:
<outspec>
<docdesc>
<charlist>
<font size="12pt" bckcol="white" fontcol="black">
</charlist>
</docdesc>
<e-i-c gi="h1"><font size="24pt" bckcol="red", fontcol="white"></e-i-c>
<e-i-c gi="h2"><font size="20pt" bckcol="red", fgcol="white"></e-i-c>
<e-i-c gi="a"><font fgcol="red"></e-i-c>
<e-i-c gi="cmd kbd screen listing example"><font style="monoser"></e-i-c>
</outspec>
Возможно, вы не понимаете, что такое docdesc
или charlist
, как не понимали этого и члены www-talk
. Единственная контекстная информация заключается в том, что e-i-c
означает «element in context». Однако FOSI примечателен тем, что в нём впервые появилась единица измерения em
, которая теперь стала предпочтительным способом задания размера в CSS.
Разразившийся конфликт языков на самом деле был столь же древним, как и само программирование. Это была битва функционального синтаксиса «в стиле lisp» с синтаксисом более декларативных языков. Сам Пей-Юань описывал свой синтаксис как «LISP-подобный», но появление на сцене истинного LISP-варианта было лишь вопросом времени.
Тьюринг-полная таблица стилей
Несмотря на свою сложность, FOSI на самом деле воспринимался как промежуточное решение задачи форматирования документов. Долговременный план заключался в создании языка, основанного на языке функционального программирования Scheme, способного реализовать самые мощные преобразования документов, которые можно представить. Этот язык назвали DSSSL. Дадим слово одному из разработчиков языка Джону Босаку:
Не стоит ошибочно относить DSSSL к категории скриптовых языков. Да, DSSSL Тьюринг-полный; да, это язык прогрммирования. Но скриптовый язык (по крайней мере, в моей интерпретации термина) процедурен; а DSSSL с полной определённостью таковым не является. DSSSL полностью функционален и полностью свободен от побочных эффектов. В таблице стилей DSSSL никогда ничего не происходит. Таблица стилей — это одна огромная функция, значением которой является абстрактное, аппаратно-независимое, непроцедурное описание отформатированного документа, как спецификация (декларация, если пожелаете) отображаемых областей, передаваемое находящимся ниже процессам рендеринга.
В своём простейшем виде DSSSL и в самом деле довольно логичный язык стилизации:
(element H1
(make paragraph
font-size: 14pt
font-weight: 'bold))
Поскольку он был языком программирования, в нём можно было даже задавать функции:
(define (create-heading heading-font-size)
(make paragraph
font-size: heading-font-size
font-weight: 'bold))
(element h1 (create-heading 24pt))
(element h2 (create-heading 18pt))
И использовать при стилизации математические конструкции, например, чтобы сделать строки таблицы полосами:
(element TR
(if (= (modulo (child-number) 2)
0)
... ;even-row
...)) ;odd-row
Чтобы вы ещё больше завидовали, скажем, что DSSSL мог обрабатывать унаследованные значения как переменные и выполнять над ними математические операции:
(element H1
(make paragraph
font-size: (+ 4pt (inherited-font-size))))
К сожалению, DSSSL обладал смертельным изъяном, свойственным всем языкам в стиле Scheme: слишком большим количеством скобок. Кроме того, его спецификация была чересчур полной на момент окончательного выпуска, что отпугивало разработчиков браузеров. Спецификация DSSSL включала в себя более 210 отдельных стилизуемых свойств.
Дальнейшая работа разработчиков привела к созданию XSL — не менее запутанного, но гораздо более популярного языка преобразования документов.
Почему таблица стилей победила
CSS не имеет родительских селекторов (способа стилизации родителя на основании содержащихся в нём дочерних элементов). Этот факт уже давно мучает пользователей Stack Overflow, но оказывается, для их отсутствия есть довольно веская причина. В первые годы развития Интернета считалось критически важным, чтобы страницу можно было отрендерить до полной загрузки документа. Другими словами, необходимо было иметь возможность отрендерить HTML начала страницы ещё до полной загрузки HTML конца страницы.
Наличие родительского селектора означало бы, что стили необходимо обновлять в процессе загрузки HTML-документа. Языки наподобие DSSSL полностью исключались, потому что они могут выполнять операции над самим документом, который не доступен полностью на момент начала рендеринга.
Первым, кто поднял эту проблему в марте 1995 года и предложил для её решения работающий язык, стал Берт Бос. В его предложении также содержится ранняя версия эмотикона «смайлик» :-).
Сам язык по синтаксису был довольно «объектно-ориентированным»:
*LI.prebreak: 0.5
*LI.postbreak: 0.5
*OL.LI.label: 1
*OL*OL.LI.label: A
Символ .
обозначал ближайшие дочерние элементы, а *
— предков.
Язык Боса обладал ещё одним интересным свойством: можно было указывать, как работают элементы наподобие ссылок в самой таблице стилей:
*A.anchor: !HREF
В примере выше мы указали, что адресом перехода для элемента-ссылки является значение в его атрибуте HREF
. Эта мысль о том, что поведение элементов наподобие ссылок должно быть контролируемым, была популярной во множестве других предложений. В эпоху до JavaScript не существовало способов контроля таких аспектов, поэтому казалось логичным включить их в эти предложения.
В одном проекте функционального языка, предложенного в 1994 году джентльменом по имени С.М. Сперберг-Маккуин, то же поведение реализовано функционально:
(style a
(block #f) ; format as inline phrase
(color blue) ; in blue if you’ve got it
(click (follow (attval 'href))) ; and on click, follow url
В его языке также появилось ключевое слово content
как способ управления содержимым HTML-элемента из таблицы стилей. Позже эта концепция была добавлена в CSS 2.1.
Каким мог бы быть веб
Прежде чем рассказать о языке, который на самом деле стал CSS, стоит упомянуть ещё одно предложение языка, хотя бы по той причине, что он, в каком-то смысле был мечтой первых веб-разработчиков.
PSL96 был, как понятно из его названия, версией Presentation Specification Language 1996 года. По своей сути PSL выглядит как CSS:
H1 {
fontSize: 20;
}
Однако всё быстро становится намного интереснее. Например, можно было выражать позицию элемента не только в зависимости от заданного ему размера (Width
), но и по истинному (Actual Width
) размеру, в котором он рендерится в браузере:
LI {
VertPos: Top = LeftSib . Actual Bottom;
}
Из примера видно, что можно было также использовать в качестве ограничения левый одноуровневый элемент.
Также в стили можно было добавлять логические выражения. Вот пример стилизации только элементов-якорей, имеющих href
:
A {
if (getAttribute(self, "href") != "") then
fgColor = "blue";
underlineNumber = 1;
endif
}
Такую стилизацию можно было расширять до всевозможных аспектов, для реализации которых мы сегодня используем классы:
LI {
if (ChildNum(Self) == round(NumChildren(Parent) / 2 + 1)) then
VertPos: Top = Parent.Top;
HorizPos: Left = LeftSib.Left + Self.Width;
else
VertPos: Top = LeftSib.Actual Bottom;
HorizPos: Left = LeftSib.Left;
endif
}
Поддержка подобной функциональности, вероятно, позволила бы наконец реализовать мечту об отделении контента от стиля. К сожалению, этот язык слишком уж расширяемым, то есть существовала большая вероятность, что его реализации бы сильно отличались в разных браузерах. Кроме того, его опубликовали в серии статей научного мира, а не в списке рассылки www-talk, где происходил основной объём конструктивной работы. Его так никогда и не интегрировали ни в один популярный браузер.
Призрак прошлого CSS
Язык, который мог непосредственно привести к созданию CSS (по крайней мере, судя по названию), назывался CHSS (Cascading HTML Style Sheets). Его предложил в 1994 году Хокон Ли (Håkon W Lie).
Как и большинство хороших идей, первоначальное предложение было довольно безумным.
h1.font.size = 24pt 100%
h2.font.size = 20pt 40%
Обратите внимание на проценты в конце правил. Этот процент означает, какую величину «владения» текущая таблица стилей имеет над этим значением. Например, если в предыдущей таблице стилей для h2
был задан размер шрифта 30pt
с 60%
«владения», а в этой таблице стилей для h2
задан стиль 20px 40%
, то два значения можно скомбинировать на основании их процента владения и получить значение около 26pt
.
Вполне понятно, почему такое предложение было внесено именно в эпоху документных HTML-страниц: такой дизайн на основе компромиссов не нашёл бы понимания в нашем ориентированном на приложения мире. Как бы то ни было, в нём появилась фундаментальная идея о необходимости каскадной структуры таблиц стилей. Иными словами, идеи о том, что на одной странице необходима возможность применения нескольких таблиц стилей.
В своей исходной формулировке эта идея была общепризнана важной, потому что она давала конечному пользователю контроль над тем, что он видит. У оригинальной страницы могла быть одна таблица стилей, а у пользователя веба могла иметься собственная таблица, и их можно было бы комбинировать для рендеринга страницы. Поддержка множественных таблиц стилей рассматривалась как способ сохранения личной свободы в вебе, а не как способ поддержки разработчиков (которые по-прежнему кодировали каждую отдельную HTML-страницу вручную).
Пользователь мог бы даже иметь возможность управления степенью контроля, который он давал рекомендациям автора страницы; такое управление в предложении языка описывалась ASCII-схемой:
User Author Font o-----x--------------o 64% Color o-x------------------o 90% Margin o-------------x------o 37% Volume o---------x----------o 50%
Как и многие из подобных предположений, данный проект содержал в себе возможности, которые появились в CSS только спустя десятилетия, а то и вообще не появились. Например, в нём была возможность написания логических выражений на основании окружения пользователя:
AGE > 3d ? background.color = pale_yellow : background.color = white
DISPLAY_HEIGHT > 30cm ? http://NYT.com/style : http://LeMonde.fr/style
В довольно оптимистичном научно-фантастическом видении будущего считалось, что браузер будет знать, насколько релевантен для пользователя каждый элемент контента, позволяя отображать его в большем размере:
RELEVANCE > 80 ? h1.font.size *= 1.5
Все мы знаем, что случилось потом
Microsoft полностью привержен открытым стандартам, особенно в Интернете.
— Джон Лудеман, 1994 год
Хокон Ли продолжил работать над упрощением своего предложения, и совместно с Бертом Босом в декабре 1996 года опубликовал первую версию спецификации CSS. В конечном итоге он написал свою докторскую диссертацию о создании CSS, и этот документ чрезвычайно помог мне в написании статьи.
По сравнению со множеством других предложений, примечательным аспектом CSS была его простота. Его легко парсить, на нём легко писать и его легко читать. Как это часто бывало в истории Интернета, победила та технология, которую проще всего освоить новичку, а не та, которая оказалась самой мощной для специалистов.
Само по себе это является напоминанием о том, насколько случайной может быть инновация. Например, поддержка контекстуальных селекторов (body ol li
) была добавлена только потому, что у Netscape уже был способ устранения границ из изображений, которые являлись гиперссылками, и казалось необходимым реализовать всё, на что способен популярный браузер. Сама функциональность вызвала значительную задержку в реализации CSS, потому что в то время при парсинге HTML большинство браузеров не хранило «стек» тегов. Это значило, что для полной поддержки CSS парсеры было необходимо переработать.
Из-за подобных проблем (и из-за повсеместного использования нестандартных HTML-тегов для задания стиля) CSS не был пригоден к использованию до 1997 года, и не поддерживался полностью ни одним браузером до марта 2000 года. Как вам скажет любой разработчик, поддержка браузерами была очень далека от соответствия стандартам и ситуация изменилась всего несколько лет назад, пятнадцать лет спустя после выпуска CSS.
Финальный босс
Если Netscape 4 игнорировал правила CSS, применяемые к элементу
<body>
, и добавлял случайное количество пробелов к каждому структурному элементу на странице, и если IE4 реализовывал<body>
правильно, но халтурил с отступами, то какой именно CSS был безопасным? Некоторые разработчики решили вообще не писать CSS. Другие писали одну таблицу стилей, чтобы компенсировать изъяны IE4 и ещё одну таблицу, чтобы компенсировать недочёты Netscape 4.— Джеффри Зельдман
Internet Explorer 3 известен тем, что был выпущен с (довольно ужасной) поддержкой CSS. Было решено, что для возможности конкурировать в Netscape 4 тоже должна быть поддержка этого языка. Однако вместо того, чтобы удвоить усилия по реализации этого третьего языка (после HTML и JavaScript), было решено, что его нужно реализовать преобразованием CSS в JavaScript с последующим его исполнением. Хуже того — было решено, что этот промежуточный язык «JavaScript Style Sheet» должен быть доступен веб-разработчикам.
Синтаксис представлял собой обычный JavaScript с добавлением API стилизации:
tags.H1.color = "blue";
tags.p.fontSize = "14pt";
with (tags.H3) {
color = "green";
}
classes.punk.all.color = "#00FF00"
ids.z098y.letterSpacing = "0.3em"
Можно было даже определять функции, значения которых вычислялись при встрече с каждым вхождением тега:
evaluate_style() {
if (color == "red"){
fontStyle = "italic";
} else {
fontWeight = "bold";
}
}
tag.UL.apply = evaluate_style();
Мысль о том, что нужно упростить разделительную линию между стилями и скриптами, довольно разумна, и сегодня она даже возрождается в сообществе React.
Сам JavaScript в то время был очень молодым языком, но благодаря реверс-инжинирингу его поддержка была добавлена уже в IE3 (в виде JScript). Гораздо более серьёзной проблемой было то, что сообщество в то время уже сплотилось вокруг CSS, а Netscape в то время рассматривался большинством представителей сообщества разработчиков стандартов как нарушитель. Когда Netscape предложил JSSS комитету по стандартам, его пропустили мимо ушей. Три года спустя Netscape 6 отказался от поддержки JSSS и тот постепенно скончался.
Что нас могло ждать
Благодаря публичному порицанию со стороны W3C, Internet Explorer 5.5 был выпущен почти с полной поддержкой CSS1 в 2000 году. Разумеется, как мы теперь знаем, браузерные реализации CSS были ужасно забагованными и трудными в работе по крайней мере ещё в течение десятка лет. К счастью, сегодня ситуация значительно улучшилась, что наконец позволило реализовать мечту разработчиков о том, что можно написать код один раз и он будет функционировать в разных браузерах (почти) одинаково.
Лично я из всего этого сделал вывод о том, насколько произвольными и контекстными являлись решения, которые руководят нашими современными инструментами. Если CSS разрабатывался как способ, совместимый с ограничениями, актуальными в 1996 году, то, может быть, двадцать с лишним лет спустя это даёт нам разрешение делать всё немного иначе.
Автор: AlastorMoody