# Startup UI — full documentation > Vue 3 UI component library for IT startups (great with Laravel + InertiaJS). Every component is prefixed with `S` (SButton, SInput, …) and strictly typed — read prop/emit/slot types from the .d.ts. FontAwesome and InertiaJS are optional, injected once via app.use(StartupUI, { icon, router, link }). Component docs below are written in Russian; the default runtime locale is English (switchable via configureStartupUi). --- # Что такое Startup UI Это библиотека UI-компонентов для [JavaScript-фреймворка Vue3](https://vuejs.org/), заточенная под типовые задачи IT-стартапов. Нативно интегрируется с эффективным для IT-стартапов стеком [Laravel](https://laravel.com/) + [InertiaJS](https://inertiajs.com/). Разработан и активно используется в [Стартап-Бюро «Сухарь и партнеры»](https://suhar.ru/) в первую очередь для собственных стартапов, и предлагается для использования и создания своих стартапов всем веб-энтузиастам под лицензией MIT. Чтобы более подробно изучить Startup UI, рекомендуем скачать example-app и локально запустить его по инструкции ## Зачем мы сделали ещё одну библиотеку компонентов Все началось с того, что мы разработали три IT-стартапа с использованием [Element Plus](https://element-plus.org/), а потом переписали один из них под [PrimeVue](https://primevue.org/). В целом обе библиотеки отличные, но мы постоянно сталкивались с рядом сложностей: 1. Китайская Element Plus заточена под привычные в китайском сегменте пользовательские интерфейсы, американская PrimeVue — под американские. Ни там, ни там нет, например, подсказок под полями ввода в формах, или кликабельного лейбла у свитчера, которые привычны в русскоязычных интерфейсах. Из-за этого разные программисты реализовывают такие интерфейсы разным кодом, они выглядят по-разному и их проблемно переиспользовать между разными проектами. 2. В классических Vue3-библиотеках не хватает более глубокой интеграции со стеком Laravel + InertiaJS: чтобы формы сразу выводили ошибки валидации, пришедшие из FormRequest-ов; чтобы фильтры и постраничная навигация бесшовно работали в связке с SPA-роутером InertiaJS. 3. В обоих библиотеках поддержка иконок реализована на уровне собственной небольшой библиотеки иконок. Не хватает нативной поддержки [Font Awesome](https://fontawesome.com/) — самой большой библиотеки бесплатных иконок, которую очень удобно использовать для быстрого создания интерфейсов в IT-стартапах. 4. Локализация на русский язык идет по совсем остаточному принципу, и периодически это выглядит как машинный перевод. 5. ... и многие другие моменты. В [документации компонентов](/pages/components/forms/sform.html) почти для каждого компонента мы описываем блок «В чем отличие от аналогов?», в котором можно прочитать про это более детально. Недостающий функционал мы стали реализовывать собственными новыми SFC-компонентами, а также обертками вокруг компонентов популярных библиотек. Со временем получилась вот такая библиотека, которой мы хотим поделиться с вами, чтобы вы быстрее и эффективнее могли создать свой IT-стартап, и как можно больше выдающихся IT-продуктов (в том числе глобальных) создавалось нашими соотечественниками! ## Как и зачем можно использовать Startup UI Библиотека распространяется под лицензией MIT — используйте, как хотите. Но эффективнее всего будет использовать эту библиотеку в веб-интерфейсе IT-стартапа (особенно хорошо для B2B SaaS), чтобы ускорить первоначальную реализацию, упростить взаимозаменяемость между страницами стартапа, дальнейшую поддержку, и внедрение нового функционала. С использованием стека Laravel + InertiaJS + Vue3 + Startup UI один веб-программист может создавать стартапы с нуля до полноценного работоспособного MVP за 3 недели. (Мы в [Стартап-Бюро](https://suhar.ru/) так и делаем.) --- # Установка Добавляем Startup UI в свой проект и подключаем первый компонент. ## Добавляем в проект Startup UI доступен в репозитории npm. ```sh # Если используете npm npm install startup-ui # Если используете yarn yarn add startup-ui ``` ## Подключаем стили ```js // app.js import 'startup-ui/dist/index.css'; ``` ## Подключаем палитру Начиная с мажорной версии библиотека **не включает** значения CSS-переменных `--s-*` в свой CSS — она их только использует (`var(--s-primary)` и т.п.). Это убирает конфликт «несколько источников одной переменной» в инструментах и даёт проекту полный контроль над палитрой. Выберите один вариант: ```js // Вариант А: подключить готовую палитру по умолчанию import 'startup-ui/defaults.css'; ``` ```scss /* Вариант Б: задать свою палитру (resources/css/variables.scss) */ :root { --s-primary: #2563eb; --s-primary-dark: #1e40af; /* ...остальные --s-* (полный список — на странице «Цвета и настройки») */ } ``` ## Регистрация плагина и локализация Можно импортировать компоненты по отдельности (как ниже), либо зарегистрировать плагин глобально. При регистрации задаётся локаль (по умолчанию — **английская**): ```js // app.js import StartupUI from 'startup-ui'; app.use(StartupUI, { locale: 'ru', // 'en' по умолчанию; 'ru' включает русскую локализацию // messages: { ru: { confirm: { accept: 'Подтвердить' } } } // точечное переопределение строк }); ``` Подробнее — на странице [«Локализация»](/pages/welcome/basics/localization). ## Подключение с InertiaJS Startup UI не требует InertiaJS. Без него ссылки в меню и пагинации работают как обычные `` (с перезагрузкой страницы), а формы отправляйте через событие `@submit`. Если же проект на Inertia, передайте при регистрации плагина две вещи из `@inertiajs/vue3` — роутер `router` и компонент ссылок `Link`. После этого компоненты с навигацией начнут работать «по-Inertia», без перезагрузки страницы: ```js // app.js import StartupUI from 'startup-ui'; import { router, Link } from '@inertiajs/vue3'; app.use(StartupUI, { locale: 'ru', router, // отправка SForm, пагинация SPagination, фильтры SFilterGroup (bind-to-query) link: Link, // ссылки в меню (SMenu / SVerticalMenu) и пагинации }); ``` Что это включает: - **SForm** — отправку формы по `method`/`action` (Inertia-визит с подстановкой ошибок валидации); - **SPagination** и **SFilterGroup** (`bind-to-query`) — переключение страниц и фильтров без перезагрузки; - **меню и пагинацию** — SPA-ссылки вместо обычных ``. На стороне бэкенда: для клиента @inertiajs/core 3.x включите в config/inertia.php опцию use_script_element_for_initial_page — иначе начальная страница не прочитается и вы получите пустой экран без ошибок. ## Подключение иконок Иконки в `SActionIcon`, `SNote`, `SStatus` (и опционально в `STooltip`, `SDatePicker`) рендерятся через FontAwesome — она **опциональна** и подключается отдельно. Как установить FA и зарегистрировать рендерер (глобально или инъекцией при подключении плагина) — на странице [«Иконки»](/pages/welcome/basics/icons). ## Добавляем первый компонент Чтобы убедиться, что всё работает, добавляем любой компонент в SFC-шаблон:
```vue // Index.vue ```
Должна появиться кнопка:
Все работает!
--- # Локализация Все пользовательские строки компонентов берутся из единого словаря. Локаль и переопределения задаются один раз — централизованно. ## Выбор локали Дефолтная локаль — **английская**. Русская (`ru`) встроена и включается при регистрации плагина: ```js // app.js import StartupUI from 'startup-ui'; app.use(StartupUI, { locale: 'ru' }); ``` Если не используете глобальный плагин (импортируете компоненты по отдельности), настройте локализацию напрямую: ```js import { configureStartupUi } from 'startup-ui'; configureStartupUi({ locale: 'ru' }); ``` ## Переопределение строк `messages` сливаются (deep-merge) поверх встроенных словарей — переопределять можно точечно, по отдельным ключам: ```js app.use(StartupUI, { locale: 'en', messages: { en: { confirm: { accept: 'Confirm', cancel: 'Dismiss' }, pagination: { perPage: 'Show {n}' }, }, }, }); ``` ## Своя локаль Можно добавить произвольную локаль целиком, передав полный словарь и выбрав её: ```js import { configureStartupUi } from 'startup-ui'; import de from './locales/startup-ui.de'; configureStartupUi({ locale: 'de', messages: { de } }); ``` ## Смена локали в рантайме ```js import { setLocale } from 'startup-ui'; setLocale('en'); // все компоненты реактивно перерисуются ``` ## Региональные локали Коды с региональным субтегом резолвятся с фолбэком по базовому языку: `en-US` → `en`, `ru-RU` → `ru` (и в конце — `en`). Поэтому можно безопасно передавать `navigator.language`. Встроенная локаль `en-US` отличается от `en` первым днём недели — **воскресенье** (в `en`/`ru` неделя начинается с понедельника) — и **12-часовым форматом времени** с AM/PM в `SDatePicker`: ```js configureStartupUi({ locale: 'en-US' }); // SDatePicker: неделя с воскресенья, время в формате AM/PM ``` Первый день недели можно задать и точечно — пропом `first-day` у `SDatePicker` (`0` — вс, `1` — пн). ## Доступные ключи | Группа | Ключи | |--------|-------| | `confirm` | `title`, `accept`, `cancel` | | `pagination` | `shown`, `of`, `perPage` (плейсхолдер `{n}`) | | `select` | `noData` | | `table` | `noData` | | `canvas` | `sidebarMobileTitle` | | `upload` | `selectFile`, `selectFiles` | | `copyText` | `copy` | | `columnSettings` | `configure`, `reset`, `resetTo` (`{title}`), `resetChanges` | | `dropdownMenu` | `placeholder` | | `datePicker` | `weekDays[]`, `months[]`, `notSelected`, `firstDay` (0 — вс, 1 — пн), `hour12` (12-часовой формат с AM/PM), `am`, `pm` | | `htmlEditor` | `language`, `blocks.*` | Точечные пропы компонентов (например `sidebar-mobile-title` у `SCanvas`, `nodata` у `STable`) по-прежнему имеют приоритет над словарём. --- # Вайб-кодинг со Startup UI Startup UI спроектирован под сборку интерфейсов **промптами**: ты описываешь ИИ-агенту задачу, а он верстает (и правит) экраны из готовых `S`-компонентов — единообразно и по правилам библиотеки. Чтобы агент использовал компоненты правильно, а не выдумывал API, документация отдаётся в машинно-читаемом виде, и **агент разбирается в ней сам**. Тебе как человеку ничего вручную искать, дописывать `.md` к ссылкам или копировать в промпт не нужно — достаточно работать в проекте или один раз упомянуть библиотеку. ## Сценарий 1. Агент работает в проекте, где установлен Startup UI Главный случай — агент разбирается сам, **без твоих действий и даже без упоминания библиотеки**. Документация едет внутри npm-пакета и привязана к установленной версии (работает оффлайн): - `node_modules/startup-ui/README.md` и `AGENTS.md` — точка входа с правилами использования; - `node_modules/startup-ui/llms/llms.txt` — индекс всех компонентов; - `node_modules/startup-ui/llms/components/<категория>/<имя>.md` — полная спецификация компонента (пропсы, слоты, примеры). Встретив незнакомый импорт `import { SButton } from 'startup-ui'`, современные кодинг-агенты (Claude Code, Cursor, Copilot и др.) заглядывают в пакет — читают `package.json`, `README`, типы — и оттуда попадают на спеки в `llms/`. ### Чтобы это работало гарантированно Агенты читают `AGENTS.md` / `CLAUDE.md` из **корня проекта**, а не из `node_modules`. Добавь туда один раз короткий указатель — и агент держит библиотеку в контексте всегда, ещё до того как полезет в код: ```md ## Startup UI Проект использует библиотеку компонентов `startup-ui` (все компоненты с префиксом `S`). Перед версткой UI читай правила и спеки внутри пакета: `node_modules/startup-ui/AGENTS.md`, индекс — `node_modules/startup-ui/llms/llms.txt`. ``` > Claude Code не читает `AGENTS.md` — положи эту вставку в корневой `CLAUDE.md` (внутри самого пакета шим `CLAUDE.md` → `@AGENTS.md` уже есть). ## Сценарий 2. Ты упомянул библиотеку агенту или чату Тогда агент сам сходит за документацией онлайн (или найдёт её по адресу `startup-ui.ru`). При желании дай прямую ссылку: - **[/llms.txt](/llms.txt)** — компактный индекс всех компонентов по стандарту [llmstxt.org](https://llmstxt.org); - **[/llms-full.txt](/llms-full.txt)** — вся документация одним файлом, когда нужен полный контекст; - **[/llms/rules.txt](/llms/rules.txt)** — короткие правила для системной инструкции редактора. ## Как это устроено под капотом Каждая страница доков доступна в чистом Markdown — к любому её URL дописывается `.md` (например, `startup-ui.ru/pages/components/interfaces/sbutton.md`). Это техническая деталь: ею пользуются сами агенты и `llms.txt`. Вручную дописывать `.md` и копировать текст в промпт не нужно — это забота агента, а не твоя. ## Что дальше Самый автономный вариант — **MCP-сервер**: агент запрашивает компоненты, пропсы и примеры вживую, без ручных ссылок. Он на отдельной дорожке развития; вся машинно-читаемая база под него уже готова. --- # SFilterGroup > SFilter Фильтры

В популярных библиотеках компонентов для Vue3 прямого аналога нет. По сравнению с ручной сборкой из других компонентов SFilter позволяет:

  1. Минимальным синтаксисом собирать функциональные фильтры на страницах. Это обеспечивает одинаковую реализацию разными программистами, избавляет от визуальных отличий, упрощает дальнейшую поддержку и сохраняет взаимозаменяемость между проектами.
  2. Брать первоначальные значения фильтра напрямую из GET-параметров, а не пробрасывать из контроллера (сохранение «тонких контроллеров»).
  3. Дебаунсить применение значений одним атрибутом, что всегда нужно для приятного UX ввода текстовых значений.
  4. Для пустых/незаданных значений фильтров не добавляет GET-параметры, чтобы сохранять консистентность URL.
## Базовый пример ```vue ``` ## Привязка к GET-параметрам Часто бывает удобно привязать модель напрямую к GET-параметрам:
  1. Взять стартовые значения модели из GET-параметров (без необходимости проброса через контроллер)
  2. При изменениях модели автоматически обновлять GET-параметры, сбрасывая page, но оставляя любые другие параметры.
В этом случае можно использовать атрибут `bind-to-query` , который включит такое поведение: ```vue ``` По умолчанию `bind-to-query` синхронизирует URL через нативный History API — без серверного рефетча и без зависимости от какого-либо роутера (компонент не требует InertiaJS). Если нужно, чтобы изменение фильтра инициировало серверный запрос (SPA-навигация), зарегистрируйте роутер один раз при подключении плагина — например, роутер Inertia: ```js // app.js import StartupUI from 'startup-ui'; import { router } from '@inertiajs/vue3'; app.use(StartupUI, { router }); ``` Подойдёт любой объект с совместимой сигнатурой `get(url, params, options)`. Если роутер не зарегистрирован — `bind-to-query` работает «в чистом виде» (только синхронизация URL). ## Часто используемые поля ### Горизонтальные радио-кнопки ```vue ``` Placeholder задает «не выбранный вариант» — null-значение. ## Выпадающий список ```vue ``` ## Текстовое поле ввода ```vue ``` ## Выбор периода дат ```vue ``` ## Интерфейс компонента SFilterGroup ### Свойства (Props) | Название | Тип | По умолчанию | Описание | |----------|-----|--------------|----------| | v-model | `Object` | `{}` | Объект, хранящий состояние всех вложенных `SFilter`. | | bindToQuery | boolean | `false` | Синхронизирует состояние с GET-параметрами. По умолчанию через History API; если зарегистрирован роутер (`app.use(StartupUI, { router })`) — через него (напр. Inertia-визит с серверным рефетчем). | | ignoreQueryNames | `string[]` | `['page']` | GET-параметры, которые игнорируются при сбросе фильтров. | | ignoreQueryValues | `any[]` | `['', null, undefined, false]` | Значения, при которых параметр удаляется из URL (чтобы не мусорить пустой строкой). | | title | string | - | Заголовок группы фильтров. | | loading | boolean | `false` | Состояние загрузки (скелетон или спиннер). | | showAll | boolean | `false` | Если `true`, кнопка «Показать все» заменяется на «Скрыть». | ### Слоты (Slots) | Название | Описание | |----------|----------| | default | Содержимое группы (обычно компоненты `SFilter`). | ## Интерфейс компонента SFilter ### Свойства (Props) | Название | Тип | По умолчанию | Описание | |----------|-----|--------------|----------| | name | string | `undefined` | **Обязательное.** Ключ для хранения значения в `v-model` родительской группы. | | debounce | number | `0` | Задержка в миллисекундах перед сохранением. Критично для текстовых инпутов при `bindToQuery`. | | title | string | - | Заголовок конкретного фильтра. | | opened | boolean | `false` | Раскрыт ли фильтр по умолчанию. | ### Слоты (Slots) | Название | Описание | |----------|----------| | default | Содержимое фильтра (чекбоксы, инпуты и т.д.). | --- # SPagination Постраничная навигация.

В отличие от популярных библиотек компонентов для Vue3:

  1. Принимает атрибуты ровно в том формате, в котором его отдает пагинатор Laravel, что позволяет сразу пробросить исходный пагинатор без дополнительных прописываний атрибутов через v-bind.
## Базовый пример ```vue ``` Laravel-пагинатор на выходе формирует объект с ключами `{current_page, from, last_page, links, per_page, to, total}`. SPaginator использует ровно то же именование входных атрибутов, поэтому пробрасываем их как есть через v-bind. ## Выбор кол-ва результатов на странице Чтобы также выводить выпадающий список в кол-вом результатов на странице, можно добавить атрибут per-page-options: ```vue ``` Изменение кол-ва вариантов на странице меняет query-параметр perpage, сбрасывает query-параметр page и сохраняет все остальные query-параметры нетронутыми. ## Интерфейс компонента ### Свойства (Props) | Название | Тип | По умолчанию | Описание | |----------|-----|--------------|----------| | links | `PaginationLink[]` | `[]` | Массив ссылок `[{ url, label, active }]`. Обычно генерируется бекэндом Laravel. | | perPageOptions | `number[] \| string[]` \ `Record` | `undefined` | Опции для выпадающего списка кол-ва элементов на странице (например, `[10, 20, 50]`). | | url | string | `location.pathname` | Базовый URL, используемый при смене кол-ва элементов на странице. | | preserveScroll | boolean | `true` | Пробрасывается в зарегистрированный `Link` (Inertia). Сохраняет позицию скролла. | | preserveState | boolean | `false` | Пробрасывается в зарегистрированный `Link` (Inertia). Сохраняет локальный стейт страницы. | | per_page | number | `undefined` | Выбранное кол-во элементов на странице. | | from | number | `undefined` | Начальный индекс (для надписи «Показано X-Y из Z»). | | to | number | `undefined` | Конечный индекс. | | total | number | `undefined` | Общее количество записей. | ### Слоты (Slots) | Название | Описание | |----------|----------| | default | По умолчанию слоты отсутствуют, пагинация генерируется автоматически по пропсам. | --- # STable Таблица.

В отличие от популярных библиотек компонентов для Vue3:

  1. Семантика поддерживает и итератор строк-значений, и просто вставку строк как есть.
  2. Позволяет задать состояние, когда нет данных, одним атрибутом.
  3. Позволяет сделать горизонтальный скролл сверху таблицы, что полезно для длинных таблиц с большим кол-вом колонок.
## Базовый пример ```vue ``` ## Утилиты выравнивания По умолчанию ячейки выравниваются по позиции колонки: первая — по левому краю, последняя — по правому, остальные — по центру (одинаково для ячеек `` и заголовков ``). Выравнивание конкретной ячейки можно переопределить классами на ``/`` — `.left`, `.center`, `.right`, а `.nowrap` запрещает перенос строки: ```vue ``` ## Подсветка строк при наведении Для подсветки при наведении добавляем атрибут hoverable ```vue ``` ## Явные границы по краям ячеек Для отрисовки границ добавляем атрибут bordered ```vue ``` ## Выделение четных строк Для выделения четных строк добавляем атрибут striped ```vue ``` ## Компактные ячейки По умолчанию у ячеек комфортные отступы. Для плотных таблиц с большим количеством данных можно уменьшить отступы атрибутом compact. ```vue ``` ## Фиксированная шапка Чтобы зафиксировать шапку, устанавливаем высоту таблицы в атрибуте `height`. ```vue ``` Для высоты таблицы с фиксированной шапкой удобно использовать 80vh, чтобы таблица занимала 80% от высоты экрана. ## Горизонтальный скролл сверху ```vue ``` ## Кастомный контент внутри tbody Если нужно задать явные строки в tbody вместо перебора #row, то используем дефолтный слот (в нем для отдельных строк нужно явно указать ``). ```vue ``` ## Несколько строк в header / footer Если это нужно, просто используем отдельные слоты headers / footers (добавляется в конце "s"), в которые уже добавляем ``. ```vue ``` ## Сообщение о том, что нет данных Когда задаем data и перебираем его через `