Theming
Relevant source files
The following files were used as context for generating this wiki page:The theming system for Adfluens is built upon a design philosophy called "The Digital Prism," which aims to be intelligent, fluid, and energetic. The implementation uses a "Soft Brutalism" visual language, characterized by distinct borders, tight radii, and high-contrast states, while strictly avoiding drop shadows. This is achieved through a CSS variable-driven system managed by Tailwind CSS, supporting both a light mode ("Paper & Ink") and a dark mode ("Obsidian & Neon").
The system is managed in the React frontend via a ThemeProvider context, which persists the user's choice (light, dark, or system) to localStorage. It includes an inline script to prevent a "flash of unstyled content" (FOUC) on initial load and leverages the View Transitions API for smooth theme changes. A suite of ThemeToggle components provides various UI options for users to switch themes.
Sources: DESIGN.md:5-22, apps/web/src/lib/theme.provider.tsx:1-196, apps/web/index.html:4-17
Core Concepts
The visual identity of the application is defined by a set of core principles covering color, typography, and visual language.
Sources: DESIGN.md
Design Philosophy
- The Digital Prism: Reflects the platform's function of refracting a brand's identity across social platforms. The brand voice is professional, data-driven, and modern.
- Soft Brutalism: Merges modern SaaS usability with the edge of brutalism. This is realized through:
1pxsolid borders on all structural elements.- Tight border radii (
2px,4px,8px). - Strict avoidance of blurred drop shadows in favor of hard "offset" borders or high-contrast outlines.
- A subtle noise texture overlay to reduce the sterility of flat colors.
Sources: DESIGN.md:16-22, 90-101, 334-343
Color Palette
The system uses a high-contrast, bold color palette defined as CSS variables. Colors are defined semantically for both light and dark modes. The implementation uses oklch for P3 display support.
Sources: packages/tailwind-config/shared-styles.css:5-274, DESIGN.md:58-88
Light Mode ("Paper & Ink")
| Role | Color Name | Hex | CSS Variable |
|---|---|---|---|
| Canvas | Alabaster | #FDFDFC | --background |
| Surface | White | #FFFFFF | --card |
| Ink | Carbon | #0A0A0A | --foreground |
| Primary | International Orange | #FF4F00 | --primary |
| Secondary | Deep Forest | #004D40 | --secondary |
| Accent | Lime Acid | #CCFF00 | --accent |
| Border | Smoke | #E5E5E5 | --border |
Sources: DESIGN.md:63-71, packages/tailwind-config/shared-styles.css:201-233
Dark Mode ("Obsidian & Neon")
| Role | Color Name | Hex | CSS Variable |
|---|---|---|---|
| Canvas | Obsidian | #050505 | --background |
| Surface | Charcoal | #121212 | --card |
| Ink | Mist | #EDEDED | --foreground |
| Primary | International Orange | #FF4F00 | --primary |
| Secondary | Emerald | #10B981 | --secondary |
| Accent | Lime Acid | #CCFF00 | --accent |
| Border | Graphite | #262626 | --border |
Sources: DESIGN.md:76-84, packages/tailwind-config/shared-styles.css:236-268
Typography
A distinct font stack is used to establish a unique brand voice, avoiding common choices like Inter or Roboto. The fonts are loaded via Google Fonts in the main index.html.
| Usage | Font Family | CSS Variable |
|---|---|---|
| Display (H1-H3) | Bricolage Grotesque | --font-display |
| Body (UI Text) | Instrument Sans | --font-sans |
| Monospace (Data) | JetBrains Mono | --font-mono |
Sources: DESIGN.md:26-55, packages/tailwind-config/shared-styles.css:16-18, apps/web/index.html:26-46
Architecture and Implementation
The theming system is architected to be robust, performant, and easy to manage, combining CSS custom properties, a React Context Provider, and an initial render blocking script.
CSS Variable System
The core of the theming is a set of CSS variables defined in packages/tailwind-config/shared-styles.css. The :root selector defines the light theme variables, and the .dark class selector overrides them for dark mode. These variables are defined using oklch color functions for modern display support.
The following diagram shows how theme-specific variables are applied.
This diagram illustrates the flow of CSS variables from their definition in light/dark scopes to their application on DOM elements.
Sources: packages/tailwind-config/shared-styles.css:200-274
FOUC Prevention
To prevent a "flash of unstyled content" where the default theme is shown briefly before the user's preferred theme is loaded, a small, render-blocking JavaScript snippet is included in the <head> of apps/web/index.html. This script runs before any React code, reads the theme from localStorage or system preferences, and applies the correct dark or light class to the <html> element.
<!-- apps/web/index.html -->
<script>
(function () {
const stored = localStorage.getItem("adfluens-theme");
const systemDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
const theme =
stored === "dark" ||
(stored === "system" && systemDark) ||
(!stored && systemDark)
? "dark"
: "light";
document.documentElement.classList.remove("light", "dark");
document.documentElement.classList.add(theme);
})();
</script>
Sources: apps/web/index.html:4-17
Theme Provider
The ThemeProvider component, located in apps/web/src/lib/theme.provider.tsx, is a React Context provider that wraps the application. It is responsible for managing the theme state.
Key Features:
- State Management: Manages two pieces of state:
theme(light,dark, orsystem) andresolvedTheme(lightordark). - Persistence: Reads the initial theme from
localStorage(key:adfluens-theme) and persists any changes back to it. - System Preference: When
themeis "system", it listens towindow.matchMediafor changes to the OS color scheme and updates theresolvedThemeautomatically. - View Transitions: Uses the
document.startViewTransitionAPI, if available, to create a smooth cross-fade animation when the theme is changed.
The useTheme hook provides access to the theme context for any component within the provider.
Sources: apps/web/src/lib/theme.provider.tsx:1-196
The sequence diagram below shows the process of a user changing the theme.
Components
A set of UI components are provided in the @repo/ui package to allow users to control the theme.
Theme Toggle Components
The file packages/ui/src/components/theme-toggle.tsx exports four different React components for theme switching, each suited for different UI contexts. They all use the useTheme hook to get and set the current theme.
| Component | Description | Use Case |
|---|---|---|
ThemeToggle | A full-width button with an icon and text label (e.g., "Light theme"). Cycles through system → light → dark. | Sidebars or settings panels. |
ThemeToggleIcon | A compact, icon-only button that shows a sun or moon. Cycles through themes on click. | Headers or toolbars. |
ThemeSwitcher | A pill-style segmented control showing all three options (system, light, dark) at once. | Settings pages. |
ThemeDropdown | A compact button that opens a dropdown menu with all theme options. | When space is limited but all options should be accessible. |
Sources: packages/ui/src/components/theme-toggle.tsx, packages/ui/src/index.ts:60-65
Usage Example
The Sidebar component uses the ThemeToggleIcon to provide a compact theme switcher. It retrieves the theme and setTheme function from the useTheme hook and passes them as props.
// apps/web/src/shared/components/sidebar.component.tsx
import { ThemeToggleIcon } from "@repo/ui";
import { useTheme } from "@/lib/theme.provider";
export const Sidebar: React.FC = () => {
// ...
const { theme, setTheme } = useTheme();
// ...
return (
<aside>
{/* ... */}
<div className="px-4 pb-2">
<Separator className="mb-3" />
<ThemeToggleIcon theme={theme} onThemeChange={setTheme} />
</div>
{/* ... */}
</aside>
);
};
Sources: apps/web/src/shared/components/sidebar.component.tsx:11, 107-110
Conclusion
The Adfluens theming system is a well-structured and feature-rich implementation that directly translates the project's design philosophy into a functional user experience. By combining a CSS-first approach with Tailwind, a robust React provider for state management, and careful handling of initial rendering, it provides a consistent, performant, and aesthetically pleasing interface. The variety of toggle components ensures that theme switching can be integrated seamlessly across different parts of the application.
Sources: DESIGN.md, apps/web/src/lib/theme.provider.tsx, packages/tailwind-config/shared-styles.css