# @spark-web/tabs — AI Context

## What this is

A tab interface built on Radix UI Tabs. Renders a horizontal tab bar with
associated content panels. Tabs activate on focus by default.

## Exports

- `Tabs` — root container
- `TabList` — the horizontal row of tab triggers
- `Tab` — individual tab trigger (must be a direct child of `TabList`)
- `TabPanels` — container for panels (renders a `Divider` above by default)
- `TabPanel` — individual panel (must be a direct child of `TabPanels`)

Tabs and panels are matched by position — `Tab[0]` corresponds to `TabPanel[0]`.
There are no explicit `value` props to wire up.

## Usage

```tsx
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@spark-web/tabs';

<Tabs>
  <TabList>
    <Tab>Email</Tab>
    <Tab>SMS</Tab>
  </TabList>
  <TabPanels>
    <TabPanel>
      <EmailHistoryTable />
    </TabPanel>
    <TabPanel>
      <SmsHistoryTable />
    </TabPanel>
  </TabPanels>
</Tabs>;
```

## Props

### `Tabs`

| Prop             | Type                      | Default       | Notes                                         |
| ---------------- | ------------------------- | ------------- | --------------------------------------------- |
| `defaultIndex`   | `number`                  | `0`           | Index of initially active tab                 |
| `activationMode` | `'automatic' \| 'manual'` | `'automatic'` | Automatic activates on focus; manual on click |

### `Tab`

| Prop       | Type                          | Notes                             |
| ---------- | ----------------------------- | --------------------------------- |
| `disabled` | `boolean`                     | Prevents interaction with the tab |
| `paddingY` | `keyof SparkTheme['spacing']` | Vertical padding around the tab   |

### `TabPanels`

| Prop          | Type      | Default | Notes                                          |
| ------------- | --------- | ------- | ---------------------------------------------- |
| `showDivider` | `boolean` | `true`  | Renders a `Divider` above the panels when true |

## Internal admin surface: background override

`@spark-web/tabs@5.2.1` sets a non-transparent `background: surface` on tab
buttons. This conflicts with the neutral background of `SectionCard` in admin
surfaces, producing a visible white box behind each tab trigger.

Override with the following CSS wrapper whenever `Tabs` is used inside a
`SectionCard`:

```tsx
import { css } from '@emotion/css';
import { Box } from '@spark-web/box';

<Box
  className={css({
    '> div > div > div > button': {
      backgroundColor: 'transparent !important',
      letterSpacing: 0,
      '&:focus-visible': {
        outline: '2px solid currentColor',
        outlineOffset: 2,
      },
    },
  })}
>
  <Tabs>...</Tabs>
</Box>;
```

This override is required in admin surfaces — never omit it when rendering
`Tabs` inside a `SectionCard`.

## Dynamic tab construction

Build the tabs array from data before rendering, and return `null` if no tabs
can be constructed:

```tsx
const tabs = useMemo(() => {
  const result: { label: string; content: ReactNode }[] = [];
  if (hasEmail) result.push({ label: 'Email', content: <EmailHistoryTable /> });
  if (hasMobile) result.push({ label: 'SMS', content: <SmsHistoryTable /> });
  return result;
}, [hasEmail, hasMobile]);

if (tabs.length === 0) return null;

return (
  <Tabs>
    <TabList>
      {tabs.map(tab => (
        <Tab key={tab.label}>{tab.label}</Tab>
      ))}
    </TabList>
    <TabPanels>
      {tabs.map(tab => (
        <TabPanel key={tab.label}>{tab.content}</TabPanel>
      ))}
    </TabPanels>
  </Tabs>
);
```

## Do NOTs

- NEVER render `Tabs` inside `SectionCard` without the
  `backgroundColor: 'transparent !important'` override — the surface background
  will bleed through
- NEVER omit the null guard when tabs are built dynamically — render `null`, not
  an empty `Tabs` wrapper
- NEVER mix `Tab` with non-`Tab` children inside `TabList` — it throws at
  runtime
- NEVER mix `TabPanel` with non-`TabPanel` children inside `TabPanels` — it
  throws at runtime
- NEVER use `value` strings to match tabs and panels — position-based matching
  is automatic
