Система компонентов Tailwind · Новый ресурс

Дизайн-система Preline UI.

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

Руководство OpenDesign9 глав31 справочная записьАвторское руководство по приложению
01

Preline UI Предварительный просмотр интерфейса дизайн-системы

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

Планировщик студийной субботыОткрыть полный пример
Preline 4.2.0 HSTabs и HSInputNumber с Tailwind CSS 4.3.3 и стандартной темой из пакета. · Справочник по реализации
02

Preline UI Справочник по дизайн-системе

Preline npm 4.2.0 с Tailwind CSS 4.3.3; стандартная тема из установленного пакета. На сайте маркетинга и документации может отображаться баннер с более новой версией, поэтому в исполняемом примере версия зафиксирована явно. Псевдонимы темы остаются псевдонимами; шестнадцатеричные значения не предполагаются.

Стандартная светлая семантическая тема

Источник
Название / правилоЗначение / конфигурацияПрименение
--backgroundvar(--color-white)Стандартная роль фона.
--foregroundvar(--color-gray-800)Роль основного текста.
--bordervar(--color-gray-200)Общая роль границы.
--primaryvar(--color-primary-600)Активная вкладка и основное действие.
--primary-foregroundvar(--color-white)Текст на основной кнопке.
--primary-hovervar(--color-primary-700)Состояние при наведении.
--primary-focusvar(--color-primary-700)Состояние фокуса.
--primary-activevar(--color-primary-700)Активное состояние.
--layervar(--color-white)Фон элемента управления количеством.
--layer-linevar(--color-gray-200)Граница элемента управления количеством.
--layer-hovervar(--color-gray-50)Фон при наведении.
--cardvar(--color-white)Карточки сеансов и групп.
--card-linevar(--color-gray-200)Граница карточки.
--muted-foreground-1var(--color-gray-500)Вспомогательный текст.

Фактически используемые шкалы Tailwind

Источник
Название / правилоЗначение / конфигурацияПрименение
p-2 / gap-20.5remОболочка элемента управления количеством и интервалы между вкладками.
px-3 / gap-30.75remВнутренние отступы вкладок и интервалы элемента управления числом.
p-51.25remКарточки и стандартный внутренний отступ страницы.
sm:p-82rem from 640pxВнутренний отступ страницы при малой контрольной точке.
mt-71.75remОтступ перед планировщиком.
text-xs0.75rem / 1remВспомогательные подписи.
text-sm0.875rem / 1.25remЭлементы управления и пояснительный текст.
text-lg1.125rem / 1.75remЗаголовок группы.
text-2xl1.5rem / 2remНазвание сеанса.
text-3xl1.875rem / 2.25remЗаголовок страницы и итоговая сумма.
rounded-lg / rounded-xl0.5rem / 0.75remЭлементы управления / карточки; стандартные значения радиуса Tailwind.

Оригинальное приложение и ограничения плагинов

Источник
Название / правилоЗначение / конфигурацияПрименение
Group sizemin 1 / max 6 / step 1Параметры данных HSInputNumber соответствуют ограничениям HTML для полей ввода.
Print studio allowance$12 per participantУсловная смета материалов.
Clay workshop allowance$18 per participantУсловная смета материалов.
Drawing walk allowance$5 per participantУсловная смета материалов.
LayoutFlexible session + 280px group card; stack at 760pxОригинальная адаптивная композиция.
Theme mappingbg-primary → --color-primary → --primaryСемантический класс Tailwind разрешается через псевдоним Preline.
03

Preline UI Полное руководство по дизайн-системе

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

DESIGN-preline-ui.md

Подробные рекомендации, отправная точка для реализации и ссылки на источники.

Скачать руководство

Дизайн-система Preline UI Preline объединяет утилитарную разметку с плагинами поведения

Preline объединяет утилитарную разметку с плагинами поведения

В планировщике используются два нативных плагина Preline: HSTabs выбирает панель сеанса, а HSInputNumber управляет ограниченными кнопками увеличения и уменьшения значения. Большая часть визуального оформления задаётся классами Tailwind. Оригинальная иллюстрация сеанса и расчёт бюджета относятся к приложению, а не к вымышленному универсальному компоненту бронирования.

Из этого руководства OpenDesign

Это различие важно учитывать при копировании примера. Одна только установка пакета JavaScript не компилирует утилитарные классы и не предоставляет разметку приложения. И наоборот: если скопировать HTML, но не инициализировать плагины, поведение вкладок и числового поля будет работать не полностью. В полной интеграции ниже явно представлены все необходимые части.

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: проследите семантические псевдонимы до соответствующих ролей палитры

Проследите семантические псевдонимы до соответствующих ролей палитры

В стандартной светлой теме из пакета --primary ссылается на --color-primary-600, а основой синей гаммы служит шкала primary. Основной цвет текста на этом фоне — белый; для состояний при наведении, фокусе и активации используется роль с индексом 700. Выбранная вкладка и действие Save используют одну цветовую гамму благодаря семантическим классам утилит.

Для карточек используются --card и --card-line, а для поля количества — --layer и --layer-line. Разделение этих ролей делает будущие изменения темы более предсказуемыми, чем повсеместное использование классов с буквальными значениями белого и серого. В таблице сохранены официальные псевдонимы, а не вычисленные приблизительные значения, представленные как исходные токены.

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: используйте настоящую шкалу Tailwind и отмечайте исходные исключения

Используйте настоящую шкалу Tailwind и отмечайте оригинальные исключения

Для заголовка страницы и расчётной суммы используется text-3xl, для названия сеанса — text-2xl, а для текста формы — text-sm. Отступ карточки p-5 равен 1.25rem при стандартном шаге интервалов. Увеличенный внутренний отступ страницы sm:p-8 начинает действовать на малой контрольной точке Tailwind — 640px, а исходная сетка перестраивается в одну колонку при 760px.

В локальной таблице стилей используется system-ui; необязательные пользовательские шрифты, указанные в файле темы Preline, не загружаются. При ширине 280px вкладки переносятся на две строки, а карточка группы перемещается под описание сеанса. Это намеренные решения по компоновке приложения, а не утверждение, что все макеты Preline имеют такую геометрию.

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: связывайте элементы управления вкладками и панели с помощью постоянных идентификаторов

Связывайте элементы управления вкладками с панелями посредством стабильных ID

У каждой вкладки есть data-hs-tab, указывающий на уникальную панель. Связь продублирована в aria-controls вкладки и aria-labelledby панели для вспомогательных технологий. Атрибуты active и aria-selected=true заданы только одной начальной вкладке, а остальные панели изначально скрыты. При навигации HSTabs обновляет эти состояния.

Для активного состояния используются стили hs-tab-active:bg-primary и hs-tab-active:text-primary-foreground. Эти варианты определены в variants.css из Preline, поэтому без импорта этого файла выбранное состояние может быть визуально неоднозначным, даже если JavaScript корректно меняет классы. Приложение отслеживает изменения выбора, чтобы обновлять расположенную рядом сводку.

  1. Выберите мастерскую керамики и проверьте её название, материалы и продолжительность.
  2. Увеличьте число участников с двух до трёх и проверьте расчётную сумму $54.
  3. Сохраните локальный план, затем смените вкладку, чтобы сбросить прежнее подтверждение.

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: согласуйте параметры плагинов и ограничения ввода

Настройки плагинов должны соответствовать ограничениям полей ввода

Для контейнера количества в data-hs-input-number заданы min — один, max — шесть и step — один. Те же ограничения указаны в стандартном поле ввода числа. Так и кнопки плагина, и встроенная проверка браузера используют один допустимый диапазон, а введённое вручную число не может обойти ограничение, предусмотренное только обработчиком кнопок.

Сводка отслеживает события input и change, а также обратный вызов change плагина. Если вручную введено недопустимое значение, вместо итоговой суммы показывается длинное тире, а кнопка сохранения отключается; обработчик сохранения также вызывает reportValidity перед чтением значения. Ограничение в шесть человек — примерное ограничение для планирования, а не гарантия реальной доступности.

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: рассчитывайте сумму по активной панели, а не по устаревшим подписям

Выполняйте расчёты по активной панели, а не по устаревшим меткам

В атрибутах data каждой панели хранятся постоянные название сеанса, цена и продолжительность. Функция обновления считывает видимую панель и умножает стоимость материалов на допустимое число участников. Так «Студия печати», «Мастерская керамики» и «Прогулка с рисованием» становятся разными вариантами, а не вкладками, которые показывают одну и ту же карточку-заполнитель.

В расчётную сумму включены только материалы. В исходных описаниях указано, что покрывает эта сумма, а на странице явно сказано, что она не бронирует место. За этими цифрами не стоят оплата, проверка наличия мест или внешнее расписание. Для настоящего процесса бронирования понадобятся отдельные системы и проверка данных на сервере.

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: используйте экспортируемую точку входа для ручной инициализации в этой версии пакета

Используйте экспортируемый способ ручного запуска в этой версии пакета

В проверенном пакете версии 4.2.0 доступны именованные экспорты HSTabs и HSInputNumber из preline/non-auto. В примере они импортируются, а после появления разметки вызываются их методы autoInit. Несмотря на название, здесь autoInit — явное сканирование, запускаемое приложением, а не неявный обработчик загрузки страницы для всего пакета.

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

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: отменяйте подтверждение при изменении плана

Отменяйте подтверждение при изменении плана

Кнопка «Сохранить локальный план» записывает статусное сообщение с точным названием сеанса, числом участников и общей стоимостью материалов, после чего отключается. При любом изменении количества или выбранной вкладки вызывается update: она возвращает обычное пояснительное сообщение и заново проверяет, можно ли сохранять план. Ранее сохранённое сообщение не останется привязанным к новому плану.

В локальном тесте выбрали «Мастерскую керамики», увеличили число участников до трёх и проверили сообщение с суммой $54. Также проверили фактически отображаемую страницу при ширине 280px. Фиксированная сумма или кнопка, которая лишь меняет свою подпись, не доказывают, что два встроенных плагина и состояние приложения связаны между собой.

  • Используйте область статуса для точного результата локального расчёта.
  • Не учитывайте недопустимые значения при расчёте суммы.
  • При адаптации разметки сохраняйте постоянные идентификаторы вкладок и панелей.

Прочитать вспомогательную спецификацию

Дизайн-система Preline UI: компилируйте тему, варианты и фактическую исходную разметку

Компилируйте тему, варианты и фактическую исходную разметку

Сначала таблица стилей импортирует Tailwind, затем варианты Preline и стандартную тему из пакета. Явные пути к исходным файлам включают локальный HTML, приложение JavaScript и файлы дистрибутива Preline, поэтому в сгенерированном CSS доступны утилиты, зависящие от состояния. После этих импортов подключается таблица стилей исходного макета.

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

Прочитать вспомогательную спецификацию

Preline UI Примеры компонентов дизайн-системы

Полная семантическая разметка планировщика

Идентификаторы вкладок, связи с панелями, хуки поля ввода числа и отдельная локальная область статуса.

html
<main class="max-w-5xl mx-auto p-5 sm:p-8"><header><p class="text-xs font-semibold tracking-widest text-muted-foreground-1">STUDIO SATURDAY · SAMPLE PROGRAM</p><h1 class="text-3xl font-semibold tracking-tight mt-3">Choose a different kind of afternoon.</h1><p class="text-sm text-muted-foreground-1 leading-6 mt-3 max-w-2xl">Explore three creative sessions and calculate a sample materials budget for your group. This planner does not book a place.</p></header><div class="planner-grid mt-7"><section class="min-w-0"><nav class="flex flex-wrap gap-2 mb-5" role="tablist" aria-label="Creative sessions"><button type="button" id="tab-print" data-hs-tab="#panel-print" role="tab" aria-controls="panel-print" aria-selected="true" class="active hs-tab-active:bg-primary hs-tab-active:text-primary-foreground py-2 px-3 text-sm rounded-lg border border-border text-foreground hover:bg-layer-hover focus-visible:outline-2 focus-visible:outline-primary">Print studio</button><button type="button" id="tab-ceramics" data-hs-tab="#panel-ceramics" role="tab" aria-controls="panel-ceramics" aria-selected="false" class=" hs-tab-active:bg-primary hs-tab-active:text-primary-foreground py-2 px-3 text-sm rounded-lg border border-border text-foreground hover:bg-layer-hover focus-visible:outline-2 focus-visible:outline-primary">Clay workshop</button><button type="button" id="tab-drawing" data-hs-tab="#panel-drawing" role="tab" aria-controls="panel-drawing" aria-selected="false" class=" hs-tab-active:bg-primary hs-tab-active:text-primary-foreground py-2 px-3 text-sm rounded-lg border border-border text-foreground hover:bg-layer-hover focus-visible:outline-2 focus-visible:outline-primary">Drawing walk</button></nav><div id="panel-print" role="tabpanel" aria-labelledby="tab-print" class="" data-name="Print studio" data-price="12" data-duration="90 minutes"><div class="session-art print" aria-hidden="true"><i></i><b></b><span></span></div><div class="p-5 bg-card rounded-b-xl border border-card-line border-t-0"><p class="text-xs text-muted-foreground-1 font-medium">09:30–11:00 · 90 minutes</p><h2 class="text-2xl mt-3 font-semibold">Ink, paper, repeat.</h2><p class="text-sm leading-6 mt-3 text-muted-foreground-1">Make a two-color print from a simple cut-paper composition.</p><h3 class="text-sm font-semibold mt-5">Materials included in the estimate</h3><ul class="mt-3 space-y-2 text-sm"><li class="flex gap-2"><span aria-hidden="true">↗</span>Cobalt ink</li><li class="flex gap-2"><span aria-hidden="true">↗</span>Warm white stock</li><li class="flex gap-2"><span aria-hidden="true">↗</span>Registration guide</li></ul><p class="mt-5 text-sm font-medium">Sample materials allowance: $12 per person</p></div></div><div id="panel-ceramics" role="tabpanel" aria-labelledby="tab-ceramics" class="hidden" data-name="Clay workshop" data-price="18" data-duration="120 minutes"><div class="session-art ceramics" aria-hidden="true"><i></i><b></b><span></span></div><div class="p-5 bg-card rounded-b-xl border border-card-line border-t-0"><p class="text-xs text-muted-foreground-1 font-medium">13:00–15:00 · 120 minutes</p><h2 class="text-2xl mt-3 font-semibold">A shape held by hand.</h2><p class="text-sm leading-6 mt-3 text-muted-foreground-1">Build a small pinch vessel and explore three surface textures.</p><h3 class="text-sm font-semibold mt-5">Materials included in the estimate</h3><ul class="mt-3 space-y-2 text-sm"><li class="flex gap-2"><span aria-hidden="true">↗</span>Stoneware clay</li><li class="flex gap-2"><span aria-hidden="true">↗</span>Wooden modeling tool</li><li class="flex gap-2"><span aria-hidden="true">↗</span>Texture samples</li></ul><p class="mt-5 text-sm font-medium">Sample materials allowance: $18 per person</p></div></div><div id="panel-drawing" role="tabpanel" aria-labelledby="tab-drawing" class="hidden" data-name="Drawing walk" data-price="5" data-duration="60 minutes"><div class="session-art drawing" aria-hidden="true"><i></i><b></b><span></span></div><div class="p-5 bg-card rounded-b-xl border border-card-line border-t-0"><p class="text-xs text-muted-foreground-1 font-medium">16:00–17:00 · 60 minutes</p><h2 class="text-2xl mt-3 font-semibold">Notice the edges.</h2><p class="text-sm leading-6 mt-3 text-muted-foreground-1">Make a sequence of quick drawings along an original sample walking route.</p><h3 class="text-sm font-semibold mt-5">Materials included in the estimate</h3><ul class="mt-3 space-y-2 text-sm"><li class="flex gap-2"><span aria-hidden="true">↗</span>Pocket sketchbook</li><li class="flex gap-2"><span aria-hidden="true">↗</span>Soft graphite pencil</li><li class="flex gap-2"><span aria-hidden="true">↗</span>Portable drawing board</li></ul><p class="mt-5 text-sm font-medium">Sample materials allowance: $5 per person</p></div></div></section><aside class="bg-card border border-card-line rounded-xl p-5 self-start"><h2 class="text-lg font-semibold">Your group</h2><label for="people" class="block text-sm mt-5 mb-2">Participants</label><div id="quantity" data-hs-input-number='{"min":1,"max":6,"step":1}' class="flex items-center gap-3 border border-layer-line rounded-lg p-2 bg-layer"><button type="button" data-hs-input-number-decrement aria-label="Decrease participants" class="size-9 shrink-0 rounded-md border border-border disabled:opacity-40">−</button><input id="people" type="number" value="2" min="1" max="6" step="1" data-hs-input-number-input class="w-full min-w-0 text-center bg-transparent"/><button type="button" data-hs-input-number-increment aria-label="Increase participants" class="size-9 shrink-0 rounded-md border border-border disabled:opacity-40">+</button></div><p class="text-xs text-muted-foreground-1 mt-2">1–6 participants · sample planning limit</p><dl class="mt-6 space-y-3 text-sm"><div><dt class="text-muted-foreground-1">Selected session</dt><dd id="selected-name" class="font-semibold mt-1"></dd></div><div><dt class="text-muted-foreground-1">Duration</dt><dd id="selected-duration" class="font-semibold mt-1"></dd></div><div class="pt-4 border-t border-border"><dt class="text-muted-foreground-1">Materials estimate</dt><dd id="total" class="text-3xl font-semibold mt-1"></dd></div></dl><button id="save" class="mt-6 w-full py-3 px-4 bg-primary text-primary-foreground rounded-lg font-medium text-sm hover:bg-primary-hover disabled:opacity-50" type="button">Save local plan</button><p id="feedback" role="status" class="text-xs leading-5 mt-3 text-muted-foreground-1">No booking or payment is created.</p></aside></div></main>

Справочник по коду

  • Три сеанса и цены — исходные примерные данные.
  • Ограничения встроенного поля ввода совпадают с ограничениями плагина.

Полная инициализация плагинов и расчёт

Ручная инициализация через экспортируемую точку входа non-auto; выбор и количество используются для вычисления сводки.

javascript
import {HSTabs,HSInputNumber} from 'preline/non-auto';
HSTabs.autoInit();HSInputNumber.autoInit();
const quantity=document.querySelector('#people'),save=document.querySelector('#save'),feedback=document.querySelector('#feedback');
function selected(){return document.querySelector('[role=tabpanel]:not(.hidden)')}
function update(){const panel=selected(),count=Number(quantity.value);document.querySelector('#selected-name').textContent=panel.dataset.name;document.querySelector('#selected-duration').textContent=panel.dataset.duration;document.querySelector('#total').textContent=quantity.checkValidity()&&count>=1?'$'+count*Number(panel.dataset.price):'—';save.disabled=!quantity.checkValidity()||count<1;feedback.textContent='No booking or payment is created.'}
document.querySelectorAll('[data-hs-tab]').forEach(button=>button.addEventListener('click',()=>queueMicrotask(update)));
new MutationObserver(update).observe(document.querySelector('nav'),{subtree:true,attributes:true,attributeFilter:['aria-selected']});
quantity.addEventListener('input',update);quantity.addEventListener('change',update);
const instance=HSInputNumber.getInstance('#quantity');instance.on('change',update);
save.addEventListener('click',()=>{if(!quantity.reportValidity())return;const panel=selected();feedback.textContent=`Saved locally: ${panel.dataset.name}, ${quantity.value} people, $${Number(quantity.value)*Number(panel.dataset.price)} materials estimate.`;save.disabled=true});update();

Справочник по коду

  • Используйте именованные экспорты из preline/non-auto.
  • Файлы .mjs версионированных путей плагинов не экспортируют класс по умолчанию.
  • События изменения поля ввода и обратные вызовы change плагина обновляют сводку.

Полная исходная адаптивная компоновка

Декоративные иллюстрации печати, керамики и рисунка вокруг встроенных плагинов вкладок и ввода числа.

css
html,body{margin:0;height:auto;min-height:0}body{background:#f7f8fb;font-family:ui-sans-serif,system-ui,sans-serif;color:var(--foreground)}button{cursor:pointer}button:disabled{cursor:default}.planner-grid{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:24px}.session-art{height:200px;position:relative;overflow:hidden;border-radius:12px 12px 0 0}.session-art i,.session-art b,.session-art span{display:block;position:absolute}.print{background:#e2e9f4}.print i{width:160px;height:140px;left:15%;top:45px;background:#2456a7;transform:rotate(-13deg)}.print b{width:150px;height:150px;right:14%;top:30px;background:#f0a979;border-radius:50%;mix-blend-mode:multiply}.print span{width:180px;height:8px;left:20%;top:160px;background:#f9f8ef;transform:rotate(-13deg)}.ceramics{background:#e8d9c8}.ceramics i{width:150px;height:100px;left:25%;top:65px;border-radius:15% 15% 50% 50%;background:#a87862}.ceramics b{width:150px;height:32px;border-radius:50%;left:25%;top:53px;background:#704a3d;box-shadow:inset 0 0 0 10px #bd8f78}.ceramics span{width:150px;height:12px;left:25%;top:171px;background:#c7b39e;border-radius:50%}.drawing{background:#e2e8de}.drawing i{width:160px;height:145px;left:23%;top:25px;background:#fffdf4;transform:rotate(8deg);border:1px solid #d6d8cb}.drawing b{height:90px;width:80px;left:33%;top:49px;border:2px solid #4d655b;transform:rotate(-18deg);border-radius:50% 50% 10% 10%}.drawing span{width:130px;height:7px;background:#ad754c;left:45%;top:145px;transform:rotate(-33deg)}@media(max-width:760px){.planner-grid{grid-template-columns:minmax(0,1fr)}.session-art{height:170px}}@media(max-width:360px){main{padding:14px!important}.session-art i,.session-art b,.session-art span{transform:scale(.8)}.session-art{height:150px}h1{font-size:26px!important}}

Справочник по коду

  • Иллюстрации созданы специально для CSS и скрыты от вспомогательных технологий с помощью aria-hidden.
  • Эта таблица стилей подключается после импортов Tailwind и Preline.
04

Дизайн-система Preline UI Настройка агента

Отправная точка для реализации

Проверена встроенная версия Preline 4.2.0. Полная разметка, логика и CSS приведены выше. Пути к исходным файлам в input.css предполагают, что все четыре исходных файла находятся в корне проекта.

bash
# Create an empty directory and run:
npm init -y
npm install [email protected] [email protected] @tailwindcss/[email protected] esbuild
# Save the complete examples above as body.html, app.js and style.css.
# Create input.css with exactly:
# @import "tailwindcss" source(none);
# @import "./node_modules/preline/variants.css";
# @import "./node_modules/preline/css/themes/theme.css";
# @source "./body.html";
# @source "./app.js";
# @source "./node_modules/preline/dist/*.js";
# @import "./style.css";
npx @tailwindcss/cli -i input.css -o bundle.css --minify
npx esbuild app.js --bundle --format=iife --outfile=bundle.js
# Assemble the actual root document without placeholder markup:
node -e 'const f=require("fs");f.writeFileSync("index.html",`<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Studio planner</title><link rel="stylesheet" href="bundle.css"></head><body>${f.readFileSync("body.html","utf8")}<script src="bundle.js"></script></body></html>`)'
# Serve this directory with your existing static server.

Запрос для вашего проекта

Промпт для AI-агента
Используйте Preline 4.2.0 с именованными импортами HSTabs и HSInputNumber из preline/non-auto и явно инициализируйте оба плагина. Сохраните три мастер-класса Studio Saturday, диапазон количества от одного до шести и стоимость материалов выбранного мастер-класса. Рассчитывайте сумму по активной панели и количеству участников, сбрасывайте сохранённый план при изменении любого из этих значений и проверяйте допустимость числового диапазона перед локальным сохранением. Добавьте указанную настройку темы и вариантов Tailwind и исходную компоновку. Не превращайте локальный план студии в подтверждение бронирования или оплаты.
05

Preline UI Источники и область применения дизайн-системы

Встроенный Preline npm 4.2.0 и Tailwind CSS 4.3.3. Исходные описания сеансов, иллюстрации и вымышленные суммы на материалы. Сохранение записывает только локальное состояние страницы; бронирование, оплата и отправка внешних сообщений не выполняются.