# Fluid Glass

> A ray-marched glass object that bends your own headline, image or video, follows the cursor and melts between seven shapes.

A real glass object drawn over your content with raw WebGL2: the headline, image or video behind it is refracted, split into rainbow fringes and frosted, and the glass casts a shadow with focused light below it. Move the cursor and it glides after you, stretching like a liquid. Click it to melt it into the next shape. In `drag` mode you can pick it up and throw it against the edges.

<demo-fluid-glass>



</demo-fluid-glass>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/fluid-glass.json"
```

## Usage

```vue
<script setup lang="ts">
  import FluidGlass from '~/components/ui/fluid-glass/FluidGlass.vue';
</script>

<template>
  <div class="relative h-125 w-full overflow-hidden">
    <FluidGlass
      text="Look closer."
      shape="torus"
      interaction="follow"
      :size="260"
      :refraction="1"
      :dispersion="0.35"
      @shape-change="(shape) => console.log(shape)"
    />
  </div>
</template>
```

The glass sizes itself from its container, so give the parent a real height. It has no 3D models and no dependencies beyond `@vueuse/core`. Shader programs compile in the background (with `KHR_parallel_shader_compile` where available), and while the glass is at rest the loop draws fewer frames on high refresh rate screens.

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Look closer.'
      </code>
    </td>
    
    <td>
      Headline drawn behind the glass so the glass can bend it. Use <code>
        \n
      </code>
      
       for line breaks.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        src
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Image or video behind the glass. Videos (.mp4, .webm, .mov) play muted on a loop. Needs CORS headers.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fit
      </code>
    </td>
    
    <td>
      <code>
        'cover' | 'contain'
      </code>
    </td>
    
    <td>
      <code>
        'cover'
      </code>
    </td>
    
    <td>
      How the image or video fills the area.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'transparent'
      </code>
    </td>
    
    <td>
      Colour behind the text and media. Transparent lets the page show through.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      inherited
    </td>
    
    <td>
      Colour of the text. Defaults to the CSS <code>
        color
      </code>
      
       of the container.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-family
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      inherited
    </td>
    
    <td>
      Font of the text. Defaults to the font of the container.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-weight
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        600
      </code>
    </td>
    
    <td>
      Weight of the text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      auto
    </td>
    
    <td>
      Size of the text in px. By default it scales to fill the width.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shape
      </code>
    </td>
    
    <td>
      <code>
        'torus' | 'lens' | 'blob' | 'pill' | 'cube' | 'prism' | 'diamond'
      </code>
    </td>
    
    <td>
      <code>
        'torus'
      </code>
    </td>
    
    <td>
      Shape of the glass. Changing it melts the glass into the new shape. The blob keeps rippling.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shapes
      </code>
    </td>
    
    <td>
      <code>
        FluidGlassShape[]
      </code>
    </td>
    
    <td>
      all shapes
    </td>
    
    <td>
      Shapes the glass cycles through when clicked.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        morph-on-click
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking the glass melts it into the next shape.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        260
      </code>
    </td>
    
    <td>
      Diameter of the glass in px. It shrinks to fit small areas.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        interaction
      </code>
    </td>
    
    <td>
      <code>
        'follow' | 'drag' | 'static'
      </code>
    </td>
    
    <td>
      <code>
        'follow'
      </code>
    </td>
    
    <td>
      Glide after the cursor, pick up and throw, or stay in place.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        position
      </code>
    </td>
    
    <td>
      <code>
        [number, number]
      </code>
    </td>
    
    <td>
      <code>
        [0.5, 0.5]
      </code>
    </td>
    
    <td>
      Where the glass rests, as fractions of the width and height.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        refraction
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How strongly the glass bends light, 0 to 2. Around 0.66 behaves like water.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dispersion
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.35
      </code>
    </td>
    
    <td>
      How much light splits into rainbow fringes at the edges, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        frost
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Frosts the glass so what is behind it blurs, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#ffffff'
      </code>
    </td>
    
    <td>
      Colour of the glass.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tint
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      How strongly the glass takes on its colour. Thicker parts look deeper.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reflection
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Strength of the highlights and reflections, 0 to 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Strength of the shadow and focused light below the glass, 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        elevation
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      How high the glass floats. Higher magnifies more, until the image flips.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        smoothness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How lazily the glass follows, 0 (snappy) to 1 (floaty).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wobble
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How much the glass stretches when it moves and wobbles when it stops. 0 keeps it rigid.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spin
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.3
      </code>
    </td>
    
    <td>
      Idle rotation speed for shapes like the cube and torus. Negative values spin the other way.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        idle
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Drift gently around the resting place while nobody interacts.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Pop into place on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-device-pixel-ratio
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Highest pixel ratio the canvas renders at.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      —
    </td>
    
    <td>
      Extra classes for the container.
    </td>
  </tr>
</tbody>
</table>

## Events

<table>
<thead>
  <tr>
    <th>
      Event
    </th>
    
    <th>
      Payload
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        shape-change
      </code>
    </td>
    
    <td>
      <code>
        shape: FluidGlassShape
      </code>
    </td>
    
    <td>
      Fired when the glass starts melting to a shape.
    </td>
  </tr>
</tbody>
</table>

## Slots

<table>
<thead>
  <tr>
    <th>
      Slot
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        default
      </code>
    </td>
    
    <td>
      Content layered on top, such as buttons. It sits above the glass and is not bent by it.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is `aria-hidden`; the headline is also rendered as visually hidden text so screen readers read it.
- With `prefers-reduced-motion: reduce` the glass does not drift, spin, tilt or stretch, morphs are shorter and the intro pop is skipped.
- Rendering pauses when the glass is offscreen or the tab is hidden, and video playback pauses with it.
- Ported from [React Bits](https://reactbits.dev/components/fluid-glass) by David Haz.
