feat(flutter/theme): codegen emits dark + light + flat token classes

gen_tokens.dart now produces FabledSwordDarkTokens (dark surface),
FabledSwordLightTokens (light surface), and FabledSwordFlatTokens
(colors that don't change + radii + fonts). The original
FabledSwordTokens class stays as a back-compat alias re-exporting
the dark surface + flat values; any code that reads
FabledSwordTokens.obsidian etc. continues to work during migration.

Source: shared/fabledsword.tokens.json (already had dark/light/flat
splits — this was the generator catching up).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-09 15:45:40 -04:00
parent 25ff7afc33
commit b1f62a0733
2 changed files with 90 additions and 11 deletions
+50 -11
View File
@@ -2,10 +2,16 @@
// Run via `dart run tool/gen_tokens.dart`. The generated file is
// committed (CI validates it matches the JSON to catch drift).
//
// Web tokens.json splits colors into `dark`, `light`, and `flat` blocks
// for the SPA's theme toggle. Flutter v1 ships dark-only, so we merge
// dark + flat into a single flat namespace. Light variants will be
// emitted alongside as part of the Flutter theme-toggle parity work.
// Emits three flat classes mirroring the source JSON:
// - FabledSwordDarkTokens (surface colors for dark mode)
// - FabledSwordLightTokens (surface colors for light mode)
// - FabledSwordFlatTokens (colors that don't change between modes,
// plus radii + font families)
//
// Plus a back-compat FabledSwordTokens alias that re-exports the dark
// surface colors + flat — kept so any code that still reads
// FabledSwordTokens.obsidian (etc.) continues to work during the
// migration. Safe to delete once theme_extension.dart switches over.
import 'dart:convert';
import 'dart:io';
@@ -15,9 +21,8 @@ void main() {
final colorsRoot = (tokens['colors'] as Map).cast<String, dynamic>();
final dark = (colorsRoot['dark'] as Map).cast<String, String>();
final light = (colorsRoot['light'] as Map).cast<String, String>();
final flat = (colorsRoot['flat'] as Map).cast<String, String>();
final colors = <String, String>{...dark, ...flat};
final radii = (tokens['radii'] as Map).cast<String, String>();
final fonts = (tokens['fonts'] as Map).cast<String, String>();
@@ -25,23 +30,57 @@ void main() {
..writeln('// GENERATED — do not edit. Source: shared/fabledsword.tokens.json')
..writeln('// Run `dart run tool/gen_tokens.dart` to regenerate.')
..writeln("import 'package:flutter/material.dart';")
..writeln()
..writeln('class FabledSwordTokens {');
..writeln();
colors.forEach((name, hex) {
void writeColorClass(String className, Map<String, String> colors) {
out.writeln('class $className {');
colors.forEach((name, hex) {
final value = hex.replaceFirst('#', '0xFF');
out.writeln(' static const Color ${_camel(name)} = Color($value);');
});
out.writeln('}');
out.writeln();
}
writeColorClass('FabledSwordDarkTokens', dark);
writeColorClass('FabledSwordLightTokens', light);
out.writeln('class FabledSwordFlatTokens {');
flat.forEach((name, hex) {
final value = hex.replaceFirst('#', '0xFF');
out.writeln(' static const Color ${_camel(name)} = Color($value);');
});
radii.forEach((name, px) {
final v = px.replaceAll('px', '');
out.writeln(' static const double radius${_pascal(name)} = $v;');
});
fonts.forEach((name, family) {
out.writeln(' static const String font${_pascal(name)} = ${jsonEncode(family)};');
});
out.writeln('}');
out.writeln();
// Back-compat alias: dark surface colors + flat. Lets existing
// call sites that read FabledSwordTokens.obsidian etc. keep working
// until they're migrated to the explicit dark/light/flat classes.
out.writeln('/// Back-compat alias — dark surface tokens + flat. Prefer the explicit');
out.writeln('/// FabledSwordDarkTokens / FabledSwordLightTokens / FabledSwordFlatTokens.');
out.writeln('class FabledSwordTokens {');
dark.forEach((name, hex) {
final value = hex.replaceFirst('#', '0xFF');
out.writeln(' static const Color ${_camel(name)} = Color($value);');
});
flat.forEach((name, hex) {
final value = hex.replaceFirst('#', '0xFF');
out.writeln(' static const Color ${_camel(name)} = Color($value);');
});
radii.forEach((name, px) {
final v = px.replaceAll('px', '');
out.writeln(' static const double radius${_pascal(name)} = $v;');
});
fonts.forEach((name, family) {
out.writeln(' static const String font${_pascal(name)} = ${jsonEncode(family)};');
});
out.writeln('}');
File('lib/theme/tokens.dart').writeAsStringSync(out.toString());