Skip to main content

Appearance setup

Set up Light/Dark mode with semantic colors, one startup call, and a small Appearance toggle.

1. Define semantic colors

Create app/assets/semantic.colors.json and register the color names under theme.extend.colors in config.cjs.

See Semantic colors for JSON definitions, class mapping, nesting rules, and alpha transparency.

2. Initialize Appearance at startup

Call Appearance.init() once before opening the first window:

app/controllers/index.js
const { Appearance } = require('purgetss.ui')
Appearance.init()

$.navWin.open()

This reads the saved preference from Ti.App.Properties and applies it through Ti.UI.overrideUserInterfaceStyle. If no preference is saved, the system default stays in place.

3. Build an Appearance toggle

Create a settings view where users choose their preferred mode.

XML

app/views/settings.xml
<Alloy>
<Window class="bg-surface title-attributes-on-surface bar-surface nav-tint-accent" title="Settings">
<ScrollView class="vertical content-w-screen content-h-auto">

<Label class="mx-4 mb-2 mt-6 h-auto text-xs font-semibold text-on-surface-variant">APPEARANCE</Label>

<View class="mx-4 mb-4 h-auto w-screen rounded-xl bg-surface-high shadow-sm vertical clip-enabled">

<!-- System -->
<View class="horizontal mx-4 h-12 w-screen" onClick="selectSystem">
<Label class="fa-solid fa-mobile-screen ml-0 h-12 w-8 text-blue-500" />
<Label class="h-12 text-sm font-semibold text-on-surface">System</Label>
<Label id="themeSystemCheck" class="fa-solid fa-circle-check mr-0 h-12 w-screen text-right text-green-500" />
</View>

<View class="h-px w-screen bg-border" />

<!-- Light -->
<View class="horizontal mx-4 h-12 w-screen" onClick="selectLight">
<Label class="fa-solid fa-sun ml-0 h-12 w-8 text-amber-500" />
<Label class="h-12 text-sm font-semibold text-on-surface">Light</Label>
<Label id="themeLightCheck" class="fa-solid fa-circle-check mr-0 hidden h-12 w-screen text-right text-green-500" />
</View>

<View class="h-px w-screen bg-border" />

<!-- Dark -->
<View class="horizontal mx-4 h-12 w-screen" onClick="selectDark">
<Label class="fa-solid fa-moon ml-0 h-12 w-8 text-purple-500" />
<Label class="h-12 text-sm font-semibold text-on-surface">Dark</Label>
<Label id="themeDarkCheck" class="fa-solid fa-circle-check mr-0 hidden h-12 w-screen text-right text-green-500" />
</View>

</View>

</ScrollView>
</Window>
</Alloy>

Controller

app/controllers/settings.js
const { Appearance } = require('purgetss.ui')

updateUI(Appearance.get())

function selectDark() { selectAppearance('dark') }
function selectLight() { selectAppearance('light') }
function selectSystem() { selectAppearance('system') }

function selectAppearance(value) {
Appearance.set(value)
updateUI(value)
}

function updateUI(value) {
$.themeDarkCheck.visible = (value === 'dark')
$.themeLightCheck.visible = (value === 'light')
$.themeSystemCheck.visible = (value === 'system')
}

How it fits together

┌─ app startup ───────────────────────────────────────────┐
│ │
│ 1. Appearance.init() │
│ └─ Reads saved mode from Ti.App.Properties │
│ └─ Applies Ti.UI.overrideUserInterfaceStyle │
│ │
│ 2. Semantic colors resolve automatically │
│ └─ bg-surface → surfaceColor → light or dark hex │
│ └─ text-on-surface → textColor → light or dark hex │
│ │
│ 3. User taps "Dark" in settings │
│ └─ Appearance.set('dark') │
│ └─ All semantic colors update instantly │
│ └─ Preference saved for next app launch │
│ │
└─────────────────────────────────────────────────────────┘
tip

Semantic colors apply anywhere you use bg-*, text-*, and border-* classes: Windows, Views, Labels, Buttons, TextFields, TextAreas, ListViews, and custom classes that reference a semantic name. One appearance switch updates the UI.