A warmer, friendlier admin: first pass
- The INBUXA palette on warm surfaces, softer cards, buttons and inputs, and self-hosted Inter and Space Grotesk. - Each section's icon sits on a colored tile, colored by what the section is about. - The sidebar gets a guide line for sub-pages and a labeled Management / Settings / Account switch, and folds to a rail of tiles (remembered). - Every page has a header with its section's tile. - Fields and pages with no label of their own are spelled out in words (defaultCertificateId becomes Default certificate ID). - The dashboard greets you, and its stat cards wear colored tiles. - Unavailable live numbers are a calm note, not an error. - The cat appears in empty lists and while loading. - Chart colors work again: they were hex values wrapped in hsl().
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Coffey Labs
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export type Tone = 'teal' | 'orange' | 'sky' | 'violet' | 'rose' | 'amber' | 'emerald' | 'indigo' | 'slate';
|
||||
|
||||
/**
|
||||
* Colors by meaning, for the icons the server's layout names:
|
||||
* - teal: mail itself;
|
||||
* - rose: security;
|
||||
* - amber: storage and data;
|
||||
* - sky: network and connectivity;
|
||||
* - violet: people and identity;
|
||||
* - indigo: monitoring and reports;
|
||||
* - emerald: automation and tasks;
|
||||
* - orange: look and feel;
|
||||
* - slate: the rest.
|
||||
*/
|
||||
const TONES: Record<string, Tone> = {
|
||||
'layout-dashboard': 'teal',
|
||||
mail: 'teal',
|
||||
inbox: 'teal',
|
||||
send: 'teal',
|
||||
'mail-minus': 'teal',
|
||||
plane: 'teal',
|
||||
route: 'sky',
|
||||
globe: 'sky',
|
||||
cable: 'sky',
|
||||
zap: 'sky',
|
||||
monitor: 'sky',
|
||||
'shield-check': 'rose',
|
||||
'shield-alert': 'rose',
|
||||
lock: 'rose',
|
||||
fingerprint: 'rose',
|
||||
'key-round': 'rose',
|
||||
'key-square': 'rose',
|
||||
filter: 'rose',
|
||||
database: 'amber',
|
||||
archive: 'amber',
|
||||
boxes: 'amber',
|
||||
folder: 'amber',
|
||||
search: 'amber',
|
||||
'search-code': 'amber',
|
||||
users: 'violet',
|
||||
'circle-user': 'violet',
|
||||
contact: 'violet',
|
||||
calendar: 'violet',
|
||||
activity: 'indigo',
|
||||
'chart-line': 'indigo',
|
||||
'file-text': 'indigo',
|
||||
'list-checks': 'emerald',
|
||||
clock: 'emerald',
|
||||
brain: 'emerald',
|
||||
'file-code': 'emerald',
|
||||
palette: 'orange',
|
||||
'app-window': 'orange',
|
||||
settings: 'slate',
|
||||
'sliders-horizontal': 'slate',
|
||||
};
|
||||
|
||||
const FALLBACK: Tone[] = ['teal', 'sky', 'violet', 'amber', 'emerald', 'indigo', 'orange', 'rose'];
|
||||
|
||||
/** The tone for an icon name: by meaning where known, otherwise a stable pick from its name. */
|
||||
export function toneFor(name: string): Tone {
|
||||
const known = TONES[name];
|
||||
if (known) return known;
|
||||
let h = 0;
|
||||
for (const c of name) h = (h * 31 + c.charCodeAt(0)) >>> 0;
|
||||
return FALLBACK[h % FALLBACK.length];
|
||||
}
|
||||
Reference in New Issue
Block a user