Card Component
Overview
Card is a themed, server-renderable container component for displaying grouped content with an optional title and footer. It extends BaseComponent<CardProps> and is decorated with @Component({ selector: 'udiot-card' }). Like Button, it injects ThemeService for automatic light/dark styling and supports an optional AIStyler for AI-generated CSS.
Key Concepts
- Renders a
<div class="card ...">element with optional title (card-title), content (card-content), and footer (card-footer) sections. - Three variants:
default,elevated(drop shadow),outlined(2px border). - Theme-aware fallback styles control background, text, border, and shadow colours.
createCard(props)factory for convenience instantiation.
API Reference
CardProps
interface CardProps {
title?: string; // Card header text (optional)
content: string; // Body content HTML or text (required)
footer?: string; // Footer content HTML or text (optional)
variant?: 'default' | 'elevated' | 'outlined'; // Visual style (default: 'default')
aiStyler?: AIStyler; // AI styling bridge; omit for built-in styles
}
Card
@Component({ selector: 'udiot-card' })
class Card extends BaseComponent<CardProps> {
constructor(props: CardProps);
render(): string; // Returns full HTML string including <style>
}
Factory
function createCard(props: CardProps): Card;
Basic Usage
import { createCard } from '@tagna/udiot';
const card = createCard({
title: 'Getting Started',
content: '<p>Welcome to UDIOT. Read the docs to learn more.</p>',
footer: 'Last updated: 2026-03-22',
variant: 'elevated',
});
console.log(card.render());
// <style>.card { ... }</style>
// <div class="card card-elevated">
// <div class="card-title">Getting Started</div>
// <div class="card-content"><p>Welcome to UDIOT...</p></div>
// <div class="card-footer">Last updated: 2026-03-22</div>
// </div>
Common Patterns
All three variants
import { createCard } from '@tagna/udiot';
const base = createCard({ content: 'Default card', variant: 'default' });
const elevated = createCard({ content: 'Elevated card', variant: 'elevated' });
const outlined = createCard({ content: 'Outlined card', variant: 'outlined' });
Content-only card (no title or footer)
const infoCard = createCard({
content: '<p>This card has only a body.</p>',
});
Composing cards in a grid layout
import { createCard } from '@tagna/udiot';
function renderProductGrid(products: Product[]): string {
const cards = products.map(p =>
createCard({
title: p.name,
content: `<p>${p.description}</p><strong>$${p.price}</strong>`,
footer: `SKU: ${p.sku}`,
variant: 'elevated',
}).render()
);
return `<div class="grid grid-cols-3">${cards.join('')}</div>`;
}
With AI styling
import { createCard } from '@tagna/udiot';
import { AIStyler } from '@tagna/udiot';
const card = createCard({
title: 'AI-styled card',
content: '<p>Styled by AI.</p>',
aiStyler: new AIStyler(aiClient),
variant: 'default',
});
Integration Notes
- Like
Button,Card.render()emits a<style>block per instance. For pages with many cards, consider extracting the shared.cardCSS into a global stylesheet and settingstylesdirectly rather than relying on per-component injection. - The
contentandfooterprops are interpolated directly into the HTML string without escaping. This allows rich HTML inside cards, but requires you to sanitise user-supplied content before passing it toCardProps.content. ThemeServiceadjusts box-shadow opacity, border colour, background colour, and text colour. WithoutThemeService, the component defaults to light-theme values (background: #ffffff,border: 1px solid #e0e0e0).variant: 'elevated'addsbox-shadow: 0 4px 6px rgba(0,0,0,0.1)in light mode, andrgba(0,0,0,0.3)in dark mode. Theoutlinedvariant replaces the default 1px border with a 2px border using the same colour token.
Pitfalls
contentis required even though it accepts an empty string. Do not passundefined— TypeScript will reject it, but at runtime it would renderundefinedas the card body text.- The
card-footersection is only rendered whenfooteris non-empty. Do not rely on thecard-footerelement being present in the DOM for CSS targeting if thefooterprop is omitted. - AI styling (
aiStyler) is asynchronous. Callrender()after the style generationPromiseresolves; otherwise the initial render will use empty styles and a second render is needed to display the AI-generated CSS.
From the canonical UDIOT public documentation.Try it in the playground ↗