# Theming

Every component reads its colors, radii and type from tokens. The Salesforce themes fill them with SLDS 2's own values, extracted from Salesforce's stylesheet; Neutral fills them from shadcn's variables. Pick a theme with its theme step, `npx shadcn@latest add @wisty/<theme>` (see Installation), and dark mode with the `dark` class on `<html>`.

## The themes

| Theme          | Add it with             | What it is                                                        |
| -------------- | ----------------------- | ----------------------------------------------------------------- |
| Cosmos         | `@wisty/cosmos`         | Salesforce's current look, and the default                        |
| Lightning Blue | `@wisty/lightning-blue` | Salesforce's earlier Lightning look, SLDS 1. It has no dark mode. |
| Neutral        | `@wisty/neutral`        | shadcn's own look                                                 |

Cosmos and Neutral have a dark mode, under the `dark` class. A theme step writes one theme into your stylesheet's `:root` and `.dark`, so the whole app takes it. Edit the values in place to adjust them; the next theme step writes over them.

## More than one theme

To switch themes while the app runs, or to show one theme inside another, import the theme files by hand (see Installation) and pick a theme with an attribute:

| Theme          | Set it with                                 |
| -------------- | ------------------------------------------- |
| Cosmos         | `data-theme="cosmos"`, or nothing           |
| Cosmos dark    | `data-theme="cosmos"` and the `dark` class  |
| Lightning Blue | `data-theme="lightning-blue"`               |
| Neutral        | `data-theme="neutral"`                      |
| Neutral dark   | `data-theme="neutral"` and the `dark` class |

Import `tokens.css`, then each theme file you use, Neutral's last. Put the attribute and the `dark` class on the same element, usually `<html>`. Popovers, selects and dialogs render into `document.body`, so a theme on a smaller element doesn't reach them.

```html
<html data-theme="cosmos" class="dark">
	<body>
		…
	</body>
</html>
```

## Neutral

Neutral is for apps that use the components and blocks without Salesforce's look. It starts from shadcn's neutral theme and reads shadcn's variables: `--primary`, `--background`, `--border`, `--ring`, `--radius` and the rest. After `npx shadcn@latest add @wisty/neutral`, they're in your stylesheet's `:root` and `.dark` with shadcn's values. Change them as you would in any shadcn project. Components and blocks follow, and so do tokens shadcn has no variable for, like the selected item's fill and the link color.

```css
:root {
	--primary: oklch(0.55 0.2 260);
	--radius: 0.5rem;
}
.dark {
	--primary: oklch(0.7 0.15 260);
}
```

With the files imported by hand, Neutral needs `data-theme="neutral"` on `<html>`. Its light-mode values have zero specificity and its dark-mode values have `.dark`'s, so your own `:root` and `.dark` rules after the import win.

Success, warning and info colors, which shadcn has no variable for, come from Tailwind's palette. Display density works in Neutral. Salesforce's brand hooks don't apply there, so set `--primary` instead.

## Your own overrides

Override the tokens, never the class strings: components read `var(--primary)`, `var(--border)` and the rest, the names shadcn uses where SLDS has the same idea. After a theme step, the theme's values are your `:root` and `.dark` values: edit them there, and run the step again to get the theme's own back. With the files imported by hand, the default theme sits at zero specificity, so your own `:root` values win without `!important`.

Fonts work the way they do in shadcn, in every theme. Set `--font-sans` and `--font-mono` on `:root`. The page's text, the `font-sans` and `font-mono` utilities and every component use them. Without them, each theme keeps its own stack: Salesforce's system font in Cosmos and Lightning Blue, Tailwind's in Neutral. `font-serif` is Tailwind's own utility and reads your `--font-serif` the same way.

```css
:root {
	--font-sans: "Geist", system-ui, sans-serif;
	--font-mono: "Geist Mono", ui-monospace, monospace;
}
```

## Your org's brand

In the Salesforce themes, an admin can give a Salesforce org its own theme: a brand color, optional accent colors and an optional text color. Lightning puts them on the page as Salesforce's own styling hooks. The components read the same hooks and use the theme's colors until you set them.

Set them on `<html>`. With the theme files, any element above the one with `data-theme` works too. Hooks set lower down are ignored.

```css
:root {
	/* The brand ramp Lightning generates from the org's brand color */
	--slds-r-color-brand-50: #c935a4;
	--slds-r-color-brand-40: #a02982;
	--slds-r-color-brand-15: #430c35;
	/* …and the other steps, 5 to 95 */

	/* Optional, when the org's theme overrides them */
	--slds-g-color-accent-container-1: light-dark(#0b7a3e, #c935a4);
	--slds-g-color-on-surface-3: light-dark(#430c35, #faddf0);
}
```

| Hooks                                                                                  | What they color                                  |
| -------------------------------------------------------------------------------------- | ------------------------------------------------ |
| `--slds-r-color-brand-5` to `--slds-r-color-brand-95`                                  | The brand ramp: buttons, links, focus, selection |
| `--slds-g-color-accent-1` to `-3`, `accent-container-1` to `-3`, `on-accent-1` to `-3` | The org's accent overrides, and text on them     |
| `--slds-g-color-border-accent-1`                                                       | Borders in the accent color                      |
| `--slds-g-color-on-surface-3`                                                          | Default text and headings                        |

To copy an org's values, open any Lightning page in that org and read these properties on `<html>` in the browser's developer tools.

An app that runs inside Salesforce in its own frame, such as a Multi-Framework app or a Canvas app, doesn't receive the org's hooks or its light or dark mode. Salesforce doesn't pass them into the frame, so the component that embeds the app has to forward them. Matching a Salesforce org lists what to read and set.

## Display density

Each Salesforce user picks a display density: comfy, the default, or compact. Compact puts field labels beside their fields and tightens spacing. Set `data-density="compact"` on `<html>`, the element that holds the theme:

```html
<html data-density="compact"></html>
```

| What changes in compact            | How                                                                                  |
| ---------------------------------- | ------------------------------------------------------------------------------------ |
| Cards, page headers                | Less padding                                                                         |
| Card, section and accordion titles | One step smaller                                                                     |
| Body text                          | Line height `normal` instead of 1.5                                                  |
| Record detail, inline edit, form   | Labels beside values and controls, from a 48rem screen; above them on a narrower one |

Buttons, inputs, menus and tables keep their size, as in Lightning. The values are Salesforce's own: the themes keep SLDS's density hooks (`--slds-g-spacing-var-*`, `--slds-g-font-scale-var-*`, `--slds-g-font-lineheight-var-base`), so a page that sets them directly gets the same result.

A field with `layout="auto"`, the record blocks' default, follows the nearest `data-density`. Put `data-density="comfy"` on a region to keep its fields stacked on a compact page, or give one block `layout="stacked"` or `layout="horizontal"`.

## Matching a Salesforce org

What the embedding component reads from the Lightning page, and what the app sets:

| The org's     | Read on the Lightning page                                                                       | Set in the app                                                                                             |
| ------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| Design system | SLDS 2 when `--slds-g-color-surface-1` on `<html>` holds a `light-dark()` pair; otherwise SLDS 1 | `data-theme="cosmos"` for SLDS 2, `data-theme="lightning-blue"` for SLDS 1, with both theme files imported |
| Brand         | The hooks in the table under Your org's brand                                                    | The same hooks on `<html>`                                                                                 |
| Light or dark | `slds-color-scheme_dark` on `<body>`                                                             | The `dark` class                                                                                           |
| Density       | Compact when `--slds-g-spacing-var-1` differs from `--slds-g-spacing-1`                          | `data-density="compact"`                                                                                   |

Lightning Blue and Salesforce's other built-in themes are SLDS 1. Cosmos and custom themes are SLDS 2. An SLDS 1 theme's color arrives in the accent hooks, so forwarding every hook under Your org's brand carries it too.

## Icons

Salesforce's icons come from the `@salesforce-ux/icons` package, unaltered. Copy its `dist/salesforce-lightning-design-system-icons` folder to `public/assets/icons`, or point an `IconProvider` at wherever you serve them.
