Ships catppuccin-mocha (faithful to the official Catppuccin Mocha palette) and catppuccin-mocha-hc (high-contrast sibling per D-69 — the faithful palette is never retuned to pass the gate). Both bundled and gated: mocha clears baseline AA, mocha-hc clears the strict extended set. Catppuccin MIT palette recorded in licenses.yaml. Closes T-82 (Mocha + hc meets every acceptance bullet; faithful Latte deferred — it can't clear baseline AA without retuning, a separate call). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
61 lines
2.3 KiB
Dart
61 lines
2.3 KiB
Dart
import 'package:clide/kernel/kernel.dart';
|
|
import 'package:flutter/services.dart' show rootBundle;
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
/// WCAG-AA contrast gate. Every bundled theme's token pairs (listed in
|
|
/// [canonicalPairs]) must clear 4.5:1 for normal text / 3:1 for large
|
|
/// text. Failing pairs are printed with their computed ratio so a
|
|
/// theme-token regression shows exactly which pair broke.
|
|
///
|
|
/// Themes whose name ends in `-hc` (high-contrast) or `-cb`
|
|
/// (colour-blind) additionally have to clear the stricter
|
|
/// [extendedPairs] set — D-69.
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
group('contrast — all bundled themes meet WCAG AA', () {
|
|
const bundledPaths = [
|
|
'lib/kernel/src/theme/themes/clide.yaml',
|
|
'lib/kernel/src/theme/themes/midnight.yaml',
|
|
'lib/kernel/src/theme/themes/paper.yaml',
|
|
'lib/kernel/src/theme/themes/terminal.yaml',
|
|
'lib/kernel/src/theme/themes/clide-hc.yaml',
|
|
'lib/kernel/src/theme/themes/midnight-hc.yaml',
|
|
'lib/kernel/src/theme/themes/paper-hc.yaml',
|
|
'lib/kernel/src/theme/themes/terminal-hc.yaml',
|
|
'lib/kernel/src/theme/themes/catppuccin-mocha.yaml',
|
|
'lib/kernel/src/theme/themes/catppuccin-mocha-hc.yaml',
|
|
];
|
|
|
|
for (final path in bundledPaths) {
|
|
test('theme: $path', () async {
|
|
final def = await const ThemeLoader().fromAsset(rootBundle, path);
|
|
const resolver = ThemeResolver();
|
|
final tokens = resolver.resolve(
|
|
palette: def.palette,
|
|
semanticOverride: def.semanticOverride,
|
|
surfaceOverride: def.surfaceOverride,
|
|
extensionOverride: def.extensionOverride,
|
|
);
|
|
final failures = failingPairs(tokens);
|
|
if (failures.isNotEmpty) {
|
|
fail(
|
|
'Contrast failures in ${def.name}:\n'
|
|
'${failures.map((f) => ' - $f').join('\n')}',
|
|
);
|
|
}
|
|
final isStrict = def.name.endsWith('-hc') || def.name.endsWith('-cb');
|
|
if (isStrict) {
|
|
final extended = failingExtendedPairs(tokens);
|
|
if (extended.isNotEmpty) {
|
|
fail(
|
|
'Extended contrast failures in ${def.name} (strict gate):\n'
|
|
'${extended.map((f) => ' - $f').join('\n')}',
|
|
);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|