diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index a1d0cbf0..1ac3f2e4 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -12,6 +12,7 @@
"@popperjs/core": "^2.11.6",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
+ "clsx": "^2.1.1",
"react-hook-form": "^7.46.1",
"react-popper": "^2.3.0",
"vite-tsconfig-paths": "^4.3.1"
@@ -3540,6 +3541,14 @@
"wrap-ansi": "^7.0.0"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -12930,6 +12939,11 @@
"wrap-ansi": "^7.0.0"
}
},
+ "clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
+ },
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 73d8e4b2..a82f5797 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -70,6 +70,7 @@
"@popperjs/core": "^2.11.6",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
+ "clsx": "^2.1.1",
"react-hook-form": "^7.46.1",
"react-popper": "^2.3.0",
"vite-tsconfig-paths": "^4.3.1"
diff --git a/frontend/src/pages/Demo/DemoTailwind.tsx b/frontend/src/pages/Demo/DemoTailwind.tsx
index 54759963..652be178 100644
--- a/frontend/src/pages/Demo/DemoTailwind.tsx
+++ b/frontend/src/pages/Demo/DemoTailwind.tsx
@@ -1,14 +1,77 @@
-import React from "react";
+import React, { useState, useEffect } from "react";
import Typography from "tw-components/Typography";
+import { Button, IconButton } from "tw-components/Buttons";
const DemoTailwind = () => {
+ // Add a setDarkMode for testing dark mode styles
+ const [darkMode, setDarkMode] = useState(false);
+
+ useEffect(() => {
+ if (darkMode) {
+ document.documentElement.classList.add("dark");
+ } else {
+ document.documentElement.classList.remove("dark");
+ }
+ }, [darkMode]);
+
return (
+
+
+ Buttons
+
+
+
+
+ {/* Regular Buttons, active is the default state */}
+
+ Active
+
+
+
+
+ {/* Medium is the default*/}
+
+
+
+
+
+
+ {/* Disabled Buttons */}
+
+ Disabled
+
+
+
+
+
+
+ {/* Hover & Active/Focused States */}
+
+ Hover, Focused and Active
+
+
+
+
+
+
+
+ {/* Icon Buttons */}
+
+ Search Icon
+
+
+
+
+
+
+
Typography Demo (Title 1 - Roboto Bold 48/137% +0)
-
Title Styles (Title 2 - Roboto Bold 36/137% +0)
diff --git a/frontend/src/tw-components/Buttons.tsx b/frontend/src/tw-components/Buttons.tsx
new file mode 100644
index 00000000..8d0d3fc5
--- /dev/null
+++ b/frontend/src/tw-components/Buttons.tsx
@@ -0,0 +1,113 @@
+import React from "react";
+import clsx from "clsx";
+import Typography from "tw-components/Typography";
+
+const buttonSizes = {
+ small: "px-[24px] h-[32px]",
+ "small-long": "px-[40px] h-[32px]",
+ medium: "px-[32px] h-[42px]",
+ "medium-long": "px-[48px] h-[42px]",
+ large: "px-[40px] h-[51px]",
+ "large-long": "px-[56px] h-[51px]",
+ "icon-only": "px-[24px] h-[42px]",
+};
+
+type ButtonSize = keyof typeof buttonSizes;
+
+type BaseButtonProps = {
+ size?: ButtonSize;
+ disabled?: boolean;
+ className?: string;
+ children?: React.ReactNode;
+ onClick?: () => void;
+};
+
+// Shared styles
+const baseButtonStyles =
+ "transition-all duration-200 flex items-center justify-center rounded-[64px] bg-blue-dark text-white hover:bg-blue-dark-hover focus:bg-blue-dark-focused focus:outline-none active:bg-blue-dark-focused disabled:bg-grey disabled:text-white disabled:cursor-not-allowed";
+
+// Dark mode styles for enabled buttons
+const enabledDarkModeStyles =
+ "dark:bg-white dark:text-blue-dark dark:hover:bg-grey-light dark:focus:bg-[#D9DBDF] dark:active:bg-[#D9DBDF] dark:disabled:bg-grey dark:disabled:text-grey-light";
+
+// Base Button, no text, just styling
+const BaseButton: React.FC
= ({
+ size = "medium",
+ disabled = false,
+ className,
+ children,
+ onClick,
+}) => {
+ return (
+
+ );
+};
+
+// Default Button, extends base button, adds typography
+const buttonTypography: Record<
+ Exclude,
+ React.ElementType
+> = {
+ small: Typography.Title7,
+ "small-long": Typography.Title7,
+ medium: Typography.Title6,
+ "medium-long": Typography.Title6,
+ large: Typography.Title5,
+ "large-long": Typography.Title5,
+};
+
+type ButtonProps = Omit & {
+ size?: Exclude;
+ children: React.ReactNode;
+};
+
+const Button: React.FC = ({
+ size = "medium",
+ children,
+ ...props
+}) => {
+ const TextComponent = buttonTypography[size];
+ return (
+
+ {children}
+
+ );
+};
+
+// Icon Button, extends base button, adds static svg
+type IconButtonProps = Omit;
+
+const IconButton: React.FC = (props) => {
+ return (
+
+
+
+ );
+};
+
+export { Button, IconButton };
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index d6a6985e..7541c68e 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -1,5 +1,6 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
+ darkMode: "class", // Enables dark mode with a class
content: [
"./src/pages/Demo/DemoTailwind.tsx",
"./src/pages/Authentication/*.tsx",