From 5f164f365733d9ab4e443de6eb44d48eb75b805e Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Mon, 21 Apr 2025 19:59:42 +0200 Subject: [PATCH 1/7] Add README.md to explain the new directory --- src/app/conf/_design-system/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/app/conf/_design-system/README.md diff --git a/src/app/conf/_design-system/README.md b/src/app/conf/_design-system/README.md new file mode 100644 index 0000000000..5e2a979477 --- /dev/null +++ b/src/app/conf/_design-system/README.md @@ -0,0 +1 @@ +UI from 2025's designs From e2b9d673c84e1a7a9d1066846f1ee4b97f92c0f3 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Mon, 21 Apr 2025 20:01:33 +0200 Subject: [PATCH 2/7] Add Anchor component for internal and external links --- src/app/conf/_design-system/anchor.tsx | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/app/conf/_design-system/anchor.tsx diff --git a/src/app/conf/_design-system/anchor.tsx b/src/app/conf/_design-system/anchor.tsx new file mode 100644 index 0000000000..72c5635b83 --- /dev/null +++ b/src/app/conf/_design-system/anchor.tsx @@ -0,0 +1,42 @@ +import { ForwardedRef, forwardRef, ReactElement } from "react" +import NextLink from "next/link" +import type { LinkProps as NextLinkProps } from "next/link" + +// eslint-disable-next-line @typescript-eslint/no-namespace +export declare namespace AnchorProps { + interface IntrinsicAnchorProps + extends React.DetailedHTMLProps< + React.AnchorHTMLAttributes, + HTMLAnchorElement + > { + href: `#${string}` | `http${string}` + } + + interface InternalAnchorProps extends NextLinkProps {} +} + +export type AnchorProps = + | AnchorProps.IntrinsicAnchorProps + | AnchorProps.InternalAnchorProps + +export const Anchor = forwardRef(function Anchor( + props: AnchorProps, + ref: ForwardedRef, +) { + return isInternal(props) ? ( + + ) : ( + + ) +}) as (props: AnchorProps) => ReactElement + +function isInternal( + props: AnchorProps, +): props is AnchorProps.InternalAnchorProps { + return ( + typeof props.href === "object" || + (typeof props.href === "string" && + !props.href.startsWith("http") && + !props.href.startsWith("#")) + ) +} From 7bf5c623914ea6c64af9480bf2a5b9ac0ab8c0be Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Mon, 21 Apr 2025 20:34:25 +0200 Subject: [PATCH 3/7] Add clsx to classFunctions in Tailwind VSCode settings --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index cb56bf9fab..02d49400e5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,6 @@ "typescript.tsdk": "node_modules/typescript/lib", "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - } + }, + "tailwindCSS.classFunctions": ["clsx"] } From 7b0ee331241c08a18a9cafa7a893c2156c8146a3 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Mon, 21 Apr 2025 20:34:40 +0200 Subject: [PATCH 4/7] Add Button component --- src/app/conf/_design-system/button.tsx | 90 ++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/app/conf/_design-system/button.tsx diff --git a/src/app/conf/_design-system/button.tsx b/src/app/conf/_design-system/button.tsx new file mode 100644 index 0000000000..6605a21752 --- /dev/null +++ b/src/app/conf/_design-system/button.tsx @@ -0,0 +1,90 @@ +import { clsx } from "clsx" +import { Anchor } from "./anchor" + +type Size = "md" | "lg" + +// eslint-disable-next-line @typescript-eslint/no-namespace +export declare namespace ButtonProps { + export interface BaseProps { + size?: Size + } + + export interface AnchorProps + extends BaseProps, + React.DetailedHTMLProps< + React.AnchorHTMLAttributes, + HTMLAnchorElement + > { + href: string + as?: never + className?: string + } + + export interface ButtonProps + extends BaseProps, + React.DetailedHTMLProps< + React.ButtonHTMLAttributes, + HTMLButtonElement + > { + href?: never + as?: never + className?: string + disabled?: boolean + type?: "button" | "submit" | "reset" + onClick?: React.MouseEventHandler + } + + /** + * Use inside `` or as visual part of bigger interactive element. + * Prefer `a` and `button` Buttons otherwise. + */ + export interface NonInteractiveProps + extends BaseProps, + React.DetailedHTMLProps, HTMLElement> { + href?: never + as: "span" | "div" + className?: string + } +} + +export type ButtonProps = + | ButtonProps.AnchorProps + | ButtonProps.ButtonProps + | ButtonProps.NonInteractiveProps + +export function Button(props: ButtonProps) { + const className = clsx( + "relative flex items-center justify-center gap-2.5 rounded-lg font-normal text-base/none text-neu-0 font-sans h-14 px-8 data-[size=md]:h-12", + props.className, + ) + + const styleAttrs = { "data-size": props.size } + + if ("href" in props && typeof props.href === "string") { + const { className: _1, size: _2, children, ...rest } = props + + return ( + + {children} + + ) + } + + if (props.as) { + const { className: _1, size: _2, children, as, ...rest } = props + const Root = as as "span" // we don't need HTMLDivElement type + return ( + + {children} + + ) + } + + const { className: _1, size: _2, children, ...rest } = props + + return ( + + ) +} From aaf944e110336fbfe2050587247f5d794804cda8 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Mon, 21 Apr 2025 22:29:46 +0200 Subject: [PATCH 5/7] Add `variant` prop --- src/app/conf/_design-system/button.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/conf/_design-system/button.tsx b/src/app/conf/_design-system/button.tsx index 6605a21752..cff8eed440 100644 --- a/src/app/conf/_design-system/button.tsx +++ b/src/app/conf/_design-system/button.tsx @@ -2,11 +2,13 @@ import { clsx } from "clsx" import { Anchor } from "./anchor" type Size = "md" | "lg" +type Variant = "primary" | "secondary" // eslint-disable-next-line @typescript-eslint/no-namespace export declare namespace ButtonProps { export interface BaseProps { size?: Size + variant?: Variant } export interface AnchorProps @@ -54,11 +56,11 @@ export type ButtonProps = export function Button(props: ButtonProps) { const className = clsx( - "relative flex items-center justify-center gap-2.5 rounded-lg font-normal text-base/none text-neu-0 font-sans h-14 px-8 data-[size=md]:h-12", + "relative flex items-center justify-center gap-2.5 font-normal text-base/none text-neu-0 font-sans h-14 px-8 data-[size=md]:h-12 data-[variant=secondary]:bg-neu-100 data-[variant=secondary]:light:text-neu-900", props.className, ) - const styleAttrs = { "data-size": props.size } + const styleAttrs = { "data-size": props.size, "data-variant": props.variant } if ("href" in props && typeof props.href === "string") { const { className: _1, size: _2, children, ...rest } = props From 088a5780936d8d76d56cb3a0baf279d6280f57fe Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 22 Apr 2025 01:03:46 +0200 Subject: [PATCH 6/7] Fix Button colors --- src/app/conf/_design-system/button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/conf/_design-system/button.tsx b/src/app/conf/_design-system/button.tsx index cff8eed440..3aa0b09ed4 100644 --- a/src/app/conf/_design-system/button.tsx +++ b/src/app/conf/_design-system/button.tsx @@ -56,7 +56,7 @@ export type ButtonProps = export function Button(props: ButtonProps) { const className = clsx( - "relative flex items-center justify-center gap-2.5 font-normal text-base/none text-neu-0 font-sans h-14 px-8 data-[size=md]:h-12 data-[variant=secondary]:bg-neu-100 data-[variant=secondary]:light:text-neu-900", + "relative flex items-center justify-center gap-2.5 font-normal text-base/none text-neu-0 bg-neu-900 hover:bg-neu-800 active:bg-neu-700 font-sans h-14 px-8 data-[size=md]:h-12 data-[variant=secondary]:bg-neu-100 data-[variant=secondary]:text-neu-900 dark:data-[variant=secondary]:text-neu-0 data-[variant=secondary]:hover:bg-neu-200/50 data-[variant=secondary]:active:bg-neu-200", props.className, ) From c04ac456900eac5cece77889ab726be4518f4d65 Mon Sep 17 00:00:00 2001 From: Piotr Monwid-Olechnowicz Date: Tue, 22 Apr 2025 02:11:21 +0200 Subject: [PATCH 7/7] Tweak hover color --- src/app/conf/_design-system/button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/conf/_design-system/button.tsx b/src/app/conf/_design-system/button.tsx index 3aa0b09ed4..af1220cf49 100644 --- a/src/app/conf/_design-system/button.tsx +++ b/src/app/conf/_design-system/button.tsx @@ -56,7 +56,7 @@ export type ButtonProps = export function Button(props: ButtonProps) { const className = clsx( - "relative flex items-center justify-center gap-2.5 font-normal text-base/none text-neu-0 bg-neu-900 hover:bg-neu-800 active:bg-neu-700 font-sans h-14 px-8 data-[size=md]:h-12 data-[variant=secondary]:bg-neu-100 data-[variant=secondary]:text-neu-900 dark:data-[variant=secondary]:text-neu-0 data-[variant=secondary]:hover:bg-neu-200/50 data-[variant=secondary]:active:bg-neu-200", + "relative flex items-center justify-center gap-2.5 font-normal text-base/none text-neu-0 bg-neu-900 hover:bg-neu-800 active:bg-neu-700 font-sans h-14 px-8 data-[size=md]:h-12 data-[variant=secondary]:bg-neu-100 data-[variant=secondary]:text-neu-900 dark:data-[variant=secondary]:text-neu-0 data-[variant=secondary]:hover:bg-neu-200/75 data-[variant=secondary]:active:bg-neu-200/90", props.className, )