Files
fusion/packages/dashboard/app/components/SessionNotificationBanner.css
gsxdsm 9d1bd393d8 FN-9202: Unify dashboard banner styling
Standardize dashboard notices on a shared, token-driven banner shell.

- Add a reusable Banner primitive with consistent tones, layouts, actions, and dismissal behavior.
- Migrate dashboard notification banners away from bespoke shells and left accent borders.
- Document and test banner styling invariants, including token-only CSS lengths and colors.
- Add a patch changeset for the published dashboard bundle.

Files changed:
 .changeset/fn-9202-banner-style-unification.md     |   7 ++
 docs/dashboard-guide.md                            |   4 +
 .../app/__tests__/banner-style-consistency.test.ts |  25 ++++
 .../app/components/ApprovalNotificationBanner.css  |  10 --
 .../app/components/ApprovalNotificationBanner.tsx  |   5 +-
 packages/dashboard/app/components/Banner.css       |  81 ++++++++++++
 packages/dashboard/app/components/Banner.tsx       |  78 ++++++++++++
 .../app/components/CapacityRiskBanner.css          |  50 +-------
 .../app/components/CapacityRiskBanner.tsx          |  39 +++---
 .../app/components/CliBinaryInstallBanner.css      | 126 ++-----------------
 .../app/components/CliBinaryInstallBanner.tsx      |  14 +--
 .../app/components/DbCorruptionBanner.css          |  18 +--
 .../app/components/DbCorruptionBanner.tsx          |   5 +-
 .../app/components/EngineStatusBanner.css          |  94 ++------------
 .../app/components/EngineStatusBanner.tsx          |  48 +++----
 .../app/components/EngineUnavailableBanner.css     |  42 +------
 .../app/components/EngineUnavailableBanner.tsx     |  21 ++--
 .../app/components/MergeAdvanceNotice.css          | 129 +++----------------
 .../app/components/MergeAdvanceNotice.tsx          |  36 +++---
 .../app/components/MigrationInProgressBanner.css   |  25 ----
 .../app/components/MigrationInProgressBanner.tsx   |  19 +--
 .../app/components/OAuthReloginBanner.css          |  53 +-------
 .../app/components/OAuthReloginBanner.tsx          |  26 +---
 .../app/components/SessionNotificationBanner.css   |  16 +--
 .../app/components/SessionNotificationBanner.tsx   |   5 +-
 .../app/components/SetupWarningBanner.css          | 106 +---------------
 .../app/components/SetupWarningBanner.tsx          |  31 +----
 .../app/components/SqliteMigrationBanner.css       |  40 +-----
 .../app/components/SqliteMigrationBanner.tsx       |  33 ++---
 .../app/components/TaskIdIntegrityBanner.css       |  18 +--
 .../app/components/TaskIdIntegrityBanner.tsx       |   5 +-
 .../dashboard/app/components/TestModeBanner.css    |  18 ---
 .../dashboard/app/components/TestModeBanner.tsx    |  12 +-
 .../app/components/UpdateAvailableBanner.css       | 139 +++------------------
 .../app/components/UpdateAvailableBanner.tsx       |  22 ++--
 .../app/components/__tests__/Banner.test.tsx       |  48 +++++++
 36 files changed, 450 insertions(+), 998 deletions(-)

Fusion-Task-Id: FN-9202

Fusion-Task-Lineage: 14762201-55e5-4124-a897-9667c3a1ab84

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-23 16:00:53 -07:00

262 lines
7.3 KiB
CSS

/* === Session Notification Banner === */
/*
FNXC:SessionBanner 2026-06-25-00:00:
FN-7020 requires the needs-input banner to stay visible when relevant without competing with primary dashboard content. Keep the desktop and mobile layouts compact by reducing padding, gaps, list heights, and text scale while preserving token-based touch targets.
*/
.session-notification-banner {
--session-notification-banner-max-height: min(52vh, calc(var(--space-2xl) * 10));
--session-notification-list-max-height: min(38vh, calc(var(--space-2xl) * 7));
--session-notification-touch-size: calc(var(--space-xl) + var(--space-xs));
box-sizing: border-box;
max-height: var(--session-notification-banner-max-height);
opacity: 1;
transition: opacity var(--transition-fast), max-height var(--transition-fast);
animation: session-notification-banner-enter var(--transition-fast);
}
.session-notification-banner > .banner__copy {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
@keyframes session-notification-banner-enter {
from {
opacity: 0;
max-height: 0;
}
to {
opacity: 1;
max-height: var(--session-notification-banner-max-height);
}
}
.session-notification-banner__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-sm);
}
.session-notification-banner__headline {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
font-size: 0.875rem;
font-weight: 600;
color: var(--text);
}
.session-notification-banner__headline svg {
color: var(--triage);
flex-shrink: 0;
}
.session-notification-banner__dismiss-all {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
min-height: var(--session-notification-touch-size);
border: var(--btn-border-width) solid transparent;
background: transparent;
color: var(--text-muted);
font-size: 0.6875rem;
font-weight: 600;
border-radius: var(--radius-sm);
padding: var(--space-xs) var(--space-sm);
cursor: pointer;
transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.session-notification-banner__dismiss-all:hover {
color: var(--text);
background: var(--card-hover);
border-color: var(--border);
}
.session-notification-banner__dismiss-all:focus-visible,
.session-notification-banner__resume:focus-visible,
.session-notification-banner__dismiss:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.session-notification-banner__list {
display: flex;
flex-direction: column;
gap: var(--space-xs);
overflow-y: auto;
max-height: var(--session-notification-list-max-height);
}
.session-notification-banner__item {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
border: var(--btn-border-width) solid var(--border);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--triage) 8%, var(--surface));
}
.session-notification-banner__item--error {
border-color: color-mix(in srgb, var(--color-error) 35%, var(--border));
background: var(--status-error-bg, color-mix(in srgb, var(--color-error) 12%, var(--surface)));
}
.session-notification-banner__item-main {
display: flex;
align-items: center;
gap: var(--space-xs);
min-width: 0;
flex: 1;
}
.session-notification-banner__type-icon {
color: var(--triage);
flex-shrink: 0;
}
.session-notification-banner__type-icon--error {
color: var(--color-error);
}
.session-notification-banner__text {
min-width: 0;
}
.session-notification-banner__title {
margin: 0;
font-size: 0.75rem;
font-weight: 600;
color: var(--text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.session-notification-banner__meta {
margin: var(--space-xs) 0 0;
font-size: 0.6875rem;
color: var(--text-muted);
}
.session-notification-banner__actions {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
}
.session-notification-banner__resume {
min-height: var(--session-notification-touch-size);
border: var(--btn-border-width) solid color-mix(in srgb, var(--triage) 60%, var(--border));
background: transparent;
color: var(--triage);
border-radius: var(--radius-sm);
font-size: 0.6875rem;
font-weight: 600;
padding: var(--space-xs) var(--space-sm);
cursor: pointer;
transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.session-notification-banner__resume:hover:not(:disabled) {
background: color-mix(in srgb, var(--triage) 14%, transparent);
border-color: var(--triage);
}
.session-notification-banner__resume:disabled,
.session-notification-banner__resume--disabled {
border-color: var(--border);
color: var(--text-muted);
background: var(--surface-muted);
cursor: not-allowed;
}
.session-notification-banner__item--error .session-notification-banner__resume {
border-color: color-mix(in srgb, var(--color-error) 60%, var(--border));
color: var(--color-error);
}
.session-notification-banner__item--error .session-notification-banner__resume:disabled,
.session-notification-banner__item--error .session-notification-banner__resume--disabled {
border-color: var(--border);
color: var(--text-muted);
background: var(--surface-muted);
}
.session-notification-banner__item--error .session-notification-banner__resume:hover:not(:disabled) {
background: color-mix(in srgb, var(--color-error) 14%, transparent);
border-color: var(--color-error);
}
.session-notification-banner__dismiss {
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--session-notification-touch-size);
height: var(--session-notification-touch-size);
border-radius: var(--radius-sm);
border: var(--btn-border-width) solid transparent;
background: transparent;
color: var(--text-muted);
cursor: pointer;
transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.session-notification-banner__dismiss:hover {
color: var(--text);
background: var(--card-hover);
border-color: var(--border);
}
@media (max-width: 768px) {
.session-notification-banner {
--session-notification-banner-max-height: min(46vh, calc(var(--space-2xl) * 9));
--session-notification-list-max-height: min(32vh, calc(var(--space-2xl) * 6));
--session-notification-touch-size: calc(var(--space-xl) + var(--space-md));
padding: var(--space-xs) var(--space-sm);
gap: var(--space-xs);
}
.session-notification-banner__header {
flex-direction: column;
align-items: flex-start;
}
.session-notification-banner__item {
flex-direction: column;
align-items: flex-start;
gap: var(--space-xs);
padding: var(--space-xs);
}
.session-notification-banner__item-main {
width: 100%;
}
.session-notification-banner__title {
max-width: 100%;
}
.session-notification-banner__actions {
width: 100%;
justify-content: flex-end;
}
}
/*
FNXC:SessionBanner 2026-07-16-20:55:
FN-8229 uses the established banner icon treatment for non-planning work that
is still generating after the footer pill is removed. Keep progress semantic
rather than introducing a separate component or hard-coded status color.
*/
.session-notification-banner__type-icon--progress {
color: var(--color-accent);
}