{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "phone",
  "title": "Phone",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/phone/Phone.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export type PhoneModel = 'iphone-17-pro' | 'galaxy-s26-ultra'\n  /** @deprecated Prefer `model`. */\n  export type PhoneVariant = 'apple' | 'android' | PhoneModel\n  export type PhoneSize = 'sm' | 'md' | 'lg'\n  export type IPhone17ProColor = 'cosmic-orange' | 'deep-blue' | 'silver'\n  export type GalaxyS26UltraColor = 'titanium-black' | 'titanium-gray' | 'titanium-silver' | 'cobalt-violet'\n  export type PhoneColor = IPhone17ProColor | GalaxyS26UltraColor | 'black' | 'natural' | 'blue'\n\n  export interface PhoneProps extends HTMLAttributes<HTMLDivElement> {\n    model?: PhoneModel\n    /** @deprecated Use `model`. */\n    variant?: PhoneVariant\n    size?: PhoneSize\n    color?: PhoneColor\n    showStatusBar?: boolean\n    time?: string\n    showHomeIndicator?: boolean\n    showNavBar?: boolean\n    ref?: HTMLDivElement | null\n  }\n\n  export interface PhoneFinish {\n    name: string\n    base: string\n    highlight: string\n    shadow: string\n    button: string\n  }\n\n  export function getFinish(model: PhoneModel, color?: PhoneColor): PhoneFinish {\n    if (model === 'iphone-17-pro') {\n      const selected: IPhone17ProColor =\n        color === 'silver'\n          ? 'silver'\n          : color === 'deep-blue' || color === 'blue' || color === 'black'\n            ? 'deep-blue'\n            : 'cosmic-orange'\n\n      return {\n        'cosmic-orange': {\n          name: 'Cosmic Orange',\n          base: '#d65f27',\n          highlight: '#f49a69',\n          button: '#bd4e1d',\n          shadow: '0 30px 70px -22px rgba(184, 72, 24, 0.62), 0 10px 24px -14px rgba(0, 0, 0, 0.5)',\n        },\n        'deep-blue': {\n          name: 'Deep Blue',\n          base: '#183867',\n          highlight: '#526f9d',\n          button: '#102d56',\n          shadow: '0 30px 70px -22px rgba(18, 45, 91, 0.7), 0 10px 24px -14px rgba(0, 0, 0, 0.55)',\n        },\n        silver: {\n          name: 'Silver',\n          base: '#b9babd',\n          highlight: '#f1f1f2',\n          button: '#9b9ca0',\n          shadow: '0 30px 70px -24px rgba(0, 0, 0, 0.42), 0 10px 24px -14px rgba(0, 0, 0, 0.38)',\n        },\n      }[selected]\n    }\n\n    const selected: GalaxyS26UltraColor =\n      color === 'titanium-gray'\n        ? 'titanium-gray'\n        : color === 'titanium-silver' || color === 'silver' || color === 'natural'\n          ? 'titanium-silver'\n          : color === 'cobalt-violet' || color === 'blue'\n            ? 'cobalt-violet'\n            : 'titanium-black'\n\n    return {\n      'titanium-black': {\n        name: 'Titanium Black',\n        base: '#242426',\n        highlight: '#66666a',\n        button: '#171719',\n        shadow: '0 30px 68px -22px rgba(0, 0, 0, 0.7), 0 10px 24px -14px rgba(0, 0, 0, 0.65)',\n      },\n      'titanium-gray': {\n        name: 'Titanium Gray',\n        base: '#66666a',\n        highlight: '#aaa9ad',\n        button: '#4a4a4e',\n        shadow: '0 30px 68px -24px rgba(0, 0, 0, 0.52), 0 10px 24px -14px rgba(0, 0, 0, 0.5)',\n      },\n      'titanium-silver': {\n        name: 'Titanium Silver',\n        base: '#b9babe',\n        highlight: '#eeeeef',\n        button: '#999a9e',\n        shadow: '0 30px 68px -24px rgba(0, 0, 0, 0.4), 0 10px 24px -14px rgba(0, 0, 0, 0.4)',\n      },\n      'cobalt-violet': {\n        name: 'Cobalt Violet',\n        base: '#4b3a74',\n        highlight: '#8a78b1',\n        button: '#352658',\n        shadow: '0 30px 68px -22px rgba(48, 31, 86, 0.68), 0 10px 24px -14px rgba(0, 0, 0, 0.55)',\n      },\n    }[selected]\n  }\n\n  const LEFT_BUTTON_STYLE =\n    'width: var(--phone-button-depth); border-radius: var(--phone-button-radius) 0 0 var(--phone-button-radius)'\n  const RIGHT_BUTTON_STYLE =\n    'width: var(--phone-button-depth); border-radius: 0 var(--phone-button-radius) var(--phone-button-radius) 0'\n\n  function styleOf(vars: Record<string, string>): string {\n    return Object.entries(vars)\n      .map(([k, v]) => `${k}:${v}`)\n      .join(';')\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let {\n    class: className,\n    model,\n    variant,\n    size = 'md',\n    color,\n    showStatusBar = true,\n    time = '9:41',\n    showHomeIndicator,\n    showNavBar,\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: PhoneProps = $props()\n\n  const sizeClass = $derived(\n    (\n      {\n        sm: 'w-[240px]',\n        md: 'w-[300px]',\n        lg: 'w-[340px]',\n      } as const\n    )[size],\n  )\n  const sizeScale = $derived(({ sm: 0.8, md: 1, lg: 1.133 })[size])\n\n  const resolvedModel = $derived.by<PhoneModel>(() => {\n    const value = model ?? variant\n    return value === 'galaxy-s26-ultra' || value === 'android' ? 'galaxy-s26-ultra' : 'iphone-17-pro'\n  })\n  const isIPhone = $derived(resolvedModel === 'iphone-17-pro')\n  const aspectRatio = $derived(isIPhone ? '71.9 / 150' : '78.1 / 163.6')\n\n  const finish = $derived(getFinish(resolvedModel, color))\n  const dataColor = $derived(finish.name.toLowerCase().replaceAll(' ', '-'))\n  const showHome = $derived(showHomeIndicator ?? isIPhone)\n  const showNav = $derived(showNavBar ?? !isIPhone)\n  const ariaLabel = $derived(`${isIPhone ? 'iPhone 17 Pro' : 'Galaxy S26 Ultra'} preview, ${finish.name}`)\n\n  const phoneVars = $derived.by(() => {\n    const scale = sizeScale\n    const px = (value: number) => `${value * scale}px`\n    const common = {\n      '--phone-finish': finish.base,\n      '--phone-finish-highlight': finish.highlight,\n      '--phone-button': finish.button,\n      '--phone-button-depth': px(3),\n      '--phone-button-radius': px(1.5),\n    }\n\n    if (isIPhone) {\n      return styleOf({\n        ...common,\n        '--phone-rim': px(2.5),\n        '--phone-bezel': px(7.5),\n        '--phone-outer-radius': px(44),\n        '--phone-bezel-radius': px(41.5),\n        '--phone-screen-radius': px(34),\n        '--phone-island-width': px(88),\n        '--phone-island-height': px(27),\n        '--phone-island-top': px(11),\n        '--phone-status-height': px(44),\n        '--phone-status-padding': px(18),\n        '--phone-status-size': px(12),\n        '--phone-indicator-height': px(5),\n        '--phone-indicator-bottom': px(8),\n      })\n    }\n\n    return styleOf({\n      ...common,\n      '--phone-rim': px(2.2),\n      '--phone-bezel': px(5.8),\n      '--phone-outer-radius': px(26.9),\n      '--phone-bezel-radius': px(24.7),\n      '--phone-screen-radius': px(18.9),\n      '--phone-camera-size': px(12),\n      '--phone-camera-top': px(10),\n      '--phone-status-height': px(36),\n      '--phone-status-padding': px(16),\n      '--phone-status-size': px(12),\n      '--phone-indicator-height': px(4),\n      '--phone-indicator-bottom': px(8),\n    })\n  })\n\n  const chassisStyle = $derived(\n    styleOf({\n      'aspect-ratio': aspectRatio,\n      'border-radius': 'var(--phone-outer-radius)',\n      background:\n        'linear-gradient(115deg, var(--phone-finish-highlight) 0%, var(--phone-finish) 38%, color-mix(in srgb, var(--phone-finish) 72%, black) 100%)',\n      'box-shadow': finish.shadow,\n    }),\n  )\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge\n  data-slot=\"phone\"\n  data-model={resolvedModel}\n  data-size={size}\n  data-color={dataColor}\n  role=\"group\"\n  aria-label={ariaLabel}\n  class={cn('relative inline-block max-w-full select-none', sizeClass, className)}\n  style={phoneVars}\n  {...restProps}\n>\n  <div data-slot=\"phone-chassis\" class=\"relative w-full\" style={chassisStyle}>\n    <div\n      aria-hidden=\"true\"\n      class=\"pointer-events-none absolute inset-0 z-30 border border-white/25 shadow-[inset_0_0_1px_1px_rgba(255,255,255,0.18),inset_0_0_3px_rgba(0,0,0,0.5)]\"\n      style=\"border-radius: var(--phone-outer-radius)\"\n    ></div>\n\n    {#if isIPhone}\n      <div\n        data-slot=\"phone-action-button\"\n        data-side=\"left\"\n        aria-hidden=\"true\"\n        class=\"absolute top-[15.8%] left-0 h-[4.8%] -translate-x-full bg-[var(--phone-button)] shadow-[inset_1px_0_rgba(255,255,255,0.22)]\"\n        style={LEFT_BUTTON_STYLE}\n      ></div>\n      <div\n        data-slot=\"phone-volume-button\"\n        data-control=\"up\"\n        data-side=\"left\"\n        aria-hidden=\"true\"\n        class=\"absolute top-[23.3%] left-0 h-[7.2%] -translate-x-full bg-[var(--phone-button)] shadow-[inset_1px_0_rgba(255,255,255,0.22)]\"\n        style={LEFT_BUTTON_STYLE}\n      ></div>\n      <div\n        data-slot=\"phone-volume-button\"\n        data-control=\"down\"\n        data-side=\"left\"\n        aria-hidden=\"true\"\n        class=\"absolute top-[32.2%] left-0 h-[7.2%] -translate-x-full bg-[var(--phone-button)] shadow-[inset_1px_0_rgba(255,255,255,0.22)]\"\n        style={LEFT_BUTTON_STYLE}\n      ></div>\n      <div\n        data-slot=\"phone-side-button\"\n        data-side=\"right\"\n        aria-hidden=\"true\"\n        class=\"absolute top-[24.1%] right-0 h-[12.2%] translate-x-full bg-[var(--phone-button)] shadow-[inset_-1px_0_rgba(255,255,255,0.22)]\"\n        style={RIGHT_BUTTON_STYLE}\n      ></div>\n      <div\n        data-slot=\"phone-camera-control\"\n        data-side=\"right\"\n        aria-hidden=\"true\"\n        class=\"absolute top-[68.5%] right-0 h-[8.7%] translate-x-full bg-[var(--phone-button)] shadow-[inset_-1px_0_rgba(255,255,255,0.22)]\"\n        style={RIGHT_BUTTON_STYLE}\n      ></div>\n    {:else}\n      <div\n        data-slot=\"phone-volume-button\"\n        data-side=\"right\"\n        aria-hidden=\"true\"\n        class=\"absolute top-[17.2%] right-0 h-[13.8%] translate-x-full bg-[var(--phone-button)] shadow-[inset_-1px_0_rgba(255,255,255,0.22)]\"\n        style={RIGHT_BUTTON_STYLE}\n      ></div>\n      <div\n        data-slot=\"phone-side-button\"\n        data-side=\"right\"\n        aria-hidden=\"true\"\n        class=\"absolute top-[33.1%] right-0 h-[8.9%] translate-x-full bg-[var(--phone-button)] shadow-[inset_-1px_0_rgba(255,255,255,0.22)]\"\n        style={RIGHT_BUTTON_STYLE}\n      ></div>\n    {/if}\n\n    <div\n      data-slot=\"phone-bezel\"\n      aria-hidden=\"true\"\n      class=\"absolute bg-[#050506] shadow-[inset_0_0_1px_rgba(255,255,255,0.32)]\"\n      style=\"inset: var(--phone-rim); border-radius: var(--phone-bezel-radius)\"\n    ></div>\n\n    <div\n      data-slot=\"phone-screen\"\n      class=\"bg-background text-foreground absolute overflow-hidden\"\n      style=\"inset: calc(var(--phone-rim) + var(--phone-bezel)); border-radius: var(--phone-screen-radius)\"\n    >\n      <div data-slot=\"phone-content\" class=\"absolute inset-0 z-10 overflow-auto\">\n        {@render children?.()}\n      </div>\n\n      {#if isIPhone}\n        <div\n          data-slot=\"phone-island\"\n          aria-hidden=\"true\"\n          class=\"absolute left-1/2 z-40 -translate-x-1/2 rounded-full bg-[#050506] shadow-[0_1px_2px_rgba(0,0,0,0.55)]\"\n          style=\"top: var(--phone-island-top); width: var(--phone-island-width); height: var(--phone-island-height)\"\n        >\n          <span\n            class=\"absolute top-1/2 right-[9%] aspect-square h-[36%] -translate-y-1/2 rounded-full bg-[#101929] ring-1 ring-[#172c4a]\"\n          >\n            <span class=\"absolute top-[16%] left-[18%] size-[28%] rounded-full bg-white/25\"></span>\n          </span>\n        </div>\n      {:else}\n        <div\n          data-slot=\"phone-camera\"\n          aria-hidden=\"true\"\n          class=\"absolute left-1/2 z-40 -translate-x-1/2 rounded-full bg-[#07090c] shadow-[0_0_0_1px_rgba(255,255,255,0.12)]\"\n          style=\"top: var(--phone-camera-top); width: var(--phone-camera-size); height: var(--phone-camera-size)\"\n        >\n          <span class=\"absolute inset-[24%] rounded-full bg-[#11243d]\"></span>\n        </div>\n      {/if}\n\n      {#if showStatusBar}\n        <div\n          data-slot=\"phone-status-bar\"\n          aria-hidden=\"true\"\n          class=\"pointer-events-none absolute inset-x-0 top-0 z-30 flex items-center justify-between font-semibold text-current\"\n          style=\"height: var(--phone-status-height); padding-inline: var(--phone-status-padding); font-size: var(--phone-status-size)\"\n        >\n          <span class=\"leading-none tabular-nums\">{time}</span>\n          <div class=\"flex items-center gap-[0.28em]\">\n            <svg viewBox=\"0 0 16 12\" class=\"h-[0.78em] w-[1.05em]\" fill=\"currentColor\" aria-hidden=\"true\">\n              <path d=\"M1 11h2V8H1v3Zm4 0h2V6H5v5Zm4 0h2V3H9v8Zm4 0h2V0h-2v11Z\" />\n            </svg>\n            <svg viewBox=\"0 0 16 12\" class=\"h-[0.78em] w-[1.05em]\" fill=\"currentColor\" aria-hidden=\"true\">\n              <path\n                d=\"M8 10.5a1.2 1.2 0 1 0 0 2.4 1.2 1.2 0 0 0 0-2.4ZM3.8 7.3l1.3 1.3a4.1 4.1 0 0 1 5.8 0l1.3-1.3a6 6 0 0 0-8.4 0ZM1 4.5l1.3 1.3a8 8 0 0 1 11.4 0L15 4.5a9.9 9.9 0 0 0-14 0Z\"\n              />\n            </svg>\n            <span class=\"relative h-[0.82em] w-[1.65em] rounded-[0.2em] border-[0.12em] border-current\">\n              <span class=\"absolute inset-[0.13em] right-[28%] rounded-[0.08em] bg-current\"></span>\n              <span class=\"absolute top-[28%] -right-[0.22em] h-[44%] w-[0.12em] rounded-r-full bg-current/65\"></span>\n            </span>\n          </div>\n        </div>\n      {/if}\n\n      {#if (isIPhone && showHome) || (!isIPhone && showNav)}\n        <div\n          data-slot={isIPhone ? 'phone-home-indicator' : 'phone-nav-bar'}\n          aria-hidden=\"true\"\n          class=\"pointer-events-none absolute inset-x-0 z-40 flex justify-center\"\n          style=\"bottom: var(--phone-indicator-bottom)\"\n        >\n          <div\n            class={cn(\n              'rounded-full bg-current',\n              isIPhone ? 'w-[36%] max-w-[120px]' : 'w-[27%] max-w-[100px] opacity-55',\n            )}\n            style=\"height: var(--phone-indicator-height)\"\n          ></div>\n        </div>\n      {/if}\n\n      <div\n        aria-hidden=\"true\"\n        class=\"pointer-events-none absolute inset-0 z-50 shadow-[inset_0_0_0_1px_rgba(0,0,0,0.5)]\"\n        style=\"border-radius: var(--phone-screen-radius)\"\n      ></div>\n    </div>\n  </div>\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/phone/Phone.svelte"
    },
    {
      "path": "packages/registry-svelte/components/phone/index.ts",
      "content": "export { default as Phone, getFinish } from './Phone.svelte'\nexport type {\n  PhoneProps,\n  PhoneModel,\n  PhoneVariant,\n  PhoneSize,\n  PhoneColor,\n  PhoneFinish,\n  IPhone17ProColor,\n  GalaxyS26UltraColor,\n} from './Phone.svelte'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/phone/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Front device frames for iPhone 17 Pro and Galaxy S26 Ultra. Real chassis aspect ratios (71.9×150 / 78.1×163.6), Dynamic Island / punch-hole, side buttons, status bar, home indicator / gesture bar, and official finish colors. Slot in any screen content.",
  "categories": [
    "display",
    "media"
  ]
}