UI Components
Building reusable server-side components with TypeScript template strings.
Component Structure
MiniWork components are TypeScript functions that return HTML strings. They follow a consistent pattern:
// Component with props interface
interface ButtonProps {
children: string;
variant?: 'primary' | 'secondary' | 'danger';
href?: string;
size?: 'sm' | 'md' | 'lg';
}
export function button({ children, variant = 'primary', href, size = 'md' }: ButtonProps): string {
const classes = `btn btn-${variant} btn-${size}`;
if (href) {
return `<a href="${href}" class="${classes}">${children}</a>`;
}
return `<button type="button" class="${classes}">${children}</button>`;
}
Composing Components
Components can be composed together to build complex UIs:
// Card component
export function card(content: string, title?: string): string {
return `
<div class="card">
${title ? `<h3 class="card-title">${title}</h3>` : ''}
<div class="card-content">${content}</div>
</div>
`;
}
// Stats card using card component
export function statsCard(label: string, value: number, icon: string): string {
return card(`
<div class="stats-icon">${icon}</div>
<div class="stats-value">${value}</div>
<div class="stats-label">${label}</div>
`);
}
Layout Components
Shared layout components ensure consistency across pages:
src/components/ui.ts
// Site header for public pages
export function siteHeader({ currentPath }: { currentPath?: string }): string {
return `
<header class="site-header">
<div class="site-header-inner">
<a href="/" class="site-logo">MiniWork</a>
<nav class="site-nav">
<a href="/docs" class="${currentPath === '/docs' ? 'active' : ''}">Docs</a>
<a href="/login" class="btn btn-primary btn-sm">Login</a>
</nav>
</div>
</header>
`;
}
// Admin sidebar with active state
export function adminSidebar({ activePage, userEmail }): string {
const links = [
{ href: '/admin', id: 'dashboard', label: 'Dashboard', icon: '📊' },
{ href: '/admin/users', id: 'users', label: 'Users', icon: '👥' },
];
return `
<aside class="admin-sidebar">
${links.map(l => `
<a href="${l.href}" class="sidebar-link ${activePage === l.id ? 'active' : ''}">
${l.icon} ${l.label}
</a>
`).join('')}
</aside>
`;
}
HTMX Integration
Add interactivity with HTMX attributes:
export function deleteButton(id: number, target: string): string {
return `
<button
hx-delete="/api/items/${id}"
hx-confirm="Are you sure?"
hx-target="${target}"
hx-swap="outerHTML"
class="btn btn-danger btn-sm"
>
Delete
</button>
`;
}
HTML Escaping
Always escape user input with the escapeHtml() helper to prevent XSS attacks.