Skip to content

Themes and colors

NgDoc styles every part of the site with CSS variables. A theme is a set of values for these variables, chosen by the data-theme attribute of the <html> element. NgDoc ships a light, a dark and an auto theme, and you can change any variable or add your own theme.

Themes

Themedata-themeColors
Light(none)The default values.
DarkdarkThe dark values.
AutoautoLight or dark, following the reader's system preference.

The light theme is part of the NgDoc styles. The dark and auto themes are in a separate stylesheet: add it after the NgDoc styles in the build target.

{
  "projects": {
    "<project-name>": {
      "architect": {
        "build": {
          "options": {
            "styles": ["node_modules/@ng-doc/app/styles/global.css", "node_modules/@ng-doc/app/styles/themes/dark.css", "src/styles.css"]
          }
        }
      }
    }
  }
}

Readers switch the theme with ng-doc-theme-toggle, which cycles through Auto, Light and Dark (Layout, navbar and sidebar [Navbar]), or with the T key, which switches between light and dark (Search and command palette [Keyboard shortcuts]). NgDoc saves the choice in the browser and restores it before the application starts, so the page doesn't flash in the wrong theme.

Theme by default

Without a data-theme attribute, the site starts in the light theme. To start in another theme, set the attribute in index.html:

index.htmlHTMLLine 2
<!doctype html>
<html lang="en" data-theme="auto">
  <head></head>
  <body>
    <app-root></app-root>
  </body>
</html>

Change colors

Override the variables in your global styles, after the NgDoc styles. A rule on :root changes the light theme; add a rule for [data-theme='dark'] to change the dark theme too:

styles.cssCSS
:root {
  --ng-doc-primary: #7c3aed;
  --ng-doc-font-family: 'Inter', sans-serif;
}

:root[data-theme='dark'] {
  --ng-doc-primary: #a78bfa;
}

@media (prefers-color-scheme: dark) {
  :root[data-theme='auto'] {
    --ng-doc-primary: #a78bfa;
  }
}

The auto theme uses the dark values inside prefers-color-scheme: dark, so repeat your dark overrides there.

Tints, borders, focus rings and badges are mixed from the main colors, so they follow your overrides. For example, the background of a selected search result is a tint of --ng-doc-primary.

Main variables

VariableDescription
--ng-doc-base-0 to --ng-doc-base-10The neutral ramp. base-0 is the page background, and each step moves further from it.
--ng-doc-backgroundThe page background. --ng-doc-base-0 by default.
--ng-doc-textBody text.
--ng-doc-text-mutedSecondary text, such as descriptions and hints.
--ng-doc-heading-colorHeadings and emphasized text.
--ng-doc-link-colorLinks. --ng-doc-primary by default.
--ng-doc-border-colorBorders and dividers.
--ng-doc-primaryThe accent color: active items, buttons and focus.
--ng-doc-info, --ng-doc-successThe colors of the note and success callouts.
--ng-doc-warning, --ng-doc-alertThe colors of the warning and alert callouts.
--ng-doc-primary-text and the other -textText on a solid fill of that color, such as --ng-doc-alert-text.
--ng-doc-font-familyThe body font.
--ng-doc-heading-font-familyThe heading font.
--ng-doc-font-sizeThe body font size.
--ng-doc-code-fontThe code font.
--ng-doc-code-backgroundThe background of code blocks.
--ng-doc-inline-code-backgroundThe color that inline code is tinted with.
--ng-doc-shadow-colorThe color of every shadow.
--ng-doc-class-background and the other kindsThe hue of an API kind, such as a class or an interface.

The sizes of the navbar, the sidebar and the page are in Layout, navbar and sidebar [Sizes and colors]. Some pages describe the variables of their components, such as Demos [Customization].

Tokens

The default values come from a token layer in @ng-doc/ui-kit: palettes such as --ng-doc-palette-brand-600, spacing (--ng-doc-space-4), radii (--ng-doc-radius-md) and type sizes. You can use the tokens in your own styles to match NgDoc. Prefer overriding the variables above to overriding tokens, because the variables are what the components read.

 Custom theme

A custom theme is a stylesheet scoped to its own data-theme value. Start from the dark theme, which overrides only what differs from the light values:

dark.scssSCSS
// dark theme
@mixin theme {
  --ng-doc-base-rgb: 24 25 29;
  --ng-doc-base-0: var(--ng-doc-palette-neutral-950);
  --ng-doc-base-1: var(--ng-doc-palette-neutral-900);
  --ng-doc-base-2: var(--ng-doc-palette-neutral-875);
  --ng-doc-base-3: var(--ng-doc-palette-neutral-850);
  --ng-doc-base-4: var(--ng-doc-palette-neutral-800);
  --ng-doc-base-5: var(--ng-doc-palette-neutral-750);
  --ng-doc-base-6: var(--ng-doc-palette-neutral-700);
  --ng-doc-base-7: var(--ng-doc-palette-neutral-600);
  --ng-doc-base-8: var(--ng-doc-palette-neutral-500);
  --ng-doc-base-9: var(--ng-doc-palette-neutral-450);
  --ng-doc-base-10: var(--ng-doc-palette-neutral-400);
  --ng-doc-heading-color: var(--ng-doc-palette-neutral-50);
  --ng-doc-text: var(--ng-doc-palette-neutral-300);
  --ng-doc-text-muted: var(--ng-doc-base-9);
  --ng-doc-text-selection: color-mix(in srgb, var(--ng-doc-primary) 30%, transparent);

  --ng-doc-shadow-color: rgb(0 0 0 / 0.45);
  --ng-doc-inline-code-background: var(--ng-doc-base-10);

  // One accent family in both themes: links, info and primary share the brand hue.
  --ng-doc-primary: var(--ng-doc-palette-brand-400);
  --ng-doc-primary-text: var(--ng-doc-palette-neutral-1000);
  --ng-doc-link-color: var(--ng-doc-primary);
  --ng-doc-info: var(--ng-doc-palette-brand-400);
  --ng-doc-info-text: var(--ng-doc-palette-neutral-1000);
  --ng-doc-success: var(--ng-doc-palette-green-400);
  --ng-doc-success-text: var(--ng-doc-palette-neutral-1000);
  --ng-doc-warning: var(--ng-doc-palette-amber-400);
  --ng-doc-warning-text: var(--ng-doc-palette-neutral-1000);
  --ng-doc-alert: var(--ng-doc-palette-red-400);
  --ng-doc-alert-text: var(--ng-doc-palette-neutral-1000);

  // The search highlight stays yellow, as in the light theme.
  --ng-doc-mark-background: color-mix(in srgb, #f5c542 28%, transparent);
  --ng-doc-search-result-color: var(--ng-doc-text);
  --ng-doc-navbar-border: 1px solid var(--ng-doc-border-color);

  // Derived tokens: lighter hues and slightly stronger tints on the dark page.
  --ng-doc-hue-violet: var(--ng-doc-palette-violet-400);
  --ng-doc-on-hue: var(--ng-doc-palette-neutral-1000);
  --ng-doc-mix-soft: 12%;
  --ng-doc-mix-chip: 16%;
  --ng-doc-mix-border: 30%;
  --ng-doc-focus-ring-color: var(--ng-doc-palette-brand-400);

  // base-7 input borders measure below 3:1 on the base-1 inspector, so its inputs use base-8.
  --ng-doc-playground-input-border: var(--ng-doc-border-size) solid var(--ng-doc-base-8);
}

// shiki theme to match dark theme
@mixin shiki-theme {
  .shiki,
  .shiki span {
    color: var(--shiki-dark) !important;
  }
}

// enable dark theme when using auto theme and prefers-color-scheme is dark
:root[data-theme='auto'] {
  @media (prefers-color-scheme: dark) {
    @include theme();
    @include shiki-theme();
  }
}

// apply dark theme when dark theme is selected
:root[data-theme='dark'] {
  @include theme();
  @include shiki-theme();
}

For a theme called ocean, add the rules to your global styles:

styles.cssCSS
:root[data-theme='ocean'] {
  --ng-doc-base-0: #0b1f2a;
  --ng-doc-base-1: #102a38;
  --ng-doc-heading-color: #e6f4f8;
  --ng-doc-text: #c2dde6;
  --ng-doc-primary: #38bdf8;
}

/* Code blocks: use the dark colors of the code theme */
:root[data-theme='ocean'] .shiki,
:root[data-theme='ocean'] .shiki span {
  color: var(--shiki-dark) !important;
}

The last rule is needed only for a dark custom theme whose code blocks use a pair of Shiki themes (the legacy builders, or shiki.themes in the configuration): code blocks use the light colors of the code theme unless the theme is dark or auto. With the default css-variables code theme, set --ng-doc-syntax-* in your theme instead.

Switch to it with NgDocThemeService, or set it by default in index.html:

theme-button.component.tsTypeScript
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
import { NgDocThemeService } from '@ng-doc/app';

@Component({
  selector: 'app-theme-button',
  template: ` <button type="button" [attr.aria-pressed]="isOcean()" (click)="toggle()">Ocean theme</button> `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ThemeButtonComponent {
  private readonly themeService = inject(NgDocThemeService);

  protected readonly isOcean = computed(() => this.themeService.theme() === 'ocean');

  protected toggle(): void {
    this.themeService.set(this.isOcean() ? 'auto' : 'ocean');
  }
}

theme() is the current theme as a signal: auto, dark, the id of your theme, or null for the light theme. set() changes it and saves it in the browser; set() without an id switches to the light theme.

Code highlighting

With the new engine (the Vite host and the ng-doc CLI), code is colored by NgDoc's css-variables theme by default: every token takes a CSS variable of the current site theme, so code follows the light, dark and custom themes. Override the variables like the others:

styles.cssCSS
:root {
  --ng-doc-syntax-keyword: #8250df;
  --ng-doc-syntax-type: #0550ae;
  --ng-doc-syntax-function: #116329;
  --ng-doc-syntax-string: #0a3069;
}

The variables are --ng-doc-syntax-plain, -punctuation, -comment, -keyword, -type, -function, -string, -number, -tag and -decorator. The legacy application and dev-server builders keep the github-light and ayu-dark Shiki themes unless you set css-variables as both themes. To use other Shiki themes, see Code highlighting.

 Gotchas

Warning

The ng-doc-theme-toggle component offers only Auto, Light and Dark. While a custom theme is set, it shows Auto, and pressing it leaves your theme. Give readers your own control for a custom theme.

💡 Tip

Override variables in a global stylesheet. A rule in a component's styles is scoped to that component and doesn't reach NgDoc.

Next: Code highlighting

Edit this page