---
name: apple-vision-pro-style-ui
description: Use when you want "Premium, clean, spatial" - Uses floating glass panels, spatial layering, blur, and gentle depth.
---

# Apple Vision Pro Style UI

> **Category:** Premium / Futuristic  -  **Personality:** Premium, clean, spatial
>
> **Best use:** Modern app UIs, product pages
>
> **Ratings:** Professional 5/5 - Casual 3/5 - Exotic 4/5 - Visual impact 5/5 - Difficulty 4/5 - Performance cost 3/5

## What it is

A spatial-computing OS mockup, in the visionOS idiom: several **frosted-glass app windows float at different depths** inside one shared 3D space, casting soft depth shadows, with a dock at the bottom. The depth is real — a `perspective` scene holds a `preserve-3d` "world" whose panels are pushed toward the viewer with `translateZ()`. A gentle pointer-driven tilt parallaxes the layers so they feel genuinely suspended in the room. Reach for it on premium product pages or app marketing where you want the calm, luminous, "computer that lives in space" feeling. (Distinct from a single liquid-glass surface — the whole point here is multiple windows layered in depth.)

## Dependencies / CDN

None - pure CSS + a little vanilla JS. The demo only loads two display/body fonts (optional):

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Onest:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
```

## HTML

A perspective `scene` → a tilting `world` → glass panels placed at different `translateZ` depths, plus a dock:

```html
<div class="stage">
  <!-- immersive room behind the glass: a blurred photo + drifting colour orbs -->
  <div class="env" aria-hidden="true">
    <span class="orb a"></span><span class="orb b"></span><span class="orb c"></span>
  </div>

  <div class="scene">
    <div class="world">
      <section class="layer gallery glass">…big photos window… <div class="handle"></div></section>
      <section class="layer music   glass">…now-playing card…   <div class="handle"></div></section>
      <section class="layer weather glass">…14&deg; widget…      <div class="handle"></div></section>
      <aside   class="layer toast   glass">…message toast…</aside>

      <nav class="layer dock glass" aria-label="App dock">
        <button class="dock-item" style="--g:linear-gradient(160deg,#3a8bff,#1fcfd0)">
          <svg viewBox="0 0 24 24"><path d="M4 16l5-6 4 4 3-3 4 5"/><circle cx="16.4" cy="8" r="1.7"/></svg>
          <span class="tip">Photos</span>
        </button>
        <!-- …more app tiles… -->
      </nav>
    </div>
  </div>
</div>
```

## CSS

```css
/* The 3D space. perspective on the parent, preserve-3d on the moving world. */
.scene{position:absolute; inset:0; perspective:1600px; perspective-origin:50% 42%}
.world{position:absolute; inset:0; transform-style:preserve-3d;
  transform: rotateX(var(--rx,2deg)) rotateY(var(--ry,-4deg));   /* JS updates --rx/--ry */
  transition: transform .5s cubic-bezier(.22,.61,.36,1)}          /* eases the parallax */

/* visionOS "dark glass" material — the look of every panel. */
.glass{
  position:absolute;
  background: linear-gradient(150deg, rgba(255,255,255,.14), rgba(255,255,255,.03) 42%, rgba(255,255,255,.07)),
              rgba(22,25,36,.46);                                 /* tint keeps text legible */
  -webkit-backdrop-filter: blur(30px) saturate(165%) brightness(1.06);
          backdrop-filter: blur(30px) saturate(165%) brightness(1.06);
  border: 1px solid rgba(255,255,255,.16);
  border-radius: 26px;
  box-shadow: 0 42px 90px -34px rgba(0,0,0,.78),                  /* the soft depth shadow */
              0 16px 40px -26px rgba(0,0,0,.6),
              inset 0 1px 0 rgba(255,255,255,.30),                /* lit top edge */
              inset 0 0 0 1px rgba(255,255,255,.04);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass{ background: rgba(16,18,28,.92) }                        /* graceful fallback */
}

/* DEPTH: each panel sits on its own Z plane → nearer panels look bigger & parallax more. */
.gallery{ left:7%;   top:14%; width:46%; transform: translateZ(0)     }
.music  { right:6%;  top:21%; width:30%; transform: translateZ(62px)  }
.toast  { left:9%;   top:64%; width:31%; transform: translateZ(92px)  }
.weather{ left:3.5%; top:7%;  width:21%; transform: translateZ(118px) }
.dock   { left:50%;  bottom:4.5%;        transform: translateX(-50%) translateZ(150px) }

/* The little visionOS "grab" handle under each window */
.handle{ width:46px; height:5px; border-radius:99px; margin:14px auto 2px; background:rgba(255,255,255,.34) }

/* Drifting colour orbs behind the glass give the blur something to refract */
.orb{ position:absolute; border-radius:50%; filter:blur(72px); opacity:.5 }
.orb.a{ width:48%; height:62%; background:#3b6dff; top:-14%; left:-8%;  animation:drift 22s ease-in-out infinite }
@keyframes drift{ 0%,100%{transform:translate3d(0,0,0) scale(1)} 50%{transform:translate3d(0,-26px,0) scale(1.09)} }

@media (prefers-reduced-motion:reduce){ .orb{animation:none} .world{transition:none} }
```

## JavaScript

```js
// Tilt the whole world toward the pointer; the depth layers parallax automatically.
var stage = document.querySelector('.stage'), world = document.querySelector('.world');
var reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;

if (stage && world && !reduce){
  stage.addEventListener('pointermove', function(e){
    if (window.innerWidth <= 760) return;                 // stacked layout on small screens
    var r = stage.getBoundingClientRect();
    var px = (e.clientX - r.left) / r.width  - 0.5;       // -0.5 .. 0.5
    var py = (e.clientY - r.top)  / r.height - 0.5;
    world.style.setProperty('--ry', (-4 + px * 10).toFixed(2) + 'deg');
    world.style.setProperty('--rx', ( 2 - py * 5 ).toFixed(2) + 'deg');
  });
  stage.addEventListener('pointerleave', function(){
    world.style.removeProperty('--rx');                   // ease back to the resting tilt
    world.style.removeProperty('--ry');
  });
}
```

## How it works

- **`perspective` + `translateZ` = real depth.** The `.scene` sets the camera (`perspective:1600px`); `.world` is `transform-style:preserve-3d` so its children keep their own Z position. Each panel is pushed forward a different amount (`translateZ(62px)`, `118px`, …). Under the perspective camera, nearer panels render slightly larger and — crucially — shift by different amounts when the world rotates. That differential motion is the "suspended in space" cue.
- **One tilt drives all the parallax.** JS only writes two custom properties (`--rx`, `--ry`) on `.world`; the CSS `transition` eases every change, so a single rotation animates the entire stack of depths coherently. No per-panel math.
- **The glass is a tinted, blurred surface.** `backdrop-filter: blur() saturate() brightness()` frosts whatever is behind it; a `rgba(22,25,36,.46)` base tint keeps white text readable and guarantees the panels still read as glass even where a browser is stingy about sampling the backdrop inside a 3D context.
- **Depth shadow sells the float.** A large, very soft, downward `box-shadow` plus an inset top highlight makes each pane look like a lit slab hovering above the room. Drifting blurred orbs behind the glass give the blur colour to refract.

## Customization

- **Depth spread:** raise the `translateZ` values (and/or lower `perspective`, e.g. `1200px`) for a more dramatic, exploded layering; tighten them for a flatter, calmer stack.
- **Parallax strength:** change the `px * 10` / `py * 5` multipliers in the JS, and the `--rx`/`--ry` defaults (`2deg` / `-4deg`) set the resting tilt.
- **Glass tone:** for "light" visionOS glass, swap the base tint to `rgba(255,255,255,.16)` and drop `brightness` — but re-check text contrast.
- **Frost & lift:** `blur(18px)` → `blur(36px)` for lighter/heavier frost; bigger, softer `box-shadow` = floats higher.
- **The room:** replace the background photo and the orb colours to re-theme the whole environment (a warm desert, a cool night, a studio).

## Accessibility & performance

- `backdrop-filter` is GPU-bound — keep the number of simultaneously-blurred panels modest (this demo uses ~6) and **never animate the blur radius**; animate `transform` only.
- The parallax, the orbs and the equaliser are all gated behind `@media (prefers-reduced-motion: reduce)` (and the JS checks `matchMedia` before binding), so reduced-motion users get a clean **static layered scene**.
- All controls are real `<button>`s with `aria-label`s; SVG glyphs are `aria-hidden`; the play button reflects state via `aria-pressed`; `:focus-visible` rings are provided.
- Below 760px the 3D is switched off (`perspective:none`, `transform-style:flat`) and the panels reflow into a single stacked column — no horizontal scroll, no clipped windows.

## Gotchas

- **`backdrop-filter` needs the `-webkit-` prefix** (Safari/iOS) or it silently does nothing.
- **Backdrop-filter inside `preserve-3d` is browser-finicky.** Sampling of the backdrop across a 3D context can be imperfect, so the panels carry a solid-ish tint as insurance — they read as glass regardless. Keep the blurred backdrop (photo/orbs) as a sibling *behind* `.world`, not inside it.
- **An ancestor `filter` will break a descendant's `backdrop-filter`.** Don't wrap the glass in a `filter`-ed element; use the drifting orbs (their `filter:blur` is on themselves, behind the glass) instead.
- **`overflow:hidden` + `border-radius`** on the stage needs `isolation:isolate` so the blur samples only inside the frame.
- **Don't position panels with `transform: translate()` if you also need `translateZ`** — set placement with `left/top` and reserve the panel's `transform` for depth, or the two will fight. (The pointer parallax lives one level up, on `.world`.)
