Skip to content
Draft
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-hook-form": "^7.68.0",
"react-icons": "^5.6.0",
"tailwind-merge": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^4.2.1"
Expand All @@ -44,7 +45,7 @@
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3"
},
"packageManager": "pnpm@10.17.1",
"packageManager": "pnpm@10.32.1",
"engines": {
"node": ">=24.5.2"
}
Expand Down
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Users } from "lucide-react"
import { FaBookBookmark } from "react-icons/fa6"
import { CardCaption } from "@/components/card-caption"
import { Button } from "@/components/ui/button"
import { ButtonWithIcon } from "@/components/ui/buttonWithIcon"

Expand All @@ -17,6 +19,20 @@ export default function Home() {
<ButtonWithIcon variant="tertiaryBlur" icon={Users} iconPosition="left" text="Diventa socio" />
<Button variant="link">Link</Button>
</div>
<div className="flex gap-4">
<CardCaption
title="Title"
caption="Beccatevi questo lorem ipsum dolor sit amet: lorem ipsum dolor sit amet"
icon={FaBookBookmark}
iconPosition="right"
></CardCaption>
<CardCaption
title="CardCaption 2"
caption="Beccatevi questo lorem ipsum dolor sit amet: lorem ipsum dolor sit amet"
icon={FaBookBookmark}
iconPosition="top"
></CardCaption>
</div>
</main>
)
}
26 changes: 26 additions & 0 deletions src/components/card-caption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { IconType } from "react-icons"
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "./ui/card"

export function CardCaption({
title,
caption,
icon,
iconPosition = "right",
}: {
title: string
caption: string
icon?: IconType
iconPosition?: "top" | "right"
}) {
return (
<Card>
<CardHeader className={`flex ${iconPosition === "right" ? "justify-between" : "flex-col-reverse"}`}>
<CardTitle>{title}</CardTitle>
{icon && <CardAction icon={icon} iconSize={iconPosition === "right" ? "normal" : "large"}></CardAction>}
</CardHeader>
<CardContent>
<p>{caption}</p>
</CardContent>
</Card>
)
}
100 changes: 100 additions & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as React from "react"
import type { IconType } from "react-icons"
import { cn } from "@/lib/utils"
import { Glass } from "../glass"
import { Button } from "./button"

function Card({
className,
size = "default",
...props
}: React.ComponentProps<typeof Glass> & { size?: "default" | "sm" }) {
return (
<Glass
data-slot="card"
data-size={size}
className={cn(
"w-78 h-66 ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl text-sm has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
className
)}
{...props}
/>
)
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"gap-1 rounded-t-xl group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
className
)}
{...props}
/>
)
}

function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn(
"text-[1.5rem] leading-snug font-medium group-data-[size=sm]/card:text-base bg-linear-to-b from-blue-secondary to-blue-primary bg-clip-text text-transparent",
className
)}
{...props}
/>
)
}

function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return <div data-slot="card-description" className={cn("text-muted-foreground text-sm", className)} {...props} />
}

function CardAction({
className,
icon: Icon,
iconSize = "normal",
...props
}: React.ComponentProps<"div"> & { icon: IconType; iconSize: "normal" | "large" }) {
const gradientId = React.useId()

return (
<div
data-slot="card-action"
className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end ", className)}
{...props}
>
<svg width="0" height="0" className="absolute">
<title>Icon gradient helper</title>
<linearGradient id={gradientId} x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" className="text-blue-secondary" stopColor="currentColor" />
<stop offset="100%" className="text-blue-primary" stopColor="currentColor" />
</linearGradient>
</svg>

<Icon
size={iconSize === "normal" ? "2rem" : "3.5rem"}
fill={`url(#${gradientId})`}
stroke={`url(#${gradientId})`}
/>
</div>
)
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("text-[.875rem] group-data-[size=sm]/card:px-3", className)}
{...props}
/>
)
}

function CardBottomButton({ className, ...props }: React.ComponentProps<typeof Button>) {
return <Button data-slot="card-footer" className={cn("self-end", className)} {...props} />
}

export { Card, CardHeader, CardBottomButton, CardTitle, CardAction, CardDescription, CardContent }