- No data uploaded
- Instant results
Tailwind CSS Login Modal for Vanilla JS
A production-ready minimal login modal: a centered sign-in dialog with email and password fields, “remember me”, a forgot-password link and a social sign-in button. Pick your brand color, toggle dark mode and corner radius — the live preview and the Vanilla JS code update instantly in your browser.
Live component editor
<!-- Requires Tailwind CSS v3 (CLI/PostCSS build, or the Play CDN for prototypes) -->
<div>
<button type="button" class="inline-flex items-center justify-center px-4 py-2.5 text-sm font-semibold rounded-lg bg-[#4f46e5] hover:bg-[#433cc3] text-white shadow-sm transition-colors focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:ring-offset-2 focus:ring-offset-white" data-open="open">Sign in</button>
<div data-show="open">
<div class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm" role="dialog" aria-modal="true" aria-labelledby="login-title">
<div class="relative w-full max-w-md p-8 bg-white border border-slate-200 shadow-sm rounded-xl">
<button type="button" class="absolute top-0 right-0 m-3 inline-flex items-center justify-center w-8 h-8 rounded-full text-slate-500 hover:bg-slate-900/5" aria-label="Close" data-close="open">×</button>
<h2 id="login-title" class="text-2xl font-bold mb-1 text-slate-900">Welcome back</h2>
<p class="text-sm mb-6 text-slate-500">Sign in to continue to your dashboard.</p>
<form class="flex flex-col gap-4">
<div>
<label for="login-email" class="block mb-1.5 text-sm font-medium text-slate-700">Email</label>
<input id="login-email" type="email" autocomplete="email" placeholder="you@company.com" class="block w-full px-3 py-2 text-sm rounded-lg border bg-white border-slate-300 text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:border-[#4f46e5]" required>
</div>
<div>
<div class="flex items-center justify-between">
<label for="login-password" class="block mb-1.5 text-sm font-medium text-slate-700">Password</label>
<a href="#" class="text-sm font-medium text-[#4f46e5] hover:underline">Forgot?</a>
</div>
<input id="login-password" type="password" autocomplete="current-password" placeholder="••••••••" class="block w-full px-3 py-2 text-sm rounded-lg border bg-white border-slate-300 text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:border-[#4f46e5]" required>
</div>
<label class="flex items-center gap-2 text-sm text-slate-700">
<input type="checkbox" class="h-4 w-4 accent-[#4f46e5]"> Remember me for 30 days
</label>
<button type="submit" class="inline-flex items-center justify-center px-4 py-2.5 text-sm font-semibold rounded-lg bg-[#4f46e5] hover:bg-[#433cc3] text-white shadow-sm transition-colors focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:ring-offset-2 focus:ring-offset-white w-full">Sign in</button>
</form>
<div class="flex items-center gap-3 my-6">
<span class="flex-1 border-t border-slate-200"></span>
<span class="text-xs uppercase text-slate-500">or</span>
<span class="flex-1 border-t border-slate-200"></span>
</div>
<button type="button" class="inline-flex items-center justify-center px-4 py-2.5 text-sm font-semibold rounded-lg border transition-colors border-slate-300 text-slate-700 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-[#4f46e5] focus:ring-offset-2 focus:ring-offset-white w-full">Continue with Google</button>
<p class="mt-6 text-center text-sm text-slate-500">No account? <a href="#" class="font-medium text-[#4f46e5] hover:underline">Create one</a></p>
</div>
</div>
</div>
</div>
<script>
(() => {
const state = { open: true };
const actions = { };
const pair = (s) => s.split(':');
const sync = () => {
document.querySelectorAll('[data-show]').forEach((el) => (el.hidden = !state[el.dataset.show]));
document.querySelectorAll('[data-hide]').forEach((el) => (el.hidden = !!state[el.dataset.hide]));
document.querySelectorAll('[data-when]').forEach((el) => { const [k, v] = pair(el.dataset.when); el.hidden = state[k] !== v; });
document.querySelectorAll('[data-pressed]').forEach((el) => { const [k, v] = pair(el.dataset.pressed); el.setAttribute('aria-pressed', String(state[k] === v)); });
document.querySelectorAll('[data-expanded]').forEach((el) => el.setAttribute('aria-expanded', String(!!state[el.dataset.expanded])));
};
document.addEventListener('click', (e) => {
const t = e.target.closest('[data-toggle],[data-open],[data-close],[data-set],[data-call]');
if (!t) return;
const { toggle, open, close, set, call } = t.dataset;
if (call) actions[call]?.(e);
if (toggle) state[toggle] = !state[toggle];
if (open) state[open] = true;
if (close) state[close] = false;
if (set) { const [k, v] = pair(set); state[k] = v; }
sync();
});
sync();
})();
</script>
More Tailwind components for Vanilla JS
All components →- Signup Form A registration card with name, email and password fields, a password hint, a terms-of-service checkbox and a primary call to action
- Navbar A responsive top navigation bar with a logo, primary links, a sign-in/CTA pair on desktop and a collapsible hamburger menu on mobile
- Cookie Consent Banner A GDPR-style cookie banner with equally prominent “Accept all” and “Reject all”, granular analytics/marketing toggles (off by default), stored consent and Google Consent Mode v2 signals
- Contact Form A contact card with name, email, a topic selector, a message textarea and a submit button
- Footer A multi-column site footer with a brand description, three link groups, a newsletter sign-up field and a copyright row
- Pricing Table A three-tier pricing section with a monthly/yearly billing toggle, a highlighted “most popular” plan and feature checklists
- Hero Section A landing-page hero with an announcement badge, a large headline, supporting copy, primary and secondary calls to action and a row of social-proof stats
- Feature Grid A marketing section with a centered heading and a responsive grid of six feature cards, each with an icon badge, title and description
Install Tailwind CSS for Vanilla JS
Prototype instantly with the Play CDN, or install the Tailwind CLI for production:
<script src="https://cdn.tailwindcss.com"></script>npm install -D tailwindcss@3
npx tailwindcss init
npx tailwindcss -i ./src/input.css -o ./dist/output.css --watchAdd ./**/*.html to content in tailwind.config.js and put the three @tailwind directives in src/input.css.
Use the login modal in Vanilla JS
Paste the markup where the component should appear. The small inline <script> wires up the interactive state with one delegated click listener — no framework needed.
Accessibility & customization
The dialog uses role="dialog", aria-modal="true" and aria-labelledby pointing at its title. Every field has a visible <label>, the close button has an aria-label, and inputs use the correct autocomplete tokens so password managers work.
Every color, radius and dark-mode choice is baked into plain utility classes, so the copied code has no runtime dependency on this site.
Compatibility
| Item | Details |
|---|---|
| CSS framework | Tailwind CSS v3.4+ |
| Library | Plain HTML + optional vanilla JS |
| Interactive state | open |
| Dark mode | Yes — toggle in the editor |
| Style | Minimal |