Input
Input is a single-line text field. It renders a native <input> styled from the design tokens, with optional leading/trailing icons, three sizes, and a visual invalid state.
vue
<script setup lang="ts">
import { ref } from 'vue'
import { Input } from '@myghf/ui'
const email = ref('')
</script>
<template>
<Input v-model="email" type="email" placeholder="you@example.com" />
</template>Examples
Basics
Any extra attribute — type, id, name, autocomplete, aria-* — falls through to the native <input>, so Input works with an external <label for>.
vue
<script setup lang="ts">
import { ref } from 'vue'
import { Input } from '@myghf/ui'
const email = ref('')
const search = ref('')
const invalidValue = ref('not-an-email')
</script>
<template>
<div class="grid max-w-md gap-4">
<div class="grid gap-1.5">
<label class="text-sm font-medium text-foreground" for="demo-input-email">Email</label>
<Input id="demo-input-email" v-model="email" type="email" placeholder="you@example.com" />
</div>
<Input v-model="search" leading-icon="search" placeholder="Search patients" />
<Input
v-model="invalidValue"
invalid
leading-icon="circle-alert"
aria-label="Invalid field"
/>
</div>
</template>Sizes
sm, default, and lg set the control height. invalid switches the border and focus ring to error-500; it is a visual flag only (see Accessibility).
vue
<script setup lang="ts">
import { ref } from 'vue'
import { Input } from '@myghf/ui'
const value = ref('Aswan Heart Centre')
</script>
<template>
<div class="grid max-w-md gap-3">
<Input v-model="value" size="sm" aria-label="Small input" />
<Input v-model="value" aria-label="Default input" />
<Input v-model="value" size="lg" aria-label="Large input" />
</div>
</template>Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | number | null | — | Bound value, used as the input's value. |
placeholder | string | — | Placeholder text shown while empty. |
disabled | boolean | false | Disables the input and blocks interaction. |
invalid | boolean | false | Applies the error border and focus ring. Visual only; it does not set aria-invalid. |
leadingIcon | string | — | Lucide icon name rendered before the text (decorative). |
trailingIcon | string | — | Lucide icon name rendered after the text (decorative). |
size | 'sm' | 'default' | 'lg' | 'default' | Control height (h-8 / h-9 / h-10). |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | string | Emitted on every input event with the current string value. |
Slots
Input renders no slots. Use leadingIcon / trailingIcon for adornments.
Exposed methods
| Method | Description |
|---|---|
focus() | Focuses the underlying <input>. |
Accessibility
- The control is a real
<input>, so it participates in native form submission and label association. Pair it with a visible<label for="…">and give the input a matchingid. - Every extra attribute falls through, so
aria-label,aria-describedby, andaria-invalidcan be set directly:vue<Input v-model="email" :invalid="!!error" :aria-invalid="!!error" aria-describedby="email-error" /> invalidonly changes colour. It does not setaria-invalidor announce the error — setaria-invalidyourself and describe the failure witharia-describedby.- The leading and trailing icons are rendered by
Icon, which is alwaysaria-hidden, so they never contribute to the accessible name.
Dark mode & RTL
- The field styles from semantic tokens (
bg-surface,text-foreground,border-border,placeholder:text-muted), so it adapts to dark mode automatically. The focus ring usesprimary-500; the invalid state useserror-500in both themes. - Icons are positioned with logical utilities (
start-3/end-3) and text padding usesps-*/pe-*, so the adornments flip to the correct edge under RTL. In an RTL form setdir="rtl"on an ancestor and the input text aligns to the start automatically.