# Design Guidelines & Tokens — Xertica UI

> These guidelines define the visual language and architectural rules of Xertica UI. AI agents **must follow these strictly** — violations result in visually inconsistent, non-themeable output that breaks dark mode and brand cohesion.

---

## 1. Color System

### Token Architecture

All colors are defined as CSS custom properties on `:root` and injected by `XerticaProvider`. Tailwind classes map to these variables, enabling both light and dark mode from a single source of truth.

### Semantic Token Reference

| Role                    | Background Class | Text Class                    | Use Case                              |
| ----------------------- | ---------------- | ----------------------------- | ------------------------------------- |
| **Page background**     | `bg-background`  | `text-foreground`             | Root page area                        |
| **Card / Panel**        | `bg-card`        | `text-card-foreground`        | Content containers                    |
| **Popover / Dropdown**  | `bg-popover`     | `text-popover-foreground`     | Overlays and menus                    |
| **Muted / Subdued**     | `bg-muted`       | `text-muted-foreground`       | Backgrounds of panels, secondary text |
| **Primary action**      | `bg-primary`     | `text-primary-foreground`     | Main CTA buttons, active states       |
| **Secondary action**    | `bg-secondary`   | `text-secondary-foreground`   | Secondary buttons, badges             |
| **Accent / Hover**      | `bg-accent`      | `text-accent-foreground`      | Hover states, highlighted rows        |
| **Destructive / Error** | `bg-destructive` | `text-destructive-foreground` | Delete, revoke, danger actions        |
| **Border (general)**    | `border-border`  | —                             | Dividers, card borders                |
| **Border (forms)**      | `border-input`   | —                             | Input fields                          |
| **Focus ring**          | `ring-ring`      | —                             | Keyboard focus indicators             |

### Hard Rules

```
✅ bg-primary              → Primary action button backgrounds
✅ text-muted-foreground   → Descriptive/secondary text
✅ border-border           → All borders
✅ bg-destructive          → Error / danger states
✅ bg-success / bg-warning → Semantic status states

❌ #3B82F6                 → Never hardcode hex values
❌ rgb(59, 130, 246)       → Never use rgb() in className or style
```

> **Color rule nuance:** The restriction on generic Tailwind colors applies **contextually**:
>
> - **Semantic/status contexts** (error states, warning banners, success indicators, status badges): always use semantic tokens (`bg-destructive`, `bg-success`, `bg-warning`, `text-muted-foreground`, etc.) so they respond to theme changes and dark mode.
> - **Layout, spacing, and general UI** (custom components, Storybook stories, non-semantic decorative elements): standard Tailwind color utilities (`bg-blue-500`, `text-gray-700`, `bg-slate-100`) are acceptable when no semantic token maps to the intent.
> - **Absolute prohibitions** (all contexts): raw hex values (`#3B82F6`), `rgb()`/`hsl()` in `className` or `style`, and `style={{ color: '...' }}` for theming purposes.

---

## 2. Typography

### Font Scale

Typography uses CSS variable-based font sizing tokens. Always use the Tailwind type scale — never set font sizes in pixels directly.

| Use Case              | Class                                                               |
| --------------------- | ------------------------------------------------------------------- |
| Page title            | `text-3xl font-bold tracking-tight`                                 |
| Section heading       | `text-2xl font-semibold`                                            |
| Card title            | `text-lg font-semibold` (or `text-sm font-medium` in compact cards) |
| KPI value (StatsCard) | `text-2xl font-bold`                                                |
| Body text             | `text-sm` (default)                                                 |
| Descriptive / caption | `text-xs text-muted-foreground`                                     |
| Code / mono           | `font-mono text-sm bg-muted px-1 rounded`                           |

### Font Family

The system uses the project's configured font (default: `Inter` via Google Fonts). Do not set `font-family` inline or via utility classes.

---

## 3. Spacing & Layout

### Spacing Scale

Use Tailwind's spacing scale (based on `0.25rem` units). Common patterns:

| Context                | Value   | Class                  |
| ---------------------- | ------- | ---------------------- |
| Card internal padding  | 24px    | `p-6`                  |
| Compact panel padding  | 16px    | `p-4`                  |
| Section gap (vertical) | 24px    | `space-y-6` or `gap-6` |
| Inline element gap     | 8px     | `gap-2`                |
| Icon-to-text gap       | 8px     | `gap-2`                |
| Form field stack       | 8px     | `gap-2`                |
| Form section gap       | 16–24px | `gap-4` to `gap-6`     |

### Grid Patterns

| Pattern              | Class                                                  |
| -------------------- | ------------------------------------------------------ |
| Stats grid (4 cols)  | `grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4` |
| Dashboard dual panel | `grid grid-cols-1 lg:grid-cols-7 gap-4`                |
| Form grid            | `grid grid-cols-1 md:grid-cols-2 gap-6`                |
| Full-width field     | Add `col-span-full` or `md:col-span-2`                 |

**Never use hardcoded pixel widths in grids.** Use the responsive grid column system.

---

## 4. Border Radius

Xertica UI uses a CSS variable for border radius: `--radius`. Use Tailwind utilities that map to this variable:

| Size   | Class                        | Use                      |
| ------ | ---------------------------- | ------------------------ |
| Small  | `rounded-sm` (`--radius-sm`) | Badges, compact elements |
| Medium | `rounded-md` (`--radius-md`) | Inputs, buttons          |
| Large  | `rounded-lg` (`--radius-lg`) | Cards, panels            |
| Full   | `rounded-full`               | Avatars, pills           |

> **Never set border-radius in pixels (`rounded-[8px]`)** unless there's a very specific visual requirement.

---

## 5. Component Composition Rules

### Forms

1. All forms **must** use `react-hook-form` + `zod`. No manual state validation.
2. Form structure: `<Form>` → `<FormField>` → `<FormItem>` → `<FormLabel>` + `<FormControl>` + `<FormMessage>`.
3. Error messages flow automatically through `<FormMessage>` — never render error `<span>` manually.
4. Large entity forms use a 2-column grid: `grid grid-cols-1 md:grid-cols-2 gap-6`.
5. Long fields (description, textarea, rich selects) use `col-span-full`.

### Buttons & Actions

1. **Submit actions** in modals: left-aligned in `CardFooter`.
2. **Submit actions** on full pages: right-aligned at the bottom.
3. **Destructive actions** always require a confirmation dialog (`AlertDialog`) before execution.
4. **Icon-only buttons** use `size="icon"` on `<Button>`.
5. Never place two primary (`variant="default"`) buttons side by side.

### Tables & Data

1. Use `<Table>`, `<TableHeader>`, `<TableBody>`, `<TableRow>`, `<TableHead>`, `<TableCell>`.
2. Action menus in table rows use `<DropdownMenu>` with `<DropdownMenuTrigger asChild>`.
3. Status values are always represented as `<Badge>` variants — never plain text.
4. Filter inputs above tables use a relative container with an absolute icon: `relative` + `<Search className="absolute left-2.5 top-2.5" />` + `pl-8` on the input.

### Cards

1. Never create surfaces with raw `<div className="bg-white rounded-lg shadow border">`.
2. If you need a container without a heading, use `<Card><CardContent>...</CardContent></Card>` — omit `CardHeader`.
3. `CardFooter` must use `flex` with `justify-between` (for cancel/confirm pairs) or `justify-end` (for single confirm).

---

## 6. Page Structure & Headers

### PageHeader Component

All pages **must** use the `PageHeader` component for their primary titles and descriptions. Do not use raw `<div>` or `<h2>` tags for page-level headers.

1. **Title**: The `title` prop is mandatory and renders an H1-level heading.
2. **Subtitle**: Use the `subtitle` prop for secondary descriptions.
3. **Back Button**: If the page is a sub-view, use `backHref` (for routing) or `onBack` (for custom logic).
4. **Actions**: Use the `actions` prop to pass primary CTA buttons (e.g., "New Content", "Save").

```tsx
<PageHeader
  title="Users Management"
  subtitle="Manage your system users and permissions"
  actions={
    <Button>
      <Plus className="mr-2 h-4 w-4" /> Create User
    </Button>
  }
/>
```

### Sidebar & Navigation

The `Sidebar` is the primary navigation component. It is designed to be **autonomous** and persistent.

1. **Autonomous Mode**: Works without `LayoutProvider` using internal state, but ideally integrated via `LayoutContext`.
2. **Persistence**: The expansion state is saved in cookies (`sidebar_state`).
3. **Shortcuts**: Use `Ctrl+B` (or `Cmd+B`) to toggle expansion globally.
4. **Context Awareness**: Other components (Header, AudioPlayer) adjust their layout based on the sidebar's width and state via `LayoutContext`.

### Standard Page Composition

The typical page content area should follow this hierarchy:

1. `ScrollArea` (to ensure the layout stays fixed)
2. Outer padding (`p-2 sm:p-4 md:p-6`)
3. Max-width container (`max-w-6xl mx-auto`)
4. `PageHeader`
5. Grid or Content blocks

---

## 7. Interaction Patterns

### Loading States

Always render a skeleton placeholder — never a spinner — for data-bearing surfaces. Spinner-only loading is acceptable **only** for inline actions (button submit, "saving…" state).

**For block-level cards**, use the matching `*Skeleton` companion that ships with every card pattern:

```tsx
import { ActivityCard, ActivityCardSkeleton } from 'xertica-ui';

{
  isLoading ? <ActivityCardSkeleton rows={5} /> : <ActivityCard items={items} />;
}
```

Available skeleton companions: `ActivityCardSkeleton`, `ProfileCardSkeleton`, `ProjectCardSkeleton`, `NotificationCardSkeleton`, `QuickActionCardSkeleton`, `FeatureCardSkeleton`, `StatsCardSkeleton`.

**For grids**, render one skeleton per expected card so layout shift is minimized when data arrives:

```tsx
{
  isLoading ? (
    <>
      <FeatureCardSkeleton showAction />
      <FeatureCardSkeleton showAction />
      <FeatureCardSkeleton showAction />
    </>
  ) : (
    data.map(item => <FeatureCard key={item.id} {...item} />)
  );
}
```

**For tables**, render skeleton `<TableRow>`s using the `<Skeleton>` primitive (`xertica-ui/ui`) sized to match each cell:

```tsx
{isLoading
  ? Array.from({ length: 5 }).map((_, i) => (
      <TableRow key={i}>
        <TableCell><Skeleton className="h-3.5 w-28" /></TableCell>
        <TableCell><Skeleton className="h-3.5 w-36" /></TableCell>
        <TableCell><Skeleton className="h-5 w-16 rounded-full" /></TableCell>
      </TableRow>
    ))
  : rows.map(...)}
```

**Pass `rows={maxItems}` to list-style skeletons** (`ActivityCardSkeleton`, `NotificationCardSkeleton`) so the placeholder height matches the loaded state's row count.

### Empty States

Use `<Empty>` for zero-state views (empty tables, no results). Always include an icon, a message, and a call-to-action.

### Error States

Destructive actions use `text-destructive` and `bg-destructive`. Form errors appear via `FormMessage`. API errors use `toast.error(message)` via Sonner.

---

## 8. Internationalization (i18n)

All user-facing strings in library components are translated via **`react-i18next`**. When authoring or consuming a Xertica UI component:

### Always use translation hooks for UI strings

```tsx
import { useTranslation } from 'react-i18next';

function MyButton() {
  const { t } = useTranslation();
  return <Button aria-label={t('common.save')}>{t('common.save')}</Button>;
}
```

Never hardcode user-facing text — including `aria-label`, `placeholder`, `title`, toast messages, error text, and dropdown items.

### Factory functions, not frozen constants

When generating arrays of translated strings outside the React render cycle (mock data, fallback options), use **functions** so `i18n.t()` re-runs on every call:

```tsx
// ❌ Wrong — locked to whatever language was active at module load
export const FALLBACK_OPTIONS = [i18n.t('feedback.notWhatIWanted')];

// ✅ Correct — always returns the current language
export function getFallbackOptions() {
  return [i18n.t('feedback.notWhatIWanted')];
}
```

Inside components, do the same with `useMemo([..., t])`.

### Language-aware React Query keys

Hooks whose response contains translated strings **must** include the active language in their `queryKey`:

```tsx
import { useLanguage } from 'xertica-ui/hooks';

export function useFeatureCards() {
  const { language } = useLanguage();
  return useQuery({
    queryKey: ['home', 'feature-cards', language],
    queryFn: fetchFeatureCards,
  });
}
```

This ensures the cache flushes and refetches automatically when the user changes language — no page reload required.

### Runtime-configurable language set

The set of available languages is configured via `availableLanguages` on `<XerticaProvider>`. Read the current set from `useLanguage()`:

```tsx
const { language, setLanguage, availableLanguages, isMonolingual } = useLanguage();
```

- **Never hardcode the language list** in custom settings UIs — iterate over `availableLanguages` so it stays in sync with the consumer's `<XerticaProvider>` config.
- **`<LanguageSelector>` auto-hides** when `isMonolingual` is true. Never wrap it in a conditional yourself; pass `showWhenMonolingual` only when you explicitly need it visible.

---

## 9. Accessibility

All Xertica UI interactive components are built on Radix UI primitives, which provide:

- Full keyboard navigation
- ARIA roles and attributes
- Focus management
- Screen reader announcements

Do not override or disable these behaviors. If adding custom interactive elements, ensure:

- All interactive elements have visible focus indicators
- Buttons have accessible labels (`aria-label` if icon-only) — and that `aria-label` must be passed through `t()` for translation
- Form fields are associated with labels via `htmlFor` / `id`
