A production button: gradient fill + inner-light rim
css
tailwind
cva
buttons
Ask your agent to implement this
Read the full writeup at https://seangeng.com/writing/a-production-button.md and implement it in my project.
It covers: A production button: gradient fill + inner-light rim. The button I actually ship: a class-variance-authority component whose depth comes from two pseudo-element layers (gradient fill, inner-light rim) and a color-matched shadow. Eight variants, four sizes, asChild.
Requirements:
- Follow the technique/approach exactly as described in the writeup.
- Adapt names, colors, and styling to my project's existing conventions.
- If it's a component, make it reusable with sensible props and TypeScript types.
- Keep it accessible: semantic HTML, keyboard support, and respect prefers-reduced-motion.
- When done, tell me which files you created or changed and how to use it.
Most of my flashy buttons are one-offs. This is the one I reach for everywhere.
I ported it from the B3OS design system. It looks like a real object without a
wall of CSS, because the depth lives on two pseudo-element layers.
Two layers do the work
The element carries a color-matched drop shadow. A before: layer paints the
gradient fill, an after: layer paints the inner-light rim, and the label sits
on a relative z-10 wrapper above both:
Why pseudo-elements instead of just a gradient background? Because you can
animate the fill on its own. hover:before:brightness-110 brightens the
gradient and leaves the text alone, while active:translate-y-px presses the
whole key. rounded-[inherit] keeps the layers matching whatever radius the
size sets.
Variants are cheap
Every variant is the same recipe with two gradient stops and a shadow tint
swapped out: black, white, destructive, success, plus the flat
outline, ghost, and link. It's a cva config, so
variant and size come through as typed props.
asChild
asChild (via Radix Slot) lets a <Link> wear the button styling without
nesting an <a> inside a <button>: