Frontend: Theming and Appearance
Relevant source files
The following files were used as context for generating this wiki page:
The theming and appearance system in Nucleus provides a dynamic and persistent way to manage the application's visual style. It is built upon Tailwind CSS and leverages a custom Svelte store to control color schemes, fonts, and other UI properties. Users can select their preferred appearance, and the settings are saved in local storage for a consistent experience across sessions.
The system is designed to be extensible, with themes and fonts defined in configuration files. A custom Tailwind plugin translates these theme definitions into CSS custom properties, allowing for real-time theme switching without reloading the application. The core of this system is the AppearanceStore, which orchestrates state management, persistence, and the application of styles to the DOM.
Architecture Overview
The theming architecture revolves around three main parts: the AppearanceStore for state management, configuration files for defining themes and fonts, and a custom Tailwind CSS plugin for style generation.
This diagram illustrates the flow from user interaction to the application of theme styles.
Appearance Store
The AppearanceStore is a Svelte store responsible for managing all appearance-related settings. It is a singleton class that handles state initialization, modification, and persistence.
Sources: client/stores/appearance.store.ts
Key Properties
The store manages several aspects of the application's appearance, which are persisted in local storage under the key 21n_appearance.
| Property | Type | Description |
|---|---|---|
theme | Theme | The current color theme (e.g., 'light', 'dark'). |
font | string | The ID of the currently active font. |
colorScheme | ColorScheme | The current color scheme (e.g., 'classic', 'nebula'). |
isConstrainedWidth | boolean | Toggles a constrained width layout. |
isZenMode | boolean | Toggles a distraction-free "Zen mode". |
isFocusMode | boolean | Toggles a "Focus mode". |
isTransparent | boolean | Enables or disables UI transparency. |
Sources: client/stores/appearance.store.ts:18-32
Class Diagram
This diagram shows the structure of the AppearanceStore class.
Initialization and Persistence
On initialization, the AppearanceStore attempts to load settings from local storage. If no settings are found, it determines the user's system preference for dark or light mode and applies a default theme.
The init() method performs the following steps:
- Reads the
21n_appearancekey from local storage. - If data exists, it merges the stored settings with the default state.
- If no data exists, it checks
window.matchMedia('(prefers-color-scheme: dark)')to set an initial theme. - Calls the
apply()method to inject the theme and font styles into the document. - Subscribes to its own changes to call
persist()andapply()whenever the state is updated.
The apply() method is crucial for dynamically changing the appearance. It sets the data-theme, data-color-scheme, and data-font attributes on the <body> element and updates the CSS custom properties for the active color scheme.
Sources: client/stores/appearance.store.ts:42-83, apps/nucleus/src/app.html:11
Tailwind CSS Integration
The project uses a custom Tailwind plugin named tidigit to integrate the dynamic color schemes. This plugin reads color definitions from colorschemes.json and makes them available as CSS custom properties that Tailwind can use.
Sources: client/theme/tidigit.tailwind.js
tidigit Plugin
The plugin logic is defined in client/theme/tidigit.tailwind.js. It extends Tailwind's theme by:
- Iterating through each color scheme defined in
colorschemes.json. - For each scheme, it creates a set of CSS custom properties (e.g.,
--color-primary,--color-secondary). - These properties are added to the
:rootselector within a[data-color-scheme="..."]attribute selector. This allows the colors to change when thedata-color-schemeattribute on the<body>is updated by theAppearanceStore. - It maps Tailwind's color utilities (e.g.,
bg-primary,text-secondary) to these CSS custom properties.
This flow shows how color schemes are processed by the plugin.
Color Scheme Configuration
Color schemes are defined in client/theme/colorschemes.json. Each scheme has a unique name and a map of color tokens to HEX values. The system defines a default scheme and a dark variant for each.
Here is an example of the classic color scheme structure:
{
"classic": {
"default": {
"primary": "#0F1215",
"secondary": "#4B5563",
"tertiary": "#9CA3AF",
"accent": "#2563EB",
"bg-primary": "#FFFFFF",
"bg-secondary": "#F9FAFB",
"bg-tertiary": "#F3F4F6",
"border": "#E5E7EB"
},
"dark": {
"primary": "#F9FAFB",
"secondary": "#9CA3AF",
"tertiary": "#4B5563",
"accent": "#3B82F6",
"bg-primary": "#000000",
"bg-secondary": "#111111",
"bg-tertiary": "#1F2023",
"border": "#27282C"
}
}
}
Sources: client/theme/colorschemes.json:2-23
Font Configuration
The application supports multiple fonts, which are defined in client/layout/layers/themeLayer/fonts.config.ts. The AppearanceStore uses this configuration to load the selected font.
Font Definitions
The fonts array contains objects, each representing a selectable font. Each font object has an id, a label for the UI, the CSS font-family name, and a url to the Google Fonts stylesheet.
| Property | Type | Description |
|---|---|---|
id | string | A unique identifier for the font (e.g., 'inter'). |
label | string | The display name for the font in the UI (e.g., 'Inter'). |
family | string | The CSS font-family value. |
url | string | The URL to the font stylesheet. |
Sources: client/layout/layers/themeLayer/fonts.config.ts:3-10
Font Application
When a user selects a font, the setFont method in the AppearanceStore is called. This method updates the font property in the store and triggers the apply method. The apply method then:
- Finds the corresponding font object in the
fontsconfig. - Creates a
<link>element for the font's stylesheet URL. - Appends the
<link>element to the document's<head>. - Sets the
data-fontattribute on the<body>element to the font's ID. - The main CSS can then use this attribute to apply the correct
font-family.
Sources: client/stores/appearance.store.ts:114-132, client/layout/layers/themeLayer/fonts.config.ts