# iPhone 17 Pro Max

> Front-facing iPhone 17 Pro Max device frame with silver titanium rail, black rounded bezel, Dynamic Island, and iOS status bar - empty screen by default; optional children slot.

A shadcn/ui block from the [@uiception](https://uiception.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/uiception/iphone-17-pro-max).

- Page, with a live preview: https://shadcn.exchange/r/uiception/iphone-17-pro-max
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/uiception/iphone-17-pro-max-light-7647cbe2a5124b68.png
- Registry item: https://uiception.com/r/iphone-17-pro-max.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://uiception.com/r/iphone-17-pro-max.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Files

### `registry/new-york/blocks/iphone-17-pro-max/page.tsx`

```tsx
import Iphone17ProMax from "./components/iphone-17-pro-max"

export default function Page() {
  return (
    <div className="px-4 py-4 md:px-6 md:py-6">
      <Iphone17ProMax />
    </div>
  )
}
```

### `registry/new-york/blocks/iphone-17-pro-max/components/iphone-17-pro-max.tsx`

```tsx
import { cn } from "@/lib/utils"

import IphoneDynamicIsland from "./iphone-dynamic-island"
import IphoneScreen, { type IphoneScreenProps } from "./iphone-screen"
import IphoneStatusBar, {
  type IphoneStatusBarProps,
} from "./iphone-status-bar"

import "../styles/iphone-17-pro-max.css"

type Iphone17ProMaxProps = {
  className?: string
  statusBar?: Omit<IphoneStatusBarProps, "className">
} & Omit<IphoneScreenProps, "className">

export default function Iphone17ProMax({
  className,
  minHeight,
  aspectRatio = "1320 / 2740",
  statusBar,
  children,
}: Iphone17ProMaxProps) {
  return (
    <div
      className={cn(
        "ip17-root relative mx-auto w-full max-w-[18rem] min-w-0 drop-shadow-(--ip17-shell-shadow)",
        className
      )}
      role="img"
      aria-label="iPhone 17 Pro Max device frame mockup"
    >
      {/* Left side buttons: Action + Volume Up/Down */}
      <span
        className="ip17-button absolute top-[14%] -left-0.5 z-0 h-7 w-0.75 rounded-l-sm"
        aria-hidden
      />
      <span
        className="ip17-button absolute top-[22%] -left-0.5 z-0 h-11 w-0.75 rounded-l-sm"
        aria-hidden
      />
      <span
        className="ip17-button absolute top-[32%] -left-0.5 z-0 h-11 w-0.75 rounded-l-sm"
        aria-hidden
      />

      {/* Right side button: Power */}
      <span
        className="ip17-button absolute top-[26%] -right-0.5 z-0 h-16 w-0.75 rounded-r-sm"
        aria-hidden
      />

      {/*
        Media fills the shell first; silver rail + black lip are overlays on top.
        That way corners cannot show a light gap between content and frame.
      */}
      <div className="ip17-shell relative z-1 overflow-hidden rounded-[3rem]">
        <IphoneScreen minHeight={minHeight} aspectRatio={aspectRatio}>
          {children}
        </IphoneScreen>
        <div className="ip17-rail-overlay" aria-hidden />
        <div className="ip17-bezel-overlay" aria-hidden />
        <IphoneDynamicIsland />
        <IphoneStatusBar {...statusBar} />
      </div>
    </div>
  )
}
```

### `registry/new-york/blocks/iphone-17-pro-max/components/iphone-screen.tsx`

```tsx
import type { CSSProperties, ReactNode } from "react"

import { cn } from "@/lib/utils"

export type IphoneScreenProps = {
  className?: string
  minHeight?: string | number
  aspectRatio?: string
  children?: ReactNode
}

export default function IphoneScreen({
  className,
  minHeight = "26rem",
  aspectRatio = "1320 / 2740",
  children,
}: IphoneScreenProps) {
  const style = {
    ...(aspectRatio ? { aspectRatio } : {}),
    minHeight: typeof minHeight === "number" ? `${minHeight}px` : minHeight,
  } satisfies CSSProperties

  return (
    <div
      className={cn(
        "ip17-screen relative z-1 box-border w-full overflow-hidden rounded-[3rem]",
        children ? "bg-black" : "bg-(--ip17-screen)",
        className
      )}
      style={style}
    >
      {children ? (
        <div className="ip17-screen-slot absolute inset-0 overflow-hidden rounded-[inherit]">
          {children}
        </div>
      ) : null}
    </div>
  )
}
```

### `registry/new-york/blocks/iphone-17-pro-max/components/iphone-status-bar.tsx`

```tsx
import { cn } from "@/lib/utils"

/** iOS battery fill states: yellow Low Power Mode, red low, green charging. */
export type IphoneBatteryState = "normal" | "low-power" | "low" | "charging"

export type IphoneStatusBarProps = {
  className?: string
  time?: string
  signalBars?: 1 | 2 | 3 | 4
  batteryLevel?: number
  batteryState?: IphoneBatteryState
  tint?: "dark" | "light"
}

/** Every glyph renders at this exact height so the row reads as one size. */
const GLYPH_HEIGHT = 12

/** `normal` inherits the row tint; the rest use scoped iOS system colors. */
const BATTERY_FILL: Record<IphoneBatteryState, string> = {
  normal: "currentColor",
  "low-power": "var(--ip17-battery-low-power)",
  low: "var(--ip17-battery-low)",
  charging: "var(--ip17-battery-charging)",
}

function SignalBarsIcon({ bars }: { bars: 1 | 2 | 3 | 4 }) {
  const barHeights = [5, 7.25, 9.5, 12] as const

  return (
    <svg
      viewBox="0 0 16.5 12"
      width="16.5"
      height={GLYPH_HEIGHT}
      fill="currentColor"
      aria-hidden
    >
      {barHeights.map((height, index) => (
        <rect
          key={height}
          x={index * 4.5}
          y={12 - height}
          width="3"
          height={height}
          rx="1"
          opacity={index < bars ? 1 : 0.3}
        />
      ))}
    </svg>
  )
}

function WifiIcon() {
  return (
    <svg
      viewBox="0 0 16 12"
      width="16"
      height={GLYPH_HEIGHT}
      fill="none"
      aria-hidden
    >
      <path
        d="M0.8 4.9a9.6 9.6 0 0 1 14.4 0"
        stroke="currentColor"
        strokeWidth="1.7"
        strokeLinecap="round"
      />
      <path
        d="M4.5 8.4a5 5 0 0 1 7 0"
        stroke="currentColor"
        strokeWidth="1.7"
        strokeLinecap="round"
      />
      <circle cx="8" cy="10.6" r="1.4" fill="currentColor" />
    </svg>
  )
}

function BatteryIcon({
  level,
  state,
}: {
  level: number
  state: IphoneBatteryState
}) {
  const clamped = Math.min(100, Math.max(0, level))
  const fillWidth = (clamped / 100) * 18

  return (
    <svg
      viewBox="0 0 25 12"
      width="25"
      height={GLYPH_HEIGHT}
      fill="none"
      aria-hidden
    >
      <rect
        x="0.5"
        y="0.5"
        width="21"
        height="11"
        rx="3.2"
        stroke="currentColor"
        strokeOpacity="0.4"
      />
      <path
        d="M22.8 4.2a1.8 1.8 0 0 1 0 3.6Z"
        fill="currentColor"
        fillOpacity="0.4"
      />
      <rect
        x="2"
        y="2"
        width={fillWidth}
        height="8"
        rx="2"
        fill={BATTERY_FILL[state]}
      />
      {state === "charging" ? (
        <path
          d="M11.9 2.4 8.2 7.1h2.2l-.9 2.9 3.7-4.9h-2.3z"
          fill="var(--ip17-bezel)"
          fillOpacity="0.85"
        />
      ) : null}
    </svg>
  )
}

export default function IphoneStatusBar({
  className,
  time = "08:00",
  signalBars = 4,
  batteryLevel = 100,
  batteryState = "normal",
  tint = "dark",
}: IphoneStatusBarProps) {
  return (
    <div
      className={cn(
        "pointer-events-none absolute inset-x-0 top-3.5 z-6 flex h-7 items-center justify-between px-5",
        tint === "dark" ? "text-black" : "text-white",
        className
      )}
      aria-hidden
    >
      <span className="text-[0.8125rem] leading-none font-semibold tracking-tight">
        {time}
      </span>
      <div className="flex items-center gap-1.5">
        <SignalBarsIcon bars={signalBars} />
        <WifiIcon />
        <BatteryIcon level={batteryLevel} state={batteryState} />
      </div>
    </div>
  )
}
```

### `registry/new-york/blocks/iphone-17-pro-max/components/iphone-dynamic-island.tsx`

```tsx
import { cn } from "@/lib/utils"

type IphoneDynamicIslandProps = {
  className?: string
}

export default function IphoneDynamicIsland({
  className,
}: IphoneDynamicIslandProps) {
  return (
    <div
      className={cn(
        "pointer-events-none absolute top-7 left-[51%] z-6 flex h-5 w-[27%] max-w-24 min-w-19 -translate-x-1/2 -translate-y-1/2 items-center justify-end rounded-full bg-(--ip17-island) pr-1.5",
        className
      )}
      aria-hidden
    >
      <span className="ip17-camera relative block size-2 shrink-0 rounded-full" />
    </div>
  )
}
```

### `registry/new-york/blocks/iphone-17-pro-max/styles/iphone-17-pro-max.css`

```css
/* iphone-17-pro-max — silver titanium tokens, rail/button gradients, camera pseudo-elements */

.ip17-root {
  --ip17-silver-light: #cfcfd3;
  --ip17-silver-mid: #b4b4b9;
  --ip17-silver-dark: #96969c;
  --ip17-silver-deep: #7a7a80;
  --ip17-bezel: #060608;
  --ip17-screen: #f5f5f7;
  --ip17-island: #0a0a0c;
  --ip17-frame-highlight: rgb(255 255 255 / 0.45);
  --ip17-frame-shadow: rgb(0 0 0 / 0.28);
  --ip17-shell-shadow: 0 24px 48px -16px rgb(0 0 0 / 0.28);
  --ip17-shell-ring: rgb(0 0 0 / 0.14);
  --ip17-button: #b0b0b5;
  --ip17-button-edge: #909096;
  --ip17-rail-width: 3px;
  --ip17-bezel-width: 3.5px;
  --ip17-battery-low-power: #ffd60a;
  --ip17-battery-low: #ff453a;
  --ip17-battery-charging: #34c759;
}

.dark .ip17-root {
  --ip17-shell-shadow: 0 28px 56px -16px rgb(0 0 0 / 0.55);
  --ip17-shell-ring: rgb(255 255 255 / 0.1);
}

.ip17-shell {
  box-shadow: 0 0 0 1px var(--ip17-shell-ring);
}

/*
  Silver titanium ring drawn as an overlay (masked gradient border).
  Center stays transparent so media underneath shows through.
*/
.ip17-rail-overlay {
  position: absolute;
  inset: 0;
  z-index: 5;
  border-radius: inherit;
  pointer-events: none;
  padding: var(--ip17-rail-width);
  background: linear-gradient(
    180deg,
    var(--ip17-silver-light) 0%,
    var(--ip17-silver-mid) 55%,
    var(--ip17-silver-dark) 100%
  );
  box-shadow:
    inset 0 1px 0 var(--ip17-frame-highlight),
    inset 0 -1px 0 var(--ip17-frame-shadow);
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  mask-composite: exclude;
}

/* Black display lip on top of media — separates content from the silver ring. */
.ip17-bezel-overlay {
  position: absolute;
  inset: var(--ip17-rail-width);
  z-index: 5;
  border-radius: calc(3rem - var(--ip17-rail-width));
  pointer-events: none;
  box-shadow: inset 0 0 0 var(--ip17-bezel-width) var(--ip17-bezel);
}

.ip17-button {
  background: linear-gradient(
    180deg,
    var(--ip17-silver-light) 0%,
    var(--ip17-button) 45%,
    var(--ip17-button-edge) 100%
  );
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.45),
    inset 0 -1px 0 rgb(0 0 0 / 0.2),
    0 0 0 0.5px rgb(0 0 0 / 0.12);
}

.ip17-camera {
  background: radial-gradient(
    circle at 38% 32%,
    #2e2e38 0%,
    #17171e 42%,
    #0a0a0e 78%,
    #050507 100%
  );
  box-shadow:
    0 0 0 1px rgb(0 0 0 / 0.7),
    inset 0 1px 1.5px rgb(0 0 0 / 0.6),
    inset 0 -0.5px 0 rgb(255 255 255 / 0.05);
}

.ip17-camera::before {
  content: "";
  position: absolute;
  inset: 22%;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, #181820 0%, #08080c 100%);
  box-shadow: inset 0 0 1px rgb(0 0 0 / 0.8);
}

.ip17-camera::after {
  content: "";
  position: absolute;
  top: 22%;
  left: 30%;
  width: 28%;
  height: 28%;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 50%,
    rgb(255 255 255 / 0.35) 0%,
    rgb(255 255 255 / 0.08) 55%,
    transparent 100%
  );
}

.ip17-screen-slot > * {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Overscale so object-cover media fully covers the curved clip, including AA fringe. */
  transform: scale(1.08);
  transform-origin: center;
}
```
