add claude.ai/design handoff under docs/claude-design/

Four self-contained HTML mockups (wireframe, flows, hi-fi, design
system), four Dart theme files (clide, midnight, paper, terminal),
and flat PNG renders. Reference material for the UI build — not
runtime assets.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-04-22 16:11:22 +02:00
co-authored by Claude
parent 1a5e682dc8
commit ae1c3d8a34
18 changed files with 1282 additions and 0 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+134
View File
@@ -0,0 +1,134 @@
# clide · design handoff
Bundle for importing into the clide repo and driving further work with Claude Code.
---
## What's in here
```
bundle/
├─ README.md ← this file
├─ Wireframe.html ← architectural snapshot of current code
├─ Wireframe - Flows.html ← 8 interaction storyboards
├─ Clide Hi-Fi.html ← 3 hi-fi scenes · 4 swappable themes
├─ Clide Design System.html ← tokens · type · components · syntax
├─ themes/ ← DROP-IN Dart files
│ ├─ clide_theme.dart (default · cool near-black + periwinkle)
│ ├─ midnight_theme.dart (VS Code-adjacent muted dark)
│ ├─ paper_theme.dart (drafting-sheet light)
│ └─ terminal_theme.dart (near-black + amber)
└─ png/ ← flat renders for tickets / PRs
├─ wireframe-layout.png
├─ wireframe-flows.png
├─ design-system.png
├─ hifi-clide-main.png
├─ hifi-clide-editor.png
├─ hifi-clide-welcome.png
├─ hifi-midnight-main.png
├─ hifi-paper-main.png
└─ hifi-terminal-main.png
```
All four HTML files are **fully self-contained** — fonts, JS, CSS inlined. Open them with any browser, commit them to the repo, or hand their paths to `claude` directly.
---
## Using this with Claude Code
Drop the bundle into the repo (e.g. `docs/design/`) and point Claude at it:
```bash
claude "read docs/design/README.md and docs/design/Clide\ Design\ System.html,
then implement the Clide theme from docs/design/themes/clide_theme.dart
so it's wired through kernel/theme.dart"
```
Claude Code can parse the HTML natively and see every token value, component, and layout annotation. It can also read the Dart theme files without conversion.
---
## Design intent, in one pass
### 1 · Typography
- **Display** — `Josefin Sans 300` for titles, section heads, and the wordmark. Light weight is load-bearing; using 400+ changes the feel entirely.
- **UI + code** — `JetBrains Mono 400/500` for everything else. Tab labels, file paths, status bar, editor. Monospace is a deliberate choice — clide is an IDE for people who like their grids.
### 2 · Layout (classicPreset)
Three columns + statusbar. Every box in the frame is a `SlotHost` slot populated by `TabContribution`s sorted by priority (lower = leftmost tab).
```
┌────────────┬──────────────────────┬──────────────┐
│ sidebar │ workspace │ context │
│ 240 px │ flex │ 300 px │
│ 180400 │ │ 220420 │
├────────────┴──────────────────────┴──────────────┤
│ statusbar · 24 px │
└──────────────────────────────────────────────────┘
```
See `Wireframe.html` for the full architectural breakdown — every red-pencil annotation cites the source file it came from.
### 3 · Themes
Four presets ship by default. `clide` is the reference — the other three are variations on the same component vocabulary, differing only in token values.
| name | bg | accent | feel |
|------------|-----------|-----------|-------------------------------|
| `clide` | `#20202C` | `#78A0F8` | cool near-black + periwinkle |
| `midnight` | `#1E1E1E` | `#569CD6` | VS Code-adjacent |
| `paper` | `#F4F1EA` | `#C14B2A` | drafting sheet · light |
| `terminal` | `#0A0A0A` | `#E0B050` | near-black + amber |
All four share the same semantic token names (`bg`, `surface`, `border`, `text`, `accent`, `ok/warn/err/info`, syntax roles). A widget written against the tokens will render correctly in all four.
### 4 · Drop-in usage
```dart
import 'themes/clide_theme.dart';
MaterialApp(
theme: ClideTheme.data,
home: const ClideApp(),
);
```
The `ClideTheme.tokens` object exposes semantic colors for cases where a Material widget can't carry the meaning — e.g. syntax highlighting, git status markers, Claude message kinds.
---
## Screens in the hi-fi
| # | screen | purpose |
|---|--------------------------------|----------------------------------------------------------|
| 1 | **Main IDE · at rest** | cold-start settled · all slots populated · Claude primary |
| 2 | **Editor · with Claude panel** | file editor in workspace · Claude as context-column tab |
| 3 | **Welcome · no project** | first-run landing · start actions + recent projects |
Open `Clide Hi-Fi.html` and use the floating Tweaks panel (bottom-right, toggle from the toolbar) to swap between themes live.
---
## Flows not yet realized
From `Wireframe - Flows.html` — things with commands/contracts wired but no UI yet:
- **Project picker** (cold start with no `lastProject`)
- **Multi-buffer editor tabs** (editor is single-buffer today)
- **Toolbar slot** (`Slots.toolbar` reserved, no preset renders it)
- **Command palette keybinding** (`⌘⇧P` not bound by default)
- **Secondary Claude panes** (command exists; UI wiring pending)
These are the highest-leverage next design targets.
---
## File integrity
All HTMLs render offline, no network required after first open. Fonts are bundled as CSS and fall back to system monospace if Josefin Sans or JetBrains Mono are blocked.
Generated by the design pass · refreshed 2026-04
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+186
View File
@@ -0,0 +1,186 @@
// clide_theme.dart
//
// Default theme for clide.
// Sampled from the Clide Web session dashboard — cool near-black canvas
// with a single periwinkle accent.
//
// Usage:
// MaterialApp(theme: ClideTheme.data, ...)
import 'package:flutter/material.dart';
class ClideTheme {
// ─── palette ──────────────────────────────────────────────────────────
static const _bg = Color(0xFF20202C); // page / editor canvas
static const _bgSunken = Color(0xFF1A1A24); // sidebar, gutters
static const _surface = Color(0xFF242838); // cards, table header, pills
static const _surfaceHi = Color(0xFF2C3046); // hover, selected row
static const _border = Color(0xFF343850);
static const _borderHi = Color(0xFF3C445C);
static const _textHi = Color(0xFFE6E8F2); // primary text
static const _text = Color(0xFFB1BBE3); // section titles, labels
static const _textDim = Color(0xFF78809C); // secondary
static const _textMute = Color(0xFF545C84); // small-caps labels
static const _accent = Color(0xFF78A0F8); // periwinkle primary
static const _accentPress = Color(0xFF6C90DC);
static const _accentSoft = Color(0x2278A0F8); // 13% accent for fills
static const _ok = Color(0xFF7DD3A8);
static const _warn = Color(0xFFE6C370);
static const _err = Color(0xFFE87D7D);
static const _info = _accent;
// syntax (Dart-biased)
static const _synKeyword = Color(0xFFC792EA); // class, const, final
static const _synType = Color(0xFF78A0F8); // Widget, BuildContext
static const _synString = Color(0xFFA8D99B);
static const _synNumber = Color(0xFFE6C370);
static const _synComment = Color(0xFF545C84);
static const _synMethod = Color(0xFF82B1FF);
static const _synPunct = Color(0xFF78809C);
// ─── exposed tokens ───────────────────────────────────────────────────
static const tokens = ClideTokens(
bg: _bg, bgSunken: _bgSunken, surface: _surface, surfaceHi: _surfaceHi,
border: _border, borderHi: _borderHi,
textHi: _textHi, text: _text, textDim: _textDim, textMute: _textMute,
accent: _accent, accentPress: _accentPress, accentSoft: _accentSoft,
ok: _ok, warn: _warn, err: _err, info: _info,
synKeyword: _synKeyword, synType: _synType, synString: _synString,
synNumber: _synNumber, synComment: _synComment, synMethod: _synMethod,
synPunct: _synPunct,
);
// ─── ThemeData ────────────────────────────────────────────────────────
static ThemeData get data => ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
scaffoldBackgroundColor: _bg,
canvasColor: _bg,
colorScheme: const ColorScheme.dark(
brightness: Brightness.dark,
primary: _accent,
onPrimary: Color(0xFF0D1020),
secondary: _synKeyword,
onSecondary: Color(0xFF0D1020),
surface: _surface,
onSurface: _textHi,
surfaceContainerHighest: _surfaceHi,
outline: _border,
outlineVariant: _borderHi,
error: _err,
onError: Color(0xFF0D1020),
),
textTheme: const TextTheme(
// Josefin Sans Light for display; JetBrains Mono for code/body.
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, height: 1.1, letterSpacing: 0.2, color: _textHi),
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, height: 1.15, letterSpacing: 0.2, color: _textHi),
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, height: 1.2, letterSpacing: 0.2, color: _textHi),
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, height: 1.3, letterSpacing: 0.3, color: _text),
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, height: 1.2, letterSpacing: 1.0, color: _textMute),
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, height: 1.4, color: _text),
),
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
cardTheme: CardThemeData(
color: _surface,
elevation: 0,
shape: RoundedRectangleBorder(
side: const BorderSide(color: _border),
borderRadius: BorderRadius.circular(6),
),
margin: EdgeInsets.zero,
),
inputDecorationTheme: InputDecorationTheme(
isDense: true,
filled: true,
fillColor: _bgSunken,
hintStyle: const TextStyle(color: _textMute),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: const BorderSide(color: _border),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: const BorderSide(color: _border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: const BorderSide(color: _accent, width: 1.5),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: _accent,
foregroundColor: const Color(0xFF0D1020),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 12, fontWeight: FontWeight.w500),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: _accent,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 12),
),
),
// Pill-style chips (matches the Projects row in the dashboard).
chipTheme: ChipThemeData(
backgroundColor: _surface,
side: const BorderSide(color: _borderHi),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
labelStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 11, color: _text),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
),
iconTheme: const IconThemeData(color: _textDim, size: 14),
tooltipTheme: TooltipThemeData(
decoration: BoxDecoration(
color: _surfaceHi,
border: Border.all(color: _borderHi),
borderRadius: BorderRadius.circular(3),
),
textStyle: const TextStyle(fontFamily: 'JetBrains Mono', fontSize: 11, color: _textHi),
),
scrollbarTheme: ScrollbarThemeData(
thumbColor: WidgetStatePropertyAll(_border),
thickness: const WidgetStatePropertyAll(6),
radius: const Radius.circular(3),
),
);
}
/// Raw color tokens — use when Material widgets can't carry the meaning.
class ClideTokens {
const ClideTokens({
required this.bg, required this.bgSunken,
required this.surface, required this.surfaceHi,
required this.border, required this.borderHi,
required this.textHi, required this.text,
required this.textDim, required this.textMute,
required this.accent, required this.accentPress, required this.accentSoft,
required this.ok, required this.warn, required this.err, required this.info,
required this.synKeyword, required this.synType, required this.synString,
required this.synNumber, required this.synComment,
required this.synMethod, required this.synPunct,
});
final Color bg, bgSunken, surface, surfaceHi;
final Color border, borderHi;
final Color textHi, text, textDim, textMute;
final Color accent, accentPress, accentSoft;
final Color ok, warn, err, info;
final Color synKeyword, synType, synString, synNumber, synComment, synMethod, synPunct;
}
@@ -0,0 +1,74 @@
// midnight_theme.dart
//
// Midnight — classic dark, VS Code-adjacent, maximally muted. Gets out of the way.
import 'package:flutter/material.dart';
import 'clide_theme.dart' show ClideTokens;
class MidnightTheme {
static const _bg = Color(0xFF1E1E1E);
static const _bgSunken = Color(0xFF181818);
static const _surface = Color(0xFF252526);
static const _surfaceHi = Color(0xFF2D2D2E);
static const _border = Color(0xFF333333);
static const _borderHi = Color(0xFF3F3F3F);
static const _textHi = Color(0xFFD4D4D4);
static const _text = Color(0xFFBBBBBB);
static const _textDim = Color(0xFF858585);
static const _textMute = Color(0xFF6A6A6A);
static const _accent = Color(0xFF569CD6); // muted azure
static const _accentPress = Color(0xFF4785BD);
static const _accentSoft = Color(0x22569CD6);
static const _ok = Color(0xFF89D185);
static const _warn = Color(0xFFD7BA7D);
static const _err = Color(0xFFF48771);
static const _info = _accent;
static const _synKeyword = Color(0xFFC586C0);
static const _synType = Color(0xFF4EC9B0);
static const _synString = Color(0xFFCE9178);
static const _synNumber = Color(0xFFB5CEA8);
static const _synComment = Color(0xFF6A9955);
static const _synMethod = Color(0xFFDCDCAA);
static const _synPunct = Color(0xFF858585);
static const tokens = ClideTokens(
bg: _bg, bgSunken: _bgSunken, surface: _surface, surfaceHi: _surfaceHi,
border: _border, borderHi: _borderHi,
textHi: _textHi, text: _text, textDim: _textDim, textMute: _textMute,
accent: _accent, accentPress: _accentPress, accentSoft: _accentSoft,
ok: _ok, warn: _warn, err: _err, info: _info,
synKeyword: _synKeyword, synType: _synType, synString: _synString,
synNumber: _synNumber, synComment: _synComment, synMethod: _synMethod,
synPunct: _synPunct,
);
static ThemeData get data => ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
scaffoldBackgroundColor: _bg,
canvasColor: _bg,
colorScheme: const ColorScheme.dark(
primary: _accent, onPrimary: Color(0xFF0B1220),
secondary: _synKeyword, onSecondary: Color(0xFF0B1220),
surface: _surface, onSurface: _textHi,
surfaceContainerHighest: _surfaceHi,
outline: _border, outlineVariant: _borderHi,
error: _err, onError: Color(0xFF0B1220),
),
textTheme: const TextTheme(
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textMute),
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
),
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
iconTheme: const IconThemeData(color: _textDim, size: 14),
);
}
@@ -0,0 +1,76 @@
// paper_theme.dart
//
// Paper — carries the wireframe aesthetic into the product.
// Off-white drafting sheet, near-black ink, red-pencil accent for annotations.
import 'package:flutter/material.dart';
import 'clide_theme.dart' show ClideTokens;
class PaperTheme {
static const _bg = Color(0xFFF4F1EA); // paper
static const _bgSunken = Color(0xFFECE7DB); // paper-2
static const _surface = Color(0xFFFBF8F1);
static const _surfaceHi = Color(0xFFECE7DB);
static const _border = Color(0xFF1A1A1A);
static const _borderHi = Color(0xFF4A4A4A);
static const _textHi = Color(0xFF1A1A1A); // ink
static const _text = Color(0xFF4A4A4A); // ink-2
static const _textDim = Color(0xFF8A8A82); // ink-3
static const _textMute = Color(0xFFA8A89E);
static const _accent = Color(0xFFC14B2A); // red pencil
static const _accentPress = Color(0xFFA03D20);
static const _accentSoft = Color(0x22C14B2A);
static const _ok = Color(0xFF2D8A52);
static const _warn = Color(0xFFB88A2A);
static const _err = Color(0xFFB03A2A);
static const _info = Color(0xFF2A6FC1);
// Syntax tuned for cream paper — desaturated so code reads like print.
static const _synKeyword = Color(0xFF7B3F8C);
static const _synType = Color(0xFF2A6FC1);
static const _synString = Color(0xFF2D8A52);
static const _synNumber = Color(0xFFB88A2A);
static const _synComment = Color(0xFF8A8A82);
static const _synMethod = Color(0xFF1E5D9E);
static const _synPunct = Color(0xFF4A4A4A);
static const tokens = ClideTokens(
bg: _bg, bgSunken: _bgSunken, surface: _surface, surfaceHi: _surfaceHi,
border: _border, borderHi: _borderHi,
textHi: _textHi, text: _text, textDim: _textDim, textMute: _textMute,
accent: _accent, accentPress: _accentPress, accentSoft: _accentSoft,
ok: _ok, warn: _warn, err: _err, info: _info,
synKeyword: _synKeyword, synType: _synType, synString: _synString,
synNumber: _synNumber, synComment: _synComment, synMethod: _synMethod,
synPunct: _synPunct,
);
static ThemeData get data => ThemeData(
useMaterial3: true,
brightness: Brightness.light,
scaffoldBackgroundColor: _bg,
canvasColor: _bg,
colorScheme: const ColorScheme.light(
primary: _accent, onPrimary: Color(0xFFFBF8F1),
secondary: _info, onSecondary: Color(0xFFFBF8F1),
surface: _surface, onSurface: _textHi,
surfaceContainerHighest: _surfaceHi,
outline: _border, outlineVariant: _borderHi,
error: _err, onError: Color(0xFFFBF8F1),
),
textTheme: const TextTheme(
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textDim),
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
),
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
iconTheme: const IconThemeData(color: _text, size: 14),
);
}
@@ -0,0 +1,77 @@
// terminal_theme.dart
//
// Terminal — near-black bg, high-contrast mono, single amber accent.
// For users who want the IDE to read like a tmux window.
import 'package:flutter/material.dart';
import 'clide_theme.dart' show ClideTokens;
class TerminalTheme {
static const _bg = Color(0xFF0A0A0A);
static const _bgSunken = Color(0xFF000000);
static const _surface = Color(0xFF111111);
static const _surfaceHi = Color(0xFF181818);
static const _border = Color(0xFF242424);
static const _borderHi = Color(0xFF2E2E2E);
static const _textHi = Color(0xFFE6E6E6);
static const _text = Color(0xFFBDBDBD);
static const _textDim = Color(0xFF7A7A7A);
static const _textMute = Color(0xFF4A4A4A);
static const _accent = Color(0xFFE0B050); // amber
static const _accentPress = Color(0xFFC29438);
static const _accentSoft = Color(0x22E0B050);
static const _ok = Color(0xFF8FDC9B);
static const _warn = Color(0xFFE0B050);
static const _err = Color(0xFFE05050);
static const _info = Color(0xFFA3C4FF);
// Classic 16-color palette feel
static const _synKeyword = Color(0xFFE05050);
static const _synType = Color(0xFFE0B050);
static const _synString = Color(0xFF8FDC9B);
static const _synNumber = Color(0xFFC792EA);
static const _synComment = Color(0xFF4A4A4A);
static const _synMethod = Color(0xFFA3C4FF);
static const _synPunct = Color(0xFF7A7A7A);
static const tokens = ClideTokens(
bg: _bg, bgSunken: _bgSunken, surface: _surface, surfaceHi: _surfaceHi,
border: _border, borderHi: _borderHi,
textHi: _textHi, text: _text, textDim: _textDim, textMute: _textMute,
accent: _accent, accentPress: _accentPress, accentSoft: _accentSoft,
ok: _ok, warn: _warn, err: _err, info: _info,
synKeyword: _synKeyword, synType: _synType, synString: _synString,
synNumber: _synNumber, synComment: _synComment, synMethod: _synMethod,
synPunct: _synPunct,
);
static ThemeData get data => ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
scaffoldBackgroundColor: _bg,
canvasColor: _bg,
colorScheme: const ColorScheme.dark(
primary: _accent, onPrimary: Color(0xFF000000),
secondary: _info, onSecondary: Color(0xFF000000),
surface: _surface, onSurface: _textHi,
surfaceContainerHighest: _surfaceHi,
outline: _border, outlineVariant: _borderHi,
error: _err, onError: Color(0xFF000000),
),
textTheme: const TextTheme(
displayLarge: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 48, color: _textHi),
displayMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w300, fontSize: 34, color: _textHi),
headlineSmall: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 20, color: _textHi),
titleMedium: TextStyle(fontFamily: 'Josefin Sans', fontWeight: FontWeight.w400, fontSize: 14, color: _text),
// Terminal mockups go full-mono even for display.
labelSmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w500, fontSize: 10, letterSpacing: 1.0, color: _textMute),
bodyMedium: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 12, height: 1.45, color: _textHi),
bodySmall: TextStyle(fontFamily: 'JetBrains Mono', fontWeight: FontWeight.w400, fontSize: 11, color: _text),
),
dividerTheme: const DividerThemeData(color: _border, thickness: 1, space: 1),
iconTheme: const IconThemeData(color: _textDim, size: 14),
);
}