Pure TailwindMIT licensed

Tailwind 3D Perspective Flip Dropdown

A panel that folds open from its top edge like a hinged lid. v4 finally ships the 3D primitives as first-class utilities — perspective-[800px], transform-3d, rotate-x-90, origin-top — so the entire effect is markup, and the stylesheet holds nothing but tokens.

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="tdd-03 w-full min-h-svh grid justify-center content-start bg-linear-[135deg,var(--color-tdd03-bg1),var(--color-tdd03-bg2)] font-tdd03-sans px-5 py-8" aria-label="3D perspective flip dropdown demo">
  <nav class="relative z-10 flex items-center gap-10 rounded-2xl bg-tdd03-ink px-8 py-4 shadow-[0_8px_32px_oklch(0.22_0.04_265/0.25)]" aria-label="Primary">
    <span class="mr-auto text-lg font-bold tracking-[3px] text-white">NOVA</span>
    <div class="flex gap-1">
      <div class="group relative after:absolute after:inset-x-0 after:top-full after:h-2.5 after:content-['']">
        <a href="#work" class="block rounded-lg px-4 py-2 text-sm font-semibold tracking-[0.5px] text-white/75 no-underline transition-colors duration-150 group-hover:bg-white/10 group-hover:text-white focus-visible:bg-white/10 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd03-acc">Work</a>
        <div class="pointer-events-none absolute top-[calc(100%+10px)] left-1/2 -translate-x-1/2 perspective-[800px] transform-3d group-hover:pointer-events-auto group-focus-within:pointer-events-auto">
          <div class="flex min-w-45 -rotate-x-90 origin-top flex-col gap-0.5 rounded-xl border border-tdd03-edge bg-white p-1.5 opacity-0 shadow-[0_20px_60px_oklch(0.22_0.04_265/0.2)] transition-[transform,opacity] duration-400 ease-tdd03 group-hover:rotate-x-0 group-hover:opacity-100 group-focus-within:rotate-x-0 group-focus-within:opacity-100 motion-reduce:rotate-x-0 motion-reduce:duration-150">
            <a href="#case-studies" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Case Studies</a>
            <a href="#branding" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Branding</a>
            <a href="#motion" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Motion</a>
            <a href="#web-design" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Web Design</a>
          </div>
        </div>
      </div>
      <div class="group relative after:absolute after:inset-x-0 after:top-full after:h-2.5 after:content-['']">
        <a href="#services" class="block rounded-lg px-4 py-2 text-sm font-semibold tracking-[0.5px] text-white/75 no-underline transition-colors duration-150 group-hover:bg-white/10 group-hover:text-white focus-visible:bg-white/10 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd03-acc">Services</a>
        <div class="pointer-events-none absolute top-[calc(100%+10px)] left-1/2 -translate-x-1/2 perspective-[800px] transform-3d group-hover:pointer-events-auto group-focus-within:pointer-events-auto">
          <div class="flex min-w-45 -rotate-x-90 origin-top flex-col gap-0.5 rounded-xl border border-tdd03-edge bg-white p-1.5 opacity-0 shadow-[0_20px_60px_oklch(0.22_0.04_265/0.2)] transition-[transform,opacity] duration-400 ease-tdd03 group-hover:rotate-x-0 group-hover:opacity-100 group-focus-within:rotate-x-0 group-focus-within:opacity-100 motion-reduce:rotate-x-0 motion-reduce:duration-150">
            <a href="#strategy" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Strategy</a>
            <a href="#design" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Design</a>
            <a href="#development" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Development</a>
          </div>
        </div>
      </div>
      <div class="group relative after:absolute after:inset-x-0 after:top-full after:h-2.5 after:content-['']">
        <a href="#studio" class="block rounded-lg px-4 py-2 text-sm font-semibold tracking-[0.5px] text-white/75 no-underline transition-colors duration-150 group-hover:bg-white/10 group-hover:text-white focus-visible:bg-white/10 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd03-acc">Studio</a>
        <div class="pointer-events-none absolute top-[calc(100%+10px)] left-1/2 -translate-x-1/2 perspective-[800px] transform-3d group-hover:pointer-events-auto group-focus-within:pointer-events-auto">
          <div class="flex min-w-45 -rotate-x-90 origin-top flex-col gap-0.5 rounded-xl border border-tdd03-edge bg-white p-1.5 opacity-0 shadow-[0_20px_60px_oklch(0.22_0.04_265/0.2)] transition-[transform,opacity] duration-400 ease-tdd03 group-hover:rotate-x-0 group-hover:opacity-100 group-focus-within:rotate-x-0 group-focus-within:opacity-100 motion-reduce:rotate-x-0 motion-reduce:duration-150">
            <a href="#about" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">About</a>
            <a href="#team" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Team</a>
            <a href="#culture" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Culture</a>
          </div>
        </div>
      </div>
    </div>
  </nav>
</section>
@import "tailwindcss";

@theme {
  --color-tdd03-bg1: #f1f5f9;
  --color-tdd03-bg1: oklch(0.97 0.005 250);
  --color-tdd03-bg2: #e0e7ff;
  --color-tdd03-bg2: oklch(0.92 0.04 275);
  --color-tdd03-ink: #0f172a;
  --color-tdd03-ink: oklch(0.21 0.04 265);
  --color-tdd03-edge: #e2e8f0;
  --color-tdd03-edge: oklch(0.92 0.01 255);
  --color-tdd03-tint: #eef2ff;
  --color-tdd03-tint: oklch(0.96 0.02 275);
  --color-tdd03-acc: #6366f1;
  --color-tdd03-acc: oklch(0.59 0.2 278);
  --font-tdd03-sans: 'Syne', system-ui, sans-serif;
  --ease-tdd03: cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tdd-03 {
  width: 100%;
  min-height: 100svh;
}

.tdd-03 ::selection {
  background: var(--color-tdd03-ink);
  color: #e2e8f0;
}

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";

@theme {
  --color-tdd03-bg1: #f1f5f9;
  --color-tdd03-bg1: oklch(0.97 0.005 250);
  --color-tdd03-bg2: #e0e7ff;
  --color-tdd03-bg2: oklch(0.92 0.04 275);
  --color-tdd03-ink: #0f172a;
  --color-tdd03-ink: oklch(0.21 0.04 265);
  --color-tdd03-edge: #e2e8f0;
  --color-tdd03-edge: oklch(0.92 0.01 255);
  --color-tdd03-tint: #eef2ff;
  --color-tdd03-tint: oklch(0.96 0.02 275);
  --color-tdd03-acc: #6366f1;
  --color-tdd03-acc: oklch(0.59 0.2 278);
  --font-tdd03-sans: 'Syne', system-ui, sans-serif;
  --ease-tdd03: cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tdd-03 {
  width: 100%;
  min-height: 100svh;
}

.tdd-03 ::selection {
  background: var(--color-tdd03-ink);
  color: #e2e8f0;
}
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=Syne:wght@500;600;700&display=swap">

What changed in the port

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

  • v4 ships perspective-*, transform-3d and rotate-x-*, so this demo needs zero arbitrary properties — the CSS version's whole 3D block disappears.
  • Splitting the frustum (wrapper) from the rotation (panel) across two elements is now self-documenting: you can see which element owns which half of the effect.
  • The hover bridge sits on the group rather than on the perspective wrapper, because the wrapper is pointer-events-none while closed.
  • motion-reduce:rotate-x-0 leaves the opacity transition doing the work — the reduced-motion path is still a reveal, just a flat one.
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 3D Perspective Flip Dropdown
Source: https://codefronts.com/navigation/tailwind-dropdown-menus/tailwind-3d-perspective-flip-dropdown/

A panel that folds open from its top edge like a hinged lid. v4 finally ships the 3D primitives as first-class utilities — perspective-[800px], transform-3d, rotate-x-90, origin-top — so the entire effect is markup, and the stylesheet holds nothing but tokens.
## HTML
```html
<section class="tdd-03 w-full min-h-svh grid justify-center content-start bg-linear-[135deg,var(--color-tdd03-bg1),var(--color-tdd03-bg2)] font-tdd03-sans px-5 py-8" aria-label="3D perspective flip dropdown demo">
  <nav class="relative z-10 flex items-center gap-10 rounded-2xl bg-tdd03-ink px-8 py-4 shadow-[0_8px_32px_oklch(0.22_0.04_265/0.25)]" aria-label="Primary">
    <span class="mr-auto text-lg font-bold tracking-[3px] text-white">NOVA</span>
    <div class="flex gap-1">
      <div class="group relative after:absolute after:inset-x-0 after:top-full after:h-2.5 after:content-['']">
        <a href="#work" class="block rounded-lg px-4 py-2 text-sm font-semibold tracking-[0.5px] text-white/75 no-underline transition-colors duration-150 group-hover:bg-white/10 group-hover:text-white focus-visible:bg-white/10 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd03-acc">Work</a>
        <div class="pointer-events-none absolute top-[calc(100%+10px)] left-1/2 -translate-x-1/2 perspective-[800px] transform-3d group-hover:pointer-events-auto group-focus-within:pointer-events-auto">
          <div class="flex min-w-45 -rotate-x-90 origin-top flex-col gap-0.5 rounded-xl border border-tdd03-edge bg-white p-1.5 opacity-0 shadow-[0_20px_60px_oklch(0.22_0.04_265/0.2)] transition-[transform,opacity] duration-400 ease-tdd03 group-hover:rotate-x-0 group-hover:opacity-100 group-focus-within:rotate-x-0 group-focus-within:opacity-100 motion-reduce:rotate-x-0 motion-reduce:duration-150">
            <a href="#case-studies" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Case Studies</a>
            <a href="#branding" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Branding</a>
            <a href="#motion" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Motion</a>
            <a href="#web-design" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Web Design</a>
          </div>
        </div>
      </div>
      <div class="group relative after:absolute after:inset-x-0 after:top-full after:h-2.5 after:content-['']">
        <a href="#services" class="block rounded-lg px-4 py-2 text-sm font-semibold tracking-[0.5px] text-white/75 no-underline transition-colors duration-150 group-hover:bg-white/10 group-hover:text-white focus-visible:bg-white/10 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd03-acc">Services</a>
        <div class="pointer-events-none absolute top-[calc(100%+10px)] left-1/2 -translate-x-1/2 perspective-[800px] transform-3d group-hover:pointer-events-auto group-focus-within:pointer-events-auto">
          <div class="flex min-w-45 -rotate-x-90 origin-top flex-col gap-0.5 rounded-xl border border-tdd03-edge bg-white p-1.5 opacity-0 shadow-[0_20px_60px_oklch(0.22_0.04_265/0.2)] transition-[transform,opacity] duration-400 ease-tdd03 group-hover:rotate-x-0 group-hover:opacity-100 group-focus-within:rotate-x-0 group-focus-within:opacity-100 motion-reduce:rotate-x-0 motion-reduce:duration-150">
            <a href="#strategy" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Strategy</a>
            <a href="#design" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Design</a>
            <a href="#development" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Development</a>
          </div>
        </div>
      </div>
      <div class="group relative after:absolute after:inset-x-0 after:top-full after:h-2.5 after:content-['']">
        <a href="#studio" class="block rounded-lg px-4 py-2 text-sm font-semibold tracking-[0.5px] text-white/75 no-underline transition-colors duration-150 group-hover:bg-white/10 group-hover:text-white focus-visible:bg-white/10 focus-visible:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdd03-acc">Studio</a>
        <div class="pointer-events-none absolute top-[calc(100%+10px)] left-1/2 -translate-x-1/2 perspective-[800px] transform-3d group-hover:pointer-events-auto group-focus-within:pointer-events-auto">
          <div class="flex min-w-45 -rotate-x-90 origin-top flex-col gap-0.5 rounded-xl border border-tdd03-edge bg-white p-1.5 opacity-0 shadow-[0_20px_60px_oklch(0.22_0.04_265/0.2)] transition-[transform,opacity] duration-400 ease-tdd03 group-hover:rotate-x-0 group-hover:opacity-100 group-focus-within:rotate-x-0 group-focus-within:opacity-100 motion-reduce:rotate-x-0 motion-reduce:duration-150">
            <a href="#about" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">About</a>
            <a href="#team" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Team</a>
            <a href="#culture" class="block rounded-lg px-3.5 py-2.5 text-sm font-medium text-tdd03-ink no-underline transition-colors duration-150 hover:bg-tdd03-tint hover:text-tdd03-acc focus-visible:bg-tdd03-tint focus-visible:outline-none">Culture</a>
          </div>
        </div>
      </div>
    </div>
  </nav>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tdd03-bg1: #f1f5f9;
  --color-tdd03-bg1: oklch(0.97 0.005 250);
  --color-tdd03-bg2: #e0e7ff;
  --color-tdd03-bg2: oklch(0.92 0.04 275);
  --color-tdd03-ink: #0f172a;
  --color-tdd03-ink: oklch(0.21 0.04 265);
  --color-tdd03-edge: #e2e8f0;
  --color-tdd03-edge: oklch(0.92 0.01 255);
  --color-tdd03-tint: #eef2ff;
  --color-tdd03-tint: oklch(0.96 0.02 275);
  --color-tdd03-acc: #6366f1;
  --color-tdd03-acc: oklch(0.59 0.2 278);
  --font-tdd03-sans: 'Syne', system-ui, sans-serif;
  --ease-tdd03: cubic-bezier(0.34, 1.56, 0.64, 1);
}

.tdd-03 {
  width: 100%;
  min-height: 100svh;
}

.tdd-03 ::selection {
  background: var(--color-tdd03-ink);
  color: #e2e8f0;
}
```

How this works

Perspective on the parent, rotation on the child. The oldest mistake in CSS 3D is putting both on one element, which flattens the result. The utility split makes the two roles obvious:

<div class="perspective-[800px] transform-3d">
  <div class="-rotate-x-90 origin-top group-hover:rotate-x-0">

The hinge is origin-top. Move it to origin-bottom and the same two utilities give you a trapdoor.

The overshoot sells it. ease-tdd03 is cubic-bezier(0.34,1.56,0.64,1); the rotation passes 0° by a couple of degrees and settles, which is what a real lid does.

No overflow-hidden anywhere in the 3D subtree — it silently flattens transform-3d, and the fold becomes a fade.

Make it yours

  • Depth: lower perspective-[800px] to perspective-[400px] for a far more extreme frustum.
  • Hinge: origin-top → origin-bottom for an upward trapdoor, or origin-left for a door.
  • Settle: swap ease-tdd03 for ease-out if the overshoot reads as sloppy at your panel size.
  • Duration: duration-400; push to 600ms to emphasise the physical metaphor.

Gotchas — read before shipping

  • perspective on the same element as rotate-x-* produces a flat result — it must be on an ancestor.
  • overflow-hidden and transform-3d are mutually exclusive in practice: the former flattens the latter.
  • Safari can misbehave when a preserve-3d subtree sits inside a fixed ancestor; keep the dropdown in a normal stacking context.
  • The folded panel still occupies layout space in the stacking context — keep pointer-events-none on the wrapper until open.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

CSS 3D transforms are universally supported; v4's perspective-*/transform-3d utilities compile to plain CSS.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

Search CodeFronts

Loading…