Генератор веб-приложений из спецификации. Умеет генерировать PWA (SPA) на Svelte, с бронебойной автономностью. Есть интеграция с open-source бэкенд-платформой Appwrite. Узнать подробнее
Время на прочтение7 мин
Охват и читатели7.4K
Обзор
Привет, Хабр!
Я принёс Specify — open‑source генератор веб‑приложений из спецификации. Это как Framer, только без визуального редактора и без чёрной магии AI под капотом.
Из коробки Specify умеет генерировать PWA (SPA) на Svelte, с бронебойной автономностью, приложение откроется в лифте, на парковке, при белых списках и даже если физически не станет сервера. Есть интеграция с open‑source бэкенд‑платформой Appwrite, о которой я расскажу ниже.
Зачем я это сделалВообще, я не разработчик, мой основной опыт в продуктовом дизайне, а программирую я для души).
Примерно 3 года назад у меня возникла мотивация закрыть весь цикл разработки продукта самостоятельно. Если 15–20 лет назад я мог сам создать сайт, сверстать дизайн и натянуть HTML c CSS на CMS, то сегодня этого опыта недостаточно. Чтобы сделать современное веб‑приложение, нужно погрузиться во фронтенд‑фреймворки, сборщики проектов, современный бэк с веб‑сокетами и так далее
Я быстро понял, что ручная верстка и перенос изменений из дизайна в код сильно меня тормозят. И я начал делать, по сути, свой аналог Framer. За 3 года я прошел путь от автоматизации переноса дизайна в код (парсинга JSON‑данных дизайна и полной сборки приложения) — до понимания, что нужно делать свой редактор.
Я хотел сделать свой полноценный визуальный редактор, но пока что на это нет ресурса. Зато я понял, что можно сделать MVP без собственного редактора, используя максимум возможностей IDE для улучшения DX.
Так появился Specify, объединяющий абстракцию дизайна и фронтенда в одной спецификации.
Что такое SpecifySpecify — это не фреймворк и не билдер, хотя похоже на одно и другое. Это декларативная спецификация и компилятор (транслятор) спецификации в код приложения.
Я делал его для решения своих задач, но если это поможет другим дизайнерам и командам перестать играть в сломанный телефон между дизайном и разработкой — буду рад.
Дизайнер, если ты ещё здесь, Specify — это не больно:
Спецификация на языке дизайна — здесь почти вся терминология из Figma.
IDE помогает писать: автоподстановка свойств и значений.
Любое изменение мгновенно отображается в браузере (HMR).
Не хочешь писать спецификацию руками — можно попробовать генерировать её с помощью AI и получить что‑то похожее на Google Stitch (в проекте для этого есть Skill).
Лучше один раз увидеть, чем сто раз услышать:
(Слева YAML‑экрана, под ним Svelte‑код этого экрана, который обновляется в реальном времени, справа вкладка браузера.)
Чтобы дизайнерам дальше было проще воспринимать Specify, проведу параллели с Figma.
СтилиВ Specify в папке specs/ находится файл tokens.yaml — почти как Figma Variables. В specs/components/ — YAML‑файлы компонентов, в specs/screens/ — экранов. У каждого экрана и компонента есть слои (frame, text, image и так далее), у каждого слоя — стили.
Стили сгруппированы по префиксам и почти дублируют свойства в Figma:
pos — секция Position

lay — секция Auto Layout

vis — секция Appearance

txt — секция Typography

fill, stroke, shadow, blur — самостоятельные, без префиксов.
Пример:
styles:
lay: column # Auto Layout — Flow: Vertical
lay-width: fill-container # Тянем на всю ширину
lay-align-h: center # Выравнивание по горизонтали
lay-align-v: center # Выравнивание по вертикалиВ Figma для состояний делают варианты компонента. В Specify состояния — это стили, почти как в CSS:
hover — наведение курсора
focus — фокус в поле или при навигации с клавиатуры
pressed — клик/нажатие
disabled — неактивный элемент
selected — выбранный (поддерживает вложенные: hover, focus, pressed, disabled)
valid — поля внутри контейнера прошли валидацию (с вложенными состояниями)
invalid — не прошли валидацию (тоже с вложенными).
Пример:
styles:
fill: "#F5F5F5"
hover:
fill: "#EBEBEB"
pressed:
fill: "#E0E0E0"
focus:
stroke:
- color: white
weight: 1
position: inside
- color: blue
weight: 2
position: outside
invalid:
stroke:
- color: red
weight: 2
position: outsideСвойства компонентов и реактивностьУ компонентов есть:
props — входные данные снаружи (как в Figma).
state — внутреннее состояние.
stores — глобальные реактивные переменные с логикой, доступны из любого компонента.
logic — пользовательский код, подключаемый к компоненту.
data — статичные или динамические данные с бэкенда.
У экранов так же, только вместо props — path и params (о них в документации).
Пример:
button:
props:
label: Text # Объявляем props
layers:
- text:
content: "{{ props.label }}" # Выводим значение propsВызываем компонент на экране и пробрасываем props:
demo:
path: /demo
layers:
- c.button:
props:
label: Нажми на меняУсловия и циклыcond-if позволяет показывать элементы по условию.
demo:
path: /demo
state:
count: 0
min: -10
max: 10
layers:
- text:
content: "Счетчик лайков: {{ state.count }}"
- c.button:
# Кнопка отображается, если state.count больше state.min
cond-if: "{{ state.count > state.min }}"
props:
label: 👎
on-click:
set-state:
count: "{{ state.count - 1 }}"
- c.button:
# Кнопка отображается, если state.count меньше state.max
cond-if: "{{ state.count < state.max }}"
props:
label: 👍
on-click:
set-state:
count: "{{ state.count + 1 }}"Циклы размножают слой по массиву данных:
demo:
path: /demo
state:
isAnnually: false
data:
pricing: # Массив данных
- id: 1
title: Free
monthly: 0
annually: 0
- id: 2
title: Pro
monthly: 100
annually: 80
- id: 3
title: Max
monthly: 200
annually: 150
layers:
- frame:
for: item in data.pricing # Создаём цикл, этот frame будет повторяться столько раз, сколько записей в массиве
key: item.id # Уникальный ключ для корректного диффа в DOM
layers:
- text:
content: "{{ item.title }}" # Выводим название
- text:
content: "Цена: {{ state.isAnnually ? item.annually : item.monthly }} ₽" # Выводим цену по условию state.isAnnuallyТриггеры событийАналог Interactions из Figma. События:
on-click — клик.
on-mount / on-unmount — появление/исчезновение слоя.
on-change — изменение значения (для слоёв field и text).
on-submit — отправка формы.
on-keydown — нажатие клавиши.
События могут запустить действия:
set-state — изменить state.
set-props — для исключительных случаев с редактируемыми props (подробнее в документации).
navigate-to — переход на другой экран (с анимацией) или внешнюю ссылку.
navigate-back — назад.
set-notification — снекбар.
post-data — отправка в Appwrite.
run — выполнение пользовательского кода, от console.log() до вызова своей функции или стора.
submit / reset — отправить/сбросить форму.
Не все компоненты реально сделать без дополнительной логики. Например, в компоненте slider-md есть функция createSlider, которая следит за захватом ползунка и обновляет value.
Specify даёт возможность подключить свой код:
screenName:
path: /demo
logic:
- fnName # Функция из src/logic
stores:
- cart # Стор корзины из src/stores
on-keydown:
- key: Control+S
run: "{{ logic.fnName(stores.cart.list) }}" # Вызовем функцию при нажатии Control+S
layers:
- text:
content: "В корзине: {{ stores.cart.list.length }}" # Выведем количество товаров
- c.button:
props:
label: Добавить
on-click:
run: "{{ stores.cart.add({ name: 'Эспрессо', price: 150 }) }}" # Добавляем в корзину товарРасширяйте приложение, не затрагивая сгенерированный код.
Интеграция с AppwriteНемного про AppwriteЭто open‑source бэкенд, разворачивается на своём VPS, не сильно сложнее установки WordPress. Юзерфрендли, закрывает много вопросов через веб‑интерфейс, сам выпускает SSL для привязанных доменов. Очень быстро развивается, в этом году переработали API, добавили MongoDB и PostgreSQL. Тянет большие нагрузки, есть публичный бенчмарк, где один сервер держит больше миллиона одновременных WebSocket‑соединений. По экономике: VPS дороже хостинга, но на одном сервере можно вести несколько проектов и хостить сайты.
Запрос данных (get‑data)Надстройка над Appwrite SDK. Управляет кешем, мгновенно отвечает данными из кеша и затем возвращает актуальные данные. Не кеширует ответы по отдельности, а работает как локальная БД — сама фильтрует и сортирует. Это позволяет приложению предсказуемо работать в оффлайне. Через get-data можно получить данные из базы, профиля пользователя, команд и файлов. Поддерживается Realtime (обновление по WebSocket, включается флагом realtime) и бесконечный скролл (флаг infinite-scroll). Подробнее в документации.
Тоже надстройка над SDK. В оффлайне складывает запросы в очередь, а как появится сеть — отправляет в Appwrite. Создаёт, обновляет, удаляет записи в БД. Всё, что касается аккаунта пользователя. Управление командами. Загрузка файлов в Storage. Подробнее в документации.
РасширяемостьSpecify не навязывает стек. Трансляция спецификации в итоговый код идёт через Vite‑плагины, и вы можете:
Написать свой Vite‑плагин и транслировать спецификацию в любой фреймворк: React, Vue, Svelte, Solid — что угодно.
Расширить DSL под свой бэкенд: если у вас не Appwrite, а Supabase, Firebase, свой REST или GraphQL — можете дописать свои операторы get-data / post-data и работать с ними как с родными.
Добавить необходимый минимум базовых компонентов.
Добавить Push‑уведомления, подписка/отписка на клиенте.
Расширить документацию и добавить больше примеров.
P. S. Если тебе откликается Specify и ты хочешь принять участие в развитии (код, документация, примеры, дизайн компонентов) — пиши на kov@lenko.email. Также буду рад фидбеку и звёздочкам на GitHub.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Микрофронтенды. Стабильная интеграция нескольких SPA-приложений. Часть 1 | 0 | 5 | 29-06-2026 |
| 2 | Meet the OpenAppSec: open-source WAF на базе машинного обучения | 0 | 11.06 | 22-09-2026 |
| 3 | Ваш AI‑агент не ошибся. Он точно выполнил плохую спецификацию | 0 | 9.48 | 03-08-2026 |
| 4 | Ты не найдёшь эту ошибку. Потому что её нет в твоём коде. Как Self-describing API спасает от чужих рефакторингов | 5 | 8 | 07-07-2026 |
| 5 | Point0 — фулстек TypeScript-фреймворк на Bun и React, о котором я мечтал | 5 | 7 | 01-07-2026 |
| 6 | WASM кроссбраузерность во Flutter Web на 2026 | 0 | 8.01 | 08-10-2026 |
| 7 | Оптимизация без AI: как я автоматизировал API-ручки и типы | -2 | 5 | 29-06-2026 |
| 8 | Один файл, одна команда: как мы упростили запуск dev-окружения с помощью Runium | 0 | 12.11 | 24-07-2026 |
| 9 | Делюсь большой крутой дизайн-системой, которую мы используем на реальных проектах | 0 | 11.24 | 19-05-2026 |
| 10 | Нейросеть-автопилот вместо 400 Playwright-тестов | 0 | 7 | 07-07-2026 |