# super-input

A lightweight element that wraps and upgrades the native `<input>` element.


```html
<form>
  <super-input
    text-format="(xxx) xxx-xxxx"
    invalid-message="That doesn't look like a US phone number"
    auto-label
    class="raised"
  >
    <label>Phone number</label>
    <input required pattern="^\(\d{3}\)\s\d{3}-\d{4}" placeholder="(123) 456-7890" inputmode="numeric" />
  </super-input>
  <!-- Form and button exist purely for the demo -->
  <button type="submit">Submit</button>
</form>
```


## Features

- **Live text formatting** e.g. phone numbers, dates, SSNs
- **Custom, native validity messages** uses the browser's built-in validation UI to show your message
- **Auto-labeling** it stitches a sibling `<label>` to the `<input>` via `id`/`for`
- **Progressively enhanced** Does **not** replace the native input; it enhances it. Everything you know
about `<input>` still applies.
- **Range slider** ships with upgraded slider styling and functionality

## Installation


`@excom/super-input` v0.1.5

```bash
pnpm add @excom/super-input
```

```bash
npm install @excom/super-input
```

```bash
yarn add @excom/super-input
```

### Import

```ts
import "@excom/super-input";
```



## Usage

Wrap a native `<input>` and optionally a `<label>`. Nothing else is required. 

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description |
| --- | --- | --- | --- | --- | --- |
| `text-format` | option | `string` |  |  | Pattern defining visible formatting. Use `x` for any digit and any other character as a literal. Examples: `(xxx) xxx-xxxx`, `xx/xx/xxxx`, `xxx-xx-xxxx`. When set, the input value is live-formatted on every keystroke. |
| `invalid-message` | option | `string` |  |  | If set, this message is installed via `setCustomValidity()` when the native `invalid` event fires, and cleared on the next `keydown`. Makes built-in HTML validation surface a custom message. |
| `reflect-value` | option | `boolean` |  |  | When set, the current input value is mirrored to the `current-value` attribute so CSS and selectors can respond to it. Off by default because reflecting every keystroke is not free. |
| `current-value` | state | `string` |  |  | Mirrors the wrapped `<input>`'s value when `reflect-value` is set. Read-only from the app's point of view — writing it does not change the input. |
| `auto-label` | option | `boolean` |  |  | When set, generates or uses an existing `id` on the `<input>` and associates the `<label>`. |

#### Recognized Elements

| Relationship | Selector | Required | Description |
| --- | --- | --- | --- |
| `input` | descendant | yes | Required native `<input>` to enhance. |
| `label` | descendant | no | Optional native `<label>`. When `auto-label` is set, its `for` attribute is linked to the input's `id`. |

#### CSS Custom Properties

| Name | Syntax | Default | Description |
| --- | --- | --- | --- |
| `--super-input-color` | `<color>` | `var(--v-color, inherit)` | Foreground / label color. |
| `--super-input-bg` | `<color>` | `var(--v-form-element-background-color, transparent)` | Surface background behind the control (and raised-label mask). |
| `--super-input-border-color` | `<color>` | `var( --v-form-element-border-color, currentColor )` | Default border color for raised inputs. |
| `--super-input-focus-color` | `<color>` | `var( --v-form-element-focus-color, var(--super-input-border-color) )` | Focus ring / focus border color. |
| `--super-input-border-radius` | `<length>` | `var(--v-border-radius, 4px)` | Corner radius for raised inputs. |
| `--super-input-border-width` | `<length>` | `var(--v-border-width, 1px)` | Border width for raised inputs. |
| `--super-input-transition-duration` | `<time>` | `var(--v-transition-duration-fast, 0.15s)` | Duration for border / label transitions. |
| `--super-input-transition-ease` | `*` | `var(--v-transition-ease-out, ease-out)` | Easing for border / label transitions. |
| `--super-input-primary` | `<color>` | `var(--v-primary, blue)` | Accent color (range fill, thumb border, focus chrome). |
| `--super-input-label-margin-bottom` | `<length>` | `calc(21px * 0.375)` | Gap between a floating label and the control. |
| `--super-input-font-size` | `<length>` | `16px` | Base font size for the control and value readout. |
| `--super-input-label-height` | `<length>` | `calc( var(--v-line-height, 1.5) * var(--super-input-font-size) )` | Computed label line box height (used when a `<label>` is present). |
| `--super-input-height` | `<length>` | `calc(var(--super-input-thumb-size) + 36px)` | Host height for range layout (thumb + value readout). |
| `--super-input-width` | `<length> \| <percentage>` | `100%` | Host width for range layout. |
| `--super-input-min` | `<number>` | `0` | Range minimum. Prefer setting on the host (or via CSS): CSS cannot read the child input's `min` attribute from the parent. |
| `--super-input-max` | `<number>` | `100` | Range maximum. Prefer setting on the host (or via CSS). |
| `--super-input-value` | `<number>` | `50` | Current range value used for fill / readout positioning. With `reflect-value`, may be driven from `attr(current-value)` where typed `attr()` is supported. |
| `--super-input-thumb-size` | `<length>` | `44px` | Diameter of the range thumb. |
| `--super-input-thumb-color` | `<color>` | `var( --v-range-thumb-color, var(--super-input-bg) )` | Range thumb fill color. |
| `--super-input-accent` | `<color>` | `var(--super-input-primary, blue)` | Active track / accent color for the range fill. |
| `--super-input-track-height` | `<length>` | `2px` | Height of the range track. |
| `--super-input-font-weight` | `<number> \| <integer>` | `700` | Font weight for the range value readout. |

#### CSS Classes

| Name | Description |
| --- | --- |
| `.raised` | Raised / floating-label text field appearance. Apply as `class="raised"` on `<super-input>` wrapping a text-like `<input>`. |

#### CSS Aliases

| Alias | Kind | Matches | Description |
| --- | --- | --- | --- |
| `:--super-input` | element | `super-input`, `.tag-super-input` |  |
| `:--super-input--has-reflect-value` | state | `[reflect-value]`, `[data-reflect-value]` |  |
| `:--super-input--has-current-value` | state | `[current-value]`, `[data-current-value]` |  |



### Examples

#### Formatting input as the user types

Set `text-format` to a template using `x` as a character placeholder.


```html
<super-input text-format="(xxx) xxx-xxxx">
  <label>Phone number</label>
  <input value="8902340170" inputmode="numeric" />
</super-input>
```


Common templates:

- Phone (US): `(xxx) xxx-xxxx`
- Date: `xx/xx/xxxx`
- SSN: `xxx-xx-xxxx`

The wrapped `<input>` sees the formatted value. Pair with `pattern` for
validation.

#### Custom validity messages

Set `invalid-message` and the browser's native validation UI will surface it
when the input fails. Use the native `pattern`, `required`, `min`, `max`, etc for validation.
Try submitting the form in the demo below with an invalid phone number.


```html
<form>
  <super-input text-format="(xxx) xxx-xxxx" invalid-message="That doesn't look like a US phone number">
    <label>Phone number</label>
    <input required pattern="^\(\d{3}\)\s\d{3}-\d{4}" inputmode="numeric" />
  </super-input>
  <button type="submit">Submit</button>
</form>
```


The message is installed via `setCustomValidity()` on the `invalid` event and
cleared on the next `keydown`, so the input stops being marked invalid as
soon as the user tries again.

#### Using reflect-value
The `reflect-value` attribute has two primary uses:
- Is required for animating `raised` labels without an input `placeholder` attribute
- Allows you to hook into it to run your own behaviors

Here's an example with a raising label that will display an error if the user has not entered an email with an `@` or `.` characters.


```html
<super-input reflect-value class="raised" id="si-rv">
    <label>Email</label>
    <input type="email" />
    <style>
        super-input#si-rv {
            &::after { color: orange; }
            &:not([current-value*="."])::after {
                content: 'Email is missing "." character.';
            }
            &:not([current-value*="@"])::after {
                content: 'Email is missing "@" character.';
            }
        }
    </style>
</super-input>
```


#### Range slider
`<super-input>` ships with advanced slider CSS. Add `[type="range"]` to the `input`. `[reflect-value]` is required for this to work natively in Chromium browsers. Safari and Firefox will need some help via Quark or JS until they support [the CSS `type()` function](https://caniuse.com/mdn-css_types_type). You must also set the min/max CSS variables to match the min/max on the input.

Be cautious using this feature, as it aesthetically relies on non-standard pseudo elements for the time being.


```html
<div>
    <super-input reflect-value style="--super-input-min: 18; --super-input-max: 85;">
        <label>Age</label>
        <input type="range" name="age" step="1" value="36" min="18" max="85">
    </super-input>
    <!-- This is needed for Safari / Firefox -->
    <quark-sheet>
        super-input { --super-input-value: attr("current-value"); }
    </quark-sheet>
</div>
```

## Release notes

### 0.1.1 (2026-09-23)

- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
