# Themes

> Change how the hosted pages look: theme options, languages and texts in the console, or your own Handlebars templates.

Source: https://9in.io/docs/themes/

The hosted pages (sign-in, sign-up, codes, two-factor, consent, account, device and errors) use a **theme**. The built-in **9in Default** theme is designed to be branded through options; for full control, write your own theme in Handlebars.

## Edit on the preview

**Themes** in the console shows a live preview of every page, on desktop, tablet and phone, in each of your languages. With **Edit on page** on:

- **Click anything** (the logo, a button, the card, the background, the side panel) to see its options in the inspector on the right: accent color, corners, logo images and alignment, card border, side panel and more. The path above them (Page › Layout › Card › Button) selects the elements around it.
- **Click any text** to change it in place, in the language you're previewing.
- **Dashed "+" chips** mark things that are switched off, such as "+ Show a side panel"; click one to turn it on. Things hidden at the current screen size are listed under **Hidden here**.
- **Show an error** displays a typical error message for the page, so you can see and reword it.

Changes are previewed instantly and go live when you save. The **Design** tab also lists every option, and **Themes** switches between the built-in theme and your own.

## Logos and images

Upload images under **Branding**. The logo and icon there are the defaults everywhere (pages, emails, favicon); a theme can use other images from the same library for dark pages and the side panel. Uploaded files get private names made from their content, so their public URLs say nothing about the original file.

## Texts and languages

Every text on the pages and in the emails exists in English, Arabic, Chinese, French, German, Hebrew, Japanese, Persian, Russian, Spanish and Turkish. Choose your languages under **Themes → Languages** (and the default, used when the browser asks for none of them). Pages follow the visitor's browser, or the `ui_locales` parameter of the authorization request, and right-to-left languages are laid out right to left.

Change texts by clicking them in a preview, or in the full list under **Translations**, which also shows texts that aren't translated yet. Texts you leave alone keep getting 9in's updates.

## Per-application themes

An application can use a different theme from the rest of the tenant (**Applications → app → Settings → Theme for this app**), for example a product with its own brand.

## Your own theme

**Themes → Import theme**, or **Edit code** to start from the default theme in the browser editor with a live preview. A theme is a set of Handlebars templates:

- `layout.hbs`: the page shell, with named blocks (`{{#component brand}}…{{/component}}`) placed with `{{#render brand}}{{/render}}`;
- one partial per page: `login`, `registration`, `otp-options`, `otp-verification`, `password-recovery`, `password-reset`, `authorization`, `account`, `logout`, `error`, `lost`, `redirect`, `device`, `mfa`, `mfa-enroll`;
- `manifest.json`: the options your theme offers in the console, and the outside origins it may load.

Pages your theme doesn't include come from the default theme. Templates see translations as `{{ $.login.title }}`, your options as `{{ theme.button_color }}`, and the page's data (`application`, `user`, `errors`, `csrf`…). Scripts need `nonce="{{ csp_nonce }}"`. Uploaded CSS, scripts and fonts are referenced as `{{ assets_url }}<file name>`.

Themes from SSOfy import as they are. Every variable, helper, page and manifest field is in the [theme reference](https://9in.io/docs/reference/themes/).

### Make your theme editable on the preview

9in works out what each option affects from your templates: anything you output from `{{ theme.x }}`, or show inside `{{#if theme.x}}`, becomes clickable, and when an option hides something, a "+" chip offers to turn it back on. Optional hints make it more precise:

```json
{
  "version": 1,
  "options": {
    "Look": {
      "properties": {
        "button_color": {
          "title": "Accent color",
          "type": "color",
          "default": "#1a1a1a",
          "target": ".btn-primary",
          "css_var": "--primary",
          "preview": { "page": "login", "device": "desktop" }
        }
      }
    }
  }
}
```

- `target`: a CSS selector for the elements an option styles, for options used only in CSS.
- `css_var` (and `unit`, such as `px`): the custom property the option sets, so the preview updates instantly instead of re-rendering. The page also gets a `9in:options` event on `document`, so scripts that derive colors (readable text on the accent, for example) can recompute.
- `preview`: the page and device where the option shows, so the console takes the preview there.

In a template, `{{option-target "logo logo_position" name="Logo"}}` inside a tag marks an element by hand and names it in the console. On real pages it writes nothing.
