Skip to main content
Neural Nativev0.1.0-beta.1Documentation

One root theme, inherited by child components

The neu-theme container shares its palette through dependency injection and exposes --neu-* variables for native styles. Add it once at the root; descendant screens and controls inherit that theme.

Import NeuThemeComponent from @neural-ui/native/theme and add it to the root component’s imports. No global theme initialization provider or stylesheet import is needed. Keep the safe-area components around your screen content as shown in the root example.

Light, dark and system

SchemeBehavior
systemDefault. Follows the current device appearance and its changes.
lightUses the light palette even when the device is dark.
darkUses the dark palette even when the device is light.

The documentation’s theme changes the web preview; a static Android or iOS capture keeps its recorded appearance. Choosing a library theme does not configure every operating-system surface, the status bar or the application’s Expo appearance settings.

Define a brand palette for both modes

Create src/app/theme-tokens.ts. A partial token object changes only the named properties; the remaining values come from the selected light or dark library palette. Keep accent and onAccent as a readable pair. If you customize an accent color, review tonal backgrounds too.

src/app/theme-tokens.ts
import type { NeuTokens } from '@neural-ui/native/foundation';

export const brandLight = {
  accent: '#3B5BDB',
  accentText: '#3048A5',
  onAccent: '#FFFFFF',
  tonal: '#EBEFFF',
  surfaceBorder: '#D8DEEB',
  divider: '#E8ECF3',
  surfaceSecondary: '#F0F3FA',
  mediaCanvas: '#101828',
  mediaText: '#F1F5F9',
  mediaBarStyle: 'light',
  radius: 16,
} satisfies Partial<NeuTokens>;

export const brandDark = {
  accent: '#B8C6FF',
  accentText: '#C6D0FF',
  onAccent: '#172955',
  tonal: '#293756',
  surfaceBorder: '#3B4863',
  divider: '#33405A',
  surfaceSecondary: '#263249',
  mediaCanvas: '#101828',
  mediaText: '#F1F5F9',
  mediaBarStyle: 'light',
  radius: 16,
} satisfies Partial<NeuTokens>;

Apply the palette and switch reactively

This complete src/app/app.ts uses PreferencesComponent from Design principles and the token file above. The scheme signal selects the mode. The computed tokens follow that selection and, in system mode, the device’s ColorScheme service. Buttons update the mode without rebuilding the screen.

src/app/app.ts
import { Component, computed, inject, signal } from '@angular/core';
import { SafeAreaProvider, SafeAreaView } from '@ng-native/components';
import { ColorScheme } from '@ng-native/device';
import { NeuThemeComponent } from '@neural-ui/native/theme';
import { NeuButtonComponent } from '@neural-ui/native/button';
import type { NeuColorScheme } from '@neural-ui/native/foundation';
import { brandLight, brandDark } from './theme-tokens';
import { PreferencesComponent } from './preferences.component';

@Component({
  selector: 'app-root',
  imports: [SafeAreaProvider, SafeAreaView, NeuThemeComponent, NeuButtonComponent, PreferencesComponent],
  template: `
    <safe-area-provider>
      <neu-theme [scheme]="scheme()" [tokens]="tokens()">
        <safe-area-view class="screen">
          <app-preferences />
          <neu-button label="Use light theme" variant="text" (pressed)="scheme.set('light')" />
          <neu-button label="Use dark theme" variant="text" (pressed)="scheme.set('dark')" />
          <neu-button label="Follow device" variant="text" (pressed)="scheme.set('system')" />
        </safe-area-view>
      </neu-theme>
    </safe-area-provider>
  `,
  styles: `
    :host, .screen { flex: 1; }
  `,
})
export class App {
  readonly scheme = signal<NeuColorScheme>('system');
  private readonly deviceScheme = inject(ColorScheme);
  readonly tokens = computed(() => {
    const mode = this.scheme() === 'system' ? this.deviceScheme.current() : this.scheme();
    return mode === 'dark' ? brandDark : brandLight;
  });
}

A fixed tokens object is applied unchanged in both light and dark modes. Use a mode-aware computed object when your brand needs different colors in each. If you only override radius or spacing, the default color palette can continue to follow system without any color-specific computation.

Local themes and their scope

Add a nested neu-theme only for a deliberate local appearance, such as a dark promotional area within a light app. Descendant controls use the nearest theme. Each neu-theme starts from its selected built-in palette and merges its own tokens; it does not automatically merge the parent’s brand overrides.

local-theme.component.html
<neu-theme scheme="dark" [tokens]="brandDark">
  <neu-button label="Explore membership" />
</neu-theme>

This is a template fragment for a separate section. Import NeuThemeComponent and NeuButtonComponent there and expose brandDark from the token file. The theme container has flex: 1, so define the surrounding layout deliberately rather than adding nested wrappers for ordinary controls.