shy-surfaces.css (23202B)
1 /*== ShyFox Surfaces ================================================================================ 2 3 Material and motion for the panels ShyFox already positions: the revealed navigation bar, the 4 sidebar and its bookmarks strip, the right-hand tool strip, the menubar, the findbar, the indicator 5 bars of hidden panels, the busy indicators, the status pill, modal dialogs and the page card. 6 7 Nothing here moves anything. No rule below sets a size, a position, a margin or a padding: every 8 panel keeps the geometry, the 1px border and the --transition timing that shy-navbar.css, 9 shy-sidebar.css, shy-toolbar.css, shy-findbar.css and shy-global.css give it. Only the material 10 changes: 11 12 * the opaque fill becomes a translucent tint of the same theme colour it already used, 13 * `outline: var(--shadow)` -- ShyFox's faked shadow -- becomes a real layered box-shadow, 14 * the 1px border keeps its width but hands its colour to the first shadow layer, so the hairline 15 fades in with the card instead of snapping on, 16 * a blurred pane is added on a pseudo-element *child*, never on the panel itself. 17 18 The last point is not a style choice. #urlbar is position:fixed with viewport coordinates, so a 19 backdrop-filter, filter, transform or contain on #nav-bar, #nav-bar-customization-target, 20 #urlbar-container, #navigator-toolbox or #titlebar would make that element the containing block for 21 the address field and drop it back into the toolbar. A pseudo-element is never an ancestor of 22 anything, so it is the only safe host for the blur. 23 24 Switch the whole layer off with `shyfox.disable.polish`, the blur alone with `shyfox.disable.blur`. 25 26 ===================================================================================================*/ 27 28 @media not -moz-pref("shyfox.disable.polish") { 29 30 /* 31 32 --- LOCAL TOKENS ----------------------------------------------------------------------------------- 33 34 Derived from shy-tokens.css. Every rule below is built out of these six and the shared keyframes. 35 36 */ 37 38 :root{ 39 /* the single hairline a floating panel gets; private mode keeps its own colour so the window 40 still reads as private at a glance */ 41 --shy-panel-edge: var(--shy-hairline-strong); 42 43 /* a revealed panel: hairline ring first, then the two elevation layers */ 44 --shy-panel-lift: 0 0 0 1px var(--shy-panel-edge), var(--shy-elev-2); 45 46 /* the same shadow collapsed to nothing. Declaring it layer-for-layer means the shadow 47 interpolates instead of popping when the panel slides in */ 48 --shy-panel-lift-off: 0 0 0 0 transparent, 0 0 0 0 transparent, 0 0 0 0 transparent; 49 50 /* indicator bars: an accent-tinted capsule, its resting glow and the bloom it answers with */ 51 --shy-indicator: color-mix(in srgb, var(--shy-color) 40%, var(--bt-col)); 52 --shy-indicator-glow: 0 0 6px color-mix(in srgb, var(--shy-color) 30%, transparent); 53 --shy-indicator-glow-lit: 0 0 14px color-mix(in srgb, var(--shy-color) 65%, transparent); 54 } 55 56 #main-window[privatebrowsingmode="temporary"]{--shy-panel-edge: var(--private-col);} 57 58 /* --shy-shimmer sweeps background-position, but every busy gradient ShyFox draws is an !important 59 declaration, and important author declarations outrank animations -- the sweep would never move. 60 Animating the offset as a registered custom property and substituting it into the important 61 declaration keeps the shared -140%..240% travel without fighting the cascade. If @property is 62 ever unavailable the offset stays at its initial value and the sweep is simply invisible. */ 63 @property --shy-sweep { 64 syntax: "<percentage>"; 65 inherits: false; 66 initial-value: -140%; 67 } 68 69 @keyframes shy-sweep { 70 from {--shy-sweep: -140%;} 71 to {--shy-sweep: 240%;} 72 } 73 74 /* 75 76 --- NAVIGATION BAR --------------------------------------------------------------------------------- 77 78 */ 79 80 /* same gate shy-navbar.css uses: navbar hidden or clean mode, and not the customizing page */ 81 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]){ 82 83 /* the card rests flat, so the shadow has somewhere to fade in from */ 84 #nav-bar {box-shadow: var(--shy-panel-lift-off);} 85 86 /* revealed. shy-navbar.css:274-278 fills the bar with an opaque --tb-col, draws 87 `border: var(--outline)` and fakes a shadow with `outline: var(--shadow)`; all three are 88 replaced here. The border keeps its 1px width -- only its colour goes, because the hairline is 89 now the first layer of the shadow and can fade with the reveal. */ 90 &:has( 91 #nav-bar-customization-target:hover, 92 #nav-bar-customization-target :focus-visible, 93 #nav-bar-customization-target *:not(:is(#unified-extensions-button, #firefox-view-button))[open], 94 #nav-bar-customization-target #searchbar:focus-within, 95 #urlbar:focus-within, 96 #urlbar:is([focused], [open], [breakout-extend]), 97 #nav-bar-overflow-button[open], 98 #nav-bar-overflow-button:hover 99 ){ 100 #nav-bar{ 101 background-color: var(--shy-surface) !important; 102 border-color: transparent !important; 103 outline: none !important; 104 box-shadow: var(--shy-panel-lift) !important; 105 106 /* Glass pane. #nav-bar::before and ::after are the indicator bar and the tab-load wash, and 107 #nav-bar itself may never carry a backdrop-filter, so the free host is 108 #nav-bar-customization-target::after. That element is statically positioned, so `inset: 0` 109 resolves against #nav-bar's padding box and the pane covers the whole card exactly. */ 110 #nav-bar-customization-target::after{ 111 content: ""; 112 position: absolute; 113 inset: 0; 114 z-index: -1; /* behind the toolbar items, above the panel fill */ 115 pointer-events: none; /* the address field has to stay hit-testable */ 116 border-radius: var(--big-rounding) !important; 117 } 118 } 119 } 120 } 121 122 /* 123 124 --- SIDEBAR AND BOOKMARKS -------------------------------------------------------------------------- 125 126 */ 127 128 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]){ 129 130 #sidebar-box, #PersonalToolbar {box-shadow: var(--shy-panel-lift-off);} 131 132 /* revealed; replaces the fill, border colour and faked shadow at shy-sidebar.css:247-250 */ 133 &:not([titlepreface*=""]):has( 134 #sidebar-box:hover, 135 #sidebar-box:focus-within, 136 #PersonalToolbar:hover, 137 #PersonalToolbar *:not(#firefox-view-button)[open], 138 #sidebar-switcher-target[aria-expanded="true"] 139 ){ 140 #sidebar-box, 141 #PersonalToolbar 142 { 143 background-color: var(--shy-surface-deep) !important; 144 border-color: transparent !important; 145 outline: none !important; 146 box-shadow: var(--shy-panel-lift) !important; 147 } 148 149 /* Of the two panels this rule covers, the bookmarks strip is the one with a spare child to 150 blur through: #PersonalToolbar::before and ::after are both hover targets, but 151 #personal-bookmarks is a static, flex-filling child, so its ::before resolves against the 152 panel's padding box. #sidebar-box gets no pane at all -- see the note below. */ 153 #personal-bookmarks::before{ 154 content: ""; 155 position: absolute; 156 inset: 0; 157 z-index: -1; 158 pointer-events: none; 159 border-radius: var(--big-rounding) !important; 160 } 161 } 162 } 163 164 /* #sidebar-box has no blurred pane on purpose. Both of its pseudo-elements are taken (::before is 165 the hover target, ::after the indicator bar) and its only element child is the <browser> that 166 paints the sidebar document opaquely across the whole panel, so there would be nothing behind 167 the pane to blur. It keeps the tint, the hairline and the elevation. */ 168 169 /* 170 171 --- RIGHT-HAND TOOL STRIP -------------------------------------------------------------------------- 172 173 */ 174 175 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]){ 176 177 #TabsToolbar {box-shadow: var(--shy-panel-lift-off);} 178 179 /* revealed; replaces the fill, border colour and faked shadow at shy-toolbar.css:274-277 */ 180 &:not([titlepreface*=""]):has( 181 #TabsToolbar:hover 182 .titlebar-buttonbox-container:not(:hover), 183 #TabsToolbar-customization-target *:not(#firefox-view-button, tab)[open], 184 #TabsToolbar-customization-target #searchbar:focus-within, 185 #TabsToolbar-customization-target :focus-visible, 186 #PanelUI-menu-button:focus-visible, 187 #unified-extensions-button:focus-visible, 188 #PanelUI-menu-button[open], 189 #PanelUI-menu-button:hover, 190 #unified-extensions-button[open], 191 #unified-extensions-button:hover 192 ){ 193 #TabsToolbar{ 194 background-color: var(--shy-surface) !important; 195 border-color: transparent !important; 196 outline: none !important; 197 box-shadow: var(--shy-panel-lift) !important; 198 } 199 200 /* #TabsToolbar::before and ::after are the hover target and the indicator bar, and the strip 201 itself may not take a backdrop-filter either: the fallback tab bar (#tabbrowser-tabs, 202 shy-sidebar.css:311) is a position:fixed child of it and would be re-anchored. So the pane 203 goes on #TabsToolbar-customization-target::before, pulled up over the padding that reserves 204 room for the menu and extensions buttons so it covers the whole card. */ 205 #TabsToolbar-customization-target::before{ 206 content: ""; 207 position: absolute; 208 inset: calc(-1 * (var(--toolbar-item-hgt) * 2 + 3px)) 0 0 0; 209 z-index: -1; 210 pointer-events: none; 211 border-radius: var(--big-rounding) !important; 212 } 213 } 214 } 215 216 /* 217 218 --- MENUBAR ---------------------------------------------------------------------------------------- 219 220 */ 221 222 @media not (-moz-platform: macos) { 223 224 /* the menubar is a floating card too once alt is pressed; shy-global.css:492-495 gives it the 225 same border plus faked shadow, so it gets the same treatment and drops in rather than appearing */ 226 #toolbar-menubar:not([inactive="true"]){ 227 background-color: var(--shy-surface) !important; 228 border-color: transparent !important; 229 outline: none !important; 230 box-shadow: var(--shy-panel-lift) !important; 231 /* fade, not drop: shy-drop-in sets `translate`, and shy-controls.css hosts the position:fixed 232 window-control buttonbox inside this element, which a translated ancestor would re-anchor. */ 233 animation: shy-fade-in var(--shy-dur) var(--shy-ease) !important; 234 235 /* the menubar clips to its own box (overflow: clip, shy-global.css:473), so the pane can never 236 escape the rounded corners */ 237 &::before{ 238 content: ""; 239 position: absolute; 240 inset: 0; 241 z-index: -1; 242 pointer-events: none; 243 border-radius: var(--rounding) !important; 244 } 245 } 246 } 247 248 /* 249 250 --- FINDBAR ---------------------------------------------------------------------------------------- 251 252 */ 253 254 findbar{ 255 background-color: var(--shy-surface) !important; 256 border-color: transparent !important; 257 outline: none !important; 258 box-shadow: var(--shy-panel-lift) !important; 259 260 /* both of the findbar's own pseudo-elements are free */ 261 &::before{ 262 content: ""; 263 position: absolute; 264 inset: 0; 265 z-index: -1; 266 pointer-events: none; 267 border-radius: var(--big-rounding) !important; 268 } 269 } 270 271 /* the findbar is never display:none -- it slides out below the window edge -- so its shadow is 272 collapsed rather than removed, and grows back as the bar comes up */ 273 findbar[hidden] {box-shadow: var(--shy-panel-lift-off);} 274 275 /* 276 277 --- HIDDEN PANEL INDICATORS ------------------------------------------------------------------------ 278 279 The three capsules that mark where a hidden panel lives. --hide-bar-opct still owns their 280 visibility: shy-variables.css:122-124 forces it to 0 in clean mode and fullscreen with !important, 281 and nothing here touches opacity, so that contract is intact. 282 283 */ 284 285 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]) #nav-bar::before, 286 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]) #TabsToolbar::after, 287 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]) #sidebar-box::after 288 { 289 background-color: var(--shy-indicator) !important; 290 box-shadow: var(--shy-indicator-glow) !important; 291 292 /* keep ShyFox's own transition (it carries the opacity fade and the position of the capsule) and 293 re-time the two properties this file animates. A duplicated property takes its last entry. */ 294 transition: var(--transition), 295 box-shadow var(--shy-dur-fast) var(--shy-ease), 296 scale var(--shy-dur-fast) var(--shy-ease) !important; 297 } 298 299 /* Entering a panel's hover hitbox is what reveals it, so the capsule answers the approach by 300 blooming across its short axis as the panel slides out from behind it: it grows and its glow 301 widens over 110ms while the reveal fades it out over --trans-dur. */ 302 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]) #nav-bar:hover::before{ 303 scale: 1 1.5; 304 box-shadow: var(--shy-indicator-glow-lit) !important; 305 } 306 307 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]) #TabsToolbar:hover::after, 308 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]) #sidebar-box:hover::after 309 { 310 scale: 1.5 1; 311 box-shadow: var(--shy-indicator-glow-lit) !important; 312 } 313 314 /* 315 316 --- BUSY AND PROGRESS ------------------------------------------------------------------------------ 317 318 The load and download indicators keep the geometry and the fill percentage ShyFox computes; they 319 gain the accent glow and a slow sweep. The second background layer is exactly as wide (or tall) as 320 the bar, and a percentage background-position of a full-size layer resolves to a zero offset, so 321 only the sweep moves. The percentages carry a 0% fallback: --shy-download-pcent comes from a table 322 of :has() rules in shy-variables.css that only covers whole percents, and an unresolved var would 323 take the whole declaration down with it and leave the bar blank. 324 325 */ 326 327 /* current tab loading, in the navbar capsule (shy-navbar.css:212-218) */ 328 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]):has(.tabbrowser-tab[selected][busy]) #nav-bar::before{ 329 background-image: 330 linear-gradient(90deg, transparent, color-mix(in srgb, var(--bt-col) 55%, transparent), transparent), 331 linear-gradient(to right, 332 var(--shy-color) 0 var(--shy-tab-load-pcent, 0%), 333 var(--shy-text-faint) var(--shy-tab-load-pcent, 0%) 100%) !important; 334 background-size: 45% 100%, 100% 100% !important; 335 background-repeat: no-repeat !important; 336 background-position: var(--shy-sweep) 0, 0 0 !important; 337 box-shadow: var(--shy-indicator-glow-lit) !important; 338 animation: shy-sweep 2.4s var(--shy-ease-in-out) infinite !important; 339 } 340 341 /* the same load state on the address field (shy-navbar.css:67-73). The sweep is deliberately much 342 fainter here: it passes behind the URL. */ 343 #main-window:not([customizing]):not([titlepreface*=""]):has(.tabbrowser-tab[selected][busy]) #urlbar:not(:is([focused], [open], [breakout-extend])) :is(#urlbar-background, .urlbar-background){ 344 background-image: 345 linear-gradient(90deg, transparent, color-mix(in srgb, var(--bt-col) 9%, transparent), transparent), 346 linear-gradient(to right, 347 color-mix(in srgb, var(--shy-color) 16%, transparent) 0 var(--shy-tab-load-pcent, 0%), 348 color-mix(in srgb, var(--bt-col) 8%, transparent) var(--shy-tab-load-pcent, 0%) 100%) !important; 349 background-size: 35% 100%, 100% 100% !important; 350 background-repeat: no-repeat !important; 351 background-position: var(--shy-sweep) 0, 0 0 !important; 352 box-shadow: inset 0 0 0 1px var(--shy-accent-weak) !important; 353 animation: shy-sweep 2.4s var(--shy-ease-in-out) infinite !important; 354 } 355 356 /* download progress, on the tool strip capsule (shy-toolbar.css:288-297). The strip is vertical, so 357 the same offset drives the Y axis instead. */ 358 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]):has(#downloads-button[progress="true"]) #TabsToolbar::after{ 359 background-image: 360 linear-gradient(180deg, transparent, color-mix(in srgb, var(--bt-col) 55%, transparent), transparent), 361 linear-gradient(to top, 362 var(--shy-color) 0 var(--shy-download-pcent, 0%), 363 var(--shy-text-faint) var(--shy-download-pcent, 0%) 100%) !important; 364 background-size: 100% 45%, 100% 100% !important; 365 background-repeat: no-repeat !important; 366 background-position: 0 var(--shy-sweep), 0 0 !important; 367 box-shadow: var(--shy-indicator-glow-lit) !important; 368 animation: shy-sweep 2.4s var(--shy-ease-in-out) infinite !important; 369 } 370 371 /* 372 373 --- STATUS PILL ------------------------------------------------------------------------------------ 374 375 The link target at the bottom of the window (shy-global.css:131-140) 376 377 */ 378 379 #statuspanel #statuspanel-label{ 380 background-color: var(--shy-surface) !important; 381 border-color: transparent !important; 382 outline: none !important; 383 box-shadow: var(--shy-panel-lift) !important; 384 } 385 386 /* the pill is created once and reused, so the fade has to be driven by the attribute the browser 387 toggles when a link is hovered */ 388 #statuspanel:not([inactive]) #statuspanel-label{ 389 animation: shy-fade-in var(--shy-dur) var(--shy-ease) !important; 390 } 391 392 /* 393 394 --- MODAL DIALOGS ---------------------------------------------------------------------------------- 395 396 */ 397 398 /* real elevation for the dialog card (shy-global.css:185-189 gives it only a border), and a settle 399 rather than a hard cut. .dialogBox already carries `will-change: transform`, so it is its own 400 containing block and the scale costs nothing new. */ 401 .dialogBox{ 402 box-shadow: var(--shy-elev-3) !important; 403 animation: shy-pop-in var(--shy-dur) var(--shy-ease) !important; 404 } 405 406 /* The tab-modal scrim. ShyFox clears the platform overlay colour on .dialogTemplate, and the 407 fixed pseudo it draws instead is anchored and clipped by .dialogBox's own will-change, so the 408 page behind a tab-modal dialog is currently not dimmed at all. Dim it from the theme and blur it. */ 409 .dialogStack > .dialogTemplate.dialogOverlay[topmost="true"]{ 410 background-color: color-mix(in srgb, var(--bg-col) 45%, transparent) !important; 411 animation: shy-fade-in var(--shy-dur) var(--shy-ease) !important; 412 } 413 414 /* window-modal dialogs dim through the real top-layer backdrop, which still uses the platform 415 overlay colour */ 416 #window-modal-dialog::backdrop{ 417 background-color: color-mix(in srgb, var(--bg-col) 55%, transparent) !important; 418 } 419 420 /* 421 422 --- BROWSER CHROME SCROLLBARS ---------------------------------------------------------------------- 423 424 Chrome scroll containers only. userChrome.css cannot reach a web page at all, and the sidebar and 425 dialog frames are separate documents, so nothing here can touch a page scrollbar. shy-menus.css 426 already inherits the colour into popups; this adds the width, and the scrollers that sit outside a 427 popup. 428 429 */ 430 431 arrowscrollbox, 432 menupopup::part(arrowscrollbox-scrollbox), 433 panelview, 434 .panel-subview-body, 435 richlistbox, 436 tree, 437 .urlbarView, 438 #downloadsListBox, 439 #widget-overflow-scroller, 440 #customization-palette-container 441 { 442 scrollbar-width: thin; 443 scrollbar-color: var(--shy-scrollbar) !important; 444 } 445 446 /* 447 448 --- WINDOW CONTENT FRAME --------------------------------------------------------------------------- 449 450 */ 451 452 /* The page card already has its rounded corners and its 1px border (shy-global.css:46-63). This 453 adds the outer hairline and the soft shadow that lift it off the coloured field. Both paint 454 outside the border box, so the card does not move and does not change size; in fullscreen the 455 margins are zero and the shadow simply falls outside the viewport. */ 456 #browser #tabbrowser-tabbox{ 457 box-shadow: 0 0 0 1px var(--shy-hairline), var(--shy-elev-2) !important; 458 } 459 460 /* 461 462 --- BACKDROP BLUR ---------------------------------------------------------------------------------- 463 464 Every backdrop-filter in this file, in one place. Each host is either a pseudo-element that only 465 exists while its panel is revealed, or an element that is display:none until it is needed, so this 466 costs nothing while the chrome is idle -- and never more than three blurred surfaces at once. 467 468 */ 469 470 @media not -moz-pref("shyfox.disable.blur") { 471 #nav-bar-customization-target::after, 472 #TabsToolbar-customization-target::before, 473 #personal-bookmarks::before, 474 #toolbar-menubar::before, 475 findbar::before, 476 #statuspanel-label, 477 .dialogStack > .dialogTemplate.dialogOverlay[topmost="true"], 478 #window-modal-dialog::backdrop 479 { 480 backdrop-filter: blur(var(--shy-glass-blur)) saturate(var(--shy-glass-saturate)) !important; 481 } 482 } 483 484 /* 485 486 --- GRACEFUL DEGRADATION --------------------------------------------------------------------------- 487 488 This file is imported after shy-compat.css, so it restates both contracts rather than inheriting 489 them. 490 491 */ 492 493 @media (prefers-reduced-motion: reduce) { 494 495 /* Restated at the specificity of the rules they undo. Among !important author declarations 496 specificity decides, so the bare selectors below are not enough on their own for the busy 497 sweeps, the menubar, the status pill or the dialog scrim. */ 498 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]):has(.tabbrowser-tab[selected][busy]) #nav-bar::before, 499 #main-window:not([customizing]):not([titlepreface*=""]):has(.tabbrowser-tab[selected][busy]) #urlbar:not(:is([focused], [open], [breakout-extend])) :is(#urlbar-background, .urlbar-background), 500 #main-window:is([titlepreface*=""], [titlepreface*=""]):not([customizing]):has(#downloads-button[progress="true"]) #TabsToolbar::after, 501 #toolbar-menubar:not([inactive="true"]), 502 #statuspanel:not([inactive]) #statuspanel-label, 503 .dialogStack > .dialogTemplate.dialogOverlay[topmost="true"], 504 .dialogBox { 505 animation: none !important; 506 transition: none !important; 507 scale: none !important; 508 translate: none !important; 509 } 510 511 #nav-bar::before, 512 #TabsToolbar::after, 513 #sidebar-box::after, 514 #urlbar :is(#urlbar-background, .urlbar-background), 515 #toolbar-menubar, 516 #statuspanel-label, 517 .dialogBox, 518 .dialogStack > .dialogTemplate.dialogOverlay[topmost="true"] 519 { 520 animation: none !important; 521 transition: none !important; 522 scale: none !important; 523 translate: none !important; 524 } 525 } 526 527 @media (forced-colors: active) { 528 #nav-bar, 529 #TabsToolbar, 530 #sidebar-box, 531 #PersonalToolbar, 532 #toolbar-menubar, 533 findbar, 534 #statuspanel-label, 535 .dialogBox, 536 #browser #tabbrowser-tabbox 537 { 538 background-color: Canvas !important; 539 border-color: CanvasText !important; /* the hairline goes back on the real border */ 540 box-shadow: none !important; 541 } 542 543 /* no translucency and no blur: the panes would only wash out system colours */ 544 #nav-bar-customization-target::after, 545 #TabsToolbar-customization-target::before, 546 #personal-bookmarks::before, 547 #toolbar-menubar::before, 548 findbar::before 549 { 550 display: none !important; 551 } 552 553 #statuspanel-label, 554 .dialogStack > .dialogTemplate.dialogOverlay[topmost="true"], 555 #window-modal-dialog::backdrop 556 { 557 backdrop-filter: none !important; 558 } 559 560 #nav-bar::before, 561 #TabsToolbar::after, 562 #sidebar-box::after 563 { 564 background-image: none !important; 565 background-color: CanvasText !important; 566 box-shadow: none !important; 567 scale: none !important; 568 animation: none !important; 569 } 570 } 571 572 } /* shyfox.disable.polish */