forms floating-labels contact input animation css-only forms floating label input contact animation css floating label input form animated label contact form
Floating Labels Form
Fetch pattern JSON:
curl https://webspire.de/patterns/forms/floating-labels.json floating-labels.html
<section class="ws-forms-floating-labels min-h-screen bg-[var(--ws-color-bg)] py-20">
<div class="mx-auto max-w-5xl px-6">
<div class="mb-12 text-center">
<h2 class="text-3xl font-bold tracking-tight text-[var(--ws-color-text)] sm:text-4xl">Get in touch</h2>
<p class="mt-4 text-lg text-[var(--ws-color-text-soft)]">Two floating label styles — bordered box and underline-only.</p>
</div>
<div class="grid gap-10 lg:grid-cols-2">
<!-- Variant 1: Bordered Box -->
<div class="rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] p-8">
<h3 class="mb-6 text-lg font-semibold text-[var(--ws-color-text)]">Bordered variant</h3>
<form class="space-y-6" novalidate>
<div class="relative">
<input
id="fl-name"
type="text"
placeholder=" "
class="fl-input peer block w-full rounded-lg border border-[var(--ws-color-border)] bg-transparent px-4 pb-2.5 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
/>
<label for="fl-name" class="fl-label pointer-events-none absolute left-4 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Full name
</label>
</div>
<div class="relative">
<input
id="fl-email"
type="email"
placeholder=" "
class="fl-input peer block w-full rounded-lg border border-[var(--ws-color-border)] bg-transparent px-4 pb-2.5 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
/>
<label for="fl-email" class="fl-label pointer-events-none absolute left-4 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Email address
</label>
</div>
<div class="relative">
<input
id="fl-subject"
type="text"
placeholder=" "
class="fl-input peer block w-full rounded-lg border border-[var(--ws-color-border)] bg-transparent px-4 pb-2.5 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
/>
<label for="fl-subject" class="fl-label pointer-events-none absolute left-4 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Subject
</label>
</div>
<div class="relative">
<textarea
id="fl-msg"
rows="4"
placeholder=" "
class="fl-input peer block w-full resize-none rounded-lg border border-[var(--ws-color-border)] bg-transparent px-4 pb-2.5 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
></textarea>
<label for="fl-msg" class="fl-label pointer-events-none absolute left-4 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Message
</label>
</div>
<button type="submit" class="w-full rounded-lg bg-[var(--ws-color-accent)] px-6 py-3 text-sm font-semibold text-white transition hover:opacity-90">
Send message
</button>
</form>
</div>
<!-- Variant 2: Underline Only -->
<div class="rounded-2xl border border-[var(--ws-color-border)] bg-[var(--ws-color-surface)] p-8">
<h3 class="mb-6 text-lg font-semibold text-[var(--ws-color-text)]">Underline variant</h3>
<form class="space-y-6" novalidate>
<div class="relative">
<input
id="ul-name"
type="text"
placeholder=" "
class="fl-input peer block w-full border-b border-[var(--ws-color-border)] bg-transparent px-0 pb-2 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
/>
<label for="ul-name" class="fl-label pointer-events-none absolute left-0 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Full name
</label>
</div>
<div class="relative">
<input
id="ul-email"
type="email"
placeholder=" "
class="fl-input peer block w-full border-b border-[var(--ws-color-border)] bg-transparent px-0 pb-2 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
/>
<label for="ul-email" class="fl-label pointer-events-none absolute left-0 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Email address
</label>
</div>
<div class="relative">
<input
id="ul-subject"
type="text"
placeholder=" "
class="fl-input peer block w-full border-b border-[var(--ws-color-border)] bg-transparent px-0 pb-2 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
/>
<label for="ul-subject" class="fl-label pointer-events-none absolute left-0 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Subject
</label>
</div>
<div class="relative">
<textarea
id="ul-msg"
rows="4"
placeholder=" "
class="fl-input peer block w-full resize-none border-b border-[var(--ws-color-border)] bg-transparent px-0 pb-2 pt-5 text-sm text-[var(--ws-color-text)] focus:border-[var(--ws-color-accent)] focus:outline-none"
></textarea>
<label for="ul-msg" class="fl-label pointer-events-none absolute left-0 top-4 origin-top-left text-sm text-[var(--ws-color-text-soft)] transition-all duration-200 peer-focus:-translate-y-3 peer-focus:scale-75 peer-focus:text-[var(--ws-color-accent)] peer-[:not(:placeholder-shown)]:-translate-y-3 peer-[:not(:placeholder-shown)]:scale-75">
Message
</label>
</div>
<button type="submit" class="w-full rounded-lg bg-[var(--ws-color-accent)] px-6 py-3 text-sm font-semibold text-white transition hover:opacity-90">
Send message
</button>
</form>
</div>
</div>
</div>
<style>
/* Floating label transition — Tailwind peer-* handles focus/filled states */
.fl-label {
transform-origin: top left;
}
/* When textarea has content the :placeholder-shown trick needs the placeholder attribute set to " " (single space) */
</style>
</section>
Details
Responsive Dark Mode Tailwind Only SSR Safe Copy & Paste
Stable Published
formsfloating-labelscontactinputanimationcss-only
Slots
| Name | Required | Description |
|---|---|---|
| form-bordered | Yes | Bordered box variant with floating labels. |
| form-underline | Yes | Underline-only variant with floating labels. |
Two-column form card demonstrating CSS floating labels. Left card uses bordered box inputs; right card uses underline-only inputs. Both rely on Tailwind peer + :placeholder-shown to animate labels upward when an input is focused or filled — zero JavaScript required.