ui(env): explicit switcher button+modal, forced selection, 3px color bar
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 2m6s
CI / docker (push) Successful in 1m18s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s

- Replace EnvironmentSelector "All Envs" dropdown with Button+Modal (DS Modal, forced on first-use).
- Add 8-swatch preset color picker in the Environment settings "Appearance" section; commits via useUpdateEnvironment.
- Render a 3px fixed top bar in the current env's color across every page (z-index 900, below DS modals).
- New env-colors tokens (--env-color-*, light + dark) and envColorVar() helper with slate fallback.
- Vitest coverage for button, modal, and color helpers (13 new specs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-22 19:24:48 +02:00
parent 79fa4c097c
commit 2835d08418
16 changed files with 593 additions and 38 deletions

View File

@@ -0,0 +1,30 @@
import { ChevronDown } from 'lucide-react';
import type { Environment } from '../api/queries/admin/environments';
import { envColorVar } from './env-colors';
import styles from './EnvironmentSwitcherButton.module.css';
interface EnvironmentSwitcherButtonProps {
envs: Environment[];
value: string | undefined;
onClick: () => void;
}
export function EnvironmentSwitcherButton({ envs, value, onClick }: EnvironmentSwitcherButtonProps) {
const current = envs.find((e) => e.slug === value);
const displayName = current?.displayName ?? value ?? 'Select environment';
const color = envColorVar(current?.color);
return (
<button
type="button"
className={styles.button}
onClick={onClick}
aria-label="Switch environment"
aria-haspopup="dialog"
>
<span className={styles.dot} style={{ background: color }} aria-hidden />
<span className={styles.name}>{displayName}</span>
<ChevronDown size={14} className={styles.chevron} aria-hidden />
</button>
);
}