1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
# Creating a New Theme
Neko's style theme system layers an additional CSS file on top of the base
stylesheet. The base `style.css` defines CSS custom properties (variables) and
all component styles. A theme file overrides whichever of those it wants.
## Quick Start
1. Create a new CSS file in `frontend-vanilla/public/themes/yourtheme.css`
2. Register the theme name in `frontend-vanilla/src/main.ts` — add it to the
`STYLE_THEMES` array:
```ts
const STYLE_THEMES = ['default', 'refined', 'terminal', 'codex', 'sakura', 'yourtheme'] as const;
```
3. Rebuild: `cd frontend-vanilla && npm run build`
4. Copy output: `cp -r dist/* ../web/dist/v3/`
That's it. The theme will appear in Settings > Style and in the sidebar cycle
button.
## How it works
When a user picks a style theme, JavaScript inserts a `<link>` tag:
```html
<link id="style-theme-link" rel="stylesheet" href="/v3/themes/yourtheme.css">
```
This loads *after* the base stylesheet, so any rules you write will override
the defaults via normal CSS cascade (same specificity = last one wins). When
the user switches to "Default," the link tag is removed entirely.
The theme CSS files live in `public/themes/` so Vite copies them as-is into the
build output (no hashing, no bundling). They're plain CSS — no build step
required for the theme file itself.
## CSS Variables You Can Override
The base stylesheet defines these custom properties on `:root` (light mode)
and `.theme-dark` (dark mode):
```css
/* Colors */
--bg-color /* Page background */
--text-color /* Primary text */
--sidebar-bg /* Sidebar background */
--link-color /* Links and titles */
--border-color /* Borders, rules, dividers */
--accent-color /* Interactive accent (selection, focus) */
/* Fonts */
--font-body /* Body/article text (default: Palatino stack) */
--font-heading /* Dynamic heading font (user-selectable) */
--font-heading-system /* System heading font (Helvetica Neue stack) */
--font-sans /* Sans-serif stack (Inter/system-ui) */
--font-mono /* Monospace stack (Courier New) */
```
### Supporting both light and dark mode
Your theme should define colors for both modes:
```css
/* Light mode */
:root {
--bg-color: #faf8f4;
--text-color: #2c2c2c;
--link-color: #8b4513;
/* ... */
}
/* Dark mode */
.theme-dark {
--bg-color: #1c1a17;
--text-color: #d4cfc6;
--link-color: #c9956b;
/* ... */
}
```
The light/dark toggle adds/removes the `theme-dark` class on `#app`. Your
theme's `.theme-dark` rules will override your `:root` rules when active.
**Important:** The base stylesheet has some `.theme-dark .sidebar` rules that
set the sidebar to a grey background with dark text. If your theme wants a
different dark-mode sidebar, you'll need to override those specifically:
```css
.theme-dark .sidebar { background: ...; }
.theme-dark .sidebar-section li a { color: ...; }
.theme-dark .sidebar-section h3 { color: ...; }
.theme-dark .sidebar-footer a { color: ...; }
```
## Key Selectors Reference
### Layout
- `.sidebar` — Fixed sidebar (11rem wide)
- `.main-content` — Scrollable content area
- `.main-content > *` — Content max-width container (default: 35em)
### Feed items
- `.feed-item` — Individual article wrapper
- `.item-title` — Article title/link
- `.dateline` — Date + feed source line
- `.item-description` — Article body content
- `.star-btn` / `.star-btn.is-starred` / `.star-btn.is-unstarred` — Star toggle
- `.scrape-btn` — "text" button for full-content fetch
### Settings page
- `.settings-view` — Settings container
- `.settings-section` / `.settings-section h3` — Section blocks
- `.settings-group` / `.data-group` — Form groups
- `.theme-options` — Button row for theme/style selectors
- `.button-group` — Button row for export/import
- `.manage-feed-list` / `.manage-feed-item` — Feed management list
### Buttons
- `button` / `.button` — All buttons and button-styled links
- `button.active` — Currently selected option
### Sidebar details
- `.sidebar-section h3` — Section headers (FEEDS, TAGS)
- `.sidebar-section li a` — Feed/filter links
- `.sidebar-section li.active a` — Selected item
- `.sidebar-footer` — Bottom area (settings, logout, controls)
- `.sidebar-quick-controls` / `.sidebar-icon-btn` — Theme toggle icons
## Tips
- **Override fonts broadly.** Set `font-family` on `body` for article text and
on `.sidebar`, `.settings-view`, `button` for UI elements.
- **Use the cascade.** You don't need `!important` — your theme loads after the
base stylesheet. Just match or exceed the specificity of the base rule.
- **Keep it to overrides.** Don't redefine layout or structural properties
unless you have a reason. Focus on colors, typography, and spacing.
- **Test both modes.** Always check your theme in both light and dark.
- **Note on font overrides:** The user can independently select heading and body
fonts in Settings > Fonts. These apply CSS classes like `.font-sans`,
`.heading-font-serif`, etc. Your theme's font choices will be the "default"
that users see before changing font settings.
## Existing Themes
| Theme | Character |
|------------|-----------|
| `default` | No extra CSS — the base stylesheet as-is |
| `refined` | Tightened spacing, better typographic rhythm, polished details |
| `terminal` | Monospace, green phosphor accent, CRT scanlines in dark mode |
| `codex` | Book-inspired: warm cream paper, serif type, fleuron separators |
| `sakura` | Japanese aesthetic: restrained palette, muted rose accent, calm spacing |
|