feat(ui): vierte Farbwelt "Cyberpunk" in hell und dunkel
Neon in der Nacht: fast schwarze Flaechen mit blauem Stich, violettes Leuchten, Blaustahl statt Neutralgrau und Kanten von 2 px - scharf genug fuer HUD-Elemente, aber nicht eckig, das ist schon Pixel Art. Schatten sind hier kein Schatten, sondern ein Schein: farbige Kontur plus weicher Halo in der Markenfarbe. Schrift ist Chakra Petch, die Feldbeschriftungen laufen gesperrt. Der Akzent ist bewusst Violett und nicht Neon-Cyan: auf brand-500 und brand-600 steht 69 Mal weisse Schrift, und auf Cyan waere die nicht zu lesen. Cyan sitzt deshalb nur in --color-brand-accent und in den Statusfarben, wo dunkler Text darauf steht. Der helle Modus ist der Konzern bei Tageslicht: kuehles Blaugrau, dasselbe Violett nur dunkler, und aus dem Leuchten wird ein knapper kalter Schatten - ein Halo auf hellem Grund sieht nach Fehler aus, nicht nach Neon. Es waren vier Zeilen ausserhalb von index.css: die Whitelist in userFields.js, die Liste in src/types.ts mit ihrer Beschriftung, die Whitelist im Inline-Skript und die Schrift im Google-Fonts-Aufruf. Keine Komponente wurde angefasst. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
87759a8a24
commit
8403f1c39d
+1
-1
@@ -138,7 +138,7 @@ Die Darstellung hat **zwei unabhängige Achsen**, beide als Klasse am `<html>`:
|
||||
|
||||
| Achse | Klasse | Werte |
|
||||
| --- | --- | --- |
|
||||
| Farbwelt | `theme-perinco`, `theme-pixel`, `theme-outdoor` | perinco ist der Standard |
|
||||
| Farbwelt | `theme-perinco`, `theme-pixel`, `theme-outdoor`, `theme-cyberpunk` | perinco ist der Standard |
|
||||
| Modus | `dark` bzw. keine | hell, dunkel, oder der Systemeinstellung folgen |
|
||||
|
||||
`src/index.css` tauscht darin nur Variablenwerte: `canvas`, `surface`, `ink-*`,
|
||||
|
||||
@@ -66,7 +66,7 @@ die Scanner-Einstellungen. Alles andere ist für beide Rollen gleich.
|
||||
zusammen - hilfreich auf kleinen Bildschirmen.
|
||||
* Rechts oben stehen zwei Schalter. Der rechte wechselt zwischen **hell und
|
||||
dunkel**; ein **Doppelklick** darauf folgt wieder der Systemeinstellung.
|
||||
* Links daneben steht das **Design**. Es gibt drei zur Wahl, jedes in hell und
|
||||
* Links daneben steht das **Design**. Es gibt vier zur Wahl, jedes in hell und
|
||||
dunkel:
|
||||
|
||||
| Design | Aussehen |
|
||||
@@ -74,6 +74,7 @@ die Scanner-Einstellungen. Alles andere ist für beide Rollen gleich.
|
||||
| **perinco** | Das Design des Hauses: Blattgrün auf neutralem Grau |
|
||||
| **Pixel Art** | 8-bit: eckige Kanten, harte Schatten, Pixelschrift in den Überschriften |
|
||||
| **Outdoor** | Wanderkarte: Sandpapier, Bergsee-Türkis, weiche Ecken |
|
||||
| **Cyberpunk** | Neon in der Nacht: scharfe Kanten, violettes Leuchten, Blaustahl |
|
||||
|
||||
> **Die Wahl gehört dir, nicht dem Rechner.** Sie wird an deinem Benutzerkonto
|
||||
> gespeichert. Meldest du dich an einem anderen Rechner an, ist dein Design
|
||||
|
||||
@@ -222,8 +222,8 @@ Browser-Download.
|
||||
* **Kommentare erklären das Warum.** Was der Code tut, steht im Code.
|
||||
* **Kein `dark:` und keine Theme-Klasse im Markup.** Nur die Skalen `ink-*`,
|
||||
`brand-*`, `canvas`, `surface` benutzen, den Rest macht `index.css`. Das ist
|
||||
nicht Kosmetik: daran hängen drei Farbwelten in je zwei Modi, und eine feste
|
||||
Farbe im Markup bricht fünf davon. Die Rollen der Skalen und die Regeln für ein
|
||||
nicht Kosmetik: daran hängen vier Farbwelten in je zwei Modi, und eine feste
|
||||
Farbe im Markup bricht sieben davon. Die Rollen der Skalen und die Regeln für ein
|
||||
neues Theme stehen in [05 Frontend](05-frontend.md#themes-ohne-dark-varianten) -
|
||||
wichtigste davon: auf `brand-500`/`brand-600` steht weisse Schrift.
|
||||
* **Gemeinsame Klassen** statt kopierter Utility-Ketten: `inputClass`,
|
||||
|
||||
@@ -247,7 +247,7 @@ Uhrzeit, dieses Jahr Tag und Monat, sonst mit Jahr.
|
||||
| Datei | Hält | Eigenheit |
|
||||
| --- | --- | --- |
|
||||
| `auth.tsx` | Benutzer, `isAdmin`, `login`, `logout`, `updateUser` | prüft beim Start das gespeicherte Token gegen `/auth/me` |
|
||||
| `theme.tsx` | Farbwelt (perinco/Pixel Art/Outdoor) und Modus (hell/dunkel/System) | Beides gehört dem Benutzer und steht in `crm_users`; `localStorage` ist nur der Zwischenspeicher fürs Inline-Skript in `index.html`. Doppelklick auf den Modus-Schalter folgt wieder dem System |
|
||||
| `theme.tsx` | Farbwelt (perinco/Pixel Art/Outdoor/Cyberpunk) und Modus (hell/dunkel/System) | Beides gehört dem Benutzer und steht in `crm_users`; `localStorage` ist nur der Zwischenspeicher fürs Inline-Skript in `index.html`. Doppelklick auf den Modus-Schalter folgt wieder dem System |
|
||||
| `navigation.tsx` | `currentView`, `goTo(view, { tab, leadId })` | erspart es, Callbacks durch vier Ebenen zu reichen; `leadId` öffnet den Kontakt direkt |
|
||||
| `whisper.tsx` | Modellstatus, Worker, Fortschritt, `transcribe` | ein Worker für die ganze App |
|
||||
|
||||
|
||||
+5
-5
@@ -26,7 +26,7 @@
|
||||
|
||||
var palette = localStorage.getItem('perinco_crm_palette');
|
||||
// Whitelist, damit ein manipulierter Eintrag keine Klasse setzt.
|
||||
if (palette === 'pixel' || palette === 'outdoor') {
|
||||
if (palette === 'pixel' || palette === 'outdoor' || palette === 'cyberpunk') {
|
||||
document.documentElement.classList.add('theme-' + palette);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -38,12 +38,12 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<!--
|
||||
Nunito Sans traegt perinco und Pixel Art (Fliesstext), Cabin das Outdoor-
|
||||
Design, Silkscreen die Ueberschriften von Pixel Art. Alle drei in einer
|
||||
Anfrage: ein Nachladen beim Umschalten wuerde die Schrift sichtbar
|
||||
springen lassen, und Silkscreen wiegt nur wenige Kilobyte.
|
||||
Design, Silkscreen die Ueberschriften von Pixel Art, Chakra Petch das
|
||||
Cyberpunk-Design. Alle in einer Anfrage: ein Nachladen beim Umschalten
|
||||
wuerde die Schrift sichtbar springen lassen.
|
||||
-->
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,300;0,6..12,400;0,6..12,600;0,6..12,700;0,6..12,800&family=Cabin:wght@400;500;600;700&family=Silkscreen:wght@400;700&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,300;0,6..12,400;0,6..12,600;0,6..12,700;0,6..12,800&family=Cabin:wght@400;500;600;700&family=Silkscreen:wght@400;700&family=Chakra+Petch:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
|
||||
@@ -48,7 +48,7 @@ export function normalizeAvatar(value) {
|
||||
* Standard gesetzt: sonst faellt ein Tippfehler im Frontend nie auf, die
|
||||
* Oberflaeche zeigt weiter das alte Design und niemand weiss, warum.
|
||||
*/
|
||||
export const THEMES = ['perinco', 'pixel', 'outdoor'];
|
||||
export const THEMES = ['perinco', 'pixel', 'outdoor', 'cyberpunk'];
|
||||
export const THEME_MODES = ['light', 'dark'];
|
||||
|
||||
/** Farbwelt; null bzw. '' bedeutet "perinco", der Standard. */
|
||||
|
||||
+145
-7
@@ -10,9 +10,9 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Drei Farbwelten, jede in hell und dunkel. Sie haengen an einer Klasse am
|
||||
* <html> (theme-pixel, theme-outdoor); ohne Klasse gilt perinco. Der Modus
|
||||
* bleibt die vorhandene Klasse "dark".
|
||||
* Vier Farbwelten, jede in hell und dunkel. Sie haengen an einer Klasse am
|
||||
* <html> (theme-pixel, theme-outdoor, theme-cyberpunk); ohne Klasse gilt
|
||||
* perinco. Der Modus bleibt die vorhandene Klasse "dark".
|
||||
*
|
||||
* Warum das ohne Aenderung an den Komponenten geht: im Markup steht nirgends
|
||||
* eine feste Farbe, sondern durchgaengig ink-*, brand-*, canvas und surface -
|
||||
@@ -36,10 +36,10 @@
|
||||
--font-sans: 'Avenir Next', 'Avenir', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif;
|
||||
|
||||
/*
|
||||
* Zweite Schrift fuer Ueberschriften und Feldbeschriftungen. Bei perinco und
|
||||
* Outdoor ist sie mit der Grundschrift identisch, nur "Pixel Art" setzt hier
|
||||
* eine Pixelschrift ein - ohne dass ein Theme dafuer eigene Selektoren
|
||||
* braucht.
|
||||
* Zweite Schrift fuer Ueberschriften und Feldbeschriftungen. Meist ist sie mit
|
||||
* der Grundschrift identisch; nur "Pixel Art" setzt hier eine andere ein - die
|
||||
* Pixelschrift bleibt damit aus dem Fliesstext heraus, ohne dass es dafuer
|
||||
* eigene Selektoren braucht.
|
||||
*/
|
||||
--font-display: 'Avenir Next', 'Avenir', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif;
|
||||
|
||||
@@ -358,6 +358,127 @@ html.theme-pixel.dark {
|
||||
--font-display: 'Silkscreen', 'Courier New', ui-monospace, monospace;
|
||||
}
|
||||
|
||||
/* ===========================================================================
|
||||
* Cyberpunk
|
||||
*
|
||||
* Neon in der Nacht: fast schwarze Flaechen mit blauem Stich, violettes
|
||||
* Leuchten, Kanten so scharf, dass die Karten wie HUD-Elemente wirken (2 px
|
||||
* statt 12 px Radius - nicht ganz eckig, das ist schon Pixel Art).
|
||||
*
|
||||
* Der Akzent ist bewusst Violett und nicht Neon-Cyan: auf brand-500 und
|
||||
* brand-600 steht 69 Mal weisse Schrift, und auf Cyan waere die nicht zu lesen.
|
||||
* Cyan sitzt deshalb nur in --color-brand-accent und in den Statusfarben, wo
|
||||
* dunkler Text drauf steht.
|
||||
*
|
||||
* Schatten sind hier kein Schatten, sondern ein Schein: eine farbige Kontur
|
||||
* plus weicher Halo in der Markenfarbe.
|
||||
* =========================================================================== */
|
||||
html.theme-cyberpunk.dark {
|
||||
--color-canvas: #07080f;
|
||||
--color-surface: #0f1120;
|
||||
|
||||
--color-brand-50: #1a0e2e;
|
||||
--color-brand-100: #24123f;
|
||||
--color-brand-200: #4a1f7a;
|
||||
--color-brand-300: #5f27a0;
|
||||
--color-brand-400: #7a30cc;
|
||||
--color-brand-500: #8b31f0;
|
||||
--color-brand-600: #a24cff;
|
||||
--color-brand-700: #c79bff;
|
||||
--color-brand-800: #ddc4ff;
|
||||
--color-brand-900: #efe3ff;
|
||||
--color-brand-accent: #00e5ff;
|
||||
|
||||
/* Blaustahl statt Neutralgrau - Grau waere hier zu brav. */
|
||||
--color-ink-50: #141728;
|
||||
--color-ink-100: #1c2135;
|
||||
--color-ink-200: #2b4560;
|
||||
--color-ink-300: #6b7899;
|
||||
--color-ink-400: #8e9bbd;
|
||||
--color-ink-500: #a9b5d4;
|
||||
--color-ink-600: #c1cbe4;
|
||||
--color-ink-700: #d6ddf0;
|
||||
--color-ink-800: #e9eefa;
|
||||
--color-ink-900: #f7f9ff;
|
||||
--color-ink-950: #000000;
|
||||
|
||||
--radius-sm: 0.125rem;
|
||||
--radius-md: 0.125rem;
|
||||
--radius-lg: 0.1875rem;
|
||||
--radius-xl: 0.25rem;
|
||||
--radius-2xl: 0.375rem;
|
||||
|
||||
--shadow-card: 0 0 0 1px rgb(139 49 240 / 0.16), 0 0 22px rgb(139 49 240 / 0.2);
|
||||
--shadow-lift: 0 0 0 1px rgb(139 49 240 / 0.3), 0 0 40px rgb(139 49 240 / 0.35);
|
||||
|
||||
--font-sans: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif;
|
||||
--font-display: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif;
|
||||
|
||||
/* Die getoenten Pillen aus .dark ins Neon gezogen. */
|
||||
--color-sky-50: #04202e;
|
||||
--color-sky-200: #0b4a63;
|
||||
--color-sky-700: #22d3ee;
|
||||
|
||||
--color-amber-50: #2b1f04;
|
||||
--color-amber-200: #57400a;
|
||||
--color-amber-700: #ffcc4d;
|
||||
|
||||
--color-violet-50: #1d1140;
|
||||
--color-violet-200: #382068;
|
||||
--color-violet-700: #d0a2ff;
|
||||
|
||||
--color-red-50: #2c0e1c;
|
||||
--color-red-200: #5c1c34;
|
||||
--color-red-700: #ff6b8a;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cyberpunk, hell: Konzern bei Tageslicht. Kuehles Blaugrau statt warmem
|
||||
* Weiss, dasselbe Violett - nur dunkler, damit weisse Schrift darauf haelt.
|
||||
* Die scharfen Kanten bleiben, das Leuchten wird zu einem knappen kalten
|
||||
* Schatten: ein Halo auf hellem Grund sieht nach Fehler aus, nicht nach Neon.
|
||||
*/
|
||||
html.theme-cyberpunk:not(.dark) {
|
||||
--color-canvas: #eceff7;
|
||||
--color-surface: #ffffff;
|
||||
|
||||
--color-brand-50: #f3ecff;
|
||||
--color-brand-100: #e6d9ff;
|
||||
--color-brand-200: #c9adff;
|
||||
--color-brand-300: #a97bff;
|
||||
--color-brand-400: #8d4df0;
|
||||
--color-brand-500: #7326d6;
|
||||
--color-brand-600: #5d1cb0;
|
||||
--color-brand-700: #4a1590;
|
||||
--color-brand-800: #380f6e;
|
||||
--color-brand-900: #290a52;
|
||||
--color-brand-accent: #7326d6;
|
||||
|
||||
--color-ink-50: #f2f4fb;
|
||||
--color-ink-100: #e4e8f4;
|
||||
--color-ink-200: #c3cbe0;
|
||||
--color-ink-300: #98a2bd;
|
||||
--color-ink-400: #5d6786;
|
||||
--color-ink-500: #4a5473;
|
||||
--color-ink-600: #3a4260;
|
||||
--color-ink-700: #2c334d;
|
||||
--color-ink-800: #1e2438;
|
||||
--color-ink-900: #121728;
|
||||
--color-ink-950: #0a0d18;
|
||||
|
||||
--radius-sm: 0.125rem;
|
||||
--radius-md: 0.125rem;
|
||||
--radius-lg: 0.1875rem;
|
||||
--radius-xl: 0.25rem;
|
||||
--radius-2xl: 0.375rem;
|
||||
|
||||
--shadow-card: 0 1px 2px rgb(18 23 40 / 0.06), 0 6px 18px rgb(115 38 214 / 0.1);
|
||||
--shadow-lift: 0 12px 30px rgb(115 38 214 / 0.18);
|
||||
|
||||
--font-sans: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif;
|
||||
--font-display: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
html,
|
||||
body {
|
||||
@@ -414,6 +535,23 @@ html.theme-pixel.dark {
|
||||
font-smooth: never;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cyberpunk laesst die Ueberschriften atmen: die negative Laufweite zieht
|
||||
* Chakra Petch unangenehm zusammen, gesperrt liest es sich nach Anzeigetafel.
|
||||
*/
|
||||
html.theme-cyberpunk h1,
|
||||
html.theme-cyberpunk h2,
|
||||
html.theme-cyberpunk h3,
|
||||
html.theme-cyberpunk h4,
|
||||
html.theme-cyberpunk h5,
|
||||
html.theme-cyberpunk h6 {
|
||||
letter-spacing: 0.015em;
|
||||
}
|
||||
|
||||
html.theme-cyberpunk .field-label {
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
/* Fokus-Ring durchgaengig in CI-Gruen */
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--color-brand-500);
|
||||
|
||||
+5
-1
@@ -88,7 +88,7 @@ export type Role = 'admin' | 'user';
|
||||
* Tippfehler hier nicht still zum Standarddesign fuehrt.
|
||||
* server/test/userFields.test.mjs prueft, dass beide Listen gleich bleiben.
|
||||
*/
|
||||
export const THEME_PALETTES = ['perinco', 'pixel', 'outdoor'] as const;
|
||||
export const THEME_PALETTES = ['perinco', 'pixel', 'outdoor', 'cyberpunk'] as const;
|
||||
|
||||
export type ThemePalette = (typeof THEME_PALETTES)[number];
|
||||
|
||||
@@ -97,6 +97,10 @@ export const THEME_PALETTE_LABELS: Record<ThemePalette, { name: string; hint: st
|
||||
perinco: { name: 'perinco', hint: 'Das Design des Hauses - Blattgrün auf neutralem Grau' },
|
||||
pixel: { name: 'Pixel Art', hint: '8-bit: eckige Kanten, harte Schatten, Pixelschrift' },
|
||||
outdoor: { name: 'Outdoor', hint: 'Wanderkarte: Sandpapier, Bergsee-Türkis, weiche Ecken' },
|
||||
cyberpunk: {
|
||||
name: 'Cyberpunk',
|
||||
hint: 'Neon in der Nacht: scharfe Kanten, violettes Leuchten, Chakra Petch',
|
||||
},
|
||||
};
|
||||
|
||||
export interface AppUser {
|
||||
|
||||
Reference in New Issue
Block a user