fix light theme

This commit is contained in:
Fabian Freund
2026-03-18 07:52:13 +01:00
parent 978606f4ae
commit 5d3f394f7b
4 changed files with 254 additions and 178 deletions
+97 -15
View File
@@ -22,19 +22,25 @@ import 'package:flutter/material.dart';
@immutable
class AppColors extends ThemeExtension<AppColors> {
const AppColors._({
this.seedColor = const Color(0xFF167C80),
this.privateTabPurple = const Color(0xFF8000D7),
this.privateTabBackground = const Color(0xFF25003E),
this.privateTabForeground = const Color(0xFFFFFFFF),
this.privateSelectionOverlay = const Color(0x648000D7),
this.isolatedTabTeal = const Color(0xFF00897B),
this.isolatedTabBackground = const Color(0xFF003D36),
this.isolatedTabForeground = const Color(0xFFFFFFFF),
this.isolatedSelectionOverlay = const Color(0x6400897B),
this.torPurple = const Color(0xFF7D4698),
this.torActiveGreen = const Color(0xFF68B030),
this.torBackgroundGrey = const Color(0xFF333A41),
this.warningAmber = const Color(0xFFFFA000),
required this.seedColor,
required this.privateTabPurple,
required this.privateTabBackground,
required this.privateTabForeground,
required this.privateSelectionOverlay,
required this.isolatedTabTeal,
required this.isolatedTabBackground,
required this.isolatedTabForeground,
required this.isolatedSelectionOverlay,
required this.torPurple,
required this.torActiveGreen,
required this.torBackgroundGrey,
required this.warningAmber,
required this.auraPurple,
required this.auraGold,
required this.auraShadow,
required this.auraShadowHighlight,
required this.auraTint,
required this.brandLink,
});
final Color seedColor;
@@ -50,9 +56,63 @@ class AppColors extends ThemeExtension<AppColors> {
final Color torActiveGreen;
final Color torBackgroundGrey;
final Color warningAmber;
final Color auraPurple;
final Color auraGold;
final Color auraShadow;
final Color auraShadowHighlight;
final Color auraTint;
final Color brandLink;
static const light = AppColors._();
static const dark = AppColors._();
/// Brand colors derived from the logo (constant across themes).
static const brandPurple = Color(0xFF9C83F8);
static const brandYellow = Color(0xFFFBDC6B);
static const brandGrey = Color(0xFFA7A7A7);
static const light = AppColors._(
seedColor: Color(0xFF167C80),
privateTabPurple: Color(0xFF8000D7),
privateTabBackground: Color(0xFFF3E5F5),
privateTabForeground: Color(0xFF4A0072),
privateSelectionOverlay: Color(0x648000D7),
isolatedTabTeal: Color(0xFF00897B),
isolatedTabBackground: Color(0xFFE0F2F1),
isolatedTabForeground: Color(0xFF004D40),
isolatedSelectionOverlay: Color(0x6400897B),
torPurple: Color(0xFF7D4698),
torActiveGreen: Color(0xFF68B030),
torBackgroundGrey: Color(0xFFECEFF1),
warningAmber: Color(0xFFFFA000),
// Aura: brand colors lightened toward white
auraPurple: Color(0xFFE0D6FC),
auraGold: Color(0xFFFDF1D6),
auraShadow: Color(0xFFEDEDF0),
auraShadowHighlight: Color(0xFFE2E2E7),
auraTint: Color(0xFFFFFFFF),
brandLink: Color(0xFF7A5AF0),
);
static const dark = AppColors._(
seedColor: Color(0xFF167C80),
privateTabPurple: Color(0xFF8000D7),
privateTabBackground: Color(0xFF25003E),
privateTabForeground: Color(0xFFFFFFFF),
privateSelectionOverlay: Color(0x648000D7),
isolatedTabTeal: Color(0xFF00897B),
isolatedTabBackground: Color(0xFF003D36),
isolatedTabForeground: Color(0xFFFFFFFF),
isolatedSelectionOverlay: Color(0x6400897B),
torPurple: Color(0xFF7D4698),
torActiveGreen: Color(0xFF68B030),
torBackgroundGrey: Color(0xFF333A41),
warningAmber: Color(0xFFFFA000),
// Aura: brand colors darkened toward black
auraPurple: Color(0xFF2C2543),
auraGold: Color(0xFF3C3827),
auraShadow: Color(0xFF222224),
auraShadowHighlight: Color(0xFF2D2D31),
auraTint: Color(0xFF000000),
brandLink: Color(0xFFFBDC6B),
);
@override
AppColors copyWith({
@@ -69,6 +129,12 @@ class AppColors extends ThemeExtension<AppColors> {
Color? torActiveGreen,
Color? torBackgroundGrey,
Color? warningAmber,
Color? auraPurple,
Color? auraGold,
Color? auraShadow,
Color? auraShadowHighlight,
Color? auraTint,
Color? brandLink,
}) {
return AppColors._(
seedColor: seedColor ?? this.seedColor,
@@ -88,6 +154,12 @@ class AppColors extends ThemeExtension<AppColors> {
torActiveGreen: torActiveGreen ?? this.torActiveGreen,
torBackgroundGrey: torBackgroundGrey ?? this.torBackgroundGrey,
warningAmber: warningAmber ?? this.warningAmber,
auraPurple: auraPurple ?? this.auraPurple,
auraGold: auraGold ?? this.auraGold,
auraShadow: auraShadow ?? this.auraShadow,
auraShadowHighlight: auraShadowHighlight ?? this.auraShadowHighlight,
auraTint: auraTint ?? this.auraTint,
brandLink: brandLink ?? this.brandLink,
);
}
@@ -143,6 +215,16 @@ class AppColors extends ThemeExtension<AppColors> {
t,
)!,
warningAmber: Color.lerp(warningAmber, other.warningAmber, t)!,
auraPurple: Color.lerp(auraPurple, other.auraPurple, t)!,
auraGold: Color.lerp(auraGold, other.auraGold, t)!,
auraShadow: Color.lerp(auraShadow, other.auraShadow, t)!,
auraShadowHighlight: Color.lerp(
auraShadowHighlight,
other.auraShadowHighlight,
t,
)!,
auraTint: Color.lerp(auraTint, other.auraTint, t)!,
brandLink: Color.lerp(brandLink, other.brandLink, t)!,
);
}
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:flutter_svg/svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/design/app_colors.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_list.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
@@ -72,148 +73,148 @@ class BrowserHome extends ConsumerWidget {
child: BrowserPageContent(
bottomViewportInset: bottomViewportInset,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
if (containerData != null)
_ContainerHeader(container: containerData)
else
BrandHeader(colorScheme: colorScheme),
const SizedBox(height: 24),
if (!hasTabs) ...[
Text(
'WebLibre is ready',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 10),
Text(
'A browser that respects you. Open a tab and experience the web, libre.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
] else if (containerData != null) ...[
Text(
containerData.name?.isNotEmpty == true
? containerData.name!
: 'Container',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 10),
Text(
hasContainerTabs
? 'No matching tab selected'
: 'No open tabs in this container',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
],
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: colorScheme.surfaceContainer.withValues(alpha: 0.9),
borderRadius: BorderRadius.circular(28),
border: Border.all(
color: Color.alphaBlend(
BrowserPage.brandGrey.withValues(alpha: 0.18),
colorScheme.outlineVariant,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
if (containerData != null)
_ContainerHeader(container: containerData)
else
BrandHeader(colorScheme: colorScheme),
const SizedBox(height: 24),
if (!hasTabs) ...[
Text(
'WebLibre is ready',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(12),
const SizedBox(height: 10),
Text(
'A browser that respects you. Open a tab and experience the web, libre.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
] else if (containerData != null) ...[
Text(
containerData.name?.isNotEmpty == true
? containerData.name!
: 'Container',
textAlign: TextAlign.center,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 10),
Text(
hasContainerTabs
? 'No matching tab selected'
: 'No open tabs in this container',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.45,
),
),
const SizedBox(height: 28),
],
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: colorScheme.surfaceContainer.withValues(alpha: 0.9),
borderRadius: BorderRadius.circular(28),
border: Border.all(
color: Color.alphaBlend(
AppColors.brandGrey.withValues(alpha: 0.18),
colorScheme.outlineVariant,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHigh,
borderRadius: BorderRadius.circular(12),
),
child: const Icon(MdiIcons.formatQuoteOpen, size: 18),
),
child: const Icon(MdiIcons.formatQuoteOpen, size: 18),
),
const SizedBox(width: 12),
Expanded(
child: Text(
'A thought for the road',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
const SizedBox(width: 12),
Expanded(
child: Text(
'A thought for the road',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
),
),
IconButton.filledTonal(
tooltip: 'Refresh quote',
onPressed: () {
ref.invalidate(randomQuoteProvider);
},
icon: const Icon(Icons.refresh_rounded),
),
],
),
const SizedBox(height: 16),
switch (quoteAsync) {
AsyncData(:final value) => _QuoteBlock(quote: value),
AsyncError() => Text(
'Open a new tab and make this space your own.',
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.5,
),
IconButton.filledTonal(
tooltip: 'Refresh quote',
onPressed: () {
ref.invalidate(randomQuoteProvider);
},
icon: const Icon(Icons.refresh_rounded),
),
],
),
const SizedBox(height: 16),
switch (quoteAsync) {
AsyncData(:final value) => _QuoteBlock(quote: value),
AsyncError() => Text(
'Open a new tab and make this space your own.',
style: theme.textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurfaceVariant,
height: 1.5,
),
),
_ => const LinearProgressIndicator(minHeight: 3),
},
],
),
),
const SizedBox(height: 24),
Wrap(
alignment: WrapAlignment.center,
spacing: 12,
runSpacing: 12,
children: [
if (hasTabs)
OutlinedButton.icon(
onPressed: viewTabs,
icon: const Icon(Icons.tab_rounded),
label: const Text('View tabs'),
),
FilledButton.icon(
onPressed: openNewTab,
icon: const Icon(Icons.add_rounded),
label: const Text('New tab'),
),
if (hasContainerTabs)
FilledButton.tonalIcon(
onPressed: resumeLatestContainerTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
)
else if (hasTabs && containerData == null)
FilledButton.tonalIcon(
onPressed: resumeLatestTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
),
_ => const LinearProgressIndicator(minHeight: 3),
},
],
),
),
const SizedBox(height: 24),
Wrap(
alignment: WrapAlignment.center,
spacing: 12,
runSpacing: 12,
children: [
if (hasTabs)
OutlinedButton.icon(
onPressed: viewTabs,
icon: const Icon(Icons.tab_rounded),
label: const Text('View tabs'),
),
FilledButton.icon(
onPressed: openNewTab,
icon: const Icon(Icons.add_rounded),
label: const Text('New tab'),
),
if (hasContainerTabs)
FilledButton.tonalIcon(
onPressed: resumeLatestContainerTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
)
else if (hasTabs && containerData == null)
FilledButton.tonalIcon(
onPressed: resumeLatestTab,
icon: const Icon(Icons.history_rounded),
label: const Text('Resume last tab'),
),
],
),
],
),
],
),
),
);
}
@@ -21,6 +21,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/design/app_colors.dart';
import 'package:weblibre/features/onboarding/domain/entities/onboarding_mode.dart';
import 'package:weblibre/features/onboarding/domain/providers.dart';
import 'package:weblibre/presentation/widgets/browser_page.dart';
@@ -130,9 +131,9 @@ class _UpdateNotice extends StatelessWidget {
children: [
Row(
children: [
const Icon(
Icon(
Icons.auto_awesome,
color: BrowserPage.brandYellow,
color: AppColors.of(context).brandLink,
size: 20,
),
const SizedBox(width: 8),
@@ -244,7 +245,7 @@ class _ModeOption extends StatelessWidget {
Icon(
icon,
color: selected
? BrowserPage.brandYellow
? AppColors.of(context).brandLink
: colorScheme.onSurfaceVariant,
size: 28,
),
@@ -320,9 +321,9 @@ class _EulaCheckbox extends StatelessWidget {
child: Text(
'EULA',
style: theme.textTheme.bodySmall?.copyWith(
color: BrowserPage.brandYellow,
color: AppColors.of(context).brandLink,
decoration: TextDecoration.underline,
decorationColor: BrowserPage.brandYellow,
decorationColor: AppColors.of(context).brandLink,
),
),
),
@@ -341,9 +342,9 @@ class _EulaCheckbox extends StatelessWidget {
child: Text(
'Privacy Policy',
style: theme.textTheme.bodySmall?.copyWith(
color: BrowserPage.brandYellow,
color: AppColors.of(context).brandLink,
decoration: TextDecoration.underline,
decorationColor: BrowserPage.brandYellow,
decorationColor: AppColors.of(context).brandLink,
),
),
),
+14 -22
View File
@@ -4,6 +4,7 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/design/app_colors.dart';
class BrowserPage extends ConsumerWidget {
final double bottomViewportInset;
@@ -11,20 +12,11 @@ class BrowserPage extends ConsumerWidget {
const BrowserPage({super.key, this.bottomViewportInset = 0, required this.child});
static const brandPurple = Color(0xFF9C83F8);
static const brandYellow = Color(0xFFFBDC6B);
static const brandGrey = Color(0xFFA7A7A7);
static const auraPurple = Color(0xFF2C2543);
static const auraGold = Color(0xFF3C3827);
static const auraShadow = Color(0xFF222224);
static const auraShadowHighlight = Color(0xFF2D2D31);
static const auraTint = Color(0xFF000000);
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final appColors = AppColors.of(context);
return DecoratedBox(
decoration: BoxDecoration(
@@ -33,15 +25,15 @@ class BrowserPage extends ConsumerWidget {
end: Alignment.bottomRight,
colors: [
Color.alphaBlend(
auraPurple.withValues(alpha: 0.38),
appColors.auraPurple.withValues(alpha: 0.38),
colorScheme.surfaceContainerLowest,
),
Color.alphaBlend(
auraShadow.withValues(alpha: 0.72),
appColors.auraShadow.withValues(alpha: 0.72),
colorScheme.surface,
),
Color.alphaBlend(
auraGold.withValues(alpha: 0.34),
appColors.auraGold.withValues(alpha: 0.34),
colorScheme.surfaceContainerHigh,
),
],
@@ -50,23 +42,23 @@ class BrowserPage extends ConsumerWidget {
child: Stack(
fit: StackFit.expand,
children: [
const Positioned(
Positioned(
top: -70,
left: -120,
child: _BackdropOrb(width: 400, height: 400, color: auraPurple),
child: _BackdropOrb(width: 400, height: 400, color: appColors.auraPurple),
),
const Positioned(
Positioned(
top: 220,
right: -150,
child: _BackdropOrb(width: 340, height: 340, color: auraGold),
child: _BackdropOrb(width: 340, height: 340, color: appColors.auraGold),
),
const Positioned(
Positioned(
bottom: 18,
left: -8,
child: _BackdropOrb(
width: 320,
height: 320,
color: auraShadowHighlight,
color: appColors.auraShadowHighlight,
),
),
Positioned.fill(
@@ -74,7 +66,7 @@ class BrowserPage extends ConsumerWidget {
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 72, sigmaY: 72),
child: ColoredBox(color: auraTint.withValues(alpha: 0.12)),
child: ColoredBox(color: appColors.auraTint.withValues(alpha: 0.12)),
),
),
),
@@ -146,11 +138,11 @@ class BrandHeader extends StatelessWidget {
end: Alignment.bottomRight,
colors: [
Color.alphaBlend(
BrowserPage.brandPurple.withValues(alpha: 0.18),
AppColors.brandPurple.withValues(alpha: 0.18),
colorScheme.surfaceContainerHighest,
),
Color.alphaBlend(
BrowserPage.brandYellow.withValues(alpha: 0.12),
AppColors.brandYellow.withValues(alpha: 0.12),
colorScheme.surfaceContainer,
),
],