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.
+ 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. +
+ 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.
+
+ 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.
+