Files
bookhoard/tailwind.config.ts
T
John O'Keefe d5bb874651 feat(themes): Omarchy catalog, 12 light themes, Dark/Light Appearance picker
Expand the catalog from 11 themes (10 dark, 1 light) to 37 (24/13):
all 18 remaining Omarchy defaults (palettes from each theme's
colors.toml) plus 8 canonical light themes (Tokyo Night Day, Solarized
Light, One Light, Ayu Light, GitHub Light, Gruvbox Light, Everforest
Light, Kanagawa Lotus). Lupine leads the light list as the default
light theme; the app default stays tokyo-night.

Appearance panel: Dark/Light segmented filter (SSR-initialized from the
user's current theme), scrollable theme list, Bookshelf section pinned;
active row revealed on open. Light themes carry color-scheme: light and
the soft-shadow token set. Public-page theme selects get Dark/Light
optgroups. New moon/sun icons.

tailwind.config: safelist the new theme ids — Tailwind v3 purges custom
@layer base class selectors that appear in no scanned content file, so
an unsafelisted id silently ships without its CSS (found the hard way).

theme.ts: applyTheme now swaps only theme-* classes (changing theme
from the login select no longer wipes min-h-screen) and keeps the class
in sync on <html> for the new ThemeFlashGuard — a pre-paint inline
script on the four public pages that applies the saved theme before
first paint.

Kanagawa's UI accent uses crystalBlue (#7e9cd8) instead of Omarchy's
foreground-as-accent so links stay distinguishable from text.
2026-10-18 12:08:07 -04:00

152 lines
4.1 KiB
TypeScript

import type { Config } from "tailwindcss";
const config: Config = {
content: [
"./templates/**/*.{templ,html}",
"./web/static/**/*.{html,js}",
"./web/src/**/*.ts",
],
safelist: [
"theme-tokyo-night",
"theme-dracula",
"theme-nord",
"theme-solarized-dark",
"theme-monokai",
"theme-one-dark-pro",
"theme-material-dark",
"theme-catppuccin-mocha",
"theme-catppuccin-macchiato",
"theme-catppuccin-frappe",
"theme-catppuccin-latte",
// Omarchy dark themes (theme_options.go). Theme ids are runtime values
// applied via `theme-<name>` classes, so each must be safelisted or the
// CSS build purges its variable block from @layer base.
"theme-vantablack",
"theme-last-horizon",
"theme-ethereal",
"theme-hackerman",
"theme-matte-black",
"theme-solitude",
"theme-retro-82",
"theme-osaka-jade",
"theme-kanagawa",
"theme-miasma",
"theme-lumon",
"theme-ristretto",
"theme-gruvbox",
"theme-everforest",
// Light themes
"theme-tokyo-night-day",
"theme-solarized-light",
"theme-one-light",
"theme-ayu-light",
"theme-github-light",
"theme-gruvbox-light",
"theme-everforest-light",
"theme-kanagawa-lotus",
"theme-rose-pine",
"theme-lupine",
"theme-flexoki-light",
"theme-white",
],
theme: {
extend: {
screens: {
nav: "970px",
},
fontFamily: {
sans: [
"ui-sans-serif",
"system-ui",
"sans-serif",
'"Noto Sans SC"',
'"Noto Sans TC"',
'"Noto Sans JP"',
'"Noto Sans KR"',
'"PingFang SC"',
'"Microsoft YaHei"',
'"Hiragino Sans"',
'"Apple SD Gothic Neo"',
"Apple Color Emoji",
"Segoe UI Emoji",
"Segoe UI Symbol",
"Noto Color Emoji",
],
},
colors: {
// Semantic surface tokens (page bg, cards, raised layers)
surface: {
DEFAULT: "var(--bg-primary)",
raised: "var(--bg-secondary)",
hover: "var(--surface-hover)",
overlay: "var(--surface-overlay)",
},
// Text tokens
content: {
DEFAULT: "var(--text-primary)",
muted: "var(--text-secondary)",
},
// Accent / brand
brand: {
DEFAULT: "var(--accent)",
muted: "var(--accent-muted)",
},
// Borders / hairlines
line: {
DEFAULT: "var(--border)",
strong: "var(--border-strong)",
},
// Status colors (theme-aware via vars, fall back to fixed)
success: "var(--status-success)",
warning: "var(--status-warning)",
danger: "var(--status-danger)",
info: "var(--status-info)",
},
borderRadius: {
xl: "0.875rem",
"2xl": "1.25rem",
},
boxShadow: {
card: "var(--shadow-card)",
"card-hover": "var(--shadow-card-hover)",
pop: "var(--shadow-pop)",
bar: "var(--shadow-bar)",
},
backgroundImage: {
"wood-light": "url('/static/textures/wood-light.png')",
"wood-dark": "url('/static/textures/wood-dark.png')",
"wood-mahogany": "url('/static/textures/wood-mahogany.png')",
},
typography: ({ theme }) => ({
invert: {
css: {
"--tw-prose-pre-bg": "var(--bg-secondary)",
code: {
backgroundColor: "var(--bg-primary)",
color: "var(--text-primary)",
fontWeight: "400",
},
"code::before": {
content: '""',
},
"code::after": {
content: '""',
},
"pre code": {
backgroundColor: "transparent",
color: "inherit",
},
pre: {
backgroundColor: "var(--bg-secondary)",
color: "var(--text-primary)",
},
},
},
}),
},
},
plugins: [require("@tailwindcss/forms"), require("@tailwindcss/typography")],
};
export default config;