К содержанию

Навигация

Уровни Framework

Framework устроен уровнями: от дизайн-токенов и утилит до Smart-компонентов и макетов. Здесь — что даёт каждый уровень и как выбрать нужный для задачи.

Уровни Framework

Framework устроен как лестница уровней. Каждый следующий опирается на предыдущие и не заменяет их.

Уровень Что даёт Пример Когда выбирать
Дизайн-токены (Core) Общие величины: цвет, размер, отступ, шрифт, темы --sf-primary, --sf-space-1 Всегда — это основа всего остального
Утилиты Одно свойство элемента в одном классе p-4, gap-2, bg-surface-container Раскладка и точечное оформление
Компоненты Готовая структура и оформление повторяемого элемента .sf-button Элемент без собственного поведения
Smart-компоненты Собственный тег с поведением, состоянием и событиями <sf-button>, <sf-tabs> Нужны ввод, состояние, события, данные
Составные Smart-компоненты Несколько Smart-компонентов с общей логикой <sf-datepicker>; таблица данных с поиском и фильтрами Повторяется целый сценарий
Макеты Страница целиком как проверяемые данные JSON-документ страницы Структурой страницы нужно управлять

Дизайн-токены и Core

Core — обязательная основа. Он содержит базовые стили, дизайн-токены, темы, порядок каскадных слоёв и Loader.

Дизайн-токен — именованная CSS-переменная с префиксом --sf-, которая хранит одно дизайнерское решение. Токены устроены в два этажа:

  • примитивы — сырые величины: ступени палитры (--sf-primary-40, --sf-neutral-90) и базовые шаги размеров;
  • роли — смысловые величины, через которые работает интерфейс: «основной цвет» (--sf-primary), «текст на поверхности» (--sf-on-surface), «граница контрола» (--sf-outline), «отступ первой ступени» (--sf-space-1).

Роль ссылается на примитив. Например, в Core записано:

CSS
--sf-primary: light-dark(var(--sf-primary-40), var(--sf-primary-80));

Поэтому светлая и тёмная тема — не два набора стилей, а два значения одной роли. Интерфейс пользуется ролями; примитивы напрямую в разметку не попадают. Тему Core переключает классом theme-light или theme-dark на элементе <html> (по умолчанию — по системной настройке).

Стили Framework разложены по каскадным слоям в фиксированном порядке: sf.reset, sf.tokens, sf.base, sf.utilities, sf.components, sf.overrides, sf.states. Порядок слоёв, а не случайная специфичность селекторов, решает, какое правило сильнее. Подробно о токенах, цвете, темах и слоях — в разделе Токены, цвет и темы (см. Дизайн-токены, Каскадные слои и Темы); как менять дизайн — в Как менять дизайн; о размерах и лестнице — в Одна лестница размеров.

Утилиты

Утилита — короткий CSS-класс, который задаёт одно свойство: отступ, цвет, скругление, расположение, размер шрифта. Значение утилита берёт из токенов: gap-2 означает gap: var(--sf-space-2), а не «16 пикселей».

HTML
<div class="flex flex-col gap-2 p-4 bg-surface-0">
  <h1 class="text-2">Заголовок</h1>
  <p class="color-on-surface-variant">Текст страницы.</p>
</div>

Пример

Отступы и расположение элементов панели заданы одними утилитами.

НазваниеЗначение

У утилит нет префикса sf-: префикс носят компоненты. Утилита может действовать по условию — например, начиная с ширины экрана: md:gap-2. Синтаксис, условия и раскладка — см. Как устроено имя утилиты, Условия действия и Раскладка.

Компоненты

Компонент — устойчивая HTML-структура с готовым оформлением и вариантами одного элемента: кнопки, поля, карточки, вкладок. Классы компонентов начинаются с sf-, варианты пишутся через двойной дефис:

HTML
<button type="button" class="sf-button sf-button--default sf-button--primary sf-button--size-1">
  <span class="sf-button-text-container">Сохранить</span>
</button>

Разметку компонента пишет проект; оформление и, если нужно, небольшое поведение (раскрыть, закрыть) приносит Framework. Компонент собран из тех же токенов, что и утилиты, поэтому подчиняется темам и общей лестнице размеров. Точное имя класса, варианты и состояния проверяйте на странице компонента в справочнике «Компоненты».

Пример

Подробно — см. Анатомия: корень, части, модификаторы, состояния и Настройки: общий язык компонентов.

Smart-компоненты

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

HTML
<sf-button text="Сохранить" scheme="primary" size="1"></sf-button>

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

HTML
<sf-button text="Сохранить" scheme="primary"></sf-button>

Обычный компонент отвечает на вопрос «как элемент выглядит», Smart-компонент добавляет «как он себя ведёт». Своего оформления Smart-компонент не изобретает: он строит ту же разметку с теми же классами компонентов. Поэтому Smart-кнопка и кнопка на классах выглядят одинаково.

Настройки у всех уровней называются одинаково: appearance (как выглядит), status (о чём сообщает), kind (чем является), size (ступень лестницы), scheme (какой смысловой цвет). Каждый Smart-компонент описан манифестом — машиночитаемым файлом с его настройками, событиями, состояниями и уровнем готовности. Подробно — в разделе Smart-компоненты (см. Что это и что даёт и Манифест).

Составные Smart-компоненты

Составной Smart-компонент собран из других Smart-компонентов и согласует их работу в одной пользовательской задаче. Таблица данных со строкой поиска, фильтрами, сортировкой и постраничным выводом — один такой компонент, а не четыре отдельных, которые проект связывает вручную. Выбор даты (<sf-datepicker>) — тоже составной компонент: поле, кнопки и календарь в нём работают как одно целое.

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

HTML
<project-order></project-order>
<p id="order-log" role="status">Выберите количество в карточке.</p>
JavaScript
async function start() {
  if (typeof window.SF?.loadSmartBase !== 'function') {
    await new Promise((resolve, reject) => {
      const ready = () => { clearTimeout(timeout); resolve(); };
      const timeout = setTimeout(() => { window.removeEventListener('sf-loader-init', ready); reject(new Error('Loader не запустился')); }, 15000);
      window.addEventListener('sf-loader-init', ready, { once: true });
    });
  }
  await SF.loadSmartBase();
  const { SfBaseElement, html } = SF.smart;
  class ProjectQuantity extends SfBaseElement {
    static get props() { return { count: { type: Number, default: 1 } }; }
    template() {
      return html`<button type="button" @click=${() => {
        const value = this.getProp('count') + 1;
        this.setAttribute('count', String(value));
        this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true, detail: { value } }));
      }}>Количество: ${this.getProp('count')}</button>`;
    }
  }
  class ProjectProduct extends SfBaseElement {
    template() { return html`<article class="p-2 bg-surface-container radius-2"><h3>Кружка</h3><project-quantity></project-quantity></article>`; }
  }
  class ProjectOrder extends SfBaseElement {
    template() { return html`<section aria-label="Заказ"><h2>Мой заказ</h2><project-product></project-product></section>`; }
  }
  ProjectQuantity.define('project-quantity');
  ProjectProduct.define('project-product');
  ProjectOrder.define('project-order');
  document.querySelector('project-order').addEventListener('change', event => {
    document.querySelector('#order-log').textContent = `Заказ получил событие от ${event.target.localName}: ${event.detail.value}`;
  });
}
start().catch(error => { document.querySelector('#order-log').textContent = error.message; });

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

Составной Smart-компонент принадлежит своему владельцу. Framework содержит универсальные составные компоненты. Составной компонент, который нужен одному продукту, — например, представление записей его административного интерфейса, — продукт хранит и поставляет сам, в пакете продукта (см. Пакеты компонентов продуктов). С данными приложения такой компонент работает через публичный порт Framework (см. Порт хоста).

Макеты и блоки

Макет — описание страницы в виде проверяемого JSON-документа (схема simai.composition.document). Он хранит, из каких секций, колонок и компонентов состоит страница и в каком порядке они идут, а Core превращает это дерево в HTML через браузерный интерфейс SF.Composition. Макет хранит ссылку на данные, но не сами данные, права или операции.

Макет нужен не всегда. Обычная страница пишется HTML-разметкой. Макет добавляют, когда структуру страницы нужно хранить, передавать между системами, редактировать в инструменте или собирать по настройкам сайта. Подробно — в разделе Макеты и композиция.

Пример: в макете каталога корневой узел описывает страницу, секция содержит заголовок, а колонки разделяют фильтры и карточки товаров. Ссылка на данные говорит проекту, какие товары получить; сами товары, права и операции в документ не записываются.

JSON
{
  "schema": "simai.composition.document",
  "schema_version": 1,
  "id": "catalog-page",
  "profile": "ui-layout",
  "locale": "ru",
  "root": {
    "id": "page",
    "type": "layout.page",
    "slots": {
      "default": [
        {
          "id": "catalog",
          "type": "layout.section",
          "presentation": {"preset": "surface", "modifiers": ["contained"]},
          "bindings": [{"owner": "project/catalog", "ref": "products", "target": "items", "revision": "42"}],
          "slots": {
            "default": [
              {"id": "title", "type": "content.heading", "data": {"level": 1, "content": [{"type": "text", "value": "Каталог товаров"}]}},
              {"id": "lead", "type": "content.paragraph", "data": {"content": [{"type": "text", "value": "Framework показывает макет, а проект получает товары и проверяет права."}]}},
              {
                "id": "columns",
                "type": "layout.columns",
                "props": {"columns": 2},
                "presentation": {"modifiers": ["responsive"]},
                "slots": {"columns": [
                  {"id": "filters", "type": "content.paragraph", "data": {"content": [{"type": "text", "value": "Фильтры"}]}},
                  {"id": "products", "type": "content.paragraph", "data": {"content": [{"type": "text", "value": "Карточки товаров"}]}}
                ]}
              }
            ]
          }
        }
      ]
    }
  }
}

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

Пример ниже — проектный блок на HTML: заголовок, описание, цена и кнопка. Он не объявляет новый тип Framework.

HTML
<section class="p-3 bg-surface-container radius-3" aria-label="Предложение"><h2>Кружка</h2><p>Керамическая кружка, 300 мл.</p><strong>790 ₽</strong><sf-button text="Подробнее" scheme="primary"></sf-button></section>

Как уровни опираются друг на друга

Одну и ту же кнопку можно проследить через все уровни:

  1. Токен --sf-primary хранит основной цвет; в тёмной теме у него другое значение.
  2. Утилиты и стили компонента берут цвет и отступы из токенов, а не из чисел.
  3. Класс sf-button собирает из этого готовую кнопку.
  4. Тег <sf-button> строит ту же разметку с классом sf-button и добавляет поведение: состояние загрузки, блокировку, события.
  5. Составной компонент ставит такие кнопки в панель действий и связывает их с выбором строк таблицы.
  6. Макет говорит, на каком месте страницы стоит эта таблица.

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

Правило выбора уровня

Берите самый простой уровень, который решает задачу целиком и без дублирования.

  • Нужно одно свойство (отступ, цвет, выравнивание) — утилита.
  • Элемент повторяется и должен выглядеть одинаково — компонент.
  • Элементу нужны ввод, состояние, события или данные — Smart-компонент.
  • Повторяется целый сценарий из нескольких интерактивных частей — составной Smart-компонент.
  • Структуру страницы нужно хранить и собирать как данные — макет.

Обратное тоже верно: не берите уровень сложнее, чем нужно. Статичную подпись не стоит делать Smart-компонентом, а единственный отступ — новым компонентом. Внутри компонента точечную поправку делает утилита.

Поиск по документации

Введите минимум 2 символа

    Настройки

    Тема
    Размер шрифта
    Ширина страницы