Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Specify — open‑source Framer на спеках

Дата публикации: 15-10-2026 17:08:02

Генератор веб-приложений из спецификации. Умеет генерировать 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, объединяющий абстракцию дизайна и фронтенда в одной спецификации.

Что такое Specify

Specify — это не фреймворк и не билдер, хотя похоже на одно и другое. Это декларативная спецификация и компилятор (транслятор) спецификации в код приложения.

Я делал его для решения своих задач, но если это поможет другим дизайнерам и командам перестать играть в сломанный телефон между дизайном и разработкой — буду рад.

Дизайнер, если ты ещё здесь, Specify — это не больно:

  1. Спецификация на языке дизайна — здесь почти вся терминология из Figma.

  2. IDE помогает писать: автоподстановка свойств и значений.

  3. Любое изменение мгновенно отображается в браузере (HMR).

  4. Не хочешь писать спецификацию руками — можно попробовать генерировать её с помощью 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

8e32ae5d1f94af3ff63b02e3f55d974e.png
  • lay — секция Auto Layout

0bbb8a3f0b079a865ebd3db21d984c62.png
  • vis — секция Appearance

f287efc42e6719178a4dc207ace77d74.png
  • txt — секция Typography

7011059404e1fdab0f805224c0031a1c.png
  • 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). Подробнее в документации.

Отправка данных (post‑data)

Тоже надстройка над SDK. В оффлайне складывает запросы в очередь, а как появится сеть — отправляет в Appwrite. Создаёт, обновляет, удаляет записи в БД. Всё, что касается аккаунта пользователя. Управление командами. Загрузка файлов в Storage. Подробнее в документации.

Расширяемость

Specify не навязывает стек. Трансляция спецификации в итоговый код идёт через Vite‑плагины, и вы можете:

  • Написать свой Vite‑плагин и транслировать спецификацию в любой фреймворк: React, Vue, Svelte, Solid — что угодно.

  • Расширить DSL под свой бэкенд: если у вас не Appwrite, а Supabase, Firebase, свой REST или GraphQL — можете дописать свои операторы get-data / post-data и работать с ними как с родными.

Дальнейшие планы
  1. Добавить необходимый минимум базовых компонентов.

  2. Добавить Push‑уведомления, подписка/отписка на клиенте.

  3. Расширить документацию и добавить больше примеров.

P. S. Если тебе откликается Specify и ты хочешь принять участие в развитии (код, документация, примеры, дизайн компонентов) — пиши на kov@lenko.email. Также буду рад фидбеку и звёздочкам на GitHub.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1Микрофронтенды. Стабильная интеграция нескольких SPA-приложений. Часть 10529-06-2026
2Meet the OpenAppSec: open-source WAF на базе машинного обучения011.0622-09-2026
3Ваш AI‑агент не ошибся. Он точно выполнил плохую спецификацию09.4803-08-2026
4Ты не найдёшь эту ошибку. Потому что её нет в твоём коде. Как Self-describing API спасает от чужих рефакторингов5807-07-2026
5Point0 — фулстек TypeScript-фреймворк на Bun и React, о котором я мечтал5701-07-2026
6WASM кроссбраузерность во Flutter Web на 202608.0108-10-2026
7Оптимизация без AI: как я автоматизировал API-ручки и типы-2529-06-2026
8Один файл, одна команда: как мы упростили запуск dev-окружения с помощью Runium012.1124-07-2026
9Делюсь большой крутой дизайн-системой, которую мы используем на реальных проектах011.2419-05-2026
10Нейросеть-автопилот вместо 400 Playwright-тестов0707-07-2026

Классификация: Пресс-релизы. Схожих патентов: 0. Схожих новостей: 10. Тональность: 0. Информативность: 22. Источник: habr.com.