02
Detailed Case Study

Inline Alert

Establishing Clarity Between Banners, Toasts, and Contextual Feedback — how I found and filled a structural gap in the messaging system.

// How it started

The system had two tools for feedback. It needed three.

When I joined and began auditing the Shield Design System, the component library had two options for communicating status to users: Banners and Toasts. Banners were full-width, persistent, and sat at the top of the page. Toasts were floating notifications that auto-dismissed after a few seconds.

In theory, these covered the spectrum. In practice, they were being used for everything — including the things neither was designed for. Forms were throwing global page banners when a single field failed validation. Dashboards were using toasts to notify users of system-critical errors that disappeared before anyone could read them. Engineers were improvising in-between states using hardcoded div styles that weren't in the system at all.

"If your design system doesn't have the right tool, teams will build their own version — poorly, inconsistently, and at scale."
// The audit

Mapping the problem across six products.

Before designing anything, I audited every product area to understand how feedback messaging was actually being used. I documented message instances across six products and tagged them by type, trigger, placement, and severity.

The pattern was immediate: there was a massive "missing middle." Teams needed a way to show contextual, in-line feedback — tied to a specific form, table row, or UI region — that was neither a full-page announcement nor a transient float. That component didn't exist in the library.

01
Banner misuse — Global banners were appearing for local, field-level errors. A failed form submission was triggering a page-level red banner that implied the entire system had a problem.
02
Toast misuse — Critical errors were being delivered as toasts. Security warnings were auto-dismissing. Users in complex workflows were missing error states entirely because the notification had already disappeared.
03
Ad-hoc markup — At least four teams had built their own inline error states using raw div and span elements outside the design system, each with different colors, typography, and spacing. No token usage. No consistency. No accessibility.
// Defining the hierarchy

Three layers. One clear rule for each.

I looked at how Carbon (IBM) and Polaris (Shopify) had solved this. Both had arrived at a similar tiered architecture: global, contextual, and transient. That gave me validation for the structure. The design challenge was building the middle tier in a way that fit Shield's token system and cybersecurity context.

Component
Scope
Behavior
Banner
Global — page or session level
Persistent — requires user action to dismiss
Inline Alert (new)
Contextual — tied to a specific UI region
Persistent — stays until the condition resolves
Toast
Transient — floating, unanchored
Auto-dismisses — for low-stakes confirmations only

The rule I wrote into the documentation: if the error will still be true in 10 seconds, it cannot be a toast. That single sentence eliminated the most common misuse pattern immediately.

// Variant architecture

Four variants. Each one earns its color.

I designed four semantic variants. The constraint was strict: color had to communicate severity, not brand. In a security product, color carries high stakes — a yellow warning in a SIEM dashboard is not the same as a yellow promotion banner on an e-commerce site. Every choice had to be justified by the cognitive load it placed on the user.

// INFO
Neutral guidance, tips, or feature discovery. No action required. Non-interruptive. Used for onboarding nudges and configuration hints.
// SUCCESS
Confirmation of a completed positive action. Tied to the triggering UI region — not a toast. Disappears when the user navigates away, not on a timer.
// WARNING
A potential problem that doesn't block the user but requires awareness. The user can proceed — but they should read this first. Used for configuration drift, expiring sessions, and policy conflicts.
// ERROR
A critical failure or blocker. Cannot be dismissed without resolving the underlying condition. Always includes specific, actionable copy — never generic error text.
// Component decisions

Three decisions that defined the structure.

01
Compact mode for dense environments

The standard alert height was 64px. Security dashboards — SIEMs, endpoint management consoles — operate at much higher information density. I designed a Compact variant at 48px that reduced vertical padding and used a condensed label format. It carries the same semantic color and icon, but takes up 25% less vertical space. The constraint was real: in a table with 50 rows, an alert that pushed the row height up by 30px added 1,500px of scroll debt.

02
Icon as a required element, not an optional one

Color alone is never sufficient for accessibility — and in a security context, it's not sufficient for clarity either. Every variant ships with a required icon: info circle, check circle, exclamation triangle, x-circle. The icons are drawn from Shield's icon system and map to the same tokens as the text and background. Color-blind users get the icon signal. Screen readers get the aria-label. The icon isn't decoration — it's a second channel for severity communication.

03
Dismissibility tied to severity

I made dismissibility a property of the variant, not a property the consuming team configures. Info and Success alerts are dismissible by default — the information is useful but not blocking. Warning alerts are dismissible with a visible acknowledgment state. Error alerts are not dismissible at all until the underlying condition is resolved. This decision was controversial internally, but I stood by it: letting users dismiss an error they haven't fixed leads to silent failures in security workflows.

// Token architecture

Compliance baked in, not bolted on.

Every color in the Inline Alert component maps to a semantic token, not a raw hex value. The token names encode intent: feedback-error-background, feedback-warning-border, feedback-success-text. These tokens are resolved from the core palette through a semantic layer — which means when the core palette changes (rebranding, dark mode update, contrast adjustment), the alerts update automatically.

WCAG AA contrast — All text/background combinations in all four variants verified at 4.5:1 minimum in both light and dark mode. The tokens prevent non-compliant combinations by design.
Focus states — Dismissible variants have a visible focus ring that passes contrast requirements. The close button is reachable via keyboard. Tab order is logical within the component.
ARIA roles — Info and Success use role="status" (polite announcement). Warning and Error use role="alert" (assertive, immediate announcement). Screen readers prioritize the severity correctly.
// Adoption

Rolling it out across six product areas.

Shipping the component was the easier half. Getting six teams to migrate away from their ad-hoc implementations was the real work. I wrote the documentation to answer the questions I knew teams would ask: what's the difference between this and a Banner? When do I use Warning vs Error? Can I add a CTA button?

I ran office hours for two sprints after launch. Every team that had built a custom inline alert migrated to the system component without exception. The migrations were straightforward because the component was designed to be a drop-in for the patterns that already existed — just governed, tokenized, and accessible.

// Outcome

What changed and what I learned.

The Inline Alert component closed a gap that was generating real engineering debt and real user confusion. The "shouting" problem — where everything looked equally urgent regardless of severity — was structurally solved by the hierarchy. Teams stopped improvising. Users stopped missing critical errors because they'd been wrapped in an auto-dismissing toast.

"The best component design solves the use case teams already have — it just does it right. The goal wasn't to introduce a new pattern. It was to govern the one that already existed, badly."

The lesson I took from this project: gaps in a design system don't stay empty. Teams fill them with something. The question is whether that something is governed or improvised. Finding and filling the gap before it compounds is the actual systems design work.