From 807faf9b90efe7710b3ed1c26256a0df782e0268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0hsan=20G=C3=B6rgel?= Date: Wed, 12 Aug 2026 01:42:18 +0300 Subject: [PATCH 1/2] Fix unreadable release notes blockquote in dark theme MarkdownStyleSheet.fromTheme takes the blockquote text style from the theme but hard codes the fill as Colors.blue.shade100, so the side panel drew onSurface text on light blue. Release notes that open with a blockquote, like 2.60.0, were unreadable in the dark theme. The panel now passes its own blockquote decoration, and widget tests assert the text clears WCAG AA contrast in both themes. --- .../lib/src/shared/ui/side_panel.dart | 11 ++ .../release_notes/NEXT_RELEASE_NOTES.md | 4 +- .../test/shared/ui/side_panel_test.dart | 102 ++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 packages/devtools_app/test/shared/ui/side_panel_test.dart diff --git a/packages/devtools_app/lib/src/shared/ui/side_panel.dart b/packages/devtools_app/lib/src/shared/ui/side_panel.dart index 16d19434ebd..9174f9296b3 100644 --- a/packages/devtools_app/lib/src/shared/ui/side_panel.dart +++ b/packages/devtools_app/lib/src/shared/ui/side_panel.dart @@ -182,6 +182,17 @@ class SidePanel extends AnimatedWidget { : Expanded( child: Markdown( data: markdownData!, + styleSheet: MarkdownStyleSheet( + // [MarkdownStyleSheet.fromTheme], which supplies the + // rest of the style sheet, hard codes + // `Colors.blue.shade100` as the blockquote fill while + // taking the text color from the theme. In the dark + // theme that draws light gray text on light blue. + blockquoteDecoration: BoxDecoration( + color: theme.colorScheme.secondaryContainer, + borderRadius: defaultBorderRadius, + ), + ), onTapLink: (text, url, title) => unawaited(launchUrlWithErrorHandling(url!)), ), diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index f589bd1821b..3c19a153366 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -15,7 +15,9 @@ To learn more about DevTools, check out the ## General updates -TODO: Remove this section if there are not any updates. +* Fixed unreadable text in the release notes panel, where blockquotes were + drawn on a hard coded light blue background in the dark theme. + [#9945](https://github.com/flutter/devtools/issues/9945) ## Inspector updates diff --git a/packages/devtools_app/test/shared/ui/side_panel_test.dart b/packages/devtools_app/test/shared/ui/side_panel_test.dart new file mode 100644 index 00000000000..8d003a08080 --- /dev/null +++ b/packages/devtools_app/test/shared/ui/side_panel_test.dart @@ -0,0 +1,102 @@ +// Copyright 2026 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. + +import 'package:devtools_app/devtools_app.dart'; +import 'package:devtools_app_shared/ui.dart'; +import 'package:devtools_app_shared/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// The smallest contrast ratio WCAG 2.1 accepts for body text at level AA. +/// +/// See https://www.w3.org/TR/WCAG21/#contrast-minimum. +const _minimumContrastRatio = 4.5; + +void main() { + setUp(() { + setGlobal(IdeTheme, IdeTheme()); + }); + + group('$SidePanelViewer', () { + // `MarkdownStyleSheet.fromTheme` fills blockquotes with + // `Colors.blue.shade100` but takes the text color from the theme, so a + // release note that opens with a blockquote drew `onSurface` text on light + // blue in the dark theme. + // Regression test for https://github.com/flutter/devtools/issues/9945. + for (final useDarkTheme in [true, false]) { + final themeName = useDarkTheme ? 'dark' : 'light'; + testWidgets('blockquote text is legible in the $themeName theme', ( + tester, + ) async { + const summary = 'Release notes for Dart and Flutter DevTools.'; + final controller = SidePanelController(); + await tester.pumpWidget( + MaterialApp( + theme: themeFor( + isDarkTheme: useDarkTheme, + ideTheme: IdeTheme(), + theme: ThemeData( + useMaterial3: true, + colorScheme: useDarkTheme ? darkColorScheme : lightColorScheme, + ), + ), + home: SidePanelViewer(controller: controller), + ), + ); + controller.markdown.value = '# Release notes\n\n> $summary'; + controller.toggleVisibility(true); + await tester.pumpAndSettle(); + + final summaryFinder = find.byWidgetPredicate( + (widget) => + widget is RichText && widget.text.toPlainText() == summary, + ); + expect(summaryFinder, findsOneWidget); + + final textColor = _colorOfSpan( + tester.widget(summaryFinder).text, + summary, + ); + final fillColor = tester + .widgetList( + find.ancestor( + of: summaryFinder, + matching: find.byType(DecoratedBox), + ), + ) + .map((box) => box.decoration) + .whereType() + .map((decoration) => decoration.color) + .nonNulls + .first; + + expect( + _contrastRatio(textColor!, fillColor), + greaterThanOrEqualTo(_minimumContrastRatio), + ); + }); + } + }); +} + +/// The color the span holding [text] is painted with, or null if [root] holds +/// no such span. +Color? _colorOfSpan(InlineSpan root, String text) { + Color? color; + root.visitChildren((span) { + if (span is TextSpan && span.text == text) { + color = span.style?.color; + return false; + } + return true; + }); + return color; +} + +/// The WCAG contrast ratio between [a] and [b], from 1 (identical) to 21 +/// (black on white). +double _contrastRatio(Color a, Color b) { + final luminances = [a.computeLuminance(), b.computeLuminance()]..sort(); + return (luminances.last + 0.05) / (luminances.first + 0.05); +} From af1bd8cce1f39d92d0901bcfa2cf27b46ee806a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0hsan=20G=C3=B6rgel?= Date: Wed, 12 Aug 2026 14:47:18 +0300 Subject: [PATCH 2/2] Point the release note entry at the pull request --- packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 3c19a153366..87c945c27e6 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -17,7 +17,7 @@ To learn more about DevTools, check out the * Fixed unreadable text in the release notes panel, where blockquotes were drawn on a hard coded light blue background in the dark theme. - [#9945](https://github.com/flutter/devtools/issues/9945) + [#9957](https://github.com/flutter/devtools/pull/9957) ## Inspector updates