Theme Stack

Prerequisite: UI Toolkit Basics. This page assumes you know UXML, USS, TSS, VisualElement, Panel Settings.

When you imported the Essentials sample, you got the Theme folder. These are the assets Unity UI Toolkit reads to render Luna's components correctly.

Samples/LunaUI/<version>/Essentials/Theme/ ├── LunaPanelSettings.asset ← Panel Settings (screen-space) ├── LunaPanelSettingsWorldSpace.asset ← Panel Settings (world-space) ├── LunaUIDemoTheme.tss ← project Theme StyleSheet ├── UITextSettings.asset ← font fallback chain ├── Colors/ ← color tokens (USS variables) ├── Fonts/ ← font SDF assets ├── Font.uss ← font-family selection ├── Images/ ← Cursor/Icons/Patterns + USS ├── LocalizedStyles.uss ← per-locale font/style switches ├── ScrollSpeed.uss ← scroll tuning ├── UIEffect_Flair.uss ← UI Effect override └── Unity/ ← Dropdown variant USS

Panel Settings (the asset)

Every PanelRenderer in your scene references a Panel Settings asset. Panel Settings tells Unity:

  • which Theme StyleSheet (.tss) to load (Luna's Theme + your overrides arrive through this)
  • scale mode — Constant Pixel Size / Constant Physical Size / Scale With Screen Size. Luna's shipped LunaPanelSettings.asset uses Constant Pixel Size (Scale=1). Scale With Screen Size can soften custom shader effects at non-integer scales; if you switch to it and effects look blurry, prefer Constant Pixel Size with an integer Scale (e.g. 2).
  • render mode / target textureLunaPanelSettingsWorldSpace.asset uses Render Mode = World Space (Unity 6's native in-world UI; no render texture involved). Set target texture only for the render-to-texture technique (UI drawn onto a quad/screen mesh).
  • text settings — font fallback chain. UITextSettings.asset above is wired here; extend it (don't replace it) when adding fonts for new languages or emoji glyphs.

Essentials ships two Panel Settings: LunaPanelSettings.asset for screen-space UI, and LunaPanelSettingsWorldSpace.asset for in-world 3D UI. Wire your PanelRenderer's "Panel Settings" field to one of these and the full Luna theme applies automatically.

Under Luna's navigation system, view prefabs must not carry their own Panel Settings — the NavConfigSO assigned to the layer's host is the single PanelSettings owner, and the host applies it to every spawned view's PanelRenderer. Assign the field directly only on standalone (non-nav) PanelRenderers. See Navigation.

Theme StyleSheet (.tss) and @import inheritance

A .tss is just a USS file with one extra role: it's the root stylesheet that Panel Settings loads. Use TSS at the entry point; use plain USS for files imported into it.

Luna's theme is layered through @import:

unity-theme://default ← Unity built-in ↑ @import CupkekGamesMainTheme.tss ← in the Luna package ↑ @import url("...CupkekGamesMainTheme.tss") LunaUIDemoTheme.tss ← in Essentials (override here)
  • CupkekGamesMainTheme.tss (in Packages/com.cupkekgames.luna/Runtime/Setup/) imports every Luna USS: Unity-control restyles (UButton, UDropdown, UListView, USlider, UToggle, URadio, UTextField), custom components (ProgressBar, InputPrompt, RenderElement), the Library utility classes (LibBorder, LibLayout, LibSize, LibColors*, LibFont*, LibMargin, LibPadding, LibPosition, LibTooltip), and UI Effects. Don't edit this — package updates would overwrite your changes.
  • LunaUIDemoTheme.tss (in Essentials/Theme/) @imports the main theme then adds: color variables, component-color palettes, a font selection, scroll tuning, cursor/icons/patterns image atlases, and a Dropdown variant. This is where you override.

This is USS style inheritance: the demo TSS doesn't fork Luna's stylesheet, it imports and overrides selectively. When Luna updates CupkekGamesMainTheme.tss, you pick up the improvements; your local overrides survive.

Where to override what

Theme/… paths below mean your copied theme folder — make that copy once via Make the theme yours just below.

You want to change…Edit…
A specific colorTheme/Colors/color-variables.uss
A component's color paletteTheme/Colors/component-colors.uss
Default font familyTheme/Font.uss (point at fonts in Theme/Fonts/)
Scroll wheel speedTheme/ScrollSpeed.uss
A Luna component's structural USSDon't. Write a more-specific rule in your own .uss, import it into your TSS, override there. Editing the package directly loses on upgrade.

Make the theme yours

Sample folders are overwritten when you re-import the sample, so don't customize Assets/Samples/… in place. Copy the theme out once, early — the copy is the canonical baseline every customization on this page assumes:

  1. Copy the folder. Duplicate Assets/Samples/LunaUI/<version>/Essentials/Theme/ to Assets/UI/Theme/ (any path outside Samples works).
  2. Check the @import paths in the copied LunaUIDemoTheme.tss. The first import is an absolute package path (/Packages/com.cupkekgames.luna/Runtime/Setup/CupkekGamesMainTheme.tss) and stays valid; the rest are relative to the .tss, so they keep resolving as long as you copied the folder intact. One known exception: LocalizedStyles.uss's .locale-ja block points its font url()s outside the Theme folder (../../Fonts/…), which doesn't resolve in the sample or in your copy — repoint those at a font you ship (e.g. under Theme/Fonts/) or remove the block.
  3. Re-wire the copied Panel Settings. The Theme folder includes both Panel Settings assets, so the copy brought them along. Select each copy and confirm its Theme Style Sheet field points at your copied .tss — if it still references the sample's, repoint it.
  4. Repoint consumers. Standalone PanelRenderers: set their Panel Settings field to your copy. Nav layers: update the NavConfigSO assets instead (the host applies their PanelSettings to every spawned view — see the note above).
  5. Rename the copied .tss and Panel Settings to taste (e.g. MyProjectTheme.tss); references survive renames.

You wired the sample's LunaPanelSettings.asset in First View, step 1 — swap your copy in there too. From here on, "the theme" means your copy.

Recolor every button — the worked example

The most common first customization, end to end. The default button palette lives in the package: Runtime/USS/Unity/UButton.uss opens with a :root block that routes --btn-bg, --btn-bg-hover, and friends onto the slate ramp — that's why a bare class="btn" button renders grey. Re-route those variables in your own USS instead of editing the package file:

css
/* Assets/UI/Theme/MyOverrides.uss */ .btn { --btn-bg: var(--color-azure-400); --btn-bg-hover: var(--color-azure-300); --btn-bg-disabled: var(--color-azure-200); --btn-border-disabled: var(--color-azure-200); --btn-color-disabled: var(--color-azure-300); --btn-border-focus: var(--color-azure-200); }

Then add it as the last @import in your TSS (next to the theme's other imports):

css
@import url("MyOverrides.uss");

Every default button in the project is now azure. Three things worth knowing:

  • Buttons that carry an explicit color class (class="btn red") keep their color — the theme's .btn.red { … } blocks in Theme/Colors/component-colors.uss are more specific than your single-class .btn rule. That file is also your reference: it contains one such block per shipped ramp, so copy the nearest one when you want a full override.
  • The six variables above cover the solid style. The ghost / outline / soft / link / masked button styles read their own --btn-* families (--btn-ghost-color, --btn-outline-border, …) — copy a complete .btn.{color} block out of component-colors.uss into your override and edit from there if you use them.
  • azure is one of the shipped --color-{name}-{tone} ramps (Colors lists them all); substitute any other ramp, or tokens from your own Palette Generator export.

The same pattern recolors any Luna control — each one consumes its own variable family (--tab-*, --luna-fx-*, …) routed by component-colors.uss. See Components Guide for the structure-vs-color-class pattern.

Build colors with the Palette Generator instead of by hand

Theme/Colors/color-variables.uss is a flat list of --color-{name}-{tone} variables (e.g. --color-purple-500, --color-neutral-100) — 11 tones per palette color across a 50→950 ramp. Ramps are named after actual colors (red, coral, orange, …, purple, …, slate, neutral, stone), not semantic roles. Hand-tuning these from scratch is tedious and rarely produces a coherent palette.

Use the docs-site tool instead:

  • Palette Generator — web tool that builds full 50→950 ramps per palette color + a neutral ramp. Adjust hue/saturation/lightness offsets per ramp until you're happy.
  • Exports color-variables.uss (every --color-{name}-{tone} token) plus optional component templates (UButton_Colors.uss, USlider_Colors.uss, etc.) that map per-color CSS classes (.btn.red, Slider.fill-coral) onto the component's internal vars.
  • Drop the exports into Assets/UI/Theme/Colors/ and @import them in your TSS — color-variables.uss first, then any component templates.

Full round-trip walkthrough: Palette Export.

Once the palette is in place, switching a Luna component's color is a one-class change (<engine:Button class="btn red"/>class="btn coral"); no per-element style overrides needed.

See also

  • Components Guide — apply the structure-vs-color-class pattern to every Luna control
  • Views — the pre-built screens your theme now drives
  • UI Toolkit Basics — UXML / USS / TSS primer
  • Colors — the --color-* token scheme in depth
  • Palette Export — Palette Generator round-trip in full
  • Styling — per-topic deep dives (Layout, Text, Border, etc.)

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL