# Layout System

URL: https://docs.forms.saastro.io/docs/layout
> Configure responsive grid layouts with manual or auto mode.

# Layout System

`@saastro/forms` provides two layout modes for organizing fields in a grid:

| Mode       | Description                              | Use Case                           |
| ---------- | ---------------------------------------- | ---------------------------------- |
| **Manual** | Fixed N-column grid with `col-span-*`    | Precise control over field widths  |
| **Auto**   | Dynamic columns based on `minFieldWidth` | Responsive, auto-adjusting layouts |

## Manual Mode

Use manual mode when you need precise control over field widths at different breakpoints.

### How It Works

1. Set a fixed number of columns (e.g., 12) — defaults to 1 if not set
2. Each field specifies how many columns it occupies (fields without `.columns()` span 1 column)
3. Use responsive breakpoints (`md`, `lg`, etc.) for different screen sizes

### Example

```tsx

const config = FormBuilder.create('contact')
  .layout('manual') // Explicit manual mode
  .columns(12) // 12-column grid
  .gap(4) // gap-4 (1rem)
  .addField(
    'firstName',
    (f) => f.type('text').label('First Name').required().columns({ default: 12, md: 6 }), // Full width on mobile, half on md+
  )
  .addField('lastName', (f) =>
    f.type('text').label('Last Name').required().columns({ default: 12, md: 6 }),
  )
  .addField(
    'email',
    (f) => f.type('email').label('Email').email().required().columns({ default: 12 }), // Always full width
  )
  .addStep('main', ['firstName', 'lastName', 'email'])
  .build();
```

> Every non-hidden field needs a validation schema — `.required()`, `.optional()`, or `.schema(zodSchema)` — otherwise the builder throws. See [Validation](/docs/validation).

### Generated CSS

```html
<div class="grid grid-cols-12 gap-4">
  <div class="col-span-12 md:col-span-6"><!-- firstName --></div>
  <div class="col-span-12 md:col-span-6"><!-- lastName --></div>
  <div class="col-span-12"><!-- email --></div>
</div>
```

### Breakpoints

| Breakpoint | Min Width | Example                    |
| ---------- | --------- | -------------------------- |
| `default`  | 0px       | `columns({ default: 12 })` |
| `sm`       | 640px     | `columns({ sm: 6 })`       |
| `md`       | 768px     | `columns({ md: 4 })`       |
| `lg`       | 1024px    | `columns({ lg: 3 })`       |
| `xl`       | 1280px    | `columns({ xl: 2 })`       |
| `2xl`      | 1536px    | `columns({ '2xl': 2 })`    |

### Common Patterns

```tsx
// Two columns on desktop
.columns({ default: 12, md: 6 })

// Three columns on large screens
.columns({ default: 12, md: 6, lg: 4 })

// Sidebar layout (4 + 8)
.columns({ default: 12, lg: 4 })  // sidebar
.columns({ default: 12, lg: 8 })  // main

// Equal thirds
.columns({ default: 12, md: 4 })
```

---

## Auto Mode

Use auto mode when you want fields to automatically adjust to available space.

### How It Works

1. Set a minimum field width (default: 240px)
2. Grid creates as many columns as fit
3. Optionally set a maximum number of columns
4. Fields stretch equally to fill available space

### Example

```tsx
const config = FormBuilder.create('settings')
  .layout('auto') // Auto mode (default)
  .minFieldWidth(280) // Minimum 280px per field
  .columns(4) // Maximum 4 columns
  .gap(6) // gap-6 (1.5rem)
  .addField('setting1', (f) => f.type('text').label('Setting 1').optional())
  .addField('setting2', (f) => f.type('text').label('Setting 2').optional())
  .addField('setting3', (f) => f.type('text').label('Setting 3').optional())
  .addField('setting4', (f) => f.type('text').label('Setting 4').optional())
  .addField('setting5', (f) => f.type('text').label('Setting 5').optional())
  .addStep('main', ['setting1', 'setting2', 'setting3', 'setting4', 'setting5'])
  .build();
```

### Generated CSS

```html
<div
  class="grid gap-6"
  style="grid-template-columns: repeat(auto-fit, minmax(max(17.5rem, calc((100% - 4.5rem) / 4)), 1fr))"
>
  <div><!-- setting1 --></div>
  <div><!-- setting2 --></div>
  <!-- ... -->
</div>
```

### Auto Mode Options

| Option          | Type     | Default | Description                |
| --------------- | -------- | ------- | -------------------------- |
| `minFieldWidth` | `number` | `240`   | Minimum width in pixels    |
| `columns`       | `1-12`   | -       | Maximum columns (optional) |
| `gap`           | `0-12`   | `4`     | Gap between fields         |

### Important Notes

- **`col-span-*` classes are ignored** in auto mode
- All fields have equal width
- Use `minFieldWidth` and `columns` to control sizing
- Great for forms where all fields should be similar size

---

## Gap Configuration

The `gap` method sets spacing between fields. Valid values are `0` to `12` (each unit = 0.25rem), and the gap applies at all breakpoints:

```tsx
.gap(0)   // no gap (no gap class is emitted; the grid default is 0)
.gap(2)   // gap-2 (0.5rem / 8px)
.gap(4)   // gap-4 (1rem / 16px) - default
.gap(6)   // gap-6 (1.5rem / 24px)
.gap(8)   // gap-8 (2rem / 32px)
.gap(12)  // gap-12 (3rem / 48px) - maximum
```

---

## When to Use Each Mode

### Use Manual Mode When:

- You need different field widths (name: 6 cols, address: 12 cols)
- You want responsive breakpoints per field
- Precise layout control is important
- Building complex multi-column layouts

### Use Auto Mode When:

- All fields should be similar width
- You want automatic responsive behavior
- Building settings forms, preferences panels
- Content adapts to container width

---

## API Reference

### FormBuilder Layout Methods

```tsx
FormBuilder.create('form')
  .layout(mode) // 'manual' | 'auto'
  .columns(n) // 1-12 (manual: grid columns; auto: max columns)
  .gap(n) // 0-12 (each unit = 0.25rem; default 4)
  .minFieldWidth(px); // Auto mode only: minimum field width in pixels
```

> `.layoutClassName(cls)` also exists in the builder API: it stores extra CSS classes in the config, but it is **declared but not yet implemented** — the current `<Form>` runtime does not apply it to the grid container.

### FieldBuilder Column Methods

`.columns()` takes an object keyed by breakpoint (it does not accept a plain number):

```tsx
.columns({ default: 12 })             // Same span at every breakpoint
.columns({ default: 12, md: 6 })      // Responsive: by breakpoint
.columns({ default: 12, sm: 6, md: 4, lg: 3 })  // Multiple breakpoints
```

In manual mode, fields without `.columns()` span 1 column. In auto mode, per-field column spans are ignored.
