Files
bookhoard/tailwind.config.ts
T
John O'Keefe 84cd6328f4 feat(docs): standalone docs site chrome + category/subcategory navigation
Docs pages no longer embed the app Header (whose hx-post login form silently
degraded to a native GET, leaking credentials into the URL — this page never
loaded htmx). New docs-owned topbar: search, theme picker, Open App; anonymous
visitors get a plain /login link. Optional-auth middleware (hand-rolled —
echojwt's ErrorHandler return ends the chain, producing empty 200s) populates
the user for signed-in visitors.

Navigation rebuilt from the docs directory tree: 6 sections, subcategory
groups, server-rendered expansion and active states. Search results are a
templ partial (/docs/search/partial); code highlighting is server-side chroma
(previously dead hljs CSS); prose is theme-aware via typography DEFAULT vars
(prose-invert painted unreadable text under light themes). /docs renders the
index directly; standalone 404 without the tailwind CDN.

Tests: navigation routing/expansion (internal/docs), render pins asserting no
login form/password field/app sidebar ever return on docs pages.
lint-rendering: exempt *.test.ts fixtures (jsdom DOM building); docs.ts
baseline 5 -> 1.
2026-10-24 14:31:36 -04:00

171 lines
5.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 }) => ({
// Docs pages use plain `prose`; bind its palette to the theme
// variables so markdown renders correctly under dark AND light
// themes (the old layout hardcoded prose-invert, which painted
// light-on-dark text even under light themes).
DEFAULT: {
css: {
"--tw-prose-body": "var(--text-primary)",
"--tw-prose-headings": "var(--text-primary)",
"--tw-prose-lead": "var(--text-secondary)",
"--tw-prose-links": "var(--accent)",
"--tw-prose-bold": "var(--text-primary)",
"--tw-prose-counters": "var(--text-secondary)",
"--tw-prose-bullets": "var(--accent)",
"--tw-prose-hr": "var(--border)",
"--tw-prose-quotes": "var(--text-secondary)",
"--tw-prose-quote-borders": "var(--accent)",
"--tw-prose-captions": "var(--text-secondary)",
"--tw-prose-code": "var(--text-primary)",
"--tw-prose-pre-code": "var(--text-primary)",
"--tw-prose-pre-bg": "var(--bg-secondary)",
"--tw-prose-th-borders": "var(--border)",
"--tw-prose-td-borders": "var(--border)",
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;