# Give each tenant its own theme

> Apply a brand colour from data to one part of a page, with text colours that stay readable.

Source: https://platform.azmara.io/docs/recipes/theme-per-tenant

Compute a theme from the tenant's settings and put its variables on the element that should be themed. Everything inside follows, and nothing outside changes.

```tsx
"use client";

import { Button } from "@azmr/ui/button";
import { Badge, Card, CardContent, CardHeader, CardTitle } from "@azmr/ui/display";
import { createTheme } from "@azmr/ui/theme";
import { ToggleGroup, ToggleGroupItem } from "@azmr/ui/toggle";
import { useState } from "react";

// Each tenant is one brand colour. createTheme turns it into every variable the components read,
// including text colours that stay readable on it, so a tenant cannot pick an unreadable brand.
const TENANTS = {
  kauri: { label: "Kauri Timber", primary: "#2E7D32" },
  harbour: { label: "Harbour Freight", primary: "#1E88E5" },
  sunrise: { label: "Sunrise Bakery", primary: "#FFEB3B" },
} as const;

type TenantId = keyof typeof TENANTS;

export default function TenantTheme() {
  const [tenant, setTenant] = useState<TenantId>("kauri");
  // Compute the theme from the tenant's setting, then put its variables on the element that
  // should be themed. Everything inside follows, and nothing outside changes.
  const theme = createTheme({ primary: TENANTS[tenant].primary });

  return (
    <div className="flex max-w-md flex-col gap-4">
      <ToggleGroup
        aria-label="Tenant"
        variant="outline"
        value={[tenant]}
        onValueChange={(value) => {
          const chosen = (value as string[])[0] as TenantId | undefined;
          if (chosen) setTenant(chosen);
        }}
      >
        {(Object.keys(TENANTS) as TenantId[]).map((id) => (
          <ToggleGroupItem key={id} value={id}>
            {TENANTS[id].label}
          </ToggleGroupItem>
        ))}
      </ToggleGroup>

      <div style={theme.cssVariables}>
        <Card>
          <CardHeader>
            <CardTitle>{TENANTS[tenant].label}</CardTitle>
          </CardHeader>
          <CardContent className="flex items-center gap-3">
            <Button>Place order</Button>
            <Badge>New</Badge>
          </CardContent>
        </Card>
      </div>
    </div>
  );
}
```

## The pattern

```ts
import { createTheme } from "@azmr/ui/theme";

const kauri = createTheme({ primary: "#2E7D32" }).cssVariables["--primary"];
const harbour = createTheme({ primary: "#1E88E5" }).cssVariables["--primary"];
const differs = kauri !== harbour;
// => true
```

Then apply the variables:

```tsx
<div style={createTheme({ primary: tenant.primary }).cssVariables}>…</div>
```

- `createTheme` does not return your colour as given. It adjusts it until the text on it, and the focus ring, meet their contrast targets in light and dark mode, so a tenant who picks a hard-to-read brand colour still gets a readable interface. The demo's third tenant is a bright yellow and gets dark text on its buttons. See [what it guarantees](https://platform.azmara.io/docs/guide/theming.md#what-it-guarantees).
- It throws an error that names the option when a value is not a valid colour, so validate what a tenant types before it reaches the page, and show them the message.
- Put the variables on the element, not only on `<html>`: a scope that sets its own `data-theme` re-declares every colour, so variables set outside it do not reach inside. See [Apply the theme](https://platform.azmara.io/docs/guide/theming.md#apply-the-theme).
- For the whole app, apply the theme to `<html>`, and do it on the server so the first paint is already right.
