feat(kanban): choose a colour per column
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m52s

"Spalten anpassen" gets a colour dot per column that opens the choice: grey, anthracite (theme
text colour), green, yellow, blue, red, purple or the accent. columnDotClass reads the column's
colour (server defaults emerald/amber count as green/yellow; columns without one keep the colour
of their role) and drives the Kanban header, the status dot in the list view and the task detail.
Blue and purple come from new theme variables --color-info / --color-purple with fallbacks for
light and dark themes. Docs and changelog updated; v0.31.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-11-01 11:00:17 +01:00
1 parent b883315914
commit 08081e6c1d
11 files changed
+219 -77

No files matched your search

+7 -1
View File
@@ -6,6 +6,11 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
## [Unreleased] ## [Unreleased]
## [0.31.0] - 2026-11-01
### Added
- Kanban: Die Farbe jeder Spalte lässt sich einstellen. In „Spalten anpassen“ öffnet der Farbpunkt vor dem Namen die Auswahl: Grau, Anthrazit, Grün, Gelb, Blau, Rot, Lila oder die Akzentfarbe des Themes. Die Farbe erscheint im Spaltenkopf, als Statuspunkt in der Listenansicht und im Detail-Fenster. Blau und Lila passen sich hellen und dunklen Themes an.
## [0.30.0] - 2026-11-01 ## [0.30.0] - 2026-11-01
### Added ### Added
@@ -577,7 +582,8 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
- Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken. - Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken.
- Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung. - Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung.
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.30.0...dev [Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.31.0...dev
[0.31.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.30.0...v0.31.0
[0.30.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.29.0...v0.30.0 [0.30.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.29.0...v0.30.0
[0.29.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.28.0...v0.29.0 [0.29.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.28.0...v0.29.0
[0.28.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.27.1...v0.28.0 [0.28.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.27.1...v0.28.0
+1 -1
View File
@@ -9,7 +9,7 @@ Eine Aufgabe ist ein `thought` ([database.md](../database.md)) mit `kanbanStatus
## Ansichten ## Ansichten
- **Liste** mit Filtern (offen, erledigt, Papierkorb), Gruppierung (z. B. nach Status oder Fälligkeit), Sortierung und Hashtag-Filter. - **Liste** mit Filtern (offen, erledigt, Papierkorb), Gruppierung (z. B. nach Status oder Fälligkeit), Sortierung und Hashtag-Filter.
- **Kanban:** Spalten pro Liste frei konfigurierbar (Name, Farbe, „Erledigt“-Spalte). Standard: *Zu erledigen*, *In Bearbeitung*, *Wartet auf andere*, *Erledigt*. Eine Karte in eine „Erledigt“-Spalte zu ziehen setzt `isCompleted`. - **Kanban:** Spalten pro Liste frei konfigurierbar (Name, Farbe, „Erledigt“-Spalte). Farben in „Spalten anpassen“ über den Punkt vor dem Namen: Grau, Anthrazit (Textfarbe des Themes), Grün, Gelb, Blau, Rot, Lila, Akzentfarbe (`columns[].color` = `gray`/`slate`/`green`/`yellow`/`blue`/`red`/`purple`/`accent`; ältere Werte `emerald`/`amber` gelten als Grün/Gelb, Spalten ohne Farbe behalten die ihrer Rolle). Der Punkt erscheint im Spaltenkopf, vor dem Status in der Listenansicht und im Detail-Fenster ([ManageColumnsModal.tsx](../../frontend/src/features/triage/ManageColumnsModal.tsx), `columnDotClass`). Blau und Lila kommen aus `--color-info` / `--color-purple`; Themes können sie setzen, sonst gelten Standardwerte aus `index.css`. Standard: *Zu erledigen*, *In Bearbeitung*, *Wartet auf andere*, *Erledigt*. Eine Karte in eine „Erledigt“-Spalte zu ziehen setzt `isCompleted`.
- Drag & Drop sortiert in der Liste und zwischen Spalten; ein Klick öffnet das Detail-Fenster (Titel, Status, Priorität, Fälligkeit mit optionaler Uhrzeit und Schnellwahl, Liste, Hashtags, Unteraufgaben mit Fortschrittsbalken, Notizen). - Drag & Drop sortiert in der Liste und zwischen Spalten; ein Klick öffnet das Detail-Fenster (Titel, Status, Priorität, Fälligkeit mit optionaler Uhrzeit und Schnellwahl, Liste, Hashtags, Unteraufgaben mit Fortschrittsbalken, Notizen).
- Aufgaben lassen sich in eine Notiz umwandeln („Als Notiz vertiefen“). Aufgaben aus Notiz-Checkboxen (`enhanced-task`) bleiben verknüpft: Abhaken wirkt in beide Richtungen (`SyncDocBlockAndTask`), „In Notiz öffnen“ springt zum Block. - Aufgaben lassen sich in eine Notiz umwandeln („Als Notiz vertiefen“). Aufgaben aus Notiz-Checkboxen (`enhanced-task`) bleiben verknüpft: Abhaken wirkt in beide Richtungen (`SyncDocBlockAndTask`), „In Notiz öffnen“ springt zum Block.
+1 -1
View File
@@ -6,7 +6,7 @@
[![Self-Hosted](https://img.shields.io/badge/100%25-Self--Hosted-2563eb?style=flat-square)](#) [![Self-Hosted](https://img.shields.io/badge/100%25-Self--Hosted-2563eb?style=flat-square)](#)
[![Docker](https://img.shields.io/badge/Deployment-Single%20Container-2496ed?style=flat-square&logo=docker&logoColor=white)](#-schnellstart) [![Docker](https://img.shields.io/badge/Deployment-Single%20Container-2496ed?style=flat-square&logo=docker&logoColor=white)](#-schnellstart)
[![SQLite](https://img.shields.io/badge/Daten-SQLite-003b57?style=flat-square&logo=sqlite&logoColor=white)](#-deine-daten-gehören-dir) [![SQLite](https://img.shields.io/badge/Daten-SQLite-003b57?style=flat-square&logo=sqlite&logoColor=white)](#-deine-daten-gehören-dir)
[![Version](https://img.shields.io/badge/Version-v0.30.0-16a34a?style=flat-square)](CHANGELOG.md) [![Version](https://img.shields.io/badge/Version-v0.31.0-16a34a?style=flat-square)](CHANGELOG.md)
**MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server. **MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server.
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "mindsift-frontend", "name": "mindsift-frontend",
"version": "0.30.0", "version": "0.31.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mindsift-frontend", "name": "mindsift-frontend",
"version": "0.30.0", "version": "0.31.0",
"license": "Unlicense", "license": "Unlicense",
"dependencies": { "dependencies": {
"@fontsource-variable/inter": "5.3.0", "@fontsource-variable/inter": "5.3.0",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "mindsift-frontend", "name": "mindsift-frontend",
"private": true, "private": true,
"version": "0.30.0", "version": "0.31.0",
"license": "Unlicense", "license": "Unlicense",
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { import {
X, X,
Plus, Plus,
@@ -28,17 +28,32 @@ export const DEFAULT_KANBAN_COLUMNS: KanbanColumn[] = [
]; ];
// Wrapper and content split so the hooks always run in the same number (see TaskDetailModal) // Wrapper and content split so the hooks always run in the same number (see TaskDetailModal)
// Dot colour of a status column (Kanban header, list view); fixed by column role for now // Colours a column can get; all come from the theme (blue and purple via --color-info / --color-purple)
export const COLUMN_COLORS: { id: string; label: string; cls: string }[] = [
{ id: 'gray', label: 'Grau', cls: 'bg-faint' },
{ id: 'slate', label: 'Anthrazit', cls: 'bg-main' },
{ id: 'green', label: 'Grün', cls: 'bg-status-ok' },
{ id: 'yellow', label: 'Gelb', cls: 'bg-status-warn' },
{ id: 'blue', label: 'Blau', cls: 'bg-tone-blue' },
{ id: 'red', label: 'Rot', cls: 'bg-status-err' },
{ id: 'purple', label: 'Lila', cls: 'bg-tone-purple' },
{ id: 'accent', label: 'Akzentfarbe', cls: 'bg-accent' },
];
// Names the server's default columns use
const COLOR_ALIASES: Record<string, string> = { emerald: 'green', amber: 'yellow' };
const columnColorId = (col: KanbanColumn): string => {
if (col.color) return COLOR_ALIASES[col.color] || col.color;
// Columns without a colour (older boards) keep the colour of their role
if (col.isDone) return 'green';
if (col.id === 'in_progress') return 'accent';
if (col.id === 'waiting') return 'yellow';
return 'gray';
};
// Dot colour of a status column (Kanban header, list view, task detail)
export const columnDotClass = (col: KanbanColumn | undefined): string => export const columnDotClass = (col: KanbanColumn | undefined): string =>
!col (col && COLUMN_COLORS.find((c) => c.id === columnColorId(col))?.cls) || 'bg-faint';
? 'bg-faint'
: col.isDone
? 'bg-status-ok'
: col.id === 'in_progress'
? 'bg-accent'
: col.id === 'waiting'
? 'bg-status-warn'
: 'bg-faint';
export const ManageColumnsModal: React.FC<ManageColumnsModalProps> = (props) => export const ManageColumnsModal: React.FC<ManageColumnsModalProps> = (props) =>
props.isOpen ? <ManageColumnsModalContent {...props} /> : null; props.isOpen ? <ManageColumnsModalContent {...props} /> : null;
@@ -56,6 +71,44 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
const [newColName, setNewColName] = useState(''); const [newColName, setNewColName] = useState('');
const [newColIsDone, setNewColIsDone] = useState(false); const [newColIsDone, setNewColIsDone] = useState(false);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
// Colour menu: opens at the column's dot like a context menu (fixed, so the scrolling list cannot clip it)
const [colorMenu, setColorMenu] = useState<{ id: string; left: number; top?: number; bottom?: number } | null>(null);
const colorMenuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!colorMenu) return;
const close = (e: Event) => {
const target = e.target as HTMLElement;
if (colorMenuRef.current?.contains(target) || target.closest?.('[data-color-trigger]')) return;
setColorMenu(null);
};
const onKey = (e: KeyboardEvent) => e.key === 'Escape' && setColorMenu(null);
const dismiss = () => setColorMenu(null);
document.addEventListener('pointerdown', close);
document.addEventListener('keydown', onKey);
window.addEventListener('resize', dismiss);
return () => {
document.removeEventListener('pointerdown', close);
document.removeEventListener('keydown', onKey);
window.removeEventListener('resize', dismiss);
};
}, [colorMenu]);
const openColorMenu = (id: string, trigger: HTMLElement) => {
if (colorMenu?.id === id) {
setColorMenu(null);
return;
}
const rect = trigger.getBoundingClientRect();
const menuHeight = COLUMN_COLORS.length * 36 + 12;
const left = Math.min(rect.left, window.innerWidth - 200);
// Below the dot if there is room, else above it
setColorMenu(
rect.bottom + menuHeight + 8 < window.innerHeight
? { id, left, top: rect.bottom + 4 }
: { id, left, bottom: window.innerHeight - rect.top + 4 }
);
};
useEffect(() => { useEffect(() => {
setColumns(initialColumns && initialColumns.length > 0 ? initialColumns : DEFAULT_KANBAN_COLUMNS); setColumns(initialColumns && initialColumns.length > 0 ? initialColumns : DEFAULT_KANBAN_COLUMNS);
@@ -78,6 +131,12 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
); );
}; };
const handleColor = (id: string, color: string) => {
triggerHaptic(10);
setColumns((prev) => prev.map((c) => (c.id === id ? { ...c, color } : c)));
setColorMenu(null);
};
const handleToggleDone = (id: string) => { const handleToggleDone = (id: string) => {
triggerHaptic(10); triggerHaptic(10);
setColumns((prev) => setColumns((prev) =>
@@ -172,65 +231,77 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
{/* Columns List */} {/* Columns List */}
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-xs font-semibold text-main">Aktive Spalten ({columns.length}):</label> <label className="block text-xs font-semibold text-main">Aktive Spalten ({columns.length}):</label>
<div className="space-y-1.5 max-h-60 overflow-y-auto pr-1"> <div className="space-y-1.5 max-h-[45vh] overflow-y-auto pr-1" onScroll={() => setColorMenu(null)}>
{columns.map((col, idx) => ( {columns.map((col, idx) => (
<div <div key={col.id} className="p-2 bg-canvas border border-border rounded-card space-y-2">
key={col.id} <div className="flex items-center gap-2">
className="p-2 bg-canvas border border-border rounded-card flex items-center gap-2" <div className="flex flex-col gap-0.5">
> <button
<div className="flex flex-col gap-0.5"> type="button"
disabled={idx === 0}
onClick={() => handleMove(idx, 'up')}
className="text-muted hover:text-main disabled:opacity-20 p-0.5"
title="Nach oben"
>
<ArrowUp className="w-3 h-3" />
</button>
<button
type="button"
disabled={idx === columns.length - 1}
onClick={() => handleMove(idx, 'down')}
className="text-muted hover:text-main disabled:opacity-20 p-0.5"
title="Nach unten"
>
<ArrowDown className="w-3 h-3" />
</button>
</div>
<button <button
type="button" type="button"
disabled={idx === 0} data-color-trigger
onClick={() => handleMove(idx, 'up')} onClick={(e) => openColorMenu(col.id, e.currentTarget)}
className="text-muted hover:text-main disabled:opacity-20 p-0.5" className="w-7 h-7 flex items-center justify-center rounded-btn border border-border bg-panel hover:border-strong flex-shrink-0"
title="Nach oben" title="Farbe der Spalte"
aria-label={`Farbe der Spalte ${col.name}`}
aria-haspopup="menu"
aria-expanded={colorMenu?.id === col.id}
> >
<ArrowUp className="w-3 h-3" /> <span className={`w-3 h-3 rounded-full ${columnDotClass(col)}`} />
</button> </button>
<input
type="text"
value={col.name}
onChange={(e) => handleRename(col.id, e.target.value)}
className="flex-1 h-7 px-2 text-xs input-field"
placeholder="Spaltenname"
/>
{/* IsDone Toggle */}
<button <button
type="button" type="button"
disabled={idx === columns.length - 1} onClick={() => handleToggleDone(col.id)}
onClick={() => handleMove(idx, 'down')} className={`h-7 px-2 text-[10px] rounded-btn border transition-colors flex items-center gap-1 ${
className="text-muted hover:text-main disabled:opacity-20 p-0.5" col.isDone
title="Nach unten" ? 'bg-status-ok/15 border-status-ok text-status-ok font-semibold'
: 'bg-panel border-border text-faint hover:text-muted'
}`}
title={col.isDone ? 'Gilt als Erledigt' : 'Gilt als Offen'}
> >
<ArrowDown className="w-3 h-3" /> <Check className={`w-2.5 h-2.5 ${col.isDone ? 'stroke-[3]' : 'opacity-40'}`} />
<span>Erledigt</span>
</button>
<button
type="button"
onClick={() => handleDelete(col.id)}
disabled={columns.length <= 1}
className="text-muted hover:text-status-err disabled:opacity-20 p-1 transition-colors"
title="Spalte löschen"
>
<Trash2 className="w-3.5 h-3.5" />
</button> </button>
</div> </div>
<input
type="text"
value={col.name}
onChange={(e) => handleRename(col.id, e.target.value)}
className="flex-1 h-7 px-2 text-xs input-field"
placeholder="Spaltenname"
/>
{/* IsDone Toggle */}
<button
type="button"
onClick={() => handleToggleDone(col.id)}
className={`h-7 px-2 text-[10px] rounded-btn border transition-colors flex items-center gap-1 ${
col.isDone
? 'bg-status-ok/15 border-status-ok text-status-ok font-semibold'
: 'bg-panel border-border text-faint hover:text-muted'
}`}
title={col.isDone ? 'Gilt als Erledigt' : 'Gilt als Offen'}
>
<Check className={`w-2.5 h-2.5 ${col.isDone ? 'stroke-[3]' : 'opacity-40'}`} />
<span>Erledigt</span>
</button>
<button
type="button"
onClick={() => handleDelete(col.id)}
disabled={columns.length <= 1}
className="text-muted hover:text-status-err disabled:opacity-20 p-1 transition-colors"
title="Spalte löschen"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div> </div>
))} ))}
</div> </div>
@@ -303,6 +374,42 @@ const ManageColumnsModalContent: React.FC<ManageColumnsModalProps> = ({
</button> </button>
</div> </div>
</div> </div>
{colorMenu &&
(() => {
const col = columns.find((c) => c.id === colorMenu.id);
if (!col) return null;
return (
<div
ref={colorMenuRef}
role="menu"
aria-label={`Farbe der Spalte ${col.name}`}
style={{ left: colorMenu.left, top: colorMenu.top, bottom: colorMenu.bottom }}
className="fixed z-[60] w-44 p-1 bg-panel border border-border rounded-card shadow-panel"
onClick={(e) => e.stopPropagation()}
>
{COLUMN_COLORS.map((c) => {
const active = columnColorId(col) === c.id;
return (
<button
key={c.id}
type="button"
role="menuitemradio"
aria-checked={active}
onClick={() => handleColor(col.id, c.id)}
className={`w-full h-11 sm:h-8 px-2 rounded-btn text-xs flex items-center gap-2 text-left transition-colors ${
active ? 'bg-accent/10 text-main' : 'text-muted hover:text-main hover:bg-panel-hover'
}`}
>
<span className={`w-3 h-3 rounded-full flex-shrink-0 ${c.cls}`} />
<span className="flex-1">{c.label}</span>
{active && <Check className="w-3 h-3 text-accent stroke-[3]" />}
</button>
);
})}
</div>
);
})()}
</div> </div>
); );
}; };
@@ -20,6 +20,7 @@ import {
import { Thought, TaskList, TaskListMember, KanbanColumn, User, Recurrence } from '../../lib/types'; import { Thought, TaskList, TaskListMember, KanbanColumn, User, Recurrence } from '../../lib/types';
import { UserAvatar } from '../../components/UserAvatar'; import { UserAvatar } from '../../components/UserAvatar';
import { OriginBadge, originDetails } from '../../components/OriginBadge'; import { OriginBadge, originDetails } from '../../components/OriginBadge';
import { columnDotClass } from './ManageColumnsModal';
import { UserAutocomplete } from '../UserAutocomplete'; import { UserAutocomplete } from '../UserAutocomplete';
import { api, triggerHaptic } from '../../lib/api'; import { api, triggerHaptic } from '../../lib/api';
import { MarkdownViewer } from '../../lib/markdown'; import { MarkdownViewer } from '../../lib/markdown';
@@ -409,17 +410,7 @@ const TaskDetailModalContent: React.FC<TaskDetailModalProps & { task: Thought }>
: 'bg-canvas border-border text-muted hover:text-main hover:border-strong' : 'bg-canvas border-border text-muted hover:text-main hover:border-strong'
}`} }`}
> >
<span <span className={`w-2 h-2 rounded-full ${columnDotClass(col)}`} />
className={`w-2 h-2 rounded-full ${
col.isDone
? 'bg-status-ok'
: col.id === 'in_progress'
? 'bg-accent'
: col.id === 'waiting'
? 'bg-status-warn'
: 'bg-faint'
}`}
/>
<span>{col.name}</span> <span>{col.name}</span>
{isActive && <Check className="w-3 h-3 text-accent stroke-[3] ml-0.5" />} {isActive && <Check className="w-3 h-3 text-accent stroke-[3] ml-0.5" />}
</button> </button>
@@ -0,0 +1,18 @@
import { describe, it, expect } from 'vitest';
import { columnDotClass } from '../ManageColumnsModal';
describe('columnDotClass', () => {
it('uses the chosen colour, also the names of the server defaults', () => {
expect(columnDotClass({ id: 'x', name: 'Review', color: 'purple' })).toBe('bg-tone-purple');
expect(columnDotClass({ id: 'x', name: 'Blockiert', color: 'red' })).toBe('bg-status-err');
expect(columnDotClass({ id: 'waiting', name: 'Wartet', color: 'amber' })).toBe('bg-status-warn');
expect(columnDotClass({ id: 'done', name: 'Erledigt', color: 'emerald', isDone: true })).toBe('bg-status-ok');
});
it('keeps the colour of the role for columns without one', () => {
expect(columnDotClass({ id: 'in_progress', name: 'In Arbeit' })).toBe('bg-accent');
expect(columnDotClass({ id: 'x', name: 'Fertig', isDone: true })).toBe('bg-status-ok');
expect(columnDotClass({ id: 'x', name: 'Neu' })).toBe('bg-faint');
expect(columnDotClass(undefined)).toBe('bg-faint');
});
});
+11
View File
@@ -431,6 +431,17 @@ body {
--code-variable: #9cdcfe; --code-variable: #9cdcfe;
} }
/* Extra tones (Kanban column colours). Fallbacks like the code tokens: a theme's own values win. */
:where(:root) {
--color-info: #60a5fa;
--color-purple: #a78bfa;
}
:where(:root:not(.dark)) {
--color-info: #2563eb;
--color-purple: #7c3aed;
}
:where(:root:not(.dark)) { :where(:root:not(.dark)) {
--code-keyword: #0000ff; --code-keyword: #0000ff;
--code-string: #a31515; --code-string: #a31515;
+4
View File
@@ -49,6 +49,10 @@
--bg-warning: rgba(245, 158, 11, 0.12); --bg-warning: rgba(245, 158, 11, 0.12);
--border-warning: rgba(245, 158, 11, 0.38); --border-warning: rgba(245, 158, 11, 0.38);
/* Optional: Blau und Lila für Kanban-Spalten (ohne Angabe gelten Standardwerte) */
--color-info: #60a5fa;
--color-purple: #a78bfa;
/* --- 6. RUNDUNGEN (Geometrie) --- */ /* --- 6. RUNDUNGEN (Geometrie) --- */
--radius-card: 0px; /* Karten, Panels, Container (0px = eckig) */ --radius-card: 0px; /* Karten, Panels, Container (0px = eckig) */
--radius-btn: 2px; /* Buttons & Badges */ --radius-btn: 2px; /* Buttons & Badges */
+5
View File
@@ -59,6 +59,11 @@ export default {
info: v('--accent-hover'), info: v('--accent-hover'),
danger: v('--color-danger'), danger: v('--color-danger'),
}, },
// Extra tones for Kanban columns; fallbacks in index.css, themes may set their own
tone: {
blue: v('--color-info'),
purple: v('--color-purple'),
},
border: v('--border-color'), border: v('--border-color'),
// Aliases for names used in components (e.g. avatar initials), mapped to theme colours // Aliases for names used in components (e.g. avatar initials), mapped to theme colours
primary: v('--accent-primary'), primary: v('--accent-primary'),