Pilot for the components-split proposal (mara wants a look at using custom elements now that we're recent-Firefox-only). Picked the themed dialog system as the first candidate: most self-contained of our existing de-facto reusable components (transient, imperative call sites, no external render-tree coupling), and shared between the dashboard and per-agent UI already. <hive-dialog> replaces the manually-built tc-backdrop/tc-box tree in openDialog — connectedCallback renders, the keydown listener and click-outside-to-dismiss are owned by the element instead of a closure, and the outcome is reported via a hive-dialog-close CustomEvent rather than a hand-rolled resolve callback threaded through the DOM tree. <hive-toast> replaces the toast div themedToast built inline — connectedCallback starts the auto-dismiss timer, disconnectedCallback clears it (previously a closure-captured setTimeout handle with no explicit cleanup on early removal). Both are light DOM (no shadow root) — styling stays exactly where it already lived, in modal.css's .tc-* classes, imported globally by both packages' base stylesheets. This was the deliberate call for a first pilot: shadow DOM would need every shared stylesheet re-imported per instance (CSS custom properties pierce shadow boundaries for theming, but plain class rules like .btn don't), which is real migration cost. Light DOM validates the pattern (lifecycle encapsulation, less manual event bookkeeping) without paying that cost; shadow DOM is a drop-in upgrade to these same two classes if a later pilot wants real style encapsulation. Public API unchanged (openDialog/themedConfirm/themedPrompt/ themedToast) — every existing call site across dashboard + agent keeps working with no changes. Verified with a full frontend build.
117 lines
3.5 KiB
CSS
117 lines
3.5 KiB
CSS
/* modal.css — themed dialog component styles.
|
|
Pairs with modal.js (themedConfirm / themedPrompt / themedToast),
|
|
whose `<hive-dialog>` / `<hive-toast>` custom elements are styled
|
|
entirely by class selectors here (light DOM, no shadow root — see the
|
|
design note atop modal.js). The dialogs are raised from the shared
|
|
`bindAsyncForms` data-async / data-confirm handler (forms.js), used by
|
|
both the dashboard and the per-agent UI, so both packages' base
|
|
stylesheets (common.css / agent.css) @import this to keep the styles
|
|
present wherever a dialog can fire.
|
|
(Previously these rules lived in dashboard.css, so dialogs rendered
|
|
unstyled on standalone dashboard pages such as /core, and the per-agent
|
|
UI never had them at all — a themed-dialogs consistency fix moved them
|
|
here and wired both packages' base stylesheets to import this file.) */
|
|
|
|
.tc-backdrop {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 1000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 1em;
|
|
background: color-mix(in srgb, var(--crust) 60%, transparent);
|
|
-webkit-backdrop-filter: blur(2px);
|
|
backdrop-filter: blur(2px);
|
|
}
|
|
.tc-box {
|
|
background: var(--bg-elev);
|
|
border: 1px solid var(--purple-dim);
|
|
box-shadow: 0 8px 40px -8px var(--crust);
|
|
padding: 1.2em 1.4em;
|
|
max-width: min(32em, 92vw);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.9em;
|
|
}
|
|
.tc-title {
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--subtext0);
|
|
}
|
|
.tc-message { color: var(--fg); line-height: 1.45; }
|
|
.tc-checks { display: flex; flex-direction: column; gap: 0.4em; }
|
|
.tc-checkrow {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.5em;
|
|
cursor: pointer;
|
|
color: var(--subtext0);
|
|
font-size: 0.92em;
|
|
line-height: 1.35;
|
|
}
|
|
.tc-checkrow .tc-check { margin-top: 0.2em; flex: 0 0 auto; }
|
|
.tc-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 0.6em;
|
|
margin-top: 0.2em;
|
|
}
|
|
.tc-cancel { color: var(--subtext0); }
|
|
.tc-confirm { color: var(--green); }
|
|
.tc-confirm.tc-danger { color: var(--red); }
|
|
|
|
/* themedPrompt input field */
|
|
.tc-promptfield { display: flex; flex-direction: column; gap: 0.4em; }
|
|
.tc-promptlabel { color: var(--subtext0); font-size: 0.9em; }
|
|
.tc-input {
|
|
font-family: inherit;
|
|
font-size: 1em;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
background: var(--crust);
|
|
color: var(--fg);
|
|
border: 1px solid var(--purple-dim);
|
|
padding: 0.4em 0.6em;
|
|
}
|
|
.tc-input:focus { outline: 1px solid var(--green); }
|
|
/* themedPrompt's field is always a resizable textarea (Enter submits,
|
|
Shift+Enter inserts a newline) — sensible min-height, wrapped output. */
|
|
.tc-textarea {
|
|
resize: vertical;
|
|
min-height: 4.5em;
|
|
line-height: 1.4;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
/* themedToast — non-blocking transient notifications (errors/info/ok) */
|
|
.tc-toasts {
|
|
position: fixed;
|
|
top: 1em;
|
|
right: 1em;
|
|
z-index: 1100;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5em;
|
|
max-width: min(28em, 92vw);
|
|
pointer-events: none;
|
|
}
|
|
.tc-toast {
|
|
pointer-events: auto;
|
|
cursor: pointer;
|
|
background: var(--bg-elev);
|
|
border: 1px solid var(--purple-dim);
|
|
border-left-width: 3px;
|
|
box-shadow: 0 4px 20px -6px var(--crust);
|
|
padding: 0.6em 0.9em;
|
|
font-size: 0.9em;
|
|
color: var(--fg);
|
|
white-space: pre-wrap;
|
|
opacity: 1;
|
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
}
|
|
.tc-toast-out { opacity: 0; transform: translateX(0.5em); }
|
|
.tc-toast-error { border-left-color: var(--red); }
|
|
.tc-toast-info { border-left-color: var(--purple-dim); }
|
|
.tc-toast-ok { border-left-color: var(--green); }
|