Server-Rendered
Built as an Astro component for optimal SSR performance.
The CardContainer component is a reusable Astro component that wraps content in a styled card layout using the @fpkit/acss Card component. It provides consistent styling, shadows, borders, and optional titles for content sections across the application.
Server-Rendered
Built as an Astro component for optimal SSR performance.
@fpkit/acss Integration
Uses Card component from @fpkit/acss for accessibility and semantic HTML.
Flexible Content
Accepts any content via slots - text, lists, components, or forms.
Optional Titles
Supports optional titles for content organization.
<section> element with proper Card structureThe CardContainer component is located at:
src/components/astro/CardContainer.astro---import CardContainer from '#components/astro/CardContainer.astro'---
<CardContainer> <p>Your content goes here</p></CardContainer>---import CardContainer from '#components/astro/CardContainer.astro'---
<CardContainer title="Section Title"> <p>Your content goes here</p></CardContainer>---import CardContainer from '#components/astro/CardContainer.astro'import EventList from '#components/react/EventList'---
<CardContainer title="Upcoming Events"> <p class="text-neutral-600 mb-4"> Showing upcoming events for this month </p> <EventList events={events} client:load /></CardContainer>export type Props = { title?: string // Optional title displayed at top of card}| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | No | undefined | Optional title displayed in Card.Title section |
The component comes pre-configured with:
xl shadow for elevation1px solid border with --color-neutral-100rounded-lg cornersw-full for full container widthUsed in /dashboard/events to display event listings:
---import CardContainer from '#components/astro/CardContainer.astro'import { EventsListView } from '#components/react/EventsListView'import { getAllEvents, EVENT_LIST_FIELDS } from '#utils/events'
const events = await getAllEvents(50, true, undefined, EVENT_LIST_FIELDS)---
<section class="events-container w-full"> <CardContainer> <p class="text-neutral-600 mb-4 text-sm"> Showing {events.length} event{events.length !== 1 ? 's' : ''} </p> <EventsListView events={events} showActions={true} client:load /> </CardContainer></section>Wrapping a list component in a titled container. /dashboard used this
composition until it moved to the Impact grid — the pattern still applies
anywhere a titled card wraps a list:
---import CardContainer from '#components/astro/CardContainer.astro'import UpcomingEvents from '#components/astro/UpcomingEvents.astro'---
<section> <CardContainer title="Upcoming Events"> <UpcomingEvents /> </CardContainer></section>Combined with RoleGuard for role-based content:
---import CardContainer from '#components/astro/CardContainer.astro'import RoleGuard from '#components/astro/RoleGuard.astro'import { Link } from '@fpkit/acss'---
<section> <RoleGuard allowedRoles={['admin', 'super_admin']}> <CardContainer> <p>Access user management for advanced administration features.</p> <div style="text-align: center; margin-top: 2rem;"> <Link.LinkButton href="/dashboard/users">Go to User Management</Link.LinkButton> </div> </CardContainer> </RoleGuard></section>Use CardContainer to visually separate different content sections:
<section class="grid grid-cols-1 md:grid-cols-2 gap-4"> <CardContainer title="Recent Activity"> <!-- Activity feed content --> </CardContainer>
<CardContainer title="Quick Stats"> <!-- Statistics content --> </CardContainer></section>Wrap forms in CardContainer for visual emphasis:
<CardContainer title="Create New Event"> <EventFormRHF csrfToken={csrfToken} client:load /></CardContainer>Create consistent dashboard widget layouts:
<div class="dashboard-grid"> <CardContainer title="User Statistics"> <StatsDisplay stats={userStats} /> </CardContainer>
<CardContainer title="Recent Messages"> <MessageList messages={recentMessages} /> </CardContainer>
<CardContainer title="System Status"> <SystemHealthIndicator /> </CardContainer></div>While CardContainer comes with pre-configured styles, you can customize appearance:
<div class="custom-container"> <CardContainer title="Custom Styled Card"> <p>Content here</p> </CardContainer></div>
<style> .custom-container :global(section) { background: var(--color-primary-50); border-color: var(--color-primary-200); }</style><div class="p-6 bg-neutral-50"> <CardContainer title="Contained Card"> <p>Card with wrapper padding and background</p> </CardContainer></div><div class="grid grid-cols-1 md:grid-cols-3 gap-6"> <CardContainer title="Card 1"> <p>First card content</p> </CardContainer>
<CardContainer title="Card 2"> <p>Second card content</p> </CardContainer>
<CardContainer title="Card 3"> <p>Third card content</p> </CardContainer></div>Use for Content Sections
CardContainer works best for distinct content sections that benefit from visual separation:
<!-- ✅ Good: Distinct content sections --><CardContainer title="User Profile"> <UserProfileDisplay user={currentUser} /></CardContainer>
<CardContainer title="Recent Activity"> <ActivityFeed activities={recentActivities} /></CardContainer>Combine with Path Aliases
Always import using path aliases for consistency:
---// ✅ Good: Path aliasimport CardContainer from '#components/astro/CardContainer.astro'
// ❌ Bad: Relative path (do not do this)import CardContainer from '../../components/astro/CardContainer.astro'---Title Consistency
Use titles consistently - either all sections have titles or none do:
<!-- ✅ Good: Consistent approach --><CardContainer title="Section 1">...</CardContainer><CardContainer title="Section 2">...</CardContainer>
<!-- ❌ Inconsistent: Mixed approach --><CardContainer title="Section 1">...</CardContainer><CardContainer>...</CardContainer>Responsive Layouts
Combine with responsive grid utilities for adaptive layouts:
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <CardContainer title="Widget 1">...</CardContainer> <CardContainer title="Widget 2">...</CardContainer> <CardContainer title="Widget 3">...</CardContainer></div>The CardContainer component implementation:
---import { Card } from '@fpkit/acss'
export type Props = { title?: string}
const { title } = Astro.props---
<Card as="section" classes="shadow-md rounded-lg w-full border border-solid border-neutral-100" shadow="xl" style={{ border: 'solid 1px var(--color-neutral-100)' }}> { title && ( <Card.Title> <h3 class="text-lg font-semibold">{title}</h3> </Card.Title> ) } <Card.Content as="div"> <slot /> </Card.Content></Card>as="section": Renders semantic <section> elementshadow="xl": Applies @fpkit/acss extra-large shadowclasses: Additional CSS classes for stylingstyle: Inline style for border customization<slot />: Accepts any child contentComponent Location: src/components/astro/CardContainer.astro
Used In:
/dashboard - Dashboard homepage/dashboard/events - Events listing page