Skip to content

CardContainer Component

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.

  • Consistent Styling: Pre-configured shadow, border, and rounded corners
  • Flexible Layout: Uses Astro slots for maximum flexibility
  • Semantic HTML: Renders as <section> element with proper Card structure
  • Accessibility: Built on @fpkit/acss Card component with ARIA support
  • Optional Titles: Display section titles when needed
  • Dashboard Ready: Perfect for dashboard panels and content sections

The 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>

export type Props = {
title?: string // Optional title displayed at top of card
}
PropTypeRequiredDefaultDescription
titlestringNoundefinedOptional title displayed in Card.Title section

The component comes pre-configured with:

  • Shadow: xl shadow for elevation
  • Border: 1px solid border with --color-neutral-100
  • Border Radius: rounded-lg corners
  • Width: w-full for full container width
  • Border Style: Solid border with neutral color

Used 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>

  1. 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>
  2. Combine with Path Aliases

    Always import using path aliases for consistency:

    ---
    // ✅ Good: Path alias
    import CardContainer from '#components/astro/CardContainer.astro'
    // ❌ Bad: Relative path (do not do this)
    import CardContainer from '../../components/astro/CardContainer.astro'
    ---
  3. 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>
  4. 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> element
  • shadow="xl": Applies @fpkit/acss extra-large shadow
  • classes: Additional CSS classes for styling
  • style: Inline style for border customization
  • <slot />: Accepts any child content

  • Card (from @fpkit/acss) - Base card component
  • RoleGuard - Protect content based on user roles
  • UpcomingEvents - Display upcoming events
  • EventsListView - List view for events

Component Location: src/components/astro/CardContainer.astro

Used In:

  • /dashboard - Dashboard homepage
  • /dashboard/events - Events listing page
  • Various protected dashboard sections