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>
262 lines
7.3 KiB
CSS
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);
|
|
}
|