Окей, заголовок звучит максимально самоуверенно, я понимаю. Но насколько мне удалось нагуглить — это действительно первая попытка сделать что-то подобное. Если я не прав — напишите в комментах, я с удовольствием посмотрю на альтернативы. А пока давайте я расскажу, что это за зверь такой и зачем он вообще нужен.
Предыстория, или как я дошёл до жизни такой
Всё началось с микрофронтендов. Знаете, это когда у вас один проект, но внутри него живёт Vue, React, и ещё какой-нибудь легаси на jQuery, который никто не хочет трогать, потому что "оно работает, не трогай".
И вот сидишь ты такой, дизайнер приносит макет новой кнопки. Красивая кнопка, с градиентом, с hover-эффектом, всё как надо. И ты понимаешь, что сейчас тебе предстоит:
Написать эту кнопку для Vue
Написать эту же кнопку для React
Написать её ещё раз для того самого легаси
Молиться, чтобы они выглядели одинаково
И так каждый раз. Каждый. Раз.
В какой-то момент я подумал: "А что, если написать компонент один раз и использовать везде?" Революционная мысль, да? На самом деле нет, Web Components существуют уже давно. Но почему-то никто не сделал для них нормальный DX, как у того же shadcn/ui.
Что такое shadcn и почему это важно
Для тех, кто не в курсе: shadcn/ui — это не совсем библиотека в привычном понимании. Это скорее набор компонентов, которые ты копируешь себе в проект. Не устанавливаешь как зависимость, а именно копируешь.
Почему это круто:
Полный контроль над кодом
Никаких проблем с версиями
Кастомизируй как хочешь
Удаляй лишнее, добавляй нужное
Проблема в том, что shadcn написан для React. А мне нужно было что-то, что работает везде. Буквально везде.
Встречайте CapsuleUI
Короче, я взял и сделал то же самое, но для Web Components. Называется CapsuleUI, и работает это примерно так:
# Инициализируем проект
npx @zizigy/capsule init
# Добавляем компонент
npx @zizigy/capsule add Button
Всё. После этого у тебя в проекте появляется папка @capsule, а внутри — готовый компонент кнопки. Не ссылка на node_modules, не какая-то магия — просто файлы с кодом, которые ты можешь открыть, почитать и поменять.
Как это выглядит в использовании
<capsule-button variant="primary" size="lg">
Нажми меня
</capsule-button>
Да, вот так просто. Это работает в React:
function App() {
return (
<div>
<capsule-button variant="primary">
Привет из React
</capsule-button>
</div>
)
}
Это работает в Vue:
<template>
<capsule-button variant="primary">
Привет из Vue
</capsule-button>
</template>
Это работает в чистом HTML:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="@capsule/global.css">
<script type="module" src="@capsule/index.js"></script>
</head>
<body>
<capsule-button variant="primary">
Привет из 2005 года
</capsule-button>
</body>
</html>
И самое прикольное — это один и тот же компонент. Не три разных реализации, а один. Единственный. Неповторимый.
Но подожди, Web Components же...
Да, я знаю что вы хотите сказать. "Web Components — это сложно", "Shadow DOM — это боль", "А как стилизовать?", "А SSR?".
Давайте по порядку.
Сложность
Компоненты написаны на Lit. Если кто не в курсе — это легковесная библиотека от Google для создания Web Components. Она делает всю грязную работу за тебя: реактивность, шаблонизация, lifecycle. Код получается чистый и понятный.
Shadow DOM
Да, используем. Но это не проблема, а фича. Стили компонента изолированы — они не сломают твой сайт, и твой сайт не сломает их. Для кастомизации есть CSS Custom Properties и ::part().
Стилизация
Все компоненты используют CSS переменные. Хочешь поменять цвет кнопки? Пожалуйста:
:root {
--capsule-color-primary: #8b5cf6;
}
Хочешь полностью переписать стили? Файлы у тебя в проекте, открывай и редактируй.
SSR — здесь всё интересно
Окей, тут есть нюанс. Web Components работают с SSR. Сервер отрендерит сам тег компонента — он будет в HTML:
<capsule-button variant="primary">Нажми меня</capsule-button>
Но если компонент сам генерирует HTML внутри Shadow DOM (например, сложная структура со вложенными элементами), то этот HTML не будет в SSR. Тег компонента будет, содержимое Shadow DOM — нет. Это нормальное поведение для Web Components, и обычно это не проблема, потому что контент всё равно рендерится на клиенте.
Для большинства случаев это работает отлично. Если тебе критично нужен полный SSR с содержимым Shadow DOM — есть Declarative Shadow DOM, но это уже отдельная история.
Фишка с VSCode, которой я горжусь
Знаете что меня всегда бесило в Web Components? Отсутствие автодополнения. Пишешь <my-component и IDE такая: "Понятия не имею что это, удачи тебе братан".
В CapsuleUI это работает. Когда ты добавляешь компонент, CLI автоматически обновляет настройки VSCode. И твоя IDE начинает понимать кастомные элементы!
Пишешь <capsule-button — получаешь автодополнение для variant, size, disabled. Наводишь курсор — видишь документацию. Как будто это встроенный HTML-тег, только лучше.
Это работает через html.customData в настройках VSCode. CLI генерирует JSON с описанием всех компонентов и их атрибутов, а IDE это подхватывает. Магия? Нет, просто правильный DX.
Генератор VSCode data — для продвинутых
Но что если ты обновил компонент, добавил новые атрибуты, и хочешь обновить автодополнение? Или работаешь со своей папкой компонентов?
Для этого есть команда generate:
# Генерирует vscode.data.json для конкретной папки
npx @zizigy/capsule generate --dir ./my-components
# Или для отдельной папки компонента
npx @zizigy/capsule generate --dir @capsule/components/capsule-button
# Можно указать выходную директорию
npx @zizigy/capsule generate --dir ./src/components --out ./vscode-config
Эта команда парсит JavaScript и CSS файлы компонентов, извлекает все атрибуты, их типы и возможные значения, и генерирует vscode.data.json для автодополнения. Умно? Думаю, да.
Что под капотом
Окей, давайте немного технических деталей для тех, кому интересно.
Структура проекта после инициализации
@capsule/
├── components/
│ └── capsule-button/
│ ├── button.js
│ ├── button.style.css
│ └── register.js
├── global.css
└── index.js
Всё логично и понятно. Хочешь найти стили кнопки? Открываешь button.style.css. Хочешь поменять логику? Открываешь button.js. Никакой магии, никаких скрытых файлов в node_modules.
Реактивность
Компоненты полностью реактивные благодаря Lit. Меняешь атрибут — компонент перерисовывается:
const button = document.querySelector('capsule-button');
button.variant = 'secondary'; // Автоматически обновится
button.disabled = true; // И это тоже
Это работает и с JavaScript, и с фреймворками. Vue автоматически биндит атрибуты, React тоже (с небольшими нюансами, но решаемо).
Кастомные префиксы
Не нравится capsule-? Можно использовать свой:
npx @zizigy/capsule add Button --prefix ui
И получишь <ui-button> вместо <capsule-button>. Удобно, если у тебя уже есть свой неймспейс.
Модули — отдельная история
Помимо компонентов есть ещё модули. Типо, дополнительный функционал который не является компонентом, но часто нужен.
Например, модуль валидации форм:
npx @zizigy/capsule module add form
После этого у тебя появляется валидатор с кучей готовых правил:
import { CapsuleValidator, CapsuleRules } from '@capsule/modules/form';
const validator = new CapsuleValidator({
email: [CapsuleRules.required(), CapsuleRules.email()],
password: [CapsuleRules.required(), CapsuleRules.minLength(8)]
});
const result = validator.validate({
email: 'test@example.com',
password: '123'
});
// result.isValid === false
// result.errors.password === ['Минимальная длина: 8 символов']
Опять же — это твой код. Хочешь добавить своё правило? Добавляй. Хочешь убрать лишние? Убирай.
Философия проекта
CapsuleUI — это не готовая дизайн-система. Это скорее конструктор для создания своей.
Компоненты намеренно минималистичные по стилям. Базовые состояния есть, но они легко переопределяются. Потому что я понятия не имею, какой у тебя дизайн. Может у тебя Material Design, может Tailwind-стиль, может вообще что-то своё уникальное.
Идея в том, чтобы дать тебе рабочий фундамент, который ты докрутишь под себя. А не заставлять "сражаться" с чужими стилями, пытаясь их переопределить.
Реальные кейсы использования
Давайте я приведу несколько примеров, где это действительно пригодилось.
Микрофронтенды
У тебя есть проект, где часть на Vue 3, часть на React 18, а где-то ещё легаси на jQuery. И тебе нужно, чтобы все кнопки выглядели одинаково. С CapsuleUI это решается добавлением одного компонента — он работает везде.
Единая дизайн-система
Ты работаешь в большой компании, и у тебя куча разных проектов на разных технологиях. Но все должны использовать единый дизайн. Вместо того чтобы поддерживать отдельные библиотеки для каждого фреймворка, можно сделать один набор Web Components.
Быстрые прототипы
Нужно быстро забабахать прототип, но не хочется тащить целый фреймворк? Web Components работают в чистом HTML. Добавил компоненты — получил рабочую разметку.
Интеграция с CMS
Многие CMS (WordPress, Drupal и т.д.) позволяют использовать кастомные HTML. Но они не понимают React или Vue. А Web Components? Запросто. Твой редактор контента может использовать твои компоненты.
Для кого это?
Если честно, CapsuleUI — это нишевый инструмент. Он не для всех проектов.
Он идеально подходит, если:
У тебя микрофронтенды с разными фреймворками
Ты хочешь создать свою дизайн-систему на Web Components
Тебе нужны компоненты, которые работают везде
Ты хочешь полный контроль над кодом
Он скорее всего не подходит, если:
У тебя монолит на одном фреймворке (проще взять shadcn/ui для React или аналог для Vue)
Тебе нужна готовая дизайн-система из коробки (бери Vuetify, MUI, Ant Design)
Ты не хочешь заморачиваться с кастомизацией
Что дальше?
Сейчас в библиотеке есть базовый набор компонентов, который покрывает основные потребности. Но это только начало.
В планах:
Больше компонентов (модалки, дропдауны, датапикеры)
Улучшенная документация
Примеры интеграции с разными фреймворками
Возможно, CLI для генерации своих компонентов
Если есть идеи или пожелания — пишите в issues на GitHub. Я реально читаю и отвечаю.
Как попробовать
# Создаём новый проект (или используем существующий)
mkdir my-project && cd my-project
# Инициализируем
npx @zizigy/capsule init
# Добавляем компоненты
npx @zizigy/capsule add Button
npx @zizigy/capsule add Alert
npx @zizigy/capsule add Tabs
# Смотрим список доступных компонентов
npx @zizigy/capsule list
# Если обновил компонент, генерируем VSCode data заново
npx @zizigy/capsule generate --dir @capsule/components/capsule-button
GitHub: github.com/ZiZIGY/CapsuleUI
npm: @zizigy/capsule
Документация: CapsuleUI
Вместо заключения
Знаете, когда я начинал этот проект, я просто хотел решить конкретную проблему на работе. А в итоге получилось что-то, чем я реально горжусь.
Web Components долгое время были "технологией будущего, которое никогда не наступит". Но сейчас они работают во всех браузерах, есть отличные инструменты вроде Lit, и, как мне кажется, самое время дать им нормальный developer experience.
CapsuleUI — это моя попытка это сделать. Возможно, не идеальная. Возможно, с багами (если найдёте — пишите, исправлю). Но это работает, и это решает реальную проблему.
Буду рад фидбеку, звёздам на GitHub и, конечно, контрибьюторам. Потому что один в поле не воин, а хороший open-source проект — это всегда командная работа.
Спасибо, что дочитали до конца. Вы восхитительны! ✨

Комментарии (67)

venanen
01.12.2025 12:35Очень редко на хабре появляется что-то действительно годное, а не очередная поделка в стиле "я сделал Vue, но хуже и сложнее". Предварительно выглядит круто, занесу ребятам на попробовать.

nikolayshabalin
01.12.2025 12:35Решил для себя проверить доступные ли в теневом доме элементы. Оказалось вполне себе доступные как и в лайт-доме.
Попутешествовал по https://zizigy.github.io/CapsuleUI/ с помощью VoiceOver. Вполне себе годно получилось.
Возможно есть какие-то подводные камни, но при беглом осмотре найти не удалось.
Дерево досутпности строится адекватно
monochromer
01.12.2025 12:35Те же табы недоступны с клавиатуры

nikolayshabalin
01.12.2025 12:35Вот до табов я не дошёл

Хотя роли прописаны.
Это опенсорс. Можно репортнуть https://github.com/ZiZIGY/CapsuleUI/issues

kipar
01.12.2025 12:35Мне больше нравятся CSS-only библиотеки, типа Spectre.css - там еще проще, добавляешь файл и на любом фреймворке заработает, никакой npx не нужен. Правда некоторые компоненты получаются не без хаков, да и как там с доступностью я тоже не уверен.

nihil-pro
01.12.2025 12:35Ладно, сами напросились…))
Я, признаюсь, посмотрел только код автокомплита, и для себя все понял. До продакшн решения тут как пешком до луны. По большому счету, вы ничего то и не сделали.
Компонент кнопки (button) у меня уже есть в html, нет, реально, ни одной причины делать кнопку в виде веб-компонента. Большая часть остальных, типа Chip, Badge и т.д — делается несколькими строчками цсс. Ну и т.д.
Что реально нужно — автокомплит — маст хэв, только ваш готов процентов на 5, не более. Редкие и не везде нужные, но все же такие вещи, которые на цсс не сделать, например dual-range. То есть, не надо паковать то, что уже есть в хтмл в веб компоненты, надо предлагать то, чего нет.
И хотя я оцениваю решение на условные 5 из 10, в целом — плюсанул, потому что сам веб компоненты люблю, и считаю их недооцененными.
Попробуйте сделать какой-то один, но с полной поддержкой всего. Например, автокомплит должен уметь в множественных выбор, в поиск по опциям, в кастомизацию выбранных опций и самих опций + вести себя для браузера так же как инпут, то есть уметь в валидацию, в :valid, :invalid, :required :empty и тд и тп.
Направление вы выбрали правильное, на мой взгляд.

ZiZIGY Автор
01.12.2025 12:35Автокомплит в процессе, я сначала всю UI либу написал на нативе а потом решил перенести на Lit, чтобы было удобно в поддержке.
Я планирую в целом сделать все для формы чтобы оно корректно работало Select, Autocomplete и прочее, чтобы оно кастомизировалось, так же Modal/ Dialog и прочее.
Спасибо за конструктивный комментарий) Тут все по фактам :3
supercat1337
01.12.2025 12:35Спасибо, что делитесь опытом, интересная работа.
Кстати, а почему всё-таки от ваниллы отказались? С какими сложностями столкнулись?

ZiZIGY Автор
01.12.2025 12:35Я подумал что разработчик который пользуется библиотекой может не знать все подводные камни вебкомпонентов и то как работает реактивность и вместо того чтобы вычитывать всю информацию покусочкам, он просто зайдет в документацию Lit, + меньше кода

supercat1337
01.12.2025 12:35Согласен, что плодить новые сущности в виде кастомных компонентов надо тогда, когда можно существенно упростить разметку. Джаваскриптовое поведение можно и через is аккуратно подключить, оставляя понятные всем теги.

nihil-pro
01.12.2025 12:35через is
Если бы не интернет эксплорер, ой, извините — сафари))

supercat1337
01.12.2025 12:35+1. Действительно! Я-то думал, что будущее уже наступило и все давно поддерживается) Вот оно что. Тогда только кастомное имя остаётся.

Lodin
01.12.2025 12:35Разработчики сафари наотрез отказались поддерживать эту спеку. Так что её можно не ждать. Плюс многие инструменты для веб-компонентов нарочито игнорируют её. У меня есть смутное подозрение, что рано или поздно её выпилят за ненадобностью.
Вообще вначале я был очень "за" эту спеку, но после нескольких попыток её использовать я понял, что по сути она мне не нужна. Для веб компонентов я чаще всего использую ShadowDOM, а custom built-in elements мне такой возможности не дают. В результате я так ни разу ею и не воспользовался, хотя у ungap project есть хороший полифилл для этого (а ещё я сам такой полифилл писал).

Kenya-West
01.12.2025 12:35Спасибо! Как-нибудь попробую в следующем пет-проекте с Angular. Хотя я подсел на Microsoft Fluent, благо у них скоро 3.0 для веб-компонентов релизнется, но все равно не против юзнуть что-то новое.

Heggi
01.12.2025 12:35Библиотеку веб-компонентов давно уже пилит гугл https://github.com/material-components/material-web
Это немного не тоже самое. Пилят под материал дизайн и судя по готовности компонентов... пилить будут еще лет 10 до вменяемого состояния.
alexnozer
01.12.2025 12:35Её, к сожалению, временно забросили, перекинув основную часть команды на другие проекты. Поэтому может лет 10, а может и никогда

alexnozer
01.12.2025 12:35За веб-компоненты однозначно плюс, закинул себе в закладки, чтоб подробнее посмотреть. По поводу автокомплита в браузере, подсказок и прочего советую посмотреть в сторону генерации Custom Elements Manifest и набор инструментов Web Components Toolkit.

Alukos
01.12.2025 12:35По SSR можно уточнить?
Nuxt на сервере развернет в базовый html, клиент сразу отрендрит правильно.
С Web Components, вы пишите, все равно отрендрятся на клиенте, да, но сначала как стандартная строка или блок, а потом согласно js, т.е. страница каждый раз при первой загрузке будет дергаться?
По Declarative Shadow DOM, вроде шаблоны нельзя переиспользовать, нужно будет для каждого экземпляра дублировать?
ZiZIGY Автор
01.12.2025 12:35Тут момент я бы сказал будет как в Nuxt когда сервер рендерит блок а потом происходит hydration на клиенте и компонент оживает. Так как в моих компонентах все стили привязываются к тегу, браузер даже до JS может покрасить этот тег, лишь после того как DOM полностью загрузился там уже мы браузеру даем понять что это кастомный элемент. И Если мы что то внутри него рендерим то сервер это не увидит (именно рендерим HTML в нашем компоненте, а не в слоте) Все что рендерим в слоте сервак увидит и все будет хорошо

Исходный код страницы с табами, все отрендерено на сервере Надеюсь ответил на ваш вопрос)

Alukos
01.12.2025 12:35Я немного другое имел ввиду.
Предложим есть компонент календарь, который должен отображаться не инпутом с текущей датой, а табличкой с днями месяца и выделенным текущим днём.
Кастомный компонент если через свойство передавать текущий день, то вначале вообще ничего не отрендрит, а потом дорисует, а если через слот, то сначала напишет просто дату, а потом перерисует.
Типа Nuxt сразу на сервере html с табличкой с днями месяца сделают.
Хотя, наверное, таких случаев не так много и их действительно можно будет в Declarative Shadow DOM запихать.
Спасибо, за библиотеку и пояснения )

ZiZIGY Автор
01.12.2025 12:35Аааа я понял, да тут будет косяк, как раз есть компонент каледнаря в либе и он управляется со стороны, чтобы разработчик сам кнопки делал которые ему нужны, и вот тут возникает действительно проблема потому что ничего отрендерено не будет(

AleX83Xpert
01.12.2025 12:35Подскажите пожалуйста, что с обновлениями?
Вот такой кейс, например: добавлена в проект кнопка, поправлены стили и/или скрипт. А потом раз, и появилась в вашей библиотеке еще более крутая (функционально или визуально) кнопка и хочется ее к себе.
Как вариант, можно другой префикс сделать, но если в старой кнопке кастомный функционал, придется его руками перетаскивать, плюс править импорты в местах использования. Или есть тут какая-то магия?

ZiZIGY Автор
01.12.2025 12:35CLI все сделает за тебя, он перезапишет весь компонент, я предусмотрел этот момент, а так в целом структура использует 1 файл register

Главный файл в котором все подключается

evgensenin
01.12.2025 12:35лайкнул на гитхабе! хорошее дело делаете!
как будут крупные обновления, напоминайте о себе на хабре, хотелось бы поюзать в проектах, но пока компонентов и тем маловато
ZiZIGY Автор
01.12.2025 12:35Да я понимаю, просто очень уж захотелось релизнутся вдруг кому то уже подойдет табы аккордион, но вот как выше упомянули, табам надо навигацию сделать

cmyser
01.12.2025 12:35Веб компоненты гвоздями прибиты к DOM, что делает их мертворождёнными.
• Хостовой объект, приаттаченый к документу - это крайне медленно. И ЛТ компилятор тут ничем помочь не может.
• Значениями атрибутов могут быть лишь строки - нужны адовые не совместимые между либами костыли для передачи других типов значений.
• Жизненный цикл компонента начинается лишь в момент аттача хостового элемента.
• Перенос хостового элемента приводит к реаттачу всего поддерева компонент.
• Легко словить конфликт имён компонент между разными либами. И механизмов разрешения этого конфликта нет.
• Единожды зарегистрированный компонент уже нельзя удалить.
• В теневом доме отваливаются все стили - их надо копипастить в каждый теневой дом отдельно. Надо ли говорить, что это тормоза на ровном месте?
• Веб компоненты ничего не знают про пулл реактивность. Хочешь что-то им передать пуш по чём зря через атрибуты и слоты.

ZiZIGY Автор
01.12.2025 12:35Ну в целом по фактам, но есть пару НО
Уж больно категоричны вы что вебкомпоненты "мертворожденны"
Современные браузеры очень хорошо оптимизированы для работы с DOM. Проблемы с производительностью возникают при чрезмерно частых операциях, да это не виртуальный DOM а ShadowDOM, но допустим тот же самый input range, там под капотом вебкомпонент по сути основанный на дивах. Так что в каком то плане это стандартная браузерная технология и даже во Vue/React и прочих фреймах всегда будет доля вебкомпонентов, к примеру есть проекты где юзается Swiper в виде вебкомпонента (не самое хорошее решение но оно вполне себе рабочее)
По поводу конфликта имен, ну да легко можно словить если только это как то не стандартизировано, в данном случае проблем быть не должно если сам разраб не захочет себе устроить себе головняк.
По поводу отвал стилей в теневом доме это да, есть такая проблема, только мое решение не использует теневой DOM, там тупо CSS файлик который биндится на тег и проблем тут быть не может. + Используется ::part в стилях для получения доступа к теневым элементам.
А так в целом я с вами согласен) я же описал это в статье что это нишевая тема и в маленьком количестве проектов это пригодится.
Изначально когда делал это все я понимал что это больше не для фронтов решение, а больше для бекендеров которым лень писать фронт копаться в стилях, использовать JS и все такое, да и хранить в голове названия классов своих для кнопок тоже не кайф, а тут все удобно, VSCode подсказывает аттрибуты + есть генератор этой же VSCode даты прямо в CLI.
Вообщем как то так, короче это решение было больше для проектов на Bitrix / Django или что то вроде, ну и решить проблемы с несколькими фреймами в проекте
cmyser
01.12.2025 12:35хотелось бы массовое решение найти, а не нишевое
lit решает еще часть проблем, но корневые проблемы архитектурные не решить так просто

nihil-pro
01.12.2025 12:35Где-то я это уже читал…
А, точно: https://habr.com/ru/articles/968384/comments/#comment_29145688

dlartagnan
01.12.2025 12:35Это просто тролль, который скопировал чужой комментарий и вообще не разбирается в теме.
Проблема про хостовый объект вообще высосна из пальца. Мол каждый элемент имеет внутреннюю реализацию в браузере (хостовый объект), и потому веб-компоненты это плохо. Смешно.
В случае с атрибутами непонятно о каких адовых костылях речь. Но если вам надо передать функцию или массив, то просто передайте это значение через свойство DOM узла. Все современные фреймворки умеют это делать декларативно.
Да, весь жизненный цикл компонента связан с DOM. А с чем он ещё должен быть связан? Если надо сделать что-то до работы с DOM - используйте другие механизмы.
Про перенос элемента - да такой нюанс есть, но это не такая уж частая задача. Также стоимость этой операции не так уж велика на практике. И более того эта проблема уже решена с помощью ConnectedMoveCallback.
Проблема с конфликтами решается путем правильных инженерных практик. И чтобы ее поймать надо вообще не следить за тем, что вы подключаете к проекту. И также уже есть пропосал на кастомные реестры.
Автор вероятно ничего не слышал про adoptedStyleSheets.
Пулл реактивность - непонятно что имеет в виду автор. Комментировать не буду.

cmyser
01.12.2025 12:35Не троль, комент подходит, я с ним согласен, вот и скопировал
Да, весь жизненный цикл компонента связан с DOM. А с чем он ещё должен быть связан?
Не всё нужно отображать
В веб компонентах логика отображения смешивается с обычной логикой работы
Пулл реактивность - непонятно что имеет в виду автор. Комментировать не буду.
В реакте push семантика во Vue pull семантика, это же база
В $mol тоже pull семантика
В случае с атрибутами непонятно о каких адовых костылях речь.
Там только строки, в атрибутах, нет типизация получается, вот это и плохо
Проблема с конфликтами решается путем правильных инженерных практик
Может тогда ci/CD на "правильные" инженерные практики заменим ? Люди же всегда выполняют всё одинаково
adoptedStyleSheets
Есть решение лучше, чем писать стили в js
Тут опять смешение логик получается

supercat1337
01.12.2025 12:35Не всё нужно отображать
Веб-компоненты - это просто кастомные DOM-элементы, поэтому и жизненный цикл начинается с connectedCallback.
В веб компонентах логика отображения смешивается с обычной логикой работы
Вот как раз наоборот, веб-компоненты заточены только под отображение и взаимодействие с пользователем через шину событий. Логика, как и при работе с классическими элементами, обычно выносится отдельно.
Там только строки, в атрибутах, нет типизация получается, вот это и плохо
Если работать только через атрибуты, то, конечно, будут строки. И это потому что разметка страницы - это текст. Если же идет речь о том, как передать объект компоненту для его обработки, то пропишите соответствующий метод или свойство в классе веб-компонента, и вызывайте потом в коде.
adoptedStyleSheets - Есть решение лучше, чем писать стили в js
adoptedStyleSheets используется для подключения текстового содержимого в качестве css-таблицы в тень элемента. Причем это делается в connectedCallback. Само же содержимое стиля может быть вынесено в отдельный esm-модуль. Так что смешения тут никакого нет. Да и логика существования самого веб-компонента не нарушена. Потому что веб-компонент - это все-таки про вьюху.

ZiZIGY Автор
01.12.2025 12:35В любом случае все правы по своему, проблема остается, веб компоненты это все таки справедливости ради не Vue.js( это довольно нишевая тема, но на каких нибудь проектах Django / Bitrix, в целом очень даже солидно могут помочь)

dlartagnan
01.12.2025 12:35Я уверен, что автор предыдущего комментария не разбирается в теме вообще) Типизация поддерживается с помощью плагина для TS - lit-analyzer.
Стили писать на Js не надо, вы можете загрузить CSS файл через import with { type: CSS }, и подключить его в adoptedStyleSheets. Ну, либо использовать сборщик.
Опять же аргумент про конфликты имен очень слабый и автор пытается его защищать возводя в абсурд.
Про push/pull семантики мне по прежнему непонятно, что имеется в виду. В документации упомянутых Фреймворков такого не видел. Но опять же автор считает, что раскрывать свои мысли для смердов - выше его достоинства. Ему удобно кидаться умными абстрактными утверждениями.

cmyser
01.12.2025 12:35Типизация поддерживается с помощью плагина для TS - lit-analyzer.
Там строки парсятся, по сути дублируется работа самого компилятора ts
Для стилей есть три варианта, и у каждого есть недостатки, подробнее тут https://piterjs.org/#!meetup=tx5uqh_fox5zo/speech=62hjim_ogoike
Семантики реактивного програмирования есть разные, это база
Push - мы сами толкаем изменения и пишем для этого код
Pull объекты сами знают об измениях состояния и сами передают туда, куда нужно
Подробнее тут https://habr.com/ru/companies/timeweb/articles/586450/
И не надо в высокомерии меня обвинять, пожалуйста

dlartagnan
01.12.2025 12:35Вы сделали хоть один проект на веб-компонентах? Мы в команде сделали. И никаких проблем со стилями там нет, все прекрасно работает. Есть нюансы, их просто надо учитывать.
Теперь насчет lit-analyzer, да, там парсится строка. Сам TS не парсит содержимое строк, и какая разница? Главное, что типы выводятся.

cmyser
01.12.2025 12:35Пишу на $mol, там нет таких ньюансов которые нужно учитывать
В мол каждый компонент можно считать веб компонентом, реактивным
Выводить типы из строк - странно, звучит костыльно, вряд-ли оптимизируется jit

dlartagnan
01.12.2025 12:35При чем тут jit и вывод типов? Типов в runtime нет.
Доклад с piter.js, посмотрел. Доклад поверхностный. Докладчик в теме плавает, например, на вопрос про CSS-переменные и Shadow DOM ответил неправильно и абсолютно запутался в датах, когда были ключевые изменения по стандарту. Лучше посмотрите https://youtu.be/fEhBkSZ15qM
А как у вас в $mol выводятся типы в шаблоне?

cmyser
01.12.2025 12:35Посмотрел видос, в целом понятно почему для веб компонент сделан shadow Dom и отдельный css
Всё же считаю что лучше глобальный css
В $mol нет шаблонов в привычном понимании, есть отдельный DSL
*view.tree
$demo_counter $mol_button title <= count \ clicks click? <=> increment?Сборщик превращает его в
export class
mol_button {
// то, что из шаблона: title(): string { ... } // <= count \ clicks click(next?: Event): Event | null { ... } // click? <=> increment? // плюс декларации типов в *.view.d.ts, // где всё склеено с базовым классом}
Плюс типы для TS выводит
DSL поддерживает строки
Типизированные и обычные списки
Числа, булеаны, null, ?, ! И что то ещё, кажется
Подробно и кратко тут https://habr.com/ru/articles/724884/

dlartagnan
01.12.2025 12:35Нет лучше или хуже. Зависит от задачи. Самое главное, что даёт Shadow DOM с точки зрения DX - это строгий API для стилизации компонента: part, CSS переменные и слоты.
То есть ваш DSL не определяет какой тип ожидается для свойства? Проще говоря в момент когда я буду печатать в IDE у меня будет работать автокомплит, если например свойство является enum? Ну, или хотя бы будет IDE подсвечивать ошибку, если я неправильно значение в шаблоне указал?
И еще, а как мне передать функцию, если ваш DSL поддерживает только строки и списки? Вы же критиковали веб-компоненты за это же)

cmyser
01.12.2025 12:35Есть расширения для vscode, zed
Есть tree sitter, Lsp
Можно и для jetbrains написать на основе lsp
Функции передавать не нужно, методы переопределяем в *.TS файлах
Поддерживаются не только списки и строки

dlartagnan
01.12.2025 12:35Ну, вот хочется мне передать анонимную функцию в свойство.
Но хорошо допустим у меня есть метод, и есть событие которое испускает компонент.
Будет ли проверка, что аргумент метода совпадает по типу с испускаемым событием?

cmyser
01.12.2025 12:35можно написать эту функцию у компонента, расширив его, без проблем
если хочется вызвать метод у другого класса, через пропс прокидываем компонент и в .ts вызываем метод через обращение к пропсу
по поводу проверки, не знаю если честно, надо посмотреть какие типы будут
допустим такой код
$my_app $mol_page
sub /
<= Add $mol_button_minor
click? <=> add_submit? null
получит такие типыtype $mol_button_minor__click_my_app_1 = $mol_type_enforce< ReturnType< $my_app['add_submit'] > , ReturnType< $mol_button_minor['click'] > > export class $my_app extends $mol_page { add_submit( next?: any ): any Add( ): $mol_button_minor sub( ): readonly(any)[] }и такой js($.$my_app) = class $my_app extends ($.$mol_page) { add_submit(next){ if(next !== undefined) return next; return null; } Add(){ const obj = new this.$.$mol_button_minor(); (obj.click) = (next) => ((this.add_submit(next))); return obj; } sub(){ return [(this.Add())]; } }; ($mol_mem(($.$my_app.prototype), "add_submit")); ($mol_mem(($.$my_app.prototype), "Add"));
тут нету вроде как, но можно руками переопределить в view.tsadd_submit(next){ if(next !== undefined) return next; return null; }
и прописать типы

cmyser
01.12.2025 12:35view.tree специально спроектирован не тьюринг полным, вся логика в *.ts
cмешения нет
дерево компонент + логика + стили

DmitryOlkhovoi
01.12.2025 12:35В случае если Shadow DOM не работает, это работает на Shady DOM? или какие там сейчас фолбеки?

ZiZIGY Автор
01.12.2025 12:35Вообще сейчас Shadow DOM поддерживается всеми браузерами кроме IE, и Shady DOM не используется в библиотеке
(Вообще это не точная информация, может быть Lit под капотом использует)
DmitryOlkhovoi
01.12.2025 12:35Ну тогда (polymer) тоже оно всеми уже поддерживалось, но у клиентки был ноут со старой mac os, потому, что она боялась обновиться, что бы не слетел пиратский фотошоп))) Так что работает во всех новых факт, но стоит учитывать, что даже если 1% пользователей отвалится, в деньгах это может быть большой кусок)

dlartagnan
01.12.2025 12:35Lit не использует полифиллы из коробки. Если нужна поддержка в легаси браузерах, то нужно использовать Lit2 и самому подключить полифилл.

Hemml
01.12.2025 12:35Отличный проект, спасибо!
Есть проблемы с цветами текста на кнопках:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:109.0) Gecko/20100101 Firefox/115.0
В хроме такая же фигня (Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36)
Понимаю, не самые свежие браузеры, но это максимум, что можно установить на этот макос)

ZiZIGY Автор
01.12.2025 12:35скорее всего проблема в том что используется свежая реализация светлой темы и темной темы для CSS, light-dark, возможно она не поддерживается, спасибо за комментарий я посмотрю в чем проблема

daniilba
01.12.2025 12:35Сильно смело использовать свежие свойства и забыть посмотреть насколько они поддерживаются в популярных браузерах. Лучше рассмотреть возможность дописать фоллбеки

ZiZIGY Автор
01.12.2025 12:35Не знаю как бы сказать но я посмотрел на свойства и как они поддерживаются

Просто не думал что кто то еще сидит на старых браузерах, опять же 115 firefox не такой уж и старый если сравниваем IE, но все таки, я думаю можно вполне использовать, да и переписать стили с light-dark на :root.dark, :root.light вообще не так уж и сложно учитывая что все переменные в одном файле
ionicman
Жди - щас сюда ворвется главный евангелист и псевдосеньор гно-молла и начнет кричать что вот мол - это мол хорошо, а это, мол, все плохо :D
Хотя, исходя из парадигмы, это должно работать и в его поделии.
Ну и не забарсывайте это дело - вещь шужная и полезная в текущем зоопарке фреймворков.
ZiZIGY Автор
Уже жду) В любой критике есть доля смысла, а так спасибо за напутствие
dominus_augustus
Он сейчас в другом треде со мной воюет, дима не придет
c_kotik
Держите оборону! Мы с вами!
dominus_augustus
Да там не сложно, дешевые набросы и сведение все к тому, что гения никто не понял, вон он 10 лет говно пилит)
tamazyanarsen
Наткнулся на подобный комментарий от него, даже не стал с ним спорить. Это же бессмысленно)
Кстати, тема статьи была чем-то похожа, описывал свою либу для создания веб-компонентов (аналог Lit).
А критика была в том, что веб-компоненты это очень плохо, и не стоит их вообще использовать, но аргументы были пустые и устаревшие)