Input

Single-line text input with leading/trailing accessories and validation states.

Overview

The Input component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.

Why this exists

Text inputs are the backbone of every form. A poorly designed input creates friction, confusion, and accessibility failures. Byteion inputs are engineered with clear focus rings, validation states, and accessory slots (icon, clear button, error message) so every form field communicates its state without extra CSS.

Mental model

An input is a controlled text field: it accepts user input, validates against rules, and surfaces feedback. Pair every input with a <label> and optional helper/error text. The input itself is stateless — the surrounding wrapper communicates validation.

When to use

• Form fields in settings, sign-up, and checkout flows • Search bars and filter inputs in data tables • Inline-edit fields in editable tables • Inputs with leading icons for email, phone, or search patterns

When not to use

• Do not use a text input when a select, datepicker, or switch would reduce user effort • Avoid stacking inputs without labels — placeholder-only inputs fail accessibility • Do not use the error state for helper text — reserve red for actual validation failures

Basic example

The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.

Variants

Each variant communicates a different meaning. Choose based on priority, not aesthetics.

  • outline — outline style variant.
  • filled — filled style variant.

Sizes

Size should match the importance and context of the component.

  • sm — Small — filter bars, dense forms.
  • md — Default — standard form fields.
  • lg — lg size.
  • xl — xl size.

States

This component reacts seamlessly to the following interaction and data states:

  • default — Resting appearance before interaction.
  • hover — Pointer is over the element — visual feedback via color or shadow.
  • focus-visible — focus-visible state.
  • disabled — Non-interactive — grayed out, cursor not-allowed.
  • error — Validation failed — red border and error message.
  • success — Validation passed — green indicator.
  • invalid — Input value does not match expected format.

Anatomy

• Label — always visible, never placeholder-only • Input wrapper — provides border, radius, and focus ring • Leading icon — optional, signals input type (search, email, phone) • Input element — native <input> for full browser support • Trailing action — optional clear button or toggle (password visibility) • Helper/Error text — below the input, linked via aria-describedby

Usage guidelines

  • Always pair with a visible label.
  • Use helper text for format hints (e.g.,"MM/DD/YYYY").
  • Show error state only after the field has been touched or the form is submitted.
  • Use leading icons sparingly — only when they clarify the expected input type.

Do and Don't

Do

  • Always use a label, never placeholder-only.
  • Show errors after blur or submit, not on every keystroke.
  • Use type="email" or type="tel" for appropriate keyboard on mobile.

Don't

  • Do not use red helper text for non-error hints.
  • Do not disable inputs without explaining why (use readonly instead).
  • Do not stack unlabeled inputs in a row.

Accessibility

Inputs must have an associated <label for="id">. Error messages use aria-describedby linked to the input. Invalid state sets aria-invalid="true". Required fields set aria-required="true".

Role:textbox

ARIA Attributes

  • aria-invalid
  • aria-required
  • aria-describedby
  • aria-label
  • aria-readonly

Keyboard Navigation

  • Tab: Moves focus into and out of the field
  • Esc: Clears the field when type='search'

Customization

Change border color via --border. Change focus ring via --ring. Adjust height by modifying the wrapper classes.

Compose with React cva()

Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.

import { cva, cn, type VariantProps } from '@byteion/react';

const myInput = cva('base classes…', {
 variants: {
  accent: { soft:'border-primary-200', loud:'border-primary-500' },
 },
 defaultVariants: { accent:'soft' },
});

type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myInput>;

Compose with Vue cva()

Use the exportedcva() +cn() utilities inside standard Vue setup blocks.

<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';

interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();

const myInput = cva('base classes…', {
  variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});

const cls = computed(() => myInput({ accent: props.accent, class: attrs.class as string | undefined }));
</script>

Runtime events

Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.

HTML / JS events

No HTML runtime events are declared by this adapter.

React callbacks

No React callback events are declared by this adapter.

Vue emits

  • update:modelValue[value: string]

Remote / AJAX

This component does not implement Byteion JS remote loading. Use normal HTML form submission, framework state, or your application data layer around it.

API Reference

No data attributes required. For JS validation, use data-bi-component="input" with data-bi-state="error".

HTML Data Attributes

No custom data attributes required.

React Props · InputProps

Extracted directly fromreact/src/components/Input.tsx.

export interface InputProps
  extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size' | 'children'>,
    VariantProps<typeof inputStyles> {
  /** Visible field label. Auto-associates via id. */
  label?: ReactNode;
  /** Helper text shown below the field (gray). */
  helperText?: ReactNode;
  /** Error message. Sets aria-invalid + state=error automatically. */
  error?: ReactNode;
  /** Success message (rare; for confirmation states). */
  success?: ReactNode;
  /** Icon rendered inside the field on the left. */
  leadingIcon?: ReactNode;
  /** Icon or button rendered inside the field on the right. */
  trailingIcon?: ReactNode;
  /** Visually mark the field as required (`*` after the label). */
  required?: boolean;
}

React Variant Schema

Internal variant composition defined viacva().

cva(
  'w-full rounded-lg border bg-card text-foreground outline-none transition-colors duration-200 ease-out-soft shadow-field hover:shadow-field-hover focus-visible:shadow-field-focus dark:bg-input dark:shadow-field-dark dark:hover:shadow-field-dark-hover dark:focus-visible:shadow-field-focus-dark placeholder:text-muted-foreground placeholder:font-normal font-medium',
  {
    variants: {
      variant: {
        outline: 'border-border hover:border-border focus-visible:border-primary-400 dark:focus-visible:border-primary-500',
        filled:  'border-border bg-muted/60 hover:border-border hover:bg-muted focus-visible:border-primary-400 dark:border-border dark:bg-input dark:hover:border-border dark:hover:bg-input dark:focus-visible:border-primary-500',
      },
      size: {
        sm: 'h-9 rounded-md px-3 text-sm',
        md: 'h-10 px-3.5 text-sm',
        lg: 'h-11 px-4   text-base',
        xl: 'h-12 rounded-xl px-4 text-base',
      },
      state: {
        default: '',
        error:   '!border-danger-400 focus-visible:!shadow-field-error dark:focus-visible:!shadow-field-error-dark',
        success: '!border-success-400 focus-visible:!shadow-field-success dark:focus-visible:!shadow-field-success-dark',
      },
      hasLeading:  { true: '!pl-10', false: '' },
      hasTrailing: { true: '!pr-10', false: '' },
    },
    defaultVariants: { variant: 'outline', size: 'md', state: 'default', hasLeading: false, hasTrailing: false },
  },
)

Vue Props · InputProps

Extracted directly fromvue/src/components/Input.vue.

export interface InputProps {
  modelValue?: string | number;
  variant?: 'outline' | 'filled';
  size?: 'sm' | 'md' | 'lg' | 'xl';
  state?: 'default' | 'error' | 'success';
  label?: string;
  helperText?: string;
  error?: string;
  success?: string;
  /** Mark the field as required (visual `*` after label + `required` attr). */
  required?: boolean;
  type?: string;
  /** Use a different DOM id (otherwise auto-generated). */
  id?: string;
}

Real-world examples

See how this component composes in common application contexts.

In a form

We'll never share your email.

Developer tips

• Common mistake: showing validation errors on every keystroke — wait for blur. • Edge case: readonly inputs should look enabled but not accept input; disabled inputs look grayed out. • Best practice: use inputmode="numeric" for phone/zip fields to trigger the right mobile keyboard.

Common mistakes

Avoid hardcoded neutral colors, placeholder-only labels, duplicated primary actions, and missing keyboard states. Keep this component on Byteion tokens so dashboard, admin, POS, ERP, ecommerce back-office, Laravel, React, and Vue starter-kit screens can be rebranded without rewriting markup.

Production usage tips

Use the canonical HTML as the source of truth, keep React and Vue wrappers aligned with the documented variants, test dark mode and branded themes before release, and wire interactive behavior through data-bi-* attributes instead of styling hooks.

Premium examples

Advanced patterns available in Byteion Pro:

• Floating label pattern (label animates into placeholder on focus) • Password strength meter inside the input • Inline autocomplete with avatar suggestions

Unlock Premium Patterns

Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.

Explore Pro Components