Blocks

Mail Block

Application UI

A Mac Mail-style three-pane layout with folders, message list, and readable preview panel.

Starter

Three-pane mail

A dense application block that keeps spacing compact and predictable.

Inbox

From Maya Patel

Design review notes

Alex, the overall direction is much clearer. The card primitive gives the dashboard and mail examples a better base to build from.

I would keep the app blocks as recipes, then promote only the parts that repeat into real components.

MailClient.vuevue
<script setup>
import { DomButton, DomCard, DomNativeSelect } from '../../../lib/vue';

const folders = ['Inbox', 'VIP', 'Sent', 'Archive', 'Trash'];
const messages = [
	{ from: 'Maya Patel', subject: 'Design review notes', preview: 'The glass card treatment is feeling much more coherent now.', unread: true },
	{ from: 'Alex Morgan', subject: 'Invoice ready', preview: 'I have attached the final version for May.', unread: false },
	{ from: 'Product Ops', subject: 'Weekly metrics', preview: 'Conversion has improved across the onboarding funnel.', unread: false },
];
</script>

<template>
	<div class="grid w-full overflow-hidden rounded-3xl border border-border bg-background shadow-2xl shadow-black/10 lg:grid-cols-[12rem_20rem_1fr]">
		<aside class="hidden border-r border-border skin-raised p-3 lg:block">
			<DomButton class="w-full justify-start">
				<svg class="size-4" viewBox="0 0 24 24" fill="none" aria-hidden="true">
					<path d="M4 20h16M5 16l10.5-10.5 3 3L8 19H5v-3Z" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" />
				</svg>
				Compose
			</DomButton>
			<nav class="mt-5 space-y-1">
				<button
					v-for="folder in folders"
					:key="folder"
					class="flex w-full items-center justify-between rounded-xl px-3 py-2 text-sm transition first:bg-secondary first:text-secondary-fg hover:bg-secondary"
				>
					<span>{{ folder }}</span>
					<span v-if="folder === 'Inbox'" class="rounded-full bg-primary px-2 py-0.5 text-[10px] font-semibold text-primary-fg">12</span>
				</button>
			</nav>
		</aside>

		<section class="border-r border-border">
			<header class="border-b border-border skin-raised p-4 backdrop-blur">
				<div class="flex items-center justify-between gap-3">
					<h3 class="font-semibold tracking-tight">Inbox</h3>
					<DomNativeSelect
						model-value="all"
						:options="[{ label: 'All', value: 'all' }, { label: 'Unread', value: 'unread' }]"
						class="w-32"
					/>
				</div>
			</header>
			<div class="divide-y divide-border">
				<button
					v-for="message in messages"
					:key="message.subject"
					class="block w-full bg-background p-4 text-left transition first:bg-secondary/60 hover:bg-secondary"
				>
					<div class="flex items-start justify-between gap-3">
						<p class="text-sm font-semibold">{{ message.from }}</p>
						<span v-if="message.unread" class="mt-1 size-2 rounded-full bg-primary"></span>
					</div>
					<p class="mt-1 text-sm font-medium">{{ message.subject }}</p>
					<p class="mt-1 line-clamp-2 text-sm leading-5 text-muted-fg">{{ message.preview }}</p>
				</button>
			</div>
		</section>

		<main class="min-h-[34rem] p-5">
			<DomCard padding="lg" class="h-full">
				<div class="flex h-full flex-col">
					<div class="border-b border-border pb-5">
						<p class="text-sm text-muted-fg">From Maya Patel</p>
						<h3 class="mt-2 text-2xl font-semibold tracking-tight">Design review notes</h3>
					</div>
					<div class="flex-1 space-y-4 py-6 text-sm leading-7 text-muted-fg">
						<p>Alex, the overall direction is much clearer. The card primitive gives the dashboard and mail examples a better base to build from.</p>
						<p>I would keep the app blocks as recipes, then promote only the parts that repeat into real components.</p>
					</div>
					<div class="flex flex-wrap gap-2 border-t border-border pt-5">
						<DomButton size="sm">Reply</DomButton>
						<DomButton variant="secondary" size="sm">Archive</DomButton>
					</div>
				</div>
			</DomCard>
		</main>
	</div>
</template>