add four design themes + syntax tokens to theme pipeline
test / unit + widget + golden + a11y (push) Failing after 39s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped

SurfaceTokens gains seven syntax-role fields (keyword, type,
string, number, comment, method, punct). Theme loader parses
the syntax: YAML block and injects values into the surface
override layer. Resolver defaults map syntax roles to semantic
colours so themes without explicit syntax blocks still compile.

Four new theme YAMLs (clide, midnight, paper, terminal) replace
summer-night. Palette keys mapped to the existing semantic layer;
syntax values pass through directly. Goldens regenerated for the
14→font-size change.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-04-22 16:29:41 +02:00
co-authored by Claude
parent de9a7b9745
commit 5869697297
14 changed files with 220 additions and 5 deletions
+26 -1
View File
@@ -46,10 +46,35 @@ class ThemeLoader {
}
final palette = _parsePalette(paletteYaml);
// Syntax colours inject into the surface override layer so
// TokenKeys.syntax* resolve directly from the theme YAML.
final syntaxYaml = doc['syntax'];
final syntaxSurface = <String, String>{};
if (syntaxYaml is Map) {
const syntaxMap = {
'keyword': 'syntax.keyword',
'type': 'syntax.type',
'string': 'syntax.string',
'number': 'syntax.number',
'comment': 'syntax.comment',
'method': 'syntax.method',
'punct': 'syntax.punct',
};
syntaxYaml.forEach((k, v) {
final key = syntaxMap['$k'];
if (key != null && v is String) syntaxSurface[key] = v;
});
}
final semantic = doc['semantic'];
final surface = doc['surface'];
final extension = doc['extension'];
final mergedSurface = <String, String>{
...syntaxSurface,
if (surface is Map) ..._parseRefMap(surface),
};
return ThemeDefinition(
name: name,
displayName: displayName,
@@ -57,7 +82,7 @@ class ThemeLoader {
palette: palette,
semanticOverride:
semantic is Map ? _parseSemantic(semantic, palette) : null,
surfaceOverride: surface is Map ? _parseRefMap(surface) : null,
surfaceOverride: mergedSurface.isNotEmpty ? mergedSurface : null,
extensionOverride: extension is Map ? _parseRefMap(extension) : null,
);
}
+14
View File
@@ -102,6 +102,13 @@ class ThemeResolver {
statusWarning: surface[TokenKeys.statusWarning]!,
statusError: surface[TokenKeys.statusError]!,
statusInfo: surface[TokenKeys.statusInfo]!,
syntaxKeyword: surface[TokenKeys.syntaxKeyword]!,
syntaxType: surface[TokenKeys.syntaxType]!,
syntaxString: surface[TokenKeys.syntaxString]!,
syntaxNumber: surface[TokenKeys.syntaxNumber]!,
syntaxComment: surface[TokenKeys.syntaxComment]!,
syntaxMethod: surface[TokenKeys.syntaxMethod]!,
syntaxPunct: surface[TokenKeys.syntaxPunct]!,
extensionTokens: extTokens,
);
}
@@ -235,4 +242,11 @@ const Map<String, String> _defaultSurfaceMap = {
TokenKeys.statusWarning: 'semantic.warning',
TokenKeys.statusError: 'semantic.error',
TokenKeys.statusInfo: 'semantic.info',
TokenKeys.syntaxKeyword: 'semantic.calltoaction',
TokenKeys.syntaxType: 'semantic.info',
TokenKeys.syntaxString: 'semantic.success',
TokenKeys.syntaxNumber: 'semantic.warning',
TokenKeys.syntaxComment: 'semantic.text_muted',
TokenKeys.syntaxMethod: 'semantic.focus',
TokenKeys.syntaxPunct: 'semantic.text_muted',
};
@@ -0,0 +1,36 @@
# clide — cool near-black + periwinkle (default)
# Source: docs/claude-design/tokens/clide.yaml
name: clide
display_name: Clide
dark: true
palette:
background: "#20202C"
panel: "#1A1A24"
surface: "#242838"
muted: "#78809C"
foreground: "#E6E8F2"
secondary: "#B1BBE3"
primary: "#78A0F8"
accent: "#6C90DC"
success: "#7DD3A8"
warning: "#E6C370"
error: "#E87D7D"
info: "#78A0F8"
# extended palette keys for the design's richer vocabulary
surfaceHi: "#2C3046"
border: "#343850"
borderHi: "#3C445C"
textDim: "#78809C"
textMute: "#545C84"
accentSoft: "#2178A0F8"
syntax:
keyword: "#C792EA"
type: "#78A0F8"
string: "#A8D99B"
number: "#E6C370"
comment: "#545C84"
method: "#82B1FF"
punct: "#78809C"
@@ -0,0 +1,35 @@
# midnight — VS Code-adjacent muted dark
# Source: docs/claude-design/tokens/midnight.yaml
name: midnight
display_name: Midnight
dark: true
palette:
background: "#1E1E1E"
panel: "#181818"
surface: "#252526"
muted: "#858585"
foreground: "#D4D4D4"
secondary: "#BBBBBB"
primary: "#569CD6"
accent: "#4785BD"
success: "#89D185"
warning: "#D7BA7D"
error: "#F48771"
info: "#569CD6"
surfaceHi: "#2D2D2E"
border: "#333333"
borderHi: "#3F3F3F"
textDim: "#858585"
textMute: "#6A6A6A"
accentSoft: "#21569CD6"
syntax:
keyword: "#C586C0"
type: "#4EC9B0"
string: "#CE9178"
number: "#B5CEA8"
comment: "#6A9955"
method: "#DCDCAA"
punct: "#858585"
@@ -0,0 +1,35 @@
# paper — drafting sheet, red-pencil accent, light
# Source: docs/claude-design/tokens/paper.yaml
name: paper
display_name: Paper
dark: false
palette:
background: "#F4F1EA"
panel: "#ECE7DB"
surface: "#FBF8F1"
muted: "#8A8A82"
foreground: "#1A1A1A"
secondary: "#4A4A4A"
primary: "#C14B2A"
accent: "#A03D20"
success: "#2D8A52"
warning: "#B88A2A"
error: "#B03A2A"
info: "#2A6FC1"
surfaceHi: "#ECE7DB"
border: "#1A1A1A"
borderHi: "#4A4A4A"
textDim: "#8A8A82"
textMute: "#A8A89E"
accentSoft: "#21C14B2A"
syntax:
keyword: "#7B3F8C"
type: "#2A6FC1"
string: "#2D8A52"
number: "#B88A2A"
comment: "#8A8A82"
method: "#1E5D9E"
punct: "#4A4A4A"
@@ -0,0 +1,35 @@
# terminal — near-black + amber, tmux feel
# Source: docs/claude-design/tokens/terminal.yaml
name: terminal
display_name: Terminal
dark: true
palette:
background: "#0A0A0A"
panel: "#000000"
surface: "#111111"
muted: "#7A7A7A"
foreground: "#E6E6E6"
secondary: "#BDBDBD"
primary: "#E0B050"
accent: "#C29438"
success: "#8FDC9B"
warning: "#E0B050"
error: "#E05050"
info: "#A3C4FF"
surfaceHi: "#181818"
border: "#242424"
borderHi: "#2E2E2E"
textDim: "#7A7A7A"
textMute: "#4A4A4A"
accentSoft: "#21E0B050"
syntax:
keyword: "#E05050"
type: "#E0B050"
string: "#8FDC9B"
number: "#C792EA"
comment: "#4A4A4A"
method: "#A3C4FF"
punct: "#7A7A7A"
+32
View File
@@ -77,6 +77,14 @@ class SurfaceTokens {
required this.statusWarning,
required this.statusError,
required this.statusInfo,
// syntax
required this.syntaxKeyword,
required this.syntaxType,
required this.syntaxString,
required this.syntaxNumber,
required this.syntaxComment,
required this.syntaxMethod,
required this.syntaxPunct,
required this.extensionTokens,
});
@@ -146,6 +154,14 @@ class SurfaceTokens {
final Color statusError;
final Color statusInfo;
final Color syntaxKeyword;
final Color syntaxType;
final Color syntaxString;
final Color syntaxNumber;
final Color syntaxComment;
final Color syntaxMethod;
final Color syntaxPunct;
/// Extension-declared tokens keyed by their dotted path
/// (e.g. `ext.sqlite.table.background`).
final Map<String, Color> extensionTokens;
@@ -235,6 +251,15 @@ abstract class TokenKeys {
static const statusError = 'status.error';
static const statusInfo = 'status.info';
// syntax
static const syntaxKeyword = 'syntax.keyword';
static const syntaxType = 'syntax.type';
static const syntaxString = 'syntax.string';
static const syntaxNumber = 'syntax.number';
static const syntaxComment = 'syntax.comment';
static const syntaxMethod = 'syntax.method';
static const syntaxPunct = 'syntax.punct';
static const all = <String>[
globalForeground,
globalBackground,
@@ -289,5 +314,12 @@ abstract class TokenKeys {
statusWarning,
statusError,
statusInfo,
syntaxKeyword,
syntaxType,
syntaxString,
syntaxNumber,
syntaxComment,
syntaxMethod,
syntaxPunct,
];
}