The agent terminal tab now takes the whole window except the header bar, full width and height, instead of sitting in the padded content column. The connection badge floats inside the terminal's top-left corner and the toolbar row above it is gone on this route.
284 lines
9.5 KiB
CSS
284 lines
9.5 KiB
CSS
/* <Shell> — header bar + nav row + the content column every route
|
|
renders into. Colour vars come from ../theme.css (same base16 slots
|
|
the per-hive dashboard uses, so this reads as recognizably
|
|
hyperhive) — the layout itself is this package's own, not
|
|
@hive/shared's chrome.css.
|
|
|
|
`flex-wrap` on the header and nav rather than a hamburger/collapse
|
|
menu: a narrow viewport (phone or a tiled desktop window) wraps onto
|
|
a second line instead of overflowing — "don't break, don't
|
|
over-invest" is the explicit scope here, not full mobile navigation
|
|
redesign.
|
|
|
|
Page-switch animation (see Shell.tsx's file-top comment for the JS
|
|
half): both the nav indicator's slide and the page-body pop follow
|
|
the same three-rule motion-guard shape `@hive/shared/motion-apply.js`'s
|
|
own doc comment specifies — a base rule,
|
|
`@media (prefers-reduced-motion: reduce)` to respect the OS default,
|
|
`:root[data-motion='reduce']` as the explicit override (same
|
|
specificity as the media rule, so source order after it wins), then
|
|
`:root[data-motion='allow']` last to re-enable despite an OS-level
|
|
reduce preference. */
|
|
/* Sticky + frosted glass (mara: "sticky header bar ... frosted glass")
|
|
— same `color-mix` + `backdrop-filter` recipe `.terminal-wrap`/
|
|
`.ui-badge` (@hive/shared) already use, dark-only same as those (a
|
|
light bg behind glass washes out). `z-index` keeps it above
|
|
`.shell-body` once content scrolls underneath. */
|
|
.shell-header {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 1.5em;
|
|
padding: 0.75em 1.25em;
|
|
border-bottom: 1px solid var(--border);
|
|
background: color-mix(in srgb, var(--bg-elev) 80%, transparent);
|
|
-webkit-backdrop-filter: blur(8px) saturate(120%);
|
|
backdrop-filter: blur(8px) saturate(120%);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 10;
|
|
}
|
|
@media (prefers-color-scheme: light) {
|
|
.shell-header {
|
|
background: var(--bg-elev);
|
|
-webkit-backdrop-filter: none;
|
|
backdrop-filter: none;
|
|
}
|
|
}
|
|
:root[data-theme="light"] .shell-header {
|
|
background: var(--bg-elev);
|
|
-webkit-backdrop-filter: none;
|
|
backdrop-filter: none;
|
|
}
|
|
:root[data-theme="dark"] .shell-header {
|
|
background: color-mix(in srgb, var(--bg-elev) 80%, transparent);
|
|
-webkit-backdrop-filter: blur(8px) saturate(120%);
|
|
backdrop-filter: blur(8px) saturate(120%);
|
|
}
|
|
.shell-brand {
|
|
font-weight: 600;
|
|
/* Base colour only — Shell.tsx overrides it inline to the same
|
|
currently-active-tab accent `.shell-nav-indicator` uses (falling
|
|
back to this `--purple` before the first measurement lands, which
|
|
is also the "hives" default route's own accent, so there's no
|
|
visible mismatch at first paint). Transition lives here, same
|
|
140ms hop cadence + the same three-rule motion guard as the
|
|
indicator, so this and the indicator read as one thing changing,
|
|
not two independently-synced ones. */
|
|
color: var(--purple);
|
|
letter-spacing: 0.02em;
|
|
transition: color 140ms ease;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.shell-brand {
|
|
transition: none;
|
|
}
|
|
}
|
|
:root[data-motion="reduce"] .shell-brand {
|
|
transition: none;
|
|
}
|
|
:root[data-motion="allow"] .shell-brand {
|
|
transition: color 140ms ease;
|
|
}
|
|
.shell-nav {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1em;
|
|
position: relative; /* anchor for .shell-nav-indicator */
|
|
}
|
|
/* Holds `SettingsMenu` + `LinksMenu` — one `margin-left: auto` on the
|
|
wrapper, not one on each child (see Shell.tsx's comment for why two
|
|
adjacent auto margins don't sit flush together). */
|
|
.shell-header-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.25em;
|
|
margin-left: auto;
|
|
}
|
|
/* Touch target and active-state underline are deliberately on two
|
|
different elements. An earlier version put both `min-height: 2.75em`
|
|
and `border-bottom` on the same box: centering the text within a
|
|
2.75em box pushes a bottom-edge border ~0.7em away from the text,
|
|
which argus caught by actually rendering it — a real visual
|
|
regression, not just a touch-target win. So: `.shell-nav-link` (the
|
|
`<a>`) owns the full-height tappable box (same 2.75em / ~44px WCAG
|
|
2.5.5 floor as the rest of the shared kit — this is the primary nav,
|
|
the single most-clicked/tapped element in the whole shell), and
|
|
`.shell-nav-link-text` (an inner span hugging just the text) owns
|
|
the underline, so the indicator stays directly under the label
|
|
regardless of how tall the tappable box around it is. */
|
|
.shell-nav-link {
|
|
display: flex;
|
|
align-items: center;
|
|
min-height: 2.75em;
|
|
color: var(--muted);
|
|
text-decoration: none;
|
|
}
|
|
.shell-nav-link:hover {
|
|
color: var(--fg);
|
|
}
|
|
.shell-nav-link-text {
|
|
padding-bottom: 0.25em; /* gap between the text and .shell-nav-indicator below it */
|
|
}
|
|
.shell-nav-link-active {
|
|
color: var(--fg);
|
|
}
|
|
/* Shared underline, one element instead of one border-bottom per link
|
|
— see Shell.tsx's file-top comment. `left`/`top`/`width` are set
|
|
inline per-render from a real measurement; only the *transition*
|
|
between those values lives here. 140ms matches Shell.tsx's `HOP_MS`
|
|
exactly, so one hop's CSS transition finishes right as the next
|
|
hop's JS-driven style update lands, instead of the two blending into
|
|
an averaged easing curve. */
|
|
.shell-nav-indicator {
|
|
position: absolute;
|
|
height: 2px;
|
|
border-radius: 1px;
|
|
transition:
|
|
left 140ms ease,
|
|
top 140ms ease,
|
|
width 140ms ease,
|
|
background-color 140ms ease;
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.shell-nav-indicator {
|
|
transition: none;
|
|
}
|
|
}
|
|
:root[data-motion="reduce"] .shell-nav-indicator {
|
|
transition: none;
|
|
}
|
|
:root[data-motion="allow"] .shell-nav-indicator {
|
|
transition:
|
|
left 140ms ease,
|
|
top 140ms ease,
|
|
width 140ms ease,
|
|
background-color 140ms ease;
|
|
}
|
|
/* Suppresses the transition for exactly one render — the indicator's
|
|
very first real position, so it snaps into place instead of visibly
|
|
growing from the unmounted `left:0/width:0/transparent` fallback (see
|
|
Shell.tsx's `indicatorSettledOnce`). Two-class selector so it wins
|
|
over the plain `.shell-nav-indicator` rule above without `!important`,
|
|
same reasoning `:root[data-motion='reduce']`'s extra attribute
|
|
selector already relies on. */
|
|
.shell-nav-indicator.shell-nav-indicator-instant {
|
|
transition: none;
|
|
}
|
|
/* Dynamic tabs group — inline in `.shell-nav` itself, next to the
|
|
static route links (mara, PR review: "the dynamic tab should be in
|
|
the top bar, not a new one below" — an earlier cut had this as its
|
|
own sticky row under the header; that version and the reasoning for
|
|
it are still in this file's git history if the split ever needs to
|
|
come back). `margin-left: 0.5em` + `border-left` reads as "a group
|
|
attached to the nav, not another nav item" without a second landmark
|
|
or a fixed pixel gap that'd fight `.shell-nav`'s own `gap`. */
|
|
.shell-tabs {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 0.5em;
|
|
margin-left: 0.5em;
|
|
padding-left: 0.75em;
|
|
border-left: 1px solid var(--border);
|
|
}
|
|
.shell-tab {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.25em;
|
|
padding: 0.2em 0.4em 0.2em 0.7em;
|
|
border-radius: 0.3em;
|
|
background: var(--bg);
|
|
border: 1px solid var(--border);
|
|
}
|
|
.shell-tab-active {
|
|
border-color: var(--pink);
|
|
}
|
|
.shell-tab-link {
|
|
color: var(--muted);
|
|
text-decoration: none;
|
|
font-size: 0.9em;
|
|
}
|
|
.shell-tab-active .shell-tab-link {
|
|
color: var(--fg);
|
|
}
|
|
.shell-tab-link:hover {
|
|
color: var(--fg);
|
|
}
|
|
/* Same reset every icon-only chrome button in this codebase uses
|
|
(`Badge`'s own `<button>` case) — a bare `<button>` here isn't routed
|
|
through `Badge` since it's not a status/value pill, just a close
|
|
glyph, so the reset is inlined rather than reaching for a component
|
|
that doesn't fit this shape. */
|
|
.shell-tab-close {
|
|
all: unset;
|
|
cursor: pointer;
|
|
line-height: 1;
|
|
padding: 0.1em 0.3em;
|
|
border-radius: 0.2em;
|
|
color: var(--muted);
|
|
}
|
|
.shell-tab-close:hover {
|
|
color: var(--fg);
|
|
background: color-mix(in srgb, var(--fg) 10%, transparent);
|
|
}
|
|
|
|
.shell-body {
|
|
max-width: 60em;
|
|
margin: 0 auto;
|
|
padding: 1.5em 1.25em;
|
|
/* Plain fade, deliberately — mara's call on review: a scale+overshoot
|
|
"pop" (this rule's first cut) read as too much for a first pass.
|
|
Keeping it simple leaves room to make this fancier later without
|
|
having shipped something to walk back first. */
|
|
animation: shell-page-enter 160ms ease;
|
|
}
|
|
/* Opt-in widening for a route that genuinely needs more than a
|
|
readable-line-length column (see Shell.tsx's `WIDE_BODY_ROUTES`).
|
|
Only overrides `max-width` — margin/padding/animation stay
|
|
`.shell-body`'s, so this reads as "the same body, just wider" rather
|
|
than a parallel layout to keep in sync. 90em still centers with
|
|
visible margins on a typical desktop viewport; it isn't full-bleed. */
|
|
.shell-body-wide {
|
|
max-width: 90em;
|
|
}
|
|
/* Full-bleed route (the agent terminal): the shell becomes a viewport-high
|
|
column, the header keeps its natural height, and the body takes every
|
|
remaining pixel with no width cap or padding. `100dvh`, not `100vh` —
|
|
`vh` includes mobile browser chrome that is on screen. */
|
|
.shell-full {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100dvh;
|
|
}
|
|
.shell-full .shell-header {
|
|
flex-shrink: 0;
|
|
}
|
|
.shell-body-full {
|
|
flex: 1;
|
|
min-height: 0;
|
|
max-width: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
@keyframes shell-page-enter {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.shell-body {
|
|
animation: none;
|
|
}
|
|
}
|
|
:root[data-motion="reduce"] .shell-body {
|
|
animation: none;
|
|
}
|
|
:root[data-motion="allow"] .shell-body {
|
|
animation: shell-page-enter 160ms ease;
|
|
}
|