diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index f53f78c9..82466215 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -8,19 +8,19 @@ jobs: runs-on: ubuntu-latest permissions: - contents: write # Required to push to the repo + contents: write # Required to push to the repo steps: - name: Checkout code uses: actions/checkout@v4 with: - ref: ${{ github.head_ref }} # Checkout PR branch + ref: ${{ github.head_ref }} # Checkout PR branch - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' - cache: 'npm' + node-version: "20" + cache: "npm" - name: Install dependencies run: npm ci diff --git a/app/globals.css b/app/globals.css index 9a7b4790..6d808b05 100644 --- a/app/globals.css +++ b/app/globals.css @@ -2,24 +2,32 @@ @import "react-day-picker/style.css"; :root { - --background: #f5f5f5; + --background: #ffffff; --foreground: #3e3c53; + --bone-base: #e9deca; + --red-base: #ff5c5c; + --white-base: #ffffff; + --bone: #e9deca; + --lion: #deb887; + --violet: #3e3c53; + --stone: #9ca3af; + --red: #ff6b6b; --calendar-accent: var(--color-blue); --calendar-accent-background: var(--color-blue-100); } @media (prefers-color-scheme: dark) { :root { - --background: #3e3c53; - --foreground: #e9deca; + --background: var(--violet); + --foreground: var(--bone); --calendar-accent: var(--color-red); --calendar-accent-background: var(--color-red-200); } } .dark { - --background: #3e3c53; - --foreground: #e9deca; + --background: var(--violet); + --foreground: var(--bone); --calendar-accent: var(--color-red); --calendar-accent-background: var(--color-red-200); } @@ -27,6 +35,53 @@ body { background: var(--background); color: var(--foreground); + font-family: var(--font-nunito); +} + +.font-display { + font-family: var(--font-modak); + letter-spacing: -0.02em; + line-height: 0.9; +} + +.text-bone { + color: var(--bone); +} + +.text-lion { + color: var(--lion); +} + +.text-violet { + color: var(--violet); +} + +.text-outline { + -webkit-text-stroke: 2px currentColor; + color: transparent; +} + +.text-outline-dark { + -webkit-text-stroke: 2px var(--violet); + color: transparent; + text-shadow: 2px 2px 0px var(--violet); +} + +.text-outline-light { + -webkit-text-stroke: 2px var(--bone); + color: transparent; + text-shadow: 2px 2px 0px var(--bone); +} + +.text-outline-golden { + -webkit-text-stroke: 2px var(--lion); + color: transparent; +} + +.bubble-text { + font-family: var(--font-modak); + letter-spacing: 0.05em; + line-height: 1.1; } .rdp-root { diff --git a/app/layout.tsx b/app/layout.tsx index f64983ca..d33c5236 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -19,28 +19,22 @@ const nunito = Nunito({ }); export const metadata: Metadata = { - title: "tomeeto", - description: "to meet or not to meet", + title: "plancake", + description: "Stacking up perfect plans, one pancake at a time", }; export default function RootLayout({ children, -}: Readonly<{ +}: { children: React.ReactNode; -}>) { +}) { return ( - - -
- -
- {children} - -
+ + + +
+ {children} + ); diff --git a/app/page.tsx b/app/page.tsx index 2e1f2bde..b886929c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,74 +1,37 @@ -import Link from "next/link"; - -const features = [ - { - title: "Smart Planning", - description: - "Intelligently suggest optimal meeting times based on everyone's availability and preferences.", - icon: "🧠", - }, - { - title: "Easy Polling", - description: - "Create polls for time slots and let participants vote on their preferences with visual feedback.", - icon: "📊", - }, - { - title: "Seamless Sharing", - description: - "Share a simple link to collect availability from unlimited participants across time zones.", - icon: "🌍", - }, -]; +"use client"; -const steps = [ - { - step: "01", - title: "Mix Your Event", - description: - "Set up your meeting details, add time options, and customize your preferences", - }, - { - step: "02", - title: "Share & Stack", - description: - "Send the link to participants and watch responses stack up in real-time", - }, - { - step: "03", - title: "Flip & Serve", - description: - "Review the results, pick the best time, and serve up calendar invites to all", - }, -]; +import Link from "next/link"; export default function Home() { return ( -
+
{/* Hero Section */} -
+
-
- 🥞 -
-

- planning made - stack simple +

+ + planning made + + + stack +
+ simple +

-

+

The fluffiest way to coordinate schedules and plan group events. Stack up availability and serve the perfect meeting time.

-
+
- Mix Your First Plan + Mix your first plan View Dashboard @@ -76,95 +39,129 @@ export default function Home() {
- {/* Features Section */} -
+ {/* Why Plancake Section */} +
-
-

Why Choose plancake?

-

- Fluffy, simple planning that stacks up perfectly. No more burnt - schedules or half-baked meetings. -

-
+
+ {/* Pancake emoji - centered on mobile */} +
+
🥞
+
-
- {features.map((feature, index) => ( -
-
{feature.icon}
-

- {feature.title} -

-

{feature.description}

+ {/* Content - centered on mobile, left-aligned on desktop */} +
+

+ why +
+ plancake? +

+
+
+

+ Smart Planning +

+

+ Intelligently suggest optimal meeting times based on + everyone's availability and preferences. +

+
+
+

+ Easy Coordination +

+

+ Share a simple link and watch as responses stack up in + real-time without the back-and-forth. +

+
+
+

+ Perfect Results +

+

+ Get the ideal meeting time that works for everyone with + automatic calendar integration. +

+
- ))} +
- {/* How It Works Section */} -
-
-
-

Golden Stack Recipe

-

+ {/* Golden Stack Recipe */} +

+
+
+

+ golden +
+ stack recipe +

+

Follow these simple steps to cook up the perfect schedule every time.

-
- {steps.map((step, index) => ( -
-
- {step.step} +
+
+
+
+ 🍳 +
+

Mix your event

+

+ Set up your meeting details, add time options, and customize + your preferences +

+
+
+
+ 📤 +
+

Share & Stack

+

+ Send the link to participants and watch responses stack up in + real-time +

+
+
+
+ 🥞
-

{step.title}

-

{step.description}

+

Flip & Serve

+

+ Review the results, pick the best time, and serve up calendar + invites to all +

- ))} +
- {/* CTA Section */} -
-
-
- 🥞 + {/* Plan Today Section */} +
+
+

PLAN TODAY

+
+ + Start Planning +
-

- Ready to Stack Up Success? -

-

- Join teams worldwide who discovered the fluffiest way to plan - together. -

- - Start Cooking Today 🥞 - -
-
- - {/* Footer */} -
-
-
-

🥞 plancake

-

+

+
plancake
+

© 2025 plancake. Stacking up perfect plans, one pancake at a time.

-
-
+
+
); } diff --git a/app/ui/components/hamburger-menu.tsx b/app/ui/components/hamburger-menu.tsx new file mode 100644 index 00000000..736f5f79 --- /dev/null +++ b/app/ui/components/hamburger-menu.tsx @@ -0,0 +1,79 @@ +import React, { useState } from "react"; +import Link from "next/link"; + +export default function HamburgerMenu() { + const [isOpen, setIsOpen] = useState(false); + + const toggleMenu = () => setIsOpen(!isOpen); + + return ( +
+ + + {isOpen && ( +
+ {/* Vertical Line */} +
+ + {/* Dropdown Menu */} +
+
    +
  • + + Mix Your First Plan + +
  • +
  • + + Dashboard + +
  • +
  • + + About Plancake + +
  • +
  • + + Login/Signup + +
  • +
+
+
+ )} +
+ ); +} diff --git a/app/ui/components/logo.tsx b/app/ui/components/logo.tsx new file mode 100644 index 00000000..a2a95b59 --- /dev/null +++ b/app/ui/components/logo.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { useEffect, useState } from "react"; +import HamburgerMenu from "./hamburger-menu"; + +export default function Logo() { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return null; + } + + return ( +
+ {/* Background shape */} +
+ + {/* Text Container */} +
+
+ plan +
+
+ cake +
+ +
+
+ ); +} diff --git a/app/ui/components/schedule/schedule-grid.tsx b/app/ui/components/schedule/schedule-grid.tsx index 57457c6d..b8995c6e 100644 --- a/app/ui/components/schedule/schedule-grid.tsx +++ b/app/ui/components/schedule/schedule-grid.tsx @@ -130,18 +130,69 @@ export default function ScheduleGrid({ } return ( -
- {/* Column Headers */} -
+
+ {/* Arrows */} + {currentPage > 0 && ( + + )} + {currentPage < totalPages - 1 && ( + + )} + + {/* Grid */}
+ {Array.from({ + length: (numHours + 1) * (visibleDays.length + 2) + 1, + }).map((_, i) => ( +
+ ))} +
+ + {/* Time labels */} +
+
+ {Array.from({ length: numHours }).map((_, i) => { + const hour = timeRange.from + ? new Date(timeRange.from.getTime() + i * 3600000) + : new Date(); + const formatter = new Intl.DateTimeFormat("en-US", { + timeZone: timezone, + hour: "numeric", + hour12: true, + }); + return ( +
+ {formatter.format(hour)} +
+ ); + })} +
+ + {/* Column headers */} +
{visibleDays.map((day, i) => { const type = eventRange.type; @@ -173,49 +224,8 @@ export default function ScheduleGrid({
- {/* Left Arrow */} - {currentPage > 0 && ( - - )} - - {/* Right Arrow */} - {currentPage < totalPages - 1 && ( - - )} - - {/* Grid Layer */} -
- {timeBlocks?.map((block, i) => ( - 1 && block.startHour !== block.endHour - } - blockNumber={i} - userTimezone={timezone} - availability={availability} - onToggle={handleToggle} - /> - ))} -
+ {/* Right border */} +
); } diff --git a/app/ui/layout/header.tsx b/app/ui/layout/header.tsx index 70f93a75..fb153581 100644 --- a/app/ui/layout/header.tsx +++ b/app/ui/layout/header.tsx @@ -1,25 +1,18 @@ -import Image from "next/image"; -import ToggleDarkMode from "./theme-toggle"; +import Link from "next/link"; +import Logo from "../components/logo"; +import ThemeToggle from "./theme-toggle"; + export default function Header() { return ( - + <> + + + {/* Fixed theme toggle */} + + ); } diff --git a/app/ui/layout/theme-toggle.tsx b/app/ui/layout/theme-toggle.tsx index b8c0a95f..a3191f68 100644 --- a/app/ui/layout/theme-toggle.tsx +++ b/app/ui/layout/theme-toggle.tsx @@ -3,32 +3,42 @@ import { FiSun, FiMoon } from "react-icons/fi"; import { useState, useEffect } from "react"; import { useTheme } from "next-themes"; -import Image from "next/image"; -export default function ToggleDarkMode() { +export default function FixedThemeToggle() { const [mounted, setMounted] = useState(false); const { setTheme, resolvedTheme } = useTheme(); - useEffect(() => setMounted(true), []); + useEffect(() => { + setMounted(true); + }, []); - if (!mounted) + // Don't render anything until mounted to prevent hydration mismatch + if (!mounted) { return ( - Loading Light/Dark Toggle +
+
+
+
+
); - - if (resolvedTheme === "dark") { - return setTheme("light")} />; } - if (resolvedTheme === "light") { - return setTheme("dark")} />; - } + const toggleTheme = () => { + setTheme(resolvedTheme === "dark" ? "light" : "dark"); + }; + + return ( + + ); } diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..d1cd40d4 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,42 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./app/**/*.{js,ts,jsx,tsx,mdx}", + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./components/**/*.{js,ts,jsx,tsx,mdx}", + ], + darkMode: "class", + theme: { + extend: { + colors: { + bone: { + DEFAULT: "var(--bone)", + base: "var(--bone)", + }, + lion: "var(--lion)", + violet: "var(--violet)", + stone: { + 400: "var(--stone)", + }, + red: { + base: "var(--red)", + 500: "var(--red)", + }, + }, + fontFamily: { + modak: ["var(--font-modak)"], + nunito: ["var(--font-nunito)"], + }, + gridTemplateColumns: { + 1: "repeat(1, 1fr)", + 2: "repeat(2, 1fr)", + 3: "repeat(3, 1fr)", + 4: "repeat(4, 1fr)", + 5: "repeat(5, 1fr)", + 6: "repeat(6, 1fr)", + 7: "repeat(7, 1fr)", + }, + }, + }, + plugins: [], +};