# Curved Input

> An input bar bent along an arc. The text, the caret and the submit button all follow the curve.

A real text input drawn as an SVG bent along a circular arc. Click the curve and type: the text flows along the arc, the blinking caret tilts with it, long values scroll along the curve like a native input, and a click drops the caret on the nearest character. Press Enter or the curved button to submit. A positive `bend` arches up, a negative one sags down, and `0` is flat. In the demo the input follows the site theme until you change the palette in the panel.

<demo-curved-input>



</demo-curved-input>

## Installation

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

## Usage

```vue
<script setup lang="ts">
  import { ref } from 'vue';
  import CurvedInput from '~/components/ui/curved-input/CurvedInput.vue';

  const email = ref('');

  function handleSubmit(value: string): void {
    console.log('Submitted:', value);
  }
</script>

<template>
  <CurvedInput
    v-model="email"
    placeholder="david@nxui.dev"
    button-text="Get Started"
    theme="dark"
    :bend="28"
    @submit="handleSubmit"
  />

  <!-- Light palette, sagging down, no button -->
  <CurvedInput
    theme="light"
    :bend="-24"
    :show-button="false"
    shadow-size="none"
    placeholder="Search"
    type="search"
  />
</template>
```

A hidden native `<input>` keeps focus, IME, autofill and selection behaviour. The visible surface is an SVG that maps a flat coordinate space onto the arc.

## Props

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

<tbody>
  <tr>
    <td>
      <code>
        placeholder
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Enter your email'
      </code>
    </td>
    
    <td>
      Placeholder text drawn along the curve when the input is empty.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        button-text
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'Get Started'
      </code>
    </td>
    
    <td>
      Label of the curved submit button.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        type
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        'email'
      </code>
    </td>
    
    <td>
      Input type. <code>
        email
      </code>
      
       and <code>
        number
      </code>
      
       open the matching mobile keyboard.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        name
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Name attribute passed to the hidden input.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aria-label
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        undefined
      </code>
    </td>
    
    <td>
      Accessible label. Falls back to the placeholder.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        theme
      </code>
    </td>
    
    <td>
      <code>
        'dark' | 'light'
      </code>
    </td>
    
    <td>
      <code>
        'dark'
      </code>
    </td>
    
    <td>
      Color preset. Any color prop you pass overrides its theme value.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        width
      </code>
    </td>
    
    <td>
      <code>
        number | string
      </code>
    </td>
    
    <td>
      <code>
        450
      </code>
    </td>
    
    <td>
      Width of the bar, in px or any CSS width (<code>
        '100%'
      </code>
      
      ). Never wider than its container.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bend
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        28
      </code>
    </td>
    
    <td>
      How far the middle of the bar rises above its ends, in px. Negative bends down.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        height
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        64
      </code>
    </td>
    
    <td>
      Thickness of the bar, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        corner-radius
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        18
      </code>
    </td>
    
    <td>
      Corner rounding of the bar. The button rounds in proportion.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        border-width
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Outline stroke width, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        font-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        16
      </code>
    </td>
    
    <td>
      Size of the text and the button label, in px.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        background-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      From theme
    </td>
    
    <td>
      Fill of the bar.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      From theme
    </td>
    
    <td>
      Color of the typed text and the caret.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        placeholder-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      From theme
    </td>
    
    <td>
      Color of the placeholder.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        border-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      From theme
    </td>
    
    <td>
      Color of the outline.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        button-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      From theme
    </td>
    
    <td>
      Fill of the button, the focus ring and the default icon chip.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        button-text-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      From theme
    </td>
    
    <td>
      Color of the button label.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        icon-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        button-color
      </code>
    </td>
    
    <td>
      Fill of the icon chip.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadow-size
      </code>
    </td>
    
    <td>
      <code>
        'none' | 'sm' | 'md' | 'lg'
      </code>
    </td>
    
    <td>
      <code>
        'md'
      </code>
    </td>
    
    <td>
      Size of the soft drop shadow under the bar.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        shadow-color
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      From theme
    </td>
    
    <td>
      Tint of the drop shadow.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-button
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Shows the curved submit button.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        show-icon
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Shows the leading icon.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes on the wrapping form.
    </td>
  </tr>
</tbody>
</table>

## v-model, events and slots

<table>
<thead>
  <tr>
    <th>
      Name
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        v-model
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The input value. Updates on every keystroke.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        submit
      </code>
      
       event
    </td>
    
    <td>
      <code>
        (value: string)
      </code>
    </td>
    
    <td>
      Fires when the button is pressed or Enter is hit.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        #icon
      </code>
      
       slot
    </td>
    
    <td>
      —
    </td>
    
    <td>
      SVG content for the icon, centered at the origin. Replaces the default mail chip.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- Typing goes into a real `<input>`, so screen readers, autofill and IME work as usual. Its label is `aria-label`, or the placeholder.
- The submit button is focusable, has `role="button"` and responds to Enter and Space.
- The focus ring around the bar shows while the input has focus.
- Ported from [React Bits](https://reactbits.dev/components/curved-input) by David Haz.
