Tailwind Forms Plugin Playground
by tailkits
HTML
<!-- Tailwind config + CDN -->
<script>
// Needed so the "dark:" variants work with a toggle
tailwind = { config: { darkMode: "class" } }
</script>
<script src="https://cdn.tailwindcss.com"></script>
<section
id="tw-forms-playground"
class="twfp forms-base antialiased text-slate-800 bg-white"
>
<div class="max-w-6xl mx-auto p-6">
<header class="mb-6">
<h2 class="text-2xl font-bold">Tailwind Forms Plugin Playground</h2>
<p class="muted mt-1 text-sm">
Toggle between a simulated <b>base reset</b> and <b>class strategy</b>.
In production, load the real plugin via Tailwind v4
<code>@plugin</code> or v3 <code>plugins</code> config.
</p>
</header>
<!-- Controls -->
<div class="flex flex-wrap items-center gap-4 mb-6">
<label class="flex items-center gap-2 cursor-pointer">
<input id="twfp-modeToggle" type="checkbox" class="h-4 w-4" />
<span class="text-sm"
>Use <b>class</b> strategy (off = base reset)</span
>
</label>
<label class="flex items-center gap-2 cursor-pointer">
<input id="twfp-darkToggle" type="checkbox" class="h-4 w-4" />
<span class="text-sm">Dark mode</span>
</label>
<button
id="twfp-copyBtn"
type="button"
class="text-sm rounded-md border px-3 py-1 hover:bg-slate-50 dark:hover:bg-slate-800"
>
Copy sample form HTML
</button>
</div>
<div id="twfp-playground" class="grid gap-6 sm:grid-cols-2">
<!-- Base strategy panel -->
<section
id="twfp-panelBase"
class="demo-card rounded-2xl border border-slate-200 dark:border-slate-700 bg-white p-5 dark:bg-slate-800"
>
<h3 class="text-lg font-semibold mb-4">Base reset (global)</h3>
<form class="space-y-4">
<div>
<label for="twfp-emailA" class="mb-1 block text-sm font-medium"
...
CSS
/* Scoped styles so nothing leaks outside this section */
#tw-forms-playground {
color-scheme: light dark;
}
/* --- Simulated @tailwindcss/forms — BASE STRATEGY (applies when wrapper has .forms-base) --- */
#tw-forms-playground.forms-base input[type="text"],
#tw-forms-playground.forms-base input[type="email"],
#tw-forms-playground.forms-base input[type="password"],
#tw-forms-playground.forms-base input[type="number"],
#tw-forms-playground.forms-base input[type="date"],
#tw-forms-playground.forms-base input[type="time"],
#tw-forms-playground.forms-base input[type="url"],
#tw-forms-playground.forms-base input[type="search"],
#tw-forms-playground.forms-base textarea,
#tw-forms-playground.forms-base select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
color: #111827; /* gray-900 */
border: 1px solid #d1d5db; /* gray-300 */
border-radius: 0.375rem; /* rounded-md */
padding: 0.5rem 0.75rem; /* px-3 py-2 */
line-height: 1.5;
outline: none;
}
#tw-forms-playground.forms-base select {
background-image: none;
}
#tw-forms-playground.forms-base input:focus,
#tw-forms-playground.forms-base textarea:focus,
#tw-forms-playground.forms-base select:focus {
border-color: #6366f1; /* indigo-500 */
box-shadow: 0 0 0 2px rgba(199, 210, 254, 0.8); /* indigo-200 */
}
#tw-forms-playground.forms-base input[type="checkbox"],
#tw-forms-playground.forms-base input[type="radio"] {
width: 1rem;
height: 1rem;
border-radius: 0.25rem;
border: 1px solid #d1d5db;
accent-color: #6366f1; /* indigo-500 */
}
#tw-forms-playground.forms-base input[type="radio"] {
border-radius: 9999px;
}
#tw-forms-playground.forms-base input[type="file"]::file-selector-button {
margin-right: 0.75rem;
border: 1px solid #d1d5db;
background: #f9fafb;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
cursor: pointer;
}
/* --- CLASS STRATEGY (opt-in via .form-*) ---...
JavaScript
document.addEventListener('DOMContentLoaded', () => {
const root = document.getElementById('tw-forms-playground');
const modeToggle = root.querySelector('#twfp-modeToggle');
const darkToggle = root.querySelector('#twfp-darkToggle');
const playground = root.querySelector('#twfp-playground');
const panelBase = root.querySelector('#twfp-panelBase');
const panelClass = root.querySelector('#twfp-panelClass');
const copyBtn = root.querySelector('#twfp-copyBtn');
// Toggle strategy
modeToggle.addEventListener('change', () => {
const useClass = modeToggle.checked;
panelBase.classList.toggle('hidden', useClass);
panelClass.classList.toggle('hidden', !useClass);
playground.classList.toggle('forms-base', !useClass);
});
// Scoped dark mode (on wrapper only)
darkToggle.addEventListener('change', () => {
root.classList.toggle('dark', darkToggle.checked);
});
const sample = `<form class="space-y-4">
<div>
<label for="email" class="mb-1 block text-sm font-medium">Email</label>
<input id="email" type="email" class="form-input block w-full" placeholder="you@example.com" />
</div>
<div>
<label for="country" class="mb-1 block text-sm font-medium">Country</label>
<select id="country" class="form-select block w-full">
<option>Poland</option><option>Germany</option><option>Turkey</option>
</select>
</div>
<fieldset class="space-y-2">
<legend class="text-sm font-medium">Preferences</legend>
<label cl