A mark (a lambda on an ultramarine tile with the flap of an envelope), the favicon, home-screen icons and an install manifest. Every colour is a variable defined once per theme, and the Tailwind scales the screens already use read them, so dark (the default) and light are a change of values and not of classes. Geist is served by the app itself. Ctrl+K opens a command menu for folders, actions, accounts and appearance, and typing something that matches no command searches the mail; c writes a message and / goes to the search field. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
/**
|
|
* The interface's colours, as Tailwind scales that read CSS variables.
|
|
*
|
|
* The values live in globals.css, once per theme. The scales the screens
|
|
* already name - slate for neutrals, indigo for the accent, amber, rose and
|
|
* emerald for the signals - point at those variables, so switching theme
|
|
* changes what `text-slate-400` is without touching a class. `ink` is the
|
|
* colour that sits on the ground (white on dark, near-black on light): hover
|
|
* tints and hairlines are `ink` at low opacity.
|
|
*
|
|
* @type {import('tailwindcss').Config}
|
|
*/
|
|
const ramp = (name) =>
|
|
Object.fromEntries(
|
|
[50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950].map((step) => [
|
|
step,
|
|
`rgb(var(--${name}-${step}) / <alpha-value>)`,
|
|
]),
|
|
);
|
|
|
|
const neutral = ramp('n');
|
|
const accent = ramp('a');
|
|
const warn = ramp('w');
|
|
const bad = ramp('b');
|
|
const good = ramp('g');
|
|
|
|
module.exports = {
|
|
darkMode: ['selector', "[data-theme='dark']"],
|
|
content: [
|
|
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
],
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
slate: neutral,
|
|
gray: neutral,
|
|
zinc: neutral,
|
|
neutral: neutral,
|
|
// Every blue, violet and teal name in the codebase is the one accent.
|
|
indigo: accent,
|
|
violet: accent,
|
|
purple: accent,
|
|
cyan: accent,
|
|
sky: accent,
|
|
blue: accent,
|
|
brand: accent,
|
|
// Green means answered, trusted and healthy, and nothing else.
|
|
emerald: good,
|
|
teal: good,
|
|
green: good,
|
|
amber: warn,
|
|
yellow: warn,
|
|
orange: warn,
|
|
rose: bad,
|
|
red: bad,
|
|
pink: bad,
|
|
ink: 'rgb(var(--ink) / <alpha-value>)',
|
|
// The solid fill of the one main action on a screen.
|
|
fill: {
|
|
DEFAULT: 'rgb(var(--a-fill) / <alpha-value>)',
|
|
hover: 'rgb(var(--a-fill-hover) / <alpha-value>)',
|
|
},
|
|
dark: {
|
|
rail: 'rgb(var(--rail) / <alpha-value>)',
|
|
bg: 'rgb(var(--n-950) / <alpha-value>)',
|
|
panel: 'rgb(var(--n-900) / <alpha-value>)',
|
|
card: 'rgb(var(--n-800) / <alpha-value>)',
|
|
border: 'rgb(var(--ink) / 0.09)',
|
|
},
|
|
},
|
|
fontFamily: {
|
|
sans: ['"Geist Variable"', 'Geist', 'system-ui', '-apple-system', 'sans-serif'],
|
|
mono: ['ui-monospace', '"SF Mono"', 'Menlo', 'Consolas', 'monospace'],
|
|
},
|
|
borderRadius: {
|
|
xl: '12px',
|
|
'2xl': '16px',
|
|
},
|
|
boxShadow: {
|
|
// Elevation is a hairline plus ambient shade; never a coloured glow.
|
|
edge: 'inset 0 0 0 1px rgb(var(--ink) / 0.09)',
|
|
'edge-accent': 'inset 0 0 0 1px rgb(var(--a-500) / 0.45)',
|
|
lift: '0 0 0 1px rgb(var(--ink) / 0.12), var(--lm-shadow)',
|
|
},
|
|
},
|
|
},
|
|
plugins: [],
|
|
};
|