diff --git a/src/components/Button/index.stories.tsx b/src/components/Button/index.stories.tsx
index a871c0fd..0e40a22b 100644
--- a/src/components/Button/index.stories.tsx
+++ b/src/components/Button/index.stories.tsx
@@ -411,3 +411,200 @@ export const WithStates: Story = {
),
}
+
+export const RainbowButtonZIndexFix: Story = {
+ render: () => (
+
+
+
+ ),
+}
+
+export const BrandButtonInStackingContexts: Story = {
+ name: 'Brand Button in Various Stacking Contexts',
+ render: () => (
+
+
+
+ ✅ Brand Button Working in All Contexts
+
+
+ The brand button now works correctly in all stacking contexts thanks
+ to built-in fixes.
+
+
+
+
+
+
Flex Containers
+
+
+
Navigation:
+
+
+
+
+
+
+
+
+
Grid Layouts
+
+
+
Project Settings
+
Configure your project
+
+
+
+
+
+
+
High Z-Index Contexts
+
+
+ Modal Header
+
+
+
+
+
+
+
Transform Contexts
+
+
+ Transformed container:
+
+
+
+
+
+
+
+ Overflow Hidden Containers
+
+
+
+ Clipped container:
+
+
+
+
+
+
+
Complex Nested Layouts
+
+
+
+
+ Complex Layout
+
+
+
+
+
+
+
+
+
+ ),
+ parameters: {
+ docs: {
+ description: {
+ story: `
+This story demonstrates the brand button working correctly in various stacking contexts that previously caused issues with the rainbow outline.
+
+**Fixed Issues:**
+- ✅ Flex containers with \`items-center\`
+- ✅ Containers with \`overflow: hidden\`
+- ✅ High z-index contexts
+- ✅ Transform contexts
+- ✅ Complex nested layouts
+- ✅ Grid layouts
+
+**Technical Solution:**
+The component now uses \`transform: translateZ(0)\` to create an isolated stacking context and a separate background span element to ensure proper layering of the rainbow outline pseudo-elements.
+ `,
+ },
+ },
+ },
+}
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 65323684..977965f8 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -107,7 +107,7 @@ const buttonVariants = cva(
},
variant: {
brand:
- 'relative bg-btn-brand hover:bg-btn-brand-hover text-btn-brand hover:text-btn-brand-hover disabled:bg-btn-brand-disabled disabled:text-btn-brand-disabled before:absolute before:content-[""] before:-z-10 before:pointer-events-none [--gradient-rotation:220deg] before:bg-[conic-gradient(from_var(--gradient-rotation),hsl(334,54%,13%),hsl(4,67%,47%),hsl(23,96%,62%),hsl(68,52%,72%),hsl(108,24%,41%),hsl(154,100%,7%),hsl(220,100%,12%),hsl(214,69%,50%),hsl(216,100%,80%),hsl(334,54%,13%))] after:absolute after:content-[""] after:-z-20 after:pointer-events-none after:opacity-0 after:transition-opacity after:duration-300 hover:after:opacity-100 after:bg-[conic-gradient(from_var(--gradient-rotation),hsl(334,54%,13%),hsl(4,67%,47%),hsl(23,96%,62%),hsl(68,52%,72%),hsl(108,24%,41%),hsl(154,100%,7%),hsl(220,100%,12%),hsl(214,69%,50%),hsl(216,100%,80%),hsl(334,54%,13%))] after:blur-[2px] focus-visible:ring-2 focus-visible:ring-offset-3 focus-visible:ring-[var(--border-focus)] focus-visible:ring-offset-[var(--bg-surface-primary-default)]',
+ 'relative text-btn-brand hover:text-btn-brand-hover disabled:text-btn-brand-disabled [transform:translateZ(0)] before:absolute before:content-[""] before:-z-10 before:pointer-events-none [--gradient-rotation:220deg] before:bg-[conic-gradient(from_var(--gradient-rotation),hsl(334,54%,13%),hsl(4,67%,47%),hsl(23,96%,62%),hsl(68,52%,72%),hsl(108,24%,41%),hsl(154,100%,7%),hsl(220,100%,12%),hsl(214,69%,50%),hsl(216,100%,80%),hsl(334,54%,13%))] after:absolute after:content-[""] after:-z-20 after:pointer-events-none after:opacity-0 after:transition-opacity after:duration-300 hover:after:opacity-100 after:bg-[conic-gradient(from_var(--gradient-rotation),hsl(334,54%,13%),hsl(4,67%,47%),hsl(23,96%,62%),hsl(68,52%,72%),hsl(108,24%,41%),hsl(154,100%,7%),hsl(220,100%,12%),hsl(214,69%,50%),hsl(216,100%,80%),hsl(334,54%,13%))] after:blur-[2px] focus-visible:ring-2 focus-visible:ring-offset-3 focus-visible:ring-[var(--border-focus)] focus-visible:ring-offset-[var(--bg-surface-primary-default)]',
primary:
'bg-btn-primary text-btn-primary shadow-[0px_2px_1px_0px_rgba(255,255,255,0.1)_inset,0px_-2px_1px_0px_rgba(0,0,0,0.2)_inset] hover:bg-btn-primary-hover hover:text-btn-primary-hover hover:shadow-[0px_2px_1px_0px_rgba(255,255,255,0.08)_inset,0px_-2px_1px_0px_rgba(0,0,0,0.25)_inset] active:bg-btn-primary-active active:text-btn-primary-active active:shadow-none disabled:bg-btn-primary-disabled disabled:text-btn-primary-disabled',
secondary:
@@ -249,6 +249,22 @@ const Button = React.forwardRef(
const isBrandVariant = variant === 'brand'
+ // Get gap class for the current size
+ const getGapClass = (size: ButtonSize) => {
+ switch (size) {
+ case 'xs':
+ return 'gap-1'
+ case 'sm':
+ return 'gap-1.5'
+ case 'md':
+ return 'gap-2'
+ case 'lg':
+ return 'gap-2.5'
+ default:
+ return 'gap-2'
+ }
+ }
+
// Only run animation frame when brand variant is active
useAnimationFrame(
React.useCallback(
@@ -430,7 +446,24 @@ const Button = React.forwardRef(
onMouseUp={handleMouseUp}
{...props}
>
- {processedChildren}
+ {asChild ? (
+ props.children
+ ) : (
+ <>
+ {isBrandVariant && (
+
+ )}
+
+ {processedChildren}
+
+ >
+ )}
)
}