Make the upgrade banner neutral instead of gradient purple (#7696)

## What

The `promo` banner tone was a full-bleed `indigo-500 → purple-500`
gradient with white text and a black drop-shadow on the CTA. It was the
only saturated fill in the app, and against the warm neutral palette it
read as a foreign object above the workbench.

The bar is now app chrome:

| | Before | After |
|---|---|---|
| Background | 135° indigo→purple gradient | `--c-bg-raised` |
| Border | `transparent` | `--c-border-subtle` hairline |
| Icon | white glyph, no container | neutral glyph in a
`--c-surface-sunken` chip |
| Text | forced white | `--c-text` / `--c-text-muted` |
| CTA | `premium` accent (violet gradient) | `default` accent (same
primary button as the rest of the app) |


Before

<img width="1504" height="739" alt="Screenshot 2026-08-27 at 4 49 00 PM"
src="https://github.com/user-attachments/assets/a912d9e9-9590-4e1d-8202-1abb22a00f23"
/>


After

<img width="1061" height="665" alt="Screenshot 2026-08-27 at 4 48 30 PM"
src="https://github.com/user-attachments/assets/3be14aec-ccfe-4448-93b3-339a57be4937"
/>


Only caller is the friendly variant of `UpgradeBanner` (self-hosted,
under the free-tier user limit).

## Notes

- **No new theme tokens.** Every value is an existing `--c-*` semantic
token, so light and dark both follow automatically with no per-theme
overrides.
- The `premium` accent itself is untouched, so the upgrade CTAs in
`OfflineActivationCard` and `PairingPanel` are unaffected.
- `--c-hue-indigo` / `--c-hue-purple` are still used by
`SaaSOnboardingSlides`, `PaygFree` and `UpgradeModal`, so no tokens are
orphaned.
- Deleted comments describe rules that no longer exist (the gradient,
the white-on-gradient text overrides, the CTA shadow). No new comments
added.

## Verification

- `task frontend:check:all` passes (typecheck, oxlint, all four theme
linters, stylelint, format, tests, build, storybook build).
- `task frontend:storybook:a11y:changed` passes light and dark: 7
AppBanner stories, 0 violations. Both a11y baselines are empty, so this
is zero known violations rather than a baselined pass.
- Checked in Storybook under **Shared / AppBanner → All Top Bars**,
which renders every top bar the app can show side by side, in both
themes.
This commit is contained in:
EthanHealy01
2026-08-27 15:57:15 +00:00
committed by GitHub
parent a215c30068
commit 5b5e922069
2 changed files with 14 additions and 19 deletions
@@ -24,17 +24,10 @@
--app-banner-icon: var(--c-accent-fg, var(--c-primary));
}
/* The one bar meant to pop, so it takes the feature gradient rather than a tint.
Fixed hues by design — it doesn't follow the chosen accent. */
.app-banner--promo {
--app-banner-bg: linear-gradient(
135deg,
var(--c-hue-indigo) 0%,
var(--c-hue-purple) 100%
);
--app-banner-border: transparent;
--app-banner-icon: var(--color-text-on-accent);
color: var(--color-text-on-accent);
--app-banner-bg: var(--c-bg-raised);
--app-banner-border: var(--c-border-subtle);
--app-banner-icon: var(--c-text-muted);
}
.app-banner--warning {
@@ -86,16 +79,18 @@
font-size: 0.75rem;
}
/* On the gradient everything is white; muted grey would disappear. */
.app-banner--promo .app-banner__message,
.app-banner--promo .app-banner__actions .sui-btn--tertiary,
.app-banner--promo .app-banner__actions .sui-ai {
color: var(--color-text-on-accent);
.app-banner--promo .app-banner__icon {
width: 1.75rem;
height: 1.75rem;
justify-content: center;
border-radius: var(--radius-md);
background: var(--c-surface-sunken);
box-shadow: inset 0 0 0 1px var(--c-border-subtle);
}
/* Lifts the premium CTA off the gradient it sits on. */
.app-banner--promo .app-banner__actions .sui-btn--primary {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
.app-banner--promo.app-banner--compact .app-banner__icon {
width: 1.5rem;
height: 1.5rem;
}
.app-banner__actions {
@@ -11,7 +11,7 @@ export type AppBannerTone = "info" | "promo" | "warning" | "danger";
/** Tone decides the button too, so the CTA can't drift from the bar it sits on. */
const TONE_BUTTON = {
info: { variant: "secondary", accent: "default" },
promo: { variant: "primary", accent: "premium" },
promo: { variant: "primary", accent: "default" },
warning: { variant: "primary", accent: "warning" },
danger: { variant: "primary", accent: "danger" },
} as const;