---
title: Layout and positioning
description: Size and place a shader in your page with CSS, as a background, a fill, or an inline block.
icon: file-code
category: code
---

# Layout and positioning

Size and place a shader in your page with CSS, as a background, a fill, or an inline block.

## Sizing the canvas

A `<Shader>` renders one `<div>` with one `<canvas>` inside that fills it. However many components you add, there's still one canvas. The classes and styles you pass go on the `<div>`, so you size it like any other block element:

```vue-html
<!-- Tailwind -->
<Shader class="w-full h-64">
  <LinearGradient />
</Shader>

<!-- CSS class -->
<Shader class="hero-shader">
  <LinearGradient />
</Shader>

<!-- Inline -->
<Shader style="width: 600px; height: 400px;">
  <LinearGradient />
</Shader>
```

```jsx
<Shader className="w-full h-64">
  <LinearGradient />
</Shader>
```

```tsx
<Shader class="w-full h-64">
  <LinearGradient />
</Shader>
```

```tsx
<Shader class="w-full h-64">
  <LinearGradient />
</Shader>
```

```javascript
// Apply sizing to the canvas element before calling createShader
const canvas = document.getElementById('my-shader')
canvas.style.width = '100%'
canvas.style.height = '400px'

const shader = await createShader(canvas, { components: [...] })
```

In the JS API you pass your own `<canvas>`, so you style it directly.

## Covering the whole page

To fix a shader behind all page content, covering the viewport, give it `fixed inset-0` and a negative `z-index`:

```vue-html
<!-- In your layout or App.vue -->
<Shader class="fixed inset-0 -z-10">
  <Aurora />
</Shader>

<main class="relative z-10">
  <!-- Your page content scrolls over the shader -->
</main>
```

```jsx
<>
  <Shader className="fixed inset-0 -z-10">
    <Aurora />
  </Shader>
  <main className="relative z-10">
    {/* Your page content */}
  </main>
</>
```

```tsx
<Shader class="fixed inset-0 -z-10">
  <Aurora />
</Shader>

<main class="relative z-10">
  <!-- Your page content -->
</main>
```

```tsx
<>
  <Shader class="fixed inset-0 -z-10">
    <Aurora />
  </Shader>
  <main class="relative z-10">
    {/* Your page content */}
  </main>
</>
```

```html
<canvas id="bg" style="position:fixed;inset:0;z-index:-10;width:100%;height:100%;"></canvas>
<main style="position:relative;z-index:10;">Your content</main>

<script type="module">
  import { createShader } from 'shaders/js'
  const shader = await createShader(document.getElementById('bg'), {
    components: [{ type: 'Aurora', props: {} }]
  })
</script>
```

## Filling a section

To fill one section without affecting the rest of the page, position the shader absolutely inside it. The section needs `position: relative` and `overflow: hidden` to contain the canvas, and `isolation: isolate` (`isolate`) so the negative `z-index` stays inside the section instead of dropping behind its background:

```vue-html
<section class="relative isolate overflow-hidden py-24 px-8">
  <Shader class="absolute inset-0 -z-10">
    <Plasma />
  </Shader>

  <h2 class="relative text-4xl font-bold text-white">Section Heading</h2>
  <p class="relative text-white/80 mt-4">Content above the shader.</p>
</section>
```

```jsx
<section className="relative isolate overflow-hidden py-24 px-8">
  <Shader className="absolute inset-0 -z-10">
    <Plasma />
  </Shader>
  <h2 className="relative text-4xl font-bold text-white">Section Heading</h2>
  <p className="relative text-white/80 mt-4">Content above the shader.</p>
</section>
```

```tsx
<section class="relative isolate overflow-hidden py-24 px-8">
  <Shader class="absolute inset-0 -z-10">
    <Plasma />
  </Shader>
  <h2 class="relative text-4xl font-bold text-white">Section Heading</h2>
</section>
```

```tsx
<section class="relative isolate overflow-hidden py-24 px-8">
  <Shader class="absolute inset-0 -z-10">
    <Plasma />
  </Shader>
  <h2 class="relative text-4xl font-bold text-white">Section Heading</h2>
</section>
```

## Filling a card

The same pattern works as the background of a card or panel. `overflow: hidden` also clips the canvas to the card's rounded corners:

```vue-html
<div class="relative rounded-2xl overflow-hidden p-8 text-white">
  <Shader class="absolute inset-0">
    <SolidColor color="#1e1b4b" />
    <Glow :intensity="0.4" color="#6366f1" />
  </Shader>

  <!-- Content sits above the shader -->
  <div class="relative z-10">
    <h3 class="text-xl font-semibold">Card Title</h3>
    <p class="text-white/70 mt-2">Card description here.</p>
  </div>
</div>
```

```jsx
<div className="relative rounded-2xl overflow-hidden p-8 text-white">
  <Shader className="absolute inset-0">
    <SolidColor color="#1e1b4b" />
    <Glow intensity={0.4} color="#6366f1" />
  </Shader>
  <div className="relative z-10">
    <h3 className="text-xl font-semibold">Card Title</h3>
    <p className="text-white/70 mt-2">Card description here.</p>
  </div>
</div>
```

```tsx
<div class="relative rounded-2xl overflow-hidden p-8 text-white">
  <Shader class="absolute inset-0">
    <SolidColor color="#1e1b4b" />
    <Glow intensity={0.4} color="#6366f1" />
  </Shader>
  <div class="relative z-10">
    <h3 class="text-xl font-semibold">Card Title</h3>
  </div>
</div>
```

```tsx
<div class="relative rounded-2xl overflow-hidden p-8 text-white">
  <Shader class="absolute inset-0">
    <SolidColor color="#1e1b4b" />
    <Glow intensity={0.4} color="#6366f1" />
  </Shader>
  <div class="relative z-10">
    <h3 class="text-xl font-semibold">Card Title</h3>
  </div>
</div>
```

## Placing a shader inline

Without positioning, a shader flows in the document like an image or video. Give it a width and an aspect ratio:

```vue-html
<article>
  <p>Text before the shader.</p>

  <Shader class="w-full aspect-video my-8 rounded-xl">
    <Swirl />
  </Shader>

  <p>Text after the shader continues here.</p>
</article>
```

```jsx
<article>
  <p>Text before the shader.</p>
  <Shader className="w-full aspect-video my-8 rounded-xl">
    <Swirl />
  </Shader>
  <p>Text after the shader continues here.</p>
</article>
```

```tsx
<article>
  <p>Text before the shader.</p>
  <Shader class="w-full aspect-video my-8 rounded-xl">
    <Swirl />
  </Shader>
  <p>Text after the shader continues here.</p>
</article>
```

```tsx
<article>
  <p>Text before the shader.</p>
  <Shader class="w-full aspect-video my-8 rounded-xl">
    <Swirl />
  </Shader>
  <p>Text after the shader continues here.</p>
</article>
```

## Letting clicks pass through

The canvas captures pointer events like any element. When the shader is decorative, add `pointer-events: none` so clicks and hovers reach the content underneath:

```vue-html
<Shader class="absolute inset-0 pointer-events-none">
  <Aurora />
</Shader>
```

```jsx
<Shader className="absolute inset-0 pointer-events-none">
  <Aurora />
</Shader>
```

```tsx
<Shader class="absolute inset-0 pointer-events-none">
  <Aurora />
</Shader>
```

```tsx
<Shader class="absolute inset-0 pointer-events-none">
  <Aurora />
</Shader>
```

Cursor effects such as `CursorTrail` and `CursorRipples` keep working with `pointer-events: none`, because they listen on the window, not the canvas.

## Stacking content above the canvas

`z-index` only works on positioned elements. Give any content you want above the canvas `position: relative` (or another position) and a higher `z-index`:

```html
<div class="relative">
  <!-- canvas at z-0 -->
  <canvas class="absolute inset-0"></canvas>
  <!-- content above canvas -->
  <div class="relative z-10">I appear above the canvas</div>
</div>
```

## Resizing at breakpoints

The canvas follows any resize that comes from CSS, including breakpoints, aspect ratios, and viewport units:

```vue-html
<!-- Different heights at breakpoints -->
<Shader class="w-full h-48 md:h-72 lg:h-screen">
  <LinearGradient />
</Shader>

<!-- Aspect ratio preserving at any width -->
<Shader class="w-full aspect-square md:aspect-video">
  <Swirl />
</Shader>

<!-- Dynamic viewport height (mobile-safe) -->
<Shader class="w-full h-[100dvh]">
  <Aurora />
</Shader>
```

```jsx
<Shader className="w-full h-48 md:h-72 lg:h-screen">
  <LinearGradient />
</Shader>
```

```tsx
<Shader class="w-full h-48 md:h-72 lg:h-screen">
  <LinearGradient />
</Shader>
```

```tsx
<Shader class="w-full h-48 md:h-72 lg:h-screen">
  <LinearGradient />
</Shader>
```

## Styling the canvas

Borders, radii, shadows and CSS filters apply as they would to any element:

```vue-html
<!-- Rounded with border -->
<Shader class="rounded-2xl border border-white/10 w-full h-48">
  <LinearGradient />
</Shader>

<!-- Drop shadow -->
<Shader class="shadow-2xl shadow-indigo-500/30 rounded-xl w-64 h-64">
  <Plasma />
</Shader>

<!-- CSS blur (applied after GPU render) -->
<Shader class="blur-sm w-full h-32">
  <LinearGradient />
</Shader>
```

```jsx
<Shader className="rounded-2xl border border-white/10 w-full h-48">
  <LinearGradient />
</Shader>
```

```tsx
<Shader class="rounded-2xl border border-white/10 w-full h-48">
  <LinearGradient />
</Shader>
```

```tsx
<Shader class="rounded-2xl border border-white/10 w-full h-48">
  <LinearGradient />
</Shader>
```

## Next steps

- [Transforms](/docs/guide/transforms): position, size, and rotate a layer inside the canvas.
- [Performance](/docs/guide/performance): what a large or offscreen canvas costs.
- [Composing effects](/docs/guide/composing-effects): stack and nest the components inside a `<Shader>`.