@1337exp/taoui
Почему Tao UI
- Сначала токены. Цвета, радиусы, отступы и шрифты — CSS-переменные в три слоя (примитивы, семантика, тема). Перекрасить набор или одну панель можно, не трогая компоненты.
- Одна шкала, один API.
small/medium/largeвезде, одинаковые правилаv-model/change/*Text/*Labelдля всех полей, тона вместо случайных цветов. - Тёмная и светлая темы из коробки и поверхности (
flat/grain/glass) для HUD и лендингов. - Встроенные языки. Плейсхолдеры, даты и aria-подписи на русском и английском, переключаются на лету без перезагрузки.
- Nuxt и чистый Vue.
TaoLinkсам выбираетNuxtLink,RouterLinkили<a>. Полные типы, ES и CJS сборки. - Клавиатура. Ловушки фокуса,
role/aria-*на контролах.
Компоненты
Базовые
- TaoBlock — базовый контейнер с настраиваемыми отступами и скруглением
- TaoButton — кнопка:
primary/secondary/ghost/text/danger.toneкрасит ghost и text (danger, success, warning, info, neutral) - TaoCard — карточка со слотами cover / header / footer. В
#cover— фото илиTaoCarousel.flush— плитка без padding/тени на всю ячейку;#overlay— scrim на обложке - TaoCopy — иконка копирования в буфер. После клика сверху тултип «Скопировано»; если кнопку убрали — тултип тоже
- TaoInput — поле ввода с поддержкой v-model, валидации и слотов
#prefix/#suffix(иконка или текст внутри рамки) - TaoInputGroup — склейка снаружи:
#before| поле |#after(протокол, единица, кнопка) - TaoModal — модальное окно с анимацией и слотами
- TaoDrawer — боковая панель (фильтры, настройки). Esc, клик по фону, ловушка фокуса — как у модалки
- TaoSpoiler — раскрывающийся блок.
v-modelиaria-expanded. Несколько подряд сами друг друга не закрывают - TaoSpoilerGroup — аккордеон:
v-model— ключ открытой панели (null— все закрыты), у спойлераname. Безv-modelгруппа помнит ключ сама - TaoTabs — вкладки для переключения между секциями контента
- TaoTooltip — всплывающая подсказка при наведении.
top/bottom/left/right, у края экрана переворачивается - TaoPopover — панель по клику: слот с любым контентом, Esc и клик снаружи. Не тултип и не меню действий
Layout
- TaoContainer — контейнер с ограничением ширины (wide/slim/ultra-slim) и авто-центровкой
- TaoFlex — обёртка над flexbox с пропами justify/align/direction/wrap/gap
- TaoSpace — авто-расстановка дочерних элементов с равномерным gap
- TaoGrid / TaoGridItem — плитки:
min— каталог (auto-fill),cols— bento. У itemcol/row; уже ~560px span сбрасывается. Не masonry - TaoDivider —
solid(обычный border) /dashed/colored(акцент) /gap. Подпись — опциональныйtextна любом варианте;fade— плавные края линии;sizeтолько уgap(small / medium / large) - TaoSplit — две панели и полоса.
row/columnкак у Flex.v-model— размер start в%илиpx;resizable="false"фиксирует полосу. Не Divider. - TaoFieldset — рамка-группа:
legendсидит на верхней границе,align— start / center / end - TaoAnimatedBorder — контейнер с анимированной радужной рамкой (декоративный, не завязан на тему)
Формы и ввод
- TaoCheckbox — чекбокс с v-model, слотами pre/post и
indeterminate(частично выбран) - TaoSwitch — переключатель для булевых настроек (
role="switch") - TaoRadio / TaoRadioGroup — классические радиокнопки: длинные подписи, можно столбиком
- TaoSegmented / TaoSegmentedOption — компактный выбор из 2–5 коротких вариантов. Не вкладки
- TaoSelect — выпадающий список с клавиатурой, тот же визуал, что у Input.
size(small/medium/large, та же шкала, что у Button). Сброс — «Очистить» в списке или Delete;clearable="false"отключает сброс, слот#clearзаменяет содержимое строки - TaoCombobox — Select с полем: фильтр по вводу,
allow-createпишет своё вv-model,create— чтобы родитель дополнил список. Те жеsize,clearableи#clear, что уTaoSelect - TaoDate — один день:
YYYY-MM-DD, без часов и пояса. Календарь как у Select - TaoDateRange — период:
v-model—{ start, end }илиnull. Первый клик — начало, второй — конец (если конец раньше — меняются местами). Неполный выбор в модель не пишется - TaoFormField — общие label / hint / error для полей формы
- TaoInputNumber — число с min/max/step и кнопками ± справа. Пустое значение —
null.@commitсрабатывает один раз, когда правка закончена (blur, Enter, ±, стрелка), в отличие отv-model, который обновляется на каждую клавишу - TaoQuantity — количество в корзине:
− | поле | +, целые штуки, сток, минус на минимуме может убрать строку - TaoTextarea — многострочное поле с авто-высотой.
maxlengthограничивает ввод,countпоказывает «введено / максимум».variant:bar(по умолчанию) /outline(рамка как у Input) /filled/plain;readonly.noBorder,noBackground,borderInverse,dummyустарели - TaoPinCode — пин-код из N полей. По умолчанию клик стирает ячейку и все справа (
clear-on="focus");clear-on="input"заменяет цифру только при вводе.format="#-##-##"рисует визуальные разделители (в значение не входят; вставка в таком виде или чистым кодом раскладывается по ячейкам). Последняя ячейка снимает фокус и эмититcomplete - TaoSlider / TaoRangeSlider — один бегунок (
v-model— число) или два ([от, до],minGap). Мышь, тач, стрелки.marks(деления, числа,{ value, label }),discreteдля ступенчатых шкал,vertical,flat,format;variant="thin"— тонкий трек для seek / громкости - TaoColorPicker — квадрат насыщенности и яркости, полосы тона и прозрачности, образцы, пипетка (где есть
EyeDropper), режимы ввода HEX / RGB / HSL.v-model— строка hex;colorModel()подключает ваше собственное хранилище цвета - TaoColorInput — поле с образцом и hex-текстом; палитра открывается в поповере
- TaoFileDrop — зона загрузки (drag & drop + клик). Список имён под зоной,
v-model—File[]. Очистку всей пачки решает родитель (clear-request)
Отображение данных
- TaoTable — простая таблица: колонки, empty, loading, сортировка через
v-model:sort.head— подсветка шапки,lines— линии между рядами (оба по умолчанию включены).striped— зебра с первой строки,striped="even"— со второй. Не datagrid. - TaoPagination — страницы с многоточием. Клик по «…» прыгает на
jumpстраниц (5 по умолчанию) - TaoEmpty — пустой список, нет результатов, нет прав
- TaoList / TaoListItem — строка списка: иконка, заголовок, подпись, действие по ховеру. Не Select и не таблица
- TaoStages — список стадий:
wait/work/ok/bad. Не линейный степпер - TaoSkeleton — плейсхолдер загрузки (text / title / circle / rect)
- TaoCounter — витрина числа с переворотом цифр (не инпут)
- TaoCarousel — лента: целый слайд с
autoplay, карточка сpeek, полоса сper-view. Стрелки черезcontrolsили#prev/#next, точки —dots - TaoAvatar — фото или инициалы, размеры
small/medium/large. Точкаdotи счётчикcount - TaoTag — тег/бейдж со статусами.
#prefix(аватар),closableубирает чип,flat— без рамки - TaoAlert — инлайн-баннер страницы или ошибки формы (success / error / warning / info)
- TaoProgress — статичная полоса (
progressв %). Проценты сверху по центру илиshowPercentage="right"справа; слоты#left/#right— подписи, тогда процент справа в скобках.falseпрячет. Ширина —minWidth/maxWidth, не диапазон значения - TaoTimeline — play / pause, слайдер по времени с метками событий, подпись «время / длина». Время сама не двигает: ведите
v-modelсвоими часами - TaoLoader — анимированный лоадер (четыре точки)
- TaoImage — обёртка над
<img>: fade-in и плейсхолдер.lazyвключает нативныйloading="lazy" - TaoIcon — обёртка для icon-шрифта (см. раздел «Иконки» ниже)
Навигация и взаимодействие
- TaoLink —
<NuxtLink>в Nuxt,<RouterLink>при vue-router, иначе обычный<a>— без дополнительной настройки - TaoBreadcrumb — крошки: последний пункт — текущая страница
- TaoDropdownMenu — выпадающее меню с авто-позиционированием у края экрана
- TaoScrollTop — плавающая кнопка «наверх»: виды
solid/ghost/text,side, подписьlabelпри наведении мыши. Появляется при обратном скролле послеboundary
Обратная связь
- toast() — fluent-уведомления:
toast().success().message('Сохранено') - confirm() — вопрос с оверлеем: тот же fluent +
await. Не тост — ждёт ответ, не исчезает сам. - TaoToastViewport / TaoConfirmHost — контейнеры; если их нет в разметке, первый вызов сам монтирует их в
body
Соглашения
Тоны визуала: success | danger | warning | info | neutral (тип TaoTone), всегда через проп tone: Button, Alert, Tag. type у Alert / Tag — устаревший алиас.
error у Alert, Tag и toast() — синоним danger. Button уже danger.
Stages — это процесс, не тон: wait / work / ok / bad (danger принимается как bad).
Размеры контролов: small | medium | large (тип TaoSize). Одна шкала для Button, Input, InputNumber, Quantity, Textarea, Select, Combobox, Date, DateRange, Segmented, InputGroup и FormField — контролы одного размера встают в ряд ровно. Checkbox, Radio / RadioGroup и Switch масштабируют индикатор (14 / 18 / 22px) и подпись; Tag — высоту (18 / 20 / 24px); Tabs, кнопки Pagination и строки List берут высоту Button; Table масштабирует отступы ячеек (size="small" вместо compact); FileDrop — зону и строки файлов; Slider — трек и бегунок.
Без size контрол берёт размер предков, в корне — medium. На целый блок размер задаётся классом tao-size-small / -medium / -large на любом элементе или size у TaoFormField / TaoInputGroup. Явный size у контрола главнее.
<div class="tao-size-small">
<TaoInput v-model="q" />
<TaoButton>Найти</TaoButton>
</div>
Контент и раскладка держат свою шкалу и не наследуют: Avatar (24 / 32 / 48px; s / m / l устарели), Divider gap (big — устаревший алиас large), Loader / Icon / ScrollTop (пиксели).
Container живёт на другой оси — ширина: width="wide" | "slim" | "ultra-slim" (size — устаревший алиас).
Поверхности — из чего «сделана» панель: flat — заливка (по умолчанию), grain — статичное плёночное зерно, glass —
полупрозрачное стекло с размытием того, что позади. Проп surface у TaoCard / TaoBlock или класс tao-surface-grain /
-glass на любом элементе — HUD в игре, секция лендинга, сайдбар. Вид настраивается один раз токенами темы
(--tao-surface-grain-image / -size, --tao-glass-fill / -border / -blur). Поверхность принадлежит одному элементу:
вложенные панели её не наследуют. Стеклу нужно что-то позади — используйте его для того, что лежит поверх контента; над
ровным фоном это просто серая заливка, над анимацией размытие пересчитывается каждый кадр. Toast и confirm стеклянные по
умолчанию, с оттенком своего тона; toast.defaults({ surface: 'flat' }) и confirm.defaults({ surface: 'flat' })
выключают стекло, .surface('flat') — для одного. Без поддержки размытия или при prefers-reduced-transparency стекло
становится сплошной панелью.
Шкала кегля (примитивы): xs 12 … xl 18 для контролов; 2xl 24, 3xl 32, 4xl 36 для заголовков и прозы.
Отдельного компонента заголовка нет — h1 { font-size: var(--tao-font-size-3xl) }.
Правила API — им следуют все компоненты; старые имена остаются устаревшими алиасами:
- Поля принимают
v-model,disabled,error(красная рамка) иsize. Подпись, подсказка и текст ошибки — черезTaoFormField. - События:
update:Xдля v-model плюсchange, когда пользователь закончил изменение (Slider, Split — по отпусканию). Остальные события — глаголы в настоящем времени:select,close,create. - Тексты: видимые —
*Text(clearText,successText), для скринридера —*Label(closeLabel). - Списки: массивы данных —
items; выбираемые элементы несутvalue+label(TaoSelectOption,TaoTabItem). Идентификаторы обычных данных (колонки таблицы, стадии) остаютсяkey. - Булевы — прилагательные без
is/has/as:closable,clickable,masked,newTab. Ось раскладки —direction: 'row' | 'column', неvertical. - Длины (
padding,radius,gap,maxHeight): число — пиксели, строка уходит в CSS как есть (типTaoLength).
Иконки
TaoIcon не поставляет собственный набор иконок — он рендерит класс icon-<name>
по соглашению, рассчитанному на подключение внешнего icon-шрифта (например,
собранного через IcoMoon/Fontello). Чтобы иконки отображались, подключите
свой шрифт с классами вида .icon-arrow-up { ... } в проекте.
Установка
npm install @1337exp/taoui
Или локально из папки:
npm install ../path-to-tao-ui
Использование
Подключение в Nuxt 3
Создайте плагин plugins/tao-ui.ts:
import { defineNuxtPlugin } from '#app'
import TaoUI from '@1337exp/taoui'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(TaoUI)
})
Импортируйте стили в app.vue или layout:
<script setup>
import '@1337exp/taoui/styles.css'
</script>
Подключение в Vue 3 проекте
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import TaoUI from '@1337exp/taoui'
import '@1337exp/taoui/styles.css'
const app = createApp(App)
app.use(TaoUI)
app.mount('#app')
Язык
Встроенные тексты (плейсхолдеры, «Очистить», aria-подписи, даты) по умолчанию английские. Русский идёт в комплекте:
import TaoUI, { localeRu } from '@1337exp/taoui'
app.use(TaoUI, { locale: localeRu })
Поменять отдельные тексты — остальное останется от локали по умолчанию:
app.use(TaoUI, { locale: { select: { placeholder: 'Выберите город' } } })
Другой язык: скопируйте localeEn (или localeRu), переведите и передайте так же (тип TaoLocale). Ключи повторяют имена пропсов —
locale.select.placeholder — значение по умолчанию для placeholder у TaoSelect, а переданный руками проп всегда главнее.
Язык можно переключать в любой момент — из настроек, переключателя языка, по маршруту. Уже показанные компоненты обновятся сразу, без перезагрузки и без потери введённого:
import { setTaoLocale, localeEn, localeRu } from '@1337exp/taoui'
function onLanguageChange(lang) {
setTaoLocale(lang === 'ru' ? localeRu : localeEn)
}
Без плагина (отдельные компоненты) достаточно одного вызова setTaoLocale().
Переход с 3.x
В 4.0 поменялся один умолчательный параметр: встроенные тексты теперь английские (раньше были русские). Чтобы остаться
на русском, передайте локаль один раз: app.use(TaoUI, { locale: localeRu }), а без плагина — setTaoLocale(localeRu).
Хелперы дат и компоненты дат следуют локали и по умолчанию тоже en-US.
Переход с 1.x
Смотрите CHANGELOG.md: что сломалось и таблица «старое имя → новое». В 2.x старые имена ещё работают и в режиме разработки пишут предупреждение в консоль.
Использование отдельных компонентов
<script setup>
import { TaoButton, TaoFormField, TaoInput, TaoModal } from '@1337exp/taoui'
import '@1337exp/taoui/styles.css'
</script>
<template>
<TaoButton variant="primary">Нажми меня</TaoButton>
<TaoFormField label="Введите текст">
<TaoInput v-model="text" />
</TaoFormField>
<TaoModal v-model="isOpen" title="Заголовок">
Содержимое модального окна
</TaoModal>
</template>
Split
TaoSplit — две панели и полоса между ними. Это не TaoDivider (там только линия) и не раскладка на N панелей: три колонки — два вложенных сплита.
direction как у Flex: row (start | end) или column (start над end). Слоты #start / #end.
v-model — размер панели start. unit — % (по умолчанию, доля живёт при ресайзе контейнера) или px (сайдбар; вторая панель забирает остаток). Для px размер нужно задать явно: дефолт 50 — это 50px. min / max ограничивают start, min-end — минимум второй панели, все в той же единице. resizable="false" оставляет полосу, но без перетаскивания.
Сплит заполняет родителя (width / height: 100%). Родитель должен ограничить размер по оси сплита: у column без высоты не из чего считать %. Панели с min-width / min-height: 0 и overflow: hidden, чтобы контент не мешал сжатию. Скролл — на внутренней обёртке (overflow: auto; height: 100%; min-height: 0). Вложенный TaoSplit прямым ребёнком панели растягивается сам.
<TaoSplit v-model="side" unit="px" :min="160" :max="480" :min-end="240">
<template #start>Навигация</template>
<template #end>Основное</template>
</TaoSplit>
Toast
Вызов — цепочка в одном тике, setTimeout(0) отправляет уведомление.
import { toast } from '@1337exp/taoui'
toast().success().message('Сохранено')
toast().error().title('Сеть').timeout(5000).message('Нет соединения')
toast.success('Сохранено')
toast.warning('Черновик', { timeout: 1000 })
Позиция по умолчанию — topCenter. Глобально:
toast.defaults({ position: 'bottomRight' })
toast.byBottomRight()
question с оверлеем в kit нет — это confirm, не toast. Кнопки в тосте, если нужны:
toast().info().message('Удалить файл?').action('Удалить', onRemove)
Viewport можно не вставлять: первый вызов сам создаст его. Если нужен явный контроль (тесты, свой layout):
<TaoToastViewport />
Confirm
Вопрос с оверлеем: всплывает по центру и спрашивает. Это не toast — нет прогресс-бара и автозакрытия, есть await, Esc и клик по оверлею = отмена.
import { confirm } from '@1337exp/taoui'
if (await confirm().title('Удалить файл?').message('Это нельзя отменить').danger()) {
remove()
}
const ok = await confirm('Выйти без сохранения?', {
ok: 'Выйти',
cancel: 'Остаться',
})
Очередь: второй confirm(), пока открыт первый, подождёт. Подписи по умолчанию — «ОК» / «Отмена»:
confirm.defaults({ ok: 'Yes', cancel: 'No' })
Формы
TaoFormField вешает общий label / hint / error и прокидывает id во вложенный контрол.
<TaoFormField label="Город" hint="Необязательно">
<TaoSelect v-model="city" :options="cities" />
</TaoFormField>
<TaoSwitch v-model="dark" label="Тёмная тема" />
<TaoRadioGroup v-model="plan" legend="Тариф">
<TaoRadio value="free" label="Free" />
<TaoRadio value="pro" label="Pro" />
</TaoRadioGroup>
<TaoSegmented v-model="period" legend="Период">
<TaoSegmentedOption value="week">Неделя</TaoSegmentedOption>
<TaoSegmentedOption value="month">Месяц</TaoSegmentedOption>
<TaoSegmentedOption value="year">Год</TaoSegmentedOption>
</TaoSegmented>
<TaoAlert tone="warning" title="Черновик">Сохраните, прежде чем уйти.</TaoAlert>
TaoSegmented — короткий взаимоисключающий выбор, все варианты видны сразу. Не для навигации по страницам: это TaoTabs. Длинные подписи или больше 5–6 пунктов — TaoRadioGroup или TaoSelect.
TaoSelect открывается с клавиатуры (стрелки, Enter, Esc), список переворачивается у края экрана. Delete или «Очистить» внизу списка сбрасывает значение в null.
Если null не допустим (шаг сетки, порядок сортировки), передайте :clearable="false": строка сброса и Delete / Backspace пропадут. Подпись строки сброса — clear-text, для иконки или своей разметки есть слот #clear. size — small / medium / large (без него — от предков), масштабирует и раскрытый список.
<TaoSelect v-model="step" :options="steps" size="small" :clearable="false" />
<TaoSelect v-model="city" :options="cities">
<template #clear><TaoIcon name="close" /> Сбросить</template>
</TaoSelect>
TaoCombobox — то же меню, но с полем. Печатаете — варианты режутся. allow-create пишет введённое в v-model, если точного совпадения нет. Список сам не меняется: слушайте create и добавьте option снаружи.
Подпись, подсказка и текст ошибки всегда идут через TaoFormField; label / error-message у TaoInput устарели. TaoFormField также оборачивает TaoTextarea, TaoCheckbox, TaoSwitch, TaoRadioGroup и TaoSegmented — id, hint и error прокидываются сами.
TaoTag может быть чипом: #prefix слева (обычно TaoAvatar), closable рисует крестик и эмитит close. Клик по крестику наружу не всплывает. flat снимает рамку, заливка остаётся.
<TaoTag tone="neutral" closable @close="remove">
<template #prefix>
<TaoAvatar name="Анна Козлова" size="small" />
</template>
Анна Козлова
</TaoTag>
Таблица и пагинация
Таблица рендерит то, что передали: сама данные не режет и не сортирует. Это удобно и для серверной выборки, и для клиентской — страница режется снаружи.
<TaoTable
:columns="columns"
:rows="pageRows"
v-model:sort="sort"
empty-text="Пока нет записей"
>
<template #cell-status="{ row }">
<TaoTag :tone="row.status === 'active' ? 'success' : 'neutral'">
{{ row.status }}
</TaoTag>
</template>
</TaoTable>
<TaoPagination v-model:page="page" :total="rows.length" :page-size="8" />
const columns = [
{ key: 'name', label: 'Имя', sortable: true },
{ key: 'city', label: 'Город' },
{ key: 'status', label: 'Статус' },
]
Слот cell-<ключ> перекрывает ячейку. loading показывает оверлей, пустой список — текст или слот #empty. Клик по шапке с sortable крутит asc → desc → сброс.
Пустые состояния и загрузка
<TaoEmpty title="Ничего не нашлось">
Измените фильтр или сбросьте поиск.
<template #action>
<TaoButton variant="secondary">Сбросить</TaoButton>
</template>
</TaoEmpty>
<TaoSkeleton variant="title" />
<TaoSkeleton :lines="3" />
<TaoSkeleton variant="circle" />
<TaoStages :items="[
{ key: 'pay', label: 'Оплата', status: 'ok' },
{ key: 'pack', label: 'Сборка', status: 'work' },
{ key: 'ship', label: 'Доставка', status: 'wait' },
]" />
<TaoList>
<TaoListItem
title="Анна Козлова"
description="Дизайн · Москва"
clickable
:selected="picked === 'anna'"
@click="picked = 'anna'"
>
<template #prefix>
<TaoAvatar name="Анна Козлова" size="small" />
</template>
</TaoListItem>
</TaoList>
<TaoInput v-model="q" placeholder="Найти…">
<template #prefix>⌕</template>
</TaoInput>
<TaoInputGroup>
<template #before>https://</template>
<TaoInput v-model="host" placeholder="example.com" />
<template #after>
<TaoButton>Проверить</TaoButton>
</template>
</TaoInputGroup>
<form @submit.prevent="onSave">
<TaoFormField label="Email">
<TaoInput v-model="email" type="email" />
</TaoFormField>
<TaoButton type="submit">Сохранить</TaoButton>
</form>
<TaoFormField label="Количество">
<TaoInputNumber v-model="qty" :min="1" :max="99" />
</TaoFormField>
<TaoQuantity v-model="qty" :max="12" @dec="onDec" />
<TaoFormField label="День доставки" hint="В модели всегда YYYY-MM-DD">
<TaoDate v-model="day" min="2026-09-01" max="2026-09-30" />
</TaoFormField>
<TaoFormField label="Даты поездки">
<TaoDateRange v-model="stay" min="2026-09-01" max="2026-09-30" />
</TaoFormField>
<TaoCounter :value="score" :max-digits="6" />
<TaoCarousel :autoplay="4000" loop dots>
<article v-for="item in banners" :key="item.id" class="hero">
<strong>{{ item.title }}</strong>
<span>{{ item.text }}</span>
</article>
</TaoCarousel>
<TaoCarousel :peek="72" :controls="false" dots>…</TaoCarousel>
<TaoCarousel v-model="slide" :per-view="5" :peek="28">
<template #prev></template>
<template #next="{ go, disabled }">
<button type="button" :disabled="disabled" @click="go">вперёд</button>
</template>
<article v-for="item in products" :key="item.id">…</article>
</TaoCarousel>
TaoDrawer — лист сбоку, не модалка по центру:
<TaoDrawer v-model="open" title="Фильтры">
...
</TaoDrawer>
Дизайн-токены и темизация
Стили библиотеки построены в три слоя. Компоненты никогда не обращаются к цветам/размерам напрямую — только через переменные из семантического слоя. Это и есть механизм темизации: переопределяете 20-30 переменных — меняется внешний вид всех компонентов сразу, без правок самих компонентов.
primitives.css → semantic.css → themes/*.css (опционально)
сырые цвета публичный API готовые темы поверх
и размеры, темизации: semantic — пример: light
не трогать вот что менять
Слои
| Файл | Назначение | Трогать? |
|---|---|---|
primitives.css |
Сырая палитра, шкалы отступов/радиусов/теней (--tao-orange-400, --tao-space-3...) |
Обычно нет |
semantic.css |
Токены темы по умолчанию (dark): --tao-color-accent, --tao-color-surface, --tao-color-danger и т.д. |
Да — это и есть API темизации |
themes/light.css |
Пример альтернативной темы через [data-tao-theme="light"] |
Да, как образец |
base.css |
Глобальный reset (скроллбар, ссылки, box-sizing) | Редко |
Все четыре подключаются одним файлом @1337exp/taoui/styles.css.
Как сделать свою тему
Тема — это просто переопределение семантических токенов под своим селектором. Ничего в компонентах менять не нужно:
/* my-brand-theme.css */
[data-tao-theme="my-brand"] {
--tao-color-accent: #6c5ce7;
--tao-color-accent-hover: #5a4bd1;
--tao-color-bg: #0f0f1a;
--tao-color-surface: #0f0f1a;
--tao-color-surface-raised: #1a1a2e;
--tao-color-text: #d6d6e8;
--tao-color-text-strong: #ffffff;
/* остальные токены см. semantic.css */
}
<!-- app.vue -->
<template>
<div data-tao-theme="my-brand">
<NuxtPage />
</div>
</template>
Или переключайте тему целиком на <html>, чтобы стилизовать модалки/тултипы,
которые телепортируются в body:
document.documentElement.setAttribute('data-tao-theme', 'light')
Ключевые группы токенов
--tao-color-accent*— акцентный/брендовый цвет (кнопки primary, активные вкладки, ссылки)--tao-color-on-accent— текст на акцентном фоне (--tao-color-text-on-accent— алиас)--tao-color-selection,--tao-color-selection-text— выделение текста (::selection)--tao-color-surface*— фоны контейнеров (обычный / приподнятый / утопленный / hover)--tao-color-border*— границы--tao-color-text*— текст (обычный / усиленный / subtle / приглушённый / disabled)--tao-color-danger*,--tao-color-success*,--tao-color-warning*,--tao-color-info*— статусные цвета--tao-color-input-*— поля ввода отдельно, т.к. фон/текст полей часто не совпадают с surface/text--tao-radius-control,--tao-radius-panel— скругления (мелкие контролы vs крупные панели)--tao-shadow-panel,--tao-shadow-overlay— тени--tao-font-size-*— шкала кегля вprimitives.css:xs–xlконтролы,2xl–4xlзаголовки / проза--tao-control-height-*(26 / 36 / 44px),--tao-control-padding-x-*,--tao-control-padding-y-*,--tao-control-font-size-*,--tao-control-font-size-secondary-*,--tao-button-padding-x-*,--tao-indicator-size-*,--tao-tag-height-*/-padding-x-*/-font-size-*— шкала размеров вprimitives.css. Компоненты читают текущие значения изsize.css:--tao-control-height,--tao-control-padding-x/-y,--tao-control-font-size,--tao-control-font-size-secondary,--tao-button-padding-x,--tao-indicator-size,--tao-tag-height/-padding-x/-font-size. Используйте их в своих контролах, чтобы попасть в размер окружения.--tao-field-height— устаревший алиас--tao-control-height
Полный список — в src/styles/semantic.css, он же единственный файл,
который стоит открыть, чтобы понять весь доступный API темизации.
Если нужна тема с нуля, без primitives
import '@1337exp/taoui/styles/primitives.css' // шкалы можно оставить
import './my-semantic.css' // свой семантический слой вместо @1337exp/taoui/styles/semantic.css
import '@1337exp/taoui/styles/base.css'
Демонстрация
Все компоненты с живыми примерами, таблицами пропсов и готовыми сниппетами. Язык (EN / RU) и тема (dark / light) переключаются в шапке.
cd showcase
npm install
npm run dev # или из корня репозитория: npm run showcase
Showcase смотрит прямо в src/, поэтому правки компонентов видны без
npm run build. npm run showcase:build собирает статический сайт с относительными
путями — его можно положить в любую папку (например, /sandbox/taoui/).
Скриншоты и ролики в этом README сняты с showcase: см. docs/media.
Разработка
# Сборка библиотеки
npm run build
# Режим разработки (watch)
npm run dev
# Проверка типов
npm run type-check
# Тесты (Vitest)
npm test
Публикация в npm
Публикуется только dist/ (собранная библиотека) и src/styles/ (сырые
токены — чтобы можно было подключить primitives.css отдельно при
кастомной теме), плюс README.md, LICENSE и CHANGELOG.md. Исходники компонентов (src/components/*.vue),
конфиги сборки и showcase/ в пакет не попадают — они остаются в
репозитории; чтобы их получить, репозиторий клонируют.
npm publish сам прогоняет проверку типов, тесты и полную пересборку перед
публикацией (хук prepublishOnly в package.json) — так в реестр не
уедет пакет со старым или отсутствующим dist/. Собирать вручную перед
npm publish не нужно.
Проверить, что именно попадёт в пакет, без реальной публикации:
npm pack --dry-run
Токен публикации не должен лежать в репозитории: в .npmrc ссылайтесь на него как
//registry.npmjs.org/:_authToken=${NPM_TOKEN} и задайте NPM_TOKEN в окружении.
.npmrc в любом случае в .gitignore.
Участие в проекте
Issues и pull request'ы приветствуются — см. CONTRIBUTING.md. Об уязвимостях: SECURITY.md.
Лицензия
MIT © 2026 1337exp
Related Packages
Nuxt module for By Sages Elements — auto-imports the paper-and-ink Vue component...
Componentes UI atómicos como Web Components. Sin dependencias; funcionan en Reac...
Documentation theme for By Sages Elements — a Nuxt Content layer that turns a co...
Vue 3 component library with full theming, dark mode, palettes, calendar, schedu...
Version History
| Version | Released | License |
|---|---|---|
| 4.0.1 | MIT | |
| 4.0.0 | MIT | |
| 3.4.1 | MIT | |
| 3.3.1 | MIT | |
| 3.3.0 | MIT | |
| 3.2.1 | MIT | |
| 3.2.0 | MIT | |
| 3.1.1 | MIT | |
| 3.1.0 | MIT | |
| 3.0.0 | MIT | |
| 2.1.1 | MIT | |
| 2.1.0 | MIT | |
| 2.0.0 | MIT | |
| 1.13.1 | MIT | |
| 1.13.0 | MIT | |
| 1.12.0 | MIT | |
| 1.11.6 | MIT | |
| 1.11.5 | MIT | |
| 1.11.4 | MIT | |
| 1.11.3 | MIT | |
| 1.11.2 | MIT | |
| 1.11.1 | MIT | |
| 1.11.0 | MIT | |
| 1.10.4 | MIT | |
| 1.10.3 | MIT | |
| 1.10.2 | MIT | |
| 1.10.1 | MIT | |
| 1.10.0 | MIT | |
| 1.9.0 | MIT | |
| 1.8.0 | MIT |
Showing the latest 30 of 45. See every release on npm