Neon theme: pure-black Matrix palette (CAJAFUERTE-inspired) alongside light/dark/system

This commit is contained in:
Avi 2026-08-24 12:14:39 -05:00
commit 74072b7d6d
7 changed files with 58 additions and 5 deletions

View file

@ -1,4 +1,4 @@
export type Theme = 'light' | 'dark' | 'system';
export type Theme = 'light' | 'dark' | 'system' | 'neon';
/** Lifecycle of the NIP-46 remote signer. */
export type SignerPhase = 'stopped' | 'connecting' | 'connected';

View file

@ -105,8 +105,12 @@ export function SettingsScreen() {
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="system">System</option>
<option value="neon">Neon</option>
</select>
<p className="hint">“System” follows your desktop's light or dark preference.</p>
<p className="hint">
“System” follows your desktop's light or dark preference. “Neon” is a pure-black
theme with pink and purple accents.
</p>
</div>
</div>
</section>

View file

@ -337,6 +337,10 @@ export function useApp(): AppContextValue {
/** Apply the requested theme (respecting system preference for `system`). */
export function applyTheme(theme: Theme): void {
if (theme === 'neon') {
document.documentElement.dataset.theme = 'neon';
return;
}
const prefersDark =
theme === 'dark' ||
(theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches);

View file

@ -58,6 +58,35 @@
--shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.6);
}
/* "Neon" — pure-black Matrix/Cyberpunk palette inspired by the CAJAFUERTE
Obsidian theme (MIT): black panels, deep-pink primary (#ff1493), purple
secondary (#8b00ff family), yellow highlights (#ffd700). */
:root[data-theme='neon'] {
--bg: #000000;
--surface: #0b0b0e;
--surface-2: #131318;
--surface-hover: #191921;
--border: #2b1224;
--border-strong: #4d1c3d;
--text: #f5f2f7;
--text-muted: #a89bb0;
--primary: #ff1493;
--primary-hover: #ff4fb0;
--primary-soft: #2b0a1e;
--on-primary: #ffffff;
--danger: #ff3355;
--danger-soft: #330810;
--warning: #ffd700;
--warning-soft: #332b03;
--success: #00e68a;
--success-soft: #04301f;
--info: #b388ff;
--info-soft: #1c1035;
--focus: #ff1493;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 8px 24px rgba(255, 20, 147, 0.07);
--shadow-modal: 0 12px 40px rgba(0, 0, 0, 0.85), 0 0 32px rgba(255, 20, 147, 0.12);
}
* {
box-sizing: border-box;
}
@ -312,7 +341,8 @@ a {
/* The artwork is black-on-white; flip it in dark themes so it stays black
bird on dark tile instead of a glaring white square. */
html[data-theme='dark'] .sidebar-logo img {
html[data-theme='dark'] .sidebar-logo img,
html[data-theme='neon'] .sidebar-logo img {
filter: invert(1);
}

View file

@ -29,6 +29,19 @@ describe('SettingsScreen', () => {
expect(document.documentElement.dataset.theme).toBe('dark');
});
it('applies the neon theme to the document', async () => {
const backend = createFakeBackend();
installFakeBackend(backend);
renderWithApp(<SettingsScreen />);
const themeSelect = await screen.findByLabelText('Theme');
fireEvent.change(themeSelect, { target: { value: 'neon' } });
await waitFor(() => {
expect(backend.state.settings.theme).toBe('neon');
});
expect(document.documentElement.dataset.theme).toBe('neon');
});
it('toggles publish confirmation and npub shortening', async () => {
const backend = createFakeBackend();
installFakeBackend(backend);