feat(themes): 5 new dark theme presets — Cyberpunk/Ocean/Blood Red/Matrix/Midnight (P5-2)

Five complete vanilla-extract themes registered in useTheme (useThemes +
useThemeNames), each spreading darkThemeData so Success/Warning/Critical keep
their semantic colors and only Background/Surface/Primary/Secondary are
recolored. A code-review pass computed WCAG contrast for every theme; all body
and accent pairs clear AA except Midnight's Primary.OnMain which was 4.49:1 —
fixed by changing OnMain #0d1320 -> #000000 (5.07:1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 16:47:50 -04:00
parent de6cecaffc
commit 65e24bd446
2 changed files with 344 additions and 1 deletions
+49 -1
View File
@@ -2,10 +2,15 @@ import { lightTheme } from 'folds';
import { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { onDarkFontWeight, onLightFontWeight } from '../../config.css'; import { onDarkFontWeight, onLightFontWeight } from '../../config.css';
import { import {
bloodRedTheme,
butterTheme, butterTheme,
classicMatrixTheme,
cyberpunkTheme,
darkTheme, darkTheme,
lotusTerminalLightTheme, lotusTerminalLightTheme,
lotusTerminalTheme, lotusTerminalTheme,
midnightTheme,
oceanTheme,
silverTheme, silverTheme,
} from '../../colors.css'; } from '../../colors.css';
import { settingsAtom } from '../state/settings'; import { settingsAtom } from '../state/settings';
@@ -43,6 +48,31 @@ export const ButterTheme: Theme = {
kind: ThemeKind.Dark, kind: ThemeKind.Dark,
classNames: ['butter-theme', butterTheme, onDarkFontWeight, 'prism-dark'], classNames: ['butter-theme', butterTheme, onDarkFontWeight, 'prism-dark'],
}; };
export const CyberpunkTheme: Theme = {
id: 'cyberpunk-theme',
kind: ThemeKind.Dark,
classNames: ['cyberpunk-theme', cyberpunkTheme, onDarkFontWeight, 'prism-dark'],
};
export const OceanTheme: Theme = {
id: 'ocean-theme',
kind: ThemeKind.Dark,
classNames: ['ocean-theme', oceanTheme, onDarkFontWeight, 'prism-dark'],
};
export const BloodRedTheme: Theme = {
id: 'blood-red-theme',
kind: ThemeKind.Dark,
classNames: ['blood-red-theme', bloodRedTheme, onDarkFontWeight, 'prism-dark'],
};
export const ClassicMatrixTheme: Theme = {
id: 'classic-matrix-theme',
kind: ThemeKind.Dark,
classNames: ['classic-matrix-theme', classicMatrixTheme, onDarkFontWeight, 'prism-dark'],
};
export const MidnightTheme: Theme = {
id: 'midnight-theme',
kind: ThemeKind.Dark,
classNames: ['midnight-theme', midnightTheme, onDarkFontWeight, 'prism-dark'],
};
export const LotusTerminalTheme: Theme = { export const LotusTerminalTheme: Theme = {
id: 'lotus-terminal-theme', id: 'lotus-terminal-theme',
kind: ThemeKind.Dark, kind: ThemeKind.Dark,
@@ -60,7 +90,20 @@ export const LotusTerminalLightTheme: Theme = {
}; };
export const useThemes = (): Theme[] => { export const useThemes = (): Theme[] => {
const themes: Theme[] = useMemo(() => [LightTheme, SilverTheme, DarkTheme, ButterTheme], []); const themes: Theme[] = useMemo(
() => [
LightTheme,
SilverTheme,
DarkTheme,
ButterTheme,
CyberpunkTheme,
OceanTheme,
BloodRedTheme,
ClassicMatrixTheme,
MidnightTheme,
],
[],
);
return themes; return themes;
}; };
@@ -72,6 +115,11 @@ export const useThemeNames = (): Record<string, string> =>
[SilverTheme.id]: 'Silver', [SilverTheme.id]: 'Silver',
[DarkTheme.id]: 'Dark', [DarkTheme.id]: 'Dark',
[ButterTheme.id]: 'Butter', [ButterTheme.id]: 'Butter',
[CyberpunkTheme.id]: 'Cyberpunk',
[OceanTheme.id]: 'Ocean',
[BloodRedTheme.id]: 'Blood Red',
[ClassicMatrixTheme.id]: 'Classic Matrix',
[MidnightTheme.id]: 'Midnight',
}), }),
[], [],
); );
+295
View File
@@ -237,6 +237,301 @@ export const butterTheme = createTheme(color, {
}, },
}); });
export const cyberpunkTheme = createTheme(color, {
...darkThemeData,
Background: {
Container: '#0a0015',
ContainerHover: '#130722',
ContainerActive: '#1c0f30',
ContainerLine: '#26173d',
OnContainer: '#ECE6F5',
},
Surface: {
Container: '#130722',
ContainerHover: '#1c0f30',
ContainerActive: '#26173d',
ContainerLine: '#2f1f4a',
OnContainer: '#ECE6F5',
},
SurfaceVariant: {
Container: '#1c0f30',
ContainerHover: '#26173d',
ContainerActive: '#2f1f4a',
ContainerLine: '#392858',
OnContainer: '#ECE6F5',
},
Primary: {
Main: '#bf5fff',
MainHover: '#c873ff',
MainActive: '#cd7eff',
MainLine: '#d28aff',
OnMain: '#1a0033',
Container: '#3d1a5c',
ContainerHover: '#461e69',
ContainerActive: '#502276',
ContainerLine: '#592683',
OnContainer: '#EBD6FF',
},
Secondary: {
Main: '#ff2d9b',
MainHover: '#ff47a8',
MainActive: '#ff54af',
MainLine: '#ff61b6',
OnMain: '#33001a',
Container: '#5c0033',
ContainerHover: '#69003a',
ContainerActive: '#760041',
ContainerLine: '#830048',
OnContainer: '#FFD6EB',
},
Other: {
FocusRing: 'rgba(191, 95, 255, 0.5)',
Shadow: 'rgba(0, 0, 0, 1)',
Overlay: 'rgba(10, 0, 21, 0.9)',
},
});
export const oceanTheme = createTheme(color, {
...darkThemeData,
Background: {
Container: '#020b18',
ContainerHover: '#051426',
ContainerActive: '#091d34',
ContainerLine: '#0e2742',
OnContainer: '#DCEAF2',
},
Surface: {
Container: '#051426',
ContainerHover: '#091d34',
ContainerActive: '#0e2742',
ContainerLine: '#143150',
OnContainer: '#DCEAF2',
},
SurfaceVariant: {
Container: '#091d34',
ContainerHover: '#0e2742',
ContainerActive: '#143150',
ContainerLine: '#1a3b5e',
OnContainer: '#DCEAF2',
},
Primary: {
Main: '#00c9b1',
MainHover: '#1ad2bd',
MainActive: '#29d7c4',
MainLine: '#38dccb',
OnMain: '#00231f',
Container: '#004c43',
ContainerHover: '#00564c',
ContainerActive: '#006155',
ContainerLine: '#006b5e',
OnContainer: '#B3F0E8',
},
Secondary: {
Main: '#0096d6',
MainHover: '#1aa3dc',
MainActive: '#29aadf',
MainLine: '#38b1e2',
OnMain: '#001a26',
Container: '#003a52',
ContainerHover: '#00425e',
ContainerActive: '#004b6b',
ContainerLine: '#005377',
OnContainer: '#B3E2F5',
},
Other: {
FocusRing: 'rgba(0, 201, 177, 0.5)',
Shadow: 'rgba(0, 0, 0, 1)',
Overlay: 'rgba(2, 11, 24, 0.9)',
},
});
export const bloodRedTheme = createTheme(color, {
...darkThemeData,
Background: {
Container: '#0d0203',
ContainerHover: '#180608',
ContainerActive: '#240a0d',
ContainerLine: '#300e12',
OnContainer: '#F2DDDD',
},
Surface: {
Container: '#180608',
ContainerHover: '#240a0d',
ContainerActive: '#300e12',
ContainerLine: '#3c1318',
OnContainer: '#F2DDDD',
},
SurfaceVariant: {
Container: '#240a0d',
ContainerHover: '#300e12',
ContainerActive: '#3c1318',
ContainerLine: '#48181e',
OnContainer: '#F2DDDD',
},
Primary: {
Main: '#ff2233',
MainHover: '#ff3d4b',
MainActive: '#ff4a57',
MainLine: '#ff5763',
OnMain: '#330003',
Container: '#7a0010',
ContainerHover: '#8a0013',
ContainerActive: '#990015',
ContainerLine: '#a80018',
OnContainer: '#FFD1D6',
},
Secondary: {
Main: '#FFFFFF',
MainHover: '#E5E5E5',
MainActive: '#D9D9D9',
MainLine: '#CCCCCC',
OnMain: '#0d0203',
Container: '#3c1318',
ContainerHover: '#48181e',
ContainerActive: '#541d24',
ContainerLine: '#60222a',
OnContainer: '#F2DDDD',
},
Other: {
FocusRing: 'rgba(255, 34, 51, 0.5)',
Shadow: 'rgba(0, 0, 0, 1)',
Overlay: 'rgba(13, 2, 3, 0.9)',
},
});
export const classicMatrixTheme = createTheme(color, {
...darkThemeData,
Background: {
Container: '#000000',
ContainerHover: '#0a0f0a',
ContainerActive: '#121a12',
ContainerLine: '#1c281c',
OnContainer: '#C8E6C8',
},
Surface: {
Container: '#0a0f0a',
ContainerHover: '#121a12',
ContainerActive: '#1c281c',
ContainerLine: '#263626',
OnContainer: '#C8E6C8',
},
SurfaceVariant: {
Container: '#121a12',
ContainerHover: '#1c281c',
ContainerActive: '#263626',
ContainerLine: '#304530',
OnContainer: '#C8E6C8',
},
Primary: {
Main: '#00ff41',
MainHover: '#1aff57',
MainActive: '#29ff63',
MainLine: '#38ff6f',
OnMain: '#001a08',
Container: '#003311',
ContainerHover: '#003d14',
ContainerActive: '#004718',
ContainerLine: '#00521b',
OnContainer: '#9DFFB8',
},
Secondary: {
Main: '#C8E6C8',
MainHover: '#baddba',
MainActive: '#b0d6b0',
MainLine: '#a3cca3',
OnMain: '#000000',
Container: '#263626',
ContainerHover: '#304530',
ContainerActive: '#3a543a',
ContainerLine: '#446344',
OnContainer: '#DFF2DF',
},
Other: {
FocusRing: 'rgba(0, 255, 65, 0.5)',
Shadow: 'rgba(0, 0, 0, 1)',
Overlay: 'rgba(0, 0, 0, 0.9)',
},
});
export const midnightTheme = createTheme(color, {
...darkThemeData,
Background: {
Container: '#111827',
ContainerHover: '#1a2234',
ContainerActive: '#232d42',
ContainerLine: '#2c3850',
OnContainer: '#E5E9F0',
},
Surface: {
Container: '#1a2234',
ContainerHover: '#232d42',
ContainerActive: '#2c3850',
ContainerLine: '#35435e',
OnContainer: '#E5E9F0',
},
SurfaceVariant: {
Container: '#232d42',
ContainerHover: '#2c3850',
ContainerActive: '#35435e',
ContainerLine: '#3e4e6c',
OnContainer: '#E5E9F0',
},
Primary: {
Main: '#6b7ca8',
MainHover: '#7989b1',
MainActive: '#8493b8',
MainLine: '#8f9dbf',
OnMain: '#000000',
Container: '#2e3a55',
ContainerHover: '#354161',
ContainerActive: '#3c496d',
ContainerLine: '#435179',
OnContainer: '#D2DAEC',
},
Secondary: {
Main: '#E5E9F0',
MainHover: '#d4d9e3',
MainActive: '#c9cfdb',
MainLine: '#bdc4d3',
OnMain: '#111827',
Container: '#35435e',
ContainerHover: '#3e4e6c',
ContainerActive: '#47597a',
ContainerLine: '#506488',
OnContainer: '#E5E9F0',
},
Other: {
FocusRing: 'rgba(107, 124, 168, 0.5)',
Shadow: 'rgba(0, 0, 0, 1)',
Overlay: 'rgba(17, 24, 39, 0.9)',
},
});
export const lotusTerminalTheme = createTheme(color, { export const lotusTerminalTheme = createTheme(color, {
Background: { Background: {
Container: '#030508', Container: '#030508',