/* * The notch — top-centre, floating, never reserving. * * The load-bearing rule in this file is that nothing here touches * `.os-area`. The desktop has one work-area rectangle now * (`src/work-area/index.ts`, carved out of the area by the dock), * and it stays useful only while few things carve it; a notch that * reserved height would be a second claimant on the top edge for * the sake of a pill a window may cover. It floats, and it gets out * of the way instead. * * Getting out of the way is also what the stacking order says: the * notch is on the desk, under the window layer, so a window reaching * the top edge covers the pill instead of the pill hanging over it. */ /* * Anchored to the SHELL, not the viewport. `.os-shell` starts below * the admin bar when the user keeps it, and at the viewport top when * they don't, so an absolutely-positioned notch lands correctly in * every admin-bar mode without knowing which one is on. Fixed * positioning put it under the bar, which paints above the shell. */ .os-notch { position: absolute; top: 0; left: 50%; transform: translateX( -50% ); z-index: var( --os-z-notch, 60 ); display: flex; align-items: center; gap: 7px; max-width: min( 420px, 60vw ); height: 34px; padding: 0 14px; /* * A hairline all the way round except the top, where the pill meets * the screen edge and a line would draw a lid on it. Without the * border the surface disappeared into the default wallpaper — Void * on Void, with only the shadow separating them. */ border: 1px solid var( --os-ui-border-strong, rgba( 255, 251, 255, 0.22 ) ); border-top: 0; /* Square at the top, round below — it reads as hanging from the screen edge rather than floating near it. */ border-radius: 0 0 15px 15px; background: var( --os-ui-surface-raised, rgba( 20, 18, 24, 0.92 ) ); box-shadow: 0 2px 12px rgba( 0, 0, 0, 0.4 ); color: var( --os-ui-fg, #fffbff ); font-size: 13px; line-height: 1; cursor: pointer; /* `width` is not animated: the pill is sized by its content, and transitioning an auto width does nothing. The message's own max-width is what opens and closes. */ transition: background 160ms ease, opacity 160ms ease, border-color 160ms ease, top 180ms ease, visibility 0s linear 160ms; } .os-notch:hover, .os-notch:focus-visible { background: var( --os-ui-surface-elevated, rgba( 32, 29, 38, 0.96 ) ); border-color: var( --os-ui-accent-dim, rgba( 242, 82, 252, 0.5 ) ); } .os-notch:focus-visible { outline: 2px solid var( --os-ui-accent, #f252fc ); outline-offset: -2px; } .os-notch__glyph { display: flex; flex: 0 0 auto; width: 18px; height: 18px; } .os-notch__glyph svg { width: 100%; height: 100%; fill: currentColor; } /* * The resting label. It yields to a message rather than sitting * beside one — the notch says one thing at a time, and "Site * assistant · Saving…" would be two. */ .os-notch__label { overflow: hidden; max-width: 160px; white-space: nowrap; text-overflow: ellipsis; transition: max-width 200ms ease, opacity 120ms ease; } .os-notch--speaking .os-notch__label { max-width: 0; opacity: 0; } /* * The message is always in the DOM (it is an `aria-live` region and * has to be, to be announced reliably); it is its WIDTH that opens. * `max-width` rather than `display` so there is something to animate * and so the live region is never removed from the accessibility tree. */ .os-notch__message { overflow: hidden; max-width: 0; white-space: nowrap; text-overflow: ellipsis; transition: max-width 200ms ease; } .os-notch--speaking .os-notch__message { max-width: 320px; } /* * Overview is the shell talking about itself — a zoomed-out view of * every window and desktop. A pill hanging over that view is chrome * on top of chrome, and it lands squarely on the desktop thumbnails. * Mio steps aside for the same reason and in the same way. * * `visibility` alongside the fade so it stops taking clicks on the way * out; a transparent button over a desktop thumbnail still swallows * the click that was meant to switch desktops. */ .os-shell:has( .os-area--overview ) .os-notch { opacity: 0; visibility: hidden; /* No delay on the way OUT — the pill must stop taking clicks the moment it starts fading, not after. The delay in the base rule is what holds it visible while it fades back IN. */ transition-delay: 0s; } /* Solo mode is one window freed into a native OS window — the shell's own chrome has no place in it. */ body.os-solo .os-notch { display: none; } @media ( prefers-reduced-motion: reduce ) { .os-notch, .os-notch__label, .os-notch__message { transition-duration: 1ms; } } /* * Auto-hide admin bar. * * The shell starts at the viewport top in this mode (the bar is out of * flow), so the notch would be underneath the bar whenever it rolls * down. It steps down to sit below it instead, on the bar's own timing. */ body.os-active.os-admin-bar-dynamic:has( #wpadminbar:hover ) .os-notch, body.os-active.os-admin-bar-dynamic:has( #wpadminbar:focus-within ) .os-notch, body.os-active.os-admin-bar-dynamic:has( .os-notch:hover ) .os-notch, body.os-active.os-admin-bar-dynamic:has( .os-notch:focus-visible ) .os-notch { /* The bar's measured edge (src/admin-bar-height.ts), Core's token behind it — a host that made the bar taller moved this step too. */ top: var( --os-admin-bar-height, var( --wp-admin--admin-bar--height, 32px ) ); } /* * The hover bridge, and it is what makes the step stable. * * Hovering the notch is one of the things that rolls the bar down, and * the notch answers by moving 32px away from the pointer — which ends * the hover, retracts the bar, and brings the notch back up under the * pointer to start again. This claims the space the notch vacates, so * the pointer is still over the element after the step. Hovering a * pseudo-element counts as hovering its owner, the same trick the * bar's own reveal zone uses. * * Only in this mode: nothing moves in the other two, and a permanent * 32px hit area hanging off the notch would swallow clicks meant for * whatever is behind it. */ body.os-active.os-admin-bar-dynamic .os-notch::before { content: ""; position: absolute; inset-inline: 0; inset-block-end: 100%; height: var( --os-admin-bar-height, var( --wp-admin--admin-bar--height, 32px ) ); }