diff --git a/docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md b/docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md index 86aea7a..d3819f5 100644 --- a/docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md +++ b/docs/superpowers/plans/2026-10-05-weisses-design-p1-tote-klassen.md @@ -176,3 +176,22 @@ Die übrigen 58 hatten schon vor P1 keine Regel: - die Bootstrap-Namen aus `WorksheetList.jsx` (wirkungslos): `alert`, `alert-info`, `align-items-center`, `border-0`, `col-md-6`, `d-flex`, `fs-5`, `fs-6`, `justify-content-between`, `justify-content-md-end`, `mb-3`, `mb-4`, `me-1`, `me-2`, `ms-2`, `ms-3`, `mx-2`, `overflow-hidden`, `p-3`, `px-3`, `py-2`, `py-3`, `rounded-3`, `shadow-sm`, `spinner-border`, `text-dark`, `text-md-end`, `text-muted`, `visually-hidden`, `timeline`, `timeline-item`; - die Namen aus dem Plan: `p-4`, `btn-sm`, `status-filter`, `supporters-editor` (und `timeline` oben); - reine Haken ohne Styling: `credits-view`, `fin-abo-amount`, `fin-abo-main`, `fin-abo-next`, `fin-abo-product`, `fin-assign`, `fin-days`, `fin-inv-abo`, `fin-memo`, `fin-tax-card`, `fin-tax-note`, `fin-todo-main`, `fin-total-value`, `fin-tx-out`, `fin-wrap`, `legal-doc`, `offer-form`, `sp-ampel`, `sp-apps`, `sp-cleanup-body`, `status-filter-actions`, `worksheet-list`, `worksheet-supporters`. + +## Bewusst offen (P1 Task 9) + +Stand nach Task 9: `node "$W/farben.mjs" "$WT"` meldet **0 Treffer** in allen vier Gründen (`kontrast<3`, `dunkel-auf-akzent`, `weiss`, `verlauf`). Vorher waren es 41, 1, 18 und 11 (zusammen 71). Was danach noch an Farbliteralen im Quelltext steht, ist hier mit Zielpaket und gemessenem Kontrast festgehalten; keine Zeile liegt unter 3:1. + +| Stelle | Was | Kontrast (gemessen) | Zielpaket | +|---|---|---|---| +| `global.css:269` (`.select`) | Pfeil als zwei `linear-gradient` mit hartem Stopp (`transparent 50%`), Wert aus dem Prototyp; `farben.mjs` führt ihn als „ausgenommen“ (`--ohne-ausnahmen` zeigt ihn) | `--text-2` auf Weiß 5,76:1 | bleibt (Spec) | +| `global.css:1057` (KOMPAT, `select.form-control`) | derselbe Pfeil für das Altfeld | 5,76:1 | P10 (KOMPAT entfällt, `FormField` und die Altfelder gehen in P7 auf `.select`) | +| `global.css:669` `.legal-version-current` (Tönung `rgba(16,185,129,.08)`), `:680` `.legal-tone-red td` (`rgba(239,68,68,.10)`), `:682` `.legal-tone-orange td` (`rgba(245,158,11,.10)`) | Alpha-Tönungen als Zeilenhintergrund | Text `--text` auf den Tönungen 14,3 bis 15,1:1; `.legal-deadline` rot (`--danger`) 5,43:1, orange (`--orange-fg`) 6,43:1 | P8 (Rechtliches, Tönungen werden Badge-Töne) | +| `global.css:964` `.fin-confirm` (Tönung `rgba(245,158,11,.1)`) | Alpha-Tönung als Kastenhintergrund | Text `--text` auf der Tönung 15,1:1 | P7 (Finanzen) | +| `src/components/serverpuls/chartColors.js` | Canvas-Farben, die einzige erlaubte Ausnahme im Quelltext (Spec) | Linie 18,88:1, Achse 5,76:1, Spitze 3,10:1; Raster und Teilstriche sind Hilfslinien ohne Text | bleibt (Spec) | +| `src/components/Logo.jsx` (`fill="#111"`, `fill="#fff"`) | SVG-Marke mit zwei festen Farben | Weiß auf Schwarz 18,88:1 | bleibt (Marke) | + +Außerhalb des Scans (er prüft nur CSS und JSX) und **nicht** unter „bewusst offen“, weil der Kontrast unter 3:1 liegt: + +- `src/lib/previewEnv.js`, `DOT_COLORS` und `dotColor()`: Status-Punkte mit den Altfarben `#10b981` (2,54:1), `#f59e0b` (2,15:1), `#ef4444` (3,76:1) und `#a0aec0` (2,26:1) auf Weiß. Die Punkte sind höchstens 10 × 10 px groß und stehen immer neben dem Statustext (`ServicesPage`, `PreviewBlock`); der Browser-Check `dark-surface` ignoriert seit diesem Task Elemente bis 12 × 12 px. `tests/unit/previewEnv.test.mjs` hält die Hex-Werte fest, deshalb bleibt die Datei hier unverändert. Zielpaket: P5 (Projekte und Services): `dotColor()` liefert `var(--green-fg)`, `var(--orange-fg)`, `var(--red-fg)`, `var(--text-3)`, dazu der Test. + +Nicht prüfbar im Werkzeug: Der Eingabebaustein des Chats kommt aus dem Kundenportal und ist dort eine leere Datei (`loadChatComposer`). Die Variablen `--chat-*` und die Meldungsfarben (`.chat-composer-note`, `.chat-file-size`, `.chat-audio-time`) sind laut Vorgabe gesetzt; ihr Laufzeitbild muss nach dem Livegang einmal im Chat angesehen werden (Zielpaket P6). diff --git a/src/components/AnalyseRow.jsx b/src/components/AnalyseRow.jsx index e69ea06..9736f91 100644 --- a/src/components/AnalyseRow.jsx +++ b/src/components/AnalyseRow.jsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react' import { fetchAnalyse, setAnalyse } from '../lib/analyseApi' import { analyseStatus, textformLabel, canSubmitTextform, channelLabel, legalLinks, chatTemplate, privacyHint } from '../lib/analyseView' -const COLORS = { green: '#10b981', red: '#ef4444', grey: '#a0aec0' } +const COLORS = { green: 'var(--green-fg)', red: 'var(--red-fg)', grey: 'var(--text-2)' } // Kompakte Zeile "Website-Analyse" je Projekt (lädt beim Einbinden; mit key=Projekt-ID verwenden) export default function AnalyseRow({ projectId }) { diff --git a/src/components/AssignedProjectCard.jsx b/src/components/AssignedProjectCard.jsx index 2ea47e5..d28a0ef 100644 --- a/src/components/AssignedProjectCard.jsx +++ b/src/components/AssignedProjectCard.jsx @@ -10,21 +10,21 @@ import { needsPreviewButton, assignSummary } from '../lib/projectAssign' import { updateWebsiteProject, backfillProjectPushes } from '../lib/projectAdminApi' const STATUS_COLORS = { - ready: '#10b981', - live: '#10b981', - online: '#10b981', - provisioning: '#f59e0b', - pending: '#f59e0b', - building: '#3b82f6', - error: '#ef4444', - failed: '#ef4444', + ready: 'var(--green-fg)', + live: 'var(--green-fg)', + online: 'var(--green-fg)', + provisioning: 'var(--orange-fg)', + pending: 'var(--orange-fg)', + building: 'var(--blue-fg)', + error: 'var(--red-fg)', + failed: 'var(--red-fg)', } function StatusBadge({ label, value }) { if (!value) return null - const color = STATUS_COLORS[String(value).toLowerCase()] || '#a0aec0' + const color = STATUS_COLORS[String(value).toLowerCase()] || 'var(--text-2)' return ( - + {label}: @@ -79,7 +79,7 @@ export default function AssignedProjectCard({ project, onUnassign, showActions = } return ( -
+
{project.projectName} @@ -99,7 +99,7 @@ export default function AssignedProjectCard({ project, onUnassign, showActions = title="Vom Ticket lösen" aria-label="Vom Ticket lösen" onClick={() => onUnassign(project.$id)} - style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 32, height: 32, borderRadius: 6, background: 'rgba(239, 68, 68, 0.12)', border: '1px solid rgba(239, 68, 68, 0.35)', color: '#f87171', cursor: 'pointer' }} + style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 32, height: 32, borderRadius: 6, background: 'var(--red-bg)', border: '1px solid var(--line)', color: 'var(--red-fg)', cursor: 'pointer' }} > diff --git a/src/components/CreateWorksheetModal.jsx b/src/components/CreateWorksheetModal.jsx index 8e27e6e..ad86b54 100644 --- a/src/components/CreateWorksheetModal.jsx +++ b/src/components/CreateWorksheetModal.jsx @@ -191,7 +191,7 @@ export default function CreateWorksheetModal({ isOpen, onClose, workorder, onCre ))} {isAcquisition && ( - + "Zugesagt" macht den Lead automatisch zum festen Kunden. )} @@ -259,7 +259,7 @@ export default function CreateWorksheetModal({ isOpen, onClose, workorder, onCre onChange={(value) => handleChange('totalTime', value)} disabled={formData.isComment} /> - + {autoCalculate && formData.startTime && formData.endTime ? '✓ Aus Beginn und Ende berechnet, aufgerundet auf 15 Minuten' : autoCalculate ? 'Beginn und Ende eintragen oder Zeit wählen' : 'Von Hand gewählt'} diff --git a/src/components/GiteaLinkButton.jsx b/src/components/GiteaLinkButton.jsx index df337a7..4df05f7 100644 --- a/src/components/GiteaLinkButton.jsx +++ b/src/components/GiteaLinkButton.jsx @@ -17,9 +17,9 @@ export default function GiteaLinkButton({ href, title = 'In Gitea oeffnen', size width: 32, height: 32, borderRadius: 6, - background: 'rgba(99, 102, 241, 0.15)', - border: '1px solid rgba(99, 102, 241, 0.35)', - color: '#818cf8', + background: 'var(--violet-bg)', + border: '1px solid var(--line)', + color: 'var(--violet-fg)', textDecoration: 'none', flexShrink: 0, }} diff --git a/src/components/PreviewBlock.jsx b/src/components/PreviewBlock.jsx index 06498a2..8c3966e 100644 --- a/src/components/PreviewBlock.jsx +++ b/src/components/PreviewBlock.jsx @@ -208,13 +208,13 @@ export default function PreviewBlock({ projectId }) { {status.text}
{data.state === 'failed' && data.error && !isDevUnavailableError(data.error) && ( -
{data.error}
+
{data.error}
)} )} {inactive &&
{NOT_ACTIVE}.
} - {error &&
{error}
} + {error &&
{error}
} {data && !inactive && ( <> @@ -251,7 +251,7 @@ export default function PreviewBlock({ projectId }) { {logs.map((l) => )} {logFile && ( -
+                
                   {logText || '…'}
                 
)} @@ -263,7 +263,7 @@ export default function PreviewBlock({ projectId }) {
{names.length === 0 &&
Keine Variablen.
} {names.map((n) => ( -
+
{n} ••• {editName === n ? ( @@ -282,7 +282,7 @@ export default function PreviewBlock({ projectId }) { setNewValue(e.target.value)} />
- {envError &&
{envError}
} + {envError &&
{envError}
} {dirty && (
diff --git a/src/components/PreviewLinkButton.jsx b/src/components/PreviewLinkButton.jsx index 46a2e0c..c9984f8 100644 --- a/src/components/PreviewLinkButton.jsx +++ b/src/components/PreviewLinkButton.jsx @@ -30,9 +30,9 @@ export default function PreviewLinkButton({ href, subdomain, title = 'Preview oe width: 32, height: 32, borderRadius: 6, - background: 'rgba(16, 185, 129, 0.15)', - border: '1px solid rgba(16, 185, 129, 0.35)', - color: '#34d399', + background: 'var(--green-bg)', + border: '1px solid var(--line)', + color: 'var(--green-fg)', textDecoration: 'none', flexShrink: 0, }} diff --git a/src/components/WorksheetList.jsx b/src/components/WorksheetList.jsx index 0107114..2b579bf 100644 --- a/src/components/WorksheetList.jsx +++ b/src/components/WorksheetList.jsx @@ -4,21 +4,22 @@ import { serviceTypeLabel, needsTimeEntry, worksheetTone } from '../lib/ticketFo import { statusLabel } from '../lib/ticketStatus' import { parseSupporters, supportMinutes, firstName } from '../lib/support' -// Kopffarbe je Zustand (worksheetTone): orange = Git-Zeit fehlt, gruen = Git-Zeit eingetragen +// Randfarbe je Zustand (worksheetTone): orange = Git-Zeit fehlt, gruen = Git-Zeit eingetragen. Der Kopf ist fuer alle Zustaende +// grau (Schrift --text auf --bg-subtle); P2 baut die Liste um. const TONES = { - 'git-open': { border: '#f97316', base: 'linear-gradient(135deg, #f97316 0%, #ea580c 100%)', hover: 'linear-gradient(135deg, #ea580c 0%, #c2410c 100%)' }, - 'git-done': { border: '#10b981', base: 'linear-gradient(135deg, #10b981 0%, #059669 100%)', hover: 'linear-gradient(135deg, #059669 0%, #047857 100%)' }, - comment: { border: '#3b82f6', base: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)', hover: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)' }, - work: { border: '#4a5568', base: 'linear-gradient(135deg, #4a5568 0%, #2d3748 100%)', hover: 'linear-gradient(135deg, #2d3748 0%, #1a202c 100%)' }, + 'git-open': { border: 'var(--orange-fg)', base: 'var(--bg-subtle)', hover: 'var(--bg-hover)' }, + 'git-done': { border: 'var(--green-fg)', base: 'var(--bg-subtle)', hover: 'var(--bg-hover)' }, + comment: { border: 'var(--blue-fg)', base: 'var(--bg-subtle)', hover: 'var(--bg-hover)' }, + work: { border: 'var(--text-2)', base: 'var(--bg-subtle)', hover: 'var(--bg-hover)' }, } -// Hinweise im farbigen Kopf: mit der grauen Badge-Schrift waeren sie kaum lesbar -const headerBadgeStyle = { background: 'rgba(255,255,255,0.3)', color: 'white', marginLeft: '8px' } +// Hinweise im Kopf: graue Badge-Toene +const headerBadgeStyle = { background: 'var(--grey-bg)', color: 'var(--grey-fg)', marginLeft: '8px' } const headerButtonStyle = { - background: 'rgba(255,255,255,0.25)', - color: 'white', - border: '1px solid rgba(255,255,255,0.4)', + background: 'var(--bg)', + color: 'var(--text)', + border: '1px solid var(--line-strong)', borderRadius: '6px', padding: '2px 10px', fontSize: '0.8rem', @@ -88,10 +89,10 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle transition: 'transform 0.2s ease, box-shadow 0.2s ease' }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-2px)' - e.currentTarget.style.boxShadow = '0 8px 16px rgba(0,0,0,0.1)' + e.currentTarget.style.boxShadow = 'var(--shadow-btn)' }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)' - e.currentTarget.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)' + e.currentTarget.style.boxShadow = 'none' }}> {/* Header - Immer sichtbar, klickbar */}
toggleWorksheet(ws.wsid)} style={{ background: colors.base, - color: 'white', + color: 'var(--text)', border: 'none', cursor: 'pointer', userSelect: 'none', @@ -141,7 +142,7 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle )} {ws.countsToPlan && !ws.isComment && ( - + Abo-Stunden )} @@ -160,8 +161,8 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle
)} {serviceTypeLabel(ws.serviceType)}
@@ -207,9 +208,9 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle className="btn btn-sm" title={ws.countsToPlan ? 'Nicht mehr vom Abo-Stundenkonto abziehen' : 'Vom Abo-Stundenkonto des Kunden abziehen'} style={{ - background: ws.countsToPlan ? '#9d3dfe' : 'rgba(255,255,255,0.15)', - color: 'white', - border: '1px solid rgba(255,255,255,0.4)', + background: ws.countsToPlan ? 'var(--violet-bg)' : 'var(--bg)', + color: ws.countsToPlan ? 'var(--violet-fg)' : 'var(--text)', + border: '1px solid var(--line-strong)', borderRadius: '6px', padding: '2px 10px', fontSize: '0.8rem', @@ -241,12 +242,12 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle
- + {ws.employeeName} {ws.employeeShort && ( {ws.employeeShort} )}
@@ -254,8 +255,8 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle
{!ws.isComment && (
- - {formatTime(ws.totalTime)} + + {formatTime(ws.totalTime)}
)}
@@ -275,8 +276,8 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle {/* Service Type */}
{serviceTypeLabel(ws.serviceType)}
@@ -284,20 +285,20 @@ export default function WorksheetList({ worksheets, totalTime, loading, onToggle {/* Status-Änderung */} {ws.oldStatus !== ws.newStatus && (
- + Status:{' '} - {statusLabel(ws.oldStatus)} - → - {statusLabel(ws.newStatus)} + {statusLabel(ws.oldStatus)} + → + {statusLabel(ws.newStatus)}
)} {/* Details */}
{ws.details} diff --git a/src/components/WorksheetTimesModal.jsx b/src/components/WorksheetTimesModal.jsx index edbe6f0..7d66082 100644 --- a/src/components/WorksheetTimesModal.jsx +++ b/src/components/WorksheetTimesModal.jsx @@ -140,7 +140,7 @@ export default function WorksheetTimesModal({ worksheet, currentUserId, onClose, value={totalTime} onChange={(value) => setManualTotal(value)} /> - + {manualTotal === null && calculatedTotal !== null ? '✓ Aus Beginn und Ende berechnet, aufgerundet auf 15 Minuten' : 'Von Hand gewählt'} diff --git a/src/components/serverpuls/Chart.jsx b/src/components/serverpuls/Chart.jsx index c943f06..bc9b9e5 100644 --- a/src/components/serverpuls/Chart.jsx +++ b/src/components/serverpuls/Chart.jsx @@ -1,6 +1,7 @@ import { useEffect, useRef } from 'react' import uPlot from 'uplot' import { formatAxisTick, formatLegendTime } from '../../lib/serverpulsFormat' +import { CHART } from './chartColors' import 'uplot/dist/uPlot.min.css' /** @@ -25,15 +26,15 @@ export default function Chart({ data, series, height = 280, yFmt, yRange }) { scales: { y: { range: yr || ((u, min, max) => [Math.min(0, min), max === min ? min + 1 : max + (max - min) * 0.08]) } }, axes: [ { - stroke: '#9aa7b4', - grid: { stroke: 'rgba(255,255,255,0.06)' }, - ticks: { stroke: 'rgba(255,255,255,0.1)' }, + stroke: CHART.axis, + grid: { show: false }, + ticks: { stroke: CHART.tick }, values: (u, splits, axisIdx, foundSpace, foundIncr) => splits.map((t) => formatAxisTick(t, foundIncr)), }, { - stroke: '#9aa7b4', - grid: { stroke: 'rgba(255,255,255,0.06)' }, - ticks: { stroke: 'rgba(255,255,255,0.1)' }, + stroke: CHART.axis, + grid: { stroke: CHART.grid }, + ticks: { stroke: CHART.tick }, size: 70, values: (u, vals) => vals.map(fmtVal), }, @@ -42,7 +43,7 @@ export default function Chart({ data, series, height = 280, yFmt, yRange }) { { label: 'Zeit', value: (u, v) => formatLegendTime(v) }, ...s.map((x) => ({ label: x.label, - stroke: x.color || '#10b981', + stroke: x.color || CHART.line, width: 2, spanGaps: false, points: { show: false }, diff --git a/src/components/serverpuls/chartColors.js b/src/components/serverpuls/chartColors.js new file mode 100644 index 0000000..a15f6c8 --- /dev/null +++ b/src/components/serverpuls/chartColors.js @@ -0,0 +1,11 @@ +// Canvas-Farben fuer uPlot (Serverpuls). Canvas versteht kein var(--...), deshalb stehen die Werte hier als Literale; +// sie entsprechen den Tokens: line = --accent, peak = --text-3, axis = --text-2, grid = --line, tick = --line-strong. +// Einzige Stelle im Quelltext, an der Farbliterale ausserhalb von CSS erlaubt sind. +export const CHART = { + line: '#111111', + fill: 'rgba(17,17,17,.06)', + peak: '#8a93a3', + axis: '#5e6678', + grid: '#ebeef1', + tick: '#d8dde3', +} diff --git a/src/pages/AdminPage.css b/src/pages/AdminPage.css index 25341ba..464d57c 100644 --- a/src/pages/AdminPage.css +++ b/src/pages/AdminPage.css @@ -142,15 +142,15 @@ } .admin-icon-button:hover { - border-color: rgba(16, 185, 129, 0.5); + border-color: var(--accent-ring); color: var(--text); - background: rgba(16, 185, 129, 0.07); + background: var(--accent-soft); } .admin-icon-button.danger:hover { - border-color: rgba(239, 68, 68, 0.55); - color: #fca5a5; - background: rgba(239, 68, 68, 0.08); + border-color: var(--danger); + color: var(--red-fg); + background: var(--red-bg); } .admin-primary-button { @@ -160,17 +160,17 @@ justify-content: center; gap: 8px; padding: 9px 15px; - border: 1px solid #34d399; + border: 1px solid var(--accent); border-radius: 6px; background: var(--accent); - color: #05251c; + color: #fff; font: inherit; font-size: 13px; font-weight: 750; cursor: pointer; } -.admin-primary-button:hover { background: #34d399; } +.admin-primary-button:hover { background: var(--accent-hover); } .admin-primary-button:disabled { opacity: 0.55; cursor: wait; } .admin-split-layout { @@ -259,14 +259,14 @@ .admin-table td { padding: 12px 13px; - border-bottom: 1px solid rgba(255, 255, 255, 0.065); + border-bottom: 1px solid var(--line); color: var(--text); font-size: 13px; vertical-align: middle; } .admin-table tbody tr:last-child td { border-bottom: 0; } -.admin-table tbody tr:hover td { background: rgba(255, 255, 255, 0.018); } +.admin-table tbody tr:hover td { background: var(--bg-subtle); } .admin-table td > strong, .admin-table td > span { display: block; } .admin-table td > span { margin-top: 3px; color: var(--text-muted); font-size: 12px; } @@ -350,8 +350,8 @@ align-items: center; margin-bottom: 16px; padding: 13px 15px; - border: 1px solid rgba(16, 185, 129, 0.42); - background: rgba(16, 185, 129, 0.065); + border: 1px solid var(--line-strong); + background: var(--bg-subtle); } .admin-credential-result strong, diff --git a/src/pages/ServerpulsPage.jsx b/src/pages/ServerpulsPage.jsx index 6f227bf..edd5e31 100644 --- a/src/pages/ServerpulsPage.jsx +++ b/src/pages/ServerpulsPage.jsx @@ -7,6 +7,7 @@ import { ageLabel, formatBytes, formatNumber, formatPct, formatSince, usedPct, } from '../lib/serverpulsFormat' import Chart from '../components/serverpuls/Chart' +import { CHART } from '../components/serverpuls/chartColors' import Sparkline from '../components/serverpuls/Sparkline' import AppsTable from '../components/serverpuls/AppsTable' import '../styles/serverpuls.css' @@ -220,10 +221,10 @@ export default function ServerpulsPage() { const avg = pts.map((p) => p.v) const withMax = chart.res !== 'raw' && ['cpu', 'load', 'zombies'].includes(chart.metric) const data = [xs, avg] - const series = [{ label: m.label, color: '#10b981', fill: 'rgba(16,185,129,0.10)' }] + const series = [{ label: m.label, color: CHART.line, fill: CHART.fill }] if (withMax) { data.push(pts.map((p) => p.max ?? null)) - series.push({ label: `${m.label} (Spitze)`, color: '#f59e0b', dash: [4, 4] }) + series.push({ label: `${m.label} (Spitze)`, color: CHART.peak, dash: [4, 4] }) } return { data, series } }, [chart, m.label]) @@ -232,9 +233,9 @@ export default function ServerpulsPage() { if (!detail) return null const days = (detail.size || []).map((s) => Math.round(new Date(`${s.day}T12:00:00Z`).getTime() / 1000)) return { - size: { data: [days, (detail.size || []).map((s) => s.bytes)], series: [{ label: 'Größe', color: '#3b82f6', fill: 'rgba(59,130,246,0.10)' }] }, - mem: { data: [(detail.mem || []).map((p) => Math.round(p.ts / 1000)), (detail.mem || []).map((p) => p.v)], series: [{ label: 'RAM', color: '#10b981' }] }, - cpu: { data: [(detail.cpu || []).map((p) => Math.round(p.ts / 1000)), (detail.cpu || []).map((p) => p.v)], series: [{ label: 'CPU', color: '#f59e0b' }] }, + size: { data: [days, (detail.size || []).map((s) => s.bytes)], series: [{ label: 'Größe', color: CHART.line, fill: CHART.fill }] }, + mem: { data: [(detail.mem || []).map((p) => Math.round(p.ts / 1000)), (detail.mem || []).map((p) => p.v)], series: [{ label: 'RAM', color: CHART.line }] }, + cpu: { data: [(detail.cpu || []).map((p) => Math.round(p.ts / 1000)), (detail.cpu || []).map((p) => p.v)], series: [{ label: 'CPU', color: CHART.line }] }, } }, [detail]) diff --git a/src/styles/admin-areas.css b/src/styles/admin-areas.css index 916a147..e629ff9 100644 --- a/src/styles/admin-areas.css +++ b/src/styles/admin-areas.css @@ -9,12 +9,12 @@ .area-row.head { color: var(--text-muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; } .area-row.active { background: var(--accent-soft); } .area-muted { color: var(--text-muted); font-size: 13px; } -.area-danger { color: #f87171; font-size: 13px; } -.area-warn { color: #fbbf24; font-size: 13px; } +.area-danger { color: var(--red-fg); font-size: 13px; } +.area-warn { color: var(--orange-fg); font-size: 13px; } .area-summary { padding: 12px 16px; border-radius: var(--r-sm); background: var(--surface-2); margin-bottom: var(--sp-3); } -.area-summary.warn { background: rgba(245, 158, 11, 0.16); color: #fbbf24; } +.area-summary.warn { background: var(--orange-bg); color: var(--orange-fg); } .area-message { padding: 10px 14px; border-radius: var(--r-sm); background: var(--accent-soft); margin-bottom: var(--sp-3); } -.area-error { padding: 10px 14px; border-radius: var(--r-sm); background: rgba(239, 68, 68, 0.14); color: #f87171; margin: var(--sp-2) 0; } +.area-error { padding: 10px 14px; border-radius: var(--r-sm); background: var(--red-bg); color: var(--red-fg); margin: var(--sp-2) 0; } .area-kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 16px; margin: 0; } .area-kv dt { color: var(--text-muted); } .area-kv dd { margin: 0; overflow-wrap: anywhere; } diff --git a/src/styles/chat.css b/src/styles/chat.css index 7e71084..8a123bc 100644 --- a/src/styles/chat.css +++ b/src/styles/chat.css @@ -2,25 +2,32 @@ chat-composer.css des Kundenportals; hier Farben (--chat-*), Verlauf, Sprechblasen und die Liste. */ .woms-chat { --chat-accent: var(--accent); - --chat-accent-hover: var(--accent-2); + --chat-accent-hover: var(--accent-hover); --chat-accent-soft: var(--accent-soft); - --chat-accent-line: rgba(16, 185, 129, 0.5); + --chat-accent-line: rgba(17, 17, 17, 0.14); --chat-accent-text: var(--accent); - --chat-on-accent: #04130d; - --chat-surface: var(--surface-2); + --chat-on-accent: #ffffff; + --chat-surface: var(--bg); + --chat-line: var(--line-strong); + --chat-muted: var(--text-2); display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; } .woms-chat .chat-messages { display: flex; flex-direction: column; gap: 8px; min-height: 280px; max-height: 60vh; overflow-y: auto; - padding: 12px; border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface); + padding: 12px; border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--bg); } .woms-chat .chat-bubble { align-self: flex-start; max-width: 78%; padding: 8px 12px; border-radius: 14px; background: var(--surface-2); color: var(--text); overflow-wrap: anywhere; } -.woms-chat .chat-bubble.mine { align-self: flex-end; background: var(--accent-soft); border: 1px solid rgba(16, 185, 129, 0.35); } +.woms-chat .chat-bubble.mine { align-self: flex-end; background: var(--accent-soft); border: 1px solid var(--line); } .woms-chat .chat-meta { display: block; margin-top: 4px; font-size: 11.5px; color: var(--text-muted); } .woms-chat .chat-notice { font-size: 12px; } +/* Meldungen des Eingabebausteins (Stile aus chat-composer.css des Portals): Fehler rot, Info grau, beides lesbar auf Weiss */ +.woms-chat .chat-composer-note, +.woms-chat .chat-tray-item.is-error .chat-file-size, +.woms-chat .chat-audio.is-error .chat-audio-time { color: var(--red-fg); } +.woms-chat .chat-composer-note.is-info { color: var(--text-2); } /* Eingabefeld: Grundstil wie im frueheren Admin-Panel (chat-composer.css setzt nur das Layout) */ .woms-chat .chat-input-row textarea { resize: none; min-height: 46px; max-height: 130px; padding: 11px 14px; border-radius: 14px; diff --git a/src/styles/global.css b/src/styles/global.css index 1a06d80..66fc9d7 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -676,7 +676,7 @@ a.list-row:hover .cell-title { color: var(--accent-ink); } .legal-tone-red td { background: rgba(239, 68, 68, 0.10); } .legal-tone-red .legal-deadline { color: var(--danger); font-weight: 700; } .legal-tone-orange td { background: rgba(245, 158, 11, 0.10); } -.legal-tone-orange .legal-deadline { color: #b45309; font-weight: 600; } +.legal-tone-orange .legal-deadline { color: var(--orange-fg); font-weight: 600; } .legal-tone-missing .legal-deadline { color: var(--text-faint); font-style: italic; } .legal-tone-grey td { color: var(--text-faint); } .legal-detail td { background: var(--surface-2); cursor: default; } @@ -909,13 +909,13 @@ a.list-row:hover .cell-title { color: var(--accent-ink); } .fin-dialog:focus { outline: none; } .fin-dialog-error { margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4); border-radius: var(--r-sm); - border: 1px solid var(--danger); background: rgba(239, 68, 68, 0.16); color: #fecaca; overflow-wrap: anywhere; + border: 1px solid var(--danger); background: var(--red-bg); color: var(--red-fg); overflow-wrap: anywhere; } -.fin-dialog-warn { margin: 6px 0 0; font-size: 13px; color: #fbbf24; overflow-wrap: anywhere; } +.fin-dialog-warn { margin: 6px 0 0; font-size: 13px; color: var(--orange-fg); overflow-wrap: anywhere; } .fin-dialog-sub { margin: 6px 0 0; font-size: 13px; color: var(--text-muted); overflow-wrap: anywhere; } .fin-fixed-customer { margin: 0; overflow-wrap: anywhere; } .fin-address-warn { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px var(--sp-3); font-size: 13px; overflow-wrap: anywhere; } -.fin-address-warn-dialog { margin: 6px 0 0; color: #fbbf24; } +.fin-address-warn-dialog { margin: 6px 0 0; color: var(--orange-fg); } .fin-address-warn-page { margin: 0 0 var(--sp-4); padding: var(--sp-3) var(--sp-4); border: 1px solid var(--warn); border-radius: var(--r-sm); color: var(--warn); } .fin-address-warn .fin-link { font-size: 13px; } .fin-items { border: 0; padding: 0; margin: 0 0 var(--sp-4); min-width: 0; } @@ -932,7 +932,7 @@ a.list-row:hover .cell-title { color: var(--accent-ink); } width: 38px; height: 40px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; background: transparent; border: 1px solid var(--border-strong); border-radius: 6px; color: var(--text-muted); } -.fin-icon-btn:hover:not(:disabled) { color: #f87171; border-color: #f87171; } +.fin-icon-btn:hover:not(:disabled) { color: var(--red-fg); border-color: var(--red-fg); } .fin-icon-btn:disabled { opacity: 0.35; cursor: not-allowed; } .fin-icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } .fin-items-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-top: var(--sp-3); } diff --git a/src/styles/serverpuls.css b/src/styles/serverpuls.css index 15e4756..e662155 100644 --- a/src/styles/serverpuls.css +++ b/src/styles/serverpuls.css @@ -1,16 +1,16 @@ /* Serverpuls (Admin) */ .sp-page { display: flex; flex-direction: column; gap: var(--sp-4); } -.sp-error { background: rgba(239,68,68,.12); border: 1px solid rgba(239,68,68,.4); color: #fca5a5; padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md); } +.sp-error { background: var(--red-bg); border: 1px solid var(--danger-ring); color: var(--red-fg); padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md); } .sp-muted { color: var(--text-muted); } .sp-small { font-size: 12px; } .sp-head { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--r-md); padding: var(--sp-4); } -.sp-level-medium { border-color: rgba(245,158,11,.5); } -.sp-level-critical { border-color: rgba(239,68,68,.6); } +.sp-level-medium { border-color: var(--orange-fg); } +.sp-level-critical { border-color: var(--danger); } .sp-head-top { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; } .sp-headline { font-size: 18px; } .sp-age { margin-left: auto; font-size: 13px; color: var(--text-muted); } -.sp-age-stale { color: #fbbf24; background: rgba(245,158,11,.14); padding: 2px 10px; border-radius: var(--r-pill); } +.sp-age-stale { color: var(--orange-fg); background: var(--orange-bg); padding: 2px 10px; border-radius: var(--r-pill); } .sp-alerts { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; } .sp-alert { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: baseline; padding: 8px 12px; border-radius: var(--r-sm); background: var(--surface-2); border-left: 3px solid var(--text-faint); } .sp-alert-critical { border-left-color: var(--danger); }