Verified against Claude · 2026-07-20
Design a component API that won't need a breaking change in six months
Designs a design-system component's public prop contract by working backward from real use cases, with explicit composition slots instead of a new boolean prop for every variant, and a written out-of-scope list.
The prompt
Ready to copy — highlighted parts are example details you can swap.
You are designing the public prop API for a design-system component, before implementation. The goal is an API that covers the real use cases below without a boolean-prop explosion, and without a breaking change the moment the next legitimate use case shows up. COMPONENT NAME Banner USE CASES 1) An info banner at the top of a page. 2) A dismissible warning banner inline in a form. 3) An error banner with a retry action. 4) A success confirmation that auto-dismisses after a few seconds. CONSTRAINTS Must not depend on a specific styling library beyond CSS variables already defined in the design tokens; must work inside a form without stealing focus; must support RTL layouts. EXISTING SIMILAR COMPONENTS A separate Toast component already handles transient, corner-positioned notifications — Banner is only for inline, in-flow content. API DESIGN RULES Start from the use cases, not from a guess at every possible variant someone might want someday. Every prop you propose must trace back to at least one listed use case; if you cannot name which use case a prop serves, drop the prop. Prefer composition slots — children, a named slot prop, an asChild-style pattern — over a boolean prop per visual variant. If you are about to propose a fourth boolean like isCompact, isCentered, or isBordered on the same component, stop and ask whether a variant prop with a closed set of string values, or a composition slot, actually fits better; three or more independent booleans on one component is the concrete signal that the API has stopped being designed and started being accreted. Every prop needs a default that makes the component usable with zero configuration for the single most common use case in the list. Name props for what they mean to the person using the component, not for the CSS or implementation detail behind them — status, not colorScheme, when the actual intent being expressed is semantic, not decorative. Explicitly list what this component will deliberately not support — the use cases you are choosing not to design for right now — so that scope creep in a future review has something concrete to push against instead of an unstated assumption. Where the component wraps or extends a native HTML element, do not repurpose a native attribute name for a different meaning than it has natively — a prop called disabled should behave like the native disabled attribute a consumer already knows, not toggle some unrelated visual state, since reusing a familiar name for a different contract is a worse trap than inventing an unfamiliar one. Decide upfront, and state explicitly, which native HTML attributes and event handlers pass through untouched to the underlying element versus which ones this component intercepts or overrides — a consumer reaching for a standard prop like onClick or aria-label needs to know, without reading the implementation, whether it will reach the DOM node directly or get intercepted by internal logic first. OUTPUT FORMAT 1. The prop table: name, type, default, and which specific use case it serves. 2. A short usage example for the three most common use cases from the list, as real code. 3. The explicit out-of-scope list, with one line on why each item was deliberately excluded rather than just forgotten.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
Boolean-prop explosion is the single most common way a design-system component's API rots over time — isCompact plus isCentered plus isBordered eventually produce combinations nobody ever actually tested and some that aren't even valid together — so giving the model a concrete, countable trigger ("about to write a fourth boolean, stop") is measurably more effective than a vague instruction to "keep the API simple," which every design system already claims to want and few actually achieve in practice. Requiring every prop to trace back to a specific listed use case is what actually prevents speculative generality: the temptation to add a prop for a use case nobody has asked for yet is exactly how component APIs accumulate surface area that no consumer uses but every maintainer still has to support, test, and document forever. The explicit out-of-scope list matters specifically because a design-system component is a shared contract other teams build against without talking to the person who designed it — writing down what it deliberately does not support is the concrete artifact that lets a reviewer six months later say "that's a genuinely new use case, it needs its own design review" instead of quietly bolting another prop onto an already-overloaded component because the original scope was never actually written down anywhere. Forbidding a repurposed native attribute name targets a trap that is worse than an unfamiliar API, not merely annoying: a consumer who already knows what disabled means on a native button will apply that exact mental model to a design-system component's disabled prop, and if the component quietly means something else by it — dimmed but still clickable, for instance — the resulting bug is invisible in code review because the prop name itself looks completely ordinary, surfacing only when a real user clicks something they were visually told was off. Requiring an explicit statement of which native props and handlers pass through untouched versus which ones the component intercepts closes the single most common integration complaint against design-system components: a consumer passes a standard prop expecting standard behavior, discovers weeks later that the component silently swallowed or transformed it, and has no way to find that out except by reading the component's source — which defeats the entire point of a documented, reusable API in the first place.
What you get back
Prop | Type | Default | Use case variant | 'info' | 'warning' | 'error' | 'success' | 'info' | all four listed use cases dismissible | boolean | false | warning banner (2) onDismiss | () => void | undefined | warning banner (2) action | { label: string; onClick: () => void } | undefined | error banner with retry (3) autoDismissMs | number | undefined | success confirmation (4) Out of scope: stacked or queued multiple banners shown at once, and a persistent banner-history log — both deliberately left for a separate NotificationCenter component so Banner stays a single, stateless piece of inline content.
Verified against
Claude Sonnet 4.6 · 2026-07-20
ChatGPT GPT-5.1 · 2026-07-31
Changelog
- 2026-07-20 — Initial publish, verified against Claude (Sonnet 4.6) and ChatGPT (GPT-5.1).
Need this built into your business?
If a prompt isn't enough — custom software, built and maintained for you — that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
