shadimage
Smooth UI
libdatasource: ok · hit

Data

npx shadcn add "https://smoothui.dev/r/data.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Files8 · source cached 10/8/2026, 8:16:52 PM

app-icons.ts
/**
 * macOS application icons, for any demo that shows a row of apps — the dock
 * above all. Real icons rather than invented squares, because a dock only reads
 * as a dock when the icons are ones you recognise.
 *
 * Served from ImageKit at 512px; request the size you render, keeping PNG so the
 * rounded corners stay transparent: \`?tr=w-128\`.
 *
 * These are Apple’s marks, used here to illustrate the components. Fine for
 * demos on our own site; think twice before making one a shipped default in a
 * component the registry distributes.
 */

export interface AppIcon {
  /** Slug, and the filename. Stable — safe to use as a React key. */
  id: string;
  name: string;
  src: string;
}

export const appIcons: AppIcon[] = [
  {
    id: "activity-monitor",
    name: "Activity Monitor",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/activity-monitor.png",
  },
  {
    id: "airport-utility",
    name: "Airport Utility",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/airport-utility.png",
  },
  {
    id: "app-store",
    name: "App Store",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/app-store.png",
  },
  {
    id: "apple-configurator",
    name: "Apple Configurator",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/apple-configurator.png",
  },
  {
    id: "apple-developer",
    name: "Apple Developer",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/apple-developer.png",
  },
  {
    id: "apple-remote-desktop",
    name: "Apple Remote Desktop",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/apple-remote-desktop.png",
  },
  {
    id: "audio-midi-setup",
    name: "Audio MIDI Setup",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/audio-midi-setup.png",
  },
  {
    id: "automator",
    name: "Automator",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/automator.png",
  },
  {
    id: "bluetooth-file-exchange",
    name: "Bluetooth File Exchange",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/bluetooth-file-exchange.png",
  },
  {
    id: "books",
    name: "Books",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/books.png",
  },
  {
    id: "calculator",
    name: "Calculator",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/calculator.png",
  },
  {
    id: "calendar",
    name: "Calendar",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/calendar.png",
  },
  {
    id: "chess",
    name: "Chess",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/chess.png",
  },
  {
    id: "clock",
    name: "Clock",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/clock.png",
  },
  {
    id: "colorsync-utility",
    name: "ColorSync Utility",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/colorsync-utility.png",
  },
  {
    id: "compressor-encode-media",
    name: "Compressor",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/compressor-encode-media.png",
  },
  {
    id: "console",
    name: "Console",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/console.png",
  },
  {
    id: "contacts",
    name: "Contacts",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/contacts.png",
  },
  {
    id: "dictionary",
    name: "Dictionary",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/dictionary.png",
  },
  {
    id: "digital-color-meter",
    name: "Digital Color Meter",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/digital-color-meter.png",
  },
  {
    id: "disk-utility",
    name: "Disk Utility",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/disk-utility.png",
  },
  {
    id: "facetime",
    name: "FaceTime",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/facetime.png",
  },
  {
    id: "final-cut-pro-create-video",
    name: "Final Cut Pro",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/final-cut-pro-create-video.png",
  },
  {
    id: "find-my",
    name: "Find My",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/find-my.png",
  },
  {
    id: "grapher",
    name: "Grapher",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/grapher.png",
  },
  {
    id: "imovie",
    name: "iMovie",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/imovie.png",
  },
  {
    id: "keynote-design-presentations",
    name: "Keynote",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/keynote-design-presentations.png",
  },
  {
    id: "logic-pro-make-music",
    name: "Logic Pro",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/logic-pro-make-music.png",
  },
  {
    id: "mainstage-perform-live",
    name: "MainStage",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/mainstage-perform-live.png",
  },
  {
    id: "mainstage",
    name: "Mainstage",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/mainstage.png",
  },
  {
    id: "migration-assistant",
    name: "Migration Assistant",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/migration-assistant.png",
  },
  {
    id: "motion-animate-effects",
    name: "Motion",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/motion-animate-effects.png",
  },
  {
    id: "numbers-make-spreadsheets",
    name: "Numbers",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/numbers-make-spreadsheets.png",
  },
  {
    id: "pages-create-documents",
    name: "Pages",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/pages-create-documents.png",
  },
  {
    id: "photos",
    name: "Photos",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/photos.png",
  },
  {
    id: "pixelmator-pro-edit-images",
    name: "Pixelmator Pro",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/pixelmator-pro-edit-images.png",
  },
  {
    id: "podcasts",
    name: "Podcasts",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/podcasts.png",
  },
  {
    id: "preview",
    name: "Preview",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/preview.png",
  },
  {
    id: "print-center",
    name: "Print Center",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/print-center.png",
  },
  {
    id: "quicktime",
    name: "QuickTime Player",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/quicktime.png",
  },
  {
    id: "reminders",
    name: "Reminders",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/reminders.png",
  },
  {
    id: "safari",
    name: "Safari",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/safari.png",
  },
  {
    id: "screen-sharing",
    name: "Screen Sharing",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/screen-sharing.png",
  },
  {
    id: "screenshot",
    name: "Screenshot",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/screenshot.png",
  },
  {
    id: "script-editor",
    name: "Script Editor",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/script-editor.png",
  },
  {
    id: "shortcuts",
    name: "Shortcuts",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/shortcuts.png",
  },
  {
    id: "siri",
    name: "Siri",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/siri.png",
  },
  {
    id: "stickies",
    name: "Stickies",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/stickies.png",
  },
  {
    id: "stocks",
    name: "Stocks",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/stocks.png",
  },
  {
    id: "system-information",
    name: "System Information",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/system-information.png",
  },
  {
    id: "system-settings",
    name: "System Settings",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/system-settings.png",
  },
  {
    id: "terminal",
    name: "Terminal",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/terminal.png",
  },
  {
    id: "testflight",
    name: "TestFlight",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/testflight.png",
  },
  {
    id: "textedit",
    name: "TextEdit",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/textedit.png",
  },
  {
    id: "time-machine",
    name: "Time Machine",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/time-machine.png",
  },
  {
    id: "tips",
    name: "Tips",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/tips.png",
  },
  {
    id: "voice-memos",
    name: "Voice Memos",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/voice-memos.png",
  },
  {
    id: "voiceover-utility",
    name: "VoiceOver Utility",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/voiceover-utility.png",
  },
  {
    id: "weather",
    name: "Weather",
    src: "https://ik.imagekit.io/16u211libb/smoothui/app-icons/weather.png",
  },
];

export const appIconById = (id: string): AppIcon | undefined =>
  appIcons.find((icon) => icon.id === id);

/** The dock cast: recognisable, and in the order macOS ships them. */
export const dockIcons: AppIcon[] = [
  "safari",
  "photos",
  "podcasts",
  "calendar",
  "contacts",
  "reminders",
  "facetime",
  "app-store",
  "system-settings",
]
  .map((id) => appIconById(id))
  .filter((icon): icon is AppIcon => icon !== undefined);
cast.ts
import type { Scene } from "./scenes";

export interface CastImage extends Scene {
  gender: "female" | "male" | "nonbinary" | "unspecified";
  name: string;
  role: string;
  species: string;
}

export const castPeople: CastImage[] = [
  {
    alt: "Generated portrait of Maya Solis",
    gender: "female",
    id: "maya-solis",
    kind: "photo",
    name: "Maya Solis",
    role: "Product designer",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/maya-solis.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Hana Park",
    gender: "female",
    id: "hana-park",
    kind: "photo",
    name: "Hana Park",
    role: "Researcher",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/hana-park.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Zara Ndiaye",
    gender: "female",
    id: "zara-ndiaye",
    kind: "photo",
    name: "Zara Ndiaye",
    role: "Marketing",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/zara-ndiaye.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Luca Moretti",
    gender: "male",
    id: "luca-moretti",
    kind: "photo",
    name: "Luca Moretti",
    role: "Support lead",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/luca-moretti.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Jun Park",
    gender: "male",
    id: "jun-park",
    kind: "photo",
    name: "Jun Park",
    role: "Data",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/jun-park.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Marcus Hale",
    gender: "male",
    id: "marcus-hale",
    kind: "photo",
    name: "Marcus Hale",
    role: "Founder",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/marcus-hale.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Meera Kapoor",
    gender: "female",
    id: "meera-kapoor",
    kind: "photo",
    name: "Meera Kapoor",
    role: "Counsel",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/meera-kapoor.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Niels Holm",
    gender: "male",
    id: "niels-holm",
    kind: "photo",
    name: "Niels Holm",
    role: "Advisor",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/niels-holm.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Leila Hassan",
    gender: "female",
    id: "leila-hassan",
    kind: "photo",
    name: "Leila Hassan",
    role: "Researcher",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/leila-hassan.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Diego Ramos",
    gender: "male",
    id: "diego-ramos",
    kind: "photo",
    name: "Diego Ramos",
    role: "Intern",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/diego-ramos.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of In\u00e8s Moreau",
    gender: "female",
    id: "ines-moreau",
    kind: "photo",
    name: "In\u00e8s Moreau",
    role: "Ops",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/ines-moreau.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Arjun Mehta",
    gender: "male",
    id: "arjun-mehta",
    kind: "photo",
    name: "Arjun Mehta",
    role: "PM",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/arjun-mehta.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Camila Duarte",
    gender: "female",
    id: "camila-duarte",
    kind: "photo",
    name: "Camila Duarte",
    role: "Marketing",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/camila-duarte.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Sasha Kim",
    gender: "nonbinary",
    id: "sasha-kim",
    kind: "photo",
    name: "Sasha Kim",
    role: "Staff engineer",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/sasha-kim.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Freya Lund",
    gender: "female",
    id: "freya-lund",
    kind: "photo",
    name: "Freya Lund",
    role: "Product designer",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/freya-lund.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Saoirse Flynn",
    gender: "female",
    id: "saoirse-flynn",
    kind: "photo",
    name: "Saoirse Flynn",
    role: "Marketing",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/saoirse-flynn.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Rowan O'Brien",
    gender: "male",
    id: "rowan-obrien",
    kind: "photo",
    name: "Rowan O'Brien",
    role: "Support lead",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/rowan-obrien.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Erik Lindqvist",
    gender: "male",
    id: "erik-lindqvist",
    kind: "photo",
    name: "Erik Lindqvist",
    role: "Sales",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/erik-lindqvist.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Chloe Bennett",
    gender: "female",
    id: "chloe-bennett",
    kind: "photo",
    name: "Chloe Bennett",
    role: "Intern",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/chloe-bennett.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Isla Reid",
    gender: "female",
    id: "isla-reid",
    kind: "photo",
    name: "Isla Reid",
    role: "Ops",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/isla-reid.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Lars Bergman",
    gender: "male",
    id: "lars-bergman",
    kind: "photo",
    name: "Lars Bergman",
    role: "Data",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/lars-bergman.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Morgan Reed",
    gender: "nonbinary",
    id: "morgan-reed",
    kind: "photo",
    name: "Morgan Reed",
    role: "Researcher",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/morgan-reed.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Amina Osei",
    gender: "female",
    id: "amina-osei",
    kind: "photo",
    name: "Amina Osei",
    role: "Ops",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/amina-osei.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Malik Johnson",
    gender: "male",
    id: "malik-johnson",
    kind: "photo",
    name: "Malik Johnson",
    role: "PM",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/malik-johnson.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Yuki Tanaka",
    gender: "female",
    id: "yuki-tanaka",
    kind: "photo",
    name: "Yuki Tanaka",
    role: "Advisor",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/yuki-tanaka.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Helen Frost",
    gender: "female",
    id: "helen-frost",
    kind: "photo",
    name: "Helen Frost",
    role: "Counsel",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/helen-frost.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Bridget Cairns",
    gender: "female",
    id: "bridget-cairns",
    kind: "photo",
    name: "Bridget Cairns",
    role: "Founder",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/bridget-cairns.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Omar Khalil",
    gender: "male",
    id: "omar-khalil",
    kind: "photo",
    name: "Omar Khalil",
    role: "Sales",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/omar-khalil.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Sofia Reyes",
    gender: "female",
    id: "sofia-reyes",
    kind: "photo",
    name: "Sofia Reyes",
    role: "Marketing",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/sofia-reyes.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Kai Thompson",
    gender: "male",
    id: "kai-thompson",
    kind: "photo",
    name: "Kai Thompson",
    role: "Support lead",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/kai-thompson.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Ananya Sharma",
    gender: "female",
    id: "ananya-sharma",
    kind: "photo",
    name: "Ananya Sharma",
    role: "Intern",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/ananya-sharma.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Hiro Sato",
    gender: "male",
    id: "hiro-sato",
    kind: "photo",
    name: "Hiro Sato",
    role: "Advisor",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/hiro-sato.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Theo Miller",
    gender: "male",
    id: "theo-miller",
    kind: "photo",
    name: "Theo Miller",
    role: "Product designer",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/theo-miller.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Nia Brooks",
    gender: "female",
    id: "nia-brooks",
    kind: "photo",
    name: "Nia Brooks",
    role: "Marketing",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/nia-brooks.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Elise Carter",
    gender: "female",
    id: "elise-carter",
    kind: "photo",
    name: "Elise Carter",
    role: "PM",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/elise-carter.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Jordan Wells",
    gender: "nonbinary",
    id: "jordan-wells",
    kind: "photo",
    name: "Jordan Wells",
    role: "Staff engineer",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/jordan-wells.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Graham Price",
    gender: "male",
    id: "graham-price",
    kind: "photo",
    name: "Graham Price",
    role: "Counsel",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/graham-price.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Linh Nguyen",
    gender: "female",
    id: "linh-nguyen",
    kind: "photo",
    name: "Linh Nguyen",
    role: "Researcher",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/linh-nguyen.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Miguel Santos",
    gender: "male",
    id: "miguel-santos",
    kind: "photo",
    name: "Miguel Santos",
    role: "Intern",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/miguel-santos.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Amara Cole",
    gender: "female",
    id: "amara-cole",
    kind: "photo",
    name: "Amara Cole",
    role: "Product designer",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/amara-cole.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Ezra Cohen",
    gender: "male",
    id: "ezra-cohen",
    kind: "photo",
    name: "Ezra Cohen",
    role: "Founder",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/ezra-cohen.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Rosa M\u00e9ndez",
    gender: "female",
    id: "rosa-mendez",
    kind: "photo",
    name: "Rosa M\u00e9ndez",
    role: "Advisor",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/rosa-mendez.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Ren Okada",
    gender: "nonbinary",
    id: "ren-okada",
    kind: "photo",
    name: "Ren Okada",
    role: "Staff engineer",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/ren-okada.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Connor Hayes",
    gender: "male",
    id: "connor-hayes",
    kind: "photo",
    name: "Connor Hayes",
    role: "Sales",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/connor-hayes.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Fatima Rahman",
    gender: "female",
    id: "fatima-rahman",
    kind: "photo",
    name: "Fatima Rahman",
    role: "Researcher",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/fatima-rahman.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Derrick Hayes",
    gender: "male",
    id: "derrick-hayes",
    kind: "photo",
    name: "Derrick Hayes",
    role: "Support lead",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/derrick-hayes.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Nora Blake",
    gender: "female",
    id: "nora-blake",
    kind: "photo",
    name: "Nora Blake",
    role: "Ops",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/nora-blake.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Tasha Greene",
    gender: "female",
    id: "tasha-greene",
    kind: "photo",
    name: "Tasha Greene",
    role: "Marketing",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/tasha-greene.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Noah Petrov",
    gender: "male",
    id: "noah-petrov",
    kind: "photo",
    name: "Noah Petrov",
    role: "Intern",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/noah-petrov.webp",
    tone: "light",
  },
  {
    alt: "Generated portrait of Valeria Cruz",
    gender: "female",
    id: "valeria-cruz",
    kind: "photo",
    name: "Valeria Cruz",
    role: "PM",
    species: "human",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-people/valeria-cruz.webp",
    tone: "light",
  },
];

export const castAnimals: CastImage[] = [
  {
    alt: "Generated dog portrait: Maple",
    gender: "unspecified",
    id: "maple-golden",
    kind: "photo",
    name: "Maple",
    role: "Support mascot",
    species: "dog",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/maple-golden.webp",
    tone: "light",
  },
  {
    alt: "Generated dog portrait: Ink",
    gender: "unspecified",
    id: "ink-lab",
    kind: "photo",
    name: "Ink",
    role: "Ops buddy",
    species: "dog",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/ink-lab.webp",
    tone: "light",
  },
  {
    alt: "Generated dog portrait: Biscuit",
    gender: "unspecified",
    id: "biscuit-corgi",
    kind: "photo",
    name: "Biscuit",
    role: "Office pup",
    species: "dog",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/biscuit-corgi.webp",
    tone: "light",
  },
  {
    alt: "Generated cat portrait: Mango",
    gender: "unspecified",
    id: "mango-tabby",
    kind: "photo",
    name: "Mango",
    role: "Product cat",
    species: "cat",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/mango-tabby.webp",
    tone: "light",
  },
  {
    alt: "Generated cat portrait: Mist",
    gender: "unspecified",
    id: "mist-shorthair",
    kind: "photo",
    name: "Mist",
    role: "Design critter",
    species: "cat",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/mist-shorthair.webp",
    tone: "light",
  },
  {
    alt: "Generated cat portrait: Jack",
    gender: "unspecified",
    id: "tuxedo-jack",
    kind: "photo",
    name: "Jack",
    role: "Office cat",
    species: "cat",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/tuxedo-jack.webp",
    tone: "light",
  },
  {
    alt: "Generated horse portrait: Bay",
    gender: "unspecified",
    id: "bay-star",
    kind: "photo",
    name: "Bay",
    role: "Brand mare",
    species: "horse",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/bay-star.webp",
    tone: "light",
  },
  {
    alt: "Generated horse portrait: Cloud",
    gender: "unspecified",
    id: "cloud-mare",
    kind: "photo",
    name: "Cloud",
    role: "Pasture lead",
    species: "horse",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/cloud-mare.webp",
    tone: "light",
  },
  {
    alt: "Generated horse portrait: Onyx",
    gender: "unspecified",
    id: "onyx-stallion",
    kind: "photo",
    name: "Onyx",
    role: "Studio horse",
    species: "horse",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/onyx-stallion.webp",
    tone: "light",
  },
  {
    alt: "Generated chameleon portrait: Leaf",
    gender: "unspecified",
    id: "leaf-veiled",
    kind: "photo",
    name: "Leaf",
    role: "Garden scout",
    species: "chameleon",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/leaf-veiled.webp",
    tone: "light",
  },
  {
    alt: "Generated chameleon portrait: Reef",
    gender: "unspecified",
    id: "reef-panther",
    kind: "photo",
    name: "Reef",
    role: "Color QA",
    species: "chameleon",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/reef-panther.webp",
    tone: "light",
  },
  {
    alt: "Generated chameleon portrait: Sun",
    gender: "unspecified",
    id: "sun-chameleon",
    kind: "photo",
    name: "Sun",
    role: "Habitat mascot",
    species: "chameleon",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/sun-chameleon.webp",
    tone: "light",
  },
  {
    alt: "Generated fish portrait: Azure",
    gender: "unspecified",
    id: "azure-betta",
    kind: "photo",
    name: "Azure",
    role: "Aquarium lead",
    species: "fish",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/azure-betta.webp",
    tone: "light",
  },
  {
    alt: "Generated fish portrait: Coin",
    gender: "unspecified",
    id: "coin-goldfish",
    kind: "photo",
    name: "Coin",
    role: "Lucky fish",
    species: "fish",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/coin-goldfish.webp",
    tone: "light",
  },
  {
    alt: "Generated fish portrait: Flash",
    gender: "unspecified",
    id: "koi-flash",
    kind: "photo",
    name: "Flash",
    role: "Pond mascot",
    species: "fish",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/koi-flash.webp",
    tone: "light",
  },
  {
    alt: "Generated bird portrait: Rio",
    gender: "unspecified",
    id: "rio-macaw",
    kind: "photo",
    name: "Rio",
    role: "Community bird",
    species: "bird",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/rio-macaw.webp",
    tone: "light",
  },
  {
    alt: "Generated bird portrait: Pip",
    gender: "unspecified",
    id: "pip-canary",
    kind: "photo",
    name: "Pip",
    role: "Studio songbird",
    species: "bird",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/pip-canary.webp",
    tone: "light",
  },
  {
    alt: "Generated bird portrait: Luna",
    gender: "unspecified",
    id: "luna-barn",
    kind: "photo",
    name: "Luna",
    role: "Night docs",
    species: "bird",
    src: "https://ik.imagekit.io/16u211libb/smoothui/troupe-animals/luna-barn.webp",
    tone: "light",
  },
];
component-meta.ts
/**
 * Component metadata types for AI-first developer experience.
 *
 * These types define structured, machine-readable metadata for SmoothUI
 * components and blocks, enabling AI agents to discover, filter, and
 * understand components without reading source code.
 */

// ---------------------------------------------------------------------------
// Category types
// ---------------------------------------------------------------------------

export type ComponentCategory =
  | "basic-ui"
  | "button"
  | "text"
  | "ai"
  | "layout"
  | "feedback"
  | "data-display"
  | "navigation"
  | "other";

export type AnimationType = "spring" | "tween" | "gesture" | "scroll" | "none";

export type Complexity = "simple" | "moderate" | "complex";

// ---------------------------------------------------------------------------
// Component metadata
// ---------------------------------------------------------------------------

/**
 * Full metadata for a single SmoothUI component.
 *
 * Combines data from two sources:
 * - `smoothui` field in the component's package.json (authored by hand)
 * - MDX frontmatter and registry info (derived at build time)
 */
export interface ComponentMeta {
  /** Dominant animation technique */
  animationType: AnimationType;
  /** Primary UI category */
  category: ComponentCategory;
  /** Rough complexity level */
  complexity: Complexity;
  /** Hints for combining with other components */
  compositionHints: readonly string[];
  /** npm packages required (excluding peer deps) */
  dependencies: readonly string[];
  /** 1-2 sentence human-readable description */
  description: string;
  /** PascalCase display name, e.g. "AnimatedTabs" */
  displayName: string;
  /** Full documentation URL */
  docUrl: string;
  /** Whether the component respects prefers-reduced-motion */
  hasReducedMotion: boolean;
  /** shadcn-style install command */
  installCommand: string;
  /** kebab-case identifier, e.g. "animated-tabs" */
  name: string;
  /** Number of public props */
  propsCount: number;
  /** Other SmoothUI components required */
  registryDependencies: readonly string[];
  /** Registry JSON URL */
  registryUrl: string;
  /** Discoverable tags, e.g. ["animation", "tabs", "navigation"] */
  tags: readonly string[];
  /** Natural-language use-case descriptions */
  useCases: readonly string[];
}

// ---------------------------------------------------------------------------
// Block metadata
// ---------------------------------------------------------------------------

export type BlockType =
  | "hero"
  | "features"
  | "pricing"
  | "testimonials"
  | "cta"
  | "footer"
  | "header"
  | "other";

/**
 * Full metadata for a pre-built page block (section).
 */
export interface BlockMeta {
  /** Dominant animation technique */
  animationType: AnimationType;
  /** Block section type */
  blockType: BlockType;
  /** Primary UI category */
  category: ComponentCategory;
  /** Rough complexity level */
  complexity: Complexity;
  /** SmoothUI components used inside this block */
  components: readonly string[];
  /** npm packages required */
  dependencies: readonly string[];
  /** 1-2 sentence description */
  description: string;
  /** PascalCase display name */
  displayName: string;
  /** Full documentation URL */
  docUrl: string;
  /** Whether the block respects prefers-reduced-motion */
  hasReducedMotion: boolean;
  /** shadcn-style install command */
  installCommand: string;
  /** kebab-case identifier */
  name: string;
  /** Registry JSON URL */
  registryUrl: string;
  /** Discoverable tags */
  tags: readonly string[];
  /** Natural-language use-case descriptions */
  useCases: readonly string[];
}

// ---------------------------------------------------------------------------
// API response types
// ---------------------------------------------------------------------------

/**
 * Paginated list response wrapper used by all list endpoints.
 */
export interface PaginatedResponse<T> {
  data: T[];
  page: number;
  pageSize: number;
  total: number;
  totalPages: number;
}

/** GET /api/components */
export type ComponentListResponse = PaginatedResponse<ComponentMeta>;

/** GET /api/components/:name */
export interface ComponentDetailResponse {
  component: ComponentMeta;
  /** Raw source code (optional, included when ?include=source) */
  source?: string;
}

/** GET /api/blocks */
export type BlockListResponse = PaginatedResponse<BlockMeta>;

/** GET /api/blocks/:name */
export interface BlockDetailResponse {
  block: BlockMeta;
  source?: string;
}

/** Standard error envelope returned by API routes */
export interface ApiErrorResponse {
  error: string;
  status: number;
}

// ---------------------------------------------------------------------------
// Query / filter helpers
// ---------------------------------------------------------------------------

/** Supported filter parameters for component list queries */
export interface ComponentQueryParams {
  animationType?: AnimationType;
  category?: ComponentCategory;
  complexity?: Complexity;
  page?: number;
  pageSize?: number;
  search?: string;
  tag?: string;
}

/** Supported filter parameters for block list queries */
export interface BlockQueryParams {
  blockType?: BlockType;
  complexity?: Complexity;
  page?: number;
  pageSize?: number;
  search?: string;
  tag?: string;
}
index.ts
// ---------------------------------------------------------------------------
// AI-first metadata types & schema
// ---------------------------------------------------------------------------
export type {
  AnimationType,
  ApiErrorResponse,
  BlockDetailResponse,
  BlockListResponse,
  BlockMeta,
  BlockQueryParams,
  BlockType,
  Complexity,
  ComponentCategory,
  ComponentDetailResponse,
  ComponentListResponse,
  ComponentMeta,
  ComponentQueryParams,
  PaginatedResponse,
} from "./component-meta";

export type {
  ParseFailure,
  ParseResult,
  ParseSuccess,
  SmoothUIPackageMeta,
} from "./smoothui-schema";

export { parseSmoothUIMeta } from "./smoothui-schema";

// ---------------------------------------------------------------------------
// People data
// ---------------------------------------------------------------------------

/**
 * Unified Person interface for all people data
 *
 * This single interface contains all possible fields for people data.
 * Components can use only the fields they need:
 * - Team components: name, role, bio, avatar, location, experience, social, company
 * - Testimonial components: name, role, avatar, stars, content
 * - Mixed components: any combination of fields
 */
/**
 * The one real person in this package. Kept apart from the fictional cast in
 * `./people` so nothing can quietly present an invented user as a real one.
 */
export const founder = {
  avatar:
    "https://ik.imagekit.io/16u211libb/avatar-educalvolpz.jpeg?updatedAt=1765524159631",
  company: "SmoothUI",
  location: "Spain",
  name: "Eduardo Calvo",
  role: "Creator",
  social: {
    github: "https://github.com/educlopez",
    linkedin: "https://linkedin.com/in/educlopez",
    twitter: "https://twitter.com/educalvolpz",
    website: "https://educalvolopez.com",
  },
} as const;

import type { Person } from "./people";
import {
  people,
  somePeople,
  testimonials,
  testimonialsWithPeople,
} from "./people";

export type { Person, Testimonial } from "./people";
/** Fictional people and their quotes now live in `@/lib/smoothui-data/people`. */
export {
  people,
  personById,
  somePeople,
  testimonials,
  testimonialsWithPeople,
} from "./people";

// Get people who have testimonials (stars and content)
/**
 * A person joined to what they said. Blocks that render a quote need both on one
 * object, so the join happens here rather than in every block.
 */
export type QuotedPerson = Person & { content: string; stars: number };

export const testimonialsData: QuotedPerson[] = testimonialsWithPeople().map(
  (entry) => ({ ...entry.person, content: entry.quote, stars: entry.stars })
);

interface ImageKitOptions {
  format?: "auto" | "webp" | "jpg" | "jpeg" | "png" | "avif";
  height?: number;
  quality?: number;
  transformations?: string;
  width?: number;
}

/**
 * Build transformation string from options
 */
function buildTransformations(options?: ImageKitOptions): string {
  if (options?.transformations) {
    return options.transformations;
  }

  const parts: string[] = [];

  if (options?.width) {
    parts.push(`w-${options.width}`);
  }
  if (options?.height) {
    parts.push(`h-${options.height}`);
  }

  const quality = options?.quality ?? 80;
  parts.push(`q-${quality}`);

  const format = options?.format ?? "auto";
  parts.push(`f-${format}`);

  return parts.join(",");
}

/**
 * Process full URL and add transformations
 */
function processFullUrl(imagePath: string, transformations: string): string {
  const url = new URL(imagePath);
  url.searchParams.delete("updatedAt");
  const baseUrl = url.origin + url.pathname;

  if (!transformations) {
    return baseUrl;
  }
  return `${baseUrl}?tr=${transformations}`;
}

/**
 * Build local path URL
 */
function buildLocalPathUrl(imagePath: string, transformations: string): string {
  const endpoint =
    process.env.NEXT_PUBLIC_IMAGEKIT_URL_ENDPOINT ||
    process.env.IMAGEKIT_URL_ENDPOINT ||
    "https://ik.imagekit.io/16u211libb";

  const cleanPath = imagePath.startsWith("/") ? imagePath.slice(1) : imagePath;

  const imageKitPath = cleanPath.startsWith("images/")
    ? `smoothui/${cleanPath.replace("images/", "")}`
    : `smoothui/${cleanPath}`;

  const baseUrl = `${endpoint}/${imageKitPath}`;

  if (transformations) {
    return `${baseUrl}?tr=${transformations}`;
  }

  return baseUrl;
}

/**
 * Get ImageKit URL for an image with optimized transformations
 * Converts local image paths (/images/...) to ImageKit URLs with bandwidth optimization
 * @param imagePath - Local image path (e.g., "/images/avatar.jpg") or already full URL
 * @param options - Optional transformation options
 * @param options.width - Image width in pixels
 * @param options.height - Image height in pixels
 * @param options.quality - Image quality (1-100, default: 80)
 * @param options.format - Image format (auto, webp, jpg, png, etc.)
 * @param options.transformations - Raw transformation string (overrides other options)
 * @returns Full ImageKit URL with optimized transformations
 */
export function getImageKitUrl(
  imagePath: string,
  options?: ImageKitOptions
): string {
  const transformations = buildTransformations(options);

  if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
    return processFullUrl(imagePath, transformations);
  }

  return buildLocalPathUrl(imagePath, transformations);
}

/**
 * Helper function to get avatar URL with optimized size and quality
 * @param avatar - Avatar image path or URL
 * @param size - Avatar size in pixels (default: 40, will be doubled for retina)
 * @returns Optimized ImageKit URL for avatar
 */
export function getAvatarUrl(avatar: string, size = 40): string {
  // Double the size for retina displays, use higher quality for avatars
  const retinaSize = size * 2;
  return getImageKitUrl(avatar, {
    format: "auto",
    height: retinaSize,
    quality: 85, // Higher quality for faces
    width: retinaSize,
  });
}

// Helper function to get team member data (people without testimonials or all people)
export function getTeamMembers(
  count = 4,
  includeTestimonials = false
): Person[] {
  if (includeTestimonials) {
    return somePeople(count);
  }
  // People with nothing to quote, so a team grid and a testimonial wall on the
  // same page do not show the same faces.
  const quoted = new Set(testimonials.map((entry) => entry.personId));
  return people.filter((person) => !quoted.has(person.id)).slice(0, count);
}

// Helper function to get testimonials data
export function getTestimonials(count = 4): QuotedPerson[] {
  return testimonialsData.slice(0, count);
}

// Helper function to get all people data
export function getAllPeople(): Person[] {
  return people;
}

// Helper function to get people by role
export function getPeopleByRole(role: string): Person[] {
  return people.filter((person) =>
    person.role.toLowerCase().includes(role.toLowerCase())
  );
}

// Helper function to get people with testimonials
export function getPeopleWithTestimonials(): QuotedPerson[] {
  return testimonialsData;
}
people.ts
import { castPeople } from "./cast";

/** Fictional generated demo people. Names, portraits and roles come from Troupe.
 * The real founder remains separate. Company/contact fields are sample data. */
export interface Person {
  /** Deterministic per person: fallback backgrounds, rings, cursor labels. */
  accent: string;
  avatar: string;
  /** Present only where a demo needs prose about the person. */
  bio?: string;
  company: string;
  email: string;
  experience?: string;
  handle: string;
  /** Slug, and the avatar filename. Stable — safe to use as a React key. */
  id: string;
  /** Fallback for an avatar that has not loaded. */
  initials: string;
  location: string;
  name: string;
  role: string;
}

export const people: Person[] = castPeople.map((person, index) => ({
  accent: `oklch(0.72 0.15 ${(index * 47) % 360})`,
  avatar: person.src,
  company: "Demo Studio",
  email: `${person.id}@example.com`,
  handle: `@${person.id.replaceAll("-", "")}`,
  id: person.id,
  initials: person.name
    .split(" ")
    .map((part) => part[0])
    .join(""),
  location: "Remote",
  name: person.name,
  role: person.role,
}));

/** Lookup-only compatibility: old fictional IDs return complete canonical identities. */
export const legacyPersonIds: Record<string, string> = {
  "abraham-baker": "maya-solis",
  "adem-lane": "hana-park",
  "adil-floyd": "zara-ndiaye",
  "adriana-osullivan": "luca-moretti",
  "alec-whitten": "jun-park",
  "alesha-barry": "marcus-hale",
  "ali-mahdi": "meera-kapoor",
  "aliah-lane": "niels-holm",
  "alisa-hester": "leila-hassan",
  "amanda-lowery": "diego-ramos",
  "amelie-bennett": "ines-moreau",
  "amelie-laurent": "arjun-mehta",
  "ammar-foley": "camila-duarte",
  "anaiah-whitten": "sasha-kim",
  "andi-lane": "freya-lund",
  "angelica-wallace": "saoirse-flynn",
  "anita-cruz": "rowan-obrien",
  "ashton-blackwell": "erik-lindqvist",
  "ashwin-santiago": "chloe-bennett",
  "aston-hood": "isla-reid",
  "ava-bentley": "lars-bergman",
  "ava-wright": "morgan-reed",
  "ayah-wilkinson": "amina-osei",
  "aysha-becker": "malik-johnson",
  "bailey-richards": "yuki-tanaka",
  "bec-ferguson": "helen-frost",
  "belle-woods": "bridget-cairns",
  "benedict-doherty": "omar-khalil",
  "billie-wright": "sofia-reyes",
  "blake-riley": "kai-thompson",
  "brianna-ware": "ananya-sharma",
  "byron-robertson": "hiro-sato",
  "caitlyn-king": "theo-miller",
  "cameron-yang": "nia-brooks",
  "candice-wu": "elise-carter",
  "clifford-jennings": "jordan-wells",
  "cohen-lozano": "graham-price",
  "courtney-turner": "linh-nguyen",
  "danyal-lester": "miguel-santos",
  "demi-wilkinson": "amara-cole",
  "dillan-nguyen": "ezra-cohen",
  "drew-cano": "rosa-mendez",
  "eduard-franz": "ren-okada",
  "elena-owens": "connor-hayes",
  "elisa-nishikawa": "fatima-rahman",
  "elsie-roy": "derrick-hayes",
  "erica-wyatt": "nora-blake",
  "ethan-campbell": "tasha-greene",
  "ethan-valdez": "noah-petrov",
  "eva-bond": "valeria-cruz",
  "eve-leroy": "maya-solis",
  "fergus-gray": "hana-park",
  "fleur-cook": "zara-ndiaye",
  "florence-shaw": "luca-moretti",
  "frank-whitaker": "jun-park",
  "franklin-mays": "marcus-hale",
  "freya-browning": "meera-kapoor",
  "genevieve-mclean": "niels-holm",
  "harriet-rojas": "leila-hassan",
  "harry-bender": "diego-ramos",
  "hasan-johns": "ines-moreau",
  "herbert-fowler": "arjun-mehta",
  "isla-allison": "camila-duarte",
  "isobel-carroll": "sasha-kim",
  "isobel-fuller": "freya-lund",
  "jackson-reed": "saoirse-flynn",
  "jay-shepard": "rowan-obrien",
  "jaya-willis": "erik-lindqvist",
  "jayden-moss": "chloe-bennett",
  "jessie-meyton": "isla-reid",
  "jonathan-kelly": "lars-bergman",
  "jordan-burgess": "morgan-reed",
  "joshua-wilson": "amina-osei",
  "julius-vaughan": "malik-johnson",
  "kaden-scott": "yuki-tanaka",
  "kaitlin-hale": "helen-frost",
  "kari-rasmussen": "bridget-cairns",
  "kate-morrison": "omar-khalil",
  "katherine-moss": "sofia-reyes",
  "katy-fuller": "kai-thompson",
  "kelly-williams": "ananya-sharma",
  "kelsey-lowe": "hiro-sato",
  "koray-okumus": "theo-miller",
  "kyla-clay": "nia-brooks",
  "lana-steiner": "elise-carter",
  "levi-rocha": "jordan-wells",
  "leyton-fields": "graham-price",
  "liam-hood": "linh-nguyen",
  "lily-rose-chedjou": "miguel-santos",
  "loki-bright": "amara-cole",
  "lola-sanders": "ezra-cohen",
  "lori-bryson": "rosa-mendez",
  "lucy-bond": "ren-okada",
  "lulu-meyers": "connor-hayes",
  "luqman-anthony": "fatima-rahman",
  "lyle-kauffman": "derrick-hayes",
  "maddison-gillespie": "nora-blake",
  "madeleine-pitts": "tasha-greene",
  "marco-gross": "noah-petrov",
  "marco-kelly": "valeria-cruz",
  "marvin-robbins": "maya-solis",
  "mathilde-lewis": "hana-park",
  "maxwell-tan": "zara-ndiaye",
  "mikey-lawrence": "luca-moretti",
  "mollie-hall": "jun-park",
  "molly-vaughan": "marcus-hale",
  "nala-goins": "meera-kapoor",
  "natali-craig": "niels-holm",
  "nic-fassbender": "leila-hassan",
  "nicola-harris": "diego-ramos",
  "nicolas-trevino": "ines-moreau",
  "nicolas-wang": "arjun-mehta",
  "nikolas-gibbons": "camila-duarte",
  "noah-pierre": "sasha-kim",
  "noel-baldwin": "freya-lund",
  "olivia-rhye": "saoirse-flynn",
  "olly-schroeder": "rowan-obrien",
  "orlando-diggs": "erik-lindqvist",
  "owen-garcia": "chloe-bennett",
  "owen-harding": "isla-reid",
  "phoenix-baker": "lars-bergman",
  "pippa-wilkinson": "morgan-reed",
  "priya-shepard": "amina-osei",
  "rachael-strong": "malik-johnson",
  "rayhan-zua": "yuki-tanaka",
  "rene-wells": "helen-frost",
  "rhea-levine": "bridget-cairns",
  "rhianna-shepard": "omar-khalil",
  "riley-omoore": "sofia-reyes",
  "rory-huff": "kai-thompson",
  "rosalee-melvin": "ananya-sharma",
  "sally-mason": "hiro-sato",
  "sarah-page": "theo-miller",
  "scott-clayton": "nia-brooks",
  "sienna-hewitt": "elise-carter",
  "sophia-perez": "jordan-wells",
  "stefan-sears": "graham-price",
  "youssef-roberson": "linh-nguyen",
  "zahir-mays": "miguel-santos",
  "zahra-christensen": "amara-cole",
  "zaid-schwartz": "ezra-cohen",
  "zara-bush": "rosa-mendez",
  "zaynab-donnelly": "ren-okada",
  "zuzanna-burke": "connor-hayes",
};
/**
 * What someone said, kept apart from who they are — a person appears in far more
 * demos than testimonial walls, and most of them have nothing to quote.
 */
export interface Testimonial {
  personId: string;
  quote: string;
  /** 1–5. */
  stars: number;
}

export const testimonials: Testimonial[] = [
  {
    personId: "maya-solis",
    quote:
      "The design system is incredibly well thought out. Every component feels intentional and polished.",
    stars: 5,
  },
  {
    personId: "hana-park",
    quote:
      "The performance is outstanding. Our bundle size stayed the same while getting beautiful animations.",
    stars: 5,
  },
  {
    personId: "zara-ndiaye",
    quote:
      "The accessibility features are top-notch. Every component follows WCAG guidelines perfectly.",
    stars: 5,
  },
  {
    personId: "luca-moretti",
    quote: "Thoughtful details make every interaction feel easier.",
    stars: 5,
  },
  {
    personId: "jun-park",
    quote:
      "Best UI library I've used. The TypeScript support is excellent and the components are highly customizable.",
    stars: 5,
  },
  {
    personId: "marcus-hale",
    quote:
      "Our users love the smooth interactions. It's made our product feel premium and professional.",
    stars: 4,
  },
  {
    personId: "meera-kapoor",
    quote: "Thoughtful details make every interaction feel easier.",
    stars: 5,
  },
  {
    personId: "niels-holm",
    quote: "Thoughtful details make every interaction feel easier.",
    stars: 5,
  },
  {
    personId: "leila-hassan",
    quote: "Thoughtful details make every interaction feel easier.",
    stars: 5,
  },
  {
    personId: "diego-ramos",
    quote: "SmoothUI is my go-to for fast, beautiful UIs.",
    stars: 5,
  },
];

/** Lookup by id, for a demo that wants one specific person. */
export const personById = (id: string): Person | undefined =>
  people.find((person) => person.id === (legacyPersonIds[id] ?? id));

/**
 * A stable slice, so two demos asking for 4 people get the same 4 people rather
 * than a random draw that changes on every render.
 */
export const somePeople = (count: number, offset = 0): Person[] =>
  Array.from(
    { length: Math.min(count, people.length) },
    (_, index) => people[(offset + index) % people.length]
  );

/** Testimonials joined to their author, which is what a wall of quotes needs. */
export const testimonialsWithPeople = (): (Testimonial & {
  person: Person;
})[] =>
  testimonials
    .map((testimonial) => ({
      ...testimonial,
      person: personById(testimonial.personId),
    }))
    .filter(
      (entry): entry is Testimonial & { person: Person } =>
        entry.person !== undefined
    );
products.ts
/** Generated, unbranded product imagery shared by the gallery and demos. */
export const productImages = [
  {
    alt: "Ivory sneaker with coral details on a lavender surface against a blurred cobalt and apricot background",
    id: "sneaker",
    src: "https://ik.imagekit.io/16u211libb/smoothui/products/ivory-sneaker.webp",
    title: "Ivory runner",
  },
  {
    alt: "Ivory over-ear headphones on a lavender surface against a blurred coral, apricot and violet background",
    id: "headphones",
    src: "https://ik.imagekit.io/16u211libb/smoothui/products/ivory-headphones.webp",
    title: "Ivory headphones",
  },
] as const;
scenes.ts
import { castAnimals, castPeople } from "./cast";

/**
 * Curated blurred, saturated, grain-rich demo backgrounds served from ImageKit.
 * Legacy IDs resolve through aliases, but never return retired source artwork.
 * Original registry and URLs are kept in docs/contributing/media-provenance/.
 * Request rendered dimensions with `?tr=w-640,f-auto`.
 */
const BASE = "https://ik.imagekit.io/16u211libb/smoothui/scenes";

export interface Scene {
  /** Descriptive, and usable verbatim as alt text. */
  alt: string;
  /** Slug, and the filename. Stable — safe to use as a React key. */
  id: string;
  /**
   * `photo` has a subject and wants to be seen; `abstract` is texture and is
   * safe to put type on top of. Pick by whether the image is the content or the
   * surface behind it.
   */
  kind: "photo" | "abstract";
  src: string;
  /** Optional editorial title for artwork. */
  title?: string;
  /** Roughly how bright the image is, for choosing overlay and text colour. */
  tone: "light" | "dark";
}

const scene = (
  id: string,
  alt: string,
  tone: Scene["tone"],
  kind: Scene["kind"]
): Scene => ({ alt, id, kind, src: `${BASE}/${id}.webp`, tone });

/** Approved Magnific backgrounds; retired sources live only in provenance files. */
export const approvedAbstracts: Scene[] = [
  scene(
    "amber-violet",
    "Amber and violet abstract color fields",
    "light",
    "abstract"
  ),
  scene(
    "cobalt-pink",
    "Cobalt blue and pink abstract color fields",
    "dark",
    "abstract"
  ),
  scene(
    "coral-lavender",
    "Coral and lavender abstract color fields",
    "light",
    "abstract"
  ),
  scene(
    "cyan-tangerine",
    "Cyan and tangerine abstract color fields",
    "light",
    "abstract"
  ),
  scene(
    "coral-cyan",
    "Coral and cyan heavily blurred color fields with fine grain",
    "light",
    "abstract"
  ),
  scene(
    "violet-tangerine",
    "Violet and tangerine heavily blurred color fields with fine grain",
    "dark",
    "abstract"
  ),
  scene(
    "teal-apricot",
    "Teal and apricot heavily blurred color fields with fine grain",
    "light",
    "abstract"
  ),
  scene(
    "plum-coral",
    "Plum and coral heavily blurred color fields with fine grain",
    "dark",
    "abstract"
  ),
  scene(
    "azure-apricot",
    "Azure and apricot softly blurred color fields with fine grain",
    "light",
    "abstract"
  ),
  scene(
    "sky-peach",
    "Sky blue and peach heavily blurred color fields with fine grain",
    "light",
    "abstract"
  ),
  scene(
    "fuchsia-cobalt",
    "Fuchsia and cobalt heavily blurred color fields with fine grain",
    "dark",
    "abstract"
  ),
  scene(
    "golden-coral",
    "Golden yellow and coral heavily blurred color fields with fine grain",
    "light",
    "abstract"
  ),
];

/** Legacy IDs are lookup-only aliases; retired artwork is archived in docs. */
export const sceneAliases: Record<string, string> = {
  "blue-ridge-night": "fuchsia-cobalt",
  "cloud-meadow": "sky-peach",
  "cobalt-fade": "cobalt-pink",
  "cyan-aurora": "teal-apricot",
  "dune-shadow": "golden-coral",
  "ember-drift": "plum-coral",
  "ember-drift-warm": "amber-violet",
  "ember-streaks": "amber-violet",
  "golden-ridge": "violet-tangerine",
  "lake-camp": "teal-apricot",
  "lilac-bloom": "coral-lavender",
  "linen-texture": "azure-apricot",
  "meadow-haze": "teal-apricot",
  "moonrise-valley": "plum-coral",
  "nebula-canyon": "fuchsia-cobalt",
  "pale-iridescence": "coral-lavender",
  "prism-meadow": "coral-cyan",
  "rust-peak": "golden-coral",
  "silk-waves": "sky-peach",
  "watercolor-grove": "azure-apricot",
};

export const scenes: Scene[] = approvedAbstracts;

/**
 * Card art, for demos where the component is a finish applied over a printed
 * face — holographic foil above all. The artwork carries the whole card, so a
 * demo needs no fake name, HP or rarity block built around it.
 */
const CARD_BASE = "https://ik.imagekit.io/16u211libb/smoothui/cards";

export const cards: Scene[] = [
  {
    alt: "Nymara — Eclipse Guardian: a violet-winged, antlered fantasy creature above a ruined arch, framed as a Moonveil collectible card",
    id: "nymara",
    kind: "photo",
    src: `${CARD_BASE}/nymara.webp`,
    title: "Nymara — Eclipse Guardian",
    tone: "dark",
  },
];

export const landscapes: Scene[] = [
  {
    alt: "AI-generated black-sand coast with foaming surf, green volcanic cliffs and a warm horizon",
    id: "volcanic-coast",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/volcanic-coast.webp",
    title: "Volcanic coast",
    tone: "dark",
  },
  {
    alt: "AI-generated terracotta dunes with sweeping wind-shaped ridges and distant blue mountains",
    id: "terracotta-dunes",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/terracotta-dunes.webp",
    title: "Terracotta dunes",
    tone: "dark",
  },
  {
    alt: "AI-generated green rice terraces curving around misty hills in warm morning light",
    id: "emerald-terraces",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/emerald-terraces.webp",
    title: "Emerald terraces",
    tone: "light",
  },
  {
    alt: "AI-generated turquoise icebergs reflected in a calm lagoon beneath snowy mountains and a pink sky",
    id: "glacial-lagoon",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/glacial-lagoon.webp",
    title: "Glacial lagoon",
    tone: "light",
  },
  {
    alt: "AI-generated turquoise river winding around a sandstone canyon beneath a clear blue sky",
    id: "turquoise-canyon",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/turquoise-canyon.webp",
    title: "Turquoise canyon",
    tone: "light",
  },
  {
    alt: "Generated alpine mountain peaks above blue valleys in soft peach dawn light",
    id: "alpine-dawn",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/alpine-dawn.webp",
    title: "Alpine dawn",
    tone: "light",
  },
  {
    alt: "Generated turquoise sea and white surf between warm rocky coastal cliffs",
    id: "tidal-cove",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/tidal-cove.webp",
    title: "Tidal cove",
    tone: "light",
  },
  {
    alt: "Generated evergreen forest with moss, turquoise mist and golden sunlight",
    id: "emerald-forest",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/landscapes/emerald-forest.webp",
    title: "Emerald forest",
    tone: "dark",
  },
];

export const events: Scene[] = [
  {
    alt: "Generated outdoor cinema in a garden at dusk, with a glowing screen, rows of chairs and string lights",
    id: "open-air-cinema",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/events/open-air-cinema.webp",
    title: "Open-Air Cinema",
    tone: "dark",
  },
  {
    alt: "Generated surfers carrying boards toward ocean waves under a peach sunrise",
    id: "dawn-patrol",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/events/dawn-patrol.webp",
    title: "Dawn Patrol",
    tone: "light",
  },
  {
    alt: "Generated candlelit supper gathering around a long table in a courtyard",
    id: "supper-club",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/events/supper-club.webp",
    title: "Supper Club",
    tone: "dark",
  },
  {
    alt: "Generated group practicing yoga on a terrace overlooking the sea at sunrise",
    id: "sunrise-yoga",
    kind: "photo",
    src: "https://ik.imagekit.io/16u211libb/smoothui/events/sunrise-yoga.webp",
    title: "Sunrise Yoga",
    tone: "dark",
  },
];

export const sceneById = (id: string): Scene | undefined =>
  scenes.find((item) => item.id === (sceneAliases[id] ?? id)) ??
  landscapes.find((item) => item.id === id) ??
  events.find((item) => item.id === id) ??
  cards.find((item) => item.id === id) ??
  castPeople.find((item) => item.id === id) ??
  castAnimals.find((item) => item.id === id);

/** Everything of one kind, for a demo that only wants photos or only texture. */
export const scenesOfKind = (kind: Scene["kind"]): Scene[] =>
  scenes.filter((item) => item.kind === kind);

/**
 * A stable slice, so a gallery renders the same images on every load rather
 * than a fresh random draw.
 */
export const someScenes = (count: number, offset = 0): Scene[] =>
  Array.from(
    { length: Math.min(count, scenes.length) },
    (_, index) => scenes[(offset + index) % scenes.length]
  );
smoothui-schema.ts
/**
 * Schema definition and validation for the `smoothui` field in component
 * package.json files.
 *
 * Each component's package.json may include a `smoothui` object with
 * machine-readable metadata. This module provides the TypeScript type for
 * that field and a runtime validation/parsing function.
 *
 * @example package.json
 * ```json
 * {
 *   "name": "@repo/animated-tabs",
 *   "description": "Animated tabs component with sliding indicator",
 *   "smoothui": {
 *     "category": "navigation",
 *     "tags": ["animation", "tabs", "navigation"],
 *     "complexity": "moderate",
 *     "animationType": "spring",
 *     "useCases": [
 *       "Tab navigation with smooth transitions",
 *       "Section switcher with animated indicator"
 *     ],
 *     "compositionHints": [
 *       "Combine with animated-tooltip for rich tab headers"
 *     ],
 *     "hasReducedMotion": true
 *   }
 * }
 * ```
 */

import type {
  AnimationType,
  Complexity,
  ComponentCategory,
} from "./component-meta";

// ---------------------------------------------------------------------------
// Schema type
// ---------------------------------------------------------------------------

/**
 * Shape of the `smoothui` field inside a component's package.json.
 * Only includes metadata that is authored by hand — derived fields
 * (name, displayName, dependencies, URLs) are computed at build time.
 */
export interface SmoothUIPackageMeta {
  /** Dominant animation technique */
  animationType: AnimationType;
  /** Primary UI category */
  category: ComponentCategory;
  /** Rough complexity level */
  complexity: Complexity;
  /** Hints for combining with other components */
  compositionHints: string[];
  /** Whether the component respects prefers-reduced-motion */
  hasReducedMotion: boolean;
  /** Discoverable tags */
  tags: string[];
  /** Natural-language use-case descriptions */
  useCases: string[];
}

// ---------------------------------------------------------------------------
// Validation constants
// ---------------------------------------------------------------------------

const VALID_CATEGORIES: readonly ComponentCategory[] = [
  "basic-ui",
  "button",
  "text",
  "ai",
  "layout",
  "feedback",
  "data-display",
  "navigation",
  "other",
];

const VALID_COMPLEXITIES: readonly Complexity[] = [
  "simple",
  "moderate",
  "complex",
];

const VALID_ANIMATION_TYPES: readonly AnimationType[] = [
  "spring",
  "tween",
  "gesture",
  "scroll",
  "none",
];

// ---------------------------------------------------------------------------
// Validation helpers
// ---------------------------------------------------------------------------

interface ValidationError {
  field: string;
  message: string;
}

const isString = (value: unknown): value is string => typeof value === "string";

const isBoolean = (value: unknown): value is boolean =>
  typeof value === "boolean";

const isStringArray = (value: unknown): value is string[] =>
  Array.isArray(value) && value.every(isString);

const isOneOf = <T extends string>(
  value: unknown,
  allowed: readonly T[]
): value is T =>
  isString(value) && (allowed as readonly string[]).includes(value);

// ---------------------------------------------------------------------------
// Parse result
// ---------------------------------------------------------------------------

export interface ParseSuccess {
  data: SmoothUIPackageMeta;
  success: true;
}

export interface ParseFailure {
  errors: ValidationError[];
  success: false;
}

export type ParseResult = ParseSuccess | ParseFailure;

// ---------------------------------------------------------------------------
// Parser
// ---------------------------------------------------------------------------

/**
 * Validate and parse a raw `smoothui` field from a component package.json.
 *
 * Returns a discriminated union so callers can handle errors explicitly.
 *
 * @param raw - The unknown value read from `packageJson.smoothui`
 * @returns A `ParseResult` with either typed data or a list of errors
 */
export const parseSmoothUIMeta = (raw: unknown): ParseResult => {
  const errors: ValidationError[] = [];

  if (raw === null || raw === undefined || typeof raw !== "object") {
    return {
      errors: [{ field: "smoothui", message: "Must be a non-null object" }],
      success: false,
    };
  }

  const obj = raw as Record<string, unknown>;

  // category — required
  if (!isOneOf(obj.category, VALID_CATEGORIES)) {
    errors.push({
      field: "category",
      message: `Must be one of: ${VALID_CATEGORIES.join(", ")}`,
    });
  }

  // tags — required string[]
  if (!isStringArray(obj.tags)) {
    errors.push({
      field: "tags",
      message: "Must be an array of strings",
    });
  }

  // complexity — required
  if (!isOneOf(obj.complexity, VALID_COMPLEXITIES)) {
    errors.push({
      field: "complexity",
      message: `Must be one of: ${VALID_COMPLEXITIES.join(", ")}`,
    });
  }

  // animationType — required
  if (!isOneOf(obj.animationType, VALID_ANIMATION_TYPES)) {
    errors.push({
      field: "animationType",
      message: `Must be one of: ${VALID_ANIMATION_TYPES.join(", ")}`,
    });
  }

  // useCases — required string[]
  if (!isStringArray(obj.useCases)) {
    errors.push({
      field: "useCases",
      message: "Must be an array of strings",
    });
  }

  // compositionHints — required string[]
  if (!isStringArray(obj.compositionHints)) {
    errors.push({
      field: "compositionHints",
      message: "Must be an array of strings",
    });
  }

  // hasReducedMotion — required boolean
  if (!isBoolean(obj.hasReducedMotion)) {
    errors.push({
      field: "hasReducedMotion",
      message: "Must be a boolean",
    });
  }

  if (errors.length > 0) {
    return { errors, success: false };
  }

  return {
    data: {
      animationType: obj.animationType as AnimationType,
      category: obj.category as ComponentCategory,
      complexity: obj.complexity as Complexity,
      compositionHints: obj.compositionHints as string[],
      hasReducedMotion: obj.hasReducedMotion as boolean,
      tags: obj.tags as string[],
      useCases: obj.useCases as string[],
    },
    success: true,
  };
};

More from Smooth UI

Browse all components in this library →