# Elastic Mesh

> Turns text or any logo into a puffy inflated WebGL2 shape you can press, poke and stretch like jelly.

Text or a logo is inflated into a soft 3D surface lit by a studio environment. Hover to dent it, tap to poke it, and drag to pull it — it stretches, then snaps back with a jiggle. The surface tilts toward the cursor and each letter pops in on mount.

<demo-elastic-mesh>



</demo-elastic-mesh>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import ElasticMesh from '~/components/ui/elastic-mesh/ElasticMesh.vue';
</script>

<template>
  <div class="relative h-125 w-full">
    <ElasticMesh
      text="Squish"
      material="jelly"
      color="#ff5a1f"
      :stiffness="0.35"
      :wobble="0.85"
      :grab-radius="70"
      theme="dark"
    />
  </div>
</template>
```

The component fills its parent, so give the parent a height. Pass `src` with an SVG or PNG URL to inflate a logo instead of text; its transparent pixels define the shape.

## 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>
        'Squish'
      </code>
    </td>
    
    <td>
      Text to inflate. Use <code>
        \n
      </code>
      
       for more lines.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        src
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      URL of an SVG or PNG to inflate instead of text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-family
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'system-ui, sans-serif'
      </code>
    </td>
    
    <td>
      Font used for the text. Heavy, rounded faces inflate best.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-weight
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        900
      </code>
    </td>
    
    <td>
      Weight of the text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#3b5bff'
      </code>
    </td>
    
    <td>
      Base color of the material.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        image-colors
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Keeps the colors of the image passed to <code>
        src
      </code>
      
       instead of using <code>
        color
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        material
      </code>
    </td>
    
    <td>
      <code>
        'balloon' | 'chrome' | 'jelly' | 'clay'
      </code>
    </td>
    
    <td>
      <code>
        'balloon'
      </code>
    </td>
    
    <td>
      Glossy balloon, mirror chrome, glowing jelly or soft matte clay.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        inflate
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How puffy the shape is. 1 gives round tubes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stiffness
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      0 is soft and stretchy, 1 is firm and quick.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        wobble
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      How long it jiggles after a poke or a pull, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grab-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        70
      </code>
    </td>
    
    <td>
      Size of the area the cursor grabs and presses, in pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        stretch
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      How far the material can be pulled, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        press
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Depth of the dent under the cursor and of a poke.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadow
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.5
      </code>
    </td>
    
    <td>
      Strength of the soft drop shadow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Inflates the shape letter by letter on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        theme
      </code>
    </td>
    
    <td>
      <code>
        'dark' | 'light'
      </code>
    </td>
    
    <td>
      <code>
        'dark'
      </code>
    </td>
    
    <td>
      Lighting setup for dark or light pages.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dpr
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      auto
    </td>
    
    <td>
      Maximum device pixel ratio, capped at 2.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes merged onto the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The container is `role="img"` with the text (or "Elastic logo") as its label.
- It is focusable: press <kbd>

Enter

</kbd>

 or <kbd>

Space

</kbd>

 to poke the next letter, with a visible focus ring.
- With `prefers-reduced-motion: reduce`, the intro and cursor tilt are skipped and the springs are heavily damped.
- Rendering pauses while the component is offscreen or the tab is hidden, and stops once the surface settles.
- Ported from [React Bits](https://reactbits.dev/animations/elastic-mesh) by David Haz.
