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.
171 lines
5.1 KiB
TypeScript
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;
|