Card
Card is a presentational container for grouping related content: a heading, supporting text, a body, and an optional footer.
Use GroupBox when the region is a labeled cluster of controls. Use Card when the region is a content surface with a heading and actions.
#Import
use gpui_kit::component::card::{
Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
};// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { Card, CardFooter } from "card.slint";use iced::widget::{column, container, mouse_area, row, scrollable, svg, text};
use iced::{Alignment, Element, Length, Task, Theme};#Usage
#Default
Card::new()
.child(
CardHeader::new()
.child(CardTitle::new().child("Team"))
.child(CardDescription::new().child("Invite members and set their roles.")),
)
.child(CardContent::new().child("Twelve seats remaining on this workspace."))
.child(
CardFooter::new()
.child(Button::new("cancel").label("Cancel"))
.child(Button::new("invite").primary().label("Invite")),
)Card {
title: "Team";
description: "Invite members and set their roles.";
Text {
text: "Twelve seats remaining on this workspace.";
color: Theme.muted-foreground;
}
CardFooter {
Button {
text: "Cancel";
}
Button {
text: "Invite";
variant: ButtonVariant.primary;
}
}
}// The page's first documented sample and the Slint example's first
// `GroupBox`: a header, a body, and an end-aligned footer of actions.
let default = body_box(
Card::new()
.title("Team")
.description("Invite members and set their roles.")
.body(
column![
muted("Twelve seats remaining on this workspace."),
card::footer(
row![
Button::new("Cancel")
.variant(Variant::Ghost)
.on_press(Message::Nothing),
Button::new("Invite")
.variant(Variant::Primary)
.on_press(Message::Saved),
]
.spacing(theme::SPACING_SM),
),
]
.spacing(theme::SPACING_LG),
),
);#Stacked
h_flex()
.gap_4()
.items_start()
.child(
Card::new()
.flex_1()
.child(CardHeader::new().child(CardTitle::new().child("Usage")))
.child(CardContent::new().child("4.2 GB of 10 GB used.")),
)
.child(
Card::new()
.flex_1()
.child(CardHeader::new().child(CardTitle::new().child("Plan")))
.child(CardContent::new().child("Team · billed monthly.")),
)HorizontalLayout {
spacing: 16px;
Card {
title: "Usage";
Text {
text: "4.2 GB of 10 GB used.";
color: Theme.muted-foreground;
}
}
Card {
title: "Plan";
Text {
text: "Team · billed monthly.";
color: Theme.muted-foreground;
}
}
}// Two cards side by side, each filling its half: the Slint example's
// `horizontal-stretch: 1` pair.
let stacked = body_box(
row![
Card::new()
.title("Usage")
.body(muted("4.2 GB of 10 GB used.")),
Card::new()
.title("Plan")
.body(muted("Team · billed monthly.")),
]
.spacing(theme::SPACING_LG),
);#Login
GPUINo GPUI version of this example yet.
Card {
title: "Login to your account";
description: "Enter your email below to login to your account";
Input {
placeholder: "m@example.com";
}
Button {
text: "Forgot your password?";
variant: ButtonVariant.link;
horizontal-stretch: 0;
}
Button {
text: "Login";
variant: ButtonVariant.primary;
horizontal-stretch: 1;
}
}// A field and two actions in the card body: the Slint example's login form,
// with the input's placeholder and a link variant for the secondary action.
let login = body_box(
Card::new()
.title("Login to your account")
.description("Enter your email below to login to your account")
.body(
column![
Input::new("card-login-email")
.placeholder("m@example.com")
.value(app.email.clone())
.width(Length::Fill)
.on_input(Message::Email),
Button::new("Forgot your password?")
.variant(Variant::Link)
.on_press(Message::Nothing),
Button::new("Login")
.variant(Variant::Primary)
// .width(Length::Fill)
.on_press(Message::Saved),
]
.spacing(theme::SPACING_MD),
),
);#Size
GPUINo GPUI version of this example yet.
Card {
title: "Default";
description: "The standard inset and title size.";
}
Card {
title: "Small";
description: "A compact card for dense layouts.";
size: ControlSize.small;
}// The two sizes: medium keeps the 16px inset and the text-md title, small
// tightens both.
let size = body_box(
column![
Card::new()
.title("Default")
.description("The standard inset and title size."),
Card::new()
.title("Small")
.description("A compact card for dense layouts.")
.size(ControlSize::Small),
]
.spacing(theme::SPACING_LG),
);#Spacing
GPUINo GPUI version of this example yet.
Card {
title: "16 px";
description: "Inset and the gap between sections.";
inset: 16px;
}
Card {
title: "20 px";
description: "Inset and the gap between sections.";
inset: 20px;
}
Card {
title: "24 px";
description: "Inset and the gap between sections.";
inset: 24px;
}
Card {
title: "32 px";
description: "Inset and the gap between sections.";
inset: 32px;
}// The inset is the card's padding and the gap between the header, the body
// and the footer at once. Slint lets the sample set it freely; this
// component derives it from `size`, so the sample shows the two it has —
// 16px at the medium size and 12px at the small size — rather than four it
// cannot.
let spacing = body_box(
column![
Card::new()
.title("16 px")
.description("The medium inset and the gap between sections.")
.body(muted("A body at the same inset.")),
Card::new()
.title("12 px")
.description("The small inset and the gap between sections.")
.size(ControlSize::Small)
.body(muted("A body at the same inset.")),
]
.spacing(theme::SPACING_LG),
);#Image
GPUINo GPUI version of this example yet.
Card {
cover: @image-url("assets/attachment-preview.svg");
badge: "Featured";
title: "Design systems meetup";
description: "A practical talk on component APIs and shipping faster.";
}// Slint's card has a `cover` image slot above the header. This one draws
// the header first and has no slot above it, so the caller's media element
// is the body — an application passes `image(handle)` or `svg(handle)`
// here — and the badge rides above the title as it does in Slint.
let image = body_box(
Card::new()
.badge("Featured")
.title("Design systems meetup")
.description("A practical talk on component APIs and shipping faster.")
.body(cover("attachment-preview.svg")),
);#Parts
- Card — bordered surface, padding, and vertical gap.
- CardHeader — title and description stack.
- CardTitle — primary heading.
- CardDescription — supporting copy in the muted foreground.
- CardContent — main body.
- CardFooter — action row.
All parts implement [ParentElement] and [Styled], so you can add children and refine layout without a sealed API.
#Copy source
This is a GPUI-target registry component. The CLI copies crates/component/src/card.rs. There is no gpui-base Card primitive; keep gpui-base as the dependency for behavior used by other copied components.