shy-menus.css (22173B)
1 /*== ShyFox Polished Menus ========================================================================== 2 3 Material, colour and state feedback for every popup surface: context menus, the app menu and its 4 subviews, the unified extensions panel, the downloads panel, the identity / permission / protections 5 popups, the bookmarks menus and the notification bar. 6 7 Density and geometry are not touched. shy-compact.css owns menuitem height, padding and the 8 four-column extensions grid; shy-global.css owns popup placement and the accent variable block. 9 This file only changes what those surfaces are made of. 10 11 Two facts from the platform drive nearly every rule here: 12 13 * a popup paints nothing itself. The whole card -- fill, border, radius, shadow -- is drawn by 14 ::part(content), which is the <html:slot> of a `panel` and the .menupopup-arrowscrollbox of a 15 `menupopup`. Both live inside a shadow root, so a plain class selector cannot reach them and 16 ::part() is the only way in. 17 * toolkit's xul.css puts `contain: paint` on menupopup/panel/tooltip, so nothing paints outside the 18 popup widget. The only spare room is --panel-box-shadow-margin (4px), which means --shy-elev-3 19 reads as a tight edge shadow rather than a wide bloom. Widening that margin also moves the popup 20 and its click-through region on screen, so it is deliberately left alone. 21 22 ===================================================================================================*/ 23 24 @media not -moz-pref("shyfox.disable.polish") { 25 26 /* 27 --- POPUP MATERIAL --------------------------------------------------------------------------------- 28 29 One card material for menupopup, panel and arrowpanel alike. 30 31 */ 32 33 :root { 34 /* the popup card fill. Opaque by default: on a Linux WM without CSD transparency Gecko forces 35 `opacity: 1` and square corners on popups, and a translucent fill there would composite against 36 whatever the opaque widget happens to be. It is upgraded to glass just below. */ 37 --shy-popup-fill: var(--pp-col); 38 39 /* how long a press reads before it settles */ 40 --shy-dur-press: 90ms; 41 42 /* separators everywhere in menus and panels; the platform default is 25% of currentColor */ 43 --panel-separator-color: var(--shy-hairline) !important; 44 45 /* shy-global.css hovers menu items with this variable, but no Gecko version ever defined it, so 46 those rules resolved to `transparent` and context menu items had no hover at all. Give it a 47 value and they come back to life. */ 48 --panel-item-hover-bgcolor: var(--shy-accent-weak); 49 } 50 51 /* a high-contrast user asked for a visible line, not a hairline */ 52 @media (prefers-contrast) { 53 :root {--panel-separator-color: currentColor !important;} 54 } 55 56 @media (-moz-gtk-csd-transparency-available), (not (-moz-platform: linux)) { 57 :root {--shy-popup-fill: var(--shy-surface-popup);} 58 } 59 60 /* interaction tokens, scoped to popups so toolbar buttons elsewhere keep their own states. 61 155 renamed the whole family: --button-*-menu-* is the new spelling, --button-*-ghost-* the 62 140 one, and 155 still uses the ghost names for the downloads list. Set both. */ 63 :is(menupopup, panel) { 64 --button-background-color-menu-hover: var(--shy-accent-weak); 65 --button-background-color-menu-active: var(--shy-accent-tint); 66 --button-background-color-ghost-hover: var(--shy-accent-weak); 67 --button-background-color-ghost-active: var(--shy-accent-tint); 68 69 --button-text-color-menu: var(--bt-col); 70 --button-text-color-menu-hover: var(--bt-col); 71 --button-text-color-menu-active: var(--bt-col); 72 --button-text-color-ghost: var(--bt-col); 73 --button-text-color-ghost-hover: var(--bt-col); 74 --button-text-color-ghost-active: var(--bt-col); 75 76 /* one source of dimming for disabled items: the token, not a stacked opacity */ 77 --button-text-color-menu-disabled: var(--shy-text-faint); 78 --button-text-color-ghost-disabled: var(--shy-text-faint); 79 --button-background-color-menu-disabled: transparent; 80 --button-background-color-ghost-disabled: transparent; 81 --button-opacity-disabled: 1; 82 83 /* popup scroll containers. scrollbar-color inherits, so one declaration reaches the arrowscrollbox 84 inside the shadow root as well as .panel-subview-body and every richlistbox. */ 85 scrollbar-color: var(--shy-scrollbar) !important; 86 } 87 88 /* the platform draws popups natively on Windows (mica) and macOS; reset that before painting our own. 89 macOS is excluded because its native popup also carries the window shadow. */ 90 @media not (-moz-platform: macos) { 91 :is(menupopup, panel):not(#ContentSelectDropdown, #ContentSelectDropdownPopup) { 92 appearance: none !important; 93 background-color: transparent !important; 94 } 95 } 96 97 /* the card. #ContentSelectDropdown is left alone: it renders a page's <select> and carries per-option 98 colours from content, so a theme text colour there can make options unreadable. */ 99 :is(menupopup, panel):not(#ContentSelectDropdown, #ContentSelectDropdownPopup)::part(content) { 100 background: var(--shy-popup-fill) !important; 101 color: var(--bt-col) !important; 102 border: 1px solid var(--shy-hairline) !important; 103 box-shadow: var(--shy-elev-3) !important; 104 } 105 106 /* Blur goes on ::part(content) rather than the popup, which keeps it off every ancestor of the 107 floating #urlbar. It is decoration only -- a chrome backdrop-filter can silently no-op, so 108 --shy-popup-fill stays opaque enough to carry the surface by itself. */ 109 @media not -moz-pref("shyfox.disable.blur") { 110 @media (-moz-gtk-csd-transparency-available), (not (-moz-platform: linux)) { 111 :is(menupopup, panel):not(#ContentSelectDropdown, #ContentSelectDropdownPopup)::part(content) { 112 backdrop-filter: blur(var(--shy-glass-blur)) saturate(var(--shy-glass-saturate)) !important; 113 } 114 } 115 } 116 117 /* Entrance. The widget itself cannot be animated reliably -- Gecko sizes the popup window before the 118 animation runs, so a scale on the popup leaves the shadow and hit region at the old size, and 119 `@media not (-moz-panel-animations)` disables the platform's own translate on several Linux WMs. 120 Animating ::part(content) instead is safe: shy-pop-in only ever scales below 1, so it never grows 121 past the `overflow: clip` on that box. */ 122 :is(menupopup, panel):not(#ContentSelectDropdown, #ContentSelectDropdownPopup)::part(content) { 123 animation: shy-pop-in var(--shy-dur) var(--shy-ease) !important; 124 } 125 126 /* 127 --- MENU ITEMS ------------------------------------------------------------------------------------- 128 129 [_moz-menuactive] is what Gecko sets for both pointer hover and keyboard navigation on a menuitem -- 130 :hover is not used there -- and it is spelled the same in 140 and 155, so one hook covers both states 131 on both versions. The same is true of [disabled] and [checked], which 140 writes as ="true". 132 133 */ 134 135 #main-window menupopup > :is(menuitem, menu) { 136 transition: 137 background-color var(--shy-dur-fast) var(--shy-ease), 138 box-shadow var(--shy-dur-fast) var(--shy-ease), 139 color var(--shy-dur-fast) var(--shy-ease); 140 } 141 142 /* hover / keyboard highlight. Specificity 1-2-2 so it outranks the 1-1-1 rules in the CONTEXT MENU 143 section of shy-global.css, which this supersedes. */ 144 #main-window menupopup > :is(menuitem, menu):not([disabled])[_moz-menuactive] { 145 background-color: var(--shy-accent-weak) !important; 146 color: var(--bt-col) !important; 147 } 148 149 /* press: a touch more accent, and quick enough to read as a response rather than a transition */ 150 #main-window menupopup > :is(menuitem, menu):not([disabled])[_moz-menuactive]:active { 151 background-color: var(--shy-accent-tint) !important; 152 transition-duration: var(--shy-dur-press); 153 } 154 155 /* checked items keep a tint and colour the native check itself. On Linux .menu-icon is a real 156 checkbox widget, which accent-color drives; on Windows it is menu-check.svg, which fill drives. */ 157 #main-window menupopup > menuitem[checked] { 158 background-color: var(--shy-accent-weak) !important; 159 accent-color: var(--shy-color); 160 } 161 162 #main-window menupopup > menuitem[checked] > .menu-icon { 163 fill: var(--shy-color) !important; 164 fill-opacity: 1 !important; 165 } 166 167 /* disabled: one dim, no hover feedback. The platform still paints -moz-menuhoverdisabled when the 168 keyboard lands on a disabled item, so the transparent fill has to be stated. */ 169 #main-window menupopup > :is(menuitem, menu)[disabled] { 170 color: var(--shy-text-faint) !important; 171 background-color: transparent !important; 172 } 173 174 /* the accel text ("Ctrl+T") is already quieter than the label; keep it in the same family */ 175 #main-window menupopup > :is(menuitem, menu):not([disabled]) > .menu-accel { 176 color: var(--shy-text-dim) !important; 177 } 178 179 /* the submenu arrow is a hint, not a label. 140 drives it from --menu-icon-opacity and 155 from 180 --menu-arrow-opacity, so the property is set directly rather than through either name. */ 181 #main-window menupopup > menu::after { 182 fill-opacity: 0.55 !important; 183 } 184 185 /* 186 --- APP MENU AND SUBVIEWS -------------------------------------------------------------------------- 187 188 Everything in a panelview is a .subviewbutton, so the token block above already gives it the same 189 hover, press and disabled treatment as a menu item. What is left is the checked state, focus, and the 190 seams between sections. 191 192 */ 193 194 /* panel buttons cannot show a checkbox, so a checked one gets a leading accent edge. An inset shadow 195 draws it without a pseudo-element and without moving anything -- .subviewbutton::before is already 196 spoken for by several icon rules in shy-icons.css. */ 197 #main-window panel .subviewbutton[checked] { 198 background-color: var(--shy-accent-weak) !important; 199 box-shadow: inset 3px 0 0 var(--shy-accent-line) !important; 200 } 201 202 /* keyboard focus reads like hover plus the accent ring the rest of the theme uses */ 203 #main-window panel :is(.subviewbutton, .panel-info-button, .panel-subview-footer-button):focus-visible { 204 background-color: var(--shy-accent-weak) !important; 205 outline: 2px solid var(--shy-accent-line) !important; 206 outline-offset: -2px !important; 207 } 208 209 /* The footer is a section of the same card, not a bar sitting on it. Every .panel-header, 210 #downloadsFooter and #protections-popup-footer in browser.xhtml already has a toolbarseparator 211 against it, and those are hairlines now, so only .panel-footer -- a moz-button-group with no 212 separator of its own -- needs a seam drawn. It is drawn inset so nothing shifts by a pixel. */ 213 #main-window panel .panel-footer { 214 background-color: transparent !important; 215 box-shadow: inset 0 1px 0 var(--shy-hairline) !important; 216 } 217 218 #main-window panel .panel-subview-footer-button { 219 border-radius: var(--rounding) !important; 220 transition: background-color var(--shy-dur-fast) var(--shy-ease) !important; 221 } 222 223 /* the update banner is the one item in the app menu that is asking for attention */ 224 #main-window panel .panel-banner-item { 225 --panel-banner-item-background-color: var(--shy-accent-weak); 226 --panel-banner-item-hover-bgcolor: var(--shy-accent-tint); 227 --panel-banner-item-active-bgcolor: var(--shy-accent-strong); 228 --panel-banner-item-color: var(--bt-col); 229 } 230 231 /* Icon tinting is already currentColor everywhere (shy-icons.css), which means a disabled item's 232 icon fades with its label for free now that --button-text-color-*-disabled carries the dimming. 233 The one thing missing is the other end: shy-icons.css holds every panel icon at the platform's 234 reduced fill-opacity, so a highlighted or checked row had a full-strength label next to a faded 235 icon. Bring the icon up with the row. */ 236 #main-window panel .subviewbutton:is(:hover, [checked]):not([disabled]) > image { 237 fill-opacity: 1 !important; 238 } 239 240 /* 241 --- UNIFIED EXTENSIONS ----------------------------------------------------------------------------- 242 243 shy-compact.css packs this panel into a four-column icon grid and hides the labels, so each cell is a 244 bare icon that needs its own hit area. Geometry stays exactly where that file put it. 245 246 */ 247 248 #unified-extensions-panel .unified-extensions-item-action-button.subviewbutton { 249 border-radius: var(--rounding) !important; 250 transition: 251 background-color var(--shy-dur-fast) var(--shy-ease), 252 scale var(--shy-dur-press) var(--shy-ease); 253 } 254 255 #unified-extensions-panel .unified-extensions-item-action-button.subviewbutton:not([disabled]):hover { 256 background-color: var(--shy-accent-weak) !important; 257 } 258 259 /* pressed. A scale on a panel button is safe -- it is nowhere near an ancestor of #urlbar -- and it 260 is the one property that reads as "pressed" without moving the grid. */ 261 #unified-extensions-panel .unified-extensions-item-action-button.subviewbutton:not([disabled]):hover:active { 262 background-color: var(--shy-accent-tint) !important; 263 scale: 0.94; 264 } 265 266 /* an extension wanting attention already draws an accent dot (shy-global.css pins 267 --uei-button-attention-dot-color to the accent); back it with a tint so the cell reads at a glance */ 268 #unified-extensions-panel .unified-extensions-item[attention] > .unified-extensions-item-row-wrapper > .unified-extensions-item-action-button.subviewbutton { 269 background-color: var(--shy-accent-weak) !important; 270 } 271 272 /* 273 --- DOWNLOADS -------------------------------------------------------------------------------------- 274 275 */ 276 277 #downloadsPanel { 278 --download-progress-fill-color: var(--shy-color) !important; 279 --download-progress-paused-color: var(--shy-text-faint) !important; 280 /* the platform flare is a hardcoded 75% white, which fights any theme */ 281 --download-progress-flare-color: color-mix(in srgb, var(--bt-col) 55%, transparent) !important; 282 } 283 284 #downloadsPanel #downloadsListBox > richlistitem { 285 transition: background-color var(--shy-dur-fast) var(--shy-ease) !important; 286 } 287 288 /* the track is the same hairline used for every other seam in the theme */ 289 #downloadsPanel .downloadProgress { 290 background-color: var(--shy-hairline) !important; 291 } 292 293 /* the filled part carries the same accent glow the theme uses on focused surfaces */ 294 #downloadsPanel .downloadProgress::-moz-progress-bar { 295 box-shadow: 0 0 8px color-mix(in srgb, var(--shy-color) 45%, transparent) !important; 296 } 297 298 #downloadsPanel .downloadProgress[paused]::-moz-progress-bar { 299 box-shadow: none !important; 300 } 301 302 /* cancel / retry / show-in-folder */ 303 #downloadsPanel .downloadButton { 304 transition: 305 background-color var(--shy-dur-fast) var(--shy-ease), 306 scale var(--shy-dur-press) var(--shy-ease); 307 } 308 309 #downloadsPanel .downloadButton:hover:active { 310 scale: 0.92; 311 } 312 313 #downloadsPanel .downloadsPanelFooterButton { 314 border-radius: var(--rounding) !important; 315 transition: background-color var(--shy-dur-fast) var(--shy-ease) !important; 316 } 317 318 /* the summary strip sits inside the card, so it takes a tint rather than its own dimmed panel fill */ 319 #downloadsPanel #downloadsSummary { 320 background-color: var(--shy-hover-tint) !important; 321 border-radius: var(--rounding) !important; 322 } 323 324 /* 325 --- SITE INFORMATION POPUPS ------------------------------------------------------------------------ 326 327 The identity, permission, trust and protections popups. Their card material comes from the shared 328 ::part(content) rule; the rows inside them are .subviewbutton, so they already match the app menu. 329 These rules cover the pieces that are not buttons. 330 331 */ 332 333 /* a permission row is not focusable but it does hold a remove button, so it gets a row highlight */ 334 #main-window :is(#identity-popup, #permission-popup) .permission-popup-permission-item { 335 border-radius: var(--rounding) !important; 336 transition: background-color var(--shy-dur-fast) var(--shy-ease) !important; 337 } 338 339 #main-window :is(#identity-popup, #permission-popup) .permission-popup-permission-item:hover { 340 background-color: var(--shy-hover-tint) !important; 341 } 342 343 /* the connection summary is the popup's headline; keep it on the theme text colour and let the 344 platform's per-state icon carry the meaning */ 345 #main-window .site-information-popup .identity-popup-security-connection, 346 #main-window #identity-popup .identity-popup-security-connection { 347 color: var(--bt-col) !important; 348 } 349 350 /* an enabled protection category reads as active, without an accent fill behind its label */ 351 #main-window #protections-popup .protections-popup-category:not([uidisabled], [disabled]) { 352 transition: background-color var(--shy-dur-fast) var(--shy-ease) !important; 353 } 354 355 #main-window #protections-popup .protections-popup-category[uidisabled] { 356 color: var(--shy-text-faint) !important; 357 } 358 359 /* the "blocked this week" counter is a number worth highlighting; the footer icons stay neutral */ 360 #main-window #protections-popup #protections-popup-trackers-blocked-counter-description { 361 color: var(--shy-color) !important; 362 } 363 364 /* 365 --- NOTIFICATION BAR ------------------------------------------------------------------------------- 366 367 shy-global.css already floats this as a card at the top of the content area. notification-message is a 368 moz-message-bar, whose entire inside is shadow DOM driven by inherited --message-bar-* properties, so 369 those tokens are the only way in -- and the only way the close button in the shadow root gets the 370 theme's text colour. 371 372 */ 373 374 #main-window :is(#tab-notification-deck, .global-notificationbox) notification-message { 375 --message-bar-border-color: var(--shy-hairline); 376 --message-bar-text-color: var(--bt-col); 377 --message-bar-icon-background-color: transparent; 378 --message-bar-border-radius: var(--big-rounding); 379 380 background-color: var(--shy-surface) !important; 381 box-shadow: var(--shy-elev-2) !important; 382 animation: shy-drop-in var(--shy-dur) var(--shy-ease) !important; 383 } 384 385 /* The accent icon is for ordinary notices only. moz-message-bar sets --message-bar-icon-color per 386 severity inside its shadow root, and an outer declaration would win against all of them, so the 387 warning / success / error bars are excluded and keep their semantic icon colour -- which, since 388 the card fill above is now uniform, is the only severity cue left. */ 389 #main-window :is(#tab-notification-deck, .global-notificationbox) 390 notification-message:not([type="warning"], [type="success"], [type="error"], [type="critical"]) { 391 --message-bar-icon-color: var(--shy-color); 392 } 393 394 @media not -moz-pref("shyfox.disable.blur") { 395 #main-window :is(#tab-notification-deck, .global-notificationbox) notification-message { 396 backdrop-filter: blur(var(--shy-glass-blur)) saturate(var(--shy-glass-saturate)) !important; 397 } 398 } 399 400 /* the infobar's buttons are XUL buttons in the light tree, so they can take the popup states */ 401 #main-window :is(#tab-notification-deck, .global-notificationbox) .notification-button { 402 --button-background-color-hover: var(--shy-accent-weak); 403 --button-background-color-active: var(--shy-accent-tint); 404 border-radius: var(--rounding) !important; 405 transition: background-color var(--shy-dur-fast) var(--shy-ease) !important; 406 } 407 408 #main-window :is(#tab-notification-deck, .global-notificationbox) .notification-button:focus-visible { 409 outline: 2px solid var(--shy-accent-line) !important; 410 outline-offset: 1px !important; 411 } 412 413 /* 414 --- GRACEFUL DEGRADATION --------------------------------------------------------------------------- 415 416 This file is imported after shy-compat.css, so it has to restate both contracts rather than inherit 417 them. 418 419 */ 420 421 @media (prefers-reduced-motion: reduce) { 422 :root {--shy-dur-press: 0s;} 423 424 :is(menupopup, panel)::part(content), 425 #main-window menupopup > :is(menuitem, menu), 426 #main-window panel .panel-subview-footer-button, 427 #unified-extensions-panel .unified-extensions-item-action-button.subviewbutton, 428 #downloadsPanel :is(#downloadsListBox > richlistitem, .downloadButton, .downloadsPanelFooterButton), 429 #main-window :is(#identity-popup, #permission-popup) .permission-popup-permission-item, 430 #main-window #protections-popup .protections-popup-category, 431 #main-window :is(#tab-notification-deck, .global-notificationbox) notification-message, 432 #main-window :is(#tab-notification-deck, .global-notificationbox) .notification-button { 433 animation: none !important; 434 transition: none !important; 435 } 436 437 /* the pressed states stay, they just arrive instantly */ 438 #unified-extensions-panel .unified-extensions-item-action-button.subviewbutton:not([disabled]):hover:active, 439 #downloadsPanel .downloadButton:hover:active { 440 scale: 1; 441 } 442 } 443 444 @media (forced-colors: active) { 445 :root { 446 --shy-popup-fill: Canvas; 447 --panel-separator-color: CanvasText !important; 448 --panel-item-hover-bgcolor: Highlight; 449 } 450 451 :is(menupopup, panel)::part(content) { 452 background: Canvas !important; 453 color: CanvasText !important; 454 border: 1px solid CanvasText !important; 455 box-shadow: none !important; 456 backdrop-filter: none !important; 457 animation: none !important; 458 } 459 460 /* Highlight / HighlightText is the one pair the system guarantees is legible together */ 461 #main-window menupopup > :is(menuitem, menu):not([disabled])[_moz-menuactive], 462 #main-window menupopup > :is(menuitem, menu):not([disabled])[_moz-menuactive]:active, 463 #main-window menupopup > menuitem[checked], 464 #main-window panel .subviewbutton[checked], 465 #unified-extensions-panel .unified-extensions-item-action-button.subviewbutton:not([disabled]):hover { 466 background-color: Highlight !important; 467 color: HighlightText !important; 468 box-shadow: none !important; 469 } 470 471 #main-window menupopup > :is(menuitem, menu)[disabled], 472 #main-window #protections-popup .protections-popup-category[uidisabled] { 473 color: GrayText !important; 474 } 475 476 #main-window panel .panel-footer { 477 box-shadow: inset 0 1px 0 CanvasText !important; 478 } 479 480 #main-window :is(#tab-notification-deck, .global-notificationbox) notification-message { 481 --message-bar-background-color: Canvas; 482 --message-bar-border-color: CanvasText; 483 --message-bar-text-color: CanvasText; 484 --message-bar-icon-color: CanvasText; 485 486 background-color: Canvas !important; 487 box-shadow: none !important; 488 backdrop-filter: none !important; 489 animation: none !important; 490 } 491 492 #downloadsPanel .downloadProgress::-moz-progress-bar { 493 box-shadow: none !important; 494 } 495 496 #downloadsPanel #downloadsSummary { 497 background-color: Canvas !important; 498 box-shadow: inset 0 0 0 1px CanvasText !important; 499 } 500 } 501 502 } /* shyfox.disable.polish */