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>
82 lines
1.8 KiB
CSS
82 lines
1.8 KiB
CSS
/*
|
|
FNXC:DashboardBanners 2026-08-23-22:36:
|
|
Dashboard banners share one shell so tone is expressed through a tint and a hairline full border using var(--btn-border-width), preserving thick-border themes. The operator explicitly removed left highlight borders, so this primitive must never add an accent bar.
|
|
*/
|
|
.banner {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border: var(--btn-border-width) solid color-mix(in srgb, var(--banner-tone) 30%, var(--border));
|
|
background: color-mix(in srgb, var(--banner-tone) 10%, var(--surface));
|
|
color: var(--text);
|
|
}
|
|
|
|
.banner--info { --banner-tone: var(--color-info); }
|
|
.banner--warning { --banner-tone: var(--color-warning); }
|
|
.banner--error { --banner-tone: var(--color-error); }
|
|
.banner--success { --banner-tone: var(--color-success); }
|
|
.banner--neutral { --banner-tone: var(--text-muted); }
|
|
|
|
.banner--inline {
|
|
margin-bottom: var(--space-md);
|
|
border-radius: var(--radius-md);
|
|
}
|
|
|
|
.banner--chrome {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: var(--z-sticky);
|
|
border-inline: 0;
|
|
border-top: 0;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.banner--compact {
|
|
padding: var(--space-xs) var(--space-sm);
|
|
}
|
|
|
|
.banner__icon {
|
|
display: flex;
|
|
flex: 0 0 auto;
|
|
color: var(--banner-tone);
|
|
}
|
|
|
|
.banner__copy {
|
|
min-width: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.banner__title {
|
|
color: var(--text);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.banner__body { color: var(--text-muted); }
|
|
|
|
.banner__actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.banner__dismiss {
|
|
flex: 0 0 auto;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.banner__dismiss:hover { color: var(--text); }
|
|
.banner__dismiss:focus-visible { box-shadow: var(--focus-ring-strong); }
|
|
|
|
@media (max-width: 768px) {
|
|
.banner {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.banner__actions {
|
|
width: 100%;
|
|
}
|
|
}
|