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
When not to use
Basic example
The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.
With icons & actions
Icon on the leading side.
Icon on the trailing side.
Action button inside the input.
Clear text with one click.
Input groups
Combine text or icons with inputs.
Fixed prefix attached to the input.
Fixed suffix attached to the input.
Both prefix and suffix together.
Useful for currency, date, and more.
Please enter a valid email address.
Domain is available.
import { useState } from 'react';
import { Input } from '@byteion/react';
export default function InputDemo() {
const [email, setEmail] = useState('');
return (
<div className="grid max-w-md gap-4 p-6">
<Input
label="Email"
type="email"
placeholder="you@company.com"
helperText="We'll never share your email."
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input label="Password" type="password" placeholder="At least 8 characters" required />
<Input label="Search" leadingIcon={<i className="ti ti-search" />} placeholder="Find a project" />
<Input
label="Username"
defaultValue="ada-lovelace"
state="error"
helperText="That username is already taken."
/>
<Input label="Read-only" defaultValue="acme-corp" readOnly />
</div>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { Input } from '@byteion/vue';
const email = ref('');
</script>
<template>
<div class="grid max-w-md gap-4 p-6">
<Input v-model="email" label="Email" type="email"
placeholder="you@company.com"
helper-text="We'll never share your email." />
<Input label="Password" type="password" placeholder="At least 8 characters" required />
<Input label="Search" placeholder="Find a project">
<template #leading><i class="ti ti-search" /></template>
</Input>
<Input label="Username" model-value="ada-lovelace" state="error"
helper-text="That username is already taken." />
<Input label="Read-only" model-value="acme-corp" readonly />
</div>
</template>
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
<input> for full browser support
• Trailing action — optional clear button or toggle (password visibility)
• Helper/Error text — below the input, linked via aria-describedbyUsage 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-invalidaria-requiredaria-describedbyaria-labelaria-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 mistakes
Production usage tips
data-bi-* attributes instead of styling hooks.
Premium examples
Advanced patterns available in Byteion Pro:
Related components
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.
Explore Pro Components