chain-studio/lib/theme/tokens.dart
flemming-it f0a944dce7 feat(studio): award-tier visual polish
Brightness-aware elevation tokens (FaiElevation low/medium) applied
to cards, sheets and the sidebar active item; gentle hover-lift on
Store + Doc cards; a longer, eased sidebar expand using the
under-used FaiMotion.slow; and a branded empty state carrying the
F∆I delta mark with warmer spacing. Subtle, Linear/Raycast-style —
both light and dark themes tuned.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-06-11 23:52:31 +02:00

115 lines
3.8 KiB
Dart

// Design tokens for F∆I Studio.
//
// One file, semantic names, no magic numbers in the rest of the
// codebase. Both light and dark modes derive from these tokens.
import 'package:flutter/material.dart';
/// Colour palette. Single strong accent (F∆I Cyan) plus neutral
/// greys. Status colours stay restrained — no candy palette.
class FaiColors {
FaiColors._();
// Brand accent. Slightly cooler / more saturated than the
// initial #1E3A8A — this reads more as "deep tech, alive"
// than the previous corporate navy.
static const accent = Color(0xFF38BDF8); // sky-400 / cyan
static const accentDeep = Color(0xFF0EA5E9); // sky-500
static const accentMuted = Color(0xFF0369A1); // sky-700
// Semantic status. Used sparingly: tones, not flat fills.
static const success = Color(0xFF22C55E); // green-500
static const warning = Color(0xFFF59E0B); // amber-500
static const danger = Color(0xFFEF4444); // red-500
// Neutral scale. Dark-mode first.
static const black = Color(0xFF09090B); // canvas
static const surface = Color(0xFF18181B); // cards
static const surfaceHigh = Color(0xFF27272A); // elevated
static const border = Color(0xFF3F3F46); // 1px outlines
static const muted = Color(0xFF71717A); // de-emphasised text
static const text = Color(0xFFE4E4E7); // body
static const textStrong = Color(0xFFFAFAFA); // headings
// Light-mode neutrals. Calmer than pure white.
static const lightCanvas = Color(0xFFFAFAFA);
static const lightSurface = Color(0xFFFFFFFF);
static const lightSurfaceHigh = Color(0xFFF4F4F5);
static const lightBorder = Color(0xFFE4E4E7);
static const lightMuted = Color(0xFF71717A);
static const lightText = Color(0xFF18181B);
static const lightTextStrong = Color(0xFF09090B);
}
/// Spacing scale. Stick to multiples of 4. Keep the vocabulary
/// small — six steps cover everything.
class FaiSpace {
FaiSpace._();
static const xs = 4.0;
static const sm = 8.0;
static const md = 12.0;
static const lg = 16.0;
static const xl = 24.0;
static const xxl = 32.0;
static const xxxl = 48.0;
}
/// Border radii. Two sizes only.
class FaiRadius {
FaiRadius._();
static const sm = 6.0;
static const md = 10.0;
}
/// Elevation tokens. Subtle, Linear/Raycast-style depth — never
/// heavy drop-shadows. Two resting levels plus a hover bump.
/// Brightness-aware: dark themes lean on deeper, lower-alpha
/// shadows (they read as ambient occlusion against near-black
/// surfaces); light themes use softer, slightly larger spreads.
class FaiElevation {
FaiElevation._();
/// Resting elevation for cards and sheets. Just enough to lift
/// a surface off the canvas without announcing itself.
static List<BoxShadow> low(Brightness b) => b == Brightness.dark
? const [
BoxShadow(
color: Color(0x33000000),
blurRadius: 6,
offset: Offset(0, 2),
),
]
: const [
BoxShadow(
color: Color(0x14000000),
blurRadius: 8,
offset: Offset(0, 2),
),
];
/// Raised elevation for hover-lifted cards and floating sheets.
static List<BoxShadow> medium(Brightness b) => b == Brightness.dark
? const [
BoxShadow(
color: Color(0x4D000000),
blurRadius: 16,
offset: Offset(0, 6),
),
]
: const [
BoxShadow(
color: Color(0x1F000000),
blurRadius: 20,
offset: Offset(0, 8),
),
];
}
/// Animation durations. Keep them under 300ms for power-user feel.
class FaiMotion {
FaiMotion._();
static const fast = Duration(milliseconds: 120);
static const base = Duration(milliseconds: 200);
static const slow = Duration(milliseconds: 320);
static const easing = Curves.easeOutCubic;
}