mirror of
https://github.com/xpltdco/media-rip.git
synced 2026-04-03 02:53:58 -06:00
6 new themes + grouped theme picker dropdown
Dark themes: - Midnight: ultra-minimal, near-black, zero effects - Hacker: green-on-black terminal, monospace, CRT scanlines - Neon: hot pink + cyan on purple-black, synthwave, heavy glow Light themes: - Paper: warm cream/sepia, serif fonts, book-like - Arctic: cool whites and icy blues, crisp and modern - Solarized: Ethan Schoonover's solarized-light palette Theme picker: - Replaced simple dark/light toggle with grouped dropdown - Themes organized by Dark / Light sections with active checkmark - Remembers last dark and light theme separately for quick toggle - Theme metadata now includes variant field for proper grouping - Custom themes default to dark variant
This commit is contained in:
parent
4b766bb0e7
commit
2a8f59fecc
10 changed files with 424 additions and 36 deletions
|
|
@ -1,37 +1,88 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
import { useThemeStore } from '@/stores/theme'
|
import { useThemeStore } from '@/stores/theme'
|
||||||
|
|
||||||
const theme = useThemeStore()
|
const theme = useThemeStore()
|
||||||
|
const showPicker = ref(false)
|
||||||
|
|
||||||
|
function selectTheme(id: string) {
|
||||||
|
theme.setTheme(id)
|
||||||
|
showPicker.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePicker() {
|
||||||
|
showPicker.value = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<div class="theme-picker-wrapper" @mouseleave="closePicker">
|
||||||
class="dark-mode-toggle"
|
<button
|
||||||
:title="theme.isDark ? 'Switch to light mode' : 'Switch to dark mode'"
|
class="theme-toggle-btn"
|
||||||
@click="theme.toggleDarkMode()"
|
:title="'Theme: ' + (theme.currentMeta?.name || theme.currentTheme)"
|
||||||
aria-label="Toggle dark/light mode"
|
@click="showPicker = !showPicker"
|
||||||
>
|
aria-label="Theme picker"
|
||||||
<!-- Sun icon (shown in dark mode — click to go light) -->
|
>
|
||||||
<svg v-if="theme.isDark" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<!-- Sun icon (dark mode active) -->
|
||||||
<circle cx="12" cy="12" r="5"/>
|
<svg v-if="theme.isDark" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<line x1="12" y1="1" x2="12" y2="3"/>
|
<circle cx="12" cy="12" r="5"/>
|
||||||
<line x1="12" y1="21" x2="12" y2="23"/>
|
<line x1="12" y1="1" x2="12" y2="3"/>
|
||||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
<line x1="12" y1="21" x2="12" y2="23"/>
|
||||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
||||||
<line x1="1" y1="12" x2="3" y2="12"/>
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||||||
<line x1="21" y1="12" x2="23" y2="12"/>
|
<line x1="1" y1="12" x2="3" y2="12"/>
|
||||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
<line x1="21" y1="12" x2="23" y2="12"/>
|
||||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
||||||
</svg>
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||||||
<!-- Moon icon (shown in light mode — click to go dark) -->
|
</svg>
|
||||||
<svg v-else xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<!-- Moon icon (light mode active) -->
|
||||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
<svg v-else xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
</svg>
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||||
</button>
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Transition name="fade">
|
||||||
|
<div v-if="showPicker" class="theme-dropdown">
|
||||||
|
<div class="theme-group">
|
||||||
|
<div class="theme-group-label">Dark</div>
|
||||||
|
<button
|
||||||
|
v-for="t in theme.darkThemes"
|
||||||
|
:key="t.id"
|
||||||
|
class="theme-option"
|
||||||
|
:class="{ active: theme.currentTheme === t.id }"
|
||||||
|
@click="selectTheme(t.id)"
|
||||||
|
:title="t.description"
|
||||||
|
>
|
||||||
|
<span class="theme-name">{{ t.name }}</span>
|
||||||
|
<span v-if="theme.currentTheme === t.id" class="theme-check">✓</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="theme-divider"></div>
|
||||||
|
<div class="theme-group">
|
||||||
|
<div class="theme-group-label">Light</div>
|
||||||
|
<button
|
||||||
|
v-for="t in theme.lightThemes"
|
||||||
|
:key="t.id"
|
||||||
|
class="theme-option"
|
||||||
|
:class="{ active: theme.currentTheme === t.id }"
|
||||||
|
@click="selectTheme(t.id)"
|
||||||
|
:title="t.description"
|
||||||
|
>
|
||||||
|
<span class="theme-name">{{ t.name }}</span>
|
||||||
|
<span v-if="theme.currentTheme === t.id" class="theme-check">✓</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.dark-mode-toggle {
|
.theme-picker-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -46,7 +97,76 @@ const theme = useThemeStore()
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark-mode-toggle:hover {
|
.theme-toggle-btn:hover {
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.theme-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 4px;
|
||||||
|
background: var(--color-surface);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
min-width: 180px;
|
||||||
|
z-index: 100;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-group-label {
|
||||||
|
padding: 8px 14px 4px;
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-divider {
|
||||||
|
height: 1px;
|
||||||
|
background: var(--color-border);
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
font-family: var(--font-ui);
|
||||||
|
text-align: left;
|
||||||
|
transition: background var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option:hover {
|
||||||
|
background: var(--color-surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option.active {
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-check {
|
||||||
|
color: var(--color-accent);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ import './assets/base.css'
|
||||||
import './themes/cyberpunk.css'
|
import './themes/cyberpunk.css'
|
||||||
import './themes/dark.css'
|
import './themes/dark.css'
|
||||||
import './themes/light.css'
|
import './themes/light.css'
|
||||||
|
import './themes/midnight.css'
|
||||||
|
import './themes/hacker.css'
|
||||||
|
import './themes/neon.css'
|
||||||
|
import './themes/paper.css'
|
||||||
|
import './themes/arctic.css'
|
||||||
|
import './themes/solarized.css'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,24 @@ export interface ThemeMeta {
|
||||||
author?: string
|
author?: string
|
||||||
description?: string
|
description?: string
|
||||||
builtin: boolean
|
builtin: boolean
|
||||||
|
variant: 'dark' | 'light'
|
||||||
}
|
}
|
||||||
|
|
||||||
const STORAGE_KEY = 'mrip-theme'
|
const STORAGE_KEY = 'mrip-theme'
|
||||||
const DEFAULT_THEME = 'cyberpunk'
|
const DEFAULT_THEME = 'cyberpunk'
|
||||||
|
|
||||||
const BUILTIN_THEMES: ThemeMeta[] = [
|
const BUILTIN_THEMES: ThemeMeta[] = [
|
||||||
{ id: 'cyberpunk', name: 'Cyberpunk', author: 'media.rip()', description: 'Electric blue + orange, scanlines, grid overlay', builtin: true },
|
// Dark themes
|
||||||
{ id: 'dark', name: 'Dark', author: 'media.rip()', description: 'Clean neutral dark theme', builtin: true },
|
{ id: 'cyberpunk', name: 'Cyberpunk', author: 'media.rip()', description: 'Electric blue + orange, scanlines, grid overlay', builtin: true, variant: 'dark' },
|
||||||
{ id: 'light', name: 'Light', author: 'media.rip()', description: 'Clean light theme for daylight use', builtin: true },
|
{ id: 'dark', name: 'Dark', author: 'media.rip()', description: 'Clean neutral dark theme', builtin: true, variant: 'dark' },
|
||||||
|
{ id: 'midnight', name: 'Midnight', author: 'media.rip()', description: 'Ultra-minimal, near-black, zero effects', builtin: true, variant: 'dark' },
|
||||||
|
{ id: 'hacker', name: 'Hacker', author: 'media.rip()', description: 'Green-on-black terminal aesthetic', builtin: true, variant: 'dark' },
|
||||||
|
{ id: 'neon', name: 'Neon', author: 'media.rip()', description: 'Hot pink + cyan on deep purple, synthwave vibes', builtin: true, variant: 'dark' },
|
||||||
|
// Light themes
|
||||||
|
{ id: 'light', name: 'Light', author: 'media.rip()', description: 'Clean light theme for daylight use', builtin: true, variant: 'light' },
|
||||||
|
{ id: 'paper', name: 'Paper', author: 'media.rip()', description: 'Warm cream and sepia, book-like', builtin: true, variant: 'light' },
|
||||||
|
{ id: 'arctic', name: 'Arctic', author: 'media.rip()', description: 'Cool whites and icy blues, crisp and sharp', builtin: true, variant: 'light' },
|
||||||
|
{ id: 'solarized', name: 'Solarized', author: 'media.rip()', description: 'Solarized Light — easy on the eyes', builtin: true, variant: 'light' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export const useThemeStore = defineStore('theme', () => {
|
export const useThemeStore = defineStore('theme', () => {
|
||||||
|
|
@ -33,8 +42,14 @@ export const useThemeStore = defineStore('theme', () => {
|
||||||
const customThemes = ref<ThemeMeta[]>([])
|
const customThemes = ref<ThemeMeta[]>([])
|
||||||
const customThemeCSS = ref<Map<string, string>>(new Map())
|
const customThemeCSS = ref<Map<string, string>>(new Map())
|
||||||
|
|
||||||
/** Whether the current theme is a dark variant (cyberpunk and dark are dark; light is light). */
|
/** Whether the current theme is a dark variant. */
|
||||||
const isDark = computed(() => currentTheme.value !== 'light')
|
const isDark = computed(() => {
|
||||||
|
const meta = allThemes.value.find(t => t.id === currentTheme.value)
|
||||||
|
return meta ? meta.variant === 'dark' : true
|
||||||
|
})
|
||||||
|
|
||||||
|
const darkThemes = computed(() => allThemes.value.filter(t => t.variant === 'dark'))
|
||||||
|
const lightThemes = computed(() => allThemes.value.filter(t => t.variant === 'light'))
|
||||||
|
|
||||||
const allThemes = computed<ThemeMeta[]>(() => [
|
const allThemes = computed<ThemeMeta[]>(() => [
|
||||||
...BUILTIN_THEMES,
|
...BUILTIN_THEMES,
|
||||||
|
|
@ -64,11 +79,13 @@ export const useThemeStore = defineStore('theme', () => {
|
||||||
*/
|
*/
|
||||||
function toggleDarkMode(): void {
|
function toggleDarkMode(): void {
|
||||||
if (isDark.value) {
|
if (isDark.value) {
|
||||||
setTheme('light')
|
// Switch to last used light theme, or first available
|
||||||
|
const lastLight = localStorage.getItem(STORAGE_KEY + '-light') || 'light'
|
||||||
|
setTheme(lastLight)
|
||||||
} else {
|
} else {
|
||||||
// Return to the last dark theme, defaulting to cyberpunk
|
// Return to the last dark theme, defaulting to cyberpunk
|
||||||
const lastDark = localStorage.getItem(STORAGE_KEY + '-dark') || DEFAULT_THEME
|
const lastDark = localStorage.getItem(STORAGE_KEY + '-dark') || DEFAULT_THEME
|
||||||
setTheme(lastDark === 'light' ? DEFAULT_THEME : lastDark)
|
setTheme(lastDark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,8 +99,11 @@ export const useThemeStore = defineStore('theme', () => {
|
||||||
currentTheme.value = themeId
|
currentTheme.value = themeId
|
||||||
localStorage.setItem(STORAGE_KEY, themeId)
|
localStorage.setItem(STORAGE_KEY, themeId)
|
||||||
// Remember the last dark theme for toggle
|
// Remember the last dark theme for toggle
|
||||||
if (themeId !== 'light') {
|
const meta = allThemes.value.find(t => t.id === themeId)
|
||||||
|
if (meta?.variant === 'dark') {
|
||||||
localStorage.setItem(STORAGE_KEY + '-dark', themeId)
|
localStorage.setItem(STORAGE_KEY + '-dark', themeId)
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(STORAGE_KEY + '-light', themeId)
|
||||||
}
|
}
|
||||||
_apply(themeId)
|
_apply(themeId)
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +124,7 @@ export const useThemeStore = defineStore('theme', () => {
|
||||||
author: t.author,
|
author: t.author,
|
||||||
description: t.description,
|
description: t.description,
|
||||||
builtin: false,
|
builtin: false,
|
||||||
|
variant: t.variant || 'dark', // default custom themes to dark
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// If saved theme is a custom theme, validate it still exists
|
// If saved theme is a custom theme, validate it still exists
|
||||||
|
|
@ -159,6 +180,8 @@ export const useThemeStore = defineStore('theme', () => {
|
||||||
currentTheme,
|
currentTheme,
|
||||||
customThemes,
|
customThemes,
|
||||||
allThemes,
|
allThemes,
|
||||||
|
darkThemes,
|
||||||
|
lightThemes,
|
||||||
currentMeta,
|
currentMeta,
|
||||||
isDark,
|
isDark,
|
||||||
init,
|
init,
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,13 @@ describe('theme store', () => {
|
||||||
expect(store.currentTheme).toBe('cyberpunk')
|
expect(store.currentTheme).toBe('cyberpunk')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('lists 3 built-in themes', () => {
|
it('lists 9 built-in themes', () => {
|
||||||
const store = useThemeStore()
|
const store = useThemeStore()
|
||||||
expect(store.allThemes).toHaveLength(3)
|
expect(store.allThemes).toHaveLength(9)
|
||||||
expect(store.allThemes.map(t => t.id)).toEqual(['cyberpunk', 'dark', 'light'])
|
expect(store.allThemes.map(t => t.id)).toEqual([
|
||||||
|
'cyberpunk', 'dark', 'midnight', 'hacker', 'neon',
|
||||||
|
'light', 'paper', 'arctic', 'solarized',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('all built-in themes are marked builtin: true', () => {
|
it('all built-in themes are marked builtin: true', () => {
|
||||||
|
|
|
||||||
35
frontend/src/themes/arctic.css
Normal file
35
frontend/src/themes/arctic.css
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* media.rip() — Arctic Theme
|
||||||
|
*
|
||||||
|
* Cool whites, icy blues, crisp edges.
|
||||||
|
* Modern, clean, and sharp — like fresh snow.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root[data-theme="arctic"] {
|
||||||
|
--color-bg: #f0f4f8;
|
||||||
|
--color-surface: #ffffff;
|
||||||
|
--color-surface-hover: #e8eef4;
|
||||||
|
--color-border: #c8d6e5;
|
||||||
|
|
||||||
|
--color-text: #1a2a3a;
|
||||||
|
--color-text-muted: #5a7a94;
|
||||||
|
|
||||||
|
--color-accent: #0088cc;
|
||||||
|
--color-accent-hover: #006da6;
|
||||||
|
--color-accent-secondary: #0066aa;
|
||||||
|
|
||||||
|
--color-success: #00a878;
|
||||||
|
--color-warning: #e8a317;
|
||||||
|
--color-error: #d63031;
|
||||||
|
|
||||||
|
--font-display: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
|
||||||
|
--effect-scanlines: none;
|
||||||
|
--effect-grid: none;
|
||||||
|
--effect-grid-size: 0px 0px;
|
||||||
|
--effect-glow: none;
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 3px rgba(26, 42, 58, 0.06);
|
||||||
|
--shadow-md: 0 4px 12px rgba(26, 42, 58, 0.08);
|
||||||
|
--shadow-lg: 0 8px 24px rgba(26, 42, 58, 0.12);
|
||||||
|
--shadow-glow: none;
|
||||||
|
}
|
||||||
43
frontend/src/themes/hacker.css
Normal file
43
frontend/src/themes/hacker.css
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/* media.rip() — Hacker Theme
|
||||||
|
*
|
||||||
|
* Green-on-black terminal aesthetic. Monospace everything,
|
||||||
|
* phosphor glow, CRT scanlines. You're in the Matrix.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root[data-theme="hacker"] {
|
||||||
|
--color-bg: #0a0a0a;
|
||||||
|
--color-surface: #0f1a0f;
|
||||||
|
--color-surface-hover: #152215;
|
||||||
|
--color-border: #1a3a1a;
|
||||||
|
|
||||||
|
--color-text: #33ff33;
|
||||||
|
--color-text-muted: #1a8c1a;
|
||||||
|
|
||||||
|
--color-accent: #00ff41;
|
||||||
|
--color-accent-hover: #33ff66;
|
||||||
|
--color-accent-secondary: #ffcc00;
|
||||||
|
|
||||||
|
--color-success: #00ff41;
|
||||||
|
--color-warning: #ffcc00;
|
||||||
|
--color-error: #ff3333;
|
||||||
|
|
||||||
|
--font-ui: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Courier New', monospace;
|
||||||
|
--font-mono: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Courier New', monospace;
|
||||||
|
--font-display: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Courier New', monospace;
|
||||||
|
|
||||||
|
--effect-scanlines: repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent,
|
||||||
|
transparent 1px,
|
||||||
|
rgba(0, 255, 65, 0.03) 1px,
|
||||||
|
rgba(0, 255, 65, 0.03) 2px
|
||||||
|
);
|
||||||
|
--effect-grid: none;
|
||||||
|
--effect-grid-size: 0px 0px;
|
||||||
|
--effect-glow: 0 0 12px rgba(0, 255, 65, 0.2);
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
|
||||||
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||||
|
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.6);
|
||||||
|
--shadow-glow: 0 0 15px rgba(0, 255, 65, 0.12);
|
||||||
|
}
|
||||||
40
frontend/src/themes/midnight.css
Normal file
40
frontend/src/themes/midnight.css
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* media.rip() — Midnight Theme
|
||||||
|
*
|
||||||
|
* Ultra-minimal dark theme. Near-black backgrounds,
|
||||||
|
* muted steel blue accents, zero visual effects.
|
||||||
|
* For people who want the UI to disappear.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root[data-theme="midnight"] {
|
||||||
|
--color-bg: #060608;
|
||||||
|
--color-surface: #0e0e12;
|
||||||
|
--color-surface-hover: #16161c;
|
||||||
|
--color-border: #1c1c24;
|
||||||
|
|
||||||
|
--color-text: #c8ccd0;
|
||||||
|
--color-text-muted: #5c6370;
|
||||||
|
|
||||||
|
--color-accent: #6b8aaf;
|
||||||
|
--color-accent-hover: #8aa4c4;
|
||||||
|
--color-accent-secondary: #af6b8a;
|
||||||
|
|
||||||
|
--color-success: #5faa7c;
|
||||||
|
--color-warning: #c4a35a;
|
||||||
|
--color-error: #c45a5a;
|
||||||
|
|
||||||
|
--font-display: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
|
||||||
|
--effect-scanlines: none;
|
||||||
|
--effect-grid: none;
|
||||||
|
--effect-grid-size: 0px 0px;
|
||||||
|
--effect-glow: none;
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||||
|
--shadow-md: 0 4px 8px rgba(0, 0, 0, 0.6);
|
||||||
|
--shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.7);
|
||||||
|
--shadow-glow: none;
|
||||||
|
|
||||||
|
--radius-sm: 3px;
|
||||||
|
--radius-md: 6px;
|
||||||
|
--radius-lg: 8px;
|
||||||
|
}
|
||||||
42
frontend/src/themes/neon.css
Normal file
42
frontend/src/themes/neon.css
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* media.rip() — Neon Theme
|
||||||
|
*
|
||||||
|
* Hot pink + cyan on deep purple-black. Vibrant, edgy,
|
||||||
|
* synthwave-inspired. Glow effects cranked up.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root[data-theme="neon"] {
|
||||||
|
--color-bg: #0d0014;
|
||||||
|
--color-surface: #150022;
|
||||||
|
--color-surface-hover: #1e0033;
|
||||||
|
--color-border: #2a0044;
|
||||||
|
|
||||||
|
--color-text: #f0e0ff;
|
||||||
|
--color-text-muted: #9966bb;
|
||||||
|
|
||||||
|
--color-accent: #ff2d95;
|
||||||
|
--color-accent-hover: #ff5cae;
|
||||||
|
--color-accent-secondary: #00e5ff;
|
||||||
|
|
||||||
|
--color-success: #00e676;
|
||||||
|
--color-warning: #ffab00;
|
||||||
|
--color-error: #ff1744;
|
||||||
|
|
||||||
|
--font-display: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', monospace;
|
||||||
|
|
||||||
|
--effect-scanlines: repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent,
|
||||||
|
transparent 3px,
|
||||||
|
rgba(255, 45, 149, 0.04) 3px,
|
||||||
|
rgba(255, 45, 149, 0.04) 4px
|
||||||
|
);
|
||||||
|
--effect-grid: linear-gradient(rgba(0, 229, 255, 0.02) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(0, 229, 255, 0.02) 1px, transparent 1px);
|
||||||
|
--effect-grid-size: 40px 40px;
|
||||||
|
--effect-glow: 0 0 25px rgba(255, 45, 149, 0.2);
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
|
||||||
|
--shadow-md: 0 4px 12px rgba(13, 0, 20, 0.6);
|
||||||
|
--shadow-lg: 0 8px 24px rgba(13, 0, 20, 0.8);
|
||||||
|
--shadow-glow: 0 0 20px rgba(255, 45, 149, 0.15);
|
||||||
|
}
|
||||||
40
frontend/src/themes/paper.css
Normal file
40
frontend/src/themes/paper.css
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* media.rip() — Paper Theme
|
||||||
|
*
|
||||||
|
* Warm cream and sepia tones. Book-like reading feel.
|
||||||
|
* Soft, easy on the eyes in bright environments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root[data-theme="paper"] {
|
||||||
|
--color-bg: #f5f0e8;
|
||||||
|
--color-surface: #fffdf7;
|
||||||
|
--color-surface-hover: #f0ebe0;
|
||||||
|
--color-border: #d6cebf;
|
||||||
|
|
||||||
|
--color-text: #2c2416;
|
||||||
|
--color-text-muted: #7a705e;
|
||||||
|
|
||||||
|
--color-accent: #b85c38;
|
||||||
|
--color-accent-hover: #a04e2e;
|
||||||
|
--color-accent-secondary: #2e6b5e;
|
||||||
|
|
||||||
|
--color-success: #3a7d44;
|
||||||
|
--color-warning: #c48a1a;
|
||||||
|
--color-error: #b83838;
|
||||||
|
|
||||||
|
--font-ui: 'Georgia', 'Palatino Linotype', 'Book Antiqua', serif;
|
||||||
|
--font-display: 'Georgia', 'Palatino Linotype', 'Book Antiqua', serif;
|
||||||
|
|
||||||
|
--effect-scanlines: none;
|
||||||
|
--effect-grid: none;
|
||||||
|
--effect-grid-size: 0px 0px;
|
||||||
|
--effect-glow: none;
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 2px rgba(44, 36, 22, 0.06);
|
||||||
|
--shadow-md: 0 3px 8px rgba(44, 36, 22, 0.08);
|
||||||
|
--shadow-lg: 0 8px 20px rgba(44, 36, 22, 0.1);
|
||||||
|
--shadow-glow: none;
|
||||||
|
|
||||||
|
--radius-sm: 3px;
|
||||||
|
--radius-md: 6px;
|
||||||
|
--radius-lg: 10px;
|
||||||
|
}
|
||||||
36
frontend/src/themes/solarized.css
Normal file
36
frontend/src/themes/solarized.css
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* media.rip() — Solarized Light Theme
|
||||||
|
*
|
||||||
|
* Based on Ethan Schoonover's Solarized palette.
|
||||||
|
* Developer favorite — designed for extended use
|
||||||
|
* with carefully selected contrast ratios.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root[data-theme="solarized"] {
|
||||||
|
--color-bg: #fdf6e3;
|
||||||
|
--color-surface: #eee8d5;
|
||||||
|
--color-surface-hover: #e8e1cc;
|
||||||
|
--color-border: #d3cbaf;
|
||||||
|
|
||||||
|
--color-text: #586e75;
|
||||||
|
--color-text-muted: #93a1a1;
|
||||||
|
|
||||||
|
--color-accent: #268bd2;
|
||||||
|
--color-accent-hover: #1a6da3;
|
||||||
|
--color-accent-secondary: #d33682;
|
||||||
|
|
||||||
|
--color-success: #859900;
|
||||||
|
--color-warning: #b58900;
|
||||||
|
--color-error: #dc322f;
|
||||||
|
|
||||||
|
--font-display: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', monospace;
|
||||||
|
|
||||||
|
--effect-scanlines: none;
|
||||||
|
--effect-grid: none;
|
||||||
|
--effect-grid-size: 0px 0px;
|
||||||
|
--effect-glow: none;
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 2px rgba(88, 110, 117, 0.08);
|
||||||
|
--shadow-md: 0 3px 8px rgba(88, 110, 117, 0.1);
|
||||||
|
--shadow-lg: 0 8px 20px rgba(88, 110, 117, 0.12);
|
||||||
|
--shadow-glow: none;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue