Files
dotfiles/dotfiles/config/taffybar/taffybar.css
Ivan Malison 0fb5bc132a taffybar CSS: narrow selectors to prevent menu/popover color bleed
Use :not(menu):not(menuitem):not(popover):not(window) guards on all
wildcard selectors so bar typography and background-color rules don't
bleed into SNI popup menus and popovers attached via menuAttachToWidget.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 16:40:43 -08:00

541 lines
15 KiB
CSS

/* Widget/layout styling for taffybar.
*
* Colors live in `theme.css` (loaded via `palette.css`).
* IMPORTANT: use `@import url("...")` — bare `import` is silently ignored
* by GTK's CSS parser.
*/
@import url("theme.css");
/* Base typography + foreground color for the bar itself.
*
* IMPORTANT: menus/popovers created by SNI items are separate GtkWindows but
* inherit style context from the "attach widget" chain. If we apply a blanket
* `color:` rule here, it will bleed into those menus and override the GTK
* theme, making submenu text unreadable.
*/
.taffy-window .taffy-box :not(menu):not(menuitem):not(popover):not(window),
.taffy-window .taffy-box :not(menu):not(menuitem):not(popover):not(window) * {
/* Most text should come from Iosevka Aile; icon glyphs (Font Awesome / Nerd
Font PUA) should come from a Nerd Font family to avoid tiny fallback glyphs. */
font-family: "Iosevka Aile", "Iosevka Nerd Font", "Iosevka NF", "Noto Sans", sans-serif;
font-size: 11pt;
font-weight: 600;
color: @font-color;
text-shadow: none;
}
/* Icon-label combined widgets (PulseAudio, NetworkManager): the `.icon` label
uses a nerd font character, so force the nerd font family on it. */
.icon-label > .icon {
font-family: "Iosevka Nerd Font";
padding-right: 11px;
}
/* The main bar container. border-radius matches the widget squircles (6px)
so the bar itself has softly rounded corners. */
.taffy-box {
border-width: 0px;
padding: 0px;
margin: 0px;
border-radius: 6px;
box-shadow: none;
background-color: @bar-background;
background-image: none;
}
/* Each widget is wrapped in outer-pad > inner-pad > contents by
buildContentsBox (Haskell). The outer-pad draws the squircle
background pill. border-radius kept low (6px) for a more squared
"squircle" shape rather than a fully rounded pill. */
.outer-pad {
background-color: @pill-background;
border: 0px;
border-radius: 6px;
margin: 4px 6px;
/* No white outline; define shape with subtle inner highlight + dark stroke. */
box-shadow:
inset 0 1px 0 @pill-highlight,
inset 0 0 0 1px @pill-border,
0 10px 24px @pill-shadow;
}
.inner-pad {
padding: 2px 10px;
border-radius: 4px;
}
.contents {
padding: 0px;
transition: background-color .2s;
opacity: 1;
}
/* Make each widget's squircle background feel "solid": avoid GTK nodes and
labels painting their own backgrounds on top of `.outer-pad`.
Exclude menu/menuitem/popover so popup menus attached via menuAttachToWidget
aren't forced transparent. */
.outer-pad :not(menu):not(menuitem):not(popover):not(window),
.inner-pad,
.inner-pad :not(menu):not(menuitem):not(popover):not(window),
.inner-pad :not(menu):not(menuitem):not(popover):not(window) *,
.contents,
.contents :not(menu):not(menuitem):not(popover):not(window),
.contents :not(menu):not(menuitem):not(popover):not(window) * {
background-color: transparent;
}
/* Per-widget colored squircle overrides.
decorateWithClassAndBox "name" (in taffybar.hs) adds the widget class to
the same element that has .outer-pad, so `.outer-pad.audio` etc. target
each widget's pill. Colors come from @widget-*-bg/fg/border in theme.css. */
.outer-pad.audio {
background-color: @widget-audio-bg;
border-color: @widget-audio-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.audio :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.audio :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-audio-fg;
}
.outer-pad.network {
background-color: @widget-network-bg;
border-color: @widget-network-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.network :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.network :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-network-fg;
}
.outer-pad.mpris {
background-color: @widget-mpris-bg;
border-color: @widget-mpris-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.mpris :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.mpris :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-mpris-fg;
}
.outer-pad.mpris .icon {
font-family: "Iosevka Nerd Font";
}
.outer-pad.clock {
background-color: @widget-clock-bg;
border-color: @widget-clock-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.clock :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.clock :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-clock-fg;
}
.outer-pad.disk-usage {
background-color: @widget-disk-bg;
border-color: @widget-disk-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.disk-usage :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.disk-usage :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-disk-fg;
}
.outer-pad.sni-tray {
background-color: @widget-tray-bg;
border-color: @widget-tray-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.sni-tray :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.sni-tray :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-tray-fg;
}
.outer-pad.battery {
background-color: @widget-battery-bg;
border-color: @widget-battery-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.battery :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.battery :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-battery-fg;
}
.outer-pad.backlight {
background-color: @widget-backlight-bg;
border-color: @widget-backlight-border;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30);
}
.outer-pad.backlight :not(menu):not(menuitem):not(popover):not(window),
.outer-pad.backlight :not(menu):not(menuitem):not(popover):not(window) * {
color: @widget-backlight-fg;
}
/* Workspaces styling */
/* Asymmetric padding: 10px left leaves room for the overlaid workspace number
label; 3px right keeps the pill tight against the trailing icon edge. */
.workspaces .inner-pad {
box-shadow: none;
border-width: 0;
padding: 2px 3px 2px 10px;
}
.workspaces .contents {
box-shadow: none;
border-radius: 5px;
border-width: 0px;
border-style: solid;
border-color: @transparent;
padding: 0px 4px;
}
.workspace-label {
/* Overlay label (workspace number) that sits inside the icon "squircle". */
padding: 1px 3px;
margin: 0px;
font-size: 10pt;
opacity: 0.92;
font-weight: 700;
transition: color .2s;
background-color: @transparent;
}
/* The workspace label is overlaid bottom-left over the workspace icon strip.
Positioning: halign=start (left), valign=end (bottom) set in Haskell code.
Margins offset from that anchor point. */
.workspaces .overlay-box {
transition: background-color .2s, border-color .2s;
background-color: transparent;
border: none;
margin: 0px;
padding: 0px;
}
/* Position the workspace number inside the squircle pill. GTK overlays ignore
CSS margin for child positioning, so we use padding on the label itself:
padding-bottom pushes the text up from the bottom edge, padding-left pushes
it inward from the left corner to clear the border-radius. */
.workspaces .overlay-box .workspace-label {
background-color: transparent;
border: none;
padding: 2px 5px 5px 12px;
color: rgba(255, 255, 255, 0.92);
}
/* Accent-tinted background on the active workspace's squircle pill.
.active is set on the GtkOverlay (by setWorkspaceWidgetStatusClass in
Haskell), and .outer-pad is a child of that overlay, so
`.workspaces .active .outer-pad` correctly targets the pill. */
.workspaces .active .outer-pad {
background-color: rgba(241, 178, 178, 0.25);
box-shadow:
inset 0 0 0 1px rgba(241, 178, 178, 0.5),
inset 0 1px 0 @pill-highlight,
0 10px 24px @pill-shadow;
}
.workspaces .active .overlay-box {
background-color: transparent;
border-color: transparent;
}
.contents .window-icon {
border-width: 3px;
}
.active .contents .window-icon {
opacity: 1;
}
.active .contents {
background-color: rgba(255, 255, 255, 0.10);
opacity: 1;
}
/* (Handled above for workspaces.) */
.visible .contents {
background-color: rgba(255, 255, 255, 0.06);
}
.workspaces .window-icon-container {
/* Don't give each window icon its own background/border; the workspace
squircle is the background. */
background-color: transparent;
border: 0px;
box-shadow: none;
padding: 0px 2px;
}
.workspaces .window-icon-container.active {
background-color: rgba(255, 255, 255, 0.10);
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 4px;
padding: 2px 4px;
}
.workspaces .active .contents,
.workspaces .visible .contents {
background-color: transparent;
}
.workspaces .window-icon {
border-width: 0px;
}
.window-icon-container {
transition: opacity .2s, box-shadow .2s;
opacity: 1;
border-radius: 5px;
transition: background-color .2s;
background-color: rgba(255, 255, 255, 0.04);
padding: 1px 4px;
border: 1px solid rgba(255, 255, 255, 0.08);
}
/* This gives space for the box-shadow (they look like underlines) that follow.
This will actually affect all widgets, (not just the workspace icons), but
that is what we want since we want the icons to look the same. */
.auto-size-image, .sni-tray {
padding: 1px 4px;
}
/* If the SNI tray is wrapped in our standard widget box, avoid double-padding. */
.sni-tray .sni-tray {
padding: 0px;
}
.window-icon-container.active {
background-color: rgba(255, 255, 255, 0.13);
border-color: rgba(255, 255, 255, 0.16);
}
.window-icon-container.urgent {
}
.window-icon-container.minimized .window-icon {
opacity: .3;
}
.window-icon {
opacity: 1;
transition: opacity .5s;
}
/* Button styling */
.taffy-window button {
all: initial;
background-color: @transparent;
border-width: 0px;
border-radius: 0px;
}
.taffy-window button:checked, .taffy-window button:hover .Contents:hover {
box-shadow: inset 0 -2px @accent;
background-color: rgba(255, 255, 255, 0.06);
}
/* ── Menu styling ───────────────────────────────────────────────────────
Force a clean white-background / black-text look on all popup menus
(GtkMenu, DBusMenu, popovers) regardless of the active GTK theme.
The CSS provider runs at priority 900 (above USER = 800).
The .outer-pad background rule uses :not(menu):not(menuitem) to avoid
forcing transparency on popup menu containers. For color, we need
high-specificity overrides since labels inside menus still match :not(menu). */
/* --- Popup menu containers ---
.taffy-window menu (0,1,1) beats .taffy-window * (0,1,0) for background.
.outer-pad.sni-tray menu (0,2,1) beats .outer-pad.sni-tray * (0,2,0). */
menu,
.taffy-window menu,
.outer-pad menu {
background-color: #ffffff;
background-image: none;
color: #000000;
font-family: "Noto Sans", sans-serif;
font-size: 10pt;
font-weight: 400;
text-shadow: none;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 6px;
padding: 4px 0;
}
/* --- Menu items ---
High specificity to beat .outer-pad.sni-tray * color bleed (0,2,0). */
.outer-pad.sni-tray menu menuitem,
.outer-pad menu menuitem,
.taffy-window menu menuitem,
menu menuitem {
background-color: #ffffff;
background-image: none;
color: #000000;
font-family: "Noto Sans", sans-serif;
font-size: 10pt;
font-weight: 400;
text-shadow: none;
padding: 4px 12px;
min-height: 20px;
}
/* Children: transparent bg so hover shows through; override color bleed.
Use :not(menu) to avoid forcing transparency on nested submenu containers. */
.outer-pad.sni-tray menu menuitem :not(menu),
.outer-pad menu menuitem :not(menu),
.taffy-window menu menuitem :not(menu),
menu menuitem :not(menu) {
background-color: transparent;
background-image: none;
color: #000000;
font-family: "Noto Sans", sans-serif;
font-size: 10pt;
font-weight: 400;
text-shadow: none;
}
/* --- Hover --- */
.outer-pad.sni-tray menu menuitem:hover,
.outer-pad menu menuitem:hover,
menu menuitem:hover {
background-color: #4a90d9;
}
.outer-pad.sni-tray menu menuitem:hover :not(menu),
.outer-pad menu menuitem:hover :not(menu),
menu menuitem:hover :not(menu),
menu menuitem:hover {
color: #ffffff;
}
/* --- Separators --- */
menuitem separator {
background-color: transparent;
min-height: 1px;
margin: 4px 8px;
border-top: 1px solid rgba(0, 0, 0, 0.12);
}
/* --- Arrows & toggle indicators --- */
menuitem arrow {
background-color: transparent;
color: rgba(0, 0, 0, 0.4);
}
menuitem check, menuitem radio {
background-color: transparent;
color: #000000;
}
/* --- Background/decoration nodes in popup windows ---
Submenus are separate popup windows that may NOT be CSS descendants of
.taffy-window or .outer-pad. Cover every popup window layer. */
window.popup, window.popup.background,
window.menu, window.menu.background {
background-color: #ffffff;
background-image: none;
}
window.popup decoration, window.menu decoration {
background-color: #ffffff;
background-image: none;
}
/* Catch submenus that aren't CSS descendants of .taffy-window/.outer-pad.
Use .dbusmenu-submenu class (added by DBusMenu.hs) plus type selectors. */
/* Submenu rules need specificity > 0,2,0 to beat .outer-pad.sni-tray *
which bleeds tray colors into submenus via the CSS parent chain. */
.outer-pad .dbusmenu-submenu,
.dbusmenu-submenu {
background-color: #ffffff;
background-image: none;
color: #000000;
font-family: "Noto Sans", sans-serif;
font-size: 10pt;
font-weight: 400;
text-shadow: none;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 6px;
padding: 4px 0;
}
.outer-pad.sni-tray .dbusmenu-submenu menuitem,
.outer-pad .dbusmenu-submenu menuitem,
.dbusmenu-submenu menuitem {
background-color: #ffffff;
background-image: none;
color: #000000;
font-family: "Noto Sans", sans-serif;
font-size: 10pt;
font-weight: 400;
text-shadow: none;
padding: 4px 12px;
min-height: 20px;
}
.outer-pad.sni-tray .dbusmenu-submenu menuitem *,
.outer-pad .dbusmenu-submenu menuitem *,
.dbusmenu-submenu menuitem * {
background-color: transparent;
background-image: none;
color: #000000;
font-family: "Noto Sans", sans-serif;
font-size: 10pt;
font-weight: 400;
text-shadow: none;
}
.outer-pad.sni-tray .dbusmenu-submenu menuitem:hover,
.outer-pad .dbusmenu-submenu menuitem:hover,
.dbusmenu-submenu menuitem:hover {
background-color: #4a90d9;
}
.outer-pad.sni-tray .dbusmenu-submenu menuitem:hover *,
.outer-pad .dbusmenu-submenu menuitem:hover *,
.dbusmenu-submenu menuitem:hover *,
.outer-pad.sni-tray .dbusmenu-submenu menuitem:hover,
.dbusmenu-submenu menuitem:hover {
color: #ffffff;
}
/* --- Popovers (some SNI apps use these instead of GtkMenu) --- */
popover, popover.background, popover > contents {
background-color: #ffffff;
background-image: none;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 6px;
}
popover *, popover modelbutton, popover modelbutton * {
background-color: transparent;
color: #000000;
font-family: "Noto Sans", sans-serif;
font-size: 10pt;
font-weight: 400;
text-shadow: none;
}
popover modelbutton {
background-color: #ffffff;
padding: 4px 12px;
}
popover modelbutton:hover {
background-color: #4a90d9;
color: #ffffff;
}
.clock label,
.mpris label,
.battery label {
letter-spacing: 0.2px;
}
.mpris label {
color: @font-muted;
}