Input Components
Beautiful, accessible input components built with TailwindCSS. Perfect for forms, search bars, and user interactions. Copy, paste, and customize for React, Vue, or HTML.
1. Minimal Input
A clean, minimal input with subtle border and focus states. Perfect for simple forms and clean interfaces.
<div>
<label for="minimal-input" class="block text-sm font-medium text-gray-700 mb-2">Email Address</label>
<input type="email" id="minimal-input" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="you@example.com">
</div>
<div>
<label for="minimal-password" class="block text-sm font-medium text-gray-700 mb-2">Password</label>
<input type="password" id="minimal-password" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="••••••••">
</div>
<template>
<div class="space-y-6">
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-2">Email Address</label>
<input
type="email"
id="email"
v-model="email"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="you@example.com"
>
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700 mb-2">Password</label>
<input
type="password"
id="password"
v-model="password"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="••••••••"
>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const email = ref('')
const password = ref('')
</script>
import React, { useState } from 'react'
export default function MinimalInput() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
return (
<div className="space-y-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
Email Address
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="you@example.com"
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-2">
Password
</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="••••••••"
/>
</div>
</div>
)
}
2. Input with Heroicons
Enhanced inputs with Heroicons for better visual hierarchy and user experience. Includes search and user icons.
<div class="relative">
<label for="search-input" class="block text-sm font-medium text-gray-700 mb-2">Search</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
<input type="text" id="search-input" class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="Search...">
</div>
</div>
<div class="relative">
<label for="user-input" class="block text-sm font-medium text-gray-700 mb-2">Full Name</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
</div>
<input type="text" id="user-input" class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors" placeholder="John Doe">
</div>
</div>
<template>
<div class="space-y-6">
<div class="relative">
<label for="search" class="block text-sm font-medium text-gray-700 mb-2">Search</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
<input
type="text"
id="search"
v-model="searchQuery"
class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="Search..."
>
</div>
</div>
<div class="relative">
<label for="fullName" class="block text-sm font-medium text-gray-700 mb-2">Full Name</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
</div>
<input
type="text"
id="fullName"
v-model="fullName"
class="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="John Doe"
>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const searchQuery = ref('')
const fullName = ref('')
</script>
import React, { useState } from 'react'
export default function InputWithIcons() {
const [searchQuery, setSearchQuery] = useState('')
const [fullName, setFullName] = useState('')
return (
<div className="space-y-6">
<div className="relative">
<label htmlFor="search" className="block text-sm font-medium text-gray-700 mb-2">
Search
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<input
type="text"
id="search"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="Search..."
/>
</div>
</div>
<div className="relative">
<label htmlFor="fullName" className="block text-sm font-medium text-gray-700 mb-2">
Full Name
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
<input
type="text"
id="fullName"
value={fullName}
onChange={(e) => setFullName(e.target.value)}
className="w-full pl-10 pr-3 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 focus:border-violet-500 transition-colors"
placeholder="John Doe"
/>
</div>
</div>
</div>
)
}
3. Neobrutalist Text Input
Bold, modern input with thick borders, shadows, and vibrant focus states.
<!-- Basic Neobrutalist Input -->
<div>
<label class="block text-sm font-black uppercase tracking-wider mb-2" for="email">Email Address</label>
<input
type="email"
id="email"
placeholder="your@email.com"
class="w-full px-4 py-3 border-4 border-black bg-white text-black placeholder-gray-500 font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:shadow-[6px_6px_0px_0px_rgba(139,92,246,1)] focus:border-violet focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]"
>
</div>
<!-- Colored Variant -->
<div>
<label class="block text-sm font-black uppercase tracking-wider mb-2" for="name">Full Name</label>
<input
type="text"
id="name"
placeholder="John Doe"
class="w-full px-4 py-3 border-4 border-black bg-[#ffde59] text-black placeholder-gray-700 font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:shadow-[6px_6px_0px_0px_rgba(255,92,92,1)] focus:border-[#ff5c5c] focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]"
>
</div>
<template>
<div>
<label :for="inputId" class="block text-sm font-black uppercase tracking-wider mb-2">
{{ label }}
</label>
<input
:type="type"
:id="inputId"
:placeholder="placeholder"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
:class="inputClasses"
>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
label: String,
type: { type: String, default: 'text' },
placeholder: String,
modelValue: String,
variant: { type: String, default: 'default' },
inputId: String
})
const emit = defineEmits(['update:modelValue'])
const inputClasses = computed(() => {
const baseClasses = 'w-full px-4 py-3 border-4 border-black font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]'
const variants = {
default: 'bg-white text-black placeholder-gray-500 focus:shadow-[6px_6px_0px_0px_rgba(139,92,246,1)] focus:border-violet',
yellow: 'bg-[#ffde59] text-black placeholder-gray-700 focus:shadow-[6px_6px_0px_0px_rgba(255,92,92,1)] focus:border-[#ff5c5c]'
}
return `${baseClasses} ${variants[props.variant] || variants.default}`
})
</script>
import React from 'react'
const NeobrutalistInput = ({
label,
type = 'text',
placeholder,
value,
onChange,
variant = 'default',
id,
...props
}) => {
const getInputClasses = () => {
const baseClasses = 'w-full px-4 py-3 border-4 border-black font-medium shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] focus:outline-none transition-all duration-200 hover:shadow-[5px_5px_0px_0px_rgba(0,0,0,1)]'
const variants = {
default: 'bg-white text-black placeholder-gray-500 focus:shadow-[6px_6px_0px_0px_rgba(139,92,246,1)] focus:border-violet',
yellow: 'bg-[#ffde59] text-black placeholder-gray-700 focus:shadow-[6px_6px_0px_0px_rgba(255,92,92,1)] focus:border-[#ff5c5c]'
}
return `${baseClasses} ${variants[variant] || variants.default}`
}
return (
<div>
{label && (
<label htmlFor={id} className="block text-sm font-black uppercase tracking-wider mb-2">
{label}
</label>
)}
<input
type={type}
id={id}
placeholder={placeholder}
value={value}
onChange={onChange}
className={getInputClasses()}
{...props}
/>
</div>
)
}
export default NeobrutalistInput
4. Floating Label Input
Elegant floating label design with smooth animations and modern styling.
<!-- Text Input with Floating Label -->
<div class="relative">
<input type="email" id="floating-email" class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer" placeholder=" " />
<label for="floating-email" class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1">Email address</label>
</div>
<!-- Password Input with Floating Label -->
<div class="relative">
<input type="password" id="floating-password" class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer" placeholder=" " />
<label for="floating-password" class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1">Password</label>
</div>
<!-- Textarea with Floating Label -->
<div class="relative">
<textarea id="floating-message" rows="4" class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer resize-none" placeholder=" "></textarea>
<label for="floating-message" class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-6 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1">Your message</label>
</div>
<template>
<div class="relative">
<input
:type="type"
:id="inputId"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
class="block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer"
placeholder=" "
/>
<label
:for="inputId"
:class="labelClasses"
>
{{ label }}
</label>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
label: { type: String, required: true },
type: { type: String, default: 'text' },
modelValue: String,
inputId: { type: String, required: true },
isTextarea: { type: Boolean, default: false }
})
const emit = defineEmits(['update:modelValue'])
const labelClasses = computed(() => {
const baseClasses = 'absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1'
return props.isTextarea
? `${baseClasses} peer-placeholder-shown:top-6`
: `${baseClasses} peer-placeholder-shown:top-1/2`
})
</script>
import React from 'react'
const FloatingLabelInput = ({
label,
type = 'text',
value,
onChange,
id,
isTextarea = false,
rows = 4,
...props
}) => {
const getLabelClasses = () => {
const baseClasses = 'absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-2 z-10 origin-[0] bg-white px-2 peer-focus:px-2 peer-focus:text-violet peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-focus:top-2 peer-focus:scale-75 peer-focus:-translate-y-4 left-1'
return isTextarea
? `${baseClasses} peer-placeholder-shown:top-6`
: `${baseClasses} peer-placeholder-shown:top-1/2`
}
const inputClasses = "block px-2.5 pb-2.5 pt-4 w-full text-sm text-gray-900 bg-transparent rounded-lg border border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-violet peer"
return (
<div className="relative">
{isTextarea ? (
<textarea
id={id}
value={value}
onChange={onChange}
rows={rows}
className={`${inputClasses} resize-none`}
placeholder=" "
{...props}
/>
) : (
<input
type={type}
id={id}
value={value}
onChange={onChange}
className={inputClasses}
placeholder=" "
{...props}
/>
)}
<label htmlFor={id} className={getLabelClasses()}>
{label}
</label>
</div>
)
}
export default FloatingLabelInput