---
name: layout-design-system
description: >
  Design system for evaluating and composing dashboard layouts, data tables, forms, and page
  structure. Provides spacing rules, visual hierarchy principles, and density guidelines to achieve
  clean, breathable interfaces that maximize data consumption without visual stress. Trigger: When
  reviewing UI layout, adjusting spacing, designing new pages, evaluating visual balance, or
  composing dashboard/form layouts.
license: MIT
metadata:
  author: aurora
  version: '1.0'
---

## When to Use

- Reviewing or adjusting spacing in a page layout
- Creating a new list/table view, form, or dashboard page
- Evaluating visual balance and breathing room
- User asks about spacing, padding, margins, or visual density
- Composing page sections (header, toolbar, content, pagination)
- Comparing layout against reference designs (e.g., Spartan dashboard)

---

## Core Philosophy

> Clean dashboards that let you consume maximum information in a single glance, without feeling
> visual stress.

Every spacing decision serves two goals simultaneously:

1. **Breathing room** -- elements need air to be scannable
2. **Data density** -- wasted space means more scrolling and less information per viewport

The sweet spot is **structured density**: tight where repetition creates rhythm (table rows),
generous where the eye needs anchoring (section transitions).

---

## Spacing Scale

Use Tailwind's spacing scale consistently. These are the approved values for layout composition:

| Token | px  | Use case                                          |
| ----- | --- | ------------------------------------------------- |
| `0`   | 0   | Collapsed gaps, flush alignment                   |
| `0.5` | 2   | Micro adjustments (icon padding)                  |
| `1`   | 4   | Tight inner padding (compact buttons, cells)      |
| `2`   | 8   | Default cell padding, inline element spacing      |
| `3`   | 12  | Section-to-section within a group                 |
| `4`   | 16  | Content padding, layout container gaps            |
| `6`   | 24  | Major section separation (title block to content) |
| `8`   | 32  | Page-level padding, hero spacing                  |

**CRITICAL**: Avoid arbitrary values (`mt-[13px]`). Stay on the scale. If a spacing feels wrong,
move one step up or down -- don't invent intermediate values.

---

## Page Anatomy

Every data page follows this vertical structure:

```
┌─────────────────────────────────────────────────────┐
│ Site Header (breadcrumbs + sidebar trigger)          │  Fixed
├─────────────────────────────────────────────────────┤
│ gap-3 (12px)                                        │  Layout gap
├─────────────────────────────────────────────────────┤
│ Page Header (title + subtitle + primary action)     │  mb-3 to content
├─────────────────────────────────────────────────────┤
│ Toolbar (search + filters + column toggles)         │  space-y-3 group
├─────────────────────────────────────────────────────┤
│ Data Table                                          │  Core content
│   ├─ Sticky header (bg-muted, z-10)                │
│   ├─ Rows (49px height each)                       │
│   └─ Scrollable container (max-h-[700px])          │
├─────────────────────────────────────────────────────┤
│ Pagination footer                                   │  mt-1 from table
└─────────────────────────────────────────────────────┘
```

### Reference spacing values (current implementation)

| Zone                             | Class       | px  |
| -------------------------------- | ----------- | --- |
| Layout → content gap             | `gap-3`     | 12  |
| Content container padding        | `px-6 pb-6` | 24  |
| Section spacing (within content) | `space-y-3` | 12  |
| Page header → toolbar separation | `mb-3`      | 12  |
| Pagination → table separation    | `mt-1`      | 4   |

---

## Data Table Rules

### Row density

| Element       | Height | How achieved                                |
| ------------- | ------ | ------------------------------------------- |
| Header row    | 40px   | Default `hlmTh` padding                     |
| Data row      | 49px   | Cell padding `py-2 pr-0 pl-2` + content     |
| Action button | 24px   | `class="h-6 w-6 p-0.5"` (not `size="icon"`) |

### Header

- Always sticky: `class="bg-muted sticky top-0 z-10"` on `<thead>`
- Scrollable container: `max-h-[700px] overflow-auto` on wrapper div

### Cell padding

```
Data cells:    py-2 pr-0 pl-2   (8px top/bottom, 0 right, 8px left)
Header cells:  Default hlmTh     (0px top/bottom, 8px left)
```

This matches the Spartan dashboard reference: `padding: 8px 0px 8px 8px`.

### Column order convention

```
1. Actions (ellipsis menu)  -- size: 50, enableHiding: false
2. Select (checkbox)        -- size: 40, enableHiding: false
3. Primary identifier       -- (name, title, etc.)
4. Secondary fields         -- (status, type, date, etc.)
```

Actions go first (leftmost) for quick access. Select follows. Data columns fill the rest.

---

## Pagination Bar

The pagination bar sits below the table and contains three zones:

```
┌──────────────────────────────────────────────────────────┐
│ [X of Y row(s) selected]   [Rows per page: 10 v]        │
│                             [Page X of Y]  [« ‹ › »]    │
└──────────────────────────────────────────────────────────┘
```

- Left: selection count (`text-muted-foreground text-sm`)
- Right: page size selector + page indicator + navigation buttons
- Page sizes: `[5, 10, 20, 10000]` (10000 renders as "All")
- Navigation: 4 buttons (first, prev, next, last) with `size="icon-sm"` and `variant="outline"`

---

## Toolbar Rules

The toolbar row contains search/filter inputs and action buttons:

```
┌──────────────────────────────────────────────────────────┐
│ [Filter by name...]                [Customize Columns v] │
└──────────────────────────────────────────────────────────┘
```

- Layout: `flex items-center justify-between gap-2`
- Input: `w-full md:w-80` (responsive, caps at 320px)
- Customize Columns button: `variant="outline" size="sm"` with `lucideColumns2` +
  `lucideChevronDown`
- No vertical padding on the toolbar div itself -- let `space-y-3` handle it

---

## Button Sizing Guide

| Context                | Size      | Notes                                  |
| ---------------------- | --------- | -------------------------------------- |
| Primary page action    | default   | "New Tag", "Save"                      |
| Toolbar actions        | `sm`      | "Customize Columns", "Export"          |
| Pagination navigation  | `icon-sm` | Chevron buttons                        |
| Row actions (ellipsis) | custom    | `h-6 w-6 p-0.5` with `variant="ghost"` |
| Dialog actions         | default   | "Cancel", "Delete"                     |

---

## Visual Hierarchy Checklist

When evaluating a page layout, check these in order:

### 1. Vertical rhythm

- [ ] Is there a consistent spacing scale? (no jumps from 4px to 32px)
- [ ] Does each section boundary use the same gap?
- [ ] Is the title block clearly separated from the toolbar?

### 2. Breathing room

- [ ] Does the page header have enough air above it? (min 12px from site header)
- [ ] Is there space between the toolbar and the table? (12px)
- [ ] Does the pagination have a slight separation from the table? (4px extra via mt-1)

### 3. Data density

- [ ] Are table rows compact enough? (49px target)
- [ ] Is the action button small enough to not dominate? (24px, not 36px)
- [ ] Can you see 10+ rows without scrolling on a standard viewport?

### 4. Horizontal balance

- [ ] Left-aligned content (titles, names) vs right-aligned actions (buttons)?
- [ ] Does the toolbar stretch edge to edge with content at both ends?
- [ ] Are column widths proportional to their content?

### 5. Visual weight

- [ ] Is the primary action (New Tag) the heaviest element? (filled button)
- [ ] Are secondary actions lighter? (outline/ghost variants)
- [ ] Does the header row (`bg-muted`) create a subtle but clear boundary?

### 6. Stress test

- [ ] With 20+ rows, does the page still feel scannable?
- [ ] With long text in cells, does truncation or wrapping behave correctly?
- [ ] With all columns visible, is horizontal scroll manageable?

---

## Form Layout Rules

### Spacing

| Zone                       | Class       | px  |
| -------------------------- | ----------- | --- |
| Form section gap           | `space-y-6` | 24  |
| Field-to-field gap         | `space-y-4` | 16  |
| Label to input             | `space-y-2` | 8   |
| Form actions (Save/Cancel) | `mt-6`      | 24  |

### Structure

```
┌─────────────────────────────────────────────────┐
│ Section Title                                    │
│ Section description (muted)                      │
│                                                  │
│ [Label]                                          │
│ [Input field                                   ] │
│                                                  │
│ [Label]                                          │
│ [Input field                                   ] │
│                                                  │
│                          [Cancel]  [Save]        │
└─────────────────────────────────────────────────┘
```

- Labels: always above inputs (not inline)
- Full-width inputs by default, use `max-w-md` for short fields (emails, numbers)
- Action buttons right-aligned in a `flex justify-end gap-2` container

---

## Anti-Patterns

| Problem                            | Why it fails                                          | Fix                              |
| ---------------------------------- | ----------------------------------------------------- | -------------------------------- |
| `space-y-6` everywhere             | Uniform large gaps waste viewport                     | Use `space-y-3` for tight groups |
| `p-6` on content container         | Creates dead space at top when layout already has gap | Use `px-6 pb-6` (no top padding) |
| `size="icon"` for row actions      | 36px button inflates row height to 53px               | Use `h-6 w-6 p-0.5` (24px)       |
| Inconsistent section gaps          | 24px here, 8px there -- breaks visual rhythm          | Pick one scale step per zone     |
| `rounded-full` on toolbar buttons  | Pill shape clashes with standard rounded UI           | Use default border-radius        |
| Padding on both layout AND content | Double padding creates excessive margins              | One level owns padding, not both |

---

## Theme Variables (CRITICAL)

The Spartan Helm components use CSS custom properties for border-radius. If your theme scopes
`--radius` inside a class (e.g., `.theme-neutral`), you MUST also define the derived variables in
the same scope:

```css
:root .theme-neutral {
  --radius: 0.625rem;
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
}
```

Without these, Helm components render with `border-radius: 0` because the preset defines the derived
variables at `:root` level where `--radius` doesn't exist in scoped themes.

---

## Reference Implementation

The tag list (`iam/tag/features/tag-list.component.ts`) is the reference implementation for data
table pages. When creating new list views, use it as the template for:

- Page structure and spacing
- Table configuration (columns, sorting, filtering, pagination)
- Selection column setup
- Action cell pattern
- Customize Columns dropdown
- Delete confirmation dialog

The layout wrapper (`domains/admin/layout/layout.component.ts`) defines the outer spacing:

```html
<div class="flex flex-1 flex-col gap-3 px-4 pb-4">
  <router-outlet />
</div>
```

Content components should use `px-6 pb-6` (no top padding) to avoid double-padding with the layout.
