Стилобат

Концепция

Дисклеймер: концепция изменилась! Точнее, в основе того, что будет в будущем, продолжат лежать те же принципы, но это будет не «фреймворк на стайлусе», а «постпроцессор на ноде», независимый от препроцессора и диалекта CSS. Чуть подробнее об этом см. наброски будущего.

Вводная

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

Этот проект предполагает, что нужно выкинуть все имеющиеся навыки вёрстки — забыть про привычные методологии и концепции, забыть про многие аспекты вёрстки, и попробовать посмотреть на всё под другим углом.

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

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

Ещё важнее: скажите привет Опенсорсу — пишите замечания, сообщайте об ошибках, критикуйте подход и всё-всё-всё, присылайте пулл-реквесты! По фреймворку или конкретной реализации — issues в соответствующих проектах на Гитхабе, всё остальное можно писать мне на рабочую почту — kizu@.

Абсолютная независимость

АНБ

Широко известная концепция АНБ — Абсолютно Независимых Блоков — упирает на то, что любой блок должен стремиться быть независимым от окружения и стилей других независимых блоков. Однако, в АНБ упускается то, что подобный блок остаётся зависимым от одной сущности. Себя.

Это значит, что стили блока привязаны к конкретным имени и HTML-структуре блока.

Подобный CSS, состоящий из имён классов будь то .button, .b-button или b-mail-button сложно (а то и невозможно) применять на стороннем проекте. По сути, мы должны знать структуру HTML, к которой применяется необходимый CSS, мы должны гарантировать, что не будет пересечений в именах классов, а выбрав конкретную реализацию с конкретными именами классов мы не сможем просто переключиться на другую реализацию — нам придётся менять все классы или в HTML, или в CSS.

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

Препроцессоры

Но — у нас же есть препроцессоры.

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

Чтобы описать нужный блок, можно написать вот так:

.super-button
  kind: button
  skin: action-button fly small

Вместо того, чтобы прописывать все стили к этому классу напрямую.

Можно увидеть, что в коде используется только комбинация двух псевдо-CSS свойств: kind и skin, со всякими аргументами. Ниже на этой странице я объясню как это работает и почему оно работает именно так.

Если кратко, то наша таблица стилей подключает, во-первых, фреймворк Stylobate, во-вторых, реализованные с помощью этого фреймворка визуальные стили, лежащие миксинами в отдельном проекте. Подобное разделение, как уже упоминалось, позволяет обновлять стили в миксинах не затрагивая HTML-представление и конкретные классы. Можно подключить «портальный дизайн», реализованный подобным образом, на несколько разных проектах — с разными классами и подходами к вёрстке, и получить возможность обновлять всё одним git pull в сабмодуле реализации.

«Типы» (kind)

«Тип» (его ещё можно называть «паттерном») — основной компонент любого блока. Это специальный миксин, который должен быть максимально независим от визуальной стороны блока (если это не его основное свойство и если это не опциональный параметр), который является «кирпичиком», лежащим в основе любого блока, задающий его базовую раскладку и поведение.

На данный момент все готовые типы собраны в основном репозитории фреймворка — stylobate, документация по ним появится на этом сайте позже.

Использовать «тип» довольно просто — он выглядит как обычное CSS-свойство kind, значением которого является тип (или типы), применяемые к нужному селектору:

.btn
  kind: button

Если нужно как-то параметризовать тип, дополнительные аргументы можно передавать сразу после имени типа:

.frame
  kind: fill fixed 1em

Первым значением всегда идёт имя типа, остальные значения — параметры этого типа.

За раз можно задать больше одного типа, перечисляя их через запятую:

kind: block, clear

Если тип нужно использовать внутри объявления другого типа, из-за ограничений Стайлуса, нужно вызвать типы как функции:

kind-button()
  kind-pill()

  …

Скины (skin)

«Скин» — визуальная сторона блока, весь смысл фреймворка заключается именно в том, чтобы разделять вёрстку каждого блока на раскладку и отображение. Чаще всего разные по раскладке и поведению блоки могут иметь разное отображение («острова» могут быть как в контенте, так и попапами, «кнопка» — не только кнопка, но и чекбокс или радиобатон, и так далее), кроме того на странице может возникнуть желание использовать несколько разных по внешнему виду блоков, но имеющих одно поведение. Например, кнопка и псевдо-кнопка — имеют одинаковое поведение, но разный внешний вид.

Скин в применении почти во всём аналогичен типу: можно перечислять несколько типов через запятую, первое значение — имя скина и так далее.

.button
  skin: button

.promo-button_s
  skin: button 10px promo

Неймспейсы

Однако, если «типы» по задумке должны быть одинаковыми для всех сайтов (так как существует конечное число возможных раскладок и паттернов вёрстки), то скины могут быть объединены в «наборы», например, теоретически, может существовать скин твиттеровского бутстрапа и скин яндексовых островов, и должна быть возможность использовать разные скины одновременно.

Для этого у скинов, в отличие от типов, есть понятие неймспейса. Скажем, может существовать два набора из кнопок и инпутов:

skin-bootstrap-button()
skin-bootstrap-input()

skin-islands-button()
skin-islands-input()

Если при написании скинов всегда использовать префикс (bootstrap и islands в примере выше), то пересечений не возникнет и можно будет подключать сколько угодно скинов, не боясь конфликтов.

Понятно, что если использовать только «островные» контролы, то писать каждый раз

skin: islands-button

skin: islands-input

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

@import "skins/islands"
set-skin-namespace('islands')

То пример с соответствующими кнопкой и инпутом можно будет записать так:

skin: button

skin: input

Что делать, если захочется использовать скины из другого набора при включённом неймспейсе? Пока что вариант один: использовать полную запись в виде функции:

skin: button

skin: input

skin-bootstrap-button()

В дальнейшем, возможно, появится возможность использовать скины из других наборов и CSS-like записи.

Параметризация типов или скинов

И скины, и типы можно вызывать с различными параметрами. Если правильно писать миксины типов и скинов, то порядок разнотипных свойств будет неважен, но для этого нужно знать несколько правил о том, как получать в миксинах скинов и типов нужные параметры.

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

Существует несколько сценариев использования параметров при написании миксинов:

Проверка на наличие какого-либо ключевого слова

Делается нативными средствами Стайлуса. Допустим, у нас может быть какая-то промо-кнопка. Мы хотим проверять в скине кнопки ключевое слово promo на наличие, которое, например, будет передаваться вот так:

.promo-button
  skin: button promo

Для этого нужно написать в нашем скине такую проверку:

skin-button()
  // …

  if promo in arguments
    // Наш код

Получение аргумента определённого типа

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

skin-button()
  padding: get_unit(arguments, 10px)
  background: get_color(arguments)
  margin-top: get_unit(arguments, $index: 1)

  &:before
    content: get_string(arguments)

Видно, что все хелперы имеют имя вида get_ + тип объекта в Стайлусе, вторым аргументом может идти опциональный фолбек, третьим аргументом (или именованным $index) — порядок аргумента этого типа при вызове. Таким образом, не нужно заботиться о порядке и наличии неоднотипных аргументов, порядок становится важным только если мы будет передавать несколько однотипных аргументов.

Получение аргумента по фолбеку

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

skin-button()
  padding: alike(arguments, 10px)
  background: alike(arguments, #EEE)
  margin-top: alike(arguments, 0, 1)

  &:before
    content: alike(arguments, "")

Таким образом мы всегда используем только один хелпер — это и короче, и не нужно помнить тип аргумента. Ведь если мы передаём фолбек, то мы знаем его тип, а, значит, нам и не нужно вызывать правильно именованный хелпер.

Получение одного из возможных свойств

Если на вход может приходить одно из списка свойств, например хочется прокидывать возможные свойства для position, такие как static и fixed, можно использовать функцию retrieve:

kind-fill()
  position: retrieve(arguments, fixed static sticky, absolute)

Эта функция работает очень просто:

  1. Первым аргументом привычно передаём то, где ищем аргументы.
  2. Вторым аргументом передаём искомое свойство или массив свойств.
  3. Третьим аргументом передаём фолбек.

Получение массива значений-сторон

Если хочется иметь возможность указывать те или иные стороны в формате шортхендов (как для margin/padding), можно использовать функцию get_sides:

kind-fill()
  $sides = get_sides(arguments)
  top: $sides[0]
  right: $sides[1]
  bottom: $sides[2]
  left: $sides[3]

Она автоматически «достанет» единицы измерения, записанные в формате trbl1em 2em, 1em 2em 3em 4em и т.д.

Пока что для того, чтобы «пропускать» те или иные значения можно задавать ключевые слова top и подобные, в этом случае будут пропущены противоположные стороны, но это временное решение, в дальнейшем надо будет использовать ключевое слово auto (но пока оно не работает).

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

Переменные

Если в скинах задаются глобальные переменные, надо или использовать префикс скина, например $islands_xs предпочтительнее $xs, или же, если переменные относятся к каким-то скинам, использовать имя скина в качестве префикса — $skin-islands-arrow-height. Это позволит избежать конфликтов с другими скинами и/или миксинами Стайлуса.

Если же планируется использовать только один скин, то во время его применения можно будет дописать в своей таблице стилей шорткаты вида $xs = $islands_xs.

Яндекс.Метрика