Tailwind v4MIT licensed

Tailwind Liquid Glass Input Form Fields

A sign-up form where focusing a field lights a refractive border that travels around it. The port keeps the one thing utilities genuinely cannot do — @property registering the angle as an <angle> so it can tween — in CSS, and expresses everything else, including the mask-composite rim trick, as before: utilities under a focus-within: variant.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="lgt-07 grid w-full min-h-svh place-items-center p-15 font-lgt07-sans [background:conic-gradient(from_210deg_at_30%_30%,#2a2350,#3a2a60,#1c2450,#2a2350)]" aria-label="Tailwind liquid glass form fields">
  <form novalidate class="w-[min(400px,92vw)] rounded-[26px] border border-white/28 bg-white/10 p-8.5 backdrop-blur-[22px] backdrop-saturate-170 shadow-[inset_0_1px_0_rgba(255,255,255,.4),0_30px_60px_-30px_rgba(0,0,0,.6)]">
    <h3 class="mb-5.5 text-2xl font-extrabold text-white">Create account</h3>

    <div class="mb-4.5">
      <label for="lgt-07-name" class="mb-1.75 block text-xs font-semibold uppercase tracking-[0.06em] text-white/70">Full name</label>
      <span class="relative block before:pointer-events-none before:absolute before:-inset-0.5 before:rounded-[16px] before:p-0.5 before:opacity-0 before:transition-opacity before:duration-300 before:[background:conic-gradient(from_var(--lgt-07-angle),var(--color-lgt07-accent),oklch(0.7_0.18_200),oklch(0.72_0.17_330),var(--color-lgt07-accent))] before:[mask-image:linear-gradient(#000_0_0),linear-gradient(#000_0_0)] before:[mask-clip:content-box,border-box] before:[mask-composite:exclude] focus-within:before:opacity-100 focus-within:before:animate-lgt07-spin motion-reduce:focus-within:before:animate-none">
        <input id="lgt-07-name" type="text" autocomplete="name" placeholder="Ada Lovelace" class="relative z-1 block w-full rounded-[14px] border border-white/25 bg-white/8 px-3.75 py-3.25 text-[15px] text-white outline-none backdrop-blur-[8px] shadow-[inset_0_1px_0_rgba(255,255,255,.3)] transition-colors duration-200 placeholder:text-white/50 focus:bg-white/14">
      </span>
    </div>

    <div class="mb-4.5">
      <label for="lgt-07-email" class="mb-1.75 block text-xs font-semibold uppercase tracking-[0.06em] text-white/70">Email</label>
      <span class="relative block before:pointer-events-none before:absolute before:-inset-0.5 before:rounded-[16px] before:p-0.5 before:opacity-0 before:transition-opacity before:duration-300 before:[background:conic-gradient(from_var(--lgt-07-angle),var(--color-lgt07-accent),oklch(0.7_0.18_200),oklch(0.72_0.17_330),var(--color-lgt07-accent))] before:[mask-image:linear-gradient(#000_0_0),linear-gradient(#000_0_0)] before:[mask-clip:content-box,border-box] before:[mask-composite:exclude] focus-within:before:opacity-100 focus-within:before:animate-lgt07-spin motion-reduce:focus-within:before:animate-none">
        <input id="lgt-07-email" type="email" autocomplete="email" placeholder="ada@studio.dev" class="relative z-1 block w-full rounded-[14px] border border-white/25 bg-white/8 px-3.75 py-3.25 text-[15px] text-white outline-none backdrop-blur-[8px] shadow-[inset_0_1px_0_rgba(255,255,255,.3)] transition-colors duration-200 placeholder:text-white/50 focus:bg-white/14">
      </span>
    </div>

    <div class="mb-4.5">
      <label for="lgt-07-pass" class="mb-1.75 block text-xs font-semibold uppercase tracking-[0.06em] text-white/70">Password</label>
      <span class="relative block before:pointer-events-none before:absolute before:-inset-0.5 before:rounded-[16px] before:p-0.5 before:opacity-0 before:transition-opacity before:duration-300 before:[background:conic-gradient(from_var(--lgt-07-angle),var(--color-lgt07-accent),oklch(0.7_0.18_200),oklch(0.72_0.17_330),var(--color-lgt07-accent))] before:[mask-image:linear-gradient(#000_0_0),linear-gradient(#000_0_0)] before:[mask-clip:content-box,border-box] before:[mask-composite:exclude] focus-within:before:opacity-100 focus-within:before:animate-lgt07-spin motion-reduce:focus-within:before:animate-none">
        <input id="lgt-07-pass" type="password" autocomplete="new-password" placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;" class="relative z-1 block w-full rounded-[14px] border border-white/25 bg-white/8 px-3.75 py-3.25 text-[15px] text-white outline-none backdrop-blur-[8px] shadow-[inset_0_1px_0_rgba(255,255,255,.3)] transition-colors duration-200 placeholder:text-white/50 focus:bg-white/14">
      </span>
    </div>

    <button type="submit" class="mt-1.5 w-full cursor-pointer rounded-[14px] border border-lgt07-accent/60 bg-linear-to-b from-lgt07-accent-top to-lgt07-accent py-3.5 text-base font-bold text-white shadow-[inset_0_1px_0_rgba(255,255,255,.5),0_14px_28px_-16px_rgba(0,0,0,.6)] transition-transform duration-150 hover:-translate-y-0.5 focus-visible:-translate-y-0.5 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-white motion-reduce:transition-none">Sign up</button>
  </form>
</section>
@import "tailwindcss";
/* Not portable to a utility, and must not be dropped: registering the angle as an
   <angle> is what lets the browser interpolate it instead of snapping. */

@property --lgt-07-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@theme {
  --color-lgt07-accent: #8b5cf6;
  --color-lgt07-accent: oklch(0.68 0.19 285);
  --color-lgt07-accent-top: #a78bfa;
  --color-lgt07-accent-top: oklch(0.74 0.16 285);
  --font-lgt07-sans: 'Manrope', system-ui, sans-serif;
  --animate-lgt07-spin: lgt-07-spin 3s linear infinite;
}

@keyframes lgt-07-spin {
  to {
    --lgt-07-angle: 360deg;
  }
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";
/* Not portable to a utility, and must not be dropped: registering the angle as an
   <angle> is what lets the browser interpolate it instead of snapping. */

@property --lgt-07-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@theme {
  --color-lgt07-accent: #8b5cf6;
  --color-lgt07-accent: oklch(0.68 0.19 285);
  --color-lgt07-accent-top: #a78bfa;
  --color-lgt07-accent-top: oklch(0.74 0.16 285);
  --font-lgt07-sans: 'Manrope', system-ui, sans-serif;
  --animate-lgt07-spin: lgt-07-spin 3s linear infinite;
}

@keyframes lgt-07-spin {
  to {
    --lgt-07-angle: 360deg;
  }
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;800&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • @property stays in CSS because it is a type registration, not a style — and without it the conic border cannot tween.
  • The mask-composite rim trick becomes three arbitrary before: utilities, so the technique is visible on the element that uses it instead of buried in a stylesheet.
  • focus-within: replaces the CSS version's identical selector and remains the entire state machine — no JS focus handlers.
  • The spin is an --animate-* theme entry, so retiming every field is a one-line change.
  • placeholder: and focus: variants absorb what were two separate rules per input.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Liquid Glass Input Form Fields
Source: https://codefronts.com/design-styles/tailwind-liquid-glass-effects/tailwind-liquid-glass-input-form-fields/

A sign-up form where focusing a field lights a refractive border that travels around it. The port keeps the one thing utilities genuinely cannot do &mdash; @property registering the angle as an <angle> so it can tween &mdash; in CSS, and expresses everything else, including the mask-composite rim trick, as before: utilities under a focus-within: variant.
## HTML
```html
<section class="lgt-07 grid w-full min-h-svh place-items-center p-15 font-lgt07-sans [background:conic-gradient(from_210deg_at_30%_30%,#2a2350,#3a2a60,#1c2450,#2a2350)]" aria-label="Tailwind liquid glass form fields">
  <form novalidate class="w-[min(400px,92vw)] rounded-[26px] border border-white/28 bg-white/10 p-8.5 backdrop-blur-[22px] backdrop-saturate-170 shadow-[inset_0_1px_0_rgba(255,255,255,.4),0_30px_60px_-30px_rgba(0,0,0,.6)]">
    <h3 class="mb-5.5 text-2xl font-extrabold text-white">Create account</h3>

    <div class="mb-4.5">
      <label for="lgt-07-name" class="mb-1.75 block text-xs font-semibold uppercase tracking-[0.06em] text-white/70">Full name</label>
      <span class="relative block before:pointer-events-none before:absolute before:-inset-0.5 before:rounded-[16px] before:p-0.5 before:opacity-0 before:transition-opacity before:duration-300 before:[background:conic-gradient(from_var(--lgt-07-angle),var(--color-lgt07-accent),oklch(0.7_0.18_200),oklch(0.72_0.17_330),var(--color-lgt07-accent))] before:[mask-image:linear-gradient(#000_0_0),linear-gradient(#000_0_0)] before:[mask-clip:content-box,border-box] before:[mask-composite:exclude] focus-within:before:opacity-100 focus-within:before:animate-lgt07-spin motion-reduce:focus-within:before:animate-none">
        <input id="lgt-07-name" type="text" autocomplete="name" placeholder="Ada Lovelace" class="relative z-1 block w-full rounded-[14px] border border-white/25 bg-white/8 px-3.75 py-3.25 text-[15px] text-white outline-none backdrop-blur-[8px] shadow-[inset_0_1px_0_rgba(255,255,255,.3)] transition-colors duration-200 placeholder:text-white/50 focus:bg-white/14">
      </span>
    </div>

    <div class="mb-4.5">
      <label for="lgt-07-email" class="mb-1.75 block text-xs font-semibold uppercase tracking-[0.06em] text-white/70">Email</label>
      <span class="relative block before:pointer-events-none before:absolute before:-inset-0.5 before:rounded-[16px] before:p-0.5 before:opacity-0 before:transition-opacity before:duration-300 before:[background:conic-gradient(from_var(--lgt-07-angle),var(--color-lgt07-accent),oklch(0.7_0.18_200),oklch(0.72_0.17_330),var(--color-lgt07-accent))] before:[mask-image:linear-gradient(#000_0_0),linear-gradient(#000_0_0)] before:[mask-clip:content-box,border-box] before:[mask-composite:exclude] focus-within:before:opacity-100 focus-within:before:animate-lgt07-spin motion-reduce:focus-within:before:animate-none">
        <input id="lgt-07-email" type="email" autocomplete="email" placeholder="ada@studio.dev" class="relative z-1 block w-full rounded-[14px] border border-white/25 bg-white/8 px-3.75 py-3.25 text-[15px] text-white outline-none backdrop-blur-[8px] shadow-[inset_0_1px_0_rgba(255,255,255,.3)] transition-colors duration-200 placeholder:text-white/50 focus:bg-white/14">
      </span>
    </div>

    <div class="mb-4.5">
      <label for="lgt-07-pass" class="mb-1.75 block text-xs font-semibold uppercase tracking-[0.06em] text-white/70">Password</label>
      <span class="relative block before:pointer-events-none before:absolute before:-inset-0.5 before:rounded-[16px] before:p-0.5 before:opacity-0 before:transition-opacity before:duration-300 before:[background:conic-gradient(from_var(--lgt-07-angle),var(--color-lgt07-accent),oklch(0.7_0.18_200),oklch(0.72_0.17_330),var(--color-lgt07-accent))] before:[mask-image:linear-gradient(#000_0_0),linear-gradient(#000_0_0)] before:[mask-clip:content-box,border-box] before:[mask-composite:exclude] focus-within:before:opacity-100 focus-within:before:animate-lgt07-spin motion-reduce:focus-within:before:animate-none">
        <input id="lgt-07-pass" type="password" autocomplete="new-password" placeholder="&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;" class="relative z-1 block w-full rounded-[14px] border border-white/25 bg-white/8 px-3.75 py-3.25 text-[15px] text-white outline-none backdrop-blur-[8px] shadow-[inset_0_1px_0_rgba(255,255,255,.3)] transition-colors duration-200 placeholder:text-white/50 focus:bg-white/14">
      </span>
    </div>

    <button type="submit" class="mt-1.5 w-full cursor-pointer rounded-[14px] border border-lgt07-accent/60 bg-linear-to-b from-lgt07-accent-top to-lgt07-accent py-3.5 text-base font-bold text-white shadow-[inset_0_1px_0_rgba(255,255,255,.5),0_14px_28px_-16px_rgba(0,0,0,.6)] transition-transform duration-150 hover:-translate-y-0.5 focus-visible:-translate-y-0.5 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-white motion-reduce:transition-none">Sign up</button>
  </form>
</section>
```
## CSS
```css
@import "tailwindcss";
/* Not portable to a utility, and must not be dropped: registering the angle as an
   <angle> is what lets the browser interpolate it instead of snapping. */

@property --lgt-07-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@theme {
  --color-lgt07-accent: #8b5cf6;
  --color-lgt07-accent: oklch(0.68 0.19 285);
  --color-lgt07-accent-top: #a78bfa;
  --color-lgt07-accent-top: oklch(0.74 0.16 285);
  --font-lgt07-sans: 'Manrope', system-ui, sans-serif;
  --animate-lgt07-spin: lgt-07-spin 3s linear infinite;
}

@keyframes lgt-07-spin {
  to {
    --lgt-07-angle: 360deg;
  }
}
```

How this works

The rim is a masked gradient, and it ports cleanly. A before: layer inset by 2px paints the conic gradient, then two masks composited with exclude punch out the middle so only a 2px rim survives. It looks exotic; as utilities it is four arbitrary values on one element:

before:absolute before:-inset-0.5 before:rounded-[16px] before:p-0.5
before:[background:conic-gradient(from_var(--lgt-07-angle),...)]
before:[mask-image:linear-gradient(#000_0_0),linear-gradient(#000_0_0)]
before:[mask-clip:content-box,border-box]
before:[mask-composite:exclude]

@property is the part that cannot leave. Without registering --lgt-07-angle as an <angle>, the browser treats it as an opaque string and the conic gradient snaps between values instead of rotating. There is no utility for a property registration, and there should not be — it is a type declaration, not a style.

focus-within: is the whole state machine. The wrapper lights its rim, the input lifts its tint, and both are driven by the browser's own notion of 'something inside me has focus'. No JS, no focus/blur handlers, no state.

Labels stay real. Every field is a genuine <label for> plus input pair with a matching autocomplete token, because a glass form that browsers cannot autofill is a worse form.

Make it yours

  • Recolour the travelling rim by editing the conic gradient's stops; three hues around the wheel reads as refraction, more than four reads as a rainbow.
  • Slow the spin to 6s in the --animate-lgt07-spin entry for a calmer, more expensive feel.
  • Make the rim static by dropping the focus-within:before:animate-* utility and keeping the gradient — still a clear focus affordance, zero animation cost.
  • Adjust the frost per field with backdrop-blur-[8px]; heavier blur on inputs hurts legibility fast.
  • Add validation states as user-invalid: variants, which key off the browser's own constraint validation rather than a JS class.

Gotchas — read before shipping

  • @property is load-bearing. Drop it in the port and the border stops animating with no error — the gradient simply jumps.
  • Use mask-image + mask-clip, not the mask shorthand. Tailwind emits each arbitrary property as its own rule and the shorthand can land after mask-composite, resetting it to add — at which point the gradient fills the whole box and sits behind your text as a coloured slab.
  • outline-none on an input is only acceptable if you replace the affordance visibly elsewhere. Here the wrapper's lit rim does that job.
  • Arbitrary values with commas inside functions are fine, but every space must be an underscore — conic-gradient(from_var(--a),#fff,#000).
  • An always-spinning border on an unfocused form is a distraction; the animation is deliberately scoped to focus-within only.
  • Input text must stay full-opacity white. text-white/70 over a frosted tile is the classic way glass forms fail WCAG.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

@property (Chrome 85+, Safari 16.4+, Firefox 128+) is what animates the rim, and it sets the same floor Tailwind v4 does. Where it is missing the field shows a static glowing ring on focus — same affordance, no rotation.

Techniques used in this demo

Plain CSSno Tailwind surface

HTML & JSno Tailwind involved

Search CodeFronts

Loading…