+ );
+}
diff --git a/apps/landing/components/theme/theme-provider.tsx b/apps/landing/components/theme/theme-provider.tsx
new file mode 100644
index 0000000..e018a73
--- /dev/null
+++ b/apps/landing/components/theme/theme-provider.tsx
@@ -0,0 +1,11 @@
+"use client"
+
+import * as React from "react"
+import { ThemeProvider as NextThemesProvider } from "next-themes"
+
+export function ThemeProvider({
+ children,
+ ...props
+}: React.ComponentProps) {
+ return {children}
+}
\ No newline at end of file
diff --git a/apps/landing/components/theme/theme-toggler.tsx b/apps/landing/components/theme/theme-toggler.tsx
new file mode 100644
index 0000000..497fce4
--- /dev/null
+++ b/apps/landing/components/theme/theme-toggler.tsx
@@ -0,0 +1,33 @@
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { useTheme } from "next-themes";
+import { FiMoon, FiSun } from "react-icons/fi";
+import { IconButton } from "@repo/ui";
+
+export default function ThemeToggler() {
+ const { theme, setTheme } = useTheme();
+ const [mounted, setMounted] = useState(false);
+
+ // useEffect only runs on the client, so now we can safely show the UI
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ if (!mounted) {
+ return null;
+ }
+
+ const toggleTheme = () => {
+ setTheme(theme === "dark" ? "light" : "dark");
+ };
+
+ return (
+
+ );
+}
diff --git a/apps/landing/package.json b/apps/landing/package.json
index c900639..7da371d 100644
--- a/apps/landing/package.json
+++ b/apps/landing/package.json
@@ -11,10 +11,12 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
- "@repo/ui": "*",
"@assets": "workspace:*",
+ "@repo/ui": "workspace:*",
+ "lucide-react": "^0.563.0",
"motion": "^12.23.25",
"next": "^15.5.0",
+ "next-themes": "^0.4.6",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
@@ -26,6 +28,7 @@
"@types/react": "19.1.0",
"@types/react-dom": "19.1.1",
"eslint": "^9.34.0",
- "typescript": "5.9.2"
+ "typescript": "5.9.2",
+ "@repo/fonts": "workspace:*"
}
}
diff --git a/apps/landing/public/logo.png b/apps/landing/public/logo.png
new file mode 100644
index 0000000..011a5da
Binary files /dev/null and b/apps/landing/public/logo.png differ
diff --git a/apps/landing/public/signin.png b/apps/landing/public/signin.png
new file mode 100644
index 0000000..f24049a
Binary files /dev/null and b/apps/landing/public/signin.png differ
diff --git a/apps/landing/public/signup.png b/apps/landing/public/signup.png
new file mode 100644
index 0000000..6075508
Binary files /dev/null and b/apps/landing/public/signup.png differ
diff --git a/bun.lockb b/bun.lockb
index b6439a1..92f20b1 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/packages/assets/public/logo.png b/packages/assets/public/logo.png
new file mode 100644
index 0000000..011a5da
Binary files /dev/null and b/packages/assets/public/logo.png differ
diff --git a/packages/fonts/Instrument_Serif/InstrumentSerif-Italic.ttf b/packages/fonts/Instrument_Serif/InstrumentSerif-Italic.ttf
new file mode 100644
index 0000000..8fa958d
Binary files /dev/null and b/packages/fonts/Instrument_Serif/InstrumentSerif-Italic.ttf differ
diff --git a/packages/fonts/Instrument_Serif/InstrumentSerif-Regular.ttf b/packages/fonts/Instrument_Serif/InstrumentSerif-Regular.ttf
new file mode 100644
index 0000000..9763031
Binary files /dev/null and b/packages/fonts/Instrument_Serif/InstrumentSerif-Regular.ttf differ
diff --git a/packages/fonts/Instrument_Serif/OFL.txt b/packages/fonts/Instrument_Serif/OFL.txt
new file mode 100644
index 0000000..0531e87
--- /dev/null
+++ b/packages/fonts/Instrument_Serif/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/packages/fonts/Poppins/OFL.txt b/packages/fonts/Poppins/OFL.txt
new file mode 100644
index 0000000..3e92931
--- /dev/null
+++ b/packages/fonts/Poppins/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/packages/fonts/Poppins/Poppins-Black.ttf b/packages/fonts/Poppins/Poppins-Black.ttf
new file mode 100644
index 0000000..71c0f99
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-Black.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-BlackItalic.ttf b/packages/fonts/Poppins/Poppins-BlackItalic.ttf
new file mode 100644
index 0000000..7aeb58b
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-BlackItalic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-Bold.ttf b/packages/fonts/Poppins/Poppins-Bold.ttf
new file mode 100644
index 0000000..00559ee
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-Bold.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-BoldItalic.ttf b/packages/fonts/Poppins/Poppins-BoldItalic.ttf
new file mode 100644
index 0000000..e61e8e8
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-BoldItalic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-ExtraBold.ttf b/packages/fonts/Poppins/Poppins-ExtraBold.ttf
new file mode 100644
index 0000000..df70936
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-ExtraBold.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-ExtraBoldItalic.ttf b/packages/fonts/Poppins/Poppins-ExtraBoldItalic.ttf
new file mode 100644
index 0000000..14d2b37
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-ExtraBoldItalic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-ExtraLight.ttf b/packages/fonts/Poppins/Poppins-ExtraLight.ttf
new file mode 100644
index 0000000..e76ec69
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-ExtraLight.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-ExtraLightItalic.ttf b/packages/fonts/Poppins/Poppins-ExtraLightItalic.ttf
new file mode 100644
index 0000000..89513d9
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-ExtraLightItalic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-Italic.ttf b/packages/fonts/Poppins/Poppins-Italic.ttf
new file mode 100644
index 0000000..12b7b3c
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-Italic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-Light.ttf b/packages/fonts/Poppins/Poppins-Light.ttf
new file mode 100644
index 0000000..bc36bcc
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-Light.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-LightItalic.ttf b/packages/fonts/Poppins/Poppins-LightItalic.ttf
new file mode 100644
index 0000000..9e70be6
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-LightItalic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-Medium.ttf b/packages/fonts/Poppins/Poppins-Medium.ttf
new file mode 100644
index 0000000..6bcdcc2
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-Medium.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-MediumItalic.ttf b/packages/fonts/Poppins/Poppins-MediumItalic.ttf
new file mode 100644
index 0000000..be67410
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-MediumItalic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-Regular.ttf b/packages/fonts/Poppins/Poppins-Regular.ttf
new file mode 100644
index 0000000..9f0c71b
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-Regular.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-SemiBold.ttf b/packages/fonts/Poppins/Poppins-SemiBold.ttf
new file mode 100644
index 0000000..74c726e
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-SemiBold.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-SemiBoldItalic.ttf b/packages/fonts/Poppins/Poppins-SemiBoldItalic.ttf
new file mode 100644
index 0000000..3e6c942
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-SemiBoldItalic.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-Thin.ttf b/packages/fonts/Poppins/Poppins-Thin.ttf
new file mode 100644
index 0000000..03e7366
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-Thin.ttf differ
diff --git a/packages/fonts/Poppins/Poppins-ThinItalic.ttf b/packages/fonts/Poppins/Poppins-ThinItalic.ttf
new file mode 100644
index 0000000..e26db5d
Binary files /dev/null and b/packages/fonts/Poppins/Poppins-ThinItalic.ttf differ
diff --git a/packages/fonts/index.css b/packages/fonts/index.css
new file mode 100644
index 0000000..20e8095
--- /dev/null
+++ b/packages/fonts/index.css
@@ -0,0 +1,161 @@
+/* Instrument Serif */
+@font-face {
+ font-family: 'Instrument Serif';
+ src: url('./Instrument_Serif/InstrumentSerif-Regular.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Instrument Serif';
+ src: url('./Instrument_Serif/InstrumentSerif-Italic.ttf') format('truetype');
+ font-weight: 400;
+ font-style: italic;
+ font-display: swap;
+}
+
+/* Poppins */
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-Thin.ttf') format('truetype');
+ font-weight: 100;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-ThinItalic.ttf') format('truetype');
+ font-weight: 100;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-ExtraLight.ttf') format('truetype');
+ font-weight: 200;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-ExtraLightItalic.ttf') format('truetype');
+ font-weight: 200;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-Light.ttf') format('truetype');
+ font-weight: 300;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-LightItalic.ttf') format('truetype');
+ font-weight: 300;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-Regular.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-Italic.ttf') format('truetype');
+ font-weight: 400;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-Medium.ttf') format('truetype');
+ font-weight: 500;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-MediumItalic.ttf') format('truetype');
+ font-weight: 500;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-SemiBold.ttf') format('truetype');
+ font-weight: 600;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-SemiBoldItalic.ttf') format('truetype');
+ font-weight: 600;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-Bold.ttf') format('truetype');
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-BoldItalic.ttf') format('truetype');
+ font-weight: 700;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-ExtraBold.ttf') format('truetype');
+ font-weight: 800;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-ExtraBoldItalic.ttf') format('truetype');
+ font-weight: 800;
+ font-style: italic;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-Black.ttf') format('truetype');
+ font-weight: 900;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Poppins';
+ src: url('./Poppins/Poppins-BlackItalic.ttf') format('truetype');
+ font-weight: 900;
+ font-style: italic;
+ font-display: swap;
+}
diff --git a/packages/fonts/package.json b/packages/fonts/package.json
new file mode 100644
index 0000000..f93c43e
--- /dev/null
+++ b/packages/fonts/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "@repo/fonts",
+ "version": "0.0.0",
+ "private": true,
+ "exports": {
+ ".": "./index.css",
+ "./*": "./*"
+ },
+ "files": [
+ "index.css",
+ "Instrument_Serif",
+ "Poppins"
+ ]
+}
diff --git a/packages/tailwind-config/theme.css b/packages/tailwind-config/theme.css
index 8521036..32ac3ef 100644
--- a/packages/tailwind-config/theme.css
+++ b/packages/tailwind-config/theme.css
@@ -1,10 +1,432 @@
@theme {
- --color-primary: #2563eb;
- --color-secondary: #9333ea;
- --color-background: #f9fafb;
- --color-text: #1f2937;
+ --color-*: initial;
+
+ /* ========================================
+ FONT FAMILIES
+ ======================================== */
+ --font-sans: "Poppins", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ --font-serif: "Instrument Serif", ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
+
+ /* ========================================
+ BRAND COLORS
+ ======================================== */
+ --color-arctic-powder: hsl(156 19% 95%);
+ --color-mystic-mint: hsl(156 26% 88%);
+ --color-nocturnal-expedition: hsl(192 68% 21%);
+ --color-oceanic-noir: hsl(201 40% 15%);
+
+ /* ========================================
+ NEUTRAL SCALE (Shark Palette)
+ ======================================== */
+ --color-neutral-50: #f6f6f6;
+ --color-neutral-100: #e7e7e7;
+ --color-neutral-200: #d1d1d1;
+ --color-neutral-300: #b0b0b0;
+ --color-neutral-400: #888888;
+ --color-neutral-500: #6d6d6d;
+ --color-neutral-600: #5d5d5d;
+ --color-neutral-700: #4f4f4f;
+ --color-neutral-800: #454545;
+ --color-neutral-900: #3d3d3d;
+ --color-neutral-950: #262626;
+
+ /* ========================================
+ SEMANTIC TOKENS - LIGHT MODE
+ ======================================== */
+
+ /* Primary & Secondary */
+ --color-primary: var(--color-oceanic-noir);
+ --color-primary-foreground: var(--color-arctic-powder);
+ --color-secondary: var(--color-nocturnal-expedition);
+ --color-secondary-foreground: var(--color-arctic-powder);
+
+ /* Background & Foreground */
+ --color-background: var(--color-arctic-powder);
+ --color-foreground: var(--color-oceanic-noir);
+ --color-foreground-muted: var(--color-nocturnal-expedition);
+ --color-foreground-subtle: var(--color-mystic-mint);
+
+ /* Surface (cards, panels, elevated elements) */
+ --color-surface: hsl(0 0% 100%);
+ --color-surface-elevated: var(--color-neutral-50);
+ --color-surface-hover: var(--color-neutral-100);
+
+ /* Borders */
+ --color-border: var(--color-mystic-mint);
+ --color-border-muted: var(--color-arctic-powder);
+ --color-border-strong: var(--color-nocturnal-expedition);
+
+ /* Muted & Accent */
+ --color-muted: var(--color-mystic-mint);
+ --color-muted-foreground: var(--color-nocturnal-expedition);
+ --color-accent: var(--color-nocturnal-expedition);
+ --color-accent-foreground: var(--color-arctic-powder);
+
+ /* Interactive States */
+ --color-hover: var(--color-neutral-100);
+ --color-active: var(--color-neutral-200);
+ --color-disabled: var(--color-neutral-300);
+ --color-disabled-foreground: var(--color-neutral-400);
+
+ /* Input */
+ --color-input: var(--color-mystic-mint);
+ --color-input-foreground: var(--color-oceanic-noir);
+
+ /* ========================================
+ BUTTON TOKENS - LIGHT MODE
+ ======================================== */
+ --button-primary: var(--color-oceanic-noir);
+ --button-primary-fg: var(--color-arctic-powder);
+ --button-primary-hover: var(--color-nocturnal-expedition);
+ --button-primary-pressed: var(--color-oceanic-noir); /* Default to primary for now */
+ --button-primary-disabled: var(--color-neutral-200);
+ --button-primary-disabled-text: var(--color-neutral-400);
+
+ --button-outlined-border: var(--color-oceanic-noir);
+ --button-outlined-hover-bg: var(--color-mystic-mint);
+ --button-outlined-pressed-bg: var(--color-mystic-mint);
+ --button-outlined-disabled-border: var(--color-neutral-300);
+
+ --button-texted-hover-bg: var(--color-mystic-mint);
+ --button-texted-pressed-bg: var(--color-mystic-mint);
+
+ --button-hover-text: var(--color-oceanic-noir); /* Ensure contrast on Mystic Mint */
+
+
+
+ /* Status Colors */
+ --color-danger: hsl(0 84% 66%);
+ --color-danger-foreground: hsl(0 0% 100%);
+ --color-message: hsl(217 91% 68%);
+ --color-message-foreground: hsl(0 0% 100%);
+ --color-success: hsl(123 43% 54%);
+ --color-success-foreground: hsl(0 0% 100%);
+ --color-warning: hsl(38 92% 50%);
+ --color-warning-foreground: hsl(0 0% 100%);
+
+ /* ========================================
+ BREAKPOINTS
+ ======================================== */
+ --breakpoint-mobile: 375px;
+ --breakpoint-tablet: 960px;
+ --breakpoint-desktop: 1248px;
+
+
+ /* ========================================
+ SHADOWS
+ ======================================== */
+ --shadow-large: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+ --shadow-small: 0 1px 2px 0 rgb(0 0 0 / 0.05);
+}
+
+
+
+/* ========================================
+ LAYOUT TOKENS (Responsive)
+ ======================================== */
+:root {
+ --layout-gap: 0px;
+ --layout-cols: 1;
+ --layout-margin: 16px;
+}
+
+@media (width >= 960px) {
+ :root {
+ --layout-gap: 32px;
+ --layout-cols: 12;
+ --layout-margin: 32px;
+ }
+}
+
+@media (width >= 1248px) {
+ :root {
+ --layout-margin: 64px;
+ }
+}
+
+/* ========================================
+ TYPOGRAPHY TOKENS (Responsive)
+ ======================================== */
+:root {
+ /* Body */
+ --text-body-size: 12px;
+ --text-body-lh: 18px;
+
+ /* Headings */
+ --text-h1-size: 48px;
+ --text-h1-lh: 57.6px;
+ --text-h1-tracking: -0.02em;
+
+ --text-h2-size: 40px;
+ --text-h2-lh: 48px;
+ --text-h2-tracking: -0.02em;
+
+ --text-h3-size: 32px;
+ --text-h3-lh: 44.8px;
+ --text-h3-tracking: -0.02em;
+
+ --text-h4-size: 24px;
+ --text-h4-lh: 33.6px;
+
+ /* Pre-title */
+ --text-pre-title-size: 12px;
+ --text-pre-title-lh: 18px;
+
+ /* Small */
+ --text-small-size: 10px;
+ --text-small-lh: 15px;
+
+ /* Subtitle */
+ --text-subtitle-size: 20px;
+ --text-subtitle-lh: 20px;
+}
+
+@media (width >= 960px) {
+ :root {
+ --text-h1-size: 64px;
+ --text-h1-lh: 76.8px;
+ --text-h2-size: 56px;
+ --text-h2-lh: 67.2px;
+ --text-h3-size: 40px;
+ --text-h3-lh: 56px;
+ --text-h4-size: 28px;
+ --text-h4-lh: 39.2px;
+ --text-pre-title-size: 14px;
+ --text-pre-title-lh: 21px;
+ --text-small-size: 11px;
+ --text-small-lh: 16.5px;
+ --text-subtitle-size: 24px;
+ --text-subtitle-lh: 24px;
+ }
+}
+
+@media (width >= 1248px) {
+ :root {
+ --text-h1-size: 72px;
+ --text-h1-lh: 86.4px;
+ --text-h2-size: 64px;
+ --text-h2-lh: 76.8px;
+ --text-h3-size: 48px;
+ --text-h3-lh: 67.2px;
+ --text-h4-size: 32px;
+ --text-h4-lh: 44.8px;
+ }
+}
+
+/* ========================================
+ UTILITY CLASSES
+ ======================================== */
+
+@utility layout-grid {
+ display: grid;
+ grid-template-columns: repeat(var(--layout-cols), minmax(0, 1fr));
+ gap: var(--layout-gap);
+}
+
+@utility layout-container {
+ width: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: var(--layout-margin);
+ padding-right: var(--layout-margin);
+}
+
+/* Body Utilities */
+@utility text-body-regular {
+ font-family: var(--font-sans);
+ font-size: var(--text-body-size);
+ line-height: var(--text-body-lh);
+ font-weight: 400;
+}
+@utility text-body-medium {
+ font-family: var(--font-sans);
+ font-size: var(--text-body-size);
+ line-height: var(--text-body-lh);
+ font-weight: 500;
+}
+@utility text-body-bold {
+ font-family: var(--font-sans);
+ font-size: var(--text-body-size);
+ line-height: var(--text-body-lh);
+ font-weight: 700;
+}
+
+/* Heading Utilities */
+@utility text-h1 {
+ font-family: var(--font-serif);
+ font-size: var(--text-h1-size);
+ line-height: var(--text-h1-lh);
+ letter-spacing: var(--text-h1-tracking);
+ font-weight: 700;
+}
+@utility text-h2 {
+ font-family: var(--font-serif);
+ font-size: var(--text-h2-size);
+ line-height: var(--text-h2-lh);
+ letter-spacing: var(--text-h2-tracking);
+ font-weight: 700;
+}
+@utility text-h3 {
+ font-family: var(--font-serif);
+ font-size: var(--text-h3-size);
+ line-height: var(--text-h3-lh);
+ letter-spacing: var(--text-h3-tracking);
+ font-weight: 700;
+}
+@utility text-h4-bold {
+ font-family: var(--font-serif);
+ font-size: var(--text-h4-size);
+ line-height: var(--text-h4-lh);
+ font-weight: 700;
+}
+@utility text-h4-medium {
+ font-family: var(--font-serif);
+ font-size: var(--text-h4-size);
+ line-height: var(--text-h4-lh);
+ font-weight: 500;
+}
+
+/* Pre-title Utilities */
+@utility text-pre-title-regular {
+ font-family: var(--font-serif);
+ font-size: var(--text-pre-title-size);
+ line-height: var(--text-pre-title-lh);
+ font-weight: 400;
+}
+@utility text-pre-title-medium {
+ font-family: var(--font-serif);
+ font-size: var(--text-pre-title-size);
+ line-height: var(--text-pre-title-lh);
+ font-weight: 500;
+}
+@utility text-pre-title-bold {
+ font-family: var(--font-serif);
+ font-size: var(--text-pre-title-size);
+ line-height: var(--text-pre-title-lh);
+ font-weight: 700;
+}
+@utility text-pre-title-light {
+ font-family: var(--font-serif);
+ font-size: var(--text-pre-title-size);
+ line-height: var(--text-pre-title-lh);
+ font-weight: 300;
+}
+
+/* Small Utilities */
+@utility text-small-regular {
+ font-family: var(--font-serif);
+ font-size: var(--text-small-size);
+ line-height: var(--text-small-lh);
+ font-weight: 400;
+}
+@utility text-small-medium {
+ font-family: var(--font-serif);
+ font-size: var(--text-small-size);
+ line-height: var(--text-small-lh);
+ font-weight: 500;
+}
+@utility text-small-bold {
+ font-family: var(--font-serif);
+ font-size: var(--text-small-size);
+ line-height: var(--text-small-lh);
+ font-weight: 700;
+}
+
+/* Subtitle Utilities */
+@utility text-subtitle-regular {
+ font-family: var(--font-sans);
+ font-size: var(--text-subtitle-size);
+ line-height: var(--text-subtitle-lh);
+ font-weight: 400;
+}
+@utility text-subtitle-medium {
+ font-family: var(--font-sans);
+ font-size: var(--text-subtitle-size);
+ line-height: var(--text-subtitle-lh);
+ font-weight: 500;
+}
+@utility text-subtitle-bold {
+ font-family: var(--font-sans);
+ font-size: var(--text-subtitle-size);
+ line-height: var(--text-subtitle-lh);
+ font-weight: 700;
}
@layer base {
-
+ .dark {
+ /* ========================================
+ SEMANTIC TOKENS - DARK MODE
+ ======================================== */
+
+ /* Primary & Secondary */
+ --color-primary: var(--color-arctic-powder);
+ --color-primary-foreground: var(--color-oceanic-noir);
+ --color-secondary: var(--color-mystic-mint);
+ --color-secondary-foreground: var(--color-oceanic-noir);
+
+ /* Background & Foreground */
+ --color-background: var(--color-oceanic-noir);
+ --color-foreground: var(--color-arctic-powder);
+ --color-foreground-muted: var(--color-mystic-mint);
+ --color-foreground-subtle: var(--color-nocturnal-expedition);
+
+ /* Surface (cards, panels, elevated elements) */
+ /* Surface (cards, panels, elevated elements) */
+ --color-surface: hsl(201 40% 25%);
+ --color-surface-elevated: hsl(201 40% 30%);
+ --color-surface-hover: hsl(201 40% 35%);
+
+ /* Borders */
+ --color-border: var(--color-mystic-mint);
+ --color-border-muted: var(--color-oceanic-noir);
+ --color-border-strong: var(--color-mystic-mint);
+
+ /* Muted & Accent */
+ --color-muted: var(--color-nocturnal-expedition);
+ --color-muted-foreground: var(--color-neutral-400);
+ --color-accent: var(--color-mystic-mint);
+ --color-accent-foreground: var(--color-oceanic-noir);
+
+ /* Interactive States */
+ --color-hover: hsl(201 40% 20%);
+ --color-active: hsl(201 40% 25%);
+ --color-disabled: var(--color-neutral-800);
+ --color-disabled-foreground: var(--color-neutral-600);
+
+ /* Input */
+ --color-input: var(--color-neutral-300);
+ --color-input-foreground: var(--color-arctic-powder);
+
+ /* Status Colors (same in dark mode) */
+ --color-danger: hsl(0 84% 66%);
+ --color-danger-foreground: hsl(0 0% 100%);
+ --color-message: hsl(217 91% 68%);
+ --color-message-foreground: hsl(0 0% 100%);
+ --color-success: hsl(123 43% 54%);
+ --color-success-foreground: hsl(0 0% 100%);
+ --color-warning: hsl(38 92% 50%);
+ --color-warning-foreground: hsl(0 0% 100%);
+
+ /* ========================================
+ BUTTON TOKENS - DARK MODE
+ ======================================== */
+ --button-primary: var(--color-arctic-powder);
+ --button-primary-fg: var(--color-oceanic-noir);
+ --button-primary-hover: var(--color-mystic-mint);
+ --button-primary-pressed: var(--color-arctic-powder);
+ --button-primary-disabled: var(--color-neutral-800);
+ --button-primary-disabled-text: var(--color-neutral-600);
+
+ --button-outlined-border: var(--color-arctic-powder);
+ --button-outlined-hover-bg: var(--color-mystic-mint);
+ --button-outlined-pressed-bg: var(--color-mystic-mint);
+ --button-outlined-disabled-border: var(--color-neutral-700);
+
+ --button-texted-hover-bg: var(--color-mystic-mint);
+ --button-texted-pressed-bg: var(--color-mystic-mint);
+
+ --button-hover-text: var(--color-oceanic-noir); /* Contrast on Mystic Mint */
+
+
+ }
}
\ No newline at end of file
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 7277db0..55c3368 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"exports": {
+ ".": "./src/index.ts",
"./*": "./src/*.tsx"
},
"scripts": {
@@ -20,7 +21,11 @@
"typescript": "5.9.2"
},
"dependencies": {
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
"react": "^19.1.0",
- "react-dom": "^19.1.0"
+ "react-dom": "^19.1.0",
+ "react-icons": "^5.5.0",
+ "tailwind-merge": "^3.4.0"
}
}
diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx
deleted file mode 100644
index 78e5420..0000000
--- a/packages/ui/src/button.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-"use client";
-
-import { ReactNode } from "react";
-
-interface ButtonProps {
- children: ReactNode;
- className?: string;
- appName: string;
-}
-
-export const Button = ({ children, className, appName }: ButtonProps) => {
- return (
-
- );
-};
diff --git a/packages/ui/src/code.tsx b/packages/ui/src/code.tsx
deleted file mode 100644
index f7cbd22..0000000
--- a/packages/ui/src/code.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { type JSX } from "react";
-
-export function Code({
- children,
- className,
-}: {
- children: React.ReactNode;
- className?: string;
-}): JSX.Element {
- return {children};
-}
diff --git a/packages/ui/src/components/button/button.tsx b/packages/ui/src/components/button/button.tsx
new file mode 100644
index 0000000..6831a25
--- /dev/null
+++ b/packages/ui/src/components/button/button.tsx
@@ -0,0 +1,100 @@
+"use client";
+
+import {
+ ReactNode,
+ ButtonHTMLAttributes,
+ forwardRef,
+ ElementType,
+} from "react";
+import { cva, type VariantProps } from "class-variance-authority";
+import { cn } from "../../lib/utils";
+
+export const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-2xl text-sm font-medium transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 w-fit px-[30px] py-[12px]",
+ {
+ variants: {
+ variant: {
+ contained:
+ "bg-[var(--button-primary)] text-[var(--button-primary-fg)] hover:bg-[var(--button-primary-hover)] active:scale-95",
+ outlined:
+ "border-[1.5px] border-[var(--button-outlined-border)] bg-transparent text-[var(--button-primary)] hover:bg-[var(--button-outlined-hover-bg)] hover:text-[var(--button-hover-text)] active:bg-[var(--button-outlined-pressed-bg)]",
+ texted:
+ "bg-transparent text-[var(--button-primary)] hover:bg-[var(--button-texted-hover-bg)] hover:text-[var(--button-hover-text)] active:bg-[var(--button-texted-pressed-bg)]",
+ },
+ size: {
+ default: "h-12 px-[30px]",
+ icon: "h-12 px-6",
+ },
+ },
+ defaultVariants: {
+ variant: "contained",
+ size: "default",
+ },
+ },
+);
+
+export interface ButtonProps
+ extends
+ ButtonHTMLAttributes,
+ VariantProps {
+ children?: ReactNode;
+ isLoading?: boolean;
+ leadingIcon?: ReactNode;
+ icon?: ElementType;
+ iconClassName?: string;
+}
+
+export const Button = forwardRef(
+ (
+ {
+ className,
+ variant,
+ size,
+ isLoading,
+ leadingIcon,
+ icon: Icon,
+ iconClassName,
+ children,
+ ...props
+ },
+ ref,
+ ) => {
+ // Adjust size variant logic if icon is present?
+ // The spec says "Padding (With Icon): 24px".
+ // I'll handle this dynamically or assume the user passes size="icon" if they want that exact padding,
+ // OR I can conditionally apply the class.
+
+ const computedSize = leadingIcon ? "icon" : size;
+
+ return (
+
+ );
+ },
+);
+Button.displayName = "Button";
+Button.displayName = "Button";
diff --git a/packages/ui/src/components/button/icon-button.tsx b/packages/ui/src/components/button/icon-button.tsx
new file mode 100644
index 0000000..7c5cf55
--- /dev/null
+++ b/packages/ui/src/components/button/icon-button.tsx
@@ -0,0 +1,52 @@
+"use client";
+
+import { forwardRef, ElementType } from "react";
+import { Button, ButtonProps } from "./button";
+import { cn } from "../../lib/utils";
+
+export interface IconButtonProps extends Omit<
+ ButtonProps,
+ "children" | "leadingIcon" | "icon" | "size"
+> {
+ icon: ElementType;
+ size?: "default" | "sm" | "lg";
+}
+
+export const IconButton = forwardRef(
+ ({ icon: Icon, className, size = "default", ...props }, ref) => {
+ // Map abstract sizes to dimensions if needed, or just rely on Button's size but override padding.
+ // User requested "same padding on x and padding on y".
+ // We'll use Button's variant styles but force a square aspect ratio and centered content.
+
+ // size mapping to Button's size prop or just classes?
+ // Button 'icon' size is h-[48px] px-[24px].
+ // If we want square, we want h-[48px] w-[48px] (p-0 or centered flex).
+
+ // Let's assume 'default' maps to 48px square.
+
+ const sizeClasses = {
+ default: "h-[48px] w-[48px] p-0", // 48px
+ sm: "h-[36px] w-[36px] p-0",
+ lg: "h-[56px] w-[56px] p-0",
+ };
+
+ // If user passed a size not in our map (if we allowed string), we'd fallback.
+ // Since we omit 'size' from ButtonProps and redefine it, we control it.
+
+ return (
+
+ );
+ },
+);
+IconButton.displayName = "IconButton";
diff --git a/packages/ui/src/components/button/index.ts b/packages/ui/src/components/button/index.ts
new file mode 100644
index 0000000..399b1dc
--- /dev/null
+++ b/packages/ui/src/components/button/index.ts
@@ -0,0 +1,2 @@
+export * from "./button";
+export * from "./icon-button";
diff --git a/packages/ui/src/components/capsule-message/capsule.tsx b/packages/ui/src/components/capsule-message/capsule.tsx
new file mode 100644
index 0000000..742ac52
--- /dev/null
+++ b/packages/ui/src/components/capsule-message/capsule.tsx
@@ -0,0 +1,56 @@
+import React from "react";
+import { cn } from "../../lib/utils";
+
+export interface CapsuleProps extends React.HTMLAttributes {
+ children?: React.ReactNode;
+ text?: string;
+ className?: string;
+ type?: "message" | "link";
+ href?: string;
+}
+
+export function Capsule({
+ text,
+ children,
+ className,
+ type = "message",
+ href,
+ ...props
+}: CapsuleProps) {
+ const content = text || children;
+ const commonClasses = cn(
+ "flex w-fit items-center justify-center rounded-full",
+ "bg-surface border border-border-strong",
+ "px-4 py-1.5",
+ "text-foreground font-sans text-small-medium",
+ "shadow-small transition-colors hover:opacity-90",
+ className,
+ );
+
+ if (type === "link" && href) {
+ return (
+ )}
+ >
+ {content}
+
+ );
+ }
+
+ return (
+
)}
+ >
+ {content}
+
+ );
+}
+
+// Keeping the default export compatible if possible, or we can switch to named export preference.
+// The previous file had `export default function CapsuleMessage`.
+// I will keep a named export `Capsule` as primary, and alias default for backward compatibility if needed,
+// but the user asked for "Create a simple simple capsule", so I'll prioritize the new name.
+export default Capsule;
diff --git a/packages/ui/src/card.tsx b/packages/ui/src/components/card/card.tsx
similarity index 89%
rename from packages/ui/src/card.tsx
rename to packages/ui/src/components/card/card.tsx
index 7b98893..6769d3c 100644
--- a/packages/ui/src/card.tsx
+++ b/packages/ui/src/components/card/card.tsx
@@ -1,4 +1,4 @@
-import { type JSX } from "react";
+import { ReactNode } from "react";
export function Card({
className,
@@ -10,7 +10,7 @@ export function Card({
title: string;
children: React.ReactNode;
href: string;
-}): JSX.Element {
+}): ReactNode {
return (
{
+ label: string;
+ error?: string;
+ containerClassName?: string;
+}
+
+export const Input = forwardRef(
+ ({ className, containerClassName, label, error, ...props }, ref) => {
+ return (
+