Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Tailwindcss
  4. /Forms Interactive Elements
Your Progress0%
0 of 20 completed

TailwindCSS Topics

Getting Started

  • What is Tailwind CSS?
  • Tailwind vs Traditional CSS
  • Setting Up Tailwind CSS v4
  • Understanding Utility-First CSS

Layout Fundamentals

  • Layout Utilities
  • Flexbox Utilities
  • Grid Utilities
  • Spacing & Sizing

Typography & Colors

  • Typography Utilities
  • Colors & Backgrounds

Borders & Effects

  • Borders & Rounded Corners
  • Shadows & Ring Utilities

Responsive Design

  • Responsive Design Basics
  • Dark Mode

Transforms & Animations

  • Transforms & Transitions
  • Filters & Visual Effects

Customization & Theming

  • Custom Styles & CSS Variables

Practical Application

  • Component Patterns
  • Forms & Interactive Elements
  • Production Best Practices

Forms & Interactive Elements

Styling forms, inputs, buttons, and interactive states

Forms are critical to user interaction, yet they're often the most challenging to style well. Tailwind makes it easy to create beautiful, accessible forms with proper focus states, validation feedback, and interactive elements. In this lesson, you'll master styling inputs, selects, checkboxes, radio buttons, and build forms that are both functional and delightful to use.

Text Input Styling

Create polished text inputs with proper states:

Basic Text Input

HTML
<!-- Basic input -->
<input 
  type="text"
  class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
  placeholder="Enter text..."
>

<!-- Input with label -->
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Full Name
  </label>
  <input 
    type="text"
    class="w-full px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder:text-gray-500 dark:placeholder:text-gray-400 focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 dark:focus:border-blue-400 transition"
    placeholder="John Doe"
  >
</div>

<!-- Disabled input -->
<input 
  type="text"
  disabled
  class="w-full px-4 py-2 bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-500 dark:text-gray-400 cursor-not-allowed"
  placeholder="Disabled input"
>

<!-- Read-only input -->
<input 
  type="text"
  readonly
  value="Read-only value"
  class="w-full px-4 py-2 bg-gray-50 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white cursor-default"
>

Input Variants

HTML
<!-- Small input -->
<input 
  type="text"
  class="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
  placeholder="Small input"
>

<!-- Default input -->
<input 
  type="text"
  class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
  placeholder="Default input"
>

<!-- Large input -->
<input 
  type="text"
  class="w-full px-5 py-3 text-lg border border-gray-300 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
  placeholder="Large input"
>

<!-- Pill-shaped input -->
<input 
  type="text"
  class="w-full px-6 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
  placeholder="Pill input"
>

Input with Icons

HTML
<!-- Input with leading icon -->
<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"/>
    </svg>
  </div>
  <input 
    type="text"
    class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
    placeholder="Search..."
  >
</div>

<!-- Input with trailing icon -->
<div class="relative">
  <input 
    type="email"
    class="w-full pl-4 pr-10 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
    placeholder="Email address"
  >
  <div class="absolute inset-y-0 right-0 pr-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="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
    </svg>
  </div>
</div>

<!-- Input with both icons -->
<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"/>
    </svg>
  </div>
  <input 
    type="text"
    class="w-full pl-10 pr-10 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
    placeholder="Username"
  >
  <button class="absolute inset-y-0 right-0 pr-3 flex items-center">
    <svg class="w-5 h-5 text-gray-400 hover:text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
    </svg>
  </button>
</div>

<!-- Input with addon -->
<div class="flex">
  <span class="inline-flex items-center px-4 bg-gray-100 dark:bg-gray-700 border border-r-0 border-gray-300 dark:border-gray-600 rounded-l-lg text-gray-600 dark:text-gray-400">
    https://
  </span>
  <input 
    type="text"
    class="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-r-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition"
    placeholder="example.com"
  >
</div>

♿ Accessibility Tip

Always provide clear focus indicators with focus:ring-4 and ensure sufficient color contrast. Never use outline-none without providing an alternative focus indicator!

Validation States

Show success, error, and warning states clearly:

Success State

HTML
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Email Address
  </label>
  <div class="relative">
    <input 
      type="email"
      class="w-full pl-4 pr-10 py-2 border-2 border-green-500 dark:border-green-400 rounded-lg bg-green-50 dark:bg-green-900/20 text-gray-900 dark:text-white focus:outline-none focus:ring-4 focus:ring-green-200 dark:focus:ring-green-800 transition"
      value="john@example.com"
    >
    <div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
      <svg class="w-5 h-5 text-green-500 dark:text-green-400" fill="currentColor" viewBox="0 0 20 20">
        <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
      </svg>
    </div>
  </div>
  <p class="mt-2 text-sm text-green-600 dark:text-green-400">
    ✓ Email is valid
  </p>
</div>

Error State

HTML
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Password
  </label>
  <div class="relative">
    <input 
      type="password"
      class="w-full pl-4 pr-10 py-2 border-2 border-red-500 dark:border-red-400 rounded-lg bg-red-50 dark:bg-red-900/20 text-gray-900 dark:text-white focus:outline-none focus:ring-4 focus:ring-red-200 dark:focus:ring-red-800 transition"
      value="123"
    >
    <div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
      <svg class="w-5 h-5 text-red-500 dark:text-red-400" fill="currentColor" viewBox="0 0 20 20">
        <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
      </svg>
    </div>
  </div>
  <p class="mt-2 text-sm text-red-600 dark:text-red-400">
    Password must be at least 8 characters
  </p>
</div>

Warning State

HTML
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Username
  </label>
  <div class="relative">
    <input 
      type="text"
      class="w-full pl-4 pr-10 py-2 border-2 border-yellow-500 dark:border-yellow-400 rounded-lg bg-yellow-50 dark:bg-yellow-900/20 text-gray-900 dark:text-white focus:outline-none focus:ring-4 focus:ring-yellow-200 dark:focus:ring-yellow-800 transition"
      value="john_doe"
    >
    <div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
      <svg class="w-5 h-5 text-yellow-500 dark:text-yellow-400" fill="currentColor" viewBox="0 0 20 20">
        <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
      </svg>
    </div>
  </div>
  <p class="mt-2 text-sm text-yellow-600 dark:text-yellow-400">
    Username contains underscores - consider using hyphens instead
  </p>
</div>

Textarea Styling

Style multi-line text inputs:

HTML
<!-- Basic textarea -->
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Message
  </label>
  <textarea 
    rows="4"
    class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder:text-gray-500 dark:placeholder:text-gray-400 focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 dark:focus:border-blue-400 transition resize-none"
    placeholder="Enter your message..."
  ></textarea>
</div>

<!-- Textarea with character count -->
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Description
  </label>
  <textarea 
    rows="4"
    maxlength="200"
    class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition resize-none"
    placeholder="Enter description..."
  ></textarea>
  <div class="flex justify-between mt-2">
    <p class="text-sm text-gray-600 dark:text-gray-400">Maximum 200 characters</p>
    <p class="text-sm text-gray-600 dark:text-gray-400">
      <span class="font-semibold">0</span> / 200
    </p>
  </div>
</div>

<!-- Resizable textarea -->
<textarea 
  rows="4"
  class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition resize-y"
  placeholder="Resizable vertically..."
></textarea>

<!-- Auto-expanding textarea (requires JavaScript) -->
<textarea 
  rows="3"
  class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition resize-none"
  placeholder="Auto-expands as you type..."
  oninput="this.style.height = 'auto'; this.style.height = this.scrollHeight + 'px'"
></textarea>

Select Dropdown Styling

Create styled select dropdowns:

HTML
<!-- Basic select -->
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Country
  </label>
  <select class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 dark:focus:border-blue-400 transition">
    <option value="">Select a country</option>
    <option value="us">United States</option>
    <option value="uk">United Kingdom</option>
    <option value="ca">Canada</option>
    <option value="au">Australia</option>
  </select>
</div>

<!-- Select with custom arrow -->
<div class="mb-4">
  <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
    Category
  </label>
  <div class="relative">
    <select class="w-full px-4 py-2 pr-10 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white appearance-none focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 dark:focus:border-blue-400 transition cursor-pointer">
      <option value="">Choose category</option>
      <option value="tech">Technology</option>
      <option value="business">Business</option>
      <option value="design">Design</option>
    </select>
    <div class="absolute inset-y-0 right-0 flex items-center pr-3 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="M19 9l-7 7-7-7"/>
      </svg>
    </div>
  </div>
</div>

<!-- Disabled select -->
<select 
  disabled
  class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 cursor-not-allowed"
>
  <option>Disabled select</option>
</select>

<!-- Select with groups -->
<select class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 focus:outline-none focus:ring-4 focus:ring-blue-200 focus:border-blue-500 transition">
  <option value="">Select framework</option>
  <optgroup label="Frontend">
    <option value="react">React</option>
    <option value="vue">Vue</option>
    <option value="angular">Angular</option>
  </optgroup>
  <optgroup label="Backend">
    <option value="node">Node.js</option>
    <option value="django">Django</option>
    <option value="rails">Rails</option>
  </optgroup>
</select>

Checkboxes & Radio Buttons

Style checkboxes and radio buttons:

Checkboxes

HTML
<!-- Basic checkbox -->
<label class="flex items-center gap-2 cursor-pointer">
  <input 
    type="checkbox"
    class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200 focus:ring-offset-0"
  >
  <span class="text-gray-700 dark:text-gray-300">I agree to the terms</span>
</label>

<!-- Checkbox list -->
<div class="space-y-3">
  <label class="flex items-center gap-2 cursor-pointer">
    <input 
      type="checkbox"
      checked
      class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200"
    >
    <span class="text-gray-700 dark:text-gray-300">Option 1</span>
  </label>
  
  <label class="flex items-center gap-2 cursor-pointer">
    <input 
      type="checkbox"
      class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200"
    >
    <span class="text-gray-700 dark:text-gray-300">Option 2</span>
  </label>
  
  <label class="flex items-center gap-2 cursor-pointer">
    <input 
      type="checkbox"
      class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200"
    >
    <span class="text-gray-700 dark:text-gray-300">Option 3</span>
  </label>
</div>

<!-- Disabled checkbox -->
<label class="flex items-center gap-2 cursor-not-allowed opacity-60">
  <input 
    type="checkbox"
    disabled
    class="w-5 h-5 text-gray-400 border-gray-300 rounded"
  >
  <span class="text-gray-500">Disabled option</span>
</label>

<!-- Checkbox with description -->
<label class="flex items-start gap-3 cursor-pointer p-4 border border-gray-200 dark:border-gray-700 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition">
  <input 
    type="checkbox"
    class="w-5 h-5 mt-0.5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200"
  >
  <div>
    <p class="font-semibold text-gray-900 dark:text-white">Email notifications</p>
    <p class="text-sm text-gray-600 dark:text-gray-400">Receive updates about your account activity</p>
  </div>
</label>

Radio Buttons

HTML
<!-- Basic radio group -->
<div class="space-y-3">
  <label class="flex items-center gap-2 cursor-pointer">
    <input 
      type="radio"
      name="plan"
      value="free"
      checked
      class="w-5 h-5 text-blue-600 border-gray-300 focus:ring-4 focus:ring-blue-200"
    >
    <span class="text-gray-700 dark:text-gray-300">Free Plan</span>
  </label>
  
  <label class="flex items-center gap-2 cursor-pointer">
    <input 
      type="radio"
      name="plan"
      value="pro"
      class="w-5 h-5 text-blue-600 border-gray-300 focus:ring-4 focus:ring-blue-200"
    >
    <span class="text-gray-700 dark:text-gray-300">Pro Plan</span>
  </label>
  
  <label class="flex items-center gap-2 cursor-pointer">
    <input 
      type="radio"
      name="plan"
      value="enterprise"
      class="w-5 h-5 text-blue-600 border-gray-300 focus:ring-4 focus:ring-blue-200"
    >
    <span class="text-gray-700 dark:text-gray-300">Enterprise Plan</span>
  </label>
</div>

<!-- Radio cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
  <label class="relative flex cursor-pointer">
    <input 
      type="radio"
      name="tier"
      value="basic"
      class="peer sr-only"
    >
    <div class="w-full p-6 border-2 border-gray-300 dark:border-gray-600 rounded-lg peer-checked:border-blue-500 peer-checked:bg-blue-50 dark:peer-checked:bg-blue-900/20 peer-focus:ring-4 peer-focus:ring-blue-200 transition">
      <p class="text-xl font-bold text-gray-900 dark:text-white mb-2">Basic</p>
      <p class="text-3xl font-bold text-gray-900 dark:text-white mb-2">$9</p>
      <p class="text-sm text-gray-600 dark:text-gray-400">per month</p>
    </div>
  </label>
  
  <label class="relative flex cursor-pointer">
    <input 
      type="radio"
      name="tier"
      value="pro"
      checked
      class="peer sr-only"
    >
    <div class="w-full p-6 border-2 border-gray-300 dark:border-gray-600 rounded-lg peer-checked:border-blue-500 peer-checked:bg-blue-50 dark:peer-checked:bg-blue-900/20 peer-focus:ring-4 peer-focus:ring-blue-200 transition">
      <p class="text-xl font-bold text-gray-900 dark:text-white mb-2">Pro</p>
      <p class="text-3xl font-bold text-gray-900 dark:text-white mb-2">$29</p>
      <p class="text-sm text-gray-600 dark:text-gray-400">per month</p>
    </div>
  </label>
  
  <label class="relative flex cursor-pointer">
    <input 
      type="radio"
      name="tier"
      value="enterprise"
      class="peer sr-only"
    >
    <div class="w-full p-6 border-2 border-gray-300 dark:border-gray-600 rounded-lg peer-checked:border-blue-500 peer-checked:bg-blue-50 dark:peer-checked:bg-blue-900/20 peer-focus:ring-4 peer-focus:ring-blue-200 transition">
      <p class="text-xl font-bold text-gray-900 dark:text-white mb-2">Enterprise</p>
      <p class="text-3xl font-bold text-gray-900 dark:text-white mb-2">$99</p>
      <p class="text-sm text-gray-600 dark:text-gray-400">per month</p>
    </div>
  </label>
</div>

💡 Checkbox & Radio Best Practices

  • Always wrap in <label> for larger click areas
  • Use cursor-pointer on labels
  • Provide clear focus states with focus:ring-4
  • Consider using card-style options for important choices

Toggle Switches

Create custom toggle switches:

HTML
<!-- Basic toggle switch -->
<label class="relative inline-flex items-center cursor-pointer">
  <input type="checkbox" class="sr-only peer">
  <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
  <span class="ms-3 text-sm font-medium text-gray-900 dark:text-gray-300">Toggle me</span>
</label>

<!-- Larger toggle switch -->
<label class="relative inline-flex items-center cursor-pointer">
  <input type="checkbox" class="sr-only peer">
  <div class="w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[4px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 after:w-6 after:transition-all peer-checked:bg-blue-600"></div>
  <span class="ms-3 text-base font-medium text-gray-900 dark:text-gray-300">Large toggle</span>
</label>

<!-- Toggle with description -->
<label class="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition">
  <div class="flex-1">
    <p class="font-semibold text-gray-900 dark:text-white">Enable notifications</p>
    <p class="text-sm text-gray-600 dark:text-gray-400">Receive email updates about your account</p>
  </div>
  <div class="relative inline-flex items-center">
    <input type="checkbox" class="sr-only peer">
    <div class="w-11 h-6 bg-gray-200 peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
  </div>
</label>

<!-- Disabled toggle -->
<label class="relative inline-flex items-center cursor-not-allowed opacity-60">
  <input type="checkbox" disabled class="sr-only peer">
  <div class="w-11 h-6 bg-gray-200 rounded-full peer dark:bg-gray-700 after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all"></div>
  <span class="ms-3 text-sm font-medium text-gray-500">Disabled toggle</span>
</label>

File Input Styling

Create custom file upload interfaces:

HTML
<!-- Custom file upload button -->
<label class="cursor-pointer">
  <input type="file" class="sr-only">
  <div class="inline-flex items-center gap-2 px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg transition">
    <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
    </svg>
    <span>Choose File</span>
  </div>
</label>

<!-- File upload dropzone -->
<label class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 dark:border-gray-600 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 transition">
  <input type="file" class="sr-only">
  <div class="flex flex-col items-center justify-center pt-5 pb-6">
    <svg class="w-12 h-12 mb-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
    </svg>
    <p class="mb-2 text-sm text-gray-600 dark:text-gray-400">
      <span class="font-semibold">Click to upload</span> or drag and drop
    </p>
    <p class="text-xs text-gray-500 dark:text-gray-400">PNG, JPG or GIF (MAX. 10MB)</p>
  </div>
</label>

<!-- File upload with preview -->
<div class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-6">
  <label class="cursor-pointer">
    <input type="file" accept="image/*" class="sr-only">
    <div class="flex flex-col items-center">
      <div class="w-32 h-32 mb-4 bg-gray-100 dark:bg-gray-700 rounded-lg flex items-center justify-center">
        <svg class="w-12 h-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
        </svg>
      </div>
      <button type="button" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg transition">
        Upload Image
      </button>
    </div>
  </label>
</div>

Complete Form Example

Put it all together in a complete form:

HTML
<form class="max-w-2xl mx-auto bg-white dark:bg-gray-800 rounded-lg shadow-lg p-8">
  <h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6">Create Account</h2>
  
  <!-- Name fields -->
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
    <div>
      <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
        First Name *
      </label>
      <input 
        type="text"
        required
        class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 transition"
        placeholder="John"
      >
    </div>
    
    <div>
      <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
        Last Name *
      </label>
      <input 
        type="text"
        required
        class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 transition"
        placeholder="Doe"
      >
    </div>
  </div>
  
  <!-- Email -->
  <div class="mb-6">
    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
      Email Address *
    </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="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
        </svg>
      </div>
      <input 
        type="email"
        required
        class="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder:text-gray-500 focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 transition"
        placeholder="john@example.com"
      >
    </div>
  </div>
  
  <!-- Password -->
  <div class="mb-6">
    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
      Password *
    </label>
    <input 
      type="password"
      required
      class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 transition"
      placeholder="••••••••"
    >
    <p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
      Must be at least 8 characters
    </p>
  </div>
  
  <!-- Country -->
  <div class="mb-6">
    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
      Country *
    </label>
    <div class="relative">
      <select class="w-full px-4 py-2 pr-10 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white appearance-none focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 transition cursor-pointer">
        <option value="">Select your country</option>
        <option value="us">United States</option>
        <option value="uk">United Kingdom</option>
        <option value="ca">Canada</option>
      </select>
      <div class="absolute inset-y-0 right-0 flex items-center pr-3 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="M19 9l-7 7-7-7"/>
        </svg>
      </div>
    </div>
  </div>
  
  <!-- Bio -->
  <div class="mb-6">
    <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
      Bio
    </label>
    <textarea 
      rows="4"
      class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder:text-gray-500 focus:outline-none focus:ring-4 focus:ring-blue-200 dark:focus:ring-blue-800 focus:border-blue-500 transition resize-none"
      placeholder="Tell us about yourself..."
    ></textarea>
  </div>
  
  <!-- Preferences -->
  <div class="mb-6">
    <p class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
      Notifications
    </p>
    <div class="space-y-3">
      <label class="flex items-center gap-2 cursor-pointer">
        <input 
          type="checkbox"
          checked
          class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200"
        >
        <span class="text-gray-700 dark:text-gray-300">Email notifications</span>
      </label>
      
      <label class="flex items-center gap-2 cursor-pointer">
        <input 
          type="checkbox"
          class="w-5 h-5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200"
        >
        <span class="text-gray-700 dark:text-gray-300">SMS notifications</span>
      </label>
    </div>
  </div>
  
  <!-- Terms -->
  <div class="mb-6">
    <label class="flex items-start gap-2 cursor-pointer">
      <input 
        type="checkbox"
        required
        class="w-5 h-5 mt-0.5 text-blue-600 border-gray-300 rounded focus:ring-4 focus:ring-blue-200"
      >
      <span class="text-sm text-gray-700 dark:text-gray-300">
        I agree to the <a href="#" class="text-blue-600 hover:text-blue-700 dark:text-blue-400">Terms of Service</a> and <a href="#" class="text-blue-600 hover:text-blue-700 dark:text-blue-400">Privacy Policy</a>
      </span>
    </label>
  </div>
  
  <!-- Submit -->
  <div class="flex gap-4">
    <button 
      type="submit"
      class="flex-1 px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow hover:shadow-lg transition-all focus:outline-none focus:ring-4 focus:ring-blue-300"
    >
      Create Account
    </button>
    <button 
      type="button"
      class="px-6 py-3 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-900 dark:text-white font-semibold rounded-lg transition-all"
    >
      Cancel
    </button>
  </div>
</form>

Forms Playground

Practice building forms:

Build Beautiful Forms

Practice styling form elements

Contact Us

Form Best Practices

Follow These Guidelines:

  1. Always provide focus states: Use focus:ring-4 for keyboard navigation
  2. Label all inputs: Use semantic <label> elements
  3. Show validation clearly: Use color + icon + message
  4. Provide helpful placeholders: Show format examples
  5. Make interactive elements obvious: Use cursor-pointer
  6. Test keyboard navigation: Ensure all elements are accessible
  7. Use appropriate input types: email, tel, url, etc.
  8. Group related fields: Use fieldsets for better organization

Key Takeaways

  • Always provide clear focus states with focus:ring-4
  • Use placeholder: variant to style placeholder text
  • Show validation with border colors, icons, and messages
  • Wrap checkboxes/radios in labels for larger click areas
  • Use appearance-none for custom select styling
  • Create custom file uploads with hidden inputs
  • Toggle switches use peer utilities for styling
  • Test all forms with keyboard navigation
  • Ensure sufficient color contrast for accessibility
  • Combine multiple validation indicators (color + icon + text)

What's Next?

You've mastered form styling! You can now create beautiful, accessible forms with proper validation and interactive states.

In the final lesson, we'll explore Production Best Practices—learning optimization techniques, file organization, maintainability strategies, and how to deploy Tailwind projects to production efficiently.

🎯 Practice Challenge!

Build a complete multi-step registration form! Include:

  • Personal information step (name, email, password)
  • Address step (street, city, country select)
  • Preferences step (checkboxes, radio buttons, toggles)
  • Review step (show all data before submission)
  • Validation on each field with proper error states
  • Progress indicator showing current step
  • Previous/Next navigation buttons
  • Full keyboard accessibility

Test Your Understanding

Question 1 of 3Score: 0/0

What's the best way to style focus states for accessibility?

Just mastered form styling with Tailwind! Creating beautiful, accessible forms is so much easier now.

Previous
Component Patterns
Next
Production Best Practices

Master Production-Ready Tailwind

Join 2,000+ developers building professional applications with Tailwind. Get best practices and tips - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

TailwindCSS Tutorials

0 of 20 completed

Your Progress0%

Topics

Getting Started

  • What is Tailwind CSS?
  • Tailwind vs Traditional CSS
  • Setting Up Tailwind CSS v4
  • Understanding Utility-First CSS

Layout Fundamentals

  • Layout Utilities
  • Flexbox Utilities
  • Grid Utilities
  • Spacing & Sizing

Typography & Colors

  • Typography Utilities
  • Colors & Backgrounds

Borders & Effects

  • Borders & Rounded Corners
  • Shadows & Ring Utilities

Responsive Design

  • Responsive Design Basics
  • Dark Mode

Transforms & Animations

  • Transforms & Transitions
  • Filters & Visual Effects

Customization & Theming

  • Custom Styles & CSS Variables

Practical Application

  • Component Patterns
  • Forms & Interactive Elements
  • Production Best Practices
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo