@ox-content/theme-liquid-glass 2.84.0 → 2.87.0

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.
package/README.md CHANGED
@@ -4,7 +4,7 @@ Liquid Glass — Refractive glass panels with specular edges and a light sweep o
4
4
 
5
5
  **Form only.** Geometry, texture, typography and motion, written entirely
6
6
  against `--octc-*` custom properties. It names no colors, so it pairs with any
7
- `@ox-content/theme-color-*` scheme. About 12.2 kB of CSS, zero JavaScript, zero
7
+ `@ox-content/theme-color-*` scheme. About 12.6 kB of CSS, zero JavaScript, zero
8
8
  runtime dependencies.
9
9
 
10
10
  ```bash
package/dist/index.cjs CHANGED
@@ -26,7 +26,6 @@ const liquidGlass = {
26
26
  maxContentWidth: "940px"
27
27
  },
28
28
  entryPage: { mode: "default" },
29
- embed: { head: "<svg aria-hidden=\"true\" width=\"0\" height=\"0\" style=\"position:absolute;width:0;height:0;overflow:hidden\"><filter id=\"octc-lg-lens\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" color-interpolation-filters=\"sRGB\"><feImage href=\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22x%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%220%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22rgb%280%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.17%22%20stop-color%3D%22rgb%28128%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.83%22%20stop-color%3D%22rgb%28128%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22rgb%28255%2C0%2C0%29%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22y%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%220%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22rgb%280%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.17%22%20stop-color%3D%22rgb%280%2C128%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.83%22%20stop-color%3D%22rgb%280%2C128%2C0%29%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22rgb%280%2C255%2C0%29%22%2F%3E%3C%2FlinearGradient%3E%3Cfilter%20id%3D%22mix%22%3E%3CfeBlend%20mode%3D%22screen%22%2F%3E%3C%2Ffilter%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22url%28%23x%29%22%2F%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22url%28%23y%29%22%20style%3D%22mix-blend-mode%3Ascreen%22%2F%3E%3C%2Fsvg%3E\" result=\"map\" preserveAspectRatio=\"none\"/><feDisplacementMap in=\"SourceGraphic\" in2=\"map\" scale=\"26\" xChannelSelector=\"R\" yChannelSelector=\"G\"/></filter></svg>" },
30
29
  tokens: {
31
30
  "motion-fast": "220ms",
32
31
  "motion-base": "420ms",
@@ -41,17 +40,18 @@ const liquidGlass = {
41
40
  42% 36% at 88% 16%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 40%,transparent),transparent 74%
42
41
  ),radial-gradient(
43
42
  54% 44% at 68% 88%,color-mix(in srgb,var(--octc-accent-b,var(--octc-color-primary)) 34%,transparent),transparent 76%
44
- );--octc-lg-rim:12px;--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg-alt) 26%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.35);--octc-lg-spec-hi:rgba(255,255,255,0.5);--octc-lg-spec-lo:rgba(255,255,255,0.24);--octc-lg-underlight:rgba(0,0,0,0.16)}[data-theme="dark"]{--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.5);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.5);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}}body{background-image:var(--octc-lg-wallpaper);background-attachment:fixed;background-repeat:no-repeat}.header,.search-modal,.feature-card,.hero-notice,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active,.content blockquote{position:relative;isolation:isolate;border:0;background:var(--octc-lg-tint);backdrop-filter:url(#octc-lg-lens) blur(7px) saturate(190%);-webkit-backdrop-filter:url(#octc-lg-lens) blur(7px) saturate(190%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.header::before,.sidebar::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;padding:var(--octc-lg-rim);backdrop-filter:var(--octc-lg-rim-filter);-webkit-backdrop-filter:var(--octc-lg-rim-filter);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.header::after,.sidebar::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1px;background:linear-gradient(
43
+ );--octc-lg-rim:12px;--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg-alt) 22%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.6) brightness(1.06);--octc-lg-spec-hi:rgba(255,255,255,0.5);--octc-lg-spec-lo:rgba(255,255,255,0.24);--octc-lg-underlight:rgba(0,0,0,0.16)}[data-theme="dark"]{--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.75) brightness(1.12);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.75) brightness(1.12);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}}body{background-image:var(--octc-lg-wallpaper);background-attachment:fixed;background-repeat:no-repeat}.header,.search-modal,.feature-card,.hero-notice,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active,.content blockquote{position:relative;isolation:isolate;border:0;background:var(--octc-lg-tint);backdrop-filter:blur(2.5px) saturate(170%);-webkit-backdrop-filter:blur(2.5px) saturate(170%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.header::before,.sidebar::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;padding:var(--octc-lg-rim);backdrop-filter:var(--octc-lg-rim-filter);-webkit-backdrop-filter:var(--octc-lg-rim-filter);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.header::after,.sidebar::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1px;background:linear-gradient(
45
44
  145deg,var(--octc-lg-spec-hi) 0%,transparent 28%,transparent 68%,var(--octc-lg-spec-lo) 100%
46
- );-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.hero-action,.search-button,.theme-toggle,.social-link,.mobile-footer-btn,.search-input,.nav-link,.toc-link{border-radius:999px}.feature-card,.hero-notice,.content pre,.content blockquote,.content img,.content table{border-radius:var(--octc-lg-r)}.search-modal{border-radius:calc(var(--octc-lg-r) + 6px)}.header{--octc-lg-rim:8px;top:0.85rem;left:50%;right:auto;transform:translateX(-50%);width:max-content;max-width:calc(100% - 1.6rem);gap:1.25rem;border-radius:999px;padding-inline:1.15rem;height:calc(var(--octc-header-height) - 1.4rem)}.layout{padding-top:calc(var(--octc-header-height) + 0.7rem)}.header-title{font-weight:620;letter-spacing:-0.015em}.search-button,.theme-toggle,.social-link{--octc-lg-rim:7px}.nav-link.active{--octc-lg-rim:8px}.sidebar{--octc-skin-sidebar-bg:var(--octc-lg-tint);--octc-lg-rim:10px;position:fixed;top:calc(var(--octc-header-height) + 1.1rem);bottom:1.1rem;left:0.9rem;width:calc(var(--octc-sidebar-width) - 1.2rem);border-radius:var(--octc-lg-r);border:0;padding:1rem 0.75rem;background:var(--octc-lg-tint);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.toc{border-left:0}.nav-title,.toc-title{font-size:0.7rem;letter-spacing:0.11em;text-transform:uppercase;opacity:0.62}.nav-link:not(.active):hover,.toc-link:hover{background:color-mix(in srgb,var(--octc-color-primary) 10%,transparent)}.nav-link.active{font-weight:620}.content h2{border-bottom:1px solid color-mix(in srgb,var(--octc-color-border) 32%,transparent)}.content :not(pre)>code{border-radius:8px;background:color-mix(in srgb,var(--octc-color-primary) 11%,transparent);padding:0.1em 0.42em}.content pre{border:0;box-shadow:0 14px 36px -20px var(--octc-lg-underlight)}.content blockquote{--octc-lg-rim:10px}.content table{overflow:hidden}.content th,.content td{border-color:color-mix(in srgb,var(--octc-color-border) 26%,transparent)}.content hr{height:1px;border:0;background:color-mix(in srgb,var(--octc-color-border) 45%,transparent)}.hero{background-image:radial-gradient(
45
+ );-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.hero-action,.search-button,.theme-toggle,.social-link,.mobile-footer-btn,.search-input,.nav-link,.toc-link{border-radius:999px}.feature-card,.hero-notice,.content pre,.content blockquote,.content img,.content table{border-radius:var(--octc-lg-r)}.search-modal{border-radius:calc(var(--octc-lg-r) + 6px)}.header{--octc-lg-rim:8px;position:fixed;top:0.85rem;left:50%;right:auto;transform:translateX(-50%);width:max-content;max-width:calc(100% - 1.6rem);gap:1.25rem;border-radius:999px;padding-inline:1.15rem;height:calc(var(--octc-header-height) - 1.4rem)}body:not(.entry-page) .layout{padding-top:calc(var(--octc-header-height) + 0.7rem)}.header-title{font-weight:620;letter-spacing:-0.015em}.search-button,.theme-toggle,.social-link{--octc-lg-rim:7px}.nav-link.active{--octc-lg-rim:8px}.sidebar{--octc-skin-sidebar-bg:var(--octc-lg-tint);--octc-lg-rim:10px;position:fixed;top:calc(var(--octc-header-height) + 1.1rem);bottom:1.1rem;left:0.9rem;width:calc(var(--octc-sidebar-width) - 1.2rem);border-radius:var(--octc-lg-r);border:0;padding:1rem 0.75rem;background:var(--octc-lg-tint);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.toc{border-left:0}.nav-title,.toc-title{font-size:0.7rem;letter-spacing:0.11em;text-transform:uppercase;opacity:0.62}.nav-link:not(.active):hover,.toc-link:hover{background:color-mix(in srgb,var(--octc-color-primary) 10%,transparent)}.nav-link.active{font-weight:620}.content h2{border-bottom:1px solid color-mix(in srgb,var(--octc-color-border) 32%,transparent)}.content :not(pre)>code{border-radius:8px;background:color-mix(in srgb,var(--octc-color-primary) 11%,transparent);padding:0.1em 0.42em}.content pre{border:0;box-shadow:0 14px 36px -20px var(--octc-lg-underlight)}.content blockquote{--octc-lg-rim:10px}.content table{overflow:hidden}.content th,.content td{border-color:color-mix(in srgb,var(--octc-color-border) 26%,transparent)}.content hr{height:1px;border:0;background:color-mix(in srgb,var(--octc-color-border) 45%,transparent)}.hero{background-image:radial-gradient(
47
46
  40% 34% at 22% 16%,color-mix(in srgb,var(--octc-accent-a,var(--octc-color-primary)) 30%,transparent),transparent 72%
48
47
  ),radial-gradient(
49
48
  36% 32% at 80% 76%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 24%,transparent),transparent 74%
50
- );background-size:cover;background-repeat:no-repeat;background-attachment:fixed}.hero-name{letter-spacing:-0.035em;font-weight:660}.hero-action{--octc-lg-rim:9px;font-weight:620;padding-inline:2rem}.hero-action:hover,.feature-card:hover,.search-button:hover,.theme-toggle:hover,.social-link:hover{--octc-lg-rim:15px;transform:translateY(-2px)}.hero-action:active,.search-button:active,.theme-toggle:active,.social-link:active{--octc-lg-rim:5px;transform:translateY(0) scale(0.985)}.features .feature-card{padding:1.4rem}:focus-visible{outline:2px solid var(--octc-color-primary);outline-offset:4px;border-radius:999px}@supports not ((mask-composite:exclude) or (-webkit-mask-composite:xor)){.header::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before,.header::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{display:none}.header,.search-modal,.feature-card,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active{border:1px solid color-mix(in srgb,var(--octc-color-border) 45%,transparent)}}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{background:var(--octc-brand-fill,var(--octc-color-primary));border-color:var(--octc-brand-edge,var(--octc-color-primary));color:var(--octc-brand-ink,var(--octc-color-bg))}.hero-action-brand:hover{background:var(--octc-brand-fill-hover,var(--octc-color-primary-hover));border-color:var(--octc-brand-edge-hover,var(--octc-color-primary-hover))}.hero-action-alt,.hero-action-alt:hover{color:var(--octc-color-text)}:root:root .sidebar{background:var(
49
+ );background-size:cover;background-repeat:no-repeat;background-attachment:fixed}.hero-name{letter-spacing:-0.035em;font-weight:660}.hero-action{--octc-lg-rim:9px;font-weight:620;padding-inline:2rem}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{--octc-brand-fill:color-mix(in srgb,var(--octc-color-primary) 52%,transparent);--octc-brand-fill-hover:color-mix(in srgb,var(--octc-color-primary) 66%,transparent);--octc-brand-edge:color-mix(in srgb,var(--octc-color-primary) 45%,transparent);--octc-brand-edge-hover:color-mix(in srgb,var(--octc-color-primary) 60%,transparent)}.hero-action:hover,.feature-card:hover,.search-button:hover,.theme-toggle:hover,.social-link:hover{--octc-lg-rim:15px;transform:translateY(-2px)}.hero-action:active,.search-button:active,.theme-toggle:active,.social-link:active{--octc-lg-rim:5px;transform:translateY(0) scale(0.985)}.features .feature-card{padding:1.4rem}:focus-visible{outline:2px solid var(--octc-color-primary);outline-offset:4px;border-radius:999px}@supports not ((mask-composite:exclude) or (-webkit-mask-composite:xor)){.header::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before,.header::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{display:none}.header,.search-modal,.feature-card,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active{border:1px solid color-mix(in srgb,var(--octc-color-border) 45%,transparent)}}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{background:var(--octc-brand-fill,var(--octc-color-primary));border-color:var(--octc-brand-edge,var(--octc-color-primary));color:var(--octc-brand-ink,var(--octc-color-bg))}.hero-action-brand:hover{background:var(--octc-brand-fill-hover,var(--octc-color-primary-hover));border-color:var(--octc-brand-edge-hover,var(--octc-color-primary-hover))}.hero-action-alt,.hero-action-alt:hover{color:var(--octc-color-text)}:root:root .sidebar{background:var(
51
50
  --octc-skin-sidebar-bg,color-mix(in srgb,var(--octc-color-bg-alt) 16%,var(--octc-color-bg))
52
51
  )}:root:root .nav-link:not(.active):hover{background:var(
53
52
  --octc-skin-nav-hover-bg,color-mix(in srgb,var(--octc-color-bg-alt) 58%,transparent)
54
- )}.header{border-bottom:0}.header{background-color:transparent;background-image:none;box-shadow:none}.content pre{box-shadow:none}`
53
+ )}.header{border-bottom:0}.header{background-color:transparent;background-image:none;box-shadow:none}.content pre{box-shadow:none}`,
54
+ js: "(()=>{try{\n// Shared WebGL2 runtime for the skins that render a live hero backdrop.\n//\n// The SSG concatenates a theme's `js` into one classic inline <script>, so this\n// cannot import anything — hence a hand-rolled runtime rather than a library.\n// It is strictly progressive enhancement: the CSS backdrop is the real design,\n// and the canvas only ever layers on top of it. Every bail-out path below\n// leaves the page exactly as it renders without JavaScript.\n\nfunction octcGL(fragmentSource, options) {\n const opts = options || {};\n const host = document.querySelector(opts.target || \".hero\");\n if (!host || typeof WebGL2RenderingContext === \"undefined\") return;\n if (matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"aria-hidden\", \"true\");\n canvas.style.cssText =\n \"position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;\" +\n \"opacity:0;transition:opacity 900ms ease\";\n const gl = canvas.getContext(\"webgl2\", {\n alpha: true,\n antialias: false,\n depth: false,\n stencil: false,\n powerPreference: \"low-power\",\n });\n if (!gl) return;\n\n // A fullscreen triangle from gl_VertexID needs no buffers at all.\n const VERT =\n \"#version 300 es\\nvoid main(){vec2 p=vec2(float((gl_VertexID<<1)&2),float(gl_VertexID&2));\" +\n \"gl_Position=vec4(p*2.0-1.0,0.0,1.0);}\";\n\n function compile(type, src) {\n const sh = gl.createShader(type);\n gl.shaderSource(sh, src);\n gl.compileShader(sh);\n if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {\n gl.deleteShader(sh);\n return null;\n }\n return sh;\n }\n\n const vs = compile(gl.VERTEX_SHADER, VERT);\n const fs = compile(gl.FRAGMENT_SHADER, fragmentSource);\n if (!vs || !fs) return;\n\n const program = gl.createProgram();\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;\n gl.useProgram(program);\n\n const uRes = gl.getUniformLocation(program, \"u_res\");\n const uTime = gl.getUniformLocation(program, \"u_time\");\n const uColors = [0, 1, 2, 3].map((i) => gl.getUniformLocation(program, \"u_c\" + i));\n\n // A skin that needs uniforms beyond the shared set (Liquid Glass feeds the\n // rects of the panels it refracts) registers them here; the returned callback\n // runs every frame before the draw, in canvas pixels.\n const onFrame = opts.setup ? opts.setup(gl, program, host, canvas) : null;\n\n // Palette values are read through a probe element rather than parsed from the\n // custom property, because a scheme may define one as color-mix(), which only\n // resolves to concrete channels once it is used.\n const probe = document.createElement(\"span\");\n probe.style.cssText = \"position:absolute;width:0;height:0;opacity:0;pointer-events:none\";\n host.appendChild(probe);\n\n function readColor(name, fallback) {\n probe.style.color = fallback;\n probe.style.color = \"var(\" + name + \",\" + fallback + \")\";\n const value = getComputedStyle(probe).color;\n const parts = value.match(/[\\d.]+/g);\n if (!parts) return [0, 0, 0];\n // `color-mix()` serialises as `color(srgb r g b)` with 0-1 channels.\n const scale = value.indexOf(\"color(\") === 0 ? 1 : 255;\n return [parts[0] / scale, parts[1] / scale, parts[2] / scale];\n }\n\n const SLOTS = opts.colors || [\n \"--octc-accent-a\",\n \"--octc-accent-b\",\n \"--octc-accent-c\",\n \"--octc-color-bg\",\n ];\n\n function pushColors() {\n for (let i = 0; i < uColors.length; i++) {\n if (!uColors[i]) continue;\n const c = readColor(SLOTS[i], \"#888888\");\n gl.uniform3f(uColors[i], c[0], c[1], c[2]);\n }\n }\n\n // Resolution is capped rather than tied to devicePixelRatio: this is a soft,\n // out-of-focus backdrop, so a retina-sharp buffer costs fill rate for nothing.\n let width = 0;\n let height = 0;\n function resize() {\n const dpr = Math.min(devicePixelRatio || 1, 1.5);\n const w = Math.max(1, Math.round(host.clientWidth * dpr));\n const h = Math.max(1, Math.round(host.clientHeight * dpr));\n if (w === width && h === height) return;\n width = canvas.width = w;\n height = canvas.height = h;\n gl.viewport(0, 0, w, h);\n gl.uniform2f(uRes, w, h);\n }\n\n let running = false;\n let visible = false;\n let frame = 0;\n const start = performance.now();\n\n function tick(now) {\n if (!running) return;\n resize();\n gl.uniform1f(uTime, (now - start) / 1000);\n if (onFrame) onFrame(now, width, height);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n frame = requestAnimationFrame(tick);\n }\n\n function setRunning(next) {\n if (next === running) return;\n running = next;\n if (running) frame = requestAnimationFrame(tick);\n else cancelAnimationFrame(frame);\n }\n\n // Scrolled past or backgrounded means no work: an ambient effect must never\n // keep a tab busy once it is off screen.\n new IntersectionObserver((entries) => {\n visible = entries[0].isIntersecting;\n setRunning(visible && !document.hidden);\n }).observe(host);\n document.addEventListener(\"visibilitychange\", () => {\n setRunning(visible && !document.hidden);\n });\n addEventListener(\"resize\", resize, { passive: true });\n\n // The scheme changes under us whenever the reader flips the theme toggle.\n new MutationObserver(pushColors).observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\"],\n });\n\n host.insertBefore(canvas, host.firstChild);\n pushColors();\n resize();\n // Set directly rather than from a frame callback: anything that throws\n // between mount and that callback leaves the canvas permanently invisible,\n // which is indistinguishable from the effect simply not existing.\n canvas.style.opacity = opts.opacity == null ? \"1\" : String(opts.opacity);\n}\n\n// Liquid Glass — real refraction, done the way a compositor does it.\n//\n// The math is a direct port of ShojiWM's liquid-glass fragment shader\n// (github.com/bea4dev/ShojiWM, packages/config/src/liquid-glass.frag): a\n// rounded-rect SDF locates each pixel relative to the panel, the middle of the\n// panel passes the backdrop through untouched, and an edge band bends the\n// sample toward the panel centre along a circular profile —\n// `1 - sqrt(1 - d^2)` is the silhouette of a spherical bevel, which is why the\n// result reads as a ground glass edge rather than a smear. Red is sampled a\n// little deeper than blue, so the rim carries the chromatic fringe a real lens\n// would.\n//\n// CSS cannot do any of this — `backdrop-filter: url(...)` is unsupported in\n// Safari and unstable in Chromium — so the canvas draws the hero wallpaper\n// itself and refracts it under the real DOM panels, whose rects are fed in as\n// uniforms each frame. The panels' own CSS keeps only a thin tint and a faint\n// blur, so what you see through them is this shader.\noctcGL(\n `#version 300 es\nprecision highp float;\nuniform vec2 u_res; uniform float u_time;\nuniform vec3 u_c0, u_c1, u_c2, u_c3;\nuniform vec4 u_rects[8]; uniform float u_rads[8]; uniform int u_count;\nout vec4 outColor;\n\nfloat blob(vec2 p, vec2 c, float r){\n return 1.0 - smoothstep(0.0, r, distance(p, c));\n}\n\n// The wallpaper needs structure — a rim bending a flat gradient is invisible —\n// so soft accent pools sit over slow, low-contrast bands of light.\nvec3 wallpaper(vec2 q){\n float aspect = u_res.x / max(u_res.y, 1.0);\n vec2 p = vec2(q.x * aspect, q.y);\n float t = u_time * 0.05;\n vec3 col = u_c3;\n col = mix(col, u_c0, 0.42 * blob(p, vec2(0.16 * aspect + 0.04 * sin(t), 0.22), 0.62));\n col = mix(col, u_c2, 0.36 * blob(p, vec2(0.86 * aspect, 0.18 + 0.05 * cos(t * 0.8)), 0.55));\n col = mix(col, u_c1, 0.30 * blob(p, vec2(0.62 * aspect, 0.92), 0.68));\n float bands = sin((q.x * 2.6 - q.y * 1.8 + t * 0.9) * 6.2831);\n float fine = sin((q.x * 7.0 + q.y * 4.4 - t * 0.6) * 6.2831);\n col += 0.03 * bands + 0.016 * fine;\n return col;\n}\n\nfloat sdRoundRect(vec2 p, vec2 b, float r){\n vec2 d = abs(p) - b + vec2(r);\n return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;\n}\n\nvoid main(){\n vec2 frag = vec2(gl_FragCoord.x, u_res.y - gl_FragCoord.y);\n vec3 col = vec3(0.0);\n bool inside = false;\n for (int i = 0; i < 8; i++){\n if (i >= u_count) break;\n vec2 half_ = u_rects[i].zw;\n vec2 p = frag - u_rects[i].xy;\n float s = sdRoundRect(p, half_, u_rads[i]);\n if (s >= 0.0) continue;\n inside = true;\n float size = 2.0 * min(half_.x, half_.y);\n float inv = -s / size;\n float d = 1.0 - clamp(inv / 0.34, 0.0, 1.0);\n float distortion = 1.0 - sqrt(max(1.0 - d * d, 0.0));\n vec2 dir = p / max(length(p), 0.0001);\n vec2 off = distortion * dir * half_ * 0.62;\n float edge = smoothstep(0.0, 0.02, inv);\n vec2 shift = dir * edge * 3.0;\n vec2 sp = frag - off;\n col = vec3(\n wallpaper((sp - shift) / u_res).r,\n wallpaper(sp / u_res).g,\n wallpaper((sp + shift) / u_res).b\n );\n // A lens gathers light where its curvature is highest.\n col = col * 0.985 + distortion * 0.085;\n break;\n }\n if (!inside) col = wallpaper(frag / u_res);\n outColor = vec4(col, 1.0);\n}`,\n {\n colors: [\"--octc-accent-a\", \"--octc-accent-b\", \"--octc-accent-c\", \"--octc-color-bg\"],\n setup(gl, program, host) {\n const uRects = gl.getUniformLocation(program, \"u_rects\");\n const uRads = gl.getUniformLocation(program, \"u_rads\");\n const uCount = gl.getUniformLocation(program, \"u_count\");\n if (!uRects || !uCount) return null;\n // The header capsule floats over the hero, the actions and notice sit in\n // it. Radii are static per element, so they are read once.\n const els = Array.from(\n document.querySelectorAll(\".header, .hero-action, .hero-notice\"),\n ).slice(0, 8);\n const radii = els.map((el) => parseFloat(getComputedStyle(el).borderTopLeftRadius) || 0);\n const rects = new Float32Array(32);\n const rads = new Float32Array(8);\n return function frame(now, width, height) {\n const hb = host.getBoundingClientRect();\n if (hb.width === 0 || hb.height === 0) return;\n const sx = width / hb.width;\n const sy = height / hb.height;\n let n = 0;\n for (let i = 0; i < els.length && n < 8; i++) {\n const r = els[i].getBoundingClientRect();\n if (r.width === 0 || r.bottom <= hb.top || r.top >= hb.bottom) continue;\n rects[n * 4] = (r.left + r.width / 2 - hb.left) * sx;\n rects[n * 4 + 1] = (r.top + r.height / 2 - hb.top) * sy;\n rects[n * 4 + 2] = (r.width / 2) * sx;\n rects[n * 4 + 3] = (r.height / 2) * sy;\n rads[n] = Math.min(radii[i], r.height / 2) * sx;\n n++;\n }\n gl.uniform4fv(uRects, rects);\n gl.uniform1fv(uRads, rads);\n gl.uniform1i(uCount, n);\n };\n },\n },\n);\n\n}catch(e){console.warn(\"[ox-content] theme backdrop disabled:\",e)}})();"
55
55
  };
56
56
  //#endregion
57
57
  exports.default = liquidGlass;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;cAca,aAAa"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;cAea,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;cAca,aAAa"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;cAea,aAAa"}
package/dist/index.mjs CHANGED
@@ -22,7 +22,6 @@ const liquidGlass = {
22
22
  maxContentWidth: "940px"
23
23
  },
24
24
  entryPage: { mode: "default" },
25
- embed: { head: "<svg aria-hidden=\"true\" width=\"0\" height=\"0\" style=\"position:absolute;width:0;height:0;overflow:hidden\"><filter id=\"octc-lg-lens\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" color-interpolation-filters=\"sRGB\"><feImage href=\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22x%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%220%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22rgb%280%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.17%22%20stop-color%3D%22rgb%28128%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.83%22%20stop-color%3D%22rgb%28128%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22rgb%28255%2C0%2C0%29%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22y%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%220%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22rgb%280%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.17%22%20stop-color%3D%22rgb%280%2C128%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.83%22%20stop-color%3D%22rgb%280%2C128%2C0%29%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22rgb%280%2C255%2C0%29%22%2F%3E%3C%2FlinearGradient%3E%3Cfilter%20id%3D%22mix%22%3E%3CfeBlend%20mode%3D%22screen%22%2F%3E%3C%2Ffilter%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22url%28%23x%29%22%2F%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22url%28%23y%29%22%20style%3D%22mix-blend-mode%3Ascreen%22%2F%3E%3C%2Fsvg%3E\" result=\"map\" preserveAspectRatio=\"none\"/><feDisplacementMap in=\"SourceGraphic\" in2=\"map\" scale=\"26\" xChannelSelector=\"R\" yChannelSelector=\"G\"/></filter></svg>" },
26
25
  tokens: {
27
26
  "motion-fast": "220ms",
28
27
  "motion-base": "420ms",
@@ -37,17 +36,18 @@ const liquidGlass = {
37
36
  42% 36% at 88% 16%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 40%,transparent),transparent 74%
38
37
  ),radial-gradient(
39
38
  54% 44% at 68% 88%,color-mix(in srgb,var(--octc-accent-b,var(--octc-color-primary)) 34%,transparent),transparent 76%
40
- );--octc-lg-rim:12px;--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg-alt) 26%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.35);--octc-lg-spec-hi:rgba(255,255,255,0.5);--octc-lg-spec-lo:rgba(255,255,255,0.24);--octc-lg-underlight:rgba(0,0,0,0.16)}[data-theme="dark"]{--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.5);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.5);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}}body{background-image:var(--octc-lg-wallpaper);background-attachment:fixed;background-repeat:no-repeat}.header,.search-modal,.feature-card,.hero-notice,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active,.content blockquote{position:relative;isolation:isolate;border:0;background:var(--octc-lg-tint);backdrop-filter:url(#octc-lg-lens) blur(7px) saturate(190%);-webkit-backdrop-filter:url(#octc-lg-lens) blur(7px) saturate(190%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.header::before,.sidebar::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;padding:var(--octc-lg-rim);backdrop-filter:var(--octc-lg-rim-filter);-webkit-backdrop-filter:var(--octc-lg-rim-filter);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.header::after,.sidebar::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1px;background:linear-gradient(
39
+ );--octc-lg-rim:12px;--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg-alt) 22%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.6) brightness(1.06);--octc-lg-spec-hi:rgba(255,255,255,0.5);--octc-lg-spec-lo:rgba(255,255,255,0.24);--octc-lg-underlight:rgba(0,0,0,0.16)}[data-theme="dark"]{--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.75) brightness(1.12);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.75) brightness(1.12);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}}body{background-image:var(--octc-lg-wallpaper);background-attachment:fixed;background-repeat:no-repeat}.header,.search-modal,.feature-card,.hero-notice,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active,.content blockquote{position:relative;isolation:isolate;border:0;background:var(--octc-lg-tint);backdrop-filter:blur(2.5px) saturate(170%);-webkit-backdrop-filter:blur(2.5px) saturate(170%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.header::before,.sidebar::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;padding:var(--octc-lg-rim);backdrop-filter:var(--octc-lg-rim-filter);-webkit-backdrop-filter:var(--octc-lg-rim-filter);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.header::after,.sidebar::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1px;background:linear-gradient(
41
40
  145deg,var(--octc-lg-spec-hi) 0%,transparent 28%,transparent 68%,var(--octc-lg-spec-lo) 100%
42
- );-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.hero-action,.search-button,.theme-toggle,.social-link,.mobile-footer-btn,.search-input,.nav-link,.toc-link{border-radius:999px}.feature-card,.hero-notice,.content pre,.content blockquote,.content img,.content table{border-radius:var(--octc-lg-r)}.search-modal{border-radius:calc(var(--octc-lg-r) + 6px)}.header{--octc-lg-rim:8px;top:0.85rem;left:50%;right:auto;transform:translateX(-50%);width:max-content;max-width:calc(100% - 1.6rem);gap:1.25rem;border-radius:999px;padding-inline:1.15rem;height:calc(var(--octc-header-height) - 1.4rem)}.layout{padding-top:calc(var(--octc-header-height) + 0.7rem)}.header-title{font-weight:620;letter-spacing:-0.015em}.search-button,.theme-toggle,.social-link{--octc-lg-rim:7px}.nav-link.active{--octc-lg-rim:8px}.sidebar{--octc-skin-sidebar-bg:var(--octc-lg-tint);--octc-lg-rim:10px;position:fixed;top:calc(var(--octc-header-height) + 1.1rem);bottom:1.1rem;left:0.9rem;width:calc(var(--octc-sidebar-width) - 1.2rem);border-radius:var(--octc-lg-r);border:0;padding:1rem 0.75rem;background:var(--octc-lg-tint);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.toc{border-left:0}.nav-title,.toc-title{font-size:0.7rem;letter-spacing:0.11em;text-transform:uppercase;opacity:0.62}.nav-link:not(.active):hover,.toc-link:hover{background:color-mix(in srgb,var(--octc-color-primary) 10%,transparent)}.nav-link.active{font-weight:620}.content h2{border-bottom:1px solid color-mix(in srgb,var(--octc-color-border) 32%,transparent)}.content :not(pre)>code{border-radius:8px;background:color-mix(in srgb,var(--octc-color-primary) 11%,transparent);padding:0.1em 0.42em}.content pre{border:0;box-shadow:0 14px 36px -20px var(--octc-lg-underlight)}.content blockquote{--octc-lg-rim:10px}.content table{overflow:hidden}.content th,.content td{border-color:color-mix(in srgb,var(--octc-color-border) 26%,transparent)}.content hr{height:1px;border:0;background:color-mix(in srgb,var(--octc-color-border) 45%,transparent)}.hero{background-image:radial-gradient(
41
+ );-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.hero-action,.search-button,.theme-toggle,.social-link,.mobile-footer-btn,.search-input,.nav-link,.toc-link{border-radius:999px}.feature-card,.hero-notice,.content pre,.content blockquote,.content img,.content table{border-radius:var(--octc-lg-r)}.search-modal{border-radius:calc(var(--octc-lg-r) + 6px)}.header{--octc-lg-rim:8px;position:fixed;top:0.85rem;left:50%;right:auto;transform:translateX(-50%);width:max-content;max-width:calc(100% - 1.6rem);gap:1.25rem;border-radius:999px;padding-inline:1.15rem;height:calc(var(--octc-header-height) - 1.4rem)}body:not(.entry-page) .layout{padding-top:calc(var(--octc-header-height) + 0.7rem)}.header-title{font-weight:620;letter-spacing:-0.015em}.search-button,.theme-toggle,.social-link{--octc-lg-rim:7px}.nav-link.active{--octc-lg-rim:8px}.sidebar{--octc-skin-sidebar-bg:var(--octc-lg-tint);--octc-lg-rim:10px;position:fixed;top:calc(var(--octc-header-height) + 1.1rem);bottom:1.1rem;left:0.9rem;width:calc(var(--octc-sidebar-width) - 1.2rem);border-radius:var(--octc-lg-r);border:0;padding:1rem 0.75rem;background:var(--octc-lg-tint);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.toc{border-left:0}.nav-title,.toc-title{font-size:0.7rem;letter-spacing:0.11em;text-transform:uppercase;opacity:0.62}.nav-link:not(.active):hover,.toc-link:hover{background:color-mix(in srgb,var(--octc-color-primary) 10%,transparent)}.nav-link.active{font-weight:620}.content h2{border-bottom:1px solid color-mix(in srgb,var(--octc-color-border) 32%,transparent)}.content :not(pre)>code{border-radius:8px;background:color-mix(in srgb,var(--octc-color-primary) 11%,transparent);padding:0.1em 0.42em}.content pre{border:0;box-shadow:0 14px 36px -20px var(--octc-lg-underlight)}.content blockquote{--octc-lg-rim:10px}.content table{overflow:hidden}.content th,.content td{border-color:color-mix(in srgb,var(--octc-color-border) 26%,transparent)}.content hr{height:1px;border:0;background:color-mix(in srgb,var(--octc-color-border) 45%,transparent)}.hero{background-image:radial-gradient(
43
42
  40% 34% at 22% 16%,color-mix(in srgb,var(--octc-accent-a,var(--octc-color-primary)) 30%,transparent),transparent 72%
44
43
  ),radial-gradient(
45
44
  36% 32% at 80% 76%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 24%,transparent),transparent 74%
46
- );background-size:cover;background-repeat:no-repeat;background-attachment:fixed}.hero-name{letter-spacing:-0.035em;font-weight:660}.hero-action{--octc-lg-rim:9px;font-weight:620;padding-inline:2rem}.hero-action:hover,.feature-card:hover,.search-button:hover,.theme-toggle:hover,.social-link:hover{--octc-lg-rim:15px;transform:translateY(-2px)}.hero-action:active,.search-button:active,.theme-toggle:active,.social-link:active{--octc-lg-rim:5px;transform:translateY(0) scale(0.985)}.features .feature-card{padding:1.4rem}:focus-visible{outline:2px solid var(--octc-color-primary);outline-offset:4px;border-radius:999px}@supports not ((mask-composite:exclude) or (-webkit-mask-composite:xor)){.header::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before,.header::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{display:none}.header,.search-modal,.feature-card,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active{border:1px solid color-mix(in srgb,var(--octc-color-border) 45%,transparent)}}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{background:var(--octc-brand-fill,var(--octc-color-primary));border-color:var(--octc-brand-edge,var(--octc-color-primary));color:var(--octc-brand-ink,var(--octc-color-bg))}.hero-action-brand:hover{background:var(--octc-brand-fill-hover,var(--octc-color-primary-hover));border-color:var(--octc-brand-edge-hover,var(--octc-color-primary-hover))}.hero-action-alt,.hero-action-alt:hover{color:var(--octc-color-text)}:root:root .sidebar{background:var(
45
+ );background-size:cover;background-repeat:no-repeat;background-attachment:fixed}.hero-name{letter-spacing:-0.035em;font-weight:660}.hero-action{--octc-lg-rim:9px;font-weight:620;padding-inline:2rem}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{--octc-brand-fill:color-mix(in srgb,var(--octc-color-primary) 52%,transparent);--octc-brand-fill-hover:color-mix(in srgb,var(--octc-color-primary) 66%,transparent);--octc-brand-edge:color-mix(in srgb,var(--octc-color-primary) 45%,transparent);--octc-brand-edge-hover:color-mix(in srgb,var(--octc-color-primary) 60%,transparent)}.hero-action:hover,.feature-card:hover,.search-button:hover,.theme-toggle:hover,.social-link:hover{--octc-lg-rim:15px;transform:translateY(-2px)}.hero-action:active,.search-button:active,.theme-toggle:active,.social-link:active{--octc-lg-rim:5px;transform:translateY(0) scale(0.985)}.features .feature-card{padding:1.4rem}:focus-visible{outline:2px solid var(--octc-color-primary);outline-offset:4px;border-radius:999px}@supports not ((mask-composite:exclude) or (-webkit-mask-composite:xor)){.header::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before,.header::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{display:none}.header,.search-modal,.feature-card,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active{border:1px solid color-mix(in srgb,var(--octc-color-border) 45%,transparent)}}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{background:var(--octc-brand-fill,var(--octc-color-primary));border-color:var(--octc-brand-edge,var(--octc-color-primary));color:var(--octc-brand-ink,var(--octc-color-bg))}.hero-action-brand:hover{background:var(--octc-brand-fill-hover,var(--octc-color-primary-hover));border-color:var(--octc-brand-edge-hover,var(--octc-color-primary-hover))}.hero-action-alt,.hero-action-alt:hover{color:var(--octc-color-text)}:root:root .sidebar{background:var(
47
46
  --octc-skin-sidebar-bg,color-mix(in srgb,var(--octc-color-bg-alt) 16%,var(--octc-color-bg))
48
47
  )}:root:root .nav-link:not(.active):hover{background:var(
49
48
  --octc-skin-nav-hover-bg,color-mix(in srgb,var(--octc-color-bg-alt) 58%,transparent)
50
- )}.header{border-bottom:0}.header{background-color:transparent;background-image:none;box-shadow:none}.content pre{box-shadow:none}`
49
+ )}.header{border-bottom:0}.header{background-color:transparent;background-image:none;box-shadow:none}.content pre{box-shadow:none}`,
50
+ js: "(()=>{try{\n// Shared WebGL2 runtime for the skins that render a live hero backdrop.\n//\n// The SSG concatenates a theme's `js` into one classic inline <script>, so this\n// cannot import anything — hence a hand-rolled runtime rather than a library.\n// It is strictly progressive enhancement: the CSS backdrop is the real design,\n// and the canvas only ever layers on top of it. Every bail-out path below\n// leaves the page exactly as it renders without JavaScript.\n\nfunction octcGL(fragmentSource, options) {\n const opts = options || {};\n const host = document.querySelector(opts.target || \".hero\");\n if (!host || typeof WebGL2RenderingContext === \"undefined\") return;\n if (matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"aria-hidden\", \"true\");\n canvas.style.cssText =\n \"position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;\" +\n \"opacity:0;transition:opacity 900ms ease\";\n const gl = canvas.getContext(\"webgl2\", {\n alpha: true,\n antialias: false,\n depth: false,\n stencil: false,\n powerPreference: \"low-power\",\n });\n if (!gl) return;\n\n // A fullscreen triangle from gl_VertexID needs no buffers at all.\n const VERT =\n \"#version 300 es\\nvoid main(){vec2 p=vec2(float((gl_VertexID<<1)&2),float(gl_VertexID&2));\" +\n \"gl_Position=vec4(p*2.0-1.0,0.0,1.0);}\";\n\n function compile(type, src) {\n const sh = gl.createShader(type);\n gl.shaderSource(sh, src);\n gl.compileShader(sh);\n if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {\n gl.deleteShader(sh);\n return null;\n }\n return sh;\n }\n\n const vs = compile(gl.VERTEX_SHADER, VERT);\n const fs = compile(gl.FRAGMENT_SHADER, fragmentSource);\n if (!vs || !fs) return;\n\n const program = gl.createProgram();\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;\n gl.useProgram(program);\n\n const uRes = gl.getUniformLocation(program, \"u_res\");\n const uTime = gl.getUniformLocation(program, \"u_time\");\n const uColors = [0, 1, 2, 3].map((i) => gl.getUniformLocation(program, \"u_c\" + i));\n\n // A skin that needs uniforms beyond the shared set (Liquid Glass feeds the\n // rects of the panels it refracts) registers them here; the returned callback\n // runs every frame before the draw, in canvas pixels.\n const onFrame = opts.setup ? opts.setup(gl, program, host, canvas) : null;\n\n // Palette values are read through a probe element rather than parsed from the\n // custom property, because a scheme may define one as color-mix(), which only\n // resolves to concrete channels once it is used.\n const probe = document.createElement(\"span\");\n probe.style.cssText = \"position:absolute;width:0;height:0;opacity:0;pointer-events:none\";\n host.appendChild(probe);\n\n function readColor(name, fallback) {\n probe.style.color = fallback;\n probe.style.color = \"var(\" + name + \",\" + fallback + \")\";\n const value = getComputedStyle(probe).color;\n const parts = value.match(/[\\d.]+/g);\n if (!parts) return [0, 0, 0];\n // `color-mix()` serialises as `color(srgb r g b)` with 0-1 channels.\n const scale = value.indexOf(\"color(\") === 0 ? 1 : 255;\n return [parts[0] / scale, parts[1] / scale, parts[2] / scale];\n }\n\n const SLOTS = opts.colors || [\n \"--octc-accent-a\",\n \"--octc-accent-b\",\n \"--octc-accent-c\",\n \"--octc-color-bg\",\n ];\n\n function pushColors() {\n for (let i = 0; i < uColors.length; i++) {\n if (!uColors[i]) continue;\n const c = readColor(SLOTS[i], \"#888888\");\n gl.uniform3f(uColors[i], c[0], c[1], c[2]);\n }\n }\n\n // Resolution is capped rather than tied to devicePixelRatio: this is a soft,\n // out-of-focus backdrop, so a retina-sharp buffer costs fill rate for nothing.\n let width = 0;\n let height = 0;\n function resize() {\n const dpr = Math.min(devicePixelRatio || 1, 1.5);\n const w = Math.max(1, Math.round(host.clientWidth * dpr));\n const h = Math.max(1, Math.round(host.clientHeight * dpr));\n if (w === width && h === height) return;\n width = canvas.width = w;\n height = canvas.height = h;\n gl.viewport(0, 0, w, h);\n gl.uniform2f(uRes, w, h);\n }\n\n let running = false;\n let visible = false;\n let frame = 0;\n const start = performance.now();\n\n function tick(now) {\n if (!running) return;\n resize();\n gl.uniform1f(uTime, (now - start) / 1000);\n if (onFrame) onFrame(now, width, height);\n gl.drawArrays(gl.TRIANGLES, 0, 3);\n frame = requestAnimationFrame(tick);\n }\n\n function setRunning(next) {\n if (next === running) return;\n running = next;\n if (running) frame = requestAnimationFrame(tick);\n else cancelAnimationFrame(frame);\n }\n\n // Scrolled past or backgrounded means no work: an ambient effect must never\n // keep a tab busy once it is off screen.\n new IntersectionObserver((entries) => {\n visible = entries[0].isIntersecting;\n setRunning(visible && !document.hidden);\n }).observe(host);\n document.addEventListener(\"visibilitychange\", () => {\n setRunning(visible && !document.hidden);\n });\n addEventListener(\"resize\", resize, { passive: true });\n\n // The scheme changes under us whenever the reader flips the theme toggle.\n new MutationObserver(pushColors).observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\"],\n });\n\n host.insertBefore(canvas, host.firstChild);\n pushColors();\n resize();\n // Set directly rather than from a frame callback: anything that throws\n // between mount and that callback leaves the canvas permanently invisible,\n // which is indistinguishable from the effect simply not existing.\n canvas.style.opacity = opts.opacity == null ? \"1\" : String(opts.opacity);\n}\n\n// Liquid Glass — real refraction, done the way a compositor does it.\n//\n// The math is a direct port of ShojiWM's liquid-glass fragment shader\n// (github.com/bea4dev/ShojiWM, packages/config/src/liquid-glass.frag): a\n// rounded-rect SDF locates each pixel relative to the panel, the middle of the\n// panel passes the backdrop through untouched, and an edge band bends the\n// sample toward the panel centre along a circular profile —\n// `1 - sqrt(1 - d^2)` is the silhouette of a spherical bevel, which is why the\n// result reads as a ground glass edge rather than a smear. Red is sampled a\n// little deeper than blue, so the rim carries the chromatic fringe a real lens\n// would.\n//\n// CSS cannot do any of this — `backdrop-filter: url(...)` is unsupported in\n// Safari and unstable in Chromium — so the canvas draws the hero wallpaper\n// itself and refracts it under the real DOM panels, whose rects are fed in as\n// uniforms each frame. The panels' own CSS keeps only a thin tint and a faint\n// blur, so what you see through them is this shader.\noctcGL(\n `#version 300 es\nprecision highp float;\nuniform vec2 u_res; uniform float u_time;\nuniform vec3 u_c0, u_c1, u_c2, u_c3;\nuniform vec4 u_rects[8]; uniform float u_rads[8]; uniform int u_count;\nout vec4 outColor;\n\nfloat blob(vec2 p, vec2 c, float r){\n return 1.0 - smoothstep(0.0, r, distance(p, c));\n}\n\n// The wallpaper needs structure — a rim bending a flat gradient is invisible —\n// so soft accent pools sit over slow, low-contrast bands of light.\nvec3 wallpaper(vec2 q){\n float aspect = u_res.x / max(u_res.y, 1.0);\n vec2 p = vec2(q.x * aspect, q.y);\n float t = u_time * 0.05;\n vec3 col = u_c3;\n col = mix(col, u_c0, 0.42 * blob(p, vec2(0.16 * aspect + 0.04 * sin(t), 0.22), 0.62));\n col = mix(col, u_c2, 0.36 * blob(p, vec2(0.86 * aspect, 0.18 + 0.05 * cos(t * 0.8)), 0.55));\n col = mix(col, u_c1, 0.30 * blob(p, vec2(0.62 * aspect, 0.92), 0.68));\n float bands = sin((q.x * 2.6 - q.y * 1.8 + t * 0.9) * 6.2831);\n float fine = sin((q.x * 7.0 + q.y * 4.4 - t * 0.6) * 6.2831);\n col += 0.03 * bands + 0.016 * fine;\n return col;\n}\n\nfloat sdRoundRect(vec2 p, vec2 b, float r){\n vec2 d = abs(p) - b + vec2(r);\n return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;\n}\n\nvoid main(){\n vec2 frag = vec2(gl_FragCoord.x, u_res.y - gl_FragCoord.y);\n vec3 col = vec3(0.0);\n bool inside = false;\n for (int i = 0; i < 8; i++){\n if (i >= u_count) break;\n vec2 half_ = u_rects[i].zw;\n vec2 p = frag - u_rects[i].xy;\n float s = sdRoundRect(p, half_, u_rads[i]);\n if (s >= 0.0) continue;\n inside = true;\n float size = 2.0 * min(half_.x, half_.y);\n float inv = -s / size;\n float d = 1.0 - clamp(inv / 0.34, 0.0, 1.0);\n float distortion = 1.0 - sqrt(max(1.0 - d * d, 0.0));\n vec2 dir = p / max(length(p), 0.0001);\n vec2 off = distortion * dir * half_ * 0.62;\n float edge = smoothstep(0.0, 0.02, inv);\n vec2 shift = dir * edge * 3.0;\n vec2 sp = frag - off;\n col = vec3(\n wallpaper((sp - shift) / u_res).r,\n wallpaper(sp / u_res).g,\n wallpaper((sp + shift) / u_res).b\n );\n // A lens gathers light where its curvature is highest.\n col = col * 0.985 + distortion * 0.085;\n break;\n }\n if (!inside) col = wallpaper(frag / u_res);\n outColor = vec4(col, 1.0);\n}`,\n {\n colors: [\"--octc-accent-a\", \"--octc-accent-b\", \"--octc-accent-c\", \"--octc-color-bg\"],\n setup(gl, program, host) {\n const uRects = gl.getUniformLocation(program, \"u_rects\");\n const uRads = gl.getUniformLocation(program, \"u_rads\");\n const uCount = gl.getUniformLocation(program, \"u_count\");\n if (!uRects || !uCount) return null;\n // The header capsule floats over the hero, the actions and notice sit in\n // it. Radii are static per element, so they are read once.\n const els = Array.from(\n document.querySelectorAll(\".header, .hero-action, .hero-notice\"),\n ).slice(0, 8);\n const radii = els.map((el) => parseFloat(getComputedStyle(el).borderTopLeftRadius) || 0);\n const rects = new Float32Array(32);\n const rads = new Float32Array(8);\n return function frame(now, width, height) {\n const hb = host.getBoundingClientRect();\n if (hb.width === 0 || hb.height === 0) return;\n const sx = width / hb.width;\n const sy = height / hb.height;\n let n = 0;\n for (let i = 0; i < els.length && n < 8; i++) {\n const r = els[i].getBoundingClientRect();\n if (r.width === 0 || r.bottom <= hb.top || r.top >= hb.bottom) continue;\n rects[n * 4] = (r.left + r.width / 2 - hb.left) * sx;\n rects[n * 4 + 1] = (r.top + r.height / 2 - hb.top) * sy;\n rects[n * 4 + 2] = (r.width / 2) * sx;\n rects[n * 4 + 3] = (r.height / 2) * sy;\n rads[n] = Math.min(radii[i], r.height / 2) * sx;\n n++;\n }\n gl.uniform4fv(uRects, rects);\n gl.uniform1fv(uRads, rads);\n gl.uniform1i(uCount, n);\n };\n },\n },\n);\n\n}catch(e){console.warn(\"[ox-content] theme backdrop disabled:\",e)}})();"
51
51
  };
52
52
  //#endregion
53
53
  export { liquidGlass as default, liquidGlass };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/skin.ts","../src/index.ts"],"sourcesContent":["/**\n * Liquid Glass skin stylesheet.\n *\n * Refractive glass panels with specular edges and a light sweep on hover.\n *\n * Written entirely against `--octc-*` custom properties and `color-mix()`, plus\n * neutral black/white alpha for depth — no hue is ever named, which is what lets\n * any `@ox-content/theme-color-*` package drive it.\n *\n * Generated from `scripts/theme-skins/skins/liquid-glass.css`; edit that file and\n * run `node scripts/theme-skins/generate.mjs` rather than editing this by hand.\n */\nexport const css = `@property --octc-sweep{syntax:\"<percentage>\";inherits:false;initial-value:0%}@property --octc-spin{syntax:\"<angle>\";inherits:false;initial-value:0deg}@view-transition{navigation:auto}.header{view-transition-name:octc-header}.sidebar{view-transition-name:octc-sidebar}.main{view-transition-name:octc-main}@media (prefers-reduced-motion:no-preference){::view-transition-old(octc-main){animation:octc-swap-out var(--octc-motion-base) var(--octc-motion-ease) both}::view-transition-new(octc-main){animation:octc-swap-in var(--octc-motion-base) var(--octc-motion-ease) both}}@keyframes octc-swap-out{to{opacity:0;transform:translateY(-0.5rem)}}@keyframes octc-swap-in{from{opacity:0;transform:translateY(0.75rem)}}@supports (animation-timeline:view()){@media (prefers-reduced-motion:no-preference){.content>h2,.feature-card{animation:octc-rise linear both;animation-timeline:view();animation-range:entry 0% entry 55%}.features-grid>:nth-child(2n){animation-range:entry 3% entry 58%}.features-grid>:nth-child(3n){animation-range:entry 6% entry 62%}.hero-name,.hero-text,.hero-tagline,.hero-actions{animation:octc-hero-in var(--octc-motion-slow) var(--octc-motion-spring) both}.hero-text{animation-delay:90ms}.hero-tagline{animation-delay:160ms}.hero-actions{animation-delay:240ms}}}@keyframes octc-rise{from{opacity:0;transform:translateY(var(--octc-motion-rise,0.6rem))}}@keyframes octc-hero-in{from{opacity:0;transform:translateY(var(--octc-motion-rise,0.6rem)) scale(0.994)}}.nav-link,.toc-link,.feature-card,.hero-action,.social-link,.search-button,.theme-toggle,.content a{transition:color var(--octc-motion-fast) var(--octc-motion-ease),background-color var(--octc-motion-fast) var(--octc-motion-ease),border-color var(--octc-motion-fast) var(--octc-motion-ease),box-shadow var(--octc-motion-base) var(--octc-motion-ease),transform var(--octc-motion-base) var(--octc-motion-spring)}.search-modal-overlay{transition:opacity var(--octc-motion-base) var(--octc-motion-ease),display var(--octc-motion-base) allow-discrete}@media (prefers-reduced-motion:no-preference){.search-modal{transition:transform var(--octc-motion-base) var(--octc-motion-spring)}@starting-style{.search-modal{transform:translateY(-0.75rem) scale(0.97)}}}@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:1ms !important;animation-iteration-count:1 !important;transition-duration:1ms !important;scroll-behavior:auto !important}}.entry-page .main{padding-inline:0}.entry-page .layout{padding-top:0}.entry-content{max-width:none;padding-inline:0}.entry-content>.content,.features{max-width:var(--octc-max-content-width);margin-inline:auto;padding-inline:clamp(1.25rem,4vw,2rem)}.hero{padding-inline:clamp(1.5rem,6vw,4rem);background-color:transparent;border-bottom:0;min-height:min(100vh,56rem);padding-block:calc(var(--octc-header-height) + clamp(3rem,11vh,7rem)) clamp(3.5rem,11vh,7rem)}.hero::after{content:\"\";position:absolute;inset:auto 0 0 0;height:clamp(6rem,22vh,14rem);z-index:0;pointer-events:none;background:linear-gradient(to bottom,transparent,var(--octc-color-bg))}.hero-content,.hero-notice,.hero-image{position:relative;z-index:1}.features{border-top:0}.header{background:transparent;border-bottom:0;box-shadow:none}body:not(.entry-page) .header{backdrop-filter:blur(14px) saturate(150%);-webkit-backdrop-filter:blur(14px) saturate(150%)}@supports (animation-timeline:scroll()){.header{animation:var(--octc-header-scroll,octc-header-settle) linear both;animation-timeline:scroll(root block);animation-range:0 7rem}}@keyframes octc-header-settle{from{backdrop-filter:blur(0px) saturate(100%);-webkit-backdrop-filter:blur(0px) saturate(100%)}to{backdrop-filter:blur(14px) saturate(150%);-webkit-backdrop-filter:blur(14px) saturate(150%)}}.sidebar,.toc{top:0;padding-top:calc(var(--octc-header-height) + 1rem)}.header-actions .social-link,.header-actions .theme-toggle{opacity:0.62;transition:opacity var(--octc-motion-fast) var(--octc-motion-ease)}.header:hover .social-link,.header:hover .theme-toggle,.header-actions :focus-visible{opacity:1}.content h1,.content h2,.content h3,.hero-name,.hero-text,.feature-title{text-wrap:balance}.content p,.content li,.hero-tagline,.feature-details{text-wrap:pretty}.content{hanging-punctuation:first last}.content table{font-variant-numeric:tabular-nums}.content a{text-underline-offset:0.18em;text-decoration-skip-ink:auto}.content h1 a,.content h2 a,.content h3 a{text-decoration:none}::selection{background:color-mix(in srgb,var(--octc-color-primary) 28%,transparent);color:var(--octc-color-text)}.sidebar,.toc,.content pre,.search-results{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--octc-color-text) 22%,transparent) transparent}.content pre{scroll-padding-inline:1rem}.content img,.content video,.content iframe{max-width:100%;height:auto}[data-theme=\"dark\"] body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){:root:not([data-theme=\"light\"]) body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}}:root{--octc-lg-r:22px;--octc-lg-wallpaper:radial-gradient(\n48% 38% at 12% 8%,color-mix(in srgb,var(--octc-accent-a,var(--octc-color-primary)) 46%,transparent),transparent 72%\n),radial-gradient(\n42% 36% at 88% 16%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 40%,transparent),transparent 74%\n),radial-gradient(\n54% 44% at 68% 88%,color-mix(in srgb,var(--octc-accent-b,var(--octc-color-primary)) 34%,transparent),transparent 76%\n);--octc-lg-rim:12px;--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg-alt) 26%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.35);--octc-lg-spec-hi:rgba(255,255,255,0.5);--octc-lg-spec-lo:rgba(255,255,255,0.24);--octc-lg-underlight:rgba(0,0,0,0.16)}[data-theme=\"dark\"]{--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.5);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}@media (prefers-color-scheme:dark){:root:not([data-theme=\"light\"]){--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(1px) saturate(1.5);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}}body{background-image:var(--octc-lg-wallpaper);background-attachment:fixed;background-repeat:no-repeat}.header,.search-modal,.feature-card,.hero-notice,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active,.content blockquote{position:relative;isolation:isolate;border:0;background:var(--octc-lg-tint);backdrop-filter:url(#octc-lg-lens) blur(7px) saturate(190%);-webkit-backdrop-filter:url(#octc-lg-lens) blur(7px) saturate(190%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.header::before,.sidebar::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before{content:\"\";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;padding:var(--octc-lg-rim);backdrop-filter:var(--octc-lg-rim-filter);-webkit-backdrop-filter:var(--octc-lg-rim-filter);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.header::after,.sidebar::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{content:\"\";position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1px;background:linear-gradient(\n145deg,var(--octc-lg-spec-hi) 0%,transparent 28%,transparent 68%,var(--octc-lg-spec-lo) 100%\n);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.hero-action,.search-button,.theme-toggle,.social-link,.mobile-footer-btn,.search-input,.nav-link,.toc-link{border-radius:999px}.feature-card,.hero-notice,.content pre,.content blockquote,.content img,.content table{border-radius:var(--octc-lg-r)}.search-modal{border-radius:calc(var(--octc-lg-r) + 6px)}.header{--octc-lg-rim:8px;top:0.85rem;left:50%;right:auto;transform:translateX(-50%);width:max-content;max-width:calc(100% - 1.6rem);gap:1.25rem;border-radius:999px;padding-inline:1.15rem;height:calc(var(--octc-header-height) - 1.4rem)}.layout{padding-top:calc(var(--octc-header-height) + 0.7rem)}.header-title{font-weight:620;letter-spacing:-0.015em}.search-button,.theme-toggle,.social-link{--octc-lg-rim:7px}.nav-link.active{--octc-lg-rim:8px}.sidebar{--octc-skin-sidebar-bg:var(--octc-lg-tint);--octc-lg-rim:10px;position:fixed;top:calc(var(--octc-header-height) + 1.1rem);bottom:1.1rem;left:0.9rem;width:calc(var(--octc-sidebar-width) - 1.2rem);border-radius:var(--octc-lg-r);border:0;padding:1rem 0.75rem;background:var(--octc-lg-tint);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.toc{border-left:0}.nav-title,.toc-title{font-size:0.7rem;letter-spacing:0.11em;text-transform:uppercase;opacity:0.62}.nav-link:not(.active):hover,.toc-link:hover{background:color-mix(in srgb,var(--octc-color-primary) 10%,transparent)}.nav-link.active{font-weight:620}.content h2{border-bottom:1px solid color-mix(in srgb,var(--octc-color-border) 32%,transparent)}.content :not(pre)>code{border-radius:8px;background:color-mix(in srgb,var(--octc-color-primary) 11%,transparent);padding:0.1em 0.42em}.content pre{border:0;box-shadow:0 14px 36px -20px var(--octc-lg-underlight)}.content blockquote{--octc-lg-rim:10px}.content table{overflow:hidden}.content th,.content td{border-color:color-mix(in srgb,var(--octc-color-border) 26%,transparent)}.content hr{height:1px;border:0;background:color-mix(in srgb,var(--octc-color-border) 45%,transparent)}.hero{background-image:radial-gradient(\n40% 34% at 22% 16%,color-mix(in srgb,var(--octc-accent-a,var(--octc-color-primary)) 30%,transparent),transparent 72%\n),radial-gradient(\n36% 32% at 80% 76%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 24%,transparent),transparent 74%\n);background-size:cover;background-repeat:no-repeat;background-attachment:fixed}.hero-name{letter-spacing:-0.035em;font-weight:660}.hero-action{--octc-lg-rim:9px;font-weight:620;padding-inline:2rem}.hero-action:hover,.feature-card:hover,.search-button:hover,.theme-toggle:hover,.social-link:hover{--octc-lg-rim:15px;transform:translateY(-2px)}.hero-action:active,.search-button:active,.theme-toggle:active,.social-link:active{--octc-lg-rim:5px;transform:translateY(0) scale(0.985)}.features .feature-card{padding:1.4rem}:focus-visible{outline:2px solid var(--octc-color-primary);outline-offset:4px;border-radius:999px}@supports not ((mask-composite:exclude) or (-webkit-mask-composite:xor)){.header::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before,.header::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{display:none}.header,.search-modal,.feature-card,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active{border:1px solid color-mix(in srgb,var(--octc-color-border) 45%,transparent)}}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{background:var(--octc-brand-fill,var(--octc-color-primary));border-color:var(--octc-brand-edge,var(--octc-color-primary));color:var(--octc-brand-ink,var(--octc-color-bg))}.hero-action-brand:hover{background:var(--octc-brand-fill-hover,var(--octc-color-primary-hover));border-color:var(--octc-brand-edge-hover,var(--octc-color-primary-hover))}.hero-action-alt,.hero-action-alt:hover{color:var(--octc-color-text)}:root:root .sidebar{background:var(\n--octc-skin-sidebar-bg,color-mix(in srgb,var(--octc-color-bg-alt) 16%,var(--octc-color-bg))\n)}:root:root .nav-link:not(.active):hover{background:var(\n--octc-skin-nav-hover-bg,color-mix(in srgb,var(--octc-color-bg-alt) 58%,transparent)\n)}.header{border-bottom:0}.header{background-color:transparent;background-image:none;box-shadow:none}.content pre{box-shadow:none}`;\n","import type { ThemeConfig } from \"@ox-content/vite-plugin\";\n\nimport { css } from \"./skin\";\n\n/**\n * Liquid Glass — Refractive glass panels with specular edges and a light sweep on hover.\n *\n * Form only: geometry, texture, typography and motion. It names no colors, so\n * it composes with any `@ox-content/theme-color-*` package:\n *\n * ```ts\n * theme: [liquidGlass, tokyoNight]\n * ```\n */\nexport const liquidGlass: ThemeConfig = {\n name: \"liquid-glass\",\n fonts: {\n sans: 'ui-sans-serif, system-ui, -apple-system, \"Segoe UI Variable\", \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif',\n mono: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, \"Cascadia Mono\", Consolas, monospace',\n },\n layout: {\n sidebarWidth: \"268px\",\n headerHeight: \"66px\",\n maxContentWidth: \"940px\",\n },\n entryPage: { mode: \"default\" },\n embed: {\n head: '<svg aria-hidden=\"true\" width=\"0\" height=\"0\" style=\"position:absolute;width:0;height:0;overflow:hidden\"><filter id=\"octc-lg-lens\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" color-interpolation-filters=\"sRGB\"><feImage href=\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%22x%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%221%22%20y2%3D%220%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22rgb%280%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.17%22%20stop-color%3D%22rgb%28128%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.83%22%20stop-color%3D%22rgb%28128%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22rgb%28255%2C0%2C0%29%22%2F%3E%3C%2FlinearGradient%3E%3ClinearGradient%20id%3D%22y%22%20x1%3D%220%22%20y1%3D%220%22%20x2%3D%220%22%20y2%3D%221%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22rgb%280%2C0%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.17%22%20stop-color%3D%22rgb%280%2C128%2C0%29%22%2F%3E%3Cstop%20offset%3D%220.83%22%20stop-color%3D%22rgb%280%2C128%2C0%29%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22rgb%280%2C255%2C0%29%22%2F%3E%3C%2FlinearGradient%3E%3Cfilter%20id%3D%22mix%22%3E%3CfeBlend%20mode%3D%22screen%22%2F%3E%3C%2Ffilter%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22url%28%23x%29%22%2F%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22url%28%23y%29%22%20style%3D%22mix-blend-mode%3Ascreen%22%2F%3E%3C%2Fsvg%3E\" result=\"map\" preserveAspectRatio=\"none\"/><feDisplacementMap in=\"SourceGraphic\" in2=\"map\" scale=\"26\" xChannelSelector=\"R\" yChannelSelector=\"G\"/></filter></svg>',\n },\n tokens: {\n \"motion-fast\": \"220ms\",\n \"motion-base\": \"420ms\",\n \"motion-slow\": \"1000ms\",\n \"motion-ease\": \"cubic-bezier(0.22, 1, 0.36, 1)\",\n \"motion-spring\": \"cubic-bezier(0.34, 1.56, 0.64, 1)\",\n \"motion-rise\": \"0.75rem\",\n },\n css,\n};\n\nexport default liquidGlass;\n"],"mappings":";;;;;;;;;;;;ACcA,MAAa,cAA2B;CACtC,MAAM;CACN,OAAO;EACL,MAAM;EACN,MAAM;CACR;CACA,QAAQ;EACN,cAAc;EACd,cAAc;EACd,iBAAiB;CACnB;CACA,WAAW,EAAE,MAAM,UAAU;CAC7B,OAAO,EACL,MAAM,qqDACR;CACA,QAAQ;EACN,eAAe;EACf,eAAe;EACf,eAAe;EACf,eAAe;EACf,iBAAiB;EACjB,eAAe;CACjB;CACA;;;;;;;;;;;;;;;;;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/skin.ts","../src/gl.ts","../src/index.ts"],"sourcesContent":["/**\n * Liquid Glass skin stylesheet.\n *\n * Refractive glass panels with specular edges and a light sweep on hover.\n *\n * Written entirely against `--octc-*` custom properties and `color-mix()`, plus\n * neutral black/white alpha for depth — no hue is ever named, which is what lets\n * any `@ox-content/theme-color-*` package drive it.\n *\n * Generated from `scripts/theme-skins/skins/liquid-glass.css`; edit that file and\n * run `node scripts/theme-skins/generate.mjs` rather than editing this by hand.\n */\nexport const css = `@property --octc-sweep{syntax:\"<percentage>\";inherits:false;initial-value:0%}@property --octc-spin{syntax:\"<angle>\";inherits:false;initial-value:0deg}@view-transition{navigation:auto}.header{view-transition-name:octc-header}.sidebar{view-transition-name:octc-sidebar}.main{view-transition-name:octc-main}@media (prefers-reduced-motion:no-preference){::view-transition-old(octc-main){animation:octc-swap-out var(--octc-motion-base) var(--octc-motion-ease) both}::view-transition-new(octc-main){animation:octc-swap-in var(--octc-motion-base) var(--octc-motion-ease) both}}@keyframes octc-swap-out{to{opacity:0;transform:translateY(-0.5rem)}}@keyframes octc-swap-in{from{opacity:0;transform:translateY(0.75rem)}}@supports (animation-timeline:view()){@media (prefers-reduced-motion:no-preference){.content>h2,.feature-card{animation:octc-rise linear both;animation-timeline:view();animation-range:entry 0% entry 55%}.features-grid>:nth-child(2n){animation-range:entry 3% entry 58%}.features-grid>:nth-child(3n){animation-range:entry 6% entry 62%}.hero-name,.hero-text,.hero-tagline,.hero-actions{animation:octc-hero-in var(--octc-motion-slow) var(--octc-motion-spring) both}.hero-text{animation-delay:90ms}.hero-tagline{animation-delay:160ms}.hero-actions{animation-delay:240ms}}}@keyframes octc-rise{from{opacity:0;transform:translateY(var(--octc-motion-rise,0.6rem))}}@keyframes octc-hero-in{from{opacity:0;transform:translateY(var(--octc-motion-rise,0.6rem)) scale(0.994)}}.nav-link,.toc-link,.feature-card,.hero-action,.social-link,.search-button,.theme-toggle,.content a{transition:color var(--octc-motion-fast) var(--octc-motion-ease),background-color var(--octc-motion-fast) var(--octc-motion-ease),border-color var(--octc-motion-fast) var(--octc-motion-ease),box-shadow var(--octc-motion-base) var(--octc-motion-ease),transform var(--octc-motion-base) var(--octc-motion-spring)}.search-modal-overlay{transition:opacity var(--octc-motion-base) var(--octc-motion-ease),display var(--octc-motion-base) allow-discrete}@media (prefers-reduced-motion:no-preference){.search-modal{transition:transform var(--octc-motion-base) var(--octc-motion-spring)}@starting-style{.search-modal{transform:translateY(-0.75rem) scale(0.97)}}}@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:1ms !important;animation-iteration-count:1 !important;transition-duration:1ms !important;scroll-behavior:auto !important}}.entry-page .main{padding-inline:0}.entry-page .layout{padding-top:0}.entry-content{max-width:none;padding-inline:0}.entry-content>.content,.features{max-width:var(--octc-max-content-width);margin-inline:auto;padding-inline:clamp(1.25rem,4vw,2rem)}.hero{padding-inline:clamp(1.5rem,6vw,4rem);background-color:transparent;border-bottom:0;min-height:min(100vh,56rem);padding-block:calc(var(--octc-header-height) + clamp(3rem,11vh,7rem)) clamp(3.5rem,11vh,7rem)}.hero::after{content:\"\";position:absolute;inset:auto 0 0 0;height:clamp(6rem,22vh,14rem);z-index:0;pointer-events:none;background:linear-gradient(to bottom,transparent,var(--octc-color-bg))}.hero-content,.hero-notice,.hero-image{position:relative;z-index:1}.features{border-top:0}.header{background:transparent;border-bottom:0;box-shadow:none}body:not(.entry-page) .header{backdrop-filter:blur(14px) saturate(150%);-webkit-backdrop-filter:blur(14px) saturate(150%)}@supports (animation-timeline:scroll()){.header{animation:var(--octc-header-scroll,octc-header-settle) linear both;animation-timeline:scroll(root block);animation-range:0 7rem}}@keyframes octc-header-settle{from{backdrop-filter:blur(0px) saturate(100%);-webkit-backdrop-filter:blur(0px) saturate(100%)}to{backdrop-filter:blur(14px) saturate(150%);-webkit-backdrop-filter:blur(14px) saturate(150%)}}.sidebar,.toc{top:0;padding-top:calc(var(--octc-header-height) + 1rem)}.header-actions .social-link,.header-actions .theme-toggle{opacity:0.62;transition:opacity var(--octc-motion-fast) var(--octc-motion-ease)}.header:hover .social-link,.header:hover .theme-toggle,.header-actions :focus-visible{opacity:1}.content h1,.content h2,.content h3,.hero-name,.hero-text,.feature-title{text-wrap:balance}.content p,.content li,.hero-tagline,.feature-details{text-wrap:pretty}.content{hanging-punctuation:first last}.content table{font-variant-numeric:tabular-nums}.content a{text-underline-offset:0.18em;text-decoration-skip-ink:auto}.content h1 a,.content h2 a,.content h3 a{text-decoration:none}::selection{background:color-mix(in srgb,var(--octc-color-primary) 28%,transparent);color:var(--octc-color-text)}.sidebar,.toc,.content pre,.search-results{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--octc-color-text) 22%,transparent) transparent}.content pre{scroll-padding-inline:1rem}.content img,.content video,.content iframe{max-width:100%;height:auto}[data-theme=\"dark\"] body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){:root:not([data-theme=\"light\"]) body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}}:root{--octc-lg-r:22px;--octc-lg-wallpaper:radial-gradient(\n48% 38% at 12% 8%,color-mix(in srgb,var(--octc-accent-a,var(--octc-color-primary)) 46%,transparent),transparent 72%\n),radial-gradient(\n42% 36% at 88% 16%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 40%,transparent),transparent 74%\n),radial-gradient(\n54% 44% at 68% 88%,color-mix(in srgb,var(--octc-accent-b,var(--octc-color-primary)) 34%,transparent),transparent 76%\n);--octc-lg-rim:12px;--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg-alt) 22%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.6) brightness(1.06);--octc-lg-spec-hi:rgba(255,255,255,0.5);--octc-lg-spec-lo:rgba(255,255,255,0.24);--octc-lg-underlight:rgba(0,0,0,0.16)}[data-theme=\"dark\"]{--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.75) brightness(1.12);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}@media (prefers-color-scheme:dark){:root:not([data-theme=\"light\"]){--octc-lg-tint:color-mix(in srgb,var(--octc-color-bg) 24%,transparent);--octc-lg-rim-filter:blur(0.5px) saturate(1.75) brightness(1.12);--octc-lg-spec-hi:rgba(255,255,255,0.34);--octc-lg-spec-lo:rgba(255,255,255,0.08);--octc-lg-underlight:rgba(0,0,0,0.5)}}body{background-image:var(--octc-lg-wallpaper);background-attachment:fixed;background-repeat:no-repeat}.header,.search-modal,.feature-card,.hero-notice,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active,.content blockquote{position:relative;isolation:isolate;border:0;background:var(--octc-lg-tint);backdrop-filter:blur(2.5px) saturate(170%);-webkit-backdrop-filter:blur(2.5px) saturate(170%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.header::before,.sidebar::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before{content:\"\";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;padding:var(--octc-lg-rim);backdrop-filter:var(--octc-lg-rim-filter);-webkit-backdrop-filter:var(--octc-lg-rim-filter);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.header::after,.sidebar::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{content:\"\";position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:1px;background:linear-gradient(\n145deg,var(--octc-lg-spec-hi) 0%,transparent 28%,transparent 68%,var(--octc-lg-spec-lo) 100%\n);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude}.hero-action,.search-button,.theme-toggle,.social-link,.mobile-footer-btn,.search-input,.nav-link,.toc-link{border-radius:999px}.feature-card,.hero-notice,.content pre,.content blockquote,.content img,.content table{border-radius:var(--octc-lg-r)}.search-modal{border-radius:calc(var(--octc-lg-r) + 6px)}.header{--octc-lg-rim:8px;position:fixed;top:0.85rem;left:50%;right:auto;transform:translateX(-50%);width:max-content;max-width:calc(100% - 1.6rem);gap:1.25rem;border-radius:999px;padding-inline:1.15rem;height:calc(var(--octc-header-height) - 1.4rem)}body:not(.entry-page) .layout{padding-top:calc(var(--octc-header-height) + 0.7rem)}.header-title{font-weight:620;letter-spacing:-0.015em}.search-button,.theme-toggle,.social-link{--octc-lg-rim:7px}.nav-link.active{--octc-lg-rim:8px}.sidebar{--octc-skin-sidebar-bg:var(--octc-lg-tint);--octc-lg-rim:10px;position:fixed;top:calc(var(--octc-header-height) + 1.1rem);bottom:1.1rem;left:0.9rem;width:calc(var(--octc-sidebar-width) - 1.2rem);border-radius:var(--octc-lg-r);border:0;padding:1rem 0.75rem;background:var(--octc-lg-tint);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);box-shadow:0 10px 30px -14px var(--octc-lg-underlight)}.toc{border-left:0}.nav-title,.toc-title{font-size:0.7rem;letter-spacing:0.11em;text-transform:uppercase;opacity:0.62}.nav-link:not(.active):hover,.toc-link:hover{background:color-mix(in srgb,var(--octc-color-primary) 10%,transparent)}.nav-link.active{font-weight:620}.content h2{border-bottom:1px solid color-mix(in srgb,var(--octc-color-border) 32%,transparent)}.content :not(pre)>code{border-radius:8px;background:color-mix(in srgb,var(--octc-color-primary) 11%,transparent);padding:0.1em 0.42em}.content pre{border:0;box-shadow:0 14px 36px -20px var(--octc-lg-underlight)}.content blockquote{--octc-lg-rim:10px}.content table{overflow:hidden}.content th,.content td{border-color:color-mix(in srgb,var(--octc-color-border) 26%,transparent)}.content hr{height:1px;border:0;background:color-mix(in srgb,var(--octc-color-border) 45%,transparent)}.hero{background-image:radial-gradient(\n40% 34% at 22% 16%,color-mix(in srgb,var(--octc-accent-a,var(--octc-color-primary)) 30%,transparent),transparent 72%\n),radial-gradient(\n36% 32% at 80% 76%,color-mix(in srgb,var(--octc-accent-c,var(--octc-color-primary)) 24%,transparent),transparent 74%\n);background-size:cover;background-repeat:no-repeat;background-attachment:fixed}.hero-name{letter-spacing:-0.035em;font-weight:660}.hero-action{--octc-lg-rim:9px;font-weight:620;padding-inline:2rem}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{--octc-brand-fill:color-mix(in srgb,var(--octc-color-primary) 52%,transparent);--octc-brand-fill-hover:color-mix(in srgb,var(--octc-color-primary) 66%,transparent);--octc-brand-edge:color-mix(in srgb,var(--octc-color-primary) 45%,transparent);--octc-brand-edge-hover:color-mix(in srgb,var(--octc-color-primary) 60%,transparent)}.hero-action:hover,.feature-card:hover,.search-button:hover,.theme-toggle:hover,.social-link:hover{--octc-lg-rim:15px;transform:translateY(-2px)}.hero-action:active,.search-button:active,.theme-toggle:active,.social-link:active{--octc-lg-rim:5px;transform:translateY(0) scale(0.985)}.features .feature-card{padding:1.4rem}:focus-visible{outline:2px solid var(--octc-color-primary);outline-offset:4px;border-radius:999px}@supports not ((mask-composite:exclude) or (-webkit-mask-composite:xor)){.header::before,.search-modal::before,.feature-card::before,.hero-action::before,.search-button::before,.theme-toggle::before,.social-link::before,.nav-link.active::before,.header::after,.search-modal::after,.feature-card::after,.hero-action::after,.search-button::after,.theme-toggle::after,.social-link::after,.nav-link.active::after{display:none}.header,.search-modal,.feature-card,.hero-action,.search-button,.theme-toggle,.social-link,.nav-link.active{border:1px solid color-mix(in srgb,var(--octc-color-border) 45%,transparent)}}.hero-action-brand,.hero-action-brand:hover,.hero-action-brand:focus-visible{background:var(--octc-brand-fill,var(--octc-color-primary));border-color:var(--octc-brand-edge,var(--octc-color-primary));color:var(--octc-brand-ink,var(--octc-color-bg))}.hero-action-brand:hover{background:var(--octc-brand-fill-hover,var(--octc-color-primary-hover));border-color:var(--octc-brand-edge-hover,var(--octc-color-primary-hover))}.hero-action-alt,.hero-action-alt:hover{color:var(--octc-color-text)}:root:root .sidebar{background:var(\n--octc-skin-sidebar-bg,color-mix(in srgb,var(--octc-color-bg-alt) 16%,var(--octc-color-bg))\n)}:root:root .nav-link:not(.active):hover{background:var(\n--octc-skin-nav-hover-bg,color-mix(in srgb,var(--octc-color-bg-alt) 58%,transparent)\n)}.header{border-bottom:0}.header{background-color:transparent;background-image:none;box-shadow:none}.content pre{box-shadow:none}`;\n","/**\n * Liquid Glass WebGL2 hero backdrop.\n *\n * Progressive enhancement only: it bails out under prefers-reduced-motion,\n * on a missing WebGL2 context, and whenever the hero scrolls out of view.\n * The CSS backdrop underneath is the design; this layers on top of it.\n *\n * Generated from scripts/theme-skins/js/liquid-glass.js; edit that file and\n * run node scripts/theme-skins/generate.mjs rather than editing this by hand.\n */\nexport const js =\n '(()=>{try{\\n// Shared WebGL2 runtime for the skins that render a live hero backdrop.\\n//\\n// The SSG concatenates a theme\\'s `js` into one classic inline <script>, so this\\n// cannot import anything — hence a hand-rolled runtime rather than a library.\\n// It is strictly progressive enhancement: the CSS backdrop is the real design,\\n// and the canvas only ever layers on top of it. Every bail-out path below\\n// leaves the page exactly as it renders without JavaScript.\\n\\nfunction octcGL(fragmentSource, options) {\\n const opts = options || {};\\n const host = document.querySelector(opts.target || \".hero\");\\n if (!host || typeof WebGL2RenderingContext === \"undefined\") return;\\n if (matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\\n\\n const canvas = document.createElement(\"canvas\");\\n canvas.setAttribute(\"aria-hidden\", \"true\");\\n canvas.style.cssText =\\n \"position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;\" +\\n \"opacity:0;transition:opacity 900ms ease\";\\n const gl = canvas.getContext(\"webgl2\", {\\n alpha: true,\\n antialias: false,\\n depth: false,\\n stencil: false,\\n powerPreference: \"low-power\",\\n });\\n if (!gl) return;\\n\\n // A fullscreen triangle from gl_VertexID needs no buffers at all.\\n const VERT =\\n \"#version 300 es\\\\nvoid main(){vec2 p=vec2(float((gl_VertexID<<1)&2),float(gl_VertexID&2));\" +\\n \"gl_Position=vec4(p*2.0-1.0,0.0,1.0);}\";\\n\\n function compile(type, src) {\\n const sh = gl.createShader(type);\\n gl.shaderSource(sh, src);\\n gl.compileShader(sh);\\n if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {\\n gl.deleteShader(sh);\\n return null;\\n }\\n return sh;\\n }\\n\\n const vs = compile(gl.VERTEX_SHADER, VERT);\\n const fs = compile(gl.FRAGMENT_SHADER, fragmentSource);\\n if (!vs || !fs) return;\\n\\n const program = gl.createProgram();\\n gl.attachShader(program, vs);\\n gl.attachShader(program, fs);\\n gl.linkProgram(program);\\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return;\\n gl.useProgram(program);\\n\\n const uRes = gl.getUniformLocation(program, \"u_res\");\\n const uTime = gl.getUniformLocation(program, \"u_time\");\\n const uColors = [0, 1, 2, 3].map((i) => gl.getUniformLocation(program, \"u_c\" + i));\\n\\n // A skin that needs uniforms beyond the shared set (Liquid Glass feeds the\\n // rects of the panels it refracts) registers them here; the returned callback\\n // runs every frame before the draw, in canvas pixels.\\n const onFrame = opts.setup ? opts.setup(gl, program, host, canvas) : null;\\n\\n // Palette values are read through a probe element rather than parsed from the\\n // custom property, because a scheme may define one as color-mix(), which only\\n // resolves to concrete channels once it is used.\\n const probe = document.createElement(\"span\");\\n probe.style.cssText = \"position:absolute;width:0;height:0;opacity:0;pointer-events:none\";\\n host.appendChild(probe);\\n\\n function readColor(name, fallback) {\\n probe.style.color = fallback;\\n probe.style.color = \"var(\" + name + \",\" + fallback + \")\";\\n const value = getComputedStyle(probe).color;\\n const parts = value.match(/[\\\\d.]+/g);\\n if (!parts) return [0, 0, 0];\\n // `color-mix()` serialises as `color(srgb r g b)` with 0-1 channels.\\n const scale = value.indexOf(\"color(\") === 0 ? 1 : 255;\\n return [parts[0] / scale, parts[1] / scale, parts[2] / scale];\\n }\\n\\n const SLOTS = opts.colors || [\\n \"--octc-accent-a\",\\n \"--octc-accent-b\",\\n \"--octc-accent-c\",\\n \"--octc-color-bg\",\\n ];\\n\\n function pushColors() {\\n for (let i = 0; i < uColors.length; i++) {\\n if (!uColors[i]) continue;\\n const c = readColor(SLOTS[i], \"#888888\");\\n gl.uniform3f(uColors[i], c[0], c[1], c[2]);\\n }\\n }\\n\\n // Resolution is capped rather than tied to devicePixelRatio: this is a soft,\\n // out-of-focus backdrop, so a retina-sharp buffer costs fill rate for nothing.\\n let width = 0;\\n let height = 0;\\n function resize() {\\n const dpr = Math.min(devicePixelRatio || 1, 1.5);\\n const w = Math.max(1, Math.round(host.clientWidth * dpr));\\n const h = Math.max(1, Math.round(host.clientHeight * dpr));\\n if (w === width && h === height) return;\\n width = canvas.width = w;\\n height = canvas.height = h;\\n gl.viewport(0, 0, w, h);\\n gl.uniform2f(uRes, w, h);\\n }\\n\\n let running = false;\\n let visible = false;\\n let frame = 0;\\n const start = performance.now();\\n\\n function tick(now) {\\n if (!running) return;\\n resize();\\n gl.uniform1f(uTime, (now - start) / 1000);\\n if (onFrame) onFrame(now, width, height);\\n gl.drawArrays(gl.TRIANGLES, 0, 3);\\n frame = requestAnimationFrame(tick);\\n }\\n\\n function setRunning(next) {\\n if (next === running) return;\\n running = next;\\n if (running) frame = requestAnimationFrame(tick);\\n else cancelAnimationFrame(frame);\\n }\\n\\n // Scrolled past or backgrounded means no work: an ambient effect must never\\n // keep a tab busy once it is off screen.\\n new IntersectionObserver((entries) => {\\n visible = entries[0].isIntersecting;\\n setRunning(visible && !document.hidden);\\n }).observe(host);\\n document.addEventListener(\"visibilitychange\", () => {\\n setRunning(visible && !document.hidden);\\n });\\n addEventListener(\"resize\", resize, { passive: true });\\n\\n // The scheme changes under us whenever the reader flips the theme toggle.\\n new MutationObserver(pushColors).observe(document.documentElement, {\\n attributes: true,\\n attributeFilter: [\"data-theme\"],\\n });\\n\\n host.insertBefore(canvas, host.firstChild);\\n pushColors();\\n resize();\\n // Set directly rather than from a frame callback: anything that throws\\n // between mount and that callback leaves the canvas permanently invisible,\\n // which is indistinguishable from the effect simply not existing.\\n canvas.style.opacity = opts.opacity == null ? \"1\" : String(opts.opacity);\\n}\\n\\n// Liquid Glass — real refraction, done the way a compositor does it.\\n//\\n// The math is a direct port of ShojiWM\\'s liquid-glass fragment shader\\n// (github.com/bea4dev/ShojiWM, packages/config/src/liquid-glass.frag): a\\n// rounded-rect SDF locates each pixel relative to the panel, the middle of the\\n// panel passes the backdrop through untouched, and an edge band bends the\\n// sample toward the panel centre along a circular profile —\\n// `1 - sqrt(1 - d^2)` is the silhouette of a spherical bevel, which is why the\\n// result reads as a ground glass edge rather than a smear. Red is sampled a\\n// little deeper than blue, so the rim carries the chromatic fringe a real lens\\n// would.\\n//\\n// CSS cannot do any of this — `backdrop-filter: url(...)` is unsupported in\\n// Safari and unstable in Chromium — so the canvas draws the hero wallpaper\\n// itself and refracts it under the real DOM panels, whose rects are fed in as\\n// uniforms each frame. The panels\\' own CSS keeps only a thin tint and a faint\\n// blur, so what you see through them is this shader.\\noctcGL(\\n `#version 300 es\\nprecision highp float;\\nuniform vec2 u_res; uniform float u_time;\\nuniform vec3 u_c0, u_c1, u_c2, u_c3;\\nuniform vec4 u_rects[8]; uniform float u_rads[8]; uniform int u_count;\\nout vec4 outColor;\\n\\nfloat blob(vec2 p, vec2 c, float r){\\n return 1.0 - smoothstep(0.0, r, distance(p, c));\\n}\\n\\n// The wallpaper needs structure — a rim bending a flat gradient is invisible —\\n// so soft accent pools sit over slow, low-contrast bands of light.\\nvec3 wallpaper(vec2 q){\\n float aspect = u_res.x / max(u_res.y, 1.0);\\n vec2 p = vec2(q.x * aspect, q.y);\\n float t = u_time * 0.05;\\n vec3 col = u_c3;\\n col = mix(col, u_c0, 0.42 * blob(p, vec2(0.16 * aspect + 0.04 * sin(t), 0.22), 0.62));\\n col = mix(col, u_c2, 0.36 * blob(p, vec2(0.86 * aspect, 0.18 + 0.05 * cos(t * 0.8)), 0.55));\\n col = mix(col, u_c1, 0.30 * blob(p, vec2(0.62 * aspect, 0.92), 0.68));\\n float bands = sin((q.x * 2.6 - q.y * 1.8 + t * 0.9) * 6.2831);\\n float fine = sin((q.x * 7.0 + q.y * 4.4 - t * 0.6) * 6.2831);\\n col += 0.03 * bands + 0.016 * fine;\\n return col;\\n}\\n\\nfloat sdRoundRect(vec2 p, vec2 b, float r){\\n vec2 d = abs(p) - b + vec2(r);\\n return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;\\n}\\n\\nvoid main(){\\n vec2 frag = vec2(gl_FragCoord.x, u_res.y - gl_FragCoord.y);\\n vec3 col = vec3(0.0);\\n bool inside = false;\\n for (int i = 0; i < 8; i++){\\n if (i >= u_count) break;\\n vec2 half_ = u_rects[i].zw;\\n vec2 p = frag - u_rects[i].xy;\\n float s = sdRoundRect(p, half_, u_rads[i]);\\n if (s >= 0.0) continue;\\n inside = true;\\n float size = 2.0 * min(half_.x, half_.y);\\n float inv = -s / size;\\n float d = 1.0 - clamp(inv / 0.34, 0.0, 1.0);\\n float distortion = 1.0 - sqrt(max(1.0 - d * d, 0.0));\\n vec2 dir = p / max(length(p), 0.0001);\\n vec2 off = distortion * dir * half_ * 0.62;\\n float edge = smoothstep(0.0, 0.02, inv);\\n vec2 shift = dir * edge * 3.0;\\n vec2 sp = frag - off;\\n col = vec3(\\n wallpaper((sp - shift) / u_res).r,\\n wallpaper(sp / u_res).g,\\n wallpaper((sp + shift) / u_res).b\\n );\\n // A lens gathers light where its curvature is highest.\\n col = col * 0.985 + distortion * 0.085;\\n break;\\n }\\n if (!inside) col = wallpaper(frag / u_res);\\n outColor = vec4(col, 1.0);\\n}`,\\n {\\n colors: [\"--octc-accent-a\", \"--octc-accent-b\", \"--octc-accent-c\", \"--octc-color-bg\"],\\n setup(gl, program, host) {\\n const uRects = gl.getUniformLocation(program, \"u_rects\");\\n const uRads = gl.getUniformLocation(program, \"u_rads\");\\n const uCount = gl.getUniformLocation(program, \"u_count\");\\n if (!uRects || !uCount) return null;\\n // The header capsule floats over the hero, the actions and notice sit in\\n // it. Radii are static per element, so they are read once.\\n const els = Array.from(\\n document.querySelectorAll(\".header, .hero-action, .hero-notice\"),\\n ).slice(0, 8);\\n const radii = els.map((el) => parseFloat(getComputedStyle(el).borderTopLeftRadius) || 0);\\n const rects = new Float32Array(32);\\n const rads = new Float32Array(8);\\n return function frame(now, width, height) {\\n const hb = host.getBoundingClientRect();\\n if (hb.width === 0 || hb.height === 0) return;\\n const sx = width / hb.width;\\n const sy = height / hb.height;\\n let n = 0;\\n for (let i = 0; i < els.length && n < 8; i++) {\\n const r = els[i].getBoundingClientRect();\\n if (r.width === 0 || r.bottom <= hb.top || r.top >= hb.bottom) continue;\\n rects[n * 4] = (r.left + r.width / 2 - hb.left) * sx;\\n rects[n * 4 + 1] = (r.top + r.height / 2 - hb.top) * sy;\\n rects[n * 4 + 2] = (r.width / 2) * sx;\\n rects[n * 4 + 3] = (r.height / 2) * sy;\\n rads[n] = Math.min(radii[i], r.height / 2) * sx;\\n n++;\\n }\\n gl.uniform4fv(uRects, rects);\\n gl.uniform1fv(uRads, rads);\\n gl.uniform1i(uCount, n);\\n };\\n },\\n },\\n);\\n\\n}catch(e){console.warn(\"[ox-content] theme backdrop disabled:\",e)}})();';\n","import type { ThemeConfig } from \"@ox-content/vite-plugin\";\n\nimport { css } from \"./skin\";\nimport { js } from \"./gl\";\n\n/**\n * Liquid Glass — Refractive glass panels with specular edges and a light sweep on hover.\n *\n * Form only: geometry, texture, typography and motion. It names no colors, so\n * it composes with any `@ox-content/theme-color-*` package:\n *\n * ```ts\n * theme: [liquidGlass, tokyoNight]\n * ```\n */\nexport const liquidGlass: ThemeConfig = {\n name: \"liquid-glass\",\n fonts: {\n sans: 'ui-sans-serif, system-ui, -apple-system, \"Segoe UI Variable\", \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif',\n mono: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, \"Cascadia Mono\", Consolas, monospace',\n },\n layout: {\n sidebarWidth: \"268px\",\n headerHeight: \"66px\",\n maxContentWidth: \"940px\",\n },\n entryPage: { mode: \"default\" },\n tokens: {\n \"motion-fast\": \"220ms\",\n \"motion-base\": \"420ms\",\n \"motion-slow\": \"1000ms\",\n \"motion-ease\": \"cubic-bezier(0.22, 1, 0.36, 1)\",\n \"motion-spring\": \"cubic-bezier(0.34, 1.56, 0.64, 1)\",\n \"motion-rise\": \"0.75rem\",\n },\n css,\n js,\n};\n\nexport default liquidGlass;\n"],"mappings":";;;;;;;;;;;;AEeA,MAAa,cAA2B;CACtC,MAAM;CACN,OAAO;EACL,MAAM;EACN,MAAM;CACR;CACA,QAAQ;EACN,cAAc;EACd,cAAc;EACd,iBAAiB;CACnB;CACA,WAAW,EAAE,MAAM,UAAU;CAC7B,QAAQ;EACN,eAAe;EACf,eAAe;EACf,eAAe;EACf,eAAe;EACf,iBAAiB;EACjB,eAAe;CACjB;CACA;;;;;;;;;;;;;;;;;CACA;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ox-content/theme-liquid-glass",
3
- "version": "2.84.0",
3
+ "version": "2.87.0",
4
4
  "description": "Refractive glass panels with specular edges and a light sweep on hover — an Ox Content skin that composes with any @ox-content/theme-color-* scheme",
5
5
  "keywords": [
6
6
  "documentation",
@@ -44,7 +44,7 @@
44
44
  "@typescript/native-preview": "^7.0.0-dev.20260707.2",
45
45
  "typescript": "^7.0.2",
46
46
  "vite-plus": "0.2.8",
47
- "@ox-content/vite-plugin": "2.84.0"
47
+ "@ox-content/vite-plugin": "2.87.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@ox-content/vite-plugin": ">=2.84.0"