Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions app/(main)/_landing/stats-bento.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,20 +231,22 @@ export default function StatsBento() {
</span>
</div>
{/* Category marquees */}
<div className="-mx-4 mt-3 mb-2 space-y-0.5 sm:-mx-5 sm:mb-0 lg:-mx-6">
<div
className="-mx-4 mt-3 mb-2 space-y-0.5 sm:-mx-5 sm:mb-0 lg:-mx-6"
aria-hidden="true"
>
<Marquee
pauseOnHover
applyMask={false}
className="[--duration:50s] [--gap:0.15rem]"
>
{categories.map((cat, i) => (
<Link
<div
key={`fwd-${i}`}
href={cat.href}
className="rounded-full border border-border bg-foreground/5 px-2 py-0.5 text-[10px] font-semibold text-foreground transition-colors hover:border-foreground/30 sm:px-2.5 sm:py-1 sm:text-[11px]"
className="rounded-full border border-border bg-foreground/5 px-2 py-0.5 text-[10px] font-semibold text-foreground transition-colors sm:px-2.5 sm:py-1 sm:text-[11px]"
>
{cat.label}
</Link>
</div>
))}
</Marquee>
<Marquee
Expand All @@ -254,13 +256,12 @@ export default function StatsBento() {
className="[--duration:55s] [--gap:0.15rem]"
>
{[...categories].reverse().map((cat, i) => (
<Link
<div
key={`rev-${i}`}
href={cat.href}
className="rounded-full border border-border bg-foreground/5 px-2 py-0.5 text-[10px] font-semibold text-foreground transition-colors hover:border-foreground/30 sm:px-2.5 sm:py-1 sm:text-[11px]"
className="rounded-full border border-border bg-foreground/5 px-2 py-0.5 text-[10px] font-semibold text-foreground transition-colors sm:px-2.5 sm:py-1 sm:text-[11px]"
>
{cat.label}
</Link>
</div>
))}
</Marquee>
</div>
Expand Down
138 changes: 51 additions & 87 deletions app/demo/demo-experience.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"use client";

import * as DialogPrimitive from "@radix-ui/react-dialog";
import { Command as CommandPrimitive } from "cmdk";
import {
CheckIcon,
ChevronLeftIcon,
ChevronRightIcon,
LayoutGridIcon,
Expand All @@ -21,7 +23,6 @@ import { Icons } from "@/components/icons";
import {
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
Command as CommandRoot,
Expand Down Expand Up @@ -554,69 +555,76 @@ function DemoPicker({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogPortal>
<DialogOverlay className="bg-black/50 backdrop-blur-[3px] sm:bg-black/45" />
<DialogOverlay className="bg-black/50 backdrop-blur-[2px] sm:bg-black/40" />
<DialogPrimitive.Content
style={{ "--demo-accent": accentColor } as CSSProperties}
aria-describedby={undefined}
className={cn(
"demo-picker-shell fixed z-50 grid w-full gap-0 overflow-hidden border border-white/12 bg-[#141414]/96 text-white shadow-[0_28px_90px_-28px_rgb(0_0_0_/_0.88)] backdrop-blur-2xl outline-none",
"inset-x-0 bottom-0 top-auto max-h-[min(88svh,680px)] translate-x-0 translate-y-0 rounded-t-[28px] border-b-0",
"demo-picker-shell fixed z-50 flex w-full flex-col overflow-hidden border border-white/10 bg-[#141414] text-white shadow-[0_24px_80px_-32px_rgb(0_0_0_/_0.9)] outline-none",
"inset-x-0 bottom-0 top-auto max-h-[min(88svh,640px)] translate-x-0 translate-y-0 rounded-t-[20px] border-b-0",
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-bottom-4",
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-4",
"sm:inset-x-auto sm:bottom-auto sm:top-[50%] sm:left-[50%] sm:max-w-[min(92vw,28rem)] sm:translate-x-[-50%] sm:translate-y-[-50%] sm:rounded-[24px] sm:border-b",
"sm:inset-x-auto sm:bottom-auto sm:top-[50%] sm:left-[50%] sm:max-w-[min(92vw,24rem)] sm:translate-x-[-50%] sm:translate-y-[-50%] sm:rounded-[18px] sm:border-b",
"sm:data-[state=closed]:slide-out-to-bottom-0 sm:data-[state=closed]:zoom-out-95 sm:data-[state=open]:slide-in-from-bottom-0 sm:data-[state=open]:zoom-in-95",
)}
>
<div className="sm:hidden">
<div aria-hidden="true" className="mx-auto mt-2.5 h-1 w-10 rounded-full bg-white/18" />
<div aria-hidden="true" className="mx-auto mt-2 h-1 w-9 rounded-full bg-white/15" />
</div>

<div className="flex items-start justify-between gap-3 border-b border-white/8 px-4 pb-3 pt-3 sm:pt-4">
<div className="min-w-0">
<DialogPrimitive.Title className="text-[15px] font-medium tracking-[-0.02em] text-white">
Browse demos
</DialogPrimitive.Title>
<DialogPrimitive.Description className="mt-0.5 text-[12px] text-white/45">
{DEMO_ITEMS.length} live {DEMO_ITEMS.length === 1 ? "experience" : "experiences"}
</DialogPrimitive.Description>
</div>
<div className="flex items-center justify-between gap-3 px-4 pb-3 pt-3.5 sm:pt-4">
<DialogPrimitive.Title className="text-[15px] font-medium tracking-[-0.01em] text-white">
Browse demos
</DialogPrimitive.Title>
<DialogPrimitive.Description className="sr-only">
Search and switch between live demo pages.
</DialogPrimitive.Description>
<button
type="button"
aria-label="Close demo picker"
onClick={() => onOpenChange(false)}
className="grid size-9 shrink-0 touch-manipulation place-items-center rounded-full text-white/55 transition-colors hover:bg-white/10 hover:text-white"
className="-mr-1 grid size-8 shrink-0 touch-manipulation place-items-center rounded-full text-white/45 transition-colors hover:bg-white/8 hover:text-white/75"
>
<XIcon aria-hidden="true" className="size-4" />
</button>
</div>

<CommandRoot className="bg-transparent">
<div className="px-3 pt-3">
<div className="demo-picker-search flex items-center gap-2 rounded-2xl border border-white/8 bg-white/[0.04] px-3">
<SearchIcon aria-hidden="true" className="size-4 shrink-0 text-white/35" />
<CommandInput
placeholder="Search by name or category…"
className="h-11 border-0 bg-transparent px-0 text-[15px] text-white placeholder:text-white/35 focus:ring-0"
/>
</div>
<CommandRoot className="flex min-h-0 w-full flex-col bg-transparent">
<div
cmdk-input-wrapper=""
className="flex h-11 w-full shrink-0 items-center gap-2.5 border-y border-white/8 px-4"
>
<SearchIcon aria-hidden="true" className="size-4 shrink-0 text-white/30" />
<CommandPrimitive.Input
placeholder="Search demos…"
className="min-w-0 flex-1 bg-transparent text-[15px] text-white outline-none placeholder:text-white/35"
/>
</div>

<CommandList className="demo-picker-list max-h-[min(52svh,420px)] px-2 pb-2 pt-2">
<CommandEmpty className="rounded-2xl bg-white/[0.03] py-10 text-sm text-white/45">
No demos match that search.
<CommandList className="demo-picker-list max-h-[min(52svh,400px)] px-1.5 py-1 pb-3">
<CommandEmpty className="px-4 py-10 text-[14px] text-white/45">
Nothing matched that search.
</CommandEmpty>
{DEMO_GROUPS.map((group) => (
{DEMO_GROUPS.map((group, groupIndex) => (
<CommandGroup
key={group.slug}
heading={`${group.label} · ${group.items.length}`}
className="pb-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-[10px] [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:tracking-[0.12em] [&_[cmdk-group-heading]]:text-white/40"
heading={group.label}
className="demo-picker-group p-0 [&:not(:last-child)]:pb-4 [&_[cmdk-group-heading]]:sr-only [&_[cmdk-item]:not(:last-child)]:mb-1"
>
<div
aria-hidden="true"
className={cn(
"px-3 pb-1.5 text-[12px] font-medium text-white/38",
groupIndex === 0 ? "pt-2.5" : "pt-3",
)}
>
{group.label}
</div>
{group.items.map((rawItem) => {
const flatIndex = getItemIndex(group.slug, rawItem.slug);
const entry = DEMO_ITEMS[flatIndex];
const isActive =
entry.slug === activeItem.slug && entry.group.slug === activeGroup.slug;
const itemAccent = demoThemeColor(entry, group);

return (
<CommandItem
Expand All @@ -626,33 +634,18 @@ function DemoPicker({
onFocus={() => onPrefetch(entry)}
onSelect={() => onSelect(entry)}
className={cn(
"demo-picker-item mx-0.5 cursor-pointer rounded-2xl px-3 py-2.5 aria-selected:bg-white/[0.07] data-[selected=true]:bg-white/[0.07]",
isActive && "demo-picker-item-live bg-white/[0.05]",
"demo-picker-item mx-0.5 cursor-pointer rounded-lg px-3 py-2.5 aria-selected:bg-white/[0.07] data-[selected=true]:bg-white/[0.07]",
isActive && "demo-picker-item-live bg-white/[0.04]",
)}
style={
isActive ? ({ "--item-accent": itemAccent } as CSSProperties) : undefined
}
>
<span
aria-hidden="true"
className={cn(
"mt-1.5 size-1.5 shrink-0 rounded-full bg-white/20",
isActive &&
"bg-[var(--item-accent)] shadow-[0_0_10px_color-mix(in_srgb,var(--item-accent)_70%,transparent)]",
)}
/>
<span className="min-w-0 flex-1">
<span className="block truncate text-[13px] font-medium tracking-[-0.015em] text-white/92">
{entry.label}
</span>
<span className="mt-0.5 block truncate text-[12px] leading-snug text-white/42">
{group.phrase}
</span>
<span className="min-w-0 flex-1 truncate text-[14px] text-white/88">
{entry.label}
</span>
{isActive ? (
<span className="shrink-0 self-center rounded-full border border-white/10 bg-white/[0.06] px-2 py-0.5 text-[10px] font-medium text-white/72">
live
</span>
<CheckIcon
aria-hidden="true"
className="size-3.5 shrink-0 text-white/45"
/>
) : null}
</CommandItem>
);
Expand All @@ -661,15 +654,6 @@ function DemoPicker({
))}
</CommandList>
</CommandRoot>

<div className="border-t border-white/8 px-4 py-2.5">
<p className="text-center font-mono text-[10px] leading-relaxed text-white/35 sm:hidden">
Swipe screen edges · tap grid to browse
</p>
<p className="hidden text-center font-mono text-[10px] leading-relaxed text-white/35 sm:block">
H home · ← → cycle · . picker · R refresh · F fullscreen
</p>
</div>
</DialogPrimitive.Content>
</DialogPortal>
</Dialog>
Expand Down Expand Up @@ -980,24 +964,8 @@ function DemoShellStyles() {
isolation: isolate;
}

.demo-picker-shell::before {
content: "";
pointer-events: none;
position: absolute;
inset: 0;
border-radius: inherit;
background:
linear-gradient(180deg, rgb(255 255 255 / 0.08), transparent 22%),
radial-gradient(circle at 50% 0%, rgb(255 255 255 / 0.06), transparent 42%);
}

.demo-picker-search [cmdk-input-wrapper] {
border: 0;
padding: 0;
}

.demo-picker-search [cmdk-input-wrapper] svg {
display: none;
.demo-picker-group + .demo-picker-group {
border-top: 1px solid rgb(255 255 255 / 0.05);
}

.demo-picker-list {
Expand All @@ -1016,14 +984,10 @@ function DemoShellStyles() {

.demo-picker-item {
display: flex;
align-items: flex-start;
align-items: center;
gap: 0.625rem;
}

.demo-picker-item-live {
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.08);
}

.demo-chrome-pill {
min-height: 2.75rem;
}
Expand Down
23 changes: 11 additions & 12 deletions app/demo/library/browse/cinema-row-notes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,15 @@ export function CinemaRowNotes() {
id="demo-notes-title"
eyebrow="Recipe"
title="Stream browse"
description="Apple TV-style layout: gradient hero with staggered premiere copy, a scrolling poster rail, then a craft panel with twin vertical still columns. TMDB art — demo only."
description="Apple TV-style page: gradient hero, scrolling poster rail, craft panel with two vertical still columns. TMDB posters, demo only."
/>

<DemoNotes.Section id="concept" index={1} title="Concept">
<DemoNotes.Prose>
<p>
Three bands on one page. The hero is type on a violet radial scrim — eyebrow, title,
tagline, runtime, and CTAs stagger in after load. Below that, premieres scroll
horizontally. The craft panel pairs short copy with two still columns drifting in
opposite directions.
Three bands stacked on one page. Hero copy sits on a violet radial scrim. Eyebrow,
title, tagline, runtime, and CTAs stagger in after load. Premieres scroll sideways
underneath. The craft panel is short copy plus two still columns moving opposite ways.
</p>
</DemoNotes.Prose>
</DemoNotes.Section>
Expand All @@ -91,9 +90,9 @@ export function CinemaRowNotes() {
<p>
<code>WaveReveal</code> (<code>mode="word"</code>) on the hero title.{" "}
<code>Marquee</code> on the premieres rail and both still columns (
<code>pauseOnHover</code>, <code>applyMask={"{false}"}</code>local masks instead).
Hero rise/fall and delay math live in the demo file as <code>cinema-hero-rise</code>{" "}
plus <code>heroSequenceDelays()</code>.
<code>pauseOnHover</code>, <code>applyMask={"{false}"}</code>; we use local masks
instead). Hero rise/fall and delay math live in the demo file as{" "}
<code>cinema-hero-rise</code> plus <code>heroSequenceDelays()</code>.
</p>
</DemoNotes.Prose>
<DemoNotes.ComponentLinks demoKey={DEMO_KEY} />
Expand All @@ -102,9 +101,9 @@ export function CinemaRowNotes() {
<DemoNotes.Section id="build" index={3} title="How it's built">
<DemoNotes.Prose>
<p>
<code>heroSequenceDelays()</code> derives every entrance offset from the title word
count and <code>WaveReveal</code> timing — eyebrow through the premieres rail share one
sequence. <code>cinema-hero-rise</code> is a local keyframe (blur +{" "}
<code>heroSequenceDelays()</code> works out every entrance offset from the title word
count and <code>WaveReveal</code> timing. Eyebrow through the premieres rail all share
one sequence. <code>cinema-hero-rise</code> is a local keyframe (blur +{" "}
<code>translateY</code>) on everything except the title words.
</p>
<p>
Expand All @@ -128,7 +127,7 @@ export function CinemaRowNotes() {
<DemoNotes.Code caption="Hero delay sequence + WaveReveal">
{HERO_SEQUENCE_SNIPPET}
</DemoNotes.Code>
<DemoNotes.Code caption="Premieres rail custom edge mask">
<DemoNotes.Code caption="Premieres rail, custom edge mask">
{PREMIERES_SNIPPET}
</DemoNotes.Code>
<DemoNotes.Code caption="Craft panel still columns">{STILLS_SNIPPET}</DemoNotes.Code>
Expand Down
8 changes: 4 additions & 4 deletions app/demo/library/footer/footer-wordmark-notes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function FooterWordmarkNotes() {
The type sits in the field. It doesn&apos;t float on top like a heading.
</p>
<p>
Not a clone same hierarchy. One blue family in <code>oklch</code>, Syne 800 on the
Not a clone, but same hierarchy. One blue family in <code>oklch</code>, Syne 800 on the
wordmark, IBM Plex on the links. Lucien uses a static texture; we put boids behind the
gradient at low opacity instead.
</p>
Expand All @@ -52,7 +52,7 @@ export function FooterWordmarkNotes() {

<DemoNotes.Section id="components" index={2} title="Components used">
<DemoNotes.Prose>
<p>Two Animata pieces boids and SiblingFocusNav. The rest is layout and CSS.</p>
<p>Two Animata pieces: boids and SiblingFocusNav. The rest is layout and CSS.</p>
</DemoNotes.Prose>
<DemoNotes.ComponentLinks demoKey={DEMO_KEY} />
<DemoNotes.Prose>
Expand All @@ -66,7 +66,7 @@ export function FooterWordmarkNotes() {
<DemoNotes.Prose>
<p>
The wordmark is <strong>SVG</strong> <code>&lt;text&gt;</code>, not a styled heading.
That&apos;s the trick for edge-to-edge scaling — no clipping, no fighting{" "}
That&apos;s what makes edge-to-edge scaling work. No clipping, no fighting{" "}
<code>clamp()</code> on a single HTML line.
</p>
<p>
Expand All @@ -90,7 +90,7 @@ export function FooterWordmarkNotes() {
<DemoNotes.Section id="source" index={4} title="Full source">
<DemoNotes.Prose>
<p>
Pulled from the demo file at build time. Copy what you need below — component docs are
Pulled from the demo file at build time. Copy what you need below. Component docs are
linked above.
</p>
</DemoNotes.Prose>
Expand Down
8 changes: 4 additions & 4 deletions app/demo/library/hero/launch-shift-notes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export function LaunchShiftNotes() {
<DemoNotes.Header
id="demo-notes-title"
eyebrow="Recipe"
title="Superhuman · platform hero"
description="The Grammarly Superhuman rebrand hero from superhuman.com. Not affiliated; same headline and subhead, local layout."
title="Superhuman platform hero"
description="The Grammarly to Superhuman rebrand hero from superhuman.com. Not affiliated; same headline and subhead, local layout."
/>

<DemoNotes.Section id="concept" index={1} title="Concept">
Expand All @@ -47,8 +47,8 @@ export function LaunchShiftNotes() {
</p>
<p>
Black field, warm off-white body text, violet on the badge. Headline fades white to
gray, two lines so the gradient doesn&apos;t muddy the second row. Boids behind a
vignette for edge motion.
gray, split across two lines so the gradient doesn&apos;t muddy the second row. Boids
behind a vignette for edge motion.
</p>
</DemoNotes.Prose>
</DemoNotes.Section>
Expand Down
Loading