# Creating Templates
Guide for adding a new template to `@dui/templates`.
---
## What templates are
Templates are pre-composed UI patterns built from DUI components + vanilla HTML/CSS. Unlike components (which extend primitives), templates are **self-contained** — they own all their CSS and ship a complete look that adapts via design tokens.
### Templates vs. components
| | Component | Template |
|---|---|---|
| Extends | A primitive class | `LitElement` directly |
| Styles | Aesthetic layer on top of primitive | Self-contained — owns all CSS |
| Purpose | Reusable primitive (button, badge) | Ready-to-use pattern (feed item, social post) |
| Package | `@dui/components` | `@dui/templates` |
### What templates should NOT do
- **No data fetching.** Templates are presentational. Feed them props; they render.
- **No global state.** No context providers, no stores.
- **No complex interaction logic.** Templates can compose interactive DUI components (accordion, tabs) — the component owns the interaction. But templates shouldn't implement their own state machines, pagination, or sorting.
---
## File structure
Each template category gets its own folder:
```
packages/templates/src/
{category}/
{name}.ts # Template class
index.ts # Re-exports + family array
all.ts # Barrel: all template families
```
---
## The template class
Here's the pattern, using `dui-feed-item` as an example:
```typescript
// packages/templates/src/feed/feed-item.ts
import { css, html, LitElement, nothing, type TemplateResult } from "lit";
import { property } from "lit/decorators.js";
import { base } from "@dui/core/base"; // resolves to @dui/primitives/core/base
// Side-effect imports — registers DUI components used in this template
import "@dui/components/badge";
const styles = css`
:host {
display: block;
}
article {
display: flex;
flex-direction: column;
gap: var(--space-1_5);
padding: var(--space-3) var(--space-4);
border: var(--border-width-thin) solid var(--border);
border-radius: var(--radius-md);
background: var(--surface-1);
}
.title {
font-family: var(--font-sans);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-semibold);
color: var(--foreground);
}
.meta {
font-size: var(--font-size-xs);
color: var(--text-2);
}
`;
export class DuiFeedItem extends LitElement {
static tagName = "dui-feed-item" as const;
static override styles = [base, styles];
@property() override accessor title = "";
@property() accessor subtitle = "";
@property() accessor timestamp = "";
@property() accessor severity = "";
override render(): TemplateResult {
return html`
${this.title}
${this.severity
? html`${this.severity}`
: nothing}
${this.subtitle}
`;
}
}
customElements.define(DuiFeedItem.tagName, DuiFeedItem);
```
### Key conventions
| Convention | Details |
|---|---|
| **Tag name** | `dui-{name}` — plain `dui-` prefix |
| **`static tagName`** | Required for the tag name |
| **`customElements.define()`** | At module level — self-registers on import |
| **Component dependencies** | Side-effect imports (`import "@dui/components/badge"`) — triggers registration |
| **`base` import** | Always include `base` from `@dui/core/base` (part of `@dui/primitives`) for structural resets |
| **Design tokens only** | Never hardcode `px`, `rem`, or color values |
| **Semantic HTML** | Use ``, ``, `