Text Input
Single-line text field with label, placeholder, helper text, icons, validation states, and character count.
Default
Standard input with a fieldset wrapper, legend label, and helper text below.
<fieldset class="fieldset">
<legend class="fieldset-legend">Full Name</legend>
<input type="text" class="input w-full" placeholder="e.g. John Doe" />
<p class="fieldset-label">Enter your legal name as it appears on your ID.</p>
</fieldset>
Sizes
Three size variants — input-sm, input-md (default), input-lg. Choose based on surrounding content density.
<input type="text" class="input input-sm w-full" placeholder="Small" />
<input type="text" class="input input-md w-full" placeholder="Medium" />
<input type="text" class="input input-lg w-full" placeholder="Large" />
Floating Label
Label animates up when the field is focused or filled. Implemented with Alpine.js state + Tailwind transitions — no custom CSS needed.
<div class="relative"
x-data="{ focused: false, filled: false }"
@focusin="focused = true"
@focusout="focused = false; filled = $el.querySelector('input').value.length > 0">
<input
type="text"
class="input w-full pt-5 pb-1"
@input="filled = $el.value.length > 0"
placeholder=" "
/>
<label class="absolute left-3 pointer-events-none transition-all duration-150 origin-left"
:class="focused || filled
? 'top-1.5 text-[0.7rem] text-primary font-medium'
: 'top-1/2 -translate-y-1/2 text-sm text-base-content/50'">
Field label
</label>
</div>
Prefix & Suffix Icons
Icons or addons inside the input using DaisyUI's label.input wrapper — no extra layout code required.
<!-- Prefix icon -->
<label class="input">
<i data-lucide="search" class="w-4 h-4 opacity-50 shrink-0"></i>
<input type="search" placeholder="Search…" />
</label>
<!-- Suffix icon -->
<label class="input">
<input type="text" placeholder="yourhandle" />
<i data-lucide="at-sign" class="w-4 h-4 opacity-50 shrink-0"></i>
</label>
<!-- Password toggle (Alpine.js) -->
<label class="input" x-data="{ show: false }">
<input :type="show ? 'text' : 'password'" placeholder="••••••••" class="grow" />
<button type="button" @click="show = !show">
<i :data-lucide="show ? 'eye-off' : 'eye'" class="w-4 h-4"></i>
</button>
</label>
Character Count
Live character counter with a configurable max. The counter turns red when the limit is exceeded — no custom CSS, just Tailwind conditional classes via Alpine.
<fieldset class="fieldset" x-data="{ val: '', max: 50 }">
<legend class="fieldset-legend">Product Title</legend>
<input type="text"
class="input w-full"
:class="val.length > max ? 'input-error' : ''"
x-model="val" />
<p class="fieldset-label flex justify-between">
<span>Keep it short and descriptive</span>
<span :class="val.length > max ? 'text-error font-semibold' : 'text-base-content/50'"
x-text="val.length + ' / ' + max"></span>
</p>
</fieldset>
Validation States
Error and success states using DaisyUI modifier classes. Pair with fieldset-label for contextual feedback messages.
<!-- Error -->
<fieldset class="fieldset">
<legend class="fieldset-legend">Email</legend>
<label class="input input-error w-full">
<i data-lucide="alert-circle" class="w-4 h-4 text-error"></i>
<input type="email" value="not-an-email" />
</label>
<p class="fieldset-label text-error">Please enter a valid email address.</p>
</fieldset>
<!-- Success -->
<fieldset class="fieldset">
<legend class="fieldset-legend">Username</legend>
<label class="input input-success w-full">
<i data-lucide="check-circle" class="w-4 h-4 text-success"></i>
<input type="text" value="john_doe" />
</label>
<p class="fieldset-label text-success">Username is available.</p>
</fieldset>
Readonly & Disabled
Use readonly when the value is displayable but not editable. Use disabled when the field is entirely inactive. Both reduce visual weight automatically via DaisyUI.
<!-- Readonly: value visible, not editable -->
<input type="text" class="input w-full" value="ORD-0042" readonly />
<!-- Disabled: full interaction blocked -->
<input type="text" class="input w-full" value="LAUNCH25" disabled />
Live Validation
Inline validation that runs on blur. State machine keeps feedback silent until the user has interacted — avoids premature error messages.
<fieldset class="fieldset"
x-data="{
email: '',
touched: false,
get isValid() { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email); },
get showError() { return this.touched && this.email.length > 0 && !this.isValid; },
get showSuccess() { return this.touched && this.isValid; }
}">
<legend class="fieldset-legend">Email Address</legend>
<label class="input w-full"
:class="showError ? 'input-error' : showSuccess ? 'input-success' : ''">
<input type="email"
placeholder="you@example.com"
x-model="email"
@blur="touched = true"
class="grow" />
<i x-show="showSuccess" data-lucide="check" class="w-4 h-4 text-success"></i>
<i x-show="showError" data-lucide="x" class="w-4 h-4 text-error"></i>
</label>
<p class="fieldset-label" :class="showError ? 'text-error' : showSuccess ? 'text-success' : ''">
<span x-show="showError">Please enter a valid email address.</span>
<span x-show="showSuccess">Looks good!</span>
</p>
</fieldset>