# Atlantis Usage Rules

Conventions and rules that apply to all Atlantis component usage. Read this
before generating any Atlantis code.

***

## Always use version= for migrated components

The following components have a v2 implementation. **Always pass
`version={2}`**. Never generate v1 usage for these components — v1 is
deprecated.

| Component        | Import                                                                   | v2 prop                                           |
| ---------------- | ------------------------------------------------------------------------ | ------------------------------------------------- |
| Autocomplete     | `import { Autocomplete } from "@jobber/components/Autocomplete"`         | `version={2}` — fully controlled, async support   |
| InputNumber      | `import { InputNumber } from "@jobber/components/InputNumber"`           | `version={2}`                                     |
| InputPhoneNumber | `import { InputPhoneNumber } from "@jobber/components/InputPhoneNumber"` | `version={2}`                                     |
| InputText        | `import { InputText } from "@jobber/components/InputText"`               | `version={2}`                                     |
| InputTime        | `import { InputTime } from "@jobber/components/InputTime"`               | `version={2}`                                     |
| Modal            | `import { Modal } from "@jobber/components/Modal"`                       | `version={2}` — uses `Modal.Provider` composition |

## Deprecated components — do not use

| Deprecated            | Replacement                                           | Notes                                              |
| --------------------- | ----------------------------------------------------- | -------------------------------------------------- |
| `MultiSelect`         | `Autocomplete` v2 with `multiple` prop, or `Combobox` | Fully deprecated, no longer supported.             |
| `RecurringSelect`     | None (will be removed)                                | Deprecated, will be removed in next major version. |
| `Chips` (dismissible) | `Autocomplete` v2 with `multiple` prop                | Dismissible Chips variant is deprecated.           |

### Deprecated props on still-supported components

| Component                              | Deprecated prop         | Replacement                                                                                    |
| -------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------- |
| `Button`                               | `to`                    | Use `url` for anchor links.                                                                    |
| `Card`                                 | `title`                 | Use the `header` prop, or compose with `Card.Header` and `Card.Body`.                          |
| `Toast`                                | `action`, `actionLabel` | Both will be removed in the next major version. Trigger follow-up work from outside the toast. |
| Text inputs (`InputText` v2 etc.)      | `onEnter`               | Use `onKeyDown` or `onKeyUp` and check the key.                                                |
| `FormField` (`maxLength` on v1 inputs) | `maxLength`             | Flagged in source as flawed; only kept for v1 backwards compatibility.                         |

## Import pattern

Always use named imports from subpath exports:

```tsx
// ✅ Correct
import { Button } from "@jobber/components/Button";
import { InputText } from "@jobber/components/InputText";

// ❌ Wrong — do not use barrel imports
import { Button, InputText } from "@jobber/components";
```

## Design tokens

Never hardcode colors, spacing, or other visual values. Use CSS Modules with CSS
custom properties from `@jobber/design`. Tokens are included in:
`@jobber/design/dist/foundation.css`

```css
/* ✅ Correct */
.header {
  color: var(--color-heading);
  padding: var(--space-base);
  border-radius: var(--radius-base);
}

/* ❌ Wrong */
.header {
  color: #1a1a1a;
  padding: 16px;
  border-radius: 4px;
}
```

## Form composition

v2 inputs are thin wrappers over native HTML inputs. They have no internal form
state, no built-in validation orchestration, and no React Hook Form dependency —
they behave like `<input>` elements with Atlantis styling. Compose them inside a
plain `<form>` and manage state and submission yourself (or with the form
library of your choice).

```tsx
<form onSubmit={handleSubmit}>
  <Content>
    <InputText version={2} name="name" placeholder="Name" />
    <InputEmail version={2} name="email" placeholder="Email" />
    <Button type="submit" label="Save" />
  </Content>
</form>
```

Do not wrap v2 inputs in the Atlantis `Form` or `FormField` components — those
are legacy helpers tied to v1 inputs and React Hook Form. Mixing them with v2
inputs will not give you v2's controlled API and is not supported.

### Validation on v2 inputs

v2 inputs do not run validation themselves. To show an error state, drive it
from your own state and pass `invalid` (boolean) and/or `error` (string message)
on the input. The same applies to `Autocomplete` v2 — pass `error` / `invalid`
rather than expecting the component to validate.

## Compound components — use the documented composition

Many components expose dot-notation subcomponents that must be used together.
Don't try to recreate their structure with raw markup or arbitrary nesting — the
parent reads its compound children to wire up state, accessibility, and styling.

| Component               | Subcomponents                                                                                                                                                                                                                                                                                | Usage                                                                                                                                                                                                                                                                                                                        |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Modal` v2              | `Modal.Provider`, `Modal.Activator`, `Modal.Content`, `Modal.Header`, `Modal.Actions`                                                                                                                                                                                                        | Wrap with `Modal.Provider`. `Modal.Header` and `Modal.Actions` must be **direct children** of `Modal.Content` for sticky behavior to work. Use `Modal.Activator` to control focus return when the trigger element unmounts.                                                                                                  |
| `Banner`                | `Banner.Provider`, `Banner.Content`, `Banner.Icon`, `Banner.DismissButton`, `Banner.Action`                                                                                                                                                                                                  | Either the simple `<Banner type="...">children</Banner>` form, or the Provider form: `Banner.Provider` > `Banner.Content` (+ optional `Banner.Icon` / `Banner.Action` / `Banner.DismissButton`).                                                                                                                             |
| `Card`                  | `Card.Header`, `Card.Body`                                                                                                                                                                                                                                                                   | `Card` > `Card.Header` + `Card.Body`. The legacy `header` string/element prop still works for simple cases. Use composition for any header that needs richer markup.                                                                                                                                                         |
| `Combobox`              | `Combobox.Activator`, `Combobox.Option`, `Combobox.Action`                                                                                                                                                                                                                                   | `Combobox` > optional `Combobox.Activator`, one or more `Combobox.Option`, optional `Combobox.Action`. Omitting `Combobox.Activator` renders the default activator.                                                                                                                                                          |
| `DataTable`             | `DataTable.Container`, `DataTable.Table`, `DataTable.Header`, `DataTable.HeaderCell`, `DataTable.SortableHeader`, `DataTable.Body`, `DataTable.Row`, `DataTable.Cell`, `DataTable.RowActions`, `DataTable.Actions`, `DataTable.Footer`, `DataTable.Pagination`, `DataTable.PaginationButton` | `DataTable.Footer` is a sibling of `DataTable.Body`, not a child. Place pagination outside the table (not inside the footer). For `layout="fixed"`, pass explicit widths via `style={{ width: "..." }}` on each `HeaderCell`.                                                                                                |
| `Menu` (web composable) | `Menu.Trigger`, `Menu.Content`, `Menu.Section`, `Menu.Header`, `Menu.HeaderLabel`, `Menu.Item`, `Menu.ItemIcon`, `Menu.ItemLabel`, `Menu.Separator`                                                                                                                                          | `Menu` > `Menu.Trigger` + `Menu.Content` containing `Menu.Item`s (optionally grouped in `Menu.Section`s). Each `Menu.Item` must include `textValue` for type-ahead and screen readers.                                                                                                                                       |
| `Page`                  | `Page.Header`, `Page.HeaderContent`, `Page.TitleBar`, `Page.Title`, `Page.Subtitle`, `Page.Intro`, `Page.Actions`, `Page.ActionPrimary`, `Page.ActionSecondary`, `Page.ActionMenu`, `Page.PrimaryButton`, `Page.SecondaryButton`, `Page.Menu`, `Page.Body`                                   | Composable `Page` API: `Page` > `Page.Header` (containing `Page.HeaderContent` with `Page.TitleBar` > `Page.Title` / `Page.Subtitle`, and `Page.Actions` with `Page.ActionPrimary` / `Page.ActionSecondary` / `Page.ActionMenu`) + `Page.Body`. The legacy props-based API (`title`, `subtitle`, `intro`, etc.) still works. |
| `SideDrawer`            | `SideDrawer.Title`, `SideDrawer.Toolbar`, `SideDrawer.Actions`, `SideDrawer.BackButton`, `SideDrawer.Footer`                                                                                                                                                                                 | All children are optional. The `Toolbar` is sticky; the `Footer` is fixed.                                                                                                                                                                                                                                                   |
| `Popover`               | `Popover.Provider`, `Popover.Arrow`, `Popover.DismissButton`                                                                                                                                                                                                                                 | Both the simple and Provider forms require `attachTo={ref}` — Popover is anchored to a referenced element, not the DOM tree.                                                                                                                                                                                                 |
| `DataList`              | `DataList.Layout`, `DataList.LayoutActions`, `DataList.EmptyState`, `DataList.Filters`, `DataList.Search`, `DataList.ItemActions`, `DataList.ItemAction`, `DataList.BatchActions`, `DataList.BatchAction`, `DataList.StatusBar`                                                              | Pass `data` and `headers` as props to `DataList`; declare layouts, filters, and per-row/batch actions as compound children. `DataList.Layout` accepts a `size` breakpoint prop to swap layouts at different viewport widths.                                                                                                 |
| `LightBox`              | `LightBox.Provider`, `LightBox.Content`, `LightBox.Background`, `LightBox.Overlay`, `LightBox.Toolbar`, `LightBox.Slides`, `LightBox.Navigation`, `LightBox.Caption`, `LightBox.Thumbnails`                                                                                                  | Wrap with `LightBox.Provider`; compose visible chrome (toolbar, navigation, caption, thumbnails) under `LightBox.Content`.                                                                                                                                                                                                   |
| `Tabs`                  | `Tab` (sibling export, not a dot-notation subcomponent)                                                                                                                                                                                                                                      | `Tabs` > one or more `<Tab label="...">` children. Each `Tab` is rendered as a panel.                                                                                                                                                                                                                                        |

## Accessibility — required props

* **Icon-only `Button`** (a `Button` with `icon` and no `label`) **must**
  include `ariaLabel`. The `Button` type system enforces this at the type level.
* **`ButtonDismiss`** requires `ariaLabel` (e.g. `"Close"`, `"Dismiss"`) — it
  has no visible text.
* **`Menu.Item`** must provide `textValue` so type-ahead and screen readers work
  when the visible content isn't plain text (e.g. when the item contains
  `Menu.ItemIcon` + `Menu.ItemLabel`).
* **`DataTable.PaginationButton`** requires `ariaLabel`; pass a function that
  returns a translated label per state (e.g. `"Next page"`, `"Previous page"`).
* **`RadioGroup` options whose `children` aren't text** must provide
  `aria-label` on the option so it has an accessible name.
* **`Modal` v2 without a `Modal.Header` title** must set either
  `modalLabelledBy` (id of an existing element) or `ariaLabel` on the modal so
  the dialog has an accessible name. A `Modal.Header` title takes precedence
  over `ariaLabel` when both are provided.

## UNSAFE\_ props

Props prefixed with `UNSAFE_` (`UNSAFE_className`, `UNSAFE_style`,
`UNSAFE_experimentalStyles`) are escape hatches. Avoid them by default. Reach
for them only when the component's documented API can't express the need, and
prefer design tokens inside any custom styles you do write. See
[Customizing components](../customizing-components/customizing-components.md) for the full guidance.

## Component specific guidelines

### Cards

* When using the Card component, always put a Content component inside of it,
  any content that needs to go inside the card goes inside the Content
  component.
* If using two cards next to each other horizontally, ensure they have a gap
  between them

### Modal

* When using a modal as part of an app, always wrap the inner content of the
  modal in the Content component, this is what gives it the padding the form
  needs from the edges of the modal.
* Content will also provide the gap necessary between fields

### Color

* Only use surface colours for backgrounds
