@webto-id/variant-check 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +147 -7
  2. package/dist/index.js +147 -7
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -917,6 +917,95 @@ function escAttr(s) {
917
917
  }
918
918
  __name(escAttr, "escAttr");
919
919
 
920
+ // ../variant-compiler/src/effects.ts
921
+ var WV_EFFECTS = [
922
+ "reveal-up",
923
+ "reveal-down",
924
+ "reveal-left",
925
+ "reveal-right",
926
+ "fade",
927
+ "stagger",
928
+ "counter",
929
+ "parallax-soft",
930
+ "zoom-hover"
931
+ ];
932
+ var WV_DELAY_MAX = 10;
933
+ var WV_DURATION_MIN = 100;
934
+ var WV_DURATION_MAX = 5e3;
935
+ var WV_STRENGTHS = ["soft", "medium"];
936
+ var WV_EFFECT_CSS = `@media (prefers-reduced-motion: no-preference){
937
+ .wv-js [data-wv-effect^="reveal-"],.wv-js [data-wv-effect="fade"],.wv-js [data-wv-effect="stagger"]>*{opacity:0;transition:opacity .6s cubic-bezier(.22,.61,.36,1),transform .6s cubic-bezier(.22,.61,.36,1)}
938
+ .wv-js [data-wv-effect="reveal-up"]{transform:translateY(22px)}
939
+ .wv-js [data-wv-effect="reveal-down"]{transform:translateY(-22px)}
940
+ .wv-js [data-wv-effect="reveal-left"]{transform:translateX(-22px)}
941
+ .wv-js [data-wv-effect="reveal-right"]{transform:translateX(22px)}
942
+ .wv-js [data-wv-effect="stagger"]>*{transform:translateY(16px)}
943
+ .wv-js [data-wv-effect].wv-in,.wv-js [data-wv-effect="stagger"]>.wv-in{opacity:1;transform:none}
944
+ [data-wv-effect="zoom-hover"] img{transition:transform .5s ease}
945
+ [data-wv-effect="zoom-hover"]:hover img{transform:scale(1.03)}
946
+ [data-wv-effect="parallax-soft"]{will-change:transform}
947
+ }`;
948
+ var WV_EFFECT_JS = `(function(){
949
+ if(!('IntersectionObserver' in window))return;
950
+ var els=document.querySelectorAll('[data-wv-effect]');
951
+ if(!els.length)return;
952
+ if(window.matchMedia('(prefers-reduced-motion: reduce)').matches)return;
953
+ document.documentElement.classList.add('wv-js');
954
+ function num(el,attr,def){var v=parseInt(el.getAttribute(attr)||'',10);return isFinite(v)?v:def;}
955
+ function timing(el){
956
+ var d=num(el,'data-wv-delay',0);if(d>0)el.style.transitionDelay=(d*100)+'ms';
957
+ var ms=num(el,'data-wv-duration',0);if(ms>=100)el.style.transitionDuration=ms+'ms';
958
+ }
959
+ function counter(el){
960
+ var txt=el.textContent||'';var m=txt.match(/-?\\d[\\d.,]*/);if(!m)return;
961
+ var target=parseFloat(m[0].replace(/[.,](?=\\d{3}(\\D|$))/g,''));if(!isFinite(target))return;
962
+ var pre=txt.slice(0,m.index),post=txt.slice(m.index+m[0].length);
963
+ var dur=Math.min(Math.max(num(el,'data-wv-duration',1200),100),5000),t0=null;
964
+ function tick(t){
965
+ if(t0===null)t0=t;
966
+ var p=Math.min((t-t0)/dur,1),eased=1-Math.pow(1-p,3);
967
+ el.textContent=pre+Math.round(target*eased)+post;
968
+ if(p<1)requestAnimationFrame(tick);else el.textContent=txt;
969
+ }
970
+ requestAnimationFrame(tick);
971
+ }
972
+ var para=[];
973
+ function onScroll(){
974
+ for(var i=0;i<para.length;i++){
975
+ var el=para[i],r=el.getBoundingClientRect();
976
+ var vh=window.innerHeight||1;
977
+ var f=el.getAttribute('data-wv-strength')==='medium'?0.1:0.06;
978
+ var y=((r.top+r.height/2)-vh/2)*-f;
979
+ if(y>48)y=48;if(y<-48)y=-48;
980
+ el.style.transform='translateY('+y.toFixed(1)+'px)';
981
+ }
982
+ }
983
+ var ticking=false;
984
+ function onScrollRaf(){if(ticking)return;ticking=true;requestAnimationFrame(function(){ticking=false;onScroll();});}
985
+ var io=new IntersectionObserver(function(entries){
986
+ for(var i=0;i<entries.length;i++){
987
+ var en=entries[i];if(!en.isIntersecting)continue;
988
+ var el=en.target;io.unobserve(el);
989
+ var fx=el.getAttribute('data-wv-effect');
990
+ if(fx==='counter'){counter(el);continue;}
991
+ if(fx==='stagger'){
992
+ var step=Math.max(num(el,'data-wv-delay',1),1)*100,kids=el.children;
993
+ for(var k=0;k<kids.length;k++){kids[k].style.transitionDelay=(k*step)+'ms';kids[k].classList.add('wv-in');}
994
+ continue;
995
+ }
996
+ el.classList.add('wv-in');
997
+ }
998
+ },{threshold:0.15,rootMargin:'0px 0px -5% 0px'});
999
+ for(var i=0;i<els.length;i++){
1000
+ var el=els[i],fx=el.getAttribute('data-wv-effect');
1001
+ if(fx==='zoom-hover')continue;
1002
+ if(fx==='parallax-soft'){para.push(el);continue;}
1003
+ timing(el);
1004
+ io.observe(el);
1005
+ }
1006
+ if(para.length){window.addEventListener('scroll',onScrollRaf,{passive:true});window.addEventListener('resize',onScrollRaf,{passive:true});onScroll();}
1007
+ })();`;
1008
+
920
1009
  // ../variant-compiler/src/lint/html.ts
921
1010
  var ALLOWED_TAGS = /* @__PURE__ */ new Set([
922
1011
  "a",
@@ -1169,12 +1258,39 @@ function lintNodes(nodes, ctx, ariaHidden = false) {
1169
1258
  }
1170
1259
  }
1171
1260
  if ("id" in n.attrs) push(ctx, "warning", "id-attr", `<${tag} id> \u2014 the editor's click walker stops at ids; prefer a data-* attribute`);
1261
+ lintEffectAttrs(n.attrs, tag, ctx);
1172
1262
  if (tag === "svg") ctx.inSvg++;
1173
1263
  lintNodes(n.ch, ctx, hidden);
1174
1264
  if (tag === "svg") ctx.inSvg--;
1175
1265
  }
1176
1266
  }
1177
1267
  __name(lintNodes, "lintNodes");
1268
+ function lintEffectAttrs(attrs, tag, ctx) {
1269
+ const fx = "data-wv-effect" in attrs ? staticValue(attrs["data-wv-effect"]) : void 0;
1270
+ if ("data-wv-effect" in attrs) {
1271
+ if (fx !== null && fx !== void 0 && !WV_EFFECTS.includes(fx)) {
1272
+ push(ctx, "error", "effect-unknown", `data-wv-effect="${fx}" is not a platform effect \u2014 one of: ${WV_EFFECTS.join(", ")}`);
1273
+ }
1274
+ if ("data-edit-field" in attrs || "data-edit-image" in attrs) {
1275
+ push(ctx, "warning", "effect-on-editable", `data-wv-effect on an editable element (<${tag}>) can interfere with inline editing \u2014 prefer the effect on a wrapper`);
1276
+ }
1277
+ } else if ("data-wv-delay" in attrs || "data-wv-duration" in attrs || "data-wv-strength" in attrs) {
1278
+ push(ctx, "error", "effect-param", `data-wv-delay/duration/strength on <${tag}> without data-wv-effect does nothing`);
1279
+ }
1280
+ const delay = staticValue(attrs["data-wv-delay"]);
1281
+ if (delay !== null && delay !== void 0 && !(/^\d+$/.test(delay) && Number(delay) <= WV_DELAY_MAX)) {
1282
+ push(ctx, "error", "effect-param", `data-wv-delay="${delay}" \u2014 whole steps of 100ms, 0..${WV_DELAY_MAX}`);
1283
+ }
1284
+ const dur = staticValue(attrs["data-wv-duration"]);
1285
+ if (dur !== null && dur !== void 0 && !(/^\d+$/.test(dur) && Number(dur) >= WV_DURATION_MIN && Number(dur) <= WV_DURATION_MAX)) {
1286
+ push(ctx, "error", "effect-param", `data-wv-duration="${dur}" \u2014 milliseconds, ${WV_DURATION_MIN}..${WV_DURATION_MAX}`);
1287
+ }
1288
+ const strength = staticValue(attrs["data-wv-strength"]);
1289
+ if (strength !== null && strength !== void 0 && !WV_STRENGTHS.includes(strength)) {
1290
+ push(ctx, "error", "effect-param", `data-wv-strength="${strength}" \u2014 one of: ${WV_STRENGTHS.join(", ")}`);
1291
+ }
1292
+ }
1293
+ __name(lintEffectAttrs, "lintEffectAttrs");
1178
1294
  function lintAttr(name, v, tag, ctx) {
1179
1295
  const lower = name.toLowerCase();
1180
1296
  if (/^on[a-z]/.test(lower)) {
@@ -2542,25 +2658,49 @@ __name(wrapScript, "wrapScript");
2542
2658
 
2543
2659
  // ../variant-compiler/src/preview.ts
2544
2660
  var PREVIEW_THEMES = {
2545
- light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", accent: "#f59e0b", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "8px" },
2546
- dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", accent: "#fbbf24", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "12px" },
2547
- warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", accent: "#d99a3f", heading: "Georgia, serif", body: "Inter, sans-serif", radius: "2px" }
2661
+ light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", secondaryFg: "#ffffff", accent: "#e8eefb", accentFg: "#1e293b", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "8px" },
2662
+ dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", secondaryFg: "#0b1220", accent: "#1d2a47", accentFg: "#e5e7eb", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "12px" },
2663
+ warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", secondaryFg: "#ffffff", accent: "#f1dfc0", accentFg: "#2b1d12", heading: "Georgia, serif", body: "Inter, sans-serif", radius: "2px" }
2548
2664
  };
2549
2665
  function isPreviewThemeKey(v) {
2550
2666
  return typeof v === "string" && v in PREVIEW_THEMES;
2551
2667
  }
2552
2668
  __name(isPreviewThemeKey, "isPreviewThemeKey");
2553
2669
  function buildPreviewDoc(ir, content, themeKey, opts = {}) {
2554
- const th = PREVIEW_THEMES[themeKey];
2670
+ const base2 = PREVIEW_THEMES[themeKey];
2671
+ const o = opts.themeOverride ?? {};
2672
+ const th = {
2673
+ background: o.background ?? base2.background,
2674
+ foreground: o.foreground ?? base2.foreground,
2675
+ card: o.card ?? base2.card,
2676
+ muted: o.muted ?? base2.muted,
2677
+ mutedFg: o.mutedFg ?? base2.mutedFg,
2678
+ border: o.border ?? base2.border,
2679
+ primary: o.primary ?? base2.primary,
2680
+ primaryFg: o.primaryFg ?? base2.primaryFg,
2681
+ secondary: o.secondary ?? base2.secondary,
2682
+ secondaryFg: o.secondaryFg ?? base2.secondaryFg,
2683
+ accent: o.accent ?? base2.accent,
2684
+ accentFg: o.accentFg ?? base2.accentFg,
2685
+ heading: o.heading ?? base2.heading,
2686
+ body: o.body ?? base2.body,
2687
+ radius: o.radius ?? base2.radius
2688
+ };
2689
+ const headingSize = o.headingSize ?? "48px";
2690
+ const bodySize = o.bodySize ?? "16px";
2691
+ const gradientFrom = o.gradientFrom ?? th.primary;
2692
+ const gradientTo = o.gradientTo ?? th.secondary;
2693
+ const isDark = o.dark ?? themeKey === "dark";
2694
+ const fontLinks = (o.fontLinks ?? []).filter((h) => /^https:\/\/fonts\.googleapis\.com\//.test(h)).map((h) => `<link rel="stylesheet" href="${h.replace(/"/g, "&quot;")}">`).join("");
2555
2695
  const out = renderVariant(ir, content, { t: /* @__PURE__ */ __name((k) => k, "t"), editChrome: opts.editChrome ?? false }, { uid: "wv-preview" });
2556
- const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground:#fff;--muted:${th.muted};--muted-foreground:${th.mutedFg};--accent:${th.accent};--accent-foreground:#000;--border:${th.border};--input:${th.border};--ring:${th.primary};--radius:${th.radius};--font-heading:${th.heading};--font-body:${th.body};--gradient-from:${th.primary};--gradient-to:${th.secondary};--spacing:.25rem;--site-max-width:72rem;--font-heading-size:48px;--font-body-size:16px;`;
2696
+ const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground:${th.secondaryFg};--muted:${th.muted};--muted-foreground:${th.mutedFg};--accent:${th.accent};--accent-foreground:${th.accentFg};--border:${th.border};--input:${th.border};--ring:${th.primary};--radius:${th.radius};--font-heading:${th.heading};--font-body:${th.body};--gradient-from:${gradientFrom};--gradient-to:${gradientTo};--spacing:.25rem;--site-max-width:72rem;--font-heading-size:${headingSize};--font-body-size:${bodySize};`;
2557
2697
  const tokens = `--color-background:var(--background);--color-foreground:var(--foreground);--color-card:var(--card);--color-card-foreground:var(--card-foreground);--color-primary:var(--primary);--color-primary-foreground:var(--primary-foreground);--color-secondary:var(--secondary);--color-secondary-foreground:var(--secondary-foreground);--color-muted:var(--muted);--color-muted-foreground:var(--muted-foreground);--color-accent:var(--accent);--color-accent-foreground:var(--accent-foreground);--color-border:var(--border);--color-input:var(--input);--color-ring:var(--ring);--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);`;
2558
- return `<!doctype html><html data-theme="${themeKey === "dark" ? "dark" : "light"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex">
2698
+ return `<!doctype html><html data-theme="${isDark ? "dark" : "light"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex">${fontLinks}
2559
2699
  <style>@layer wvfbase{*,::before,::after{box-sizing:border-box;border:0 solid var(--color-border)}html{font-size:var(--font-body-size,16px);-webkit-text-size-adjust:100%}body{margin:0;font-family:var(--font-body);color:var(--foreground);background:var(--background);line-height:1.5}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}h1,h2,h3,h4,p{margin:0}img,video,svg{display:block;max-width:100%;height:auto}a{color:inherit;text-decoration:inherit}button{font:inherit;color:inherit;background:none}ul,ol{margin:0;padding:0;list-style:none}}
2560
2700
  :root{${vars}${tokens}}
2561
2701
  h1{font-size:var(--font-heading-size,48px)}h2{font-size:calc(var(--font-heading-size,48px) * 0.75)}h3{font-size:calc(var(--font-heading-size,48px) * 0.6)}h4{font-size:calc(var(--font-heading-size,48px) * 0.5)}
2562
2702
  .edit-url-pill{display:none!important}</style>
2563
- <style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}</body></html>`;
2703
+ <style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}<style>${WV_EFFECT_CSS}</style><script>${WV_EFFECT_JS}</script></body></html>`;
2564
2704
  }
2565
2705
  __name(buildPreviewDoc, "buildPreviewDoc");
2566
2706
 
package/dist/index.js CHANGED
@@ -914,6 +914,95 @@ function escAttr(s) {
914
914
  }
915
915
  __name(escAttr, "escAttr");
916
916
 
917
+ // ../variant-compiler/src/effects.ts
918
+ var WV_EFFECTS = [
919
+ "reveal-up",
920
+ "reveal-down",
921
+ "reveal-left",
922
+ "reveal-right",
923
+ "fade",
924
+ "stagger",
925
+ "counter",
926
+ "parallax-soft",
927
+ "zoom-hover"
928
+ ];
929
+ var WV_DELAY_MAX = 10;
930
+ var WV_DURATION_MIN = 100;
931
+ var WV_DURATION_MAX = 5e3;
932
+ var WV_STRENGTHS = ["soft", "medium"];
933
+ var WV_EFFECT_CSS = `@media (prefers-reduced-motion: no-preference){
934
+ .wv-js [data-wv-effect^="reveal-"],.wv-js [data-wv-effect="fade"],.wv-js [data-wv-effect="stagger"]>*{opacity:0;transition:opacity .6s cubic-bezier(.22,.61,.36,1),transform .6s cubic-bezier(.22,.61,.36,1)}
935
+ .wv-js [data-wv-effect="reveal-up"]{transform:translateY(22px)}
936
+ .wv-js [data-wv-effect="reveal-down"]{transform:translateY(-22px)}
937
+ .wv-js [data-wv-effect="reveal-left"]{transform:translateX(-22px)}
938
+ .wv-js [data-wv-effect="reveal-right"]{transform:translateX(22px)}
939
+ .wv-js [data-wv-effect="stagger"]>*{transform:translateY(16px)}
940
+ .wv-js [data-wv-effect].wv-in,.wv-js [data-wv-effect="stagger"]>.wv-in{opacity:1;transform:none}
941
+ [data-wv-effect="zoom-hover"] img{transition:transform .5s ease}
942
+ [data-wv-effect="zoom-hover"]:hover img{transform:scale(1.03)}
943
+ [data-wv-effect="parallax-soft"]{will-change:transform}
944
+ }`;
945
+ var WV_EFFECT_JS = `(function(){
946
+ if(!('IntersectionObserver' in window))return;
947
+ var els=document.querySelectorAll('[data-wv-effect]');
948
+ if(!els.length)return;
949
+ if(window.matchMedia('(prefers-reduced-motion: reduce)').matches)return;
950
+ document.documentElement.classList.add('wv-js');
951
+ function num(el,attr,def){var v=parseInt(el.getAttribute(attr)||'',10);return isFinite(v)?v:def;}
952
+ function timing(el){
953
+ var d=num(el,'data-wv-delay',0);if(d>0)el.style.transitionDelay=(d*100)+'ms';
954
+ var ms=num(el,'data-wv-duration',0);if(ms>=100)el.style.transitionDuration=ms+'ms';
955
+ }
956
+ function counter(el){
957
+ var txt=el.textContent||'';var m=txt.match(/-?\\d[\\d.,]*/);if(!m)return;
958
+ var target=parseFloat(m[0].replace(/[.,](?=\\d{3}(\\D|$))/g,''));if(!isFinite(target))return;
959
+ var pre=txt.slice(0,m.index),post=txt.slice(m.index+m[0].length);
960
+ var dur=Math.min(Math.max(num(el,'data-wv-duration',1200),100),5000),t0=null;
961
+ function tick(t){
962
+ if(t0===null)t0=t;
963
+ var p=Math.min((t-t0)/dur,1),eased=1-Math.pow(1-p,3);
964
+ el.textContent=pre+Math.round(target*eased)+post;
965
+ if(p<1)requestAnimationFrame(tick);else el.textContent=txt;
966
+ }
967
+ requestAnimationFrame(tick);
968
+ }
969
+ var para=[];
970
+ function onScroll(){
971
+ for(var i=0;i<para.length;i++){
972
+ var el=para[i],r=el.getBoundingClientRect();
973
+ var vh=window.innerHeight||1;
974
+ var f=el.getAttribute('data-wv-strength')==='medium'?0.1:0.06;
975
+ var y=((r.top+r.height/2)-vh/2)*-f;
976
+ if(y>48)y=48;if(y<-48)y=-48;
977
+ el.style.transform='translateY('+y.toFixed(1)+'px)';
978
+ }
979
+ }
980
+ var ticking=false;
981
+ function onScrollRaf(){if(ticking)return;ticking=true;requestAnimationFrame(function(){ticking=false;onScroll();});}
982
+ var io=new IntersectionObserver(function(entries){
983
+ for(var i=0;i<entries.length;i++){
984
+ var en=entries[i];if(!en.isIntersecting)continue;
985
+ var el=en.target;io.unobserve(el);
986
+ var fx=el.getAttribute('data-wv-effect');
987
+ if(fx==='counter'){counter(el);continue;}
988
+ if(fx==='stagger'){
989
+ var step=Math.max(num(el,'data-wv-delay',1),1)*100,kids=el.children;
990
+ for(var k=0;k<kids.length;k++){kids[k].style.transitionDelay=(k*step)+'ms';kids[k].classList.add('wv-in');}
991
+ continue;
992
+ }
993
+ el.classList.add('wv-in');
994
+ }
995
+ },{threshold:0.15,rootMargin:'0px 0px -5% 0px'});
996
+ for(var i=0;i<els.length;i++){
997
+ var el=els[i],fx=el.getAttribute('data-wv-effect');
998
+ if(fx==='zoom-hover')continue;
999
+ if(fx==='parallax-soft'){para.push(el);continue;}
1000
+ timing(el);
1001
+ io.observe(el);
1002
+ }
1003
+ if(para.length){window.addEventListener('scroll',onScrollRaf,{passive:true});window.addEventListener('resize',onScrollRaf,{passive:true});onScroll();}
1004
+ })();`;
1005
+
917
1006
  // ../variant-compiler/src/lint/html.ts
918
1007
  var ALLOWED_TAGS = /* @__PURE__ */ new Set([
919
1008
  "a",
@@ -1166,12 +1255,39 @@ function lintNodes(nodes, ctx, ariaHidden = false) {
1166
1255
  }
1167
1256
  }
1168
1257
  if ("id" in n.attrs) push(ctx, "warning", "id-attr", `<${tag} id> \u2014 the editor's click walker stops at ids; prefer a data-* attribute`);
1258
+ lintEffectAttrs(n.attrs, tag, ctx);
1169
1259
  if (tag === "svg") ctx.inSvg++;
1170
1260
  lintNodes(n.ch, ctx, hidden);
1171
1261
  if (tag === "svg") ctx.inSvg--;
1172
1262
  }
1173
1263
  }
1174
1264
  __name(lintNodes, "lintNodes");
1265
+ function lintEffectAttrs(attrs, tag, ctx) {
1266
+ const fx = "data-wv-effect" in attrs ? staticValue(attrs["data-wv-effect"]) : void 0;
1267
+ if ("data-wv-effect" in attrs) {
1268
+ if (fx !== null && fx !== void 0 && !WV_EFFECTS.includes(fx)) {
1269
+ push(ctx, "error", "effect-unknown", `data-wv-effect="${fx}" is not a platform effect \u2014 one of: ${WV_EFFECTS.join(", ")}`);
1270
+ }
1271
+ if ("data-edit-field" in attrs || "data-edit-image" in attrs) {
1272
+ push(ctx, "warning", "effect-on-editable", `data-wv-effect on an editable element (<${tag}>) can interfere with inline editing \u2014 prefer the effect on a wrapper`);
1273
+ }
1274
+ } else if ("data-wv-delay" in attrs || "data-wv-duration" in attrs || "data-wv-strength" in attrs) {
1275
+ push(ctx, "error", "effect-param", `data-wv-delay/duration/strength on <${tag}> without data-wv-effect does nothing`);
1276
+ }
1277
+ const delay = staticValue(attrs["data-wv-delay"]);
1278
+ if (delay !== null && delay !== void 0 && !(/^\d+$/.test(delay) && Number(delay) <= WV_DELAY_MAX)) {
1279
+ push(ctx, "error", "effect-param", `data-wv-delay="${delay}" \u2014 whole steps of 100ms, 0..${WV_DELAY_MAX}`);
1280
+ }
1281
+ const dur = staticValue(attrs["data-wv-duration"]);
1282
+ if (dur !== null && dur !== void 0 && !(/^\d+$/.test(dur) && Number(dur) >= WV_DURATION_MIN && Number(dur) <= WV_DURATION_MAX)) {
1283
+ push(ctx, "error", "effect-param", `data-wv-duration="${dur}" \u2014 milliseconds, ${WV_DURATION_MIN}..${WV_DURATION_MAX}`);
1284
+ }
1285
+ const strength = staticValue(attrs["data-wv-strength"]);
1286
+ if (strength !== null && strength !== void 0 && !WV_STRENGTHS.includes(strength)) {
1287
+ push(ctx, "error", "effect-param", `data-wv-strength="${strength}" \u2014 one of: ${WV_STRENGTHS.join(", ")}`);
1288
+ }
1289
+ }
1290
+ __name(lintEffectAttrs, "lintEffectAttrs");
1175
1291
  function lintAttr(name, v, tag, ctx) {
1176
1292
  const lower = name.toLowerCase();
1177
1293
  if (/^on[a-z]/.test(lower)) {
@@ -2539,25 +2655,49 @@ __name(wrapScript, "wrapScript");
2539
2655
 
2540
2656
  // ../variant-compiler/src/preview.ts
2541
2657
  var PREVIEW_THEMES = {
2542
- light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", accent: "#f59e0b", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "8px" },
2543
- dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", accent: "#fbbf24", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "12px" },
2544
- warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", accent: "#d99a3f", heading: "Georgia, serif", body: "Inter, sans-serif", radius: "2px" }
2658
+ light: { background: "#ffffff", foreground: "#0f172a", card: "#f8fafc", muted: "#f1f5f9", mutedFg: "#64748b", border: "#e2e8f0", primary: "#3b82f6", primaryFg: "#ffffff", secondary: "#6366f1", secondaryFg: "#ffffff", accent: "#e8eefb", accentFg: "#1e293b", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "8px" },
2659
+ dark: { background: "#0b1220", foreground: "#e5e7eb", card: "#111a2e", muted: "#16213a", mutedFg: "#9aa4b8", border: "#243049", primary: "#60a5fa", primaryFg: "#0b1220", secondary: "#a78bfa", secondaryFg: "#0b1220", accent: "#1d2a47", accentFg: "#e5e7eb", heading: "Inter, sans-serif", body: "Inter, sans-serif", radius: "12px" },
2660
+ warm: { background: "#fbf6ef", foreground: "#2b1d12", card: "#f4ebdf", muted: "#efe3d2", mutedFg: "#7a6652", border: "#e2d3bf", primary: "#b5562d", primaryFg: "#ffffff", secondary: "#7c4a2d", secondaryFg: "#ffffff", accent: "#f1dfc0", accentFg: "#2b1d12", heading: "Georgia, serif", body: "Inter, sans-serif", radius: "2px" }
2545
2661
  };
2546
2662
  function isPreviewThemeKey(v) {
2547
2663
  return typeof v === "string" && v in PREVIEW_THEMES;
2548
2664
  }
2549
2665
  __name(isPreviewThemeKey, "isPreviewThemeKey");
2550
2666
  function buildPreviewDoc(ir, content, themeKey, opts = {}) {
2551
- const th = PREVIEW_THEMES[themeKey];
2667
+ const base = PREVIEW_THEMES[themeKey];
2668
+ const o = opts.themeOverride ?? {};
2669
+ const th = {
2670
+ background: o.background ?? base.background,
2671
+ foreground: o.foreground ?? base.foreground,
2672
+ card: o.card ?? base.card,
2673
+ muted: o.muted ?? base.muted,
2674
+ mutedFg: o.mutedFg ?? base.mutedFg,
2675
+ border: o.border ?? base.border,
2676
+ primary: o.primary ?? base.primary,
2677
+ primaryFg: o.primaryFg ?? base.primaryFg,
2678
+ secondary: o.secondary ?? base.secondary,
2679
+ secondaryFg: o.secondaryFg ?? base.secondaryFg,
2680
+ accent: o.accent ?? base.accent,
2681
+ accentFg: o.accentFg ?? base.accentFg,
2682
+ heading: o.heading ?? base.heading,
2683
+ body: o.body ?? base.body,
2684
+ radius: o.radius ?? base.radius
2685
+ };
2686
+ const headingSize = o.headingSize ?? "48px";
2687
+ const bodySize = o.bodySize ?? "16px";
2688
+ const gradientFrom = o.gradientFrom ?? th.primary;
2689
+ const gradientTo = o.gradientTo ?? th.secondary;
2690
+ const isDark = o.dark ?? themeKey === "dark";
2691
+ const fontLinks = (o.fontLinks ?? []).filter((h) => /^https:\/\/fonts\.googleapis\.com\//.test(h)).map((h) => `<link rel="stylesheet" href="${h.replace(/"/g, "&quot;")}">`).join("");
2552
2692
  const out = renderVariant(ir, content, { t: /* @__PURE__ */ __name((k) => k, "t"), editChrome: opts.editChrome ?? false }, { uid: "wv-preview" });
2553
- const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground:#fff;--muted:${th.muted};--muted-foreground:${th.mutedFg};--accent:${th.accent};--accent-foreground:#000;--border:${th.border};--input:${th.border};--ring:${th.primary};--radius:${th.radius};--font-heading:${th.heading};--font-body:${th.body};--gradient-from:${th.primary};--gradient-to:${th.secondary};--spacing:.25rem;--site-max-width:72rem;--font-heading-size:48px;--font-body-size:16px;`;
2693
+ const vars = `--background:${th.background};--foreground:${th.foreground};--card:${th.card};--card-foreground:${th.foreground};--primary:${th.primary};--primary-foreground:${th.primaryFg};--secondary:${th.secondary};--secondary-foreground:${th.secondaryFg};--muted:${th.muted};--muted-foreground:${th.mutedFg};--accent:${th.accent};--accent-foreground:${th.accentFg};--border:${th.border};--input:${th.border};--ring:${th.primary};--radius:${th.radius};--font-heading:${th.heading};--font-body:${th.body};--gradient-from:${gradientFrom};--gradient-to:${gradientTo};--spacing:.25rem;--site-max-width:72rem;--font-heading-size:${headingSize};--font-body-size:${bodySize};`;
2554
2694
  const tokens = `--color-background:var(--background);--color-foreground:var(--foreground);--color-card:var(--card);--color-card-foreground:var(--card-foreground);--color-primary:var(--primary);--color-primary-foreground:var(--primary-foreground);--color-secondary:var(--secondary);--color-secondary-foreground:var(--secondary-foreground);--color-muted:var(--muted);--color-muted-foreground:var(--muted-foreground);--color-accent:var(--accent);--color-accent-foreground:var(--accent-foreground);--color-border:var(--border);--color-input:var(--input);--color-ring:var(--ring);--radius-sm:calc(var(--radius) - 4px);--radius-md:calc(var(--radius) - 2px);--radius-lg:var(--radius);--radius-xl:calc(var(--radius) + 4px);`;
2555
- return `<!doctype html><html data-theme="${themeKey === "dark" ? "dark" : "light"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex">
2695
+ return `<!doctype html><html data-theme="${isDark ? "dark" : "light"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex">${fontLinks}
2556
2696
  <style>@layer wvfbase{*,::before,::after{box-sizing:border-box;border:0 solid var(--color-border)}html{font-size:var(--font-body-size,16px);-webkit-text-size-adjust:100%}body{margin:0;font-family:var(--font-body);color:var(--foreground);background:var(--background);line-height:1.5}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}h1,h2,h3,h4,p{margin:0}img,video,svg{display:block;max-width:100%;height:auto}a{color:inherit;text-decoration:inherit}button{font:inherit;color:inherit;background:none}ul,ol{margin:0;padding:0;list-style:none}}
2557
2697
  :root{${vars}${tokens}}
2558
2698
  h1{font-size:var(--font-heading-size,48px)}h2{font-size:calc(var(--font-heading-size,48px) * 0.75)}h3{font-size:calc(var(--font-heading-size,48px) * 0.6)}h4{font-size:calc(var(--font-heading-size,48px) * 0.5)}
2559
2699
  .edit-url-pill{display:none!important}</style>
2560
- <style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}</body></html>`;
2700
+ <style>${out.css}</style></head><body><div data-wv-root data-wv-inst="${out.uid}">${out.html}</div>${out.script ? `<script>${out.script}</script>` : ""}<style>${WV_EFFECT_CSS}</style><script>${WV_EFFECT_JS}</script></body></html>`;
2561
2701
  }
2562
2702
  __name(buildPreviewDoc, "buildPreviewDoc");
2563
2703
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webto-id/variant-check",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Lint, compile and preview a Webto Variant Format (.astro subset) section variant with the exact compiler the webto.id marketplace uses.",
5
5
  "license": "MIT",
6
6
  "type": "module",