---
phase: 10
slug: events
status: approved
shadcn_initialized: false
preset: none
created: 2026-05-16
reviewed_at: 2026-05-16
---

# Phase 10 - UI Design Contract

> Visual and interaction contract for the first functional tablo Events tab.

## Design System

| Property | Value |
|----------|-------|
| Tool | none |
| Preset | not applicable |
| Component library | local templ components in `backend/internal/web/ui` |
| Icon library | none for Phase 10; use text labels for clarity |
| Font | existing system sans stack from the browser/Tailwind reset |

Phase 10 must stay inside the current Go + HTMX + templ surface. Do not introduce shadcn, React calendar widgets, third-party date pickers, or a new CSS framework.

## Screen Contract

The Events tab is a compact work surface inside `TabloDetailPage`, not a marketing page or decorative calendar.

Primary screen:

- Add an `Events` tab to the existing Overview / Tasks / Files tab row.
- Render `EventsTabFragment` inside `#tab-content`.
- Use `#events-tab` as the outer refresh target for create, update, and delete mutations.
- Use `#event-form-slot` above the calendar grid for inline create, edit, validation, and delete-confirm fragments.

Visual focal point:

- The month header and calendar grid are the primary anchor.
- The `New event` button is the primary action, placed in the same row as the month title/navigation.
- Day cells are secondary scan targets. Event title pills should be visually quiet and should not compete with the primary action.

## Spacing Scale

Declared values must be multiples of 4:

| Token | Value | Usage |
|-------|-------|-------|
| xs | 4px | Weekday labels, event title gaps, compact metadata |
| sm | 8px | Day-cell internal gaps, form field gaps, inline button gaps |
| md | 16px | Form padding, calendar row gaps, toolbar gaps |
| lg | 24px | Tab section separation, form-to-calendar separation |
| xl | 32px | Major tab content separation if needed |
| 2xl | 48px | Not expected in this phase |
| 3xl | 64px | Not expected in this phase |

Exceptions:

- Interactive controls must keep the existing minimum 44px touch target when using `ui.Button` variants.
- Calendar day cells may use CSS grid sizing rather than fixed pixel heights, but each cell must have a stable minimum height of at least 96px on desktop and at least 80px on narrow screens.

## Typography

Use exactly these font roles for new Events UI:

| Role | Size | Weight | Line Height |
|------|------|--------|-------------|
| Body | 16px | 400 | 1.5 |
| Label | 14px | 600 | 1.4 |
| Heading | 20px | 600 | 1.3 |
| Display | 28px | 600 | 1.2 |

Rules:

- Do not add new font sizes for event cells. Use 14px labels/body-small treatment for weekday labels, dates, and event titles.
- Do not add a third font weight. Existing regular and semibold are enough.
- Letter spacing stays at 0.

## Color

| Role | Value | Usage |
|------|-------|-------|
| Dominant (60%) | `#ffffff` | Page background, calendar cells, forms |
| Secondary (30%) | `#f8fafc`, `#f1f5f9`, `#e2e8f0` | Header strip, muted surfaces, borders, inactive day cells |
| Accent (10%) | `#2563eb` | `New event` primary CTA, focus rings, active month navigation when needed |
| Destructive | `#b91c1c` | Delete event action and confirmation only |

Accent reserved for:

- `New event` primary CTA.
- Focus-visible outlines through existing button/input behavior.
- Text links where the app already uses `text-blue-600`, such as event deep links if needed.

Do not use blue for every clickable title in the calendar. Event titles should use slate text on white or slate-50 surfaces so the calendar remains scannable.

## Calendar Layout Contract

Calendar toolbar:

- Left: current month label, formatted like `May 2026`.
- Right: `Previous month`, `Next month`, and `New event` controls.
- On mobile, toolbar may wrap to two rows, but controls must not overlap the month label.

Calendar grid:

- Seven columns on desktop and tablet.
- On narrow screens, keep seven columns if text still fits; otherwise day cells may reduce vertical detail but must preserve the month grid.
- Weekday row uses short labels: `Mon`, `Tue`, `Wed`, `Thu`, `Fri`, `Sat`, `Sun`.
- Out-of-month cells use muted slate text/background and should not contain create affordances unless the implementation chooses to show adjacent-month dates.

Day cell contract:

- Top-left date number, 14px semibold.
- Clicking an empty area of an in-month day cell may open the create form with `date=YYYY-MM-DD` prefilled.
- Existing event titles render as small rows/pills under the date.
- Show at most three event titles per cell, followed by `+N more` when needed.
- Event titles must truncate or wrap safely without expanding adjacent columns.

## Form Contract

Create form:

- Opened by `New event` or day-cell create action.
- Rendered in `#event-form-slot`.
- Fields: `Title`, `Date`, `Start time`, `End time (optional)`, `Location (optional)`, `Description (optional)`.
- Submit CTA: `Create event`.
- Secondary action: `Close form`.

Edit form:

- Opened by clicking an event title.
- Rendered in `#event-form-slot`.
- Same fields as create.
- Submit CTA: `Save event changes`.
- Secondary action: `Close form`.
- Destructive entry point: `Delete event`.

Delete confirmation:

- Inline confirmation in `#event-form-slot`.
- Heading: `Delete event?`
- Body: `This removes the event from this tablo. This cannot be undone.`
- Confirm CTA: `Delete event`.
- Secondary action: `Keep event`.

Validation:

- Field errors appear below their fields using the existing `FieldError` pattern.
- General errors appear above the form using the existing `GeneralError` pattern.
- End-time validation copy: `End time must be after the start time.`
- Validation responses keep submitted values visible.

## Copywriting Contract

| Element | Copy |
|---------|------|
| Tab label | `Events` |
| Primary CTA | `New event` |
| Create submit | `Create event` |
| Edit submit | `Save event changes` |
| Form dismiss | `Close form` |
| Empty day | no copy |
| Empty month body | no special empty state; the visible calendar and `New event` action are the empty state |
| Error state | `End time must be after the start time.` |
| Destructive confirmation | `Delete event?` / `This removes the event from this tablo. This cannot be undone.` |

Do not introduce instructional copy explaining how calendars work. The UI should expose the action directly.

## Interaction Contract

- Switching to the Events tab uses the same HTMX tab pattern as Tasks and Files.
- Previous/next month links must update `#tab-content` and push `/tablos/{id}/events?month=YYYY-MM`.
- Create, update, and delete responses refresh `#events-tab` or `#tab-content` consistently so the calendar never shows stale event titles.
- Day-cell prefill comes from query params only when rendering a new form. Submitted form values always win over query defaults.
- Non-HTMX requests must still return a full page with the Events tab active.

## Accessibility Contract

- The calendar must use semantic labels that make the month and day context clear.
- Event title controls must expose `aria-label="Edit event: {title}"`.
- Day create controls must expose `aria-label="Create event on YYYY-MM-DD"`.
- Month navigation controls must include the target month in their accessible label.
- Focus-visible states must remain visible on buttons, links, and form inputs.

## Registry Safety

| Registry | Blocks Used | Safety Gate |
|----------|-------------|-------------|
| none | none | not applicable |

No third-party UI registry, calendar component, or date picker is approved for Phase 10.

## Checker Sign-Off

- [x] Dimension 1 Copywriting: PASS
- [x] Dimension 2 Visuals: PASS
- [x] Dimension 3 Color: PASS
- [x] Dimension 4 Typography: PASS
- [x] Dimension 5 Spacing: PASS
- [x] Dimension 6 Registry Safety: PASS

**Approval:** approved 2026-05-16
