hyperhive/frontend/packages/shared/src/hive-menu/hive-menu.css
iris 8ebefeb0d5 frontend: move hive-menu's reachable dropdown/trigger chrome out of the caller stylesheet
Per mara's review on #2881: the dropdown box chrome (background/border/
radius/shadow/min-width/white-space) and the trigger button's base
icon-button treatment are both reachable from hive-menu.css now --
the box chrome lives on hive-menu's own shadow-owned .menu-dropdown
wrapper (no slotting constraint at all), and the trigger button is
styled via ::slotted([slot='trigger']) since it's the top-level slotted
node for that slot. Item-row styling stays in the caller's stylesheet
-- ::slotted() only reaches directly-slotted elements, not their
descendants, so individual dropdown items are architecturally
unreachable from hive-menu's shadow tree. Verified interactively via
headless Chromium/CDP: trigger opacity/hover/border-radius and the
dropdown wrapper's background/border all resolve correctly, hover and
click-to-open still work.
2026-07-31 23:24:22 +02:00

58 lines
2.2 KiB
CSS

/* hive-menu.css — scoped stylesheet for the generic <hive-menu>
shadow-DOM custom element (hive-menu.js). Loaded as raw text at build
time (esbuild's `text` loader) and appended as a <style> element
inside the shadow root — see @hive/shared/shadow-css.js's header
comment for why a plain <style> tag and not adoptedStyleSheets.
Two kinds of rules live here, split by what they can actually reach:
`:host`/`.menu-dropdown` style shadow-DOM-owned markup this element
builds itself (no slotting constraint at all); `::slotted(...)` rules
reach the caller's *top-level* trigger/content nodes (the ones with a
`slot` attribute) since those are true light-DOM children, just
rendered here. What can NOT live here: anything inside the content
node (e.g. individual dropdown item rows) — `::slotted()` only
matches directly-slotted elements, not their descendants, and no CSS
mechanism pierces further than that. That's a hard Shadow DOM
architecture limit, not a scope choice — item-level styling stays in
the caller's own stylesheet regardless of how generic it looks. */
:host {
position: relative;
}
.menu-dropdown {
position: absolute;
right: 0;
top: calc(100% + 2px);
z-index: 50;
background: var(--bg-elev);
border: 1px solid var(--purple-dim);
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
min-width: 10em;
white-space: nowrap;
}
/* The trigger is the top-level `slot="trigger"` node — reachable, so its
base icon-button chrome (invisible until hover/open, via
`--menu-btn-opacity`) lives here rather than duplicated per caller. */
::slotted([slot='trigger']) {
display: block;
background: none;
border: none;
color: var(--subtext0);
font-size: 1.1em;
line-height: 1;
cursor: pointer;
padding: 0.1em 0.4em;
border-radius: 4px;
opacity: var(--menu-btn-opacity, 0);
transition: opacity 120ms, background 120ms, color 120ms;
}
::slotted([slot='trigger']:hover),
::slotted([slot='trigger']:focus-visible) {
background: color-mix(in srgb, var(--purple) 10%, transparent);
color: var(--purple);
outline: none;
}
::slotted([slot='trigger']:focus-visible) {
outline: 1px solid var(--purple);
}