import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart' show Colors; import 'package:flutter_test/flutter_test.dart'; import 'package:tane/ui/theme.dart'; /// WCAG 2.x relative luminance of a color (sRGB). double _luminance(Color c) { double channel(double v) => v <= 0.03928 ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4).toDouble(); return 0.2126 * channel(c.r) + 0.7152 * channel(c.g) + 0.0722 * channel(c.b); } /// WCAG contrast ratio between two colors (order-independent, 1..21). double _contrast(Color a, Color b) { final la = _luminance(a); final lb = _luminance(b); final lighter = max(la, lb); final darker = min(la, lb); return (lighter + 0.05) / (darker + 0.05); } void main() { void expectAa(Color foreground, Color background, String label) { expect( _contrast(foreground, background), greaterThanOrEqualTo(4.5), reason: '$label must meet WCAG AA (4.5:1) for normal text', ); } group('palette meets WCAG AA (4.5:1) for text', () { test('seedMuted (hints, subtitles) on every background it sits on', () { // White: search-field fill and home cards. Canvas/field: M3 surfaces. expectAa(seedMuted, Colors.white, 'seedMuted on white'); expectAa(seedMuted, seedCanvas, 'seedMuted on canvas'); expectAa(seedMuted, seedField, 'seedMuted on field fill'); }); test('body and secondary text on the canvas', () { expectAa(seedOnSurface, seedCanvas, 'onSurface on canvas'); expectAa(seedTitle, seedCanvas, 'titles on canvas'); expectAa(seedOnSurfaceVariant, seedCanvas, 'onSurfaceVariant on canvas'); }); test('white text on the green action colours', () { expectAa(Colors.white, seedGreen, 'white on primary green'); expectAa(Colors.white, seedAppBar, 'white on app-bar green'); }); }); }