diff --git a/CHANGELOG.md b/CHANGELOG.md index cdfac7e1..7f368678 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,10 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit. ### Changed +- Window-control close-button red, white close glyph, and palette + ambient shadow are now tokens (`windowControl.closeHover*`, + `shadow.ambient`) instead of hard-coded hex. Light themes get a + softer ink-tinted shadow (T-114). - Changelog gate is binary — dropped the soft 40-word warning, kept the 60-word hard cap. Warnings that never blocked just normalised drift. diff --git a/lib/app.dart b/lib/app.dart index 7772f559..ae38a6f3 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -346,7 +346,7 @@ class _WinBtn extends StatelessWidget { @override Widget build(BuildContext context) { - final hoverBg = isClose ? const Color(0xFFE81123) : tokens.listItemHoverBackground; + final hoverBg = isClose ? tokens.windowControlCloseHoverBackground : tokens.listItemHoverBackground; return ClideTappable( onTap: onTap, builder: (context, hovered, _) => Container( @@ -354,7 +354,11 @@ class _WinBtn extends StatelessWidget { height: hatHeight, color: hovered ? hoverBg : null, alignment: Alignment.center, - child: ClideIcon(icon, size: 14, color: hovered && isClose ? const Color(0xFFFFFFFF) : tokens.chromeForeground), + child: ClideIcon( + icon, + size: 14, + color: hovered && isClose ? tokens.windowControlCloseHoverForeground : tokens.chromeForeground, + ), ), ); } diff --git a/lib/kernel/src/theme/resolver.dart b/lib/kernel/src/theme/resolver.dart index 140fba99..caff6103 100644 --- a/lib/kernel/src/theme/resolver.dart +++ b/lib/kernel/src/theme/resolver.dart @@ -96,6 +96,9 @@ class ThemeResolver { modalOverlayBackground: surface[TokenKeys.modalOverlayBackground]!, modalSurfaceBackground: surface[TokenKeys.modalSurfaceBackground]!, modalSurfaceBorder: surface[TokenKeys.modalSurfaceBorder]!, + windowControlCloseHoverBackground: surface[TokenKeys.windowControlCloseHoverBackground]!, + windowControlCloseHoverForeground: surface[TokenKeys.windowControlCloseHoverForeground]!, + shadowAmbient: surface[TokenKeys.shadowAmbient]!, dividerColor: surface[TokenKeys.dividerColor]!, statusSuccess: surface[TokenKeys.statusSuccess]!, statusWarning: surface[TokenKeys.statusWarning]!, @@ -250,6 +253,14 @@ const Map> _defaultSurfaceMap = { TokenKeys.modalOverlayBackground: ['#C0000000'], TokenKeys.modalSurfaceBackground: ['surface', 'semantic.mainchrome'], TokenKeys.modalSurfaceBorder: ['accent', 'semantic.focus'], + // window controls — the Windows-style red close button is a hard + // platform convention, not a theme accent; the foreground stays + // white so the glyph remains visible across all themes. + TokenKeys.windowControlCloseHoverBackground: ['#FFE81123'], + TokenKeys.windowControlCloseHoverForeground: ['#FFFFFFFF'], + // shadow — translucent black works for dark themes; light themes can + // override this to a translucent ink (e.g. paper uses softer alpha). + TokenKeys.shadowAmbient: ['#40000000'], // divider TokenKeys.dividerColor: ['border', 'semantic.surface'], // status diff --git a/lib/kernel/src/theme/themes/paper-hc.yaml b/lib/kernel/src/theme/themes/paper-hc.yaml index e890f1d8..63db3fe0 100644 --- a/lib/kernel/src/theme/themes/paper-hc.yaml +++ b/lib/kernel/src/theme/themes/paper-hc.yaml @@ -28,6 +28,9 @@ palette: textMute: "#7A7A72" accentSoft: "#218E2D10" +surface: + shadow.ambient: "#26000000" + syntax: keyword: "#52215C" type: "#154168" diff --git a/lib/kernel/src/theme/themes/paper.yaml b/lib/kernel/src/theme/themes/paper.yaml index 84a7f669..4c82b23d 100644 --- a/lib/kernel/src/theme/themes/paper.yaml +++ b/lib/kernel/src/theme/themes/paper.yaml @@ -25,6 +25,9 @@ palette: textMute: "#A8A89E" accentSoft: "#21C14B2A" +surface: + shadow.ambient: "#26000000" + syntax: keyword: "#7B3F8C" type: "#2A6FC1" diff --git a/lib/kernel/src/theme/tokens.dart b/lib/kernel/src/theme/tokens.dart index 9779e9e2..0c15eed3 100644 --- a/lib/kernel/src/theme/tokens.dart +++ b/lib/kernel/src/theme/tokens.dart @@ -74,6 +74,11 @@ class SurfaceTokens { required this.modalOverlayBackground, required this.modalSurfaceBackground, required this.modalSurfaceBorder, + // window controls + required this.windowControlCloseHoverBackground, + required this.windowControlCloseHoverForeground, + // shadow + required this.shadowAmbient, // divider required this.dividerColor, // status @@ -155,6 +160,17 @@ class SurfaceTokens { final Color modalSurfaceBackground; final Color modalSurfaceBorder; + /// Hover background on the Windows-style close button. Conventionally + /// the destructive-action red even on dark themes that don't otherwise + /// surface red as a CTA. + final Color windowControlCloseHoverBackground; + final Color windowControlCloseHoverForeground; + + /// Ambient drop-shadow color for floating surfaces (palette, + /// dropdowns, modals). Typically a translucent black on dark themes + /// and a translucent ink on light themes. + final Color shadowAmbient; + final Color dividerColor; final Color statusSuccess; @@ -255,6 +271,13 @@ abstract class TokenKeys { static const modalSurfaceBackground = 'modal.surfaceBackground'; static const modalSurfaceBorder = 'modal.surfaceBorder'; + // window controls + static const windowControlCloseHoverBackground = 'windowControl.closeHoverBackground'; + static const windowControlCloseHoverForeground = 'windowControl.closeHoverForeground'; + + // shadow + static const shadowAmbient = 'shadow.ambient'; + // divider static const dividerColor = 'divider.color'; @@ -325,6 +348,9 @@ abstract class TokenKeys { modalOverlayBackground, modalSurfaceBackground, modalSurfaceBorder, + windowControlCloseHoverBackground, + windowControlCloseHoverForeground, + shadowAmbient, dividerColor, statusSuccess, statusWarning, diff --git a/lib/widgets/src/clide_column_hat.dart b/lib/widgets/src/clide_column_hat.dart index 3feb1912..71f47583 100644 --- a/lib/widgets/src/clide_column_hat.dart +++ b/lib/widgets/src/clide_column_hat.dart @@ -108,7 +108,7 @@ class _WinButton extends StatelessWidget { @override Widget build(BuildContext context) { - final hoverBg = isClose ? const Color(0xFFE81123) : tokens.listItemHoverBackground; + final hoverBg = isClose ? tokens.windowControlCloseHoverBackground : tokens.listItemHoverBackground; return ClideTappable( onTap: onTap, builder: (context, hovered, _) => Container( @@ -116,7 +116,11 @@ class _WinButton extends StatelessWidget { height: hatHeight, color: hovered ? hoverBg : null, alignment: Alignment.center, - child: ClideIcon(icon, size: 14, color: hovered && isClose ? const Color(0xFFFFFFFF) : tokens.globalTextMuted), + child: ClideIcon( + icon, + size: 14, + color: hovered && isClose ? tokens.windowControlCloseHoverForeground : tokens.globalTextMuted, + ), ), ); } diff --git a/lib/widgets/src/clide_palette.dart b/lib/widgets/src/clide_palette.dart index 72584c1e..9a658e39 100644 --- a/lib/widgets/src/clide_palette.dart +++ b/lib/widgets/src/clide_palette.dart @@ -126,11 +126,11 @@ class _ClidePaletteState extends State { color: tokens.dropdownBackground, border: Border.all(color: tokens.dropdownBorder), borderRadius: BorderRadius.circular(6), - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Color(0x40000000), + color: tokens.shadowAmbient, blurRadius: 12, - offset: Offset(0, 4), + offset: const Offset(0, 4), ), ], ),