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,
};
};