Layout
Card
A surface for grouping related content, styled with the quebi design system. Composes a header, content, and footer, with default and feature variants plus an optional hover glow.
layoutsurfacecontainerdisplay
Default
A muted surface with the signature faint cyan border.
Storage upgrade
Expand your workspace with another 500 GB of fast object storage.
Billed monthly. Cancel anytime — no long-term contract required.
€9/mo
Feature
The brand-tinted variant with a glow. Reserve it for the hero card.
Unlimited Pro
Everything, everywhere, all at once. Priority support included.
The complete quebi platform with no usage caps.
€49/mo
Interactive
Opt in to the hover lift + glow for link- or button-like cards.
Open dashboard
Jump back into your projects and recent activity.
With action
CardAction pins a control to the top-right of the header.
Team members
3 people have access to this project.
Composed manually
Use CardTitle and CardDescription directly for full control.
Custom layout
Compose the pieces yourself when props aren't enough.
Every sub-component accepts a className and forwards native div props.
Source
Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.
import { cn } from "@/lib/utils"
/**
* Card — quebi design system
*
* A surface for grouping related content. Depth comes from quebi glows, never
* drop shadows; the signature is a faint cyan border over a near-transparent
* white fill. Interactive cards lift on hover and pick up a brand glow.
*
* Sub-components compose the layout: CardHeader (title + description + action),
* CardContent, and CardFooter.
*/
export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
/** `feature` swaps the muted surface for a brand-tinted, glowing one. Reserve
* it for the hero/feature card in a set — not every card. */
variant?: "default" | "feature"
/** Opt in to the hover lift + glow. Set for cards that behave like a link or
* button. Default cards stay static so informational surfaces don't imply
* interactivity. */
interactive?: boolean
}
const Card = ({ className, variant = "default", interactive = false, ...props }: CardProps) => {
return (
<div
data-slot="card"
className={cn(
// flex-col + h-full so a child with `mt-auto` (e.g. the action button)
// pins to the bottom and buttons align across a row of cards.
"flex flex-col h-full rounded-quebi-md p-5 text-white",
variant === "feature"
? "border border-quebi-brand/30 bg-quebi-brand/[0.06] shadow-quebi-glow"
: "border border-cyan-500/10 bg-white/[0.02]",
interactive &&
"transition-all duration-200 ease-out hover:-translate-y-0.5 hover:border-quebi-brand/30 hover:shadow-quebi-glow",
className,
)}
{...props}
/>
)
}
interface HeaderProps extends React.HTMLAttributes<HTMLDivElement> {
title?: string
description?: string
}
const CardHeader = ({ className, title, description, children, ...props }: HeaderProps) => (
<div
data-slot="card-header"
className={cn(
"grid auto-rows-min grid-rows-[auto_auto] items-start gap-1 has-data-[slot=card-action]:grid-cols-[1fr_auto]",
className,
)}
{...props}
>
{title && <CardTitle>{title}</CardTitle>}
{description && <CardDescription>{description}</CardDescription>}
{!title && typeof children === "string" ? <CardTitle>{children}</CardTitle> : children}
</div>
)
const CardTitle = ({ className, ...props }: React.ComponentProps<"div">) => {
return (
<div
data-slot="card-title"
className={cn(
"font-sans font-semibold text-lg text-white tracking-[-0.01em] text-balance",
className,
)}
{...props}
/>
)
}
const CardDescription = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
data-slot="card-description"
className={cn("text-sm text-quebi-fg-muted text-pretty", className)}
{...props}
/>
)
}
const CardAction = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className,
)}
{...props}
/>
)
}
const CardContent = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
return <div data-slot="card-content" className={cn("mt-3", className)} {...props} />
}
const CardFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => {
return (
<div
data-slot="card-footer"
className={cn("flex items-center mt-4", className)}
{...props}
/>
)
}
export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }