Files
grimoire/src/styles/prism-theme.css
Alejandro f464c68bde feat: theme selector (#95)
* feat: Add reusable theme system with Plan 9 proof of concept

Implement a comprehensive theme system that:
- Defines typed Theme interface with colors, syntax highlighting, scrollbar, and gradient variables
- Creates ThemeProvider with React context for runtime theme switching
- Persists theme selection to localStorage
- Includes 3 built-in themes: dark (default), light, and plan9

Theme structure supports:
- Core UI colors (background, foreground, primary, secondary, accent, etc.)
- Status colors (success, warning, info) replacing hardcoded Tailwind colors
- Syntax highlighting variables for code blocks
- Diff highlighting colors (inserted, deleted, meta)
- Scrollbar styling variables
- Gradient colors for branding

Technical changes:
- Update CSS to use new theme variables throughout
- Update prism-theme.css to use syntax variables instead of hardcoded values
- Remove chart colors (unused)
- Add success/warning/info to tailwind.config.js
- Wire up ThemeProvider in main.tsx

For Nostr publishing (future):
- d tag: "grimoire-theme"
- name tag: theme display name

* feat: Add theme selector to user menu, remove configurable border radius

- Remove border radius from theme configuration (borders are always square)
- Add theme selector dropdown to user menu (available to all users)
- Theme selector shows active theme indicator
- Theme selection persists via localStorage

* fix: Improve theme contrast and persistence

- Fix theme persistence: properly check localStorage before using default
- Plan9: make blue subtler (reduce saturation), darken gradient colors
  for better contrast on pale yellow background
- Light theme: improve contrast with darker muted foreground and borders
- Change theme selector from flat list to dropdown submenu

* fix: Replace Plan9 yellow accent with purple, add zap/live theme colors

- Replace Plan9's bright yellow accent with purple (good contrast on pale yellow)
- Add zap and live colors to theme system (used by ZapReceiptRenderer, StatusBadge)
- Make light theme gradient orange darker for better contrast
- Update ZapReceiptRenderer to use theme zap color instead of hardcoded yellow-500
- Update StatusBadge to use theme live color instead of hardcoded red-600
- Add CSS variables and Tailwind utilities for zap/live colors

* fix: Make gradient orange darker, theme status colors

- Make gradient orange darker in light and plan9 themes for better contrast
- Make req viewer status colors themeable:
  - loading/connecting → text-warning
  - live/receiving → text-success
  - error/failed → text-destructive
  - eose → text-info
- Update relay status icons to use theme colors
- Update tests to expect theme color classes

* fix: Use themeable zap color for active user names

- Replace hardcoded text-orange-400 with text-zap in UserName component
- Replace hardcoded text-orange-400 with text-zap in SpellRenderer ($me placeholder)
- Now uses dark amber/gold with proper contrast on light/plan9 themes

* feat: Add highlight theme color for active user display

Add dedicated 'highlight' color to theme system for displaying the
logged-in user's name, replacing the use of 'zap' color which felt
semantically incorrect. The highlight color is optimized for contrast
on each theme's background.

- Add highlight to ThemeColors interface and apply.ts
- Add --highlight CSS variable to index.css (light and dark)
- Add highlight to tailwind.config.js
- Configure appropriate highlight values for dark, light, and plan9 themes
- Update UserName.tsx to use text-highlight for active account
- Update SpellRenderer.tsx MePlaceholder to use text-highlight

* fix: Restore original orange-400 highlight color for dark theme

Update dark theme highlight to match original text-orange-400 color
(27 96% 61%) for backward compatibility with existing appearance.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-14 19:24:37 +01:00

145 lines
2.6 KiB
CSS

/* Grimoire Prism Theme - Uses CSS theme variables */
code[class*="language-"],
pre[class*="language-"] {
color: hsl(var(--foreground));
background: none;
text-shadow: none;
font-family: "Oxygen Mono", monospace;
font-size: 0.75rem;
line-height: 1.5;
white-space: pre;
word-spacing: normal;
word-break: normal;
tab-size: 4;
hyphens: none;
}
/* Diff-specific tokens */
/* Deleted lines (red) - subtle background, no strikethrough */
.token.deleted {
color: hsl(var(--diff-deleted));
background: hsl(var(--diff-deleted-bg));
display: block;
margin: 0 -1rem;
padding: 0 1rem;
}
/* Added lines (green) - subtle background */
.token.inserted {
color: hsl(var(--diff-inserted));
background: hsl(var(--diff-inserted-bg));
display: block;
margin: 0 -1rem;
padding: 0 1rem;
}
/* Hunk headers (@@ -1,5 +1,7 @@) - cyan/blue */
.token.diff.coord,
.token.coord {
color: hsl(var(--diff-meta));
background: hsl(var(--diff-meta-bg));
display: block;
margin: 0 -1rem;
padding: 0 1rem;
font-weight: 600;
font-style: normal;
}
/* File headers (diff --git, ---, +++) */
.token.diff.range,
.token.prefix.unchanged,
.language-diff .token.unchanged {
color: hsl(var(--muted-foreground));
font-weight: normal;
}
/* Prefix characters (+/-) */
.language-diff .token.prefix {
font-weight: 700;
opacity: 0.7;
}
/* General syntax tokens */
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: hsl(var(--syntax-comment));
}
.token.punctuation {
color: hsl(var(--syntax-punctuation));
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol {
color: hsl(var(--syntax-property));
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin {
color: hsl(var(--syntax-string));
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: hsl(var(--syntax-operator));
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: hsl(var(--syntax-keyword));
}
.token.function,
.token.class-name {
color: hsl(var(--syntax-function));
font-weight: bold;
}
.token.regex,
.token.important,
.token.variable {
color: hsl(var(--syntax-variable));
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
/* Line highlighting */
pre[class*="language-"] > code {
display: block;
}
/* Optional: Line numbers support */
.line-numbers .line-numbers-rows {
border-right: 1px solid hsl(var(--border));
}
.line-numbers-rows > span:before {
color: hsl(var(--muted-foreground));
}