Themes
Themes are Reshape's design system, made data-driven: instead of a fixed set of CSS variables baked into the frontend, every color, radius, shadow, and font is a database record that gets compiled into CSS and injected at runtime. Users and companies can create their own themes, switch between them, and ship a light and dark variant of each.

Themes vs. appearance
"Theme" (this page) and "appearance" (light/dark/system) are two independent settings. A theme defines what the colors are; appearance decides which variant — light or dark — is currently shown. See Appearance below.
The token set
A theme's variables column (app/Models/Theme.php) stores a full shadcn/Tailwind design-token set, split into light and dark maps — colors (background, primary, chart-1…chart-5, sidebar-*), typography (font-sans, font-serif, font-mono), and shape (radius, shadow-*, spacing). The full list is Theme::VARIABLE_KEYS.
From database to CSS
// Compile this theme's variables into a plain :root {} / .dark {} stylesheet...
$theme->stylesheet : stringThis accessor is exposed on ThemeResource, and the frontend injects it verbatim as a <style> tag. Dark mode toggling is nothing more than adding a .dark class to <html> — the :root/.dark rule pair is exactly Tailwind's dark-mode convention, just generated from the database instead of a static CSS file:
:root { --primary: oklch(0.6 0.2 250); /* ... */ }
.dark { --primary: oklch(0.7 0.2 250); /* ... */ }Resolving the current theme
Resolving "the current theme" for a user follows a clear priority order: a user's company membership can pin a specific theme, which beats the user's personal choice, which beats the platform default. Switching a theme and toggling appearance (is_dark) can be done independently of each other.
Global vs. scoped themes
Like announcements, a themeable of null means the theme is available platform-wide; a set themeable (a User or Company) scopes it to that owner.
// Whether this theme is visible to a user — global, own, or their company's...
$theme->isVisibleTo(User $user) : boolTrait Methods (HasThemes)
// Every theme owned by this model, and the theme it currently has selected...
$model->themes() : Illuminate\Database\Eloquent\Relations\MorphMany
$model->theme() : Illuminate\Database\Eloquent\Relations\BelongsToActions (app/Actions/Themes/)
// Create a theme, globally or scoped to $themeable — only one global theme may be default...
CreateTheme::create(array $input, ?Model $themeable = null) : App\Models\Theme
// Update a theme...
UpdateTheme::update(Theme $theme, array $input, ?Model $themeable = null) : void
// Delete a theme...
DeleteTheme::delete(Theme $theme, ?Model $themeable = null) : void
// Switch a user's current theme — company membership if they have one, else personal...
UpdateCurrentTheme::update(User $user, ?int $themeId) : voidAppearance (light / dark / system)
Separate from the Theme system entirely: HandleAppearance middleware shares an appearance cookie value (light, dark, or system) with views, and User.is_dark is a boolean column tracking the resolved preference used for the design-token switch described above.
In the frontend
composables/use-theme.ts — setTheme(id), clearTheme(), toggleDark() (persists the choice to the server), and a computed isDark. A use-dynamic-favicon.ts composable even recolors the browser favicon based on the theme's --primary value.