Component
VHS
What happened?
Without html-in-canvas, VHS at default props is a clean no-op (0 px difference against a page without the component), which matches the fallback contract. But with barrel set to anything above zero, the fallback paints an opaque near-black field over the wrapped content. Headline, text and buttons are all invisible. The DOM underneath stays focusable and clickable, so DOM-level assertions still pass while the user sees nothing.
It behaves like a gate, not a distortion artifact: barrel: 0.000001 produces the identical blackout to 0.6 (mean luminance 254 to 3.2 in my harness, flat across five orders of magnitude). With the capability on, the same sweep stays at ~252 and content is fully legible, so the option itself works as designed. The bug is the delta between those two arms.
Where I think it lives (line numbers from the copied registry file, fetched 2026-08-07): the fallback contract is only implicit. Output alpha follows content alpha (L239), and uContent stays the transparent 1x1 placeholder because uploadContent() early-returns while !htmlInCanvas (L403). The barrel branch then forces alpha = 1.0 unconditionally (L243), and the bezel ring paints opaque independently (L168-171), so gating only the alpha line wouldn't be enough. uBarrel is uploaded ungated (L466) from the block that already has htmlInCanvas in scope. ParticleReveal and DecryptReveal handle this exact situation with uCrisp = reducedMotion || !htmlInCanvas ? 1 : 0; VHS has no equivalent.
All six docs demos use barrel: 0, which is probably why it hasn't come up.
Steps to reproduce
npx shadcn@latest add @canvas-ui/vhs-react
<VHS barrel={0.6}><h1>Hello</h1></VHS> over any visible content
- Open in Chrome without the flag or an origin-trial token. The content area renders as a near-black barrel-shaped field.
Browser and version
Chrome 151 / Chromium 151.0.7922.34, macOS
Is the html-in-canvas flag enabled?
No (WebGL overlay fallback)
Framework
React 19
Component
VHS
What happened?
Without html-in-canvas, VHS at default props is a clean no-op (0 px difference against a page without the component), which matches the fallback contract. But with
barrelset to anything above zero, the fallback paints an opaque near-black field over the wrapped content. Headline, text and buttons are all invisible. The DOM underneath stays focusable and clickable, so DOM-level assertions still pass while the user sees nothing.It behaves like a gate, not a distortion artifact:
barrel: 0.000001produces the identical blackout to0.6(mean luminance 254 to 3.2 in my harness, flat across five orders of magnitude). With the capability on, the same sweep stays at ~252 and content is fully legible, so the option itself works as designed. The bug is the delta between those two arms.Where I think it lives (line numbers from the copied registry file, fetched 2026-08-07): the fallback contract is only implicit. Output alpha follows content alpha (L239), and
uContentstays the transparent 1x1 placeholder becauseuploadContent()early-returns while!htmlInCanvas(L403). The barrel branch then forcesalpha = 1.0unconditionally (L243), and the bezel ring paints opaque independently (L168-171), so gating only the alpha line wouldn't be enough.uBarrelis uploaded ungated (L466) from the block that already hashtmlInCanvasin scope. ParticleReveal and DecryptReveal handle this exact situation withuCrisp = reducedMotion || !htmlInCanvas ? 1 : 0; VHS has no equivalent.All six docs demos use
barrel: 0, which is probably why it hasn't come up.Steps to reproduce
npx shadcn@latest add @canvas-ui/vhs-react<VHS barrel={0.6}><h1>Hello</h1></VHS>over any visible contentBrowser and version
Chrome 151 / Chromium 151.0.7922.34, macOS
Is the html-in-canvas flag enabled?
No (WebGL overlay fallback)
Framework
React 19