# Theming

> Restyle slotsmith with shared --ss-* tokens or a ready-made theme, add a dark palette, use Tailwind, write your own styles, and lay it out right to left.

Theming slotsmith means setting CSS custom properties. The stylesheet reads eleven shared `--ss-*` tokens, so a few values restyle all four components at once; a ready-made theme sets the same tokens for you. Without the stylesheet, you style the parts yourself, with plain CSS, Tailwind or a design system.

## Tokens

Set the tokens on `:root`, in a stylesheet loaded after slotsmith's:

```css
/* After slotsmith's stylesheet: one --ss-* value restyles all four components. */
:root {
  --ss-accent: #7c3aed;
  --ss-radius: 12px;
}

/* A value on :root applies to both palettes, so give the dark one its own. */
.dark,
[data-theme="dark"] {
  --ss-accent: #a78bfa;
}
```

That restyles the whole page. Set them on any other element instead, such as a wrapper around one section, to restyle only the components inside it.

| Token | What it colours or sizes |
| --- | --- |
| `--ss-surface` | The background of every component and popup |
| `--ss-text` | Text |
| `--ss-muted` | Secondary text: placeholders, captions, counters |
| `--ss-border` | Borders and dividers, and the autocomplete list's thin scrollbar |
| `--ss-accent` | Focus rings, checkboxes, the picked day, tags, upload progress and the drop zone while a file is dragged over it |
| `--ss-on-accent` | Text on the accent. Only the date picker reads it, for the picked day |
| `--ss-danger` | Errors: the error rows, a rejected file, and the border and focus ring of an `invalid` autocomplete or date picker |
| `--ss-hover` | The background of a hovered row, option, day or button |
| `--ss-selected` | The background of a selected row. Only the data table reads it |
| `--ss-radius` | Corner radius |
| `--ss-font-size` | Base font size |

Each component also has its own tokens, which fall back to the shared ones: `--sdt-*` for the [data table](https://slotsmith.dev/ar/components/data-table/api/#styling), `--sac-*` for the [autocomplete](https://slotsmith.dev/ar/components/autocomplete/api/#styling), `--sdp-*` for the [date picker](https://slotsmith.dev/ar/components/date-picker/api/#styling) and `--sfu-*` for the [file uploader](https://slotsmith.dev/ar/components/file-uploader/api/#styling). Set one of those to restyle one component without the others; it beats the shared token wherever each is set, so a `--sdt-accent` on `:root` beats a `--ss-accent` on a closer wrapper. The date picker and the uploader also follow a theme written against the data table's `--sdt-*` when no `--ss-*` value is set for the same thing. The danger colour has a component token in each too, such as `--sdp-danger` for the date picker's `invalid` state. Each component's API page lists its tokens.

The colour, radius and font-size component tokens are inputs only: the stylesheet does not declare them, so `var(--sdt-accent)` in your own CSS is empty unless you set it. Read `var(--ss-accent, <default>)` instead. The few tokens with no shared counterpart do have defaults on `:root`: the table's `--sdt-stripe`, `--sdt-skeleton-bg`, `--sdt-skeleton-bg-2` and `--sdt-max-height`, and the sizes `--sdp-cell`, `--sdp-gap`, `--sfu-gap` and `--sfu-tile-size`. Set a component token on the component or an element above it; one set on an inner part, such as `.sdt__row`, is not read. A popup rendered outside the wrapper, such as one portalled to `document.body`, does not see the wrapper's `--ss-*` and takes the page's values instead.

## Themes

Six themes ship with the library: `minimal`, `soft`, `ocean`, `forest`, `sunset` and `contrast`, a high-contrast palette. Each sets only the shared tokens, with a light and a dark palette. Pick one to try it here; the rest of this page keeps its look:

Import a theme after the stylesheet:

```ts
// After the stylesheet, so the theme's tokens win over the defaults.
import "slotsmith/styles.css";
import "slotsmith/themes/soft.css";
```

A theme sets its tokens on the same selectors as your own overrides, so the import order decides: import your overrides after the theme to keep them. To write your own theme, set only the shared tokens, on `:root` and again for dark mode, as in [Tokens](https://slotsmith.dev/ar/theming/#tokens).

## Dark mode

The stylesheet and every theme switch to their dark palette under `.dark` or `[data-theme="dark"]`, set on `<html>` or on any element above the components. Most dark-mode setups already set one of the two, so the components follow with no further code. A token you set on `:root` applies to both palettes, so give its dark value under the same two selectors.

An app that follows the system setting, with no class to toggle, sets the dark values itself in a media query:

```css
/* The stylesheet and every theme switch to dark under .dark or [data-theme="dark"],
   on <html> or on any element above the components. Nothing else is needed.

   An app that follows the system setting instead, with no class to toggle,
   sets the dark values itself. These are the stylesheet's own: */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ss-surface: #141416;
    --ss-text: #f5f5f5;
    --ss-muted: #a3a3a3;
    --ss-border: #2a2a2a;
    --ss-accent: #60a5fa;
    --ss-on-accent: #0b0b0c;
    --ss-danger: #f87171;
    --ss-hover: rgb(255 255 255 / 6%);
    --ss-selected: rgb(96 165 250 / 12%);
  }
}
```

The table's stripes and loading skeletons have no shared token and keep their light colours in that setup; set `--sdt-stripe`, `--sdt-skeleton-bg` and `--sdt-skeleton-bg-2` too if you use them.

## Tailwind

Tailwind is not needed: the stylesheet is plain CSS. With Tailwind there are two ways.

- **Keep the stylesheet** and point the tokens at your theme: Tailwind 4 exposes its colours as CSS variables, so `--ss-accent: var(--color-violet-600)` works on `:root`.
- **Drop the stylesheet** and give the parts Tailwind classes. Each element part is a plain element you can replace, and its state is a `data-*` attribute Tailwind can select:

```tsx
import {
  DataTable,
  type CellSlotProps,
  type DataTableColumnDef,
  type HeaderCellSlotProps,
  type RowSlotProps,
  type TableSlotProps,
} from "slotsmith/data-table";

type Book = { id: string; title: string; year: number };

const columns: DataTableColumnDef<Book>[] = [
  { accessorKey: "title", header: "Title" },
  { accessorKey: "year", header: "Year" },
];

// No slotsmith stylesheet: each element part is your own element with Tailwind
// classes. A selected row carries data-state="selected".
const Table = (props: TableSlotProps) => <table {...props} className="w-full text-sm" />;
const Row = (props: RowSlotProps) => <tr {...props} className="border-b border-zinc-200 data-[state=selected]:bg-violet-50" />;
const HeaderCell = (props: HeaderCellSlotProps) => <th {...props} className="px-3 py-2 text-start font-medium text-zinc-500" />;
const Cell = (props: CellSlotProps) => <td {...props} className="px-3 py-2" />;

export function Books({ books }: { books: Book[] }) {
  return <DataTable data={books} columns={columns} components={{ Table, Row, HeaderCell, Cell }} />;
}
```

The shadcn/ui adapters do this for every part; see the data table's [adapters](https://slotsmith.dev/ar/components/data-table/adapters/).

## Own styles

Without any slotsmith stylesheet, style the built-in parts by their class names. Every component names them the same way: `s` plus its initials (`sdt`, `sac`, `sdp`, `sfu`), then `__part`, then `--modifier`, with state in `data-*` attributes. A part with the same role has the same name in each component: the popups are `__popup`, an empty state is `__message`, an error state `__error`.

```css
/* No slotsmith stylesheet: style the built-in parts by their class names.
   Classes are s + the component's initials, __part, --modifier; state is in data-*. */
.sdt__table {
  width: 100%;
  border-collapse: collapse;
}

.sdt__cell {
  padding: 0.5rem 0.75rem;
  border-block-end: 1px solid #e4e4e7;
  text-align: start;
}

.sdt__row[data-state="selected"] {
  background: #f5f3ff;
}

/* A part with the same role has the same name in every component. */
.sac__popup,
.sdp__popup {
  border: 1px solid #e4e4e7;
  background: #ffffff;
}
```

Each component's API page lists its classes under Styling.

## RTL

The stylesheets use logical properties only, such as `margin-inline-start` and `inset-inline-end`, so every component mirrors when its page or a wrapper is `dir="rtl"`. The components follow the CSS direction around them; the locale does not set it. Inside `SlotsmithProvider`, `useSlotsmithLocale()` returns the active locale's tag and direction:

```tsx
import type { ReactNode } from "react";
import { useSlotsmithLocale } from "slotsmith/provider";

// The components follow the CSS direction of the page around them. Inside the
// provider, take it from the active locale: ar, fa and he are right to left.
export function Direction({ children }: { children: ReactNode }) {
  const { code, dir } = useSlotsmithLocale();
  return (
    <div lang={code} dir={dir}>
      {children}
    </div>
  );
}
```

On a right-to-left page the date picker also points its previous and next month arrows the other way. [Languages](https://slotsmith.dev/ar/languages/) lists the right-to-left packs.
