Стилобат

Наброски будущего

Задачи и идеи

Независимость

Самая главная идея: отделение стилей от селекторов.

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

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

Отделение визуальных стилей от раскладки

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

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

Реализация

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

Реализацию можно разделить на две части: описание стилей «блоков» и их применение. Части не обязательно должны быть сильно отделены друг от друга — получение стилей из описания и их применение может (и, наверное, должно) происходить в одном инструменте.

Я предлагаю как для описания, так и для применения использовать расширение CSS-синтаксиса. Аргументы:

  1. В этом случае мы не придумываем новый язык, поэтому можем использовать уже имеющиеся парсеры и инструменты, например, парсер Gonzales, уже используемый в CSSO и CSSComb.js.

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

  3. Расширение синтаксиса CSS должно быть максимально «безопасным»: таким, чтобы любой другой инструмент принял написанное за CSS и не сломался. Скажем, в обычном CSS не бывает конструкций со вложенными селекторами, но бывают вложенные @-query, следовательно для любых вложенных друг в друга сущностей можно использовать именно их.

  4. Что может лучше подходить для описания CSS, чем сам CSS? Используя синтаксис CSS мы получаем привычный и максимально подходящий задаче синтаксис, который не придётся учить тем, кто знает только CSS. Кроме того, так как этот синтаксис должен быть совместим с препроцессорами, люди, которым стандартный синтаксис CSS неприятен, смогут использовать любой альтернативный синтаксис (вроде синтаксиса, основанного на отступах, как в Sass или Stylus), и сначала запускать препроцессор, преобразуя код в CSS, а потом уже запуская на полученный CSS наш инструмент.

Описание стилей

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

Стили для блоков хочется описывать в слегка декларативном ключе, используя расширение CSS-синтаксиса.

Блоки описываются по методологии слегка напоминающей БЭМ — есть корневая сущность — блок, у него могут быть модификации и что-то вроде элементов.

Блок

Базовый блок будет выглядеть примерно так:

@stylobate foo {
    @css {
        display: block;
        width: 10px;
    }
}

Вопросы:

  1. нужно ли позволять применять CSS напрямую внутри блока? Кажется, что нет.

  2. Стоит ли такие «финальные селекторы» делать без собачки, скажем, просто css {…}? Возможно. С запретом вложения селекторов это даст более строгий синтаксис и позволит выносить все вложенные сущности отдельными блоками.

  3. Стоит ли всем подсущностям вроде @css добавлять неймспейс? Типа @stylobate-css? Есть такая вероятность: мало ли как различные препроцессоры будут интерпретировать подобные сущности — они вполне могу с чем-то пересечься, в этом случае добавление уникального префикса решит любые возможные проблемы в будущем.

  4. Стоит ли для блока использовать имя @stylobate-block? Или же можно взять просто @stylobate?

Элемент

Любая вложенная в блок сущность является элементом этого блока. Однако, это «элемент» не совсем в БЭМ-терминологии, такая сущность не обязательно будет иметь свой HTML-элемент на странице: он может быть подмешан к текущему блоку через какое-то условие, или быть расширяем внешними блоками.

Стили элемента можно прописать как вложенными к блоку:

@stylobate foo {
    @stylobate-css {}

    @stylobate-element colors {
        @stylobate-css {
            color: #000;
            background: #FFF;
        }
    }
}

Или же должна быть возможность дополнить любой имеющийся блок извне, например

@stylobate-element colors (block: foo) {
    @stylobate-css {
        color: #000;
        background: #FFF;
    }
}
Элементы по умолчанию

По умолчанию стили элемента ни к чему не применяются. Для вызова стилей элемента можно использовать параметр selector:

@stylobate-element colors (selector: "&") {
    @stylobate-css {
        color: #000;
        background: #FFF;
    }
}

В качестве селектора может выступать как и абсолютный селектор, так и относительный — с использованием т.н. «parent reference» — термина, знакомого по препроцессорам. Так, значение селектора "&_theme_bright" означает, что по умолчанию этот элемент применится как модификатор. По сути, в Стилобате нет разницы между элементами и модификаторами — ведь эта разница лишь в именовании классов, тогда как мы полностью отвязываемся от селекторов. Но для удобства «элементами» мы будем называть неизменяемые дополнительные стили, вызываемые без каких-либо параметров, тогда как модификаторами будут элементы вида «свойство: значение».

Модификаторы

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

@stylobate foo {
    @stylobate-modifier theme (default: normal) {
        @value normal {
            color: #000;
            background: #FFF;
        }
        @value bright {
            color: lime;
            background: yellow;
        }
        @value inverted {
            color: #FFF;
            background: #000;
        }
    }
}

Как видно, для модификаторов не обязательно использовать @stylobate-css, кроме того существует опциональный параметр default, задающий применяемое значение по умолчанию. Аналогично элементам можно добавить и параметр selector для переопределения стандартного селектора (&).

Наследование

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

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

Таким образом, если у нас есть какой-то вот такой блок:

@stylobate foo {
    @css {
        display: block;
        width: 10px;
    }
    @stylobate-element colors{
        @stylobate-css {
            color: #000;
            background: #FFF;
        }
    }
    @stylobate-modifier theme (default: normal) {
        @value normal {
            color: #000;
            background: #FFF;
        }
    }
}

Мы можем отнаследовать от него другой:

@stylobate bar (extends: foo) {}

Если ничего больше не писать, то блок bar, по сути, будет алиасом к foo.

Любые вложенные сущности дополнят родительский блок, так

@stylobate bar (extends: foo) {
    @css {
        height: 10px;
    }
    @stylobate-modifier theme {
        @value normal {
            padding: 10px;
        }
        @value crazy {
            position: absolute;
        }
    }
}

добавит соответствующий CSS к тому, что было прописано для foo и его модификатора со значением normal, кроме того у bar появится новое значение для модификатора themecrazy, которого не будет у родителя. Любые опущенные параметры будут браться от родителя, так у bar дефолтным значением модификатора theme останется normal.

Замена вместо наследования

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

@stylobate bar (extends: foo) {
    @css (replaces: true) {
        height: 10px;
    }
}

В этом случае bar не отнаследует CSS у родительского блока, а целиком заменит его на новый.

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