diff --git a/src/components/button/button.scss b/src/components/button/button.scss new file mode 100644 index 00000000..7262db34 --- /dev/null +++ b/src/components/button/button.scss @@ -0,0 +1,56 @@ +@use '../../settings/style'; + +.mykn-button { + --mykn-button-color-background: var(--theme-color-primary-800); + --mykn-button-color-shadow: var(--theme-shade-1000); + --mykn-button-color-text: var(--theme-shade-0); + --mykn-button-offset: 0px; + + appearance: none; + margin: 0; + padding: 12px; + background-color: var(--mykn-button-color-background); + border: 1px solid var(--mykn-button-color-border); + border-radius: 4px; + box-shadow: 0 calc(var(--mykn-button-offset) * -1) var(--mykn-button-color-shadow); + box-sizing: border-box; + color: var(--mykn-button-color-text); + cursor: pointer; + display: inline-block; + font-family: Inter, sans-serif; + font-size: var(--typography-font-size-body-s); + line-height: var(--typography-line-height-body-s); + text-decoration: none; + transition: all var(--animation-duration) var(--animation-timing-function); + transform: translateY(var(--mykn-button-offset)); + + + &--variant-primary { + &:focus, + &:hover { + --mykn-button-color-background: var(--theme-color-primary-700); + --mykn-button-offset: -2px; + } + + &:active { + --mykn-button-color-background: var(--theme-color-primary-800); + --mykn-button-offset: 0px; + } + } + + &--variant-transparent { + --mykn-button-color-background: transparent; + --mykn-button-color-shadow: currentColor; + --mykn-button-color-text: var(--theme-shade-700); + + &:focus, + &:hover { + --mykn-button-color-background: var(--theme-color-primary-200); + --mykn-button-offset: -2px; + } + + &:active { + --mykn-button-offset: 0px; + } + } +} diff --git a/src/components/button/button.stories.tsx b/src/components/button/button.stories.tsx new file mode 100644 index 00000000..2baba761 --- /dev/null +++ b/src/components/button/button.stories.tsx @@ -0,0 +1,66 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { userEvent } from "@storybook/test"; +import React from "react"; + +import { Button, ButtonLink } from "./button"; + +const meta = { + title: "Controls/Button", + component: Button, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const ButtonComponent: Story = { + args: { + children: "The quick brown fox jumps over the lazy dog.", + }, +}; + +export const TransparentButton: Story = { + args: { + children: "The quick brown fox jumps over the lazy dog.", + variant: "transparent", + }, +}; + +export const ButtonAnimatesOnHoverAndClick: Story = { + args: { + children: "The quick brown fox jumps over the lazy dog.", + }, + play: async () => { + await userEvent.tab({ delay: 10 }); + }, +}; + +export const ButtonLinkComponent: StoryObj = { + args: { + children: "The quick brown fox jumps over the lazy dog.", + href: "https://www.example.com", + target: "_blank", + }, + render: (args) => , +}; + +export const TransparentButtonLink: StoryObj = { + args: { + children: "The quick brown fox jumps over the lazy dog.", + href: "https://www.example.com", + target: "_blank", + variant: "transparent", + }, + render: (args) => , +}; + +export const ButtonLinkAnimatesOnHoverAndClick: StoryObj = { + args: { + children: "The quick brown fox jumps over the lazy dog.", + href: "https://www.example.com", + target: "_blank", + }, + play: async () => { + await userEvent.tab({ delay: 10 }); + }, + render: (args) => , +}; diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx new file mode 100644 index 00000000..ca329cbb --- /dev/null +++ b/src/components/button/button.tsx @@ -0,0 +1,73 @@ +import clsx from "clsx"; +import React from "react"; + +import "./button.scss"; + +export type ButtonProps = React.PropsWithChildren<{ + variant?: "primary" | "transparent"; +}>; + +/** + * Higher-order component (HOC) applying button logic to WrappedComponent + * @param WrappedComponent + * @private + */ +const withButtonBehavior =

( + WrappedComponent: React.ComponentType

, +) => { + // Define the enhanced component + const EnhancedButton: React.FC

= ({ + children, + variant = "primary", + ...props + }) => { + return ( + + {children} + + ); + }; + + return EnhancedButton; +}; + +/** + * Base template for Button component + * @private + */ +const BaseButton: React.FC< + ButtonProps & React.ButtonHTMLAttributes +> = (props) => { + return ; +}; + +/** + * Base template for ButtonLink component + * @private + */ +const BaseButtonLink: React.FC< + ButtonProps & React.AnchorHTMLAttributes +> = (props) => { + return {props.children}; +}; + +/** + * Button component + * @param children + * @param type + * @param props + * @constructor + */ +export const Button = withButtonBehavior(BaseButton); + +/** + * Anchor () version of button component + * @param children + * @param type + * @param props + * @constructor + */ +export const ButtonLink = withButtonBehavior(BaseButtonLink); diff --git a/src/components/button/index.ts b/src/components/button/index.ts new file mode 100644 index 00000000..98d55acd --- /dev/null +++ b/src/components/button/index.ts @@ -0,0 +1 @@ +export * from "./button"; diff --git a/src/components/index.ts b/src/components/index.ts index 55dc43c7..f27cf25a 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,4 +1,5 @@ // Auto-generated file. Do not modify manually. +export * from "./button"; export * from "./layout"; export * from "./logo"; export * from "./page"; diff --git a/src/components/typography/a/a.tsx b/src/components/typography/a/a.tsx index 1f3f88fb..8ac6878c 100644 --- a/src/components/typography/a/a.tsx +++ b/src/components/typography/a/a.tsx @@ -2,9 +2,7 @@ import React, { AnchorHTMLAttributes } from "react"; import "./a.scss"; -export type AProps = React.PropsWithChildren< - AnchorHTMLAttributes ->; +export type AProps = AnchorHTMLAttributes; /** * Anchor () component diff --git a/src/settings/_style.scss b/src/settings/_style.scss index f29b4691..280bc249 100644 --- a/src/settings/_style.scss +++ b/src/settings/_style.scss @@ -2,7 +2,7 @@ :root { /// ANIMATION --animation-timing-function: ease-in-out; - --animation-duration: 0.2s; + --animation-duration: 0.1s; /// THEME --theme-color-primary-800: #341A90;