add chrome token set for frame surfaces
New tokens: chromeBackground, chromeForeground, chromeBorder — shared root for hat bar, sidebar, context panel, status bar, spines, and drag handles. All resolve to bgSunken/textDim/border in the palette by default. Themes can override individually to diverge surfaces. Replaces direct sidebarBackground/statusBar references in chrome contexts. Updated theme-ui skill. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,19 +18,19 @@ Never hardcode colors. Never use Material/Cupertino color constants.
|
||||
|
||||
Pick tokens based on **where** the widget lives, not what it does.
|
||||
|
||||
### Chrome (hat bar, status bar, spines)
|
||||
### Chrome (hat bar, status bar, sidebar, context panel, spines, drag handles)
|
||||
|
||||
```
|
||||
background → sidebarBackground
|
||||
border → dividerColor (1px)
|
||||
text → globalTextMuted
|
||||
background → chromeBackground
|
||||
text → chromeForeground
|
||||
border → chromeBorder (1px)
|
||||
active text → globalForeground
|
||||
```
|
||||
|
||||
### Side panels (sidebar, context panel)
|
||||
|
||||
```
|
||||
background → sidebarBackground (left) / panelBackground (right)
|
||||
background → chromeBackground (both sides — they're chrome frame)
|
||||
text → sidebarForeground
|
||||
hover → sidebarItemHover
|
||||
selected → sidebarItemSelected
|
||||
@@ -149,9 +149,10 @@ The palette layer has these depth primitives:
|
||||
- `surface` (`#242838`) — elevated: pane headers, active tabs
|
||||
- `surfaceHi` (`#2C3046`) — interactive: hover states, selections
|
||||
|
||||
**Pending:** `chromeBackground`/`chromeForeground`/`chromeBorder` tokens
|
||||
need to be added to `SurfaceTokens` so hat bar, sidebar, and status bar
|
||||
share a named root instead of cross-referencing each other's tokens.
|
||||
Chrome tokens (`chromeBackground`/`chromeForeground`/`chromeBorder`) are
|
||||
the shared root for all frame surfaces. They resolve to `bgSunken` /
|
||||
`textDim` / `border` in the palette. Themes can override them to diverge
|
||||
hat from sidebar from status bar if desired.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
|
||||
+9
-9
@@ -177,7 +177,7 @@ class RootLayout extends StatelessWidget {
|
||||
if (statusVisible)
|
||||
Container(
|
||||
height: statusHeight,
|
||||
decoration: BoxDecoration(border: Border(top: BorderSide(color: ClideTheme.of(ctx).surface.dividerColor))),
|
||||
decoration: BoxDecoration(border: Border(top: BorderSide(color: ClideTheme.of(ctx).surface.chromeBorder))),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@@ -222,8 +222,8 @@ class _HatBar extends StatelessWidget {
|
||||
child: Container(
|
||||
height: hatHeight,
|
||||
decoration: BoxDecoration(
|
||||
color: tokens.sidebarBackground,
|
||||
border: Border(bottom: BorderSide(color: tokens.dividerColor, width: 1)),
|
||||
color: tokens.chromeBackground,
|
||||
border: Border(bottom: BorderSide(color: tokens.chromeBorder, width: 1)),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Row(
|
||||
@@ -238,7 +238,7 @@ class _HatBar extends StatelessWidget {
|
||||
return ClideText(
|
||||
name != null ? 'clide > $name' : 'clide',
|
||||
fontSize: 12,
|
||||
color: tokens.globalTextMuted,
|
||||
color: tokens.chromeForeground,
|
||||
fontFamily: clideMonoFamily,
|
||||
);
|
||||
},
|
||||
@@ -343,7 +343,7 @@ class _WinBtnState extends State<_WinBtn> {
|
||||
width: 36, height: hatHeight,
|
||||
color: _hover ? hoverBg : null,
|
||||
alignment: Alignment.center,
|
||||
child: ClideIcon(widget.icon, size: 14, color: _hover && widget.isClose ? const Color(0xFFFFFFFF) : widget.tokens.globalTextMuted),
|
||||
child: ClideIcon(widget.icon, size: 14, color: _hover && widget.isClose ? const Color(0xFFFFFFFF) : widget.tokens.chromeForeground),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -442,7 +442,7 @@ class _SidebarSlot extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = ClideTheme.of(context).surface;
|
||||
return Container(
|
||||
color: tokens.sidebarBackground,
|
||||
color: tokens.chromeBackground,
|
||||
alignment: Alignment.topLeft,
|
||||
padding: const EdgeInsets.fromLTRB(2, 2, 0, 0),
|
||||
child: active.build(context),
|
||||
@@ -579,10 +579,10 @@ class _BottomRail extends StatelessWidget {
|
||||
listenable: kernel.panels,
|
||||
builder: (ctx, _) {
|
||||
final tabs = kernel.panels.tabsFor(slot);
|
||||
if (tabs.isEmpty) return Container(color: tokens.statusBarBackground);
|
||||
if (tabs.isEmpty) return Container(color: tokens.chromeBackground);
|
||||
final activeId = kernel.panels.activeTabIn(slot) ?? tabs.first.id;
|
||||
return Container(
|
||||
color: tokens.statusBarBackground,
|
||||
color: tokens.chromeBackground,
|
||||
child: ClideIconRail(
|
||||
items: [
|
||||
for (final t in tabs)
|
||||
@@ -636,7 +636,7 @@ class StatusbarHost extends StatelessWidget {
|
||||
final left = items.where((i) => i.priority < 100).toList();
|
||||
final right = items.where((i) => i.priority >= 100).toList();
|
||||
return Container(
|
||||
color: tokens.statusBarBackground,
|
||||
color: tokens.chromeBackground,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
alignment: Alignment.center,
|
||||
child: Row(
|
||||
|
||||
@@ -49,7 +49,7 @@ class _DragResizeHandleState extends State<DragResizeHandle> {
|
||||
child: Container(
|
||||
width: widget.axis == Axis.horizontal ? widget.thickness : null,
|
||||
height: widget.axis == Axis.vertical ? widget.thickness : null,
|
||||
color: widget.slot == Slots.sidebar ? tokens.sidebarBackground : tokens.panelBackground,
|
||||
color: tokens.chromeBackground,
|
||||
child: Align(
|
||||
alignment: widget.slot == Slots.sidebar ? Alignment.centerRight : Alignment.centerLeft,
|
||||
child: Container(
|
||||
|
||||
@@ -50,6 +50,9 @@ class ThemeResolver {
|
||||
globalBorder: surface[TokenKeys.globalBorder]!,
|
||||
globalFocus: surface[TokenKeys.globalFocus]!,
|
||||
globalTextMuted: surface[TokenKeys.globalTextMuted]!,
|
||||
chromeBackground: surface[TokenKeys.chromeBackground]!,
|
||||
chromeForeground: surface[TokenKeys.chromeForeground]!,
|
||||
chromeBorder: surface[TokenKeys.chromeBorder]!,
|
||||
panelBackground: surface[TokenKeys.panelBackground]!,
|
||||
panelBorder: surface[TokenKeys.panelBorder]!,
|
||||
panelActiveBorder: surface[TokenKeys.panelActiveBorder]!,
|
||||
@@ -196,6 +199,10 @@ const Map<String, List<String>> _defaultSurfaceMap = {
|
||||
TokenKeys.globalBorder: ['border', 'semantic.surface'],
|
||||
TokenKeys.globalFocus: ['accent', 'semantic.focus'],
|
||||
TokenKeys.globalTextMuted: ['textDim', 'semantic.text_muted'],
|
||||
// chrome — frame surfaces (hat bar, sidebar, status bar, spines)
|
||||
TokenKeys.chromeBackground: ['bgSunken', 'semantic.mainchrome'],
|
||||
TokenKeys.chromeForeground: ['textDim', 'semantic.text_muted'],
|
||||
TokenKeys.chromeBorder: ['border', 'semantic.surface'],
|
||||
// panel
|
||||
TokenKeys.panelBackground: ['bgSunken', 'semantic.mainchrome'],
|
||||
TokenKeys.panelBorder: ['border', 'semantic.surface'],
|
||||
|
||||
@@ -17,6 +17,10 @@ class SurfaceTokens {
|
||||
required this.globalBorder,
|
||||
required this.globalFocus,
|
||||
required this.globalTextMuted,
|
||||
// chrome (hat bar, sidebar, status bar, spines — frame surfaces)
|
||||
required this.chromeBackground,
|
||||
required this.chromeForeground,
|
||||
required this.chromeBorder,
|
||||
// panel
|
||||
required this.panelBackground,
|
||||
required this.panelBorder,
|
||||
@@ -94,6 +98,10 @@ class SurfaceTokens {
|
||||
final Color globalFocus;
|
||||
final Color globalTextMuted;
|
||||
|
||||
final Color chromeBackground;
|
||||
final Color chromeForeground;
|
||||
final Color chromeBorder;
|
||||
|
||||
final Color panelBackground;
|
||||
final Color panelBorder;
|
||||
final Color panelActiveBorder;
|
||||
@@ -179,6 +187,11 @@ abstract class TokenKeys {
|
||||
static const globalFocus = 'global.focus';
|
||||
static const globalTextMuted = 'global.textMuted';
|
||||
|
||||
// chrome
|
||||
static const chromeBackground = 'chrome.background';
|
||||
static const chromeForeground = 'chrome.foreground';
|
||||
static const chromeBorder = 'chrome.border';
|
||||
|
||||
// panel
|
||||
static const panelBackground = 'panel.background';
|
||||
static const panelBorder = 'panel.border';
|
||||
@@ -266,6 +279,9 @@ abstract class TokenKeys {
|
||||
globalBorder,
|
||||
globalFocus,
|
||||
globalTextMuted,
|
||||
chromeBackground,
|
||||
chromeForeground,
|
||||
chromeBorder,
|
||||
panelBackground,
|
||||
panelBorder,
|
||||
panelActiveBorder,
|
||||
|
||||
@@ -45,7 +45,7 @@ class _ClideSpineState extends State<ClideSpine> {
|
||||
child: Container(
|
||||
width: ClideSpine.width,
|
||||
decoration: BoxDecoration(
|
||||
color: _hovered ? tokens.sidebarItemHover : tokens.sidebarBackground,
|
||||
color: _hovered ? tokens.sidebarItemHover : tokens.chromeBackground,
|
||||
border: Border(
|
||||
left: widget.side == SpineSide.right ? borderSide : BorderSide.none,
|
||||
right: widget.side == SpineSide.left ? borderSide : BorderSide.none,
|
||||
|
||||
Reference in New Issue
Block a user