;
+
+export const Default: Story = {
+ args: {
+ children: 'Primary Action',
+ },
+};
+
+export const Size: Story = {
+ render: (args) => (
+
+
+ Small
+
+
+ Medium
+
+
+ Large
+
+
+ ),
+};
+
+export const IsFullWidth: Story = {
+ args: {
+ children: 'Full Width',
+ isFullWidth: true,
+ },
+};
+
+export const StartIconName: Story = {
+ args: {
+ children: 'Start Icon',
+ startIconName: IconName.AddSquare,
+ },
+};
+
+export const EndIconName: Story = {
+ args: {
+ children: 'End Icon',
+ endIconName: IconName.AddSquare,
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ children: 'Disabled Button',
+ isDisabled: true,
+ },
+};
+
+export const Loading: Story = {
+ args: {
+ children: 'Submit this form',
+ isLoading: true,
+ loadingText: 'Submitting...',
+ },
+};
diff --git a/packages/design-system-react/src/components/ButtonHero/ButtonHero.test.tsx b/packages/design-system-react/src/components/ButtonHero/ButtonHero.test.tsx
new file mode 100644
index 00000000..8ff62565
--- /dev/null
+++ b/packages/design-system-react/src/components/ButtonHero/ButtonHero.test.tsx
@@ -0,0 +1,101 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import React, { createRef } from 'react';
+
+import { ButtonHero } from './ButtonHero';
+
+describe('ButtonHero Component', () => {
+ it('renders children correctly', () => {
+ render(Button Hero);
+ expect(screen.getByText('Button Hero')).toBeInTheDocument();
+ });
+
+ it('renders as a button element by default', () => {
+ render(Click me);
+ const button = screen.getByRole('button');
+ expect(button).toBeInTheDocument();
+ expect(button).toHaveTextContent('Click me');
+ });
+
+ it('handles click events', async () => {
+ const user = userEvent.setup();
+ const handleClick = jest.fn();
+
+ render(Click me);
+
+ const button = screen.getByRole('button');
+ await user.click(button);
+
+ expect(handleClick).toHaveBeenCalledTimes(1);
+ });
+
+ it('applies custom className correctly', () => {
+ render(Styled Button);
+ const button = screen.getByRole('button');
+ expect(button).toHaveClass('bg-default');
+ });
+
+ it('handles disabled state correctly', () => {
+ const handleClick = jest.fn();
+ render(
+
+ Disabled Button
+ ,
+ );
+
+ const button = screen.getByRole('button');
+ expect(button).toBeDisabled();
+ expect(button).toHaveAttribute('aria-disabled', 'true');
+ });
+
+ it('applies loading styles while preserving hero-specific classes', () => {
+ render(
+
+ Loading Button
+ ,
+ );
+
+ const button = screen.getByRole('button');
+ expect(button).toBeDisabled();
+ expect(button).toHaveAttribute('aria-busy', 'true');
+
+ expect(screen.getAllByText('Loading...')).toHaveLength(2);
+ });
+
+ it('displays loading text when loading', () => {
+ render(
+
+ Submit
+ ,
+ );
+
+ expect(screen.getAllByText('Please wait...')).toHaveLength(2);
+ // Original text should still be present but invisible
+ expect(screen.getByText('Submit')).toHaveClass('invisible');
+ });
+
+ it('does not apply hover/active classes when disabled or loading', () => {
+ const { rerender } = render(Disabled);
+
+ let button = screen.getByRole('button');
+ expect(button).not.toHaveClass(
+ 'hover:bg-primary-default-hover',
+ 'active:bg-primary-default-pressed',
+ );
+
+ rerender(Loading);
+ button = screen.getByRole('button');
+ expect(button).not.toHaveClass(
+ 'hover:bg-primary-default-hover',
+ 'active:bg-primary-default-pressed',
+ );
+ });
+
+ it('forwards ref correctly', () => {
+ const ref = createRef();
+ render(Button with ref);
+
+ expect(ref.current).toBeInstanceOf(HTMLButtonElement);
+ expect(ref.current).toHaveTextContent('Button with ref');
+ });
+});
diff --git a/packages/design-system-react/src/components/ButtonHero/ButtonHero.tsx b/packages/design-system-react/src/components/ButtonHero/ButtonHero.tsx
new file mode 100644
index 00000000..dcc85ef2
--- /dev/null
+++ b/packages/design-system-react/src/components/ButtonHero/ButtonHero.tsx
@@ -0,0 +1,39 @@
+import React, { forwardRef } from 'react';
+
+import { twMerge } from '../../utils/tw-merge';
+import { ButtonBase } from '../ButtonBase';
+
+import type { ButtonHeroProps } from './ButtonHero.types';
+
+export const ButtonHero = forwardRef(
+ ({ className, isDisabled, isLoading, ...props }, ref) => {
+ const isInteractive = !(isDisabled || isLoading);
+
+ const mergedClassName = twMerge(
+ // Base hero styles - locked to light theme primary colors
+ 'bg-primary-default text-primary-inverse',
+ // Loading state
+ isLoading && 'bg-primary-default-pressed',
+ // Hover/Active states - only applied when interactive
+ isInteractive && [
+ 'hover:bg-primary-default-hover',
+ 'active:bg-primary-default-pressed',
+ ],
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-default',
+ className,
+ );
+
+ return (
+
+ );
+ },
+);
+
+ButtonHero.displayName = 'ButtonHero';
diff --git a/packages/design-system-react/src/components/ButtonHero/ButtonHero.types.ts b/packages/design-system-react/src/components/ButtonHero/ButtonHero.types.ts
new file mode 100644
index 00000000..408191b1
--- /dev/null
+++ b/packages/design-system-react/src/components/ButtonHero/ButtonHero.types.ts
@@ -0,0 +1,3 @@
+import type { ButtonBaseProps } from '../ButtonBase';
+
+export type ButtonHeroProps = ButtonBaseProps;
diff --git a/packages/design-system-react/src/components/ButtonHero/README.mdx b/packages/design-system-react/src/components/ButtonHero/README.mdx
new file mode 100644
index 00000000..d3481c9c
--- /dev/null
+++ b/packages/design-system-react/src/components/ButtonHero/README.mdx
@@ -0,0 +1,253 @@
+import { Controls, Canvas } from '@storybook/addon-docs/blocks';
+
+import * as ButtonHeroStories from './ButtonHero.stories';
+
+# ButtonHero
+
+A branded, high-impact button reserved for the most important actions in Trade. Use sparingly for key user actions that require emphasis and visual prominence.
+
+Use for:
+
+- Swapping tokens
+- Claiming winnings (e.g., Polymarket bets)
+- Claiming rewards
+- Other critical, high-value actions
+
+```tsx
+import { ButtonHero } from '@metamask/design-system-react';
+
+Button Hero;
+```
+
+
+
+## Props
+
+### `size`
+
+ButtonHero supports three sizes.
+
+Available sizes:
+
+- `ButtonHeroSize.Sm` (32px)
+- `ButtonHeroSize.Md` (40px)
+- `ButtonHeroSize.Lg` (48px)
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ ButtonHeroSize
+ |
+ No |
+
+ ButtonHeroSize.Lg
+ |
+
+
+
+
+
+
+### `isFullWidth`
+
+ButtonHero can be set to take up the full width of its container.
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ boolean
+ |
+ No |
+
+ false
+ |
+
+
+
+
+
+
+### `startIconName`
+
+ButtonHero can display an icon at the start of the button.
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ IconName
+ |
+ No |
+
+ undefined
+ |
+
+
+
+
+
+
+### `endIconName`
+
+ButtonHero can display an icon at the end of the button.
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ IconName
+ |
+ No |
+
+ undefined
+ |
+
+
+
+
+
+
+### `isDisabled`
+
+Whether the button is disabled.
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ boolean
+ |
+ No |
+
+ false
+ |
+
+
+
+
+
+
+### `isLoading`
+
+Whether the button is in a loading state.
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ boolean
+ |
+ No |
+
+ false
+ |
+
+
+
+
+
+
+### `className`
+
+Use the `className` prop to add custom CSS classes to the component. These classes will be merged with the component's default classes using `twMerge`, allowing you to:
+
+- Add new styles that don't exist in the default component
+- Override the component's default styles when needed
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ string
+ |
+ No |
+
+ undefined
+ |
+
+
+
+
+### `style`
+
+The `style` prop should primarily be used for dynamic inline styles that cannot be achieved with `className` alone. For static styles, prefer using `className` with Tailwind classes.
+
+
+
+
+ | TYPE |
+ REQUIRED |
+ DEFAULT |
+
+
+
+
+
+ object
+ |
+ No |
+
+ undefined
+ |
+
+
+
+
+## Component API
+
+
+
+## References
+
+[MetaMask Design System Guides](https://www.notion.so/MetaMask-Design-System-Guides-Design-f86ecc914d6b4eb6873a122b83c12940)
diff --git a/packages/design-system-react/src/components/ButtonHero/index.ts b/packages/design-system-react/src/components/ButtonHero/index.ts
new file mode 100644
index 00000000..8d9eedd1
--- /dev/null
+++ b/packages/design-system-react/src/components/ButtonHero/index.ts
@@ -0,0 +1,3 @@
+export { ButtonHero } from './ButtonHero';
+export type { ButtonHeroProps } from './ButtonHero.types';
+export { ButtonHeroSize } from '../../types';
diff --git a/packages/design-system-react/src/components/index.ts b/packages/design-system-react/src/components/index.ts
index 36355cbc..bafe01aa 100644
--- a/packages/design-system-react/src/components/index.ts
+++ b/packages/design-system-react/src/components/index.ts
@@ -101,3 +101,6 @@ export type { TextProps } from './Text';
export { TextButton, TextButtonSize } from './TextButton';
export type { TextButtonProps } from './TextButton';
+
+export { ButtonHero, ButtonHeroSize } from './ButtonHero';
+export type { ButtonHeroProps } from './ButtonHero';
diff --git a/packages/design-system-react/src/types/index.ts b/packages/design-system-react/src/types/index.ts
index e466b3ff..f685b062 100644
--- a/packages/design-system-react/src/types/index.ts
+++ b/packages/design-system-react/src/types/index.ts
@@ -345,6 +345,7 @@ export enum ButtonBaseSize {
export { ButtonBaseSize as ButtonPrimarySize };
export { ButtonBaseSize as ButtonSecondarySize };
export { ButtonBaseSize as ButtonTertiarySize };
+export { ButtonBaseSize as ButtonHeroSize };
export { ButtonBaseSize as ButtonSize };
/**
diff --git a/yarn.lock b/yarn.lock
index c5633a93..d011b1c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3211,6 +3211,7 @@ __metadata:
"@svgr/cli": "npm:^8.1.0"
"@testing-library/jest-dom": "npm:^6.6.3"
"@testing-library/react": "npm:^16.0.1"
+ "@testing-library/user-event": "npm:^14.6.1"
"@ts-bridge/cli": "npm:^0.6.3"
"@types/jest": "npm:^27.4.1"
"@types/node": "npm:^16.18.54"