Docs/uiv1.0.0-rc.1

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' });
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 .card CSS into a global stylesheet and setting styles directly rather than relying on per-component injection.
  • The content and footer props 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 to CardProps.content.
  • ThemeService adjusts box-shadow opacity, border colour, background colour, and text colour. Without ThemeService, the component defaults to light-theme values (background: #ffffff, border: 1px solid #e0e0e0).
  • variant: 'elevated' adds box-shadow: 0 4px 6px rgba(0,0,0,0.1) in light mode, and rgba(0,0,0,0.3) in dark mode. The outlined variant replaces the default 1px border with a 2px border using the same colour token.

Pitfalls

  • content is required even though it accepts an empty string. Do not pass undefined — TypeScript will reject it, but at runtime it would render undefined as the card body text.
  • The card-footer section is only rendered when footer is non-empty. Do not rely on the card-footer element being present in the DOM for CSS targeting if the footer prop is omitted.
  • AI styling (aiStyler) is asynchronous. Call render() after the style generation Promise resolves; 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 ↗