diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/bun.lock b/bun.lock old mode 100644 new mode 100755 diff --git a/components.json b/components.json old mode 100644 new mode 100755 diff --git a/eslint.config.mjs b/eslint.config.mjs old mode 100644 new mode 100755 diff --git a/examples/websocket/frontend.tsx b/examples/websocket/frontend.tsx old mode 100644 new mode 100755 diff --git a/examples/websocket/server.ts b/examples/websocket/server.ts old mode 100644 new mode 100755 diff --git a/next.config.ts b/next.config.ts old mode 100644 new mode 100755 diff --git a/package.json b/package.json old mode 100644 new mode 100755 diff --git a/postcss.config.mjs b/postcss.config.mjs old mode 100644 new mode 100755 diff --git a/prisma/schema.prisma b/prisma/schema.prisma old mode 100644 new mode 100755 diff --git a/public/dssl/boot.js b/public/dssl/boot.js old mode 100644 new mode 100755 diff --git a/public/dssl/engine.js b/public/dssl/engine.js old mode 100644 new mode 100755 diff --git a/public/dssl/index.js b/public/dssl/index.js old mode 100644 new mode 100755 diff --git a/public/dssl/parser.js b/public/dssl/parser.js old mode 100644 new mode 100755 diff --git a/public/dssl/resolver.js b/public/dssl/resolver.js old mode 100644 new mode 100755 diff --git a/public/dssl/utils.js b/public/dssl/utils.js old mode 100644 new mode 100755 diff --git a/public/favicon.svg b/public/favicon.svg old mode 100644 new mode 100755 diff --git a/public/logo.svg b/public/logo.svg old mode 100644 new mode 100755 diff --git a/public/robots.txt b/public/robots.txt old mode 100644 new mode 100755 diff --git a/public/styles/changelog.dssl b/public/styles/changelog.dssl old mode 100644 new mode 100755 diff --git a/public/styles/docs.dssl b/public/styles/docs.dssl old mode 100644 new mode 100755 diff --git a/public/styles/home.dssl b/public/styles/home.dssl old mode 100644 new mode 100755 diff --git a/public/styles/playground.dssl b/public/styles/playground.dssl old mode 100644 new mode 100755 diff --git a/public/styles/site.dssl b/public/styles/site.dssl old mode 100644 new mode 100755 diff --git a/public/styles/tokens.dssl b/public/styles/tokens.dssl old mode 100644 new mode 100755 diff --git a/scripts/dssl-debug-grid.mjs b/scripts/dssl-debug-grid.mjs old mode 100644 new mode 100755 diff --git a/scripts/dssl-smoke.mjs b/scripts/dssl-smoke.mjs old mode 100644 new mode 100755 diff --git a/scripts/dssl-styles-check.mjs b/scripts/dssl-styles-check.mjs old mode 100644 new mode 100755 diff --git a/src/app/api/route.ts b/src/app/api/route.ts old mode 100644 new mode 100755 diff --git a/src/app/globals.css b/src/app/globals.css old mode 100644 new mode 100755 diff --git a/src/app/layout.tsx b/src/app/layout.tsx old mode 100644 new mode 100755 index bc327198fe1f97e85711491d44c1b453fe60e29f..a35c3a6f2e236db6253c1d4e505ca012eb6c4ce0 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -46,27 +46,46 @@ function dsslInlineStyles(): { name: string; src: string }[] { // Dev-only interop shim: the engine styles the SSR DOM BEFORE React hydrates // (that is the whole point — no FOUC). React 19 then logs one console.error -// about extra style attributes and, helpfully, does not patch them. On our own -// site that reads as "1 Issue" in the dev overlay. This shim filters ONLY the -// hydration notices whose diff references DSSL's var(--dssl-*) attributes and -// re-tells them as an explained info line; any genuine mismatch still shows. +// about extra style attributes and, helpfully, does not patch them. Next's +// dev overlay ALSO patches console.error (one layer around everything) and +// counts every error it sees into the "Issues" badge — so a plain filter +// installed here would sit INSIDE Next's collector and the badge would still +// show "1". This shim instead installs an assignment trap: the moment Next +// assigns its own wrapper, we capture it and re-publish OUR filter as the +// outermost layer. DSSL's own hydration diff is swallowed + explained; every +// other console.error still reaches Next's collector untouched. const hydrationShim = ` (function () { - var HYD = 'A tree hydrated but some attributes of the server rendered HTML'; - var orig = console.error; - console.error = function () { + var HYD = 'A tree hydrated but some attributes'; + var MARK = '--dssl'; + var inner = console.error; // native, until Next assigns its wrapper + var trapped = false; + function outer() { var s = ''; for (var i = 0; i < arguments.length; i++) { var a = arguments[i]; - s += (typeof a === 'string' ? a : ' '); + s += (i ? ' ' : '') + (typeof a === 'string' ? a : (a && a.message ? a.message : '')); } - if (s.indexOf(HYD) === 0 && s.indexOf('--dssl') !== -1) { + if (s.indexOf(HYD) !== -1 && s.indexOf(MARK) !== -1) { console.info('%cDSSL%c styled this page before React hydrated. The "hydrated but some attributes didn\\u2019t match" notice is expected interop: the engine owns those style attributes, and React explicitly leaves them alone.', 'background:#b3372e;color:#fff;padding:1px 6px;border-radius:3px;font-weight:700', ''); return; } - return orig.apply(console, arguments); - }; + return inner.apply(console, arguments); + } + try { + var desc = Object.getOwnPropertyDescriptor(console, 'error'); + if (!desc || !desc.configurable) throw new Error('not configurable'); + Object.defineProperty(console, 'error', { + configurable: true, + get: function () { return trapped ? outer : inner; }, + set: function (fn) { inner = fn; trapped = true; } + }); + } catch (e) { + // fallback: plain filter — console noise is still explained away, + // though the dev-overlay badge may still count the notice. + console.error = outer; + } })(); `; diff --git a/src/app/page.tsx b/src/app/page.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/CodeBlock.tsx b/src/components/dssl/CodeBlock.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/SiteRoot.tsx b/src/components/dssl/SiteRoot.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/highlight.ts b/src/components/dssl/highlight.ts old mode 100644 new mode 100755 diff --git a/src/components/dssl/icons.tsx b/src/components/dssl/icons.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/views/ChangelogView.tsx b/src/components/dssl/views/ChangelogView.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/views/CheatSheetView.tsx b/src/components/dssl/views/CheatSheetView.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/views/DocsView.tsx b/src/components/dssl/views/DocsView.tsx old mode 100644 new mode 100755 index 8a04bbf844a4f579bb9c09d240d275e3747aa49a..7b873fc8a66673772cf286a268054e4308e72c81 --- a/src/components/dssl/views/DocsView.tsx +++ b/src/components/dssl/views/DocsView.tsx @@ -17,6 +17,7 @@ const SIDE = [ { head: null, id: "motion", label: "Motion" }, { head: null, id: "errors", label: "Errors" }, { head: null, id: "api", label: "JS API" }, + { head: null, id: "interop", label: "Interop (React & co.)" }, ]; export default function DocsView() { @@ -443,6 +444,63 @@ button { use focus-ring } <html data-dssl-reset> enables the small built-in reset.

+ +
+

Interop — React, hydration & friends

+

+ DSSL is deliberately framework-agnostic: it styles whatever DOM exists, + so it works under React, Vue, Svelte or plain HTML. But if you render + with React there is one known, harmless notice you should + understand — and this very site is the living example. +

+
+ Why the notice happens. The engine applies styles to the SSR DOM + before React hydrates — that is the whole anti-flash design. React + then compares the DOM against its virtual render, finds extra + style="--dssl-*…" attributes it did not put there, and logs + one hydration diff to the console. React explicitly does not patch + them away — it leaves the engine's styles alone. Nothing is wrong: + the styles are working exactly as intended. +
+

+ In dev builds that single notice can surface as a "1 Issue" overlay badge, + because Next's dev tools wrap console.error and count + everything they see. This site installs an assignment trap: the moment + Next assigns its own collector, the shim captures it and re-publishes the + filter as the outermost layer — so DSSL's own diff is swallowed + and re-logged as an explained info line before Next can count it. + Any genuine hydration mismatch still flows through to the collector at full + volume. In production React does not diff attributes at all, so the notice + disappears entirely; the shim is dev-only. +

+ (trapped ? outer : inner), + set: (fn) => { inner = fn; trapped = true; } +}); +function outer(...args) { + const s = args.map(String).join(' '); + // swallow ONLY the DSSL-attribute hydration diff + if (s.includes('A tree hydrated but some attributes') + && s.includes('--dssl')) { + console.info('DSSL styled this page before React hydrated. Expected interop.'); + return; + } + return inner.apply(console, args); // genuine issues flow through +}`} /> +

+ Two more interop notes worth knowing. First, if a framework re-renders a + subtree and removes elements the engine had styled, the touched + registry notices the removal via MutationObserver and cleans up — no + leaked inline styles. Second, if your framework toggles attributes the + engine matches on (like data-dssl-* signal mirrors), change + them through DSSL.signal() rather than DOM writes, so rules + re-evaluate on the same tick instead of waiting for the observer. +

+
diff --git a/src/components/dssl/views/HomeView.tsx b/src/components/dssl/views/HomeView.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/views/PlaygroundView.tsx b/src/components/dssl/views/PlaygroundView.tsx old mode 100644 new mode 100755 diff --git a/src/components/dssl/views/SpecView.tsx b/src/components/dssl/views/SpecView.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/aspect-ratio.tsx b/src/components/ui/aspect-ratio.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/carousel.tsx b/src/components/ui/carousel.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/collapsible.tsx b/src/components/ui/collapsible.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/context-menu.tsx b/src/components/ui/context-menu.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/drawer.tsx b/src/components/ui/drawer.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/hover-card.tsx b/src/components/ui/hover-card.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/input-otp.tsx b/src/components/ui/input-otp.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/menubar.tsx b/src/components/ui/menubar.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/navigation-menu.tsx b/src/components/ui/navigation-menu.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/progress.tsx b/src/components/ui/progress.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/radio-group.tsx b/src/components/ui/radio-group.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/resizable.tsx b/src/components/ui/resizable.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/slider.tsx b/src/components/ui/slider.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/toaster.tsx b/src/components/ui/toaster.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/toggle-group.tsx b/src/components/ui/toggle-group.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx old mode 100644 new mode 100755 diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx old mode 100644 new mode 100755 diff --git a/src/hooks/use-mobile.ts b/src/hooks/use-mobile.ts old mode 100644 new mode 100755 diff --git a/src/hooks/use-toast.ts b/src/hooks/use-toast.ts old mode 100644 new mode 100755 diff --git a/src/lib/db.ts b/src/lib/db.ts old mode 100644 new mode 100755 diff --git a/src/lib/utils.ts b/src/lib/utils.ts old mode 100644 new mode 100755 diff --git a/tailwind.config.ts b/tailwind.config.ts old mode 100644 new mode 100755 diff --git a/tsconfig.json b/tsconfig.json old mode 100644 new mode 100755