Repository: marigold-ui/marigold
Pull request #2017 (2022-04-21 06:50:22)

refa: button use new styling

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 42328233132ead79502b701acdaf9fa59e87f729

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/marigold-ui/marigold/new/button-restructure?filename=.changeset/chilly-dryers-chew.md&value=---%0A%22%40marigold%2Fdocs%22%3A%20patch%0A%22%40marigold%2Fcomponents%22%3A%20patch%0A%22%40marigold%2Ftheme-b2b%22%3A%20patch%0A%22%40marigold%2Ftheme-core%22%3A%20patch%0A%22%40marigold%2Ftheme-unicorn%22%3A%20patch%0A---%0A%0AButton%20restructure%0A)



Patch:
diff --git a/.changeset/chilly-dryers-chew.md b/.changeset/chilly-dryers-chew.md
new file mode 100644
index 000000000..5a4b4e337
--- /dev/null
+++ b/.changeset/chilly-dryers-chew.md
@@ -0,0 +1,9 @@
+---
+"@marigold/docs": patch
+"@marigold/components": patch
+"@marigold/theme-b2b": patch
+"@marigold/theme-core": patch
+"@marigold/theme-unicorn": patch
+---
+
+Button restructure
diff --git a/docs/content/components/button.mdx b/docs/content/components/button.mdx
index d3107adfe..db1e0bdbe 100644
--- a/docs/content/components/button.mdx
+++ b/docs/content/components/button.mdx
@@ -2,8 +2,8 @@
 title: Button
 ---
 
-import { DoAndDont } from "../../src/components/DoAndDont";
-import { FigmaLink } from "../../src/components/FigmaLink";
+import { DoAndDont } from '../../src/components/DoAndDont';
+import { FigmaLink } from '../../src/components/FigmaLink';
 
 # Button
 
@@ -14,7 +14,7 @@ The style variant and size of the button can be added with the variant prop and
 
 <MarigoldTheme>
   <Inline space="small">
-    <Button>Primary</Button>
+    <Button variant="primary">Primary</Button>
     <Button variant="secondary">Secondary</Button>
     <Button variant="ghost">Ghost</Button>
     <Button variant="text">Text Only</Button>
@@ -22,23 +22,23 @@ The style variant and size of the button can be added with the variant prop and
 </MarigoldTheme>
 
 ```tsx onlyCode
-import { Button } from "@marigold/components";
+import { Button } from '@marigold/components';
 ```
 
 <FigmaLink
   to={
-    "https://www.figma.com/file/DFKyTGHAoDxOsUBPszLLxP/%F0%9F%8F%B5%EF%B8%8FMarigold?node-id=11%3A269"
+    'https://www.figma.com/file/DFKyTGHAoDxOsUBPszLLxP/%F0%9F%8F%B5%EF%B8%8FMarigold?node-id=11%3A269'
   }
 />
 
 ## Props
 
-| Property              | Type                                    | Default   |
-| :-------------------- | :-------------------------------------- | :-------- |
-| `variant` (optional)  | `primary`, `secondary`, `ghost`, `text` | `primary` |
-| `size` (optional)     | `small`, `large`                        | `large`   |
-| `disabled` (optional) | boolean                                 |           |
-| `space` (optional)    | `ResponsiveStyleValue<string>`          | `none`    |
+| Property              | Type                                    | Default |
+| :-------------------- | :-------------------------------------- | :------ |
+| `variant` (optional)  | `primary`, `secondary`, `ghost`, `text` | `none`  |
+| `size` (optional)     | `small`, `large`                        | `large` |
+| `disabled` (optional) | boolean                                 |         |
+| `space` (optional)    | `ResponsiveStyleValue<string>`          | `none`  |
 
 ## Types
 
@@ -51,13 +51,15 @@ The primary button function is to achieve the most common or most important goal
 #### Default
 
 ```tsx
-<Button>Primary</Button>
+<Button variant="primary">Primary</Button>
 ```
 
 #### Disabled
 
 ```tsx
-<Button disabled>Primary</Button>
+<Button variant="primary" disabled>
+  Primary
+</Button>
 ```
 
 ### Secondary Button
@@ -125,13 +127,13 @@ They can be used in a similar way of a secondary button, but they are meant to b
 Icons can be added in Order to make clearer what a button does. Buttons should not be made only with icons, because every user may understand them on a different way.
 
 ```tsx
-<Button space="xxsmall">
+<Button variant="primary" space="xxsmall">
   <Ticket /> Icon Primary
 </Button>
 ```
 
 ```tsx
-<Button space="xxsmall" disabled>
+<Button variant="primary" space="xxsmall" disabled>
   <Ticket /> Icon Primary
 </Button>
 ```
@@ -141,11 +143,13 @@ Icons can be added in Order to make clearer what a button does. Buttons should n
 If there are many actions in one page, is it possible to use small buttons instead of big ones. As well if the page is required to be open on mobile devices.
 
 ```tsx
-<Button size="small">Primary</Button>
+<Button variant="primary" size="small">
+  Primary
+</Button>
 ```
 
 ```tsx
-<Button size="small" disabled>
+<Button variant="primary" size="small" disabled>
   Primary
 </Button>
 ```
@@ -191,7 +195,7 @@ If there are many actions in one page, is it possible to use small buttons inste
 Icons can be added in Order to make clearer what a button does. Buttons should not be made only with icons, because every user may understand them on a different way.Icons can be added in Order to make clearer what a button does. Buttons should not be made only with icons, because every user may understand them on a different way.
 
 ```tsx
-<Button size="small" space="xxsmall">
+<Button variant="primary" size="small" space="xxsmall">
   <Ticket /> Primary
 </Button>
 ```
@@ -220,15 +224,15 @@ Icons can be added in Order to make clearer what a button does. Buttons should n
   <Box
     display="grid"
     css={{
-      gridGap: "small",
-      gridTemplateColumns: "repeat(auto-fit, minmax(min(500px, 100%), 1fr))",
+      gridGap: 'small',
+      gridTemplateColumns: 'repeat(auto-fit, minmax(min(500px, 100%), 1fr))',
     }}
   >
     <DoAndDont
       preview={
         <Inline space="small">
           <Button variant="ghost">Ghost</Button>
-          <Button>Primary</Button>
+          <Button variant="primary">Primary</Button>
         </Inline>
       }
     >
@@ -238,7 +242,7 @@ Icons can be added in Order to make clearer what a button does. Buttons should n
       preview={
         <Inline space="small">
           <Button variant="text">Text</Button>
-          <Button>Primary</Button>
+          <Button variant="primary">Primary</Button>
         </Inline>
       }
     >
@@ -258,8 +262,8 @@ Icons can be added in Order to make clearer what a button does. Buttons should n
       dont
       preview={
         <Inline space="small">
-          <Button>Primary</Button>
-          <Button>Primary</Button>
+          <Button variant="primary">Primary</Button>
+          <Button variant="primary">Primary</Button>
         </Inline>
       }
     >
diff --git a/docs/src/theme/component.ts b/docs/src/theme/component.ts
index 04a2a55b6..294c29a27 100644
--- a/docs/src/theme/component.ts
+++ b/docs/src/theme/component.ts
@@ -92,36 +92,6 @@ const select = {
  * Button component
  */
 const button = {
-  primary: {
-    appearance: 'none',
-    display: 'inline-flex',
-    textAlign: 'center',
-    lineHeight: 'medium',
-    fontSize: 'body',
-    fontFamily: 'body',
-    textDecoration: 'none',
-    color: 'gray00',
-    bg: 'primary',
-    border: 'none',
-    borderRadius: 'medium',
-    cursor: 'pointer',
-  },
-  secondary: {
-    color: 'gray00',
-    bg: 'secondary',
-  },
-  large: {
-    px: 'small',
-    py: 'xsmall',
-  },
-  table: {
-    px: 'xsmall',
-    py: 'small',
-  },
-  small: {
-    px: 'xsmall',
-    py: 'xxsmall',
-  },
   select: {
     ...selectButton,
     border: 'solid',
@@ -172,55 +142,7 @@ const button = {
 
 export const component = {
   button: {
-    __default: {
-      p: 0,
-      border: 'none',
-    },
-    large: {
-      ...button.large,
-    },
-    table: {
-      ...button.table,
-    },
-    small: {
-      ...button.small,
-    },
-    primary: {
-      ...button.primary,
-    },
-    secondary: {
-      ...button.secondary,
-    },
-    action: {
-      ...button.primary,
-      pr: 'xsmall',
-      color: 'gray60',
-      bg: 'transparent',
-      fontWeight: 'body',
-      fontSize: 'xxsmall',
-      ':focus': {
-        outline: 'none',
-      },
-    },
-    icon: {
-      ...button.primary,
-      width: '100%',
-      alignItems: 'center',
-      justifyContent: 'space-between',
-      display: 'inline-flex',
-      color: 'gray60',
-      bg: 'transparent',
-      fontWeight: 'body',
-      fontSize: 'xsmall',
-      ':focus': {
-        outline: 'none',
-      },
-      ':hover': {
-        bg: 'gray00',
-      },
-    },
     select: {
-      ...button.primary,
       ...button.select,
     },
   },
diff --git a/docs/src/theme/components/Button.style.ts b/docs/src/theme/components/Button.style.ts
new file mode 100644
index 000000000..15f465954
--- /dev/null
+++ b/docs/src/theme/components/Button.style.ts
@@ -0,0 +1,60 @@
+import type { Theme } from '@marigold/components';
+
+export const Button: Theme['components']['Button'] = {
+  base: {
+    display: 'inline-flex',
+    color: 'gray00',
+    cursor: 'pointer',
+    textAlign: 'center',
+    lineHeight: 'medium',
+    fontFamily: 'body',
+    textDecoration: 'none',
+    border: 'none',
+    borderRadius: 'medium',
+  },
+  variant: {
+    primary: {
+      bg: 'primary',
+    },
+    secondary: {
+      bg: 'secondary',
+    },
+    large: {
+      px: 'small',
+      py: 'xsmall',
+    },
+    table: {
+      px: 'xsmall',
+      py: 'small',
+    },
+    small: {
+      px: 'xsmall',
+      py: 'xxsmall',
+    },
+    action: {
+      pr: 'xsmall',
+      color: 'gray60',
+      bg: 'transparent',
+      fontWeight: 'body',
+      fontSize: 'xxsmall',
+      '&:focus': {
+        outline: 'none',
+      },
+    },
+    icon: {
+      width: '100%',
+      alignItems: 'center',
+      justifyContent: 'space-between',
+      color: 'gray60',
+      bg: 'transparent',
+      fontWeight: 'body',
+      fontSize: 'xsmall',
+      '&:focus': {
+        outline: 'none',
+      },
+      '&:hover': {
+        bg: 'gray00',
+      },
+    },
+  },
+};
diff --git a/docs/src/theme/components/index.ts b/docs/src/theme/components/index.ts
index 22882562f..a86ee7177 100644
--- a/docs/src/theme/components/index.ts
+++ b/docs/src/theme/components/index.ts
@@ -1,3 +1,4 @@
+export * from './Button.style';
 export * from './Card.style';
 export * from './Headline.style';
 export * from './Link.style';
diff --git a/packages/components/src/Button/Button.stories.tsx b/packages/components/src/Button/Button.stories.tsx
index 759d82e9d..a31764245 100644
--- a/packages/components/src/Button/Button.stories.tsx
+++ b/packages/components/src/Button/Button.stories.tsx
@@ -5,53 +5,50 @@ import { Button } from './Button';
 
 export default {
   title: 'Components/Button',
-  parameters: {
-    actions: {
-      handles: ['click'],
-    },
-  },
   argTypes: {
-    variant: {
+    children: {
       control: {
-        type: 'select',
+        type: 'text',
       },
-      options: ['primary', 'secondary', 'ghost', 'text', 'menu', 'select'],
-      description: 'What the button looks like',
-      table: {
-        defaultValue: {
-          summary: 'primary',
-        },
+      description: 'Contents of the button',
+      defaultValue: 'Click me!',
+    },
+    variant: {
+      control: {
+        type: 'text',
       },
+      description: 'Variant of the button',
+      defaultValue: 'primary',
     },
     size: {
       control: {
-        type: 'select',
-      },
-      options: ['large', 'small'],
-      description: 'How big the button is rendered',
-      table: {
-        defaultValue: {
-          summary: 'large',
-        },
+        type: 'text',
       },
+      description: 'Size of the button',
     },
     disabled: {
       control: {
         type: 'boolean',
       },
-      options: [true, false],
       description: 'Disable the button',
-      table: {
-        defaultValue: {
-          summary: 'false',
-        },
-      },
+      defaultValue: false,
     },
   },
 } as Meta;
 
 export const Basic: ComponentStory<typeof Button> = args => (
+  <Button {...args} />
+);
+
+export const WithIcon: ComponentStory<typeof Button> = ({
+  children,
+  ...args
+}) => (
   <Button {...args}>
-    <Facebook /> Like me
+    <Facebook /> {children}
   </Button>
 );
+
+export const OnPress: ComponentStory<typeof Button> = args => (
+  <Button {...args} onPress={(e: any) => console.log(e)} />
+);
diff --git a/packages/components/src/Button/Button.tsx b/packages/components/src/Button/Button.tsx
index 91063dca4..47b798499 100644
--- a/packages/components/src/Button/Button.tsx
+++ b/packages/components/src/Button/Button.tsx
@@ -1,65 +1,65 @@
-import React, { forwardRef, RefObject } from 'react';
+import React, { ReactNode, useRef } from 'react';
 import { useButton } from '@react-aria/button';
-import {
-  PolymorphicComponentWithRef,
-  PolymorphicPropsWithRef,
-} from '@marigold/types';
+import { PressEvents } from '@react-types/shared';
 
-import { Box, BoxOwnProps } from '../Box';
+import { Box, ThemeExtension, useComponentStyles } from '@marigold/system';
+import { PolymorphicComponent, PolymorphicProps } from '@marigold/types';
 
 // Theme Extension
 // ---------------
-export interface ButtonThemeExtension<Value> {
-  button?: {
-    [key: string]: Value;
-  };
-}
+export interface ButtonThemeExtension extends ThemeExtension<'Button'> {}
 
 // Props
 // ---------------
+export interface ButtonOwnProps extends PressEvents {
+  children?: ReactNode;
+  variant?: string;
+  size?: string;
+}
+
 export interface ButtonProps
-  extends PolymorphicPropsWithRef<BoxOwnProps, 'button'> {}
+  extends Omit<PolymorphicProps<ButtonOwnProps, 'button'>, 'onClick'> {}
 
 // Component
 // ---------------
-export const Button: PolymorphicComponentWithRef<BoxOwnProps, 'button'> =
-  forwardRef(
-    (
-      {
-        as = 'button',
-        variant = 'primary',
-        size = 'large',
-        space = 'none',
-        disabled,
-        children,
-        className,
-        ...props
-      },
-      ref
-    ) => {
-      const { buttonProps } = useButton(
-        {
-          ...props,
-          elementType: typeof as === 'string' ? as : 'span',
-          isDisabled: disabled,
-        },
-        ref as RefObject<HTMLSpanElement>
-      );
+export const Button = (({
+  as = 'button',
+  children,
+  variant,
+  size,
+  disabled,
+  ...props
+}: ButtonProps) => {
+  const styles = useComponentStyles('Button', { variant, size });
+
+  const ref = useRef<any>();
+  const { buttonProps } = useButton(
+    {
+      /**
+       * `react-aria` only expected `Element` but our
+       * props are from HTMLButtonElement.
+       */
+      ...(props as any),
+      elementType: typeof as === 'string' ? as : 'span',
+      isDisabled: disabled,
+    },
+    ref
+  );
 
-      return (
-        <Box
-          {...buttonProps}
-          {...props}
-          as={as}
-          display="inline-flex"
-          alignItems="center"
-          variant={[`button.${variant}`, `button.${size}`]}
-          className={className}
-          ref={ref}
-          css={{ columnGap: space }}
-        >
-          {children}
-        </Box>
-      );
-    }
+  return (
+    <Box
+      {...buttonProps}
+      as={as}
+      ref={ref}
+      __baseCSS={{
+        display: 'inline-flex',
+        alignItems: 'center',
+        gap: '0.5ch',
+        cursor: disabled ? 'not-allowed' : 'pointer',
+      }}
+      css={styles}
+    >
+      {children}
+    </Box>
   );
+}) as PolymorphicComponent<ButtonOwnProps, 'button'>;
diff --git a/packages/components/src/theme.ts b/packages/components/src/theme.ts
index 2411153a9..2007eb55c 100644
--- a/packages/components/src/theme.ts
+++ b/packages/components/src/theme.ts
@@ -7,6 +7,7 @@ import type { RootThemeExtension } from './Provider';
 // Components
 // ---------------
 import type { BadgeThemeExtension } from './Badge';
+import type { ButtonThemeExtension } from './Button';
 import type { CardThemeExtension } from './Card';
 import type { CheckboxThemeExtension } from './Checkbox';
 import type { ContentThemeExtension } from './Content';
@@ -23,7 +24,6 @@ import type { TextThemeExtension } from './Text';
 import type { TextAreaThemeExtension } from './TextArea';
 
 // Old Styling Solution
-import { type ButtonThemeExtension } from './Button';
 import { type LabelThemeExtension as LegacyLabelThemeExtension } from './Label';
 import { type MenuThemeExtension } from './Menu';
 import { type MenuItemThemeExtension } from './MenuItem';
@@ -37,6 +37,7 @@ import { type ValidationMessageThemeExtension } from './ValidationMessage';
 
 interface ComponentStyles
   extends BadgeThemeExtension,
+    ButtonThemeExtension,
     CardThemeExtension,
     CheckboxThemeExtension,
     ContentThemeExtension,
@@ -56,7 +57,6 @@ interface ComponentStyles
 export interface Theme
   extends Scales,
     RootThemeExtension<CSSObject>,
-    ButtonThemeExtension<CSSObject>,
     LegacyLabelThemeExtension<CSSObject>,
     MenuThemeExtension<CSSObject>,
     MenuItemThemeExtension<CSSObject>,
diff --git a/themes/theme-b2b/src/component.ts b/themes/theme-b2b/src/component.ts
index cd44ff0f5..ec28d03ac 100644
--- a/themes/theme-b2b/src/component.ts
+++ b/themes/theme-b2b/src/component.ts
@@ -107,65 +107,6 @@ const button = {
     borderRadius: 'small',
     display: 'inline-flex',
   },
-  primary: {
-    color: 'background',
-    bg: 'primary',
-    ':hover': {
-      color: 'background',
-      bg: 'orange40',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'gray40',
-      bg: 'gray20',
-      cursor: 'not-allowed',
-    },
-  },
-  secondary: {
-    color: 'background',
-    bg: 'secondary',
-    ':hover': {
-      color: 'background',
-      bg: 'gray60',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'gray40',
-      bg: 'gray20',
-      cursor: 'not-allowed',
-    },
-  },
-  ghost: {
-    color: 'secondary',
-    border: '1px solid',
-    outlineColor: 'gray70',
-    ':hover': {
-      color: 'secondary',
-      bg: 'gray30',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: 'gray00',
-      outlineColor: 'disabled',
-      cursor: 'not-allowed',
-    },
-  },
-  text: {
-    color: 'secondary',
-    ':hover': {
-      color: 'secondary',
-      outlineColor: 'gray70',
-      bg: 'gray30',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: 'gray00',
-      cursor: 'not-allowed',
-      outline: 'none',
-    },
-  },
   menu: {
     color: 'secondary',
     bg: 'background',
@@ -334,21 +275,8 @@ export const component = {
       lineHeight: 'large',
       paddingX: 'medium',
     },
-    primary: {
-      ...button.root,
-      ...button.primary,
-    },
-    secondary: {
-      ...button.root,
-      ...button.secondary,
-    },
-    ghost: {
-      ...button.root,
-      ...button.ghost,
-    },
     text: {
       ...button.root,
-      ...button.text,
     },
     menu: {
       ...button.root,
diff --git a/themes/theme-b2b/src/components/Button.style.ts b/themes/theme-b2b/src/components/Button.style.ts
new file mode 100644
index 000000000..dcb9ae9d7
--- /dev/null
+++ b/themes/theme-b2b/src/components/Button.style.ts
@@ -0,0 +1,50 @@
+import type { Theme } from '@marigold/components';
+
+export const Button: Theme['components']['Button'] = {
+  base: {
+    border: 'none',
+    borderRadius: 'small',
+    // FIXME: when we use open-props
+    lineHeight: '48px',
+    px: 'large',
+    '&:disabled': {
+      color: 'gray40',
+      bg: 'gray20',
+    },
+  },
+  variant: {
+    primary: {
+      color: 'background',
+      bg: 'primary',
+      '&:hover': {
+        color: 'background',
+        bg: 'orange40',
+      },
+    },
+    secondary: {
+      color: 'background',
+      bg: 'secondary',
+      ':hover': {
+        color: 'background',
+        bg: 'gray60',
+      },
+    },
+    ghost: {
+      color: 'secondary',
+      border: '1px solid',
+      outlineColor: 'gray70',
+      '&:hover': {
+        color: 'secondary',
+        bg: 'gray30',
+      },
+    },
+    text: {
+      color: 'secondary',
+      '&:hover': {
+        color: 'secondary',
+        outlineColor: 'gray70',
+        bg: 'gray30',
+      },
+    },
+  },
+};
diff --git a/themes/theme-b2b/src/components/index.ts b/themes/theme-b2b/src/components/index.ts
index 6810c4fab..6f0804e7a 100644
--- a/themes/theme-b2b/src/components/index.ts
+++ b/themes/theme-b2b/src/components/index.ts
@@ -1,4 +1,5 @@
 export * from './Badge.style';
+export * from './Button.style';
 export * from './Card.style';
 export * from './Checkbox.style';
 export * from './Divider.style';
diff --git a/themes/theme-core/src/component.ts b/themes/theme-core/src/component.ts
index ad6d5f0e4..ba7605e68 100644
--- a/themes/theme-core/src/component.ts
+++ b/themes/theme-core/src/component.ts
@@ -110,68 +110,6 @@ const button = {
     borderRadius: 'small',
     display: 'inline-flex',
   },
-  primary: {
-    color: 'background',
-    bg: 'primary',
-    ':hover': {
-      color: 'background',
-      bg: colors.orange40,
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: colors.gray00,
-      outlineColor: 'disabled',
-      cursor: 'not-allowed',
-    },
-  },
-  secondary: {
-    color: 'gray70',
-    bg: 'gray30',
-    border: '1px solid',
-    outlineColor: 'gray70',
-    ':hover': {
-      bg: colors.gray00,
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: colors.gray00,
-      outlineColor: 'disabled',
-      cursor: 'not-allowed',
-    },
-  },
-  ghost: {
-    color: 'secondary',
-    border: '1px solid',
-    outlineColor: colors.gray70,
-    ':hover': {
-      color: 'secondary',
-      bg: colors.gray10,
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: colors.gray00,
-      outlineColor: 'disabled',
-      cursor: 'not-allowed',
-    },
-  },
-  text: {
-    color: 'secondary',
-    ':hover': {
-      color: 'secondary',
-      outlineColor: colors.gray70,
-      bg: colors.gray30,
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: colors.gray00,
-      outlineColor: 'disabled',
-      cursor: 'not-allowed',
-    },
-  },
   menu: {
     color: 'secondary',
     bg: 'background',
@@ -342,28 +280,10 @@ export const component = {
       paddingX: 'small',
       fontSize: 'xxsmall',
     },
-    primary: {
-      ...button.root,
-      ...button.primary,
-    },
-    secondary: {
-      ...button.root,
-      ...button.secondary,
-    },
-    ghost: {
-      ...button.root,
-      ...button.ghost,
-    },
-    text: {
-      ...button.root,
-      ...button.text,
-    },
     menu: {
-      ...button.root,
       ...button.menu,
     },
     select: {
-      ...button.root,
       ...button.select,
     },
   },
diff --git a/themes/theme-core/src/components/Button.style.ts b/themes/theme-core/src/components/Button.style.ts
new file mode 100644
index 000000000..faebcb7c0
--- /dev/null
+++ b/themes/theme-core/src/components/Button.style.ts
@@ -0,0 +1,56 @@
+import type { Theme } from '@marigold/components';
+
+export const Button: Theme['components']['Button'] = {
+  base: {
+    p: 0,
+    border: 'none',
+    lineHeight: '30px',
+    px: 'large',
+    '&:disabled': {
+      color: 'disabled',
+      bg: 'gray00',
+      outlineColor: 'disabled',
+      cursor: 'not-allowed',
+    },
+  },
+  variant: {
+    primary: {
+      color: 'background',
+      bg: 'primary',
+      '&:hover': {
+        color: 'background',
+        bg: 'orange40',
+        cursor: 'pointer',
+      },
+    },
+    secondary: {
+      color: 'gray70',
+      bg: 'gray30',
+      border: '1px solid',
+      outlineColor: 'gray70',
+      '&:hover': {
+        bg: 'gray00',
+        cursor: 'pointer',
+      },
+    },
+    ghost: {
+      color: 'secondary',
+      border: '1px solid',
+      outlineColor: 'gray70',
+      '&:hover': {
+        color: 'secondary',
+        bg: 'gray10',
+        cursor: 'pointer',
+      },
+    },
+    text: {
+      color: 'secondary',
+      ':hover': {
+        color: 'secondary',
+        outlineColor: 'gray70',
+        bg: 'gray30',
+        cursor: 'pointer',
+      },
+    },
+  },
+};
diff --git a/themes/theme-core/src/components/index.ts b/themes/theme-core/src/components/index.ts
index d5147188e..47b020de7 100644
--- a/themes/theme-core/src/components/index.ts
+++ b/themes/theme-core/src/components/index.ts
@@ -1,4 +1,5 @@
 export * from './Badge.style';
+export * from './Button.style';
 export * from './Checkbox.style';
 export * from './Divider.style';
 export * from './Headline.style';
diff --git a/themes/theme-unicorn/src/component.ts b/themes/theme-unicorn/src/component.ts
index 30588fc52..a8b283ad5 100644
--- a/themes/theme-unicorn/src/component.ts
+++ b/themes/theme-unicorn/src/component.ts
@@ -106,62 +106,7 @@ const button = {
     borderRadius: '8px',
     display: 'inline-flex',
   },
-  primary: {
-    color: 'background',
-    bg: 'primary',
-    ':hover': {
-      bg: 'secondary',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'gray40',
-      bg: 'gray20',
-      cursor: 'not-allowed',
-    },
-  },
-  secondary: {
-    color: 'background',
-    bg: 'secondary',
-    ':hover': {
-      bg: 'primary',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'gray40',
-      bg: 'gray20',
-      cursor: 'not-allowed',
-    },
-  },
-  ghost: {
-    color: 'secondary',
-    border: '1px solid',
-    outlineColor: 'gray70',
-    ':hover': {
-      color: 'secondary',
-      bg: 'gray30',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: 'gray00',
-      outlineColor: 'disabled',
-      cursor: 'not-allowed',
-    },
-  },
-  text: {
-    color: 'secondary',
-    ':hover': {
-      outlineColor: 'gray70',
-      bg: 'gray30',
-      cursor: 'pointer',
-    },
-    ':disabled': {
-      color: 'disabled',
-      bg: 'gray00',
-      cursor: 'not-allowed',
-      outline: 'none',
-    },
-  },
+
   menu: {
     color: 'secondary',
     bg: 'background',
@@ -332,19 +277,15 @@ export const component = {
     },
     primary: {
       ...button.root,
-      ...button.primary,
     },
     secondary: {
       ...button.root,
-      ...button.secondary,
     },
     ghost: {
       ...button.root,
-      ...button.ghost,
     },
     text: {
       ...button.root,
-      ...button.text,
     },
     menu: {
       ...button.root,
diff --git a/themes/theme-unicorn/src/components/Button.style.ts b/themes/theme-unicorn/src/components/Button.style.ts
new file mode 100644
index 000000000..83fe6184c
--- /dev/null
+++ b/themes/theme-unicorn/src/components/Button.style.ts
@@ -0,0 +1,51 @@
+import type { Theme } from '@marigold/components';
+
+export const Button: Theme['components']['Button'] = {
+  base: {
+    border: 'none',
+    borderRadius: '8px',
+    lineHeight: '48px',
+    px: 'large',
+    '&:disabled': {
+      color: 'gray40',
+      bg: 'gray20',
+      cursor: 'not-allowed',
+    },
+  },
+  variant: {
+    primary: {
+      color: 'background',
+      bg: 'primary',
+      '&:hover': {
+        bg: 'secondary',
+        cursor: 'pointer',
+      },
+    },
+    secondary: {
+      color: 'background',
+      bg: 'secondary',
+      '&:hover': {
+        bg: 'primary',
+        cursor: 'pointer',
+      },
+    },
+    ghost: {
+      color: 'secondary',
+      border: '1px solid',
+      outlineColor: 'gray70',
+      '&:hover': {
+        color: 'secondary',
+        bg: 'gray30',
+        cursor: 'pointer',
+      },
+    },
+    text: {
+      color: 'secondary',
+      '&:hover': {
+        outlineColor: 'gray70',
+        bg: 'gray30',
+        cursor: 'pointer',
+      },
+    },
+  },
+};
diff --git a/themes/theme-unicorn/src/components/index.ts b/themes/theme-unicorn/src/components/index.ts
index 6810c4fab..6f0804e7a 100644
--- a/themes/theme-unicorn/src/components/index.ts
+++ b/themes/theme-unicorn/src/components/index.ts
@@ -1,4 +1,5 @@
 export * from './Badge.style';
+export * from './Button.style';
 export * from './Card.style';
 export * from './Checkbox.style';
 export * from './Divider.style';


Test patch:
diff --git a/packages/components/src/Button/Button.test.tsx b/packages/components/src/Button/Button.test.tsx
index 4c3c7e853..b2f9a400f 100644
--- a/packages/components/src/Button/Button.test.tsx
+++ b/packages/components/src/Button/Button.test.tsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import { render, screen } from '@testing-library/react';
+import { fireEvent, render, screen } from '@testing-library/react';
 import { ThemeProvider } from '@marigold/system';
 import { Button } from './Button';
 import { Facebook } from '@marigold/icons';
@@ -14,23 +14,32 @@ const theme = {
     small: 2,
     large: 16,
   },
-  button: {
-    large: {
-      p: 'large',
-    },
-    small: {
-      p: 'large',
-    },
-    primary: {
-      fontFamily: 'fancy',
-    },
-    secondary: {
-      fontFamily: 'body',
+  components: {
+    Button: {
+      base: {
+        fontFamily: 'fancy',
+      },
+      size: {
+        large: {
+          p: '16px',
+        },
+        small: {
+          p: 'large',
+        },
+      },
+      variant: {
+        primary: {
+          fontFamily: 'fancy',
+        },
+        secondary: {
+          fontFamily: 'body',
+        },
+      },
     },
   },
 };
 
-test('supports default variant', () => {
+test('supports base style', () => {
   render(
     <ThemeProvider theme={theme}>
       <Button>button</Button>
@@ -44,7 +53,7 @@ test('supports default variant', () => {
 test('supports default size', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Button>button</Button>
+      <Button size="large">button</Button>
     </ThemeProvider>
   );
   const button = screen.getByText(/button/);
@@ -52,7 +61,7 @@ test('supports default size', () => {
   expect(button).toHaveStyle(`padding: 16px`);
 });
 
-test('accepts other variant than default', () => {
+test('accepts other variants', () => {
   render(
     <ThemeProvider theme={theme}>
       <Button variant="secondary">button</Button>
@@ -122,27 +131,57 @@ test('add icon in button works as expected', () => {
 test('add space to button works as expected', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Button title="iconbutton" space="small">
+      <Button data-testid="iconbutton" space="small">
         <Facebook fill="red" size={30} title="facebook" />
         iconbutton
       </Button>
     </ThemeProvider>
   );
-  const button = screen.getByTitle(/iconbutton/);
+  const button = screen.getByTestId('iconbutton');
 
   const style = window.getComputedStyle(button);
-  expect(style.columnGap).toBe(`2px`);
+  expect(style.gap).toBe(`0.5ch`);
+});
+
+test('can be used as a "link button"', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Button as="a" href="https://karriere.reservix.net" data-testid="button">
+        I am a Link!
+      </Button>
+    </ThemeProvider>
+  );
+  const button = screen.getByTestId('button');
+  expect(button).toBeTruthy();
 });
 
-test('accepts custom styles prop className', () => {
+test('can be used as a "link button" and has button styling', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Button className="custom-class-name" title="button">
-        click
+      <Button
+        as="a"
+        href="https://karriere.reservix.net"
+        variant="primary"
+        data-testid="button"
+      >
+        I am a Link!
       </Button>
     </ThemeProvider>
   );
-  const button = screen.getByTitle(/button/);
+  const button = screen.getByTestId('button');
+  expect(button).toBeTruthy();
+});
+
+test('supports onPress', () => {
+  const onPress = jest.fn();
+  render(
+    <Button onPress={onPress} href={onPress} data-testid="button">
+      Some Button
+    </Button>
+  );
+
+  const button = screen.getByTestId('button');
+  fireEvent.click(button);
 
-  expect(button.className).toMatch('custom-class-name');
+  expect(onPress).toHaveBeenCalled();
 });
diff --git a/packages/components/src/Tooltip/Tooltip.test.tsx b/packages/components/src/Tooltip/Tooltip.test.tsx
index 9449c6d5d..fa48c671a 100644
--- a/packages/components/src/Tooltip/Tooltip.test.tsx
+++ b/packages/components/src/Tooltip/Tooltip.test.tsx
@@ -24,12 +24,12 @@ test('supports default variant', async () => {
   render(
     <ThemeProvider theme={theme}>
       <TooltipTrigger>
-        <Button title="button">Info</Button>
+        <Button data-testid="button">Info</Button>
         <Tooltip data-testid="tooltip">Tooltip</Tooltip>
       </TooltipTrigger>
     </ThemeProvider>
   );
-  const button = screen.getByTitle('button');
+  const button = screen.getByTestId('button');
   button.focus();
   expect(button).toHaveFocus();
 
@@ -41,14 +41,14 @@ test('supports other variant than default', async () => {
   render(
     <ThemeProvider theme={theme}>
       <TooltipTrigger>
-        <Button title="button">Info</Button>
+        <Button data-testid="button">Info</Button>
         <Tooltip variant="styled" data-testid="tooltip">
           Tooltip
         </Tooltip>
       </TooltipTrigger>
     </ThemeProvider>
   );
-  const button = screen.getByTitle('button');
+  const button = screen.getByTestId('button');
   button.focus();
   expect(button).toHaveFocus();
 
@@ -60,12 +60,12 @@ test('shows Tooltip', async () => {
   render(
     <ThemeProvider theme={theme}>
       <TooltipTrigger>
-        <Button title="button">Info</Button>
+        <Button data-testid="button">Info</Button>
         <Tooltip>Tooltip</Tooltip>
       </TooltipTrigger>
     </ThemeProvider>
   );
-  const button = screen.getByTitle('button');
+  const button = screen.getByTestId('button');
   button.focus();
   expect(button).toHaveFocus();