LogoNotyra Docs

Create Custom Themes

Notyra supports customizing themes using JSON files. Users can create their own color themes and fully customize Notyra's appearance to their liking.

Theme File Structure

A theme is a single .json file with the following shape:

{
  "id": "my-theme",
  "name": "My Theme",
  "nameJa": "マイテーマ",
  "description": "A short description in English",
  "descriptionJa": "日本語での説明",
  "swatches": ["#accent", "#background", "#subtle"],
  "swatchesDark": ["#accent-dark", "#background-dark", "#subtle-dark"],
  "light": {
    "--background": "oklch(...)",
    "--foreground": "oklch(...)",
    "...": "..."
  },
  "dark": {
    "--background": "oklch(...)",
    "--foreground": "oklch(...)",
    "...": "..."
  }
}

Top-level Fields

FieldTypeRequiredDescription
idstringYesUnique identifier. Use lowercase with hyphens (e.g. "ocean-blue").
namestringYesDisplay name in English.
nameJastringYesDisplay name in Japanese.
descriptionstringNoShort description in English.
descriptionJastringNoShort description in Japanese.
swatchesstring[]Yes3 hex colors for the light mode preview chip: [accent, background, subtle].
swatchesDarkstring[]Yes3 hex colors for the dark mode preview chip: [accent, background, subtle].
lightobjectYesCSS variable map applied in light mode.
darkobjectYesCSS variable map applied in dark mode.

CSS Variables Reference

All values in light and dark are set on :root via element.style.setProperty(). You can use any valid CSS value — OKLch, hex, rgba(), or references to other vars like var(--foreground).

Base Variables (shadcn/ui compatible)

These follow the shadcn/ui convention and control the overall color palette.

VariableDescription
--backgroundPage / panel background
--foregroundDefault text color
--cardCard surface background
--card-foregroundText on cards
--popoverPopover / dropdown background
--popover-foregroundText in popovers
--primaryPrimary interactive color (buttons, etc.)
--primary-foregroundText on primary color
--secondarySecondary surface (code blocks, etc.)
--secondary-foregroundText on secondary surface
--mutedSubtle background (tags, badges, muted areas)
--muted-foregroundMuted / subdued text
--accentHover/focus highlight background
--accent-foregroundText on accent background
--destructiveDestructive action color
--destructive-foregroundText on destructive color
--borderDefault border color
--inputInput field border color
--ringFocus ring color
--radiusBase border radius (e.g. "0.5rem")
VariableDescription
--sidebarSidebar panel background
--sidebar-foregroundSidebar text
--sidebar-primarySidebar primary color
--sidebar-primary-foregroundText on sidebar primary
--sidebar-accentSidebar hover/focus background
--sidebar-accent-foregroundText on sidebar accent
--sidebar-borderSidebar border color
--sidebar-ringSidebar focus ring

Chart Variables

--chart-1 through --chart-5 — five colors used for data visualization elements (not commonly needed for basic themes).


Theme-specific Variables

These variables are unique to Notyra and control accent colors throughout the UI — editor headings, active selections, links, badges, and gradient decorations.

VariableDescriptionExample (light)Example (dark)
--theme-accentPrimary accent color. Used for selected items, active icons, heading color (if configured), and interactive highlights."#7c3aed""#c084fc"
--theme-accent-hoverDarker variant of the accent for hover states on solid accent backgrounds."#6d28d9""#e879f9"
--theme-accent-subtleVery light tint of the accent. Used for selected item backgrounds, badge backgrounds, and the folder header gradient. Should be near-white in light mode and semi-transparent in dark mode."#faf5ff""rgba(76, 29, 149, 0.2)"
--theme-accent-subtle-hoverSlightly stronger version of --theme-accent-subtle for hover states."#ede9fe""rgba(76, 29, 149, 0.3)"
--theme-gradient-fromStart color of decorative gradient (app logo lines, etc.)."#a78bfa""#a78bfa"
--theme-gradient-toEnd color of decorative gradient."#c084fc""#c084fc"
--theme-linkHyperlink color in the Markdown preview."#8250df""#a371f7"
--theme-link-hoverLink color on hover."#a371f7""#c084fc"
--theme-heading-colorColor applied to Markdown headings (h1–h5) in both the editor and preview. Set to "var(--foreground)" for neutral (black/white) headings, or a hex color for accent-colored headings."var(--foreground)""var(--foreground)"

Color Format Tips

Notyra uses OKLch for base palette variables and hex / rgba for theme-specific accent variables.

OKLch

OKLch is a perceptually uniform color space: oklch(L C H) where:

  • L — lightness (0 = black, 1 = white)
  • C — chroma/saturation (0 = gray, higher = more vivid)
  • H — hue angle in degrees (0–360)
oklch(0.99 0.006 285)   → near-white with a very slight purple tint
oklch(0.13 0.04 285)    → near-black with a slight purple tint (dark bg)
oklch(0.45 0.18 250)    → medium blue (primary button)

Tip: Keep C (chroma) very low for background variables (--background, --card, --sidebar) to achieve a subtle tinted look without being distracting — values like 0.0040.01 work well.

Hex / rgba

Use hex colors for theme-specific variables (--theme-*) since they need to be vivid and readable:

"--theme-accent": "#059669"
"--theme-accent-subtle": "rgba(5, 150, 105, 0.15)"   ← for dark mode

How to Create a Custom Theme

  1. Copy one of the sample themes as a starting point.
  2. Change id, name, nameJa, and optionally description / descriptionJa.
  3. Update swatches and swatchesDark with 3 hex preview colors: [accent, background, subtle].
  4. Adjust the --background hue (the third OKLch value, H) to match your chosen color family. Keep chroma (C) around 0.0040.01.
  5. Update the --theme-* variables to your accent color in both light and dark sections.
  6. In Notyra, open Settings → Color Theme → Import and select your JSON file.

Minimal Custom Theme Template

The following template only includes the variables you most likely want to customize. All other variables will fall back to the grayscale defaults.

{
  "id": "my-custom-theme",
  "name": "My Custom Theme",
  "nameJa": "カスタムテーマ",
  "description": "My custom color theme",
  "descriptionJa": "カスタムカラーテーマ",
  "swatches": ["#ACCENT_HEX", "#F9FAFB", "#F0F0FF"],
  "swatchesDark": ["#ACCENT_DARK_HEX", "#111118", "#1E1E2E"],
  "light": {
    "--background": "oklch(0.99 0.006 HUE)",
    "--foreground": "oklch(0.13 0 0)",
    "--card": "oklch(0.99 0.006 HUE)",
    "--card-foreground": "oklch(0.13 0 0)",
    "--popover": "oklch(1 0 0)",
    "--popover-foreground": "oklch(0.13 0 0)",
    "--primary": "oklch(0.45 0.18 HUE)",
    "--primary-foreground": "oklch(0.98 0 0)",
    "--secondary": "oklch(0.965 0.008 HUE)",
    "--secondary-foreground": "oklch(0.2 0 0)",
    "--muted": "oklch(0.965 0.008 HUE)",
    "--muted-foreground": "oklch(0.5 0 0)",
    "--accent": "oklch(0.955 0.01 HUE)",
    "--accent-foreground": "oklch(0.18 0 0)",
    "--destructive": "oklch(0.577 0.245 27.325)",
    "--destructive-foreground": "oklch(0.577 0.245 27.325)",
    "--border": "oklch(0.91 0.01 HUE)",
    "--input": "oklch(0.91 0.01 HUE)",
    "--ring": "oklch(0.55 0.18 HUE)",
    "--radius": "0.5rem",
    "--sidebar": "oklch(0.975 0.008 HUE)",
    "--sidebar-foreground": "oklch(0.13 0 0)",
    "--sidebar-primary": "oklch(0.45 0.18 HUE)",
    "--sidebar-primary-foreground": "oklch(0.98 0 0)",
    "--sidebar-accent": "oklch(0.955 0.01 HUE)",
    "--sidebar-accent-foreground": "oklch(0.18 0 0)",
    "--sidebar-border": "oklch(0.91 0.01 HUE)",
    "--sidebar-ring": "oklch(0.55 0.18 HUE)",
    "--theme-accent": "#ACCENT_HEX",
    "--theme-accent-hover": "#ACCENT_DARKER_HEX",
    "--theme-accent-subtle": "#SUBTLE_HEX",
    "--theme-accent-subtle-hover": "#SUBTLE_HOVER_HEX",
    "--theme-gradient-from": "#GRADIENT_FROM_HEX",
    "--theme-gradient-to": "#GRADIENT_TO_HEX",
    "--theme-link": "#LINK_HEX",
    "--theme-link-hover": "#LINK_HOVER_HEX",
    "--theme-heading-color": "var(--foreground)"
  },
  "dark": {
    "--background": "oklch(0.13 0.04 HUE)",
    "--foreground": "oklch(0.94 0 0)",
    "--card": "oklch(0.155 0.04 HUE)",
    "--card-foreground": "oklch(0.94 0 0)",
    "--popover": "oklch(0.13 0.04 HUE)",
    "--popover-foreground": "oklch(0.94 0 0)",
    "--primary": "oklch(0.65 0.18 HUE)",
    "--primary-foreground": "oklch(0.1 0 0)",
    "--secondary": "oklch(0.22 0.045 HUE)",
    "--secondary-foreground": "oklch(0.94 0 0)",
    "--muted": "oklch(0.22 0.045 HUE)",
    "--muted-foreground": "oklch(0.62 0 0)",
    "--accent": "oklch(0.26 0.05 HUE)",
    "--accent-foreground": "oklch(0.94 0 0)",
    "--destructive": "oklch(0.396 0.141 25.723)",
    "--destructive-foreground": "oklch(0.637 0.237 25.331)",
    "--border": "oklch(0.24 0.045 HUE)",
    "--input": "oklch(0.24 0.045 HUE)",
    "--ring": "oklch(0.55 0.18 HUE)",
    "--radius": "0.5rem",
    "--sidebar": "oklch(0.17 0.042 HUE)",
    "--sidebar-foreground": "oklch(0.94 0 0)",
    "--sidebar-primary": "oklch(0.65 0.18 HUE)",
    "--sidebar-primary-foreground": "oklch(0.1 0 0)",
    "--sidebar-accent": "oklch(0.22 0.045 HUE)",
    "--sidebar-accent-foreground": "oklch(0.94 0 0)",
    "--sidebar-border": "oklch(0.24 0.045 HUE)",
    "--sidebar-ring": "oklch(0.55 0.18 HUE)",
    "--theme-accent": "#ACCENT_DARK_HEX",
    "--theme-accent-hover": "#ACCENT_DARK_HOVER_HEX",
    "--theme-accent-subtle": "rgba(R, G, B, 0.15)",
    "--theme-accent-subtle-hover": "rgba(R, G, B, 0.25)",
    "--theme-gradient-from": "#GRADIENT_FROM_DARK_HEX",
    "--theme-gradient-to": "#GRADIENT_TO_DARK_HEX",
    "--theme-link": "#LINK_DARK_HEX",
    "--theme-link-hover": "#LINK_DARK_HOVER_HEX",
    "--theme-heading-color": "var(--foreground)"
  }
}

Replace HUE with the OKLch hue angle for your color (e.g. 250 for blue, 160 for green, 55 for amber, 285 for purple).


TypeScript Interface

If you are building tooling around Notyra themes, the theme shape is:

interface ColorTheme {
  id: string
  name: string
  nameJa: string
  description?: string
  descriptionJa?: string
  /** [accent, background, subtle] — hex colors for the light mode preview chip */
  swatches: string[]
  /** [accent, background, subtle] — hex colors for the dark mode preview chip */
  swatchesDark: string[]
  /** CSS variable map applied in light mode */
  light: Record<string, string>
  /** CSS variable map applied in dark mode */
  dark: Record<string, string>
}

Themes are validated with the following minimum requirements before being imported:

  • id must be a non-empty string
  • name must be a non-empty string
  • swatches must be a non-empty array
  • swatchesDark must be a non-empty array
  • light and dark must be non-null objects