snapshot of https://github.com/shadcn-ui/ui.git (main, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
490
apps/v4/registry/bases/radix/examples/card-example.tsx
Normal file
490
apps/v4/registry/bases/radix/examples/card-example.tsx
Normal file
@@ -0,0 +1,490 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import {
|
||||
Example,
|
||||
ExampleWrapper,
|
||||
} from "@/registry/bases/radix/components/example"
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarGroup,
|
||||
AvatarGroupCount,
|
||||
AvatarImage,
|
||||
} from "@/registry/bases/radix/ui/avatar"
|
||||
import { Button } from "@/registry/bases/radix/ui/button"
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/registry/bases/radix/ui/card"
|
||||
import { Field, FieldGroup, FieldLabel } from "@/registry/bases/radix/ui/field"
|
||||
import { Input } from "@/registry/bases/radix/ui/input"
|
||||
import {
|
||||
ToggleGroup,
|
||||
ToggleGroupItem,
|
||||
} from "@/registry/bases/radix/ui/toggle-group"
|
||||
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
|
||||
|
||||
const spacingOptions = [
|
||||
{
|
||||
className: "[--card-spacing:--spacing(4)]",
|
||||
label: "16px",
|
||||
value: "4",
|
||||
},
|
||||
{
|
||||
className: "[--card-spacing:--spacing(5)]",
|
||||
label: "20px",
|
||||
value: "5",
|
||||
},
|
||||
{
|
||||
className: "[--card-spacing:--spacing(6)]",
|
||||
label: "24px",
|
||||
value: "6",
|
||||
},
|
||||
{
|
||||
className: "[--card-spacing:--spacing(8)]",
|
||||
label: "32px",
|
||||
value: "8",
|
||||
},
|
||||
]
|
||||
|
||||
export default function CardExample() {
|
||||
return (
|
||||
<ExampleWrapper>
|
||||
<CardDefault />
|
||||
<CardSmall />
|
||||
<CardContentEdgeToEdge />
|
||||
<CardCustomSpacing />
|
||||
<CardHeaderWithBorder />
|
||||
<CardFooterWithBorder />
|
||||
<CardHeaderWithBorderSmall />
|
||||
<CardFooterWithBorderSmall />
|
||||
<CardWithImage />
|
||||
<CardWithImageSmall />
|
||||
<CardLogin />
|
||||
<CardMeetingNotes />
|
||||
</ExampleWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
function CardContentEdgeToEdge() {
|
||||
return (
|
||||
<Example title="Content Edge to Edge">
|
||||
<Card className="mx-auto w-full max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>Terms of Service</CardTitle>
|
||||
<CardDescription>
|
||||
Review the terms before accepting the agreement.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="-mb-(--card-spacing) px-0">
|
||||
<div className="max-h-48 space-y-4 overflow-y-scroll border-t bg-muted/50 px-(--card-spacing) py-4 text-sm leading-relaxed">
|
||||
<p>
|
||||
These terms govern your use of the workspace, including access to
|
||||
shared documents, project files, and collaboration tools.
|
||||
</p>
|
||||
<p>
|
||||
You are responsible for the content you upload and for ensuring
|
||||
that your team has the appropriate permissions to view or edit it.
|
||||
</p>
|
||||
<p>
|
||||
We may update features or limits as the service evolves. When
|
||||
those changes materially affect your workflow, we will notify your
|
||||
workspace administrators.
|
||||
</p>
|
||||
<p>
|
||||
By continuing, you agree to keep your account credentials secure
|
||||
and to follow your organization's acceptable use policies.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="justify-end gap-2">
|
||||
<Button variant="outline">Decline</Button>
|
||||
<Button>Accept</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardCustomSpacing() {
|
||||
const [spacing, setSpacing] = React.useState("4")
|
||||
const selectedSpacing = spacingOptions.find(
|
||||
(option) => option.value === spacing
|
||||
)
|
||||
|
||||
return (
|
||||
<Example title="Custom Spacing">
|
||||
<div className="mx-auto grid w-full max-w-sm gap-4">
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
value={spacing}
|
||||
onValueChange={(value) => {
|
||||
if (value) {
|
||||
setSpacing(value)
|
||||
}
|
||||
}}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="justify-center"
|
||||
>
|
||||
{spacingOptions.map((option) => (
|
||||
<ToggleGroupItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
<Card className={selectedSpacing?.className}>
|
||||
<CardHeader>
|
||||
<CardTitle>Release Health</CardTitle>
|
||||
<CardDescription>
|
||||
Track readiness across launch signals.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-2 rounded-lg bg-muted/50 p-3 text-sm style-lyra:rounded-none style-sera:rounded-none">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-muted-foreground">Checks passed</span>
|
||||
<span className="font-medium">24 / 26</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="text-muted-foreground">Open blockers</span>
|
||||
<span className="font-medium">2</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardLogin() {
|
||||
return (
|
||||
<Example title="Login">
|
||||
<Card className="mx-auto w-full max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>Login to your account</CardTitle>
|
||||
<CardDescription>
|
||||
Enter your email below to login to your account
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form>
|
||||
<FieldGroup>
|
||||
<Field>
|
||||
<FieldLabel htmlFor="email">Email</FieldLabel>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
required
|
||||
/>
|
||||
</Field>
|
||||
<Field>
|
||||
<div className="flex items-center">
|
||||
<FieldLabel htmlFor="password">Password</FieldLabel>
|
||||
<a
|
||||
href="#"
|
||||
className="ml-auto inline-block underline-offset-4 hover:underline"
|
||||
>
|
||||
Forgot your password?
|
||||
</a>
|
||||
</div>
|
||||
<Input id="password" type="password" required />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</form>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col gap-2">
|
||||
<Button type="submit" className="w-full">
|
||||
Login
|
||||
</Button>
|
||||
<Button variant="outline" className="w-full">
|
||||
Login with Google
|
||||
</Button>
|
||||
<div className="mt-4 text-center style-nova:mt-2">
|
||||
Don't have an account?{" "}
|
||||
<a href="#" className="underline underline-offset-4">
|
||||
Sign up
|
||||
</a>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardMeetingNotes() {
|
||||
return (
|
||||
<Example title="Meeting Notes">
|
||||
<Card className="mx-auto w-full max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>Meeting Notes</CardTitle>
|
||||
<CardDescription>
|
||||
Transcript from the meeting with the client.
|
||||
</CardDescription>
|
||||
<CardAction>
|
||||
<Button variant="outline" size="sm">
|
||||
<IconPlaceholder
|
||||
lucide="CaptionsIcon"
|
||||
tabler="IconTextCaption"
|
||||
hugeicons="TextCheckIcon"
|
||||
phosphor="TextTIcon"
|
||||
remixicon="RiTextWrap"
|
||||
data-icon="inline-start"
|
||||
/>
|
||||
Transcribe
|
||||
</Button>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>
|
||||
Client requested dashboard redesign with focus on mobile
|
||||
responsiveness.
|
||||
</p>
|
||||
<ol className="mt-4 flex list-decimal flex-col gap-2 pl-6">
|
||||
<li>New analytics widgets for daily/weekly metrics</li>
|
||||
<li>Simplified navigation menu</li>
|
||||
<li>Dark mode support</li>
|
||||
<li>Timeline: 6 weeks</li>
|
||||
<li>Follow-up meeting scheduled for next Tuesday</li>
|
||||
</ol>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<AvatarGroup>
|
||||
<Avatar>
|
||||
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
|
||||
<AvatarFallback>CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src="https://github.com/maxleiter.png"
|
||||
alt="@maxleiter"
|
||||
/>
|
||||
<AvatarFallback>LR</AvatarFallback>
|
||||
</Avatar>
|
||||
<Avatar>
|
||||
<AvatarImage
|
||||
src="https://github.com/evilrabbit.png"
|
||||
alt="@evilrabbit"
|
||||
/>
|
||||
<AvatarFallback>ER</AvatarFallback>
|
||||
</Avatar>
|
||||
<AvatarGroupCount>+8</AvatarGroupCount>
|
||||
</AvatarGroup>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardWithImage() {
|
||||
return (
|
||||
<Example title="With Image">
|
||||
<Card size="default" className="relative mx-auto w-full max-w-sm pt-0">
|
||||
<div className="absolute inset-0 z-30 aspect-video bg-primary opacity-50 mix-blend-color" />
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1604076850742-4c7221f3101b?q=80&w=1887&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="Photo by mymind on Unsplash"
|
||||
title="Photo by mymind on Unsplash"
|
||||
className="relative z-20 aspect-video w-full object-cover brightness-60 grayscale"
|
||||
/>
|
||||
<CardHeader>
|
||||
<CardTitle>Beautiful Landscape</CardTitle>
|
||||
<CardDescription>
|
||||
A stunning view that captures the essence of natural beauty.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardFooter>
|
||||
<Button className="w-full">
|
||||
<IconPlaceholder
|
||||
lucide="PlusIcon"
|
||||
tabler="IconPlus"
|
||||
hugeicons="Add01Icon"
|
||||
phosphor="PlusIcon"
|
||||
remixicon="RiAddLine"
|
||||
data-icon="inline-start"
|
||||
/>
|
||||
Button
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardWithImageSmall() {
|
||||
return (
|
||||
<Example title="With Image (Small)">
|
||||
<Card size="sm" className="relative mx-auto w-full max-w-sm pt-0">
|
||||
<div className="absolute inset-0 z-30 aspect-video bg-primary opacity-50 mix-blend-color" />
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1604076850742-4c7221f3101b?q=80&w=1887&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||
alt="Photo by mymind on Unsplash"
|
||||
title="Photo by mymind on Unsplash"
|
||||
className="relative z-20 aspect-video w-full object-cover brightness-60 grayscale"
|
||||
/>
|
||||
<CardHeader>
|
||||
<CardTitle>Beautiful Landscape</CardTitle>
|
||||
<CardDescription>
|
||||
A stunning view that captures the essence of natural beauty.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardFooter>
|
||||
<Button size="sm" className="w-full">
|
||||
<IconPlaceholder
|
||||
lucide="PlusIcon"
|
||||
tabler="IconPlus"
|
||||
hugeicons="Add01Icon"
|
||||
phosphor="PlusIcon"
|
||||
remixicon="RiAddLine"
|
||||
data-icon="inline-start"
|
||||
/>
|
||||
Button
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardHeaderWithBorder() {
|
||||
return (
|
||||
<Example title="Header with Border">
|
||||
<Card className="mx-auto w-full max-w-sm">
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle>Header with Border</CardTitle>
|
||||
<CardDescription>
|
||||
This is a card with a header that has a bottom border.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>
|
||||
The header has a border-b class applied, creating a visual
|
||||
separation between the header and content sections.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardFooterWithBorder() {
|
||||
return (
|
||||
<Example title="Footer with Border">
|
||||
<Card className="mx-auto w-full max-w-sm">
|
||||
<CardContent>
|
||||
<p>
|
||||
The footer has a border-t class applied, creating a visual
|
||||
separation between the content and footer sections.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter className="border-t">
|
||||
<Button variant="outline" className="w-full">
|
||||
Footer with Border
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardDefault() {
|
||||
return (
|
||||
<Example title="Default Size">
|
||||
<Card size="default" className="mx-auto w-full max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>Default Card</CardTitle>
|
||||
<CardDescription>
|
||||
This card uses the default size variant.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>
|
||||
The card component supports a size prop that defaults to
|
||||
"default" for standard spacing and sizing.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button variant="outline" className="w-full">
|
||||
Action
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardSmall() {
|
||||
return (
|
||||
<Example title="Small Size">
|
||||
<Card size="sm" className="mx-auto w-full max-w-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>Small Card</CardTitle>
|
||||
<CardDescription>
|
||||
This card uses the small size variant.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>
|
||||
The card component supports a size prop that can be set to
|
||||
"sm" for a more compact appearance.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
<Button variant="outline" size="sm" className="w-full">
|
||||
Action
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardHeaderWithBorderSmall() {
|
||||
return (
|
||||
<Example title="Header with Border (Small)">
|
||||
<Card size="sm" className="mx-auto w-full max-w-sm">
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle>Header with Border</CardTitle>
|
||||
<CardDescription>
|
||||
This is a small card with a header that has a bottom border.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>
|
||||
The header has a border-b class applied, creating a visual
|
||||
separation between the header and content sections.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
|
||||
function CardFooterWithBorderSmall() {
|
||||
return (
|
||||
<Example title="Footer with Border (Small)">
|
||||
<Card size="sm" className="mx-auto w-full max-w-sm">
|
||||
<CardContent>
|
||||
<p>
|
||||
The footer has a border-t class applied, creating a visual
|
||||
separation between the content and footer sections.
|
||||
</p>
|
||||
</CardContent>
|
||||
<CardFooter className="border-t">
|
||||
<Button variant="outline" size="sm" className="w-full">
|
||||
Footer with Border
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Example>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user