> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Brand Customization Guide

> Brand Customization allows each organization to apply custom colors, typography, login assets, and favicons to the platform. Changes are scoped to your organiz…

## Overview

Brand Customization allows each organization to apply custom colors, typography, login assets, and favicons to the platform. Changes are scoped to your organization only and do not affect other tenants.

***

## Permissions Required

| Permission            | Role             | Description                                  |
| --------------------- | ---------------- | -------------------------------------------- |
| `pf.theme.view`       | Staff, Org Admin | View current theme settings                  |
| `pf.theme.manage`     | Org Admin        | Create, edit, import/export themes           |
| `pf.subdomain.manage` | Org Admin        | Configure subdomain and redirect preferences |

***

## Accessing Brand Settings

Navigate to **Settings → Branding & Theme**. The settings page has four tabs:

1. **Colors** — Primary, secondary, accent, background, surface, text, and status colors
2. **Typography** — Body and heading font selection
3. **Login & Assets** — Login logo, background image, welcome text, favicon
4. **Subdomain** — Custom subdomain setup and redirect configuration

***

## Colors

### Editing Colors

Each color field accepts hex (`#2563eb`), RGB (`rgb(37, 99, 235)`), or HSL (`hsl(217, 91%, 53%)`) formats.

### WCAG Contrast Gates

The platform enforces **WCAG 2.1 AA** contrast requirements before allowing saves:

| Color Pair               | Minimum Ratio                    |
| ------------------------ | -------------------------------- |
| Text on Background       | 4.5:1                            |
| Text Muted on Background | 4.5:1                            |
| Text on Surface          | 4.5:1                            |
| Primary on Background    | 3:1 (large text / UI components) |

If contrast is insufficient, the **Save** button is disabled and specific failing pairs are shown.

### Reset to Defaults

Use the **Reset to Defaults** button to restore platform default colors. A confirmation dialog appears before resetting.

***

## Typography

### Approved Font List

Only the following fonts are permitted (loaded via Google Fonts):

* `system-ui` (system default)
* `Inter`
* `Source Sans 3`
* `Open Sans`
* `Lato`
* `Roboto`

Select separate fonts for body text and headings. Fonts load with `font-display: swap` to prevent render blocking.

***

## Login & Assets

### Login Logo

Upload your organization's logo for the login page. Recommended: SVG or PNG with transparent background, max 200×80px display area.

### Login Background

Optional background image for the login page. Recommended: 1920×1080px minimum, JPEG or WebP for smaller file sizes.

### Welcome Text

Custom welcome message displayed on the login page (e.g., "Welcome to Acme Health").

### Favicon

Upload a custom favicon (`.ico`, `.png`, or `.svg`). Recommended: 32×32px or 180×180px for Apple Touch Icon.

***

## Import / Export

### Export

Click **Export Theme** to download a JSON file containing your current theme configuration. The export uses `schemaVersion: 1` and omits internal IDs and audit fields.

```json theme={null}
{
  "schemaVersion": 1,
  "theme": {
    "name": "Acme Brand",
    "colorPrimary": "#2563eb",
    "colorSecondary": "#64748b",
    "fontFamily": "Inter",
    ...
  }
}
```

### Import

Click **Import Theme** to load a previously exported JSON file. The import dialog (`sm:max-w-lg`) shows:

1. A preview of the theme to be applied
2. Warning for any unknown keys in the file
3. Confirmation before replacing the active theme

**Note:** Import replaces the current active theme. The previous theme is retained as an inactive record.

***

## Subdomain Configuration

### Setting Up a Subdomain

1. Enter your desired subdomain label (e.g., `acme` → `acme.yourdomain.com`)
2. The label must:
   * Be 3–63 characters
   * Start and end with a letter or number
   * Contain only lowercase letters, numbers, and hyphens
   * Not be a reserved label

### Reserved Labels

The following subdomain labels are not available:

`www`, `app`, `api`, `admin`, `mail`, `smtp`, `ftp`, `login`, `auth`, `sso`, `status`, `help`, `docs`, `cdn`, `assets`, `static`, `staging`, `dev`, `test`, `demo`, `beta`, `sandbox`, `internal`

### Subdomain Verification

After entering a subdomain, a platform administrator must verify it by:

1. Configuring the subdomain in your hosting provider (Vercel)
2. Ensuring SSL/TLS is provisioned for the subdomain
3. Marking the subdomain as **Verified** in platform settings

**Important:** Redirects are only enabled after verification to prevent redirect loops to hosts without valid TLS.

### Redirect Toggle

Once verified, enable **Prefer Subdomain Redirect** to activate canonical redirects. When enabled:

| Scenario                                       | Behavior                                                   |
| ---------------------------------------------- | ---------------------------------------------------------- |
| R1: User visits primary host `/o/acme/…`       | **308 redirect** to `https://acme.yourdomain.com/o/acme/…` |
| R2: User visits `acme.yourdomain.com/o/acme/…` | No redirect (already correct)                              |
| R3: User visits primary host without org path  | No redirect                                                |
| R4: User visits unknown subdomain              | No redirect (platform default)                             |
| R5: User visits wrong subdomain for org        | No redirect (deferred)                                     |

The redirect preserves the full path and query string. Status code is **308** (Permanent Redirect, preserves method).

***

## Security Notes

* **No anonymous table access:** The `pf_tenant_themes` table is never directly readable by unauthenticated users. Pre-authentication branding is served exclusively through `pf_public_login_branding_by_subdomain` and `pf_public_login_branding_by_slug` RPCs, which return a fixed projection of login-safe fields only.
* **Tenant isolation:** All theme data is scoped by `organization_id` with Row-Level Security enforced. Users cannot read or modify themes belonging to other organizations.
* **Single active theme:** Only one theme per organization can be active at a time, enforced by a database trigger and partial unique index.
* **Input validation:** Color values are validated against safe formats (hex, RGB, HSL). No arbitrary CSS or HTML injection is permitted.
* **Asset uploads:** Logo, background, and favicon uploads use existing platform storage patterns with approved file type validation.

***

## Troubleshooting

| Issue                | Resolution                                                                |
| -------------------- | ------------------------------------------------------------------------- |
| Save button disabled | Check WCAG contrast errors displayed below the color pickers              |
| Subdomain rejected   | Ensure label meets length (3–63), format, and reserved label requirements |
| Redirect not working | Verify subdomain is marked as Verified and redirect toggle is enabled     |
| Theme not applying   | Ensure the theme is marked as Active; check browser cache                 |
| Import fails         | Verify JSON file has `schemaVersion: 1` and valid color formats           |

***

## Related Documentation

* [PF-95 Spec](https://github.com/Encore-OS/encoreos/blob/development/specs/pf/specs/PF-95-tenant-white-labeling-theming.md)
* [PF-95 Integration](/architecture/integrations/tenant-white-labeling-theming-integration)
