Profile page
BlockA person's page: who they are and where, their local time, pinned work, a year of activity and what they did lately. Following updates at once and rolls back if it fails.
pnpm dlx shadcn@latest add https://caliperui.site/r/profile-page.jsonAmara Okafor
@amara · Design engineer at Halden
I build the close review screens and the component library behind them. Previously at a freight start-up. Write to me about tables, keyboard shortcuts or Lisbon coffee.
- Lisbon, Portugal
- Local time
- amara.design
- Mastodon
- Followers
- 1,284
- Following
- 211
- Projects
- 6
Pinned
- close-reviewTypeScriptThe review queue for the first morning of the close: keys, undo and bulk approve.342starsUpdated 2 days ago
- ledger-gridReactA virtualised ledger with frozen columns and inline matching.1,290starsUpdated last week
- fx-roundingNotesHow Halden decides when a difference is just rounding.58stars
- shortcut-sheetDocsEvery keyboard shortcut in the app, printable on one page.77stars
| Date | Value |
|---|---|
| 2025-10-13 | 5 |
| 2025-10-14 | 7 |
| 2025-10-15 | 10 |
| 2025-10-16 | 7 |
| 2025-10-17 | 9 |
| 2025-10-18 | 0 |
| 2025-10-19 | 0 |
| 2025-10-20 | 11 |
| 2025-10-21 | 8 |
| 2025-10-22 | 10 |
| 2025-10-23 | 7 |
| 2025-10-24 | 9 |
| 2025-10-25 | 0 |
| 2025-10-26 | 0 |
| 2025-10-27 | 10 |
| 2025-10-28 | 7 |
| 2025-10-29 | 9 |
| 2025-10-30 | 11 |
| 2025-10-31 | 8 |
| 2025-11-01 | 0 |
| 2025-11-02 | 0 |
| 2025-11-03 | 8 |
| 2025-11-04 | 10 |
| 2025-11-05 | 6 |
| 2025-11-06 | 8 |
| 2025-11-07 | 4 |
| 2025-11-08 | 0 |
| 2025-11-09 | 0 |
| 2025-11-10 | 4 |
| 2025-11-11 | 6 |
| 2025-11-12 | 2 |
| 2025-11-13 | 4 |
| 2025-11-14 | 5 |
| 2025-11-15 | 2 |
| 2025-11-16 | 0 |
| 2025-11-17 | 0 |
| 2025-11-18 | 2 |
| 2025-11-19 | 3 |
| 2025-11-20 | 0 |
| 2025-11-21 | 2 |
| 2025-11-22 | 0 |
| 2025-11-23 | 0 |
| 2025-11-24 | 2 |
| 2025-11-25 | 0 |
| 2025-11-26 | 1 |
| 2025-11-27 | 0 |
| 2025-11-28 | 0 |
| 2025-11-29 | 0 |
| 2025-11-30 | 0 |
| 2025-12-01 | 1 |
| 2025-12-02 | 0 |
| 2025-12-03 | 0 |
| 2025-12-04 | 3 |
| 2025-12-05 | 0 |
| 2025-12-06 | 0 |
| 2025-12-07 | 2 |
| 2025-12-08 | 2 |
| 2025-12-09 | 4 |
| 2025-12-10 | 1 |
| 2025-12-11 | 3 |
| 2025-12-12 | 1 |
| 2025-12-13 | 0 |
| 2025-12-14 | 0 |
| 2025-12-15 | 2 |
| 2025-12-16 | 4 |
| 2025-12-17 | 2 |
| 2025-12-18 | 4 |
| 2025-12-19 | 6 |
| 2025-12-20 | 0 |
| 2025-12-21 | 0 |
| 2025-12-22 | 2 |
| 2025-12-23 | 4 |
| 2025-12-24 | 6 |
| 2025-12-25 | 3 |
| 2025-12-26 | 5 |
| 2025-12-27 | 0 |
| 2025-12-28 | 0 |
| 2025-12-29 | 5 |
| 2025-12-30 | 2 |
| 2025-12-31 | 4 |
| 2026-01-01 | 1 |
| 2026-01-02 | 2 |
| 2026-01-03 | 0 |
| 2026-01-04 | 0 |
| 2026-01-05 | 3 |
| 2026-01-06 | 0 |
| 2026-01-07 | 1 |
| 2026-01-08 | 3 |
| 2026-01-09 | 0 |
| 2026-01-10 | 0 |
| 2026-01-11 | 0 |
| 2026-01-12 | 0 |
| 2026-01-13 | 2 |
| 2026-01-14 | 0 |
| 2026-01-15 | 1 |
| 2026-01-16 | 0 |
| 2026-01-17 | 0 |
| 2026-01-18 | 0 |
| 2026-01-19 | 0 |
| 2026-01-20 | 1 |
| 2026-01-21 | 0 |
| 2026-01-22 | 0 |
| 2026-01-23 | 2 |
| 2026-01-24 | 0 |
| 2026-01-25 | 0 |
| 2026-01-26 | 0 |
| 2026-01-27 | 2 |
| 2026-01-28 | 4 |
| 2026-01-29 | 1 |
| 2026-01-30 | 4 |
| 2026-01-31 | 2 |
| 2026-02-01 | 0 |
| 2026-02-02 | 6 |
| 2026-02-03 | 4 |
| 2026-02-04 | 6 |
| 2026-02-05 | 3 |
| 2026-02-06 | 6 |
| 2026-02-07 | 0 |
| 2026-02-08 | 0 |
| 2026-02-09 | 8 |
| 2026-02-10 | 6 |
| 2026-02-11 | 8 |
| 2026-02-12 | 10 |
| 2026-02-13 | 7 |
| 2026-02-14 | 0 |
| 2026-02-15 | 0 |
| 2026-02-16 | 9 |
| 2026-02-17 | 11 |
| 2026-02-18 | 8 |
| 2026-02-19 | 10 |
| 2026-02-20 | 7 |
| 2026-02-21 | 0 |
| 2026-02-22 | 2 |
| 2026-02-23 | 8 |
| 2026-02-24 | 10 |
| 2026-02-25 | 7 |
| 2026-02-26 | 8 |
| 2026-02-27 | 10 |
| 2026-02-28 | 0 |
| 2026-03-01 | 0 |
| 2026-03-02 | 5 |
| 2026-03-03 | 7 |
| 2026-03-04 | 9 |
| 2026-03-05 | 5 |
| 2026-03-06 | 7 |
| 2026-03-07 | 0 |
| 2026-03-08 | 0 |
| 2026-03-09 | 7 |
| 2026-03-10 | 4 |
| 2026-03-11 | 6 |
| 2026-03-12 | 2 |
| 2026-03-13 | 4 |
| 2026-03-14 | 0 |
| 2026-03-15 | 0 |
| 2026-03-16 | 5 |
| 2026-03-17 | 2 |
| 2026-03-18 | 4 |
| 2026-03-19 | 6 |
| 2026-03-20 | 3 |
| 2026-03-21 | 0 |
| 2026-03-22 | 0 |
| 2026-03-23 | 4 |
| 2026-03-24 | 6 |
| 2026-03-25 | 3 |
| 2026-03-26 | 5 |
| 2026-03-27 | 2 |
| 2026-03-28 | 0 |
| 2026-03-29 | 0 |
| 2026-03-30 | 4 |
| 2026-03-31 | 6 |
| 2026-04-01 | 3 |
| 2026-04-02 | 6 |
| 2026-04-03 | 8 |
| 2026-04-04 | 0 |
| 2026-04-05 | 0 |
| 2026-04-06 | 4 |
| 2026-04-07 | 7 |
| 2026-04-08 | 9 |
| 2026-04-09 | 6 |
| 2026-04-10 | 8 |
| 2026-04-11 | 0 |
| 2026-04-12 | 0 |
| 2026-04-13 | 9 |
| 2026-04-14 | 6 |
| 2026-04-15 | 8 |
| 2026-04-16 | 4 |
| 2026-04-17 | 6 |
| 2026-04-18 | 2 |
| 2026-04-19 | 0 |
| 2026-04-20 | 6 |
| 2026-04-21 | 3 |
| 2026-04-22 | 5 |
| 2026-04-23 | 6 |
| 2026-04-24 | 3 |
| 2026-04-25 | 0 |
| 2026-04-26 | 0 |
| 2026-04-27 | 3 |
| 2026-04-28 | 4 |
| 2026-04-29 | 1 |
| 2026-04-30 | 2 |
| 2026-05-01 | 0 |
| 2026-05-02 | 0 |
| 2026-05-03 | 0 |
| 2026-05-04 | 0 |
| 2026-05-05 | 1 |
| 2026-05-06 | 0 |
| 2026-05-07 | 0 |
| 2026-05-08 | 1 |
| 2026-05-09 | 0 |
| 2026-05-10 | 2 |
| 2026-05-11 | 0 |
| 2026-05-12 | 0 |
| 2026-05-13 | 0 |
| 2026-05-14 | 0 |
| 2026-05-15 | 0 |
| 2026-05-16 | 0 |
| 2026-05-17 | 0 |
| 2026-05-18 | 0 |
| 2026-05-19 | 0 |
| 2026-05-20 | 0 |
| 2026-05-21 | 0 |
| 2026-05-22 | 0 |
| 2026-05-23 | 0 |
| 2026-05-24 | 0 |
| 2026-05-25 | 1 |
| 2026-05-26 | 0 |
| 2026-05-27 | 1 |
| 2026-05-28 | 4 |
| 2026-05-29 | 1 |
| 2026-05-30 | 0 |
| 2026-05-31 | 0 |
| 2026-06-01 | 3 |
| 2026-06-02 | 5 |
| 2026-06-03 | 3 |
| 2026-06-04 | 5 |
| 2026-06-05 | 2 |
| 2026-06-06 | 0 |
| 2026-06-07 | 0 |
| 2026-06-08 | 4 |
| 2026-06-09 | 6 |
| 2026-06-10 | 3 |
| 2026-06-11 | 6 |
| 2026-06-12 | 8 |
| 2026-06-13 | 0 |
| 2026-06-14 | 0 |
| 2026-06-15 | 4 |
| 2026-06-16 | 6 |
| 2026-06-17 | 7 |
| 2026-06-18 | 4 |
| 2026-06-19 | 6 |
| 2026-06-20 | 0 |
| 2026-06-21 | 0 |
| 2026-06-22 | 7 |
| 2026-06-23 | 4 |
| 2026-06-24 | 5 |
| 2026-06-25 | 2 |
| 2026-06-26 | 4 |
| 2026-06-27 | 0 |
| 2026-06-28 | 0 |
| 2026-06-29 | 4 |
| 2026-06-30 | 1 |
| 2026-07-01 | 3 |
| 2026-07-02 | 5 |
| 2026-07-03 | 2 |
| 2026-07-04 | 2 |
| 2026-07-05 | 0 |
| 2026-07-06 | 3 |
| 2026-07-07 | 5 |
| 2026-07-08 | 2 |
| 2026-07-09 | 4 |
| 2026-07-10 | 1 |
| 2026-07-11 | 0 |
| 2026-07-12 | 0 |
| 2026-07-13 | 3 |
| 2026-07-14 | 5 |
| 2026-07-15 | 2 |
| 2026-07-16 | 4 |
| 2026-07-17 | 7 |
| 2026-07-18 | 0 |
| 2026-07-19 | 0 |
| 2026-07-20 | 4 |
| 2026-07-21 | 6 |
| 2026-07-22 | 9 |
| 2026-07-23 | 6 |
| 2026-07-24 | 8 |
| 2026-07-25 | 0 |
| 2026-07-26 | 2 |
| 2026-07-27 | 10 |
| 2026-07-28 | 8 |
| 2026-07-29 | 10 |
| 2026-07-30 | 7 |
| 2026-07-31 | 9 |
| 2026-08-01 | 0 |
| 2026-08-02 | 0 |
| 2026-08-03 | 11 |
| 2026-08-04 | 8 |
| 2026-08-05 | 10 |
| 2026-08-06 | 12 |
| 2026-08-07 | 9 |
| 2026-08-08 | 0 |
| 2026-08-09 | 0 |
| 2026-08-10 | 9 |
| 2026-08-11 | 11 |
| 2026-08-12 | 8 |
| 2026-08-13 | 9 |
| 2026-08-14 | 6 |
| 2026-08-15 | 0 |
| 2026-08-16 | 0 |
| 2026-08-17 | 6 |
| 2026-08-18 | 8 |
| 2026-08-19 | 4 |
| 2026-08-20 | 6 |
| 2026-08-21 | 7 |
| 2026-08-22 | 0 |
| 2026-08-23 | 0 |
| 2026-08-24 | 2 |
| 2026-08-25 | 4 |
| 2026-08-26 | 5 |
| 2026-08-27 | 2 |
| 2026-08-28 | 3 |
| 2026-08-29 | 0 |
| 2026-08-30 | 0 |
| 2026-08-31 | 4 |
| 2026-09-01 | 0 |
| 2026-09-02 | 2 |
| 2026-09-03 | 0 |
| 2026-09-04 | 1 |
| 2026-09-05 | 0 |
| 2026-09-06 | 0 |
| 2026-09-07 | 2 |
| 2026-09-08 | 0 |
| 2026-09-09 | 1 |
| 2026-09-10 | 3 |
| 2026-09-11 | 0 |
| 2026-09-12 | 0 |
| 2026-09-13 | 0 |
| 2026-09-14 | 2 |
| 2026-09-15 | 4 |
| 2026-09-16 | 1 |
| 2026-09-17 | 3 |
| 2026-09-18 | 0 |
| 2026-09-19 | 2 |
| 2026-09-20 | 0 |
| 2026-09-21 | 2 |
| 2026-09-22 | 4 |
| 2026-09-23 | 1 |
| 2026-09-24 | 4 |
| 2026-09-25 | 6 |
| 2026-09-26 | 0 |
| 2026-09-27 | 0 |
| 2026-09-28 | 2 |
| 2026-09-29 | 4 |
| 2026-09-30 | 6 |
| 2026-10-01 | 3 |
| 2026-10-02 | 5 |
| 2026-10-03 | 0 |
| 2026-10-04 | 0 |
| 2026-10-05 | 6 |
| 2026-10-06 | 3 |
| 2026-10-07 | 4 |
| 2026-10-08 | 1 |
| 2026-10-09 | 3 |
| 2026-10-10 | 0 |
| 2026-10-11 | 2 |
Follow, then unfollow: the demo’s unfollow fails, and the count is put back.
When to use #
Use it for
- Community, marketplace and team directory profiles.
- The signed-in person's own profile, with Edit profile.
Skip it when
- A list of many people; use the team directory.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/profile-page.json
Adds its 10 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
import { useState } from "react" import { ProfilePage } from "@/components/ui/profile-page" import { SegmentedControl } from "@/components/ui/segmented-control" import { type ActivityDay } from "@/components/ui/activity-heatmap" const wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)); function contributions(): ActivityDay[] { const start = Date.UTC(2025, 9, 13); return Array.from({ length: 364 }, (_, i) => { const date = new Date(start + i * 86_400_000).toISOString().slice(0, 10); const weekday = new Date(start + i * 86_400_000).getUTCDay(); const wave = Math.sin(i / 9) + Math.cos(i / 23); const value = weekday === 0 || weekday === 6 ? (i % 11 === 0 ? 2 : 0) : Math.max(0, Math.round(3 + wave * 3 + ((i * 7) % 5) - 1)); return { date, value }; }); } export function ProfilePageDemo() { const [self, setSelf] = useState(false); const [data] = useState(contributions); return ( <div className="grid w-full max-w-4xl gap-4"> <SegmentedControl className="w-fit" label="View as" value={self ? "self" : "visitor"} onValueChange={(value) => setSelf(value === "self")} options={[{ value: "visitor", label: "Visitor" }, { value: "self", label: "Owner" }]} /> <ProfilePage key={String(self)} name="Amara Okafor" handle="amara" role="Design engineer at Halden" bio="I build the close review screens and the component library behind them. Previously at a freight start-up. Write to me about tables, keyboard shortcuts or Lisbon coffee." location="Lisbon, Portugal" timeZone="Europe/Lisbon" links={[{ label: "amara.design", href: "#site" }, { label: "Mastodon", href: "#mastodon" }]} followers={1284} stats={[{ label: "Following", value: 211 }, { label: "Projects", value: 6 }]} self={self} onFollowChange={async (next) => { await wait(500); if (!next) throw new Error("demo: unfollow fails once"); }} onMessage={() => {}} onEdit={() => {}} projects={[ { id: "1", name: "close-review", description: "The review queue for the first morning of the close: keys, undo and bulk approve.", tag: "TypeScript", stars: 342, updated: "2 days ago", href: "#p1" }, { id: "2", name: "ledger-grid", description: "A virtualised ledger with frozen columns and inline matching.", tag: "React", stars: 1290, updated: "last week", href: "#p2" }, { id: "3", name: "fx-rounding", description: "How Halden decides when a difference is just rounding.", tag: "Notes", stars: 58, href: "#p3" }, { id: "4", name: "shortcut-sheet", description: "Every keyboard shortcut in the app, printable on one page.", tag: "Docs", stars: 77, href: "#p4" }, { id: "5", name: "bank-icons", description: "Monochrome marks for 300 European banks.", tag: "SVG", stars: 401, href: "#p5" }, ]} activity={[ { id: "a1", day: "Today", time: "09:42", text: "Merged “Keep the review cursor after undo” in close-review" }, { id: "a2", day: "Today", time: "08:15", text: "Commented on “Frozen columns jump on resize”" }, { id: "a3", day: "Yesterday", time: "17:03", text: "Released ledger-grid 2.4.0" }, { id: "a4", day: "Yesterday", time: "11:30", text: "Opened “Announce row counts after filtering”" }, { id: "a5", day: "Monday", time: "14:22", text: "Starred bank-icons" }, ]} contributions={data} /> <p className="text-[13px] text-muted-foreground">Follow, then unfollow: the demo’s unfollow fails, and the count is put back.</p> </div> ); }
API reference #
ProfilePage
component
Accessibility #
It exposes status semantics, with aria-hidden, aria-pressed, aria-label and aria-labelledby kept in step with state. Changes that matter are announced through a live region.
Keyboard and focus handling comes from Radix tabs: arrow keys move through items, Enter and Space select, and Escape closes and returns focus.
Motion #
Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.