App

Contact List

A responsive grid of six contact cards, up to three columns, each with a greyscale Cloud shape avatar, name, role badge and title, and a ruled footer split into Email and Call links.

Free, MIT licenseOpen in Browse
import { EnvelopeSimpleIcon, PhoneIcon } from "@phosphor-icons/react/dist/ssr"
import { cn } from "cn"

import {
  CloudShape,
  type CloudPalette,
} from "@/components/graphics/cloud-shapes"
import { Badge } from "@/components/ui/badge"
import { buttonVariants } from "@/components/ui/button"
import { Card, CardContent, CardFooter } from "@/components/ui/card"
import {
  Item,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from "@/components/ui/item"
import { Separator } from "@/components/ui/separator"

// The copy: change text here, not in the markup.
const COPY = {
  email: "Email",
  call: "Call",
} as const

// Knob: the orb's shading; zero chroma keeps it greyscale.
const ORB: CloudPalette = {
  main: "oklch(0.55 0 0)",
  accent: "oklch(0.7 0 0)",
}

const PEOPLE = [
  {
    name: "JoAnn Morgan",
    title: "Instrumentation Controller",
    role: "Firing Room",
    email: "joann@orbcore.co",
    telephone: "+1-555-0141",
  },
  {
    name: "Gene Kranz",
    title: "Flight Director",
    role: "Mission Control",
    email: "gene@orbcore.co",
    telephone: "+1-555-0142",
  },
  {
    name: "Katherine Johnson",
    title: "Trajectory Analyst",
    role: "Flight Dynamics",
    email: "katherine@orbcore.co",
    telephone: "+1-555-0143",
  },
  {
    name: "Margaret Hamilton",
    title: "Flight Software Lead",
    role: "Guidance Computer",
    email: "margaret@orbcore.co",
    telephone: "+1-555-0144",
  },
  {
    name: "Günter Wendt",
    title: "Pad Leader",
    role: "Launch Pad",
    email: "gunter@orbcore.co",
    telephone: "+1-555-0145",
  },
  {
    name: "Steve Bales",
    title: "Guidance Officer",
    role: "Mission Control",
    email: "steve@orbcore.co",
    telephone: "+1-555-0146",
  },
] as const

// The two actions share the ruled footer at equal widths.
const ACTION = cn(
  buttonVariants({ variant: "ghost" }),
  "h-14 flex-1 rounded-none"
)

export default function ContactList() {
  return (
    <ul
      role="list"
      className="mx-auto grid w-full max-w-5xl gap-(--card-gap) sm:grid-cols-2 lg:grid-cols-3"
    >
      {PEOPLE.map((person) => (
        <li key={person.email}>
          <Card className="h-full gap-0 py-0">
            <CardContent className="flex-1 py-6">
              <Item className="p-0">
                <ItemMedia>
                  <CloudShape palette={ORB} width={40} height={40} />
                </ItemMedia>
                <ItemContent className="gap-1">
                  <ItemTitle className="flex-wrap gap-x-2 gap-y-1 text-base">
                    <span>{person.name}</span>
                    <Badge variant="secondary">{person.role}</Badge>
                  </ItemTitle>
                  <ItemDescription className="text-sm">
                    {person.title}
                  </ItemDescription>
                </ItemContent>
              </Item>
            </CardContent>
            <Separator />
            <CardFooter className="px-0">
              <a href={`mailto:${person.email}`} className={ACTION}>
                <EnvelopeSimpleIcon weight="light" aria-hidden="true" />
                {COPY.email}
                <span className="sr-only">, {person.name}</span>
              </a>
              <Separator orientation="vertical" />
              <a href={`tel:${person.telephone}`} className={ACTION}>
                <PhoneIcon weight="light" aria-hidden="true" />
                {COPY.call}
                <span className="sr-only">, {person.name}</span>
              </a>
            </CardFooter>
          </Card>
        </li>
      ))}
    </ul>
  )
}

Installation

pnpm dlx shadcn add @01orbital/contact-list
npx shadcn add @01orbital/contact-list
yarn shadcn add @01orbital/contact-list
bunx --bun shadcn add @01orbital/contact-list

Free under the MIT license. Add the @01orbital registry to components.json once (install guide), then run the command.

It lands at components/blocks/app/contact-list/index.tsx, as source you own and edit.

Usage

import ContactList from "@/components/blocks/app/contact-list"

export default function Example() {
  return <ContactList />
}

Dependencies

npm: @phosphor-icons/react, cn. The CLI installs them for you.

Registry pieces it installs with it: