Компонент без пользовательской документации, theme contract и ручного checklist считается незавершённым и не принимается.
RepUI не угадывает намерения разработчика и не создаёт DOM, которого не запросил пользователь. Компонент должен рендерить ровно ту структуру, которую задаёт его API и переданный контент.
Запрещены implicit-преобразования без отдельного явно обоснованного контракта:
- первый
<h1>не превращается автоматически вCardHeaderилиTitle; - обычная кнопка не становится автоматически
Primary; - дочерний
ButtonIconне создаётся без явного запроса; Badgeне переносится в Header или Overlay самостоятельно;- отсутствующие секции не добавляются «для удобства».
Если компоненту нужна специальная DOM-секция, она должна быть выражена явным API, например {% card_header %}. Если этого тега или параметра нет, секция не появляется. Это сохраняет предсказуемость HTML, облегчает интеграцию с Django и не скрывает структуру от разработчика.
Параметр attrs — осознанный escape hatch для интеграций Django и HTMX. Он
принимает mapping с доверенными именами HTML-атрибутов; RepUI экранирует их
значения, но не нормализует и не ограничивает сами имена.
{% button attrs=button_attrs %}Сохранить{% endbutton %}Передавайте в attrs только mapping, сформированный приложением, а не
непроверенные пользовательские ключи. Для известных атрибутов предпочтительнее
публичные параметры компонента: например aria_label, hx_get или id.
RepUI разделяет CSS на четыре уровня ответственности:
Foundation
↓
Layout
↓
Theme
↓
Components
Стрелка означает не зависимость модулей, а порядок ответственности: каждый следующий слой опирается на гарантии предыдущего и не переносит его обязанности к себе. Components могут использовать публичный Layout API, а Theme остаётся набором CSS-токенов и не импортирует модули.
critical.css задаёт только стабильную геометрию первого кадра:
display,position,flex,grid;min/max/inline/block-size;overflow,contain,box-sizing.
Foundation отвечает на вопрос: «где и какого размера будет элемент?». В нём не должно быть цветов, теней, blur, radius, transition, шрифтов и других декоративных свойств.
Layout распределяет доступную область между Page, Container, Grid и Stack. Он не навязывает размер содержимому и не отвечает за оформление.
Theme отвечает на вопрос: «как элемент выглядит?». Здесь находятся background, foreground, border, shadow, radius, filter, opacity и их component tokens.
Component CSS описывает собственную композицию и состояние, но не должен создавать page-level фундамент. Компонентам запрещено самостоятельно задавать body-reset, body { margin: 0 }, 100dvh для всей страницы или подменять Foundation/Layout. Такие требования должны быть вынесены на соответствующий уровень.
Перед добавлением нового кода ответьте на эти вопросы по порядку:
- Это
Foundation? Если код касаетсяhtml,body, reset,100dvhили page-level overflow, он должен находиться в Foundation. - Это
Layout? Если задача разделяет экран, задаёт колонку/строку или растягивает область, используйте Layout. - Это
Theme? Если изменение только визуальное — background, color, radius, shadow, blur или spacing — оформите token в Theme. - Только если предыдущие ответы отрицательные, это
Component: его задача — поведение и явная композиция.
Зависимости направлены сверху вниз по ответственности, а не наоборот:
Theme не знает о Grid, Grid не знает о Button, Button не знает о Page, а Card не знает о AppBar. Компонент может использовать публичные primitive-слои, но не должен забирать ответственность соседнего или более высокого слоя.
Перед реализацией попробуйте мысленно удалить новый элемент:
- если ломается вся страница — это Foundation;
- если ломается только разметка — это Layout;
- если меняется только внешний вид — это Theme;
- если исчезает одна пользовательская возможность — это Component.
RepUI не угадывает намерения разработчика:
Cardне создаёт Header автоматически;Gridне меняет размеры детей без явного указания;Panelне становится Card;AppBarне раскладывает содержимое;Themeне меняет геометрию;Layoutне рисует оформление.
Публичная DOM-структура и поведение должны следовать явному API и переданному контенту.
Если компонент предоставляет optional runtime, он использует единый lifecycle:
mount...() должен быть идемпотентным, а возвращаемый handle — содержать
element, refresh() и destroy(). Нативные компоненты не эмулируют
браузерную семантику только ради общего API: runtime может быть lightweight
handle без собственных click/keydown listeners.
Перед добавлением компонента проверьте, что в его каталоге есть:
- краткое назначение: что это такое и когда его использовать;
- публичный API: параметры, допустимые значения и defaults;
- runtime API: функции, события и изменяемое состояние, если они есть;
- theme contract: токены, fallback-зависимости и порядок переопределения;
- правила композиции: допустимое содержимое и вложенность;
- HTMX contract:
innerHTML/outerHTML, mount и cleanup; - минимальные автоматические тесты: render, defaults, validation и DOM state;
- короткий
Manual checksдля проверки глазами и действиями.
Для компонентов с runtime отдельно проверяется отсутствие дублированных listeners после повторного mount или HTMX swap.
Страница компонента демонстрирует только его публичный контракт. Не нужно показывать внутренние CSS-классы, приватную DOM-структуру, реализацию template tag или приватные JavaScript-функции. Разделы страницы добавляются только если соответствующая возможность действительно есть в публичном API: например, Card и Panel не требуют пустого Runtime API-блока.
repui/components/<name>/
├── manifest.py
├── README.md
├── examples.py
├── quality.py
└── tests/
└── test_<name>.py
Статические файлы и Django templates размещаются по стандартным каталогам repui/static/ и repui/templates/. Если компонент имеет runtime, его API и события описываются в README.md и проверяются в тестах.
Manifest компонента перечисляет только его собственные component assets. Глобальные Foundation, Theme и Layout-файлы подключаются приложением в фиксированном порядке и не должны добавляться в manifest:
"styles": (
"repui/components/button/button.css",
)Не добавляйте туда repui/foundation/*, repui/theme/* или repui/layout/*. Это не позволяет одному компоненту изменить порядок фундаментального каскада или случайно перекрыть активную тему.
# ComponentName
## Назначение
Коротко: что делает компонент и когда его использовать.
## Public API
| Параметр | Значения | Default | Описание |
|---|---|---|---|
| `...` | `...` | `...` | `...` |
## Runtime API
Функции, события и состояние. Для stateless-компонента явно написать:
`Runtime API отсутствует`.
## Theme contract
| Token | Fallback | Назначение |
|---|---|---|
| `--rui-component-*` | `...` | `...` |
Порядок переопределения: application theme подключается после RepUI theme.
## Composition
Что можно вкладывать внутрь и в каких layout-контейнерах использовать.
## HTMX contract
Описать допустимый swap, необходимость mount и cleanup.
## Manual checks
- [ ] Основной сценарий работает мышью и клавиатурой.
- [ ] Light и dark отображаются корректно.
- [ ] Runtime-состояние применяется без reload.
- [ ] HTMX swap не создаёт дублированных listeners.Manifest — машиночитаемый паспорт компонента. Он не заменяет README, но даёт Workbench возможность в будущем собирать каталог, assets и checklist без ручного центрального реестра.
COMPONENT = {
"name": "component-name",
"docs": {
"summary": "Краткое назначение компонента.",
"manual_checks": (
"primary-interaction",
"light-dark-theme",
"htmx-no-duplicate-listeners",
),
},
"tokens": {
"consumes": {
"--rui-component-background": {
"type": "color",
"fallback": "--rui-color-surface",
},
},
},
}Названия checklist — стабильные machine-readable identifiers, а подробные инструкции остаются в README.md или отдельном документе компонента.
- обновить README, manifest и checklist;
- добавить или обновить минимальные тесты;
- проверить demo в Workbench вручную;
- выполнить тесты соответствующего компонента и
manage.py check; - не добавлять скрытую совместимость или fallback без описания в документации.
RepUI предназначена для backend-разработчиков.
Если при проектировании нового API приходится думать категориями Flexbox, CSS Grid, margin collapse или другими деталями браузера, вероятно, API находится на слишком низком уровне.
Публичный API должен описывать намерение разработчика, а не механизм реализации.
Каждый компонент RepUI может содержать собственную Workbench-композицию:
repui/components/<component>/<component>.html
Этот файл не является шаблоном самого компонента. Он предназначен для демонстрации, ручной проверки и объяснения публичного API компонента.
Приложение workbench только обнаруживает и монтирует эту композицию.
Workbench-композиция должна иметь один корневой Layout-компонент:
{% container %}
...
{% endcontainer %}или:
{% stack %}
...
{% endstack %}или:
{% grid %}
...
{% endgrid %}Обычный <div> не считается корневым Layout-контрактом.
Workbench-композиция компонента не должна:
- содержать
{% page %}; - содержать собственный AppBar страницы;
- содержать sidebar приложения;
- использовать
{% extends %}; - объявлять или переопределять
{% block %}; - знать URL, views или внутреннюю разметку приложения
workbench; - зависеть от прикладных шаблонов
workbench; - требовать специальной инициализации только ради монтирования страницы.
Page, AppBar, sidebar, navigation и рабочая область принадлежат shell приложения workbench.
Workbench-композиция может использовать:
- публичные Layout-компоненты RepUI;
- Card, Panel, Button и другие публичные компоненты;
- обычный семантический HTML;
- HTMX для локальных демонстраций;
- runtime API демонстрируемого компонента;
- прикладные стили, принадлежащие самому компоненту.
component
manifest
request
Композиция не должна требовать дополнительного контекста для базового рендера.
Замена Page на BasePage, изменение AppBar, sidebar или всей оболочки Workbench не должна требовать изменений в файлах:
repui/components/*/*.html
каталог компонента существует
→ пункт появляется
<component>.html отсутствует
→ пункт disabled
<component>.html существует и компилируется
→ пункт активен
Добавление нового компонента RepUI не должно требовать изменения кода приложения workbench.