tokenise window-control colours + palette shadow
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
test / unit + widget + golden + a11y (push) Failing after 28s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
Hard-coded Windows close-button red (#E81123), white close glyph, and the palette's 0x40000000 drop shadow were the three colour sites the UX consultant flagged as not adapting per theme. Now they're `windowControl.closeHoverBackground` / `closeHoverForeground` / `shadow.ambient`. Paper themes override the shadow to a softer ink so it doesn't read as a CRT halo on cream. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
+6
-2
@@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<String, List<String>> _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
|
||||
|
||||
@@ -28,6 +28,9 @@ palette:
|
||||
textMute: "#7A7A72"
|
||||
accentSoft: "#218E2D10"
|
||||
|
||||
surface:
|
||||
shadow.ambient: "#26000000"
|
||||
|
||||
syntax:
|
||||
keyword: "#52215C"
|
||||
type: "#154168"
|
||||
|
||||
@@ -25,6 +25,9 @@ palette:
|
||||
textMute: "#A8A89E"
|
||||
accentSoft: "#21C14B2A"
|
||||
|
||||
surface:
|
||||
shadow.ambient: "#26000000"
|
||||
|
||||
syntax:
|
||||
keyword: "#7B3F8C"
|
||||
type: "#2A6FC1"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,11 +126,11 @@ class _ClidePaletteState extends State<ClidePalette> {
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user