Initial commit

This commit is contained in:
DK
2026-01-22 17:14:00 +00:00
commit b20665fcc3
7 changed files with 2339 additions and 0 deletions

2
.env Normal file
View File

@@ -0,0 +1,2 @@
NEXT_PUBLIC_API_URL=https://dev.api.webild.io
NEXT_PUBLIC_PROJECT_ID=969407a9-8445-44c0-93c0-eab8fcca1f34

View File

@@ -0,0 +1,57 @@
name: Code Check
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to check'
required: true
default: 'main'
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout branch
uses: actions/checkout@v3
with:
ref: ${{ gitea.event.inputs.branch }}
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 24
- name: Install dependencies
run: |
set -euo pipefail
npm ci --prefer-offline --no-audit 2>&1 | tee install.log
env:
NODE_OPTIONS: '--max-old-space-size=4096'
- name: TypeScript check
run: |
set -euo pipefail
npm run typecheck 2>&1 | tee build.log
- name: ESLint check
run: |
set -euo pipefail
npm run lint 2>&1 | tee build.log
- name: Upload build log on failure
if: failure()
uses: actions/upload-artifact@v3
with:
name: build-log
path: build.log
if-no-files-found: ignore
- name: Check completed
if: success()
run: echo "Typecheck and lint passed successfully"

163
src/app/blog/page.tsx Normal file
View File

@@ -0,0 +1,163 @@
"use client";
import { useEffect, useState } from "react";
import ReactLenis from "lenis/react";
import BlogCardTwo from '@/components/sections/blog/BlogCardTwo';
import FooterSimple from '@/components/sections/footer/FooterSimple';
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarLayoutFloatingInline from '@/components/navbar/NavbarLayoutFloatingInline';
type BlogPost = {
id: string;
category: string | string[];
title: string;
excerpt: string;
imageSrc: string;
imageAlt?: string;
authorName: string;
authorAvatar?: string;
date: string;
onBlogClick?: () => void;
};
const defaultPosts: BlogPost[] = [
{
id: "1", category: ["Coffee", "Brewing"],
title: "The Art of Coffee Brewing", excerpt: "Discover the perfect techniques for brewing exceptional coffee that brings out the best flavors in every cup.", imageSrc: "/placeholders/placeholder3.avif", imageAlt: "Coffee brewing equipment", authorName: "Elena Rodriguez", authorAvatar: "/placeholders/placeholder3.avif", date: "20 Jan 2025", onBlogClick: () => console.log("Blog 1 clicked"),
},
{
id: "2", category: "Roasting", title: "From Bean to Cup: Our Roasting Process", excerpt: "Take a behind-the-scenes look at our meticulous roasting process that creates the perfect coffee experience.", imageSrc: "/placeholders/placeholder4.webp", imageAlt: "Coffee roasting process", authorName: "Marcus Chen", authorAvatar: "/placeholders/placeholder4.webp", date: "18 Jan 2025", onBlogClick: () => console.log("Blog 2 clicked"),
},
{
id: "3", category: ["Origins", "Sustainability"],
title: "Sustainable Coffee Sourcing", excerpt: "Learn about our commitment to ethical sourcing and how we support coffee farmers around the world.", imageSrc: "/placeholders/placeholder3.avif", imageAlt: "Coffee farm landscape", authorName: "Sarah Martinez", authorAvatar: "/placeholders/placeholder3.avif", date: "15 Jan 2025", onBlogClick: () => console.log("Blog 3 clicked"),
},
{
id: "4", category: "Culture", title: "Coffee Culture Around the World", excerpt: "Explore how different cultures celebrate and enjoy coffee, from Italian espresso to Ethiopian ceremonies.", imageSrc: "/placeholders/placeholder4.webp", imageAlt: "Global coffee culture", authorName: "Ahmed Hassan", authorAvatar: "/placeholders/placeholder4.webp", date: "12 Jan 2025", onBlogClick: () => console.log("Blog 4 clicked"),
},
];
export default function BlogPage() {
const [posts, setPosts] = useState<BlogPost[]>(defaultPosts);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchPosts = async () => {
try {
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
const projectId = process.env.NEXT_PUBLIC_PROJECT_ID;
if (!apiUrl || !projectId) {
console.warn("NEXT_PUBLIC_API_URL or NEXT_PUBLIC_PROJECT_ID not configured, using default posts");
setIsLoading(false);
return;
}
const url = `${apiUrl}/posts/${projectId}?status=published`;
const response = await fetch(url, {
method: "GET", headers: {
"Content-Type": "application/json"},
});
if (response.ok) {
const resp = await response.json();
const data = resp.data;
if (Array.isArray(data) && data.length > 0) {
const mappedPosts = data.map((post: any) => ({
id: post.id || String(Math.random()),
category: post.category || "General", title: post.title || "Untitled", excerpt: post.excerpt || post.content.slice(0, 30) || "", imageSrc: post.imageUrl || "/placeholders/placeholder3.avif", imageAlt: post.imageAlt || post.title || "", authorName: post.author?.name || "Anonymous", authorAvatar: post.author?.avatar || "/placeholders/placeholder3.avif", date: post.date || post.createdAt || new Date().toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" }),
onBlogClick: () => console.log(`Blog ${post.id} clicked`),
}));
setPosts(mappedPosts);
}
} else {
console.warn(`API request failed with status ${response.status}, using default posts`);
}
} catch (error) {
console.error("Error fetching posts:", error);
} finally {
setIsLoading(false);
}
};
fetchPosts();
}, []);
return (
<ThemeProvider
defaultButtonVariant="hover-bubble"
defaultTextAnimation="background-highlight"
borderRadius="soft"
contentWidth="smallMedium"
sizing="large"
background="aurora"
cardStyle="gradient-radial"
primaryButtonStyle="flat"
secondaryButtonStyle="layered"
headingFontWeight="extrabold"
>
<ReactLenis root>
<div className="min-h-screen bg-background">
<NavbarLayoutFloatingInline
brandName="BrewHaven"
navItems={[
{ name: "Home", id: "/home" },
{ name: "About", id: "about" },
{ name: "Menu", id: "products" },
{ name: "Process", id: "features" },
{ name: "Contact", id: "contact" }
]}
button={{ text: "Visit Us", href: "contact" }}
/>
{isLoading ? (
<div className="w-content-width mx-auto py-20 text-center">
<p className="text-foreground">Loading posts...</p>
</div>
) : (
<BlogCardTwo
blogs={posts}
title="Coffee Chronicles"
description="Discover the stories behind every cup, from bean to brew and everything in between"
textboxLayout="default"
useInvertedBackground="noInvert"
animationType="slide-up"
carouselMode="buttons"
tag="Blog"
/>
)}
<FooterSimple
columns={[
{
title: "Navigate", items: [
{ label: "Home", href: "#hero" },
{ label: "About", href: "#about" },
{ label: "Menu", href: "#products" },
{ label: "Contact", href: "#contact" }
]
},
{
title: "Visit Us", items: [
{ label: "123 Coffee Lane, Brew City, BC 45678" },
{ label: "Mon-Fri: 6:30am - 8:00pm" },
{ label: "Sat-Sun: 8:00am - 9:00pm" },
{ label: "(555) 123-4567" }
]
},
{
title: "Follow Us", items: [
{ label: "Instagram", href: "https://instagram.com" },
{ label: "Facebook", href: "https://facebook.com" },
{ label: "Twitter", href: "https://twitter.com" }
]
}
]}
bottomLeftText="© 2025 BrewHaven Coffee. All rights reserved."
bottomRightText="Roasting Excellence, Brewing Passion"
/>
</div>
</ReactLenis>
</ThemeProvider>
);
}

595
src/app/globals.css Normal file
View File

@@ -0,0 +1,595 @@
@import "tailwindcss";
:root {
/* Base units */
/* --vw is set by ThemeProvider */
/* --background: #f5f5f5;;
--card: #ffffff;;
--foreground: #1c1c1c;;
--primary-cta: #159c49;;
--secondary-cta: #ffffff;;
--accent: #159c49;;
--background-accent: #a8e8ba;; */
--background: #f5f5f5;;
--card: #ffffff;;
--foreground: #1c1c1c;;
--primary-cta: #159c49;;
--secondary-cta: #ffffff;;
--accent: #159c49;;
--background-accent: #a8e8ba;;
/* text sizing - set by ThemeProvider */
/* --text-2xs: clamp(0.465rem, 0.62vw, 0.62rem);
--text-xs: clamp(0.54rem, 0.72vw, 0.72rem);
--text-sm: clamp(0.615rem, 0.82vw, 0.82rem);
--text-base: clamp(0.69rem, 0.92vw, 0.92rem);
--text-lg: clamp(0.75rem, 1vw, 1rem);
--text-xl: clamp(0.825rem, 1.1vw, 1.1rem);
--text-2xl: clamp(0.975rem, 1.3vw, 1.3rem);
--text-3xl: clamp(1.2rem, 1.6vw, 1.6rem);
--text-4xl: clamp(1.5rem, 2vw, 2rem);
--text-5xl: clamp(2.025rem, 2.75vw, 2.75rem);
--text-6xl: clamp(2.475rem, 3.3vw, 3.3rem);
--text-7xl: clamp(3rem, 4vw, 4rem);
--text-8xl: clamp(3.5rem, 4.5vw, 4.5rem);
--text-9xl: clamp(5.25rem, 7vw, 7rem); */
/* Base spacing units */
--vw-0_25: calc(var(--vw) * 0.25);
--vw-0_5: calc(var(--vw) * 0.5);
--vw-0_625: calc(var(--vw) * 0.625);
--vw-0_75: calc(var(--vw) * 0.75);
--vw-1: calc(var(--vw) * 1);
--vw-1_25: calc(var(--vw) * 1.25);
--vw-1_5: calc(var(--vw) * 1.5);
--vw-1_75: calc(var(--vw) * 1.75);
--vw-2: calc(var(--vw) * 2);
--vw-2_25: calc(var(--vw) * 2.25);
--vw-2_5: calc(var(--vw) * 2.5);
--vw-2_75: calc(var(--vw) * 2.75);
--vw-3: calc(var(--vw) * 3);
/* width */
--width-5: clamp(4rem, 5vw, 6rem);
--width-7_5: clamp(5.625rem, 7.5vw, 7.5rem);
--width-10: clamp(7.5rem, 10vw, 10rem);
--width-12_5: clamp(9.375rem, 12.5vw, 12.5rem);
--width-15: clamp(11.25rem, 15vw, 15rem);
--width-17: clamp(12.75rem, 17vw, 17rem);
--width-17_5: clamp(13.125rem, 17.5vw, 17.5rem);
--width-20: clamp(15rem, 20vw, 20rem);
--width-21: clamp(15.75rem, 21vw, 21rem);
--width-22_5: clamp(16.875rem, 22.5vw, 22.5rem);
--width-25: clamp(18.75rem, 25vw, 25rem);
--width-26: clamp(19.5rem, 26vw, 26rem);
--width-27_5: clamp(20.625rem, 27.5vw, 27.5rem);
--width-30: clamp(22.5rem, 30vw, 30rem);
--width-32_5: clamp(24.375rem, 32.5vw, 32.5rem);
--width-35: clamp(26.25rem, 35vw, 35rem);
--width-37_5: clamp(28.125rem, 37.5vw, 37.5rem);
--width-40: clamp(30rem, 40vw, 40rem);
--width-42_5: clamp(31.875rem, 42.5vw, 42.5rem);
--width-45: clamp(33.75rem, 45vw, 45rem);
--width-47_5: clamp(35.625rem, 47.5vw, 47.5rem);
--width-50: clamp(37.5rem, 50vw, 50rem);
--width-52_5: clamp(39.375rem, 52.5vw, 52.5rem);
--width-55: clamp(41.25rem, 55vw, 55rem);
--width-57_5: clamp(43.125rem, 57.5vw, 57.5rem);
--width-60: clamp(45rem, 60vw, 60rem);
--width-62_5: clamp(46.875rem, 62.5vw, 62.5rem);
--width-65: clamp(48.75rem, 65vw, 65rem);
--width-67_5: clamp(50.625rem, 67.5vw, 67.5rem);
--width-70: clamp(52.5rem, 70vw, 70rem);
--width-72_5: clamp(54.375rem, 72.5vw, 72.5rem);
--width-75: clamp(56.25rem, 75vw, 75rem);
--width-77_5: clamp(58.125rem, 77.5vw, 77.5rem);
--width-80: clamp(60rem, 80vw, 80rem);
--width-82_5: clamp(61.875rem, 82.5vw, 82.5rem);
--width-85: clamp(63.75rem, 85vw, 85rem);
--width-87_5: clamp(65.625rem, 87.5vw, 87.5rem);
--width-90: clamp(67.5rem, 90vw, 90rem);
--width-92_5: clamp(69.375rem, 92.5vw, 92.5rem);
--width-95: clamp(71.25rem, 95vw, 95rem);
--width-97_5: clamp(73.125rem, 97.5vw, 97.5rem);
--width-100: clamp(75rem, 100vw, 100rem);
/* --width-content-width and --width-content-width-expanded are set by ThemeProvider */
--width-carousel-padding: calc((100vw - var(--width-content-width)) / 2 + 1px - var(--vw-1_5));
--width-carousel-padding-controls: calc((100vw - var(--width-content-width)) / 2 + 1px);
--width-carousel-padding-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px - var(--vw-1_5));
--width-carousel-padding-controls-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px);
--width-carousel-item-3: calc(var(--width-content-width) / 3 - var(--vw-1_5) / 3 * 2);
--width-carousel-item-4: calc(var(--width-content-width) / 4 - var(--vw-1_5) / 4 * 3);
--width-x-padding-mask-fade: clamp(1.5rem, 4vw, 4rem);
--height-4: 1rem;
--height-5: 1.25rem;
--height-6: 1.5rem;
--height-7: 1.75rem;
--height-8: 2rem;
--height-9: 2.25rem;
--height-10: 2.5rem;
--height-11: 2.75rem;
--height-12: 3rem;
--height-30: 7.5rem;
--height-90: 22.5rem;
--height-100: 25rem;
--height-110: 27.5rem;
--height-120: 30rem;
--height-130: 32.5rem;
--height-140: 35rem;
--height-150: 37.5rem;
/* hero page padding */
--padding-hero-page-padding-half: calc((var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10)) / 2);
--padding-hero-page-padding: calc(var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10));
--padding-hero-page-padding-1_5: calc(1.5 * (var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10)));
--padding-hero-page-padding-double: calc(2 * (var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10)));
}
@media (max-width: 767px) {
:root {
/* --vw and text sizing are set by ThemeProvider */
/* --vw: 3vw;
--text-2xs: 2.5vw;
--text-xs: 2.75vw;
--text-sm: 3vw;
--text-base: 3.25vw;
--text-lg: 3.5vw;
--text-xl: 4.25vw;
--text-2xl: 5vw;
--text-3xl: 6vw;
--text-4xl: 7vw;
--text-5xl: 7.5vw;
--text-6xl: 8.5vw;
--text-7xl: 10vw;
--text-8xl: 12vw;
--text-9xl: 14vw; */
--width-5: 5vw;
--width-7_5: 7.5vw;
--width-10: 10vw;
--width-12_5: 12.5vw;
--width-15: 15vw;
--width-17_5: 17.5vw;
--width-20: 20vw;
--width-22_5: 22.5vw;
--width-25: 25vw;
--width-27_5: 27.5vw;
--width-30: 30vw;
--width-32_5: 32.5vw;
--width-35: 35vw;
--width-37_5: 37.5vw;
--width-40: 40vw;
--width-42_5: 42.5vw;
--width-45: 45vw;
--width-47_5: 47.5vw;
--width-50: 50vw;
--width-52_5: 52.5vw;
--width-55: 55vw;
--width-57_5: 57.5vw;
--width-60: 60vw;
--width-62_5: 62.5vw;
--width-65: 65vw;
--width-67_5: 67.5vw;
--width-70: 70vw;
--width-72_5: 72.5vw;
--width-75: 75vw;
--width-77_5: 77.5vw;
--width-80: 80vw;
--width-82_5: 82.5vw;
--width-85: 85vw;
--width-87_5: 87.5vw;
--width-90: 90vw;
--width-92_5: 92.5vw;
--width-95: 95vw;
--width-97_5: 97.5vw;
--width-100: 100vw;
/* --width-content-width and --width-content-width-expanded are set by ThemeProvider */
--width-carousel-padding: calc((100vw - var(--width-content-width)) / 2 + 1px - var(--vw-1_5));
--width-carousel-padding-controls: calc((100vw - var(--width-content-width)) / 2 + 1px);
--width-carousel-padding-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px - var(--vw-1_5));
--width-carousel-padding-controls-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px);
--width-carousel-item-3: var(--width-content-width);
--width-carousel-item-4: var(--width-content-width);
--width-x-padding-mask-fade: 10vw;
--height-4: 3.5vw;
--height-5: 4.5vw;
--height-6: 5.5vw;
--height-7: 6.5vw;
--height-8: 7.5vw;
--height-9: 8.5vw;
--height-10: 9vw;
--height-11: 10vw;
--height-12: 11vw;
--height-30: 25vw;
--height-90: 81vw;
--height-100: 90vw;
--height-110: 99vw;
--height-120: 108vw;
--height-130: 117vw;
--height-140: 126vw;
--height-150: 135vw;
}
}
@theme inline {
--color-background: var(--background);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-primary-cta: var(--primary-cta);
--color-secondary-cta: var(--secondary-cta);
--color-accent: var(--accent);
--color-background-accent: var(--background-accent);
/* theme border radius */
--radius-theme: var(--theme-border-radius);
--radius-theme-capped: var(--theme-border-radius-capped);
/* text */
--text-2xs: var(--text-2xs);
--text-xs: var(--text-xs);
--text-sm: var(--text-sm);
--text-base: var(--text-base);
--text-lg: var(--text-lg);
--text-xl: var(--text-xl);
--text-2xl: var(--text-2xl);
--text-3xl: var(--text-3xl);
--text-4xl: var(--text-4xl);
--text-5xl: var(--text-5xl);
--text-6xl: var(--text-6xl);
--text-7xl: var(--text-7xl);
--text-8xl: var(--text-8xl);
--text-9xl: var(--text-9xl);
/* height */
--height-4: var(--height-4);
--height-5: var(--height-5);
--height-6: var(--height-6);
--height-7: var(--height-7);
--height-8: var(--height-8);
--height-9: var(--height-9);
--height-11: var(--height-11);
--height-12: var(--height-12);
--height-10: var(--height-10);
--height-30: var(--height-30);
--height-90: var(--height-90);
--height-100: var(--height-100);
--height-110: var(--height-110);
--height-120: var(--height-120);
--height-130: var(--height-130);
--height-140: var(--height-140);
--height-150: var(--height-150);
--height-page-padding: calc(2.25rem+var(--vw-1_5)+var(--vw-1_5));
/* width */
--width-5: var(--width-5);
--width-7_5: var(--width-7_5);
--width-10: var(--width-10);
--width-12_5: var(--width-12_5);
--width-15: var(--width-15);
--width-17: var(--width-17);
--width-17_5: var(--width-17_5);
--width-20: var(--width-20);
--width-21: var(--width-21);
--width-22_5: var(--width-22_5);
--width-25: var(--width-25);
--width-26: var(--width-26);
--width-27_5: var(--width-27_5);
--width-30: var(--width-30);
--width-32_5: var(--width-32_5);
--width-35: var(--width-35);
--width-37_5: var(--width-37_5);
--width-40: var(--width-40);
--width-42_5: var(--width-42_5);
--width-45: var(--width-45);
--width-47_5: var(--width-47_5);
--width-50: var(--width-50);
--width-52_5: var(--width-52_5);
--width-55: var(--width-55);
--width-57_5: var(--width-57_5);
--width-60: var(--width-60);
--width-62_5: var(--width-62_5);
--width-65: var(--width-65);
--width-67_5: var(--width-67_5);
--width-70: var(--width-70);
--width-72_5: var(--width-72_5);
--width-75: var(--width-75);
--width-77_5: var(--width-77_5);
--width-80: var(--width-80);
--width-82_5: var(--width-82_5);
--width-85: var(--width-85);
--width-87_5: var(--width-87_5);
--width-90: var(--width-90);
--width-92_5: var(--width-92_5);
--width-95: var(--width-95);
--width-97_5: var(--width-97_5);
--width-100: var(--width-100);
--width-content-width: var(--width-content-width);
--width-carousel-padding: var(--width-carousel-padding);
--width-carousel-padding-controls: var(--width-carousel-padding-controls);
--width-carousel-padding-expanded: var(--width-carousel-padding-expanded);
--width-carousel-padding-controls-expanded: var(--width-carousel-padding-controls-expanded);
--width-carousel-item-3: var(--width-carousel-item-3);
--width-carousel-item-4: var(--width-carousel-item-4);
--width-x-padding-mask-fade: var(--width-x-padding-mask-fade);
--width-content-width-expanded: var(--width-content-width-expanded);
/* gap */
--spacing-1: var(--vw-0_25);
--spacing-2: var(--vw-0_5);
--spacing-3: var(--vw-0_75);
--spacing-4: var(--vw-1);
--spacing-5: var(--vw-1_25);
--spacing-6: var(--vw-1_5);
--spacing-7: var(--vw-1_75);
--spacing-8: var(--vw-2);
--spacing-x-1: var(--vw-0_25);
--spacing-x-2: var(--vw-0_5);
--spacing-x-3: var(--vw-0_75);
--spacing-x-4: var(--vw-1);
--spacing-x-5: var(--vw-1_25);
--spacing-x-6: var(--vw-1_5);
/* border radius */
--radius-none: 0;
--radius-sm: var(--vw-0_5);
--radius: var(--vw-0_75);
--radius-md: var(--vw-1);
--radius-lg: var(--vw-1_25);
--radius-xl: var(--vw-1_75);
--radius-full: 999px;
/* padding */
--padding-1: var(--vw-0_25);
--padding-2: var(--vw-0_5);
--padding-2.5: var(--vw-0_625);
--padding-3: var(--vw-0_75);
--padding-4: var(--vw-1);
--padding-5: var(--vw-1_25);
--padding-6: var(--vw-1_5);
--padding-7: var(--vw-1_75);
--padding-8: var(--vw-2);
--padding-x-1: var(--vw-0_25);
--padding-x-2: var(--vw-0_5);
--padding-x-3: var(--vw-0_75);
--padding-x-4: var(--vw-1);
--padding-x-5: var(--vw-1_25);
--padding-x-6: var(--vw-1_5);
--padding-x-7: var(--vw-1_75);
--padding-x-8: var(--vw-2);
--padding-hero-page-padding-half: var(--padding-hero-page-padding-half);
--padding-hero-page-padding: var(--padding-hero-page-padding);
--padding-hero-page-padding-1_5: var(--padding-hero-page-padding-1_5);
--padding-hero-page-padding-double: var(--padding-hero-page-padding-double);
/* margin */
--margin-1: var(--vw-0_25);
--margin-2: var(--vw-0_5);
--margin-3: var(--vw-0_75);
--margin-4: var(--vw-1);
--margin-5: var(--vw-1_25);
--margin-6: var(--vw-1_5);
--margin-7: var(--vw-1_75);
--margin-8: var(--vw-2);
--margin-x-1: var(--vw-0_25);
--margin-x-2: var(--vw-0_5);
--margin-x-3: var(--vw-0_75);
--margin-x-4: var(--vw-1);
--margin-x-5: var(--vw-1_25);
--margin-x-6: var(--vw-1_5);
--margin-x-7: var(--vw-1_75);
--margin-x-8: var(--vw-2);
}
@layer components {}
@layer utilities {
/* Card, primary-button, and secondary-button styles are now dynamically injected via ThemeProvider */
/* .card {
@apply backdrop-blur-sm bg-gradient-to-br from-card/80 to-card/40 shadow-sm border border-card;
}
.primary-button {
@apply bg-gradient-to-b from-primary-cta/83 to-primary-cta;
box-shadow:
color-mix(in srgb, var(--color-background) 25%, transparent) 0px 1px 1px 0px inset,
color-mix(in srgb, var(--color-primary-cta) 15%, transparent) 3px 3px 3px 0px;
}
.secondary-button {
@apply backdrop-blur-sm bg-gradient-to-br from-secondary-cta/80 to-secondary-cta shadow-sm border border-secondary-cta;
} */
.tag-card {
@apply backdrop-blur-sm bg-gradient-to-br from-card/80 to-card/40 shadow-sm border border-card;
}
.mask-padding-x {
-webkit-mask-image: linear-gradient(to right, transparent 0%, black var(--width-x-padding-mask-fade), black calc(100% - var(--width-x-padding-mask-fade)), transparent 100%);
mask-image: linear-gradient(to right, transparent 0%, black var(--width-x-padding-mask-fade), black calc(100% - var(--width-x-padding-mask-fade)), transparent 100%);
}
.mask-fade-bottom {
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 100%);
mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 100%);
}
.mask-fade-y {
mask-image: linear-gradient(to bottom,
transparent 0%,
black var(--vw-1_5),
black calc(100% - var(--vw-1_5)),
transparent 100%);
}
.mask-fade-bottom-large {
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 75%, transparent 100%);
mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 75%, transparent 100%);
}
.mask-fade-bottom-long {
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 5%, transparent 100%);
mask-image: linear-gradient(to bottom, black 0%, black 5%, transparent 100%);
}
.mask-fade-top-long {
-webkit-mask-image: linear-gradient(to top, black 0%, black 5%, transparent 100%);
mask-image: linear-gradient(to top, black 0%, black 5%, transparent 100%);
}
.mask-fade-xy {
-webkit-mask-image:
linear-gradient(to right, transparent 0%, black 20%, black 80%, transparent 100%),
linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
mask-image:
linear-gradient(to right, transparent 0%, black 20%, black 80%, transparent 100%),
linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%);
-webkit-mask-composite: source-in;
mask-composite: intersect;
}
/* ANIMATION */
.animation-container {
animation:
fadeInOpacity 0.8s ease-in-out forwards,
fadeInTranslate 0.6s forwards;
}
.animation-container-fade {
animation: fadeInOpacity 0.8s ease-in-out forwards;
}
@keyframes fadeInOpacity {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeInTranslate {
from {
transform: translateY(0.75vh);
}
to {
transform: translateY(0vh);
}
}
@keyframes aurora {
from {
background-position: 50% 50%, 50% 50%;
}
to {
background-position: 350% 50%, 350% 50%;
}
}
@keyframes spin-slow {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes spin-reverse {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
.animate-spin-slow {
animation: spin-slow 15s linear infinite;
}
.animate-spin-reverse {
animation: spin-reverse 10s linear infinite;
}
@keyframes marquee-vertical {
from {
transform: translateY(0);
}
to {
transform: translateY(-50%);
}
}
.animate-marquee-vertical {
animation: marquee-vertical 20s linear infinite;
}
@keyframes orbit {
from {
transform: rotate(var(--initial-position, 0deg)) translateX(var(--translate-position, 120px)) rotate(calc(-1 * var(--initial-position, 0deg)));
}
to {
transform: rotate(calc(var(--initial-position, 0deg) + 360deg)) translateX(var(--translate-position, 120px)) rotate(calc(-1 * (var(--initial-position, 0deg) + 360deg)));
}
}
@keyframes map-dot-pulse {
0%, 100% {
transform: scale(0.4);
opacity: 0.6;
}
50% {
transform: scale(1.4);
opacity: 1;
}
}
}
* {
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 1) rgba(255, 255, 255, 0);
}
html {
overscroll-behavior: none;
overscroll-behavior-y: none;
}
body {
background-color: var(--background);
color: var(--foreground);
font-family: var(--font-noto-sans), sans-serif;
position: relative;
min-height: 100vh;
overscroll-behavior: none;
overscroll-behavior-y: none;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: var(--font-noto-sans), sans-serif;
}

1263
src/app/layout.tsx Normal file

File diff suppressed because it is too large Load Diff

254
src/app/page.tsx Normal file
View File

@@ -0,0 +1,254 @@
"use client"
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarLayoutFloatingInline from '@/components/navbar/NavbarLayoutFloatingInline';
import HeroBillboardCarousel from '@/components/sections/hero/HeroBillboardCarousel';
import MetricSplitMediaAbout from '@/components/sections/about/MetricSplitMediaAbout';
import ProductCardFour from '@/components/sections/product/ProductCardFour';
import FeatureProcessSteps from '@/components/sections/feature/FeatureProcessSteps';
import TestimonialCardTwo from '@/components/sections/testimonial/TestimonialCardTwo';
import FaqBase from '@/components/sections/faq/FaqBase';
import ContactSplitForm from '@/components/sections/contact/ContactSplitForm';
import FooterSimple from '@/components/sections/footer/FooterSimple';
import { Coffee, Sparkles, Star, Heart, HelpCircle } from 'lucide-react';
export default function CoffeePage() {
return (
<ThemeProvider
defaultButtonVariant="hover-bubble"
defaultTextAnimation="background-highlight"
borderRadius="soft"
contentWidth="smallMedium"
sizing="large"
background="aurora"
cardStyle="gradient-radial"
primaryButtonStyle="flat"
secondaryButtonStyle="layered"
headingFontWeight="extrabold"
>
<div id="nav" data-section="nav">
<NavbarLayoutFloatingInline
brandName="BrewHaven"
navItems={[
{ name: "About", id: "about" },
{ name: "Menu", id: "products" },
{ name: "Process", id: "features" },
{ name: "Contact", id: "contact" }
]}
button={{
text: "Visit Us", href: "contact"
}}
/>
</div>
<div id="hero" data-section="hero">
<HeroBillboardCarousel
title="Discover Exceptional Coffee"
description="Handcrafted beverages made from the finest beans, roasted with passion. Experience the perfect blend of quality, artistry, and warmth."
tag="Premium Coffee"
tagIcon={Coffee}
background={{ variant: "aurora" }}
mediaItems={[
{
imageSrc: "https://img.b2bpic.net/free-photo/cup-coffee-with-foam-flower_1203-1629.jpg", imageAlt: "Freshly brewed latte with latte art"
},
{
imageSrc: "https://img.b2bpic.net/free-photo/high-view-cup-coffee-sweets_23-2148255018.jpg", imageAlt: "Professional barista preparing espresso"
},
{
imageSrc: "https://img.b2bpic.net/free-photo/cups-coffee-table_23-2148900624.jpg", imageAlt: "Freshly roasted specialty coffee beans"
},
{
imageSrc: "https://img.b2bpic.net/free-photo/cups-cappuccino-with-heart-rosetta-latte-art_140725-1471.jpg", imageAlt: "Warm ceramic coffee cup"
},
{
imageSrc: "https://img.b2bpic.net/free-photo/coffee-latte-with-croissant-blurred-glasses_23-2148441256.jpg", imageAlt: "Cozy cafe interior with warm lighting"
}
]}
buttons={[
{ text: "View Menu", href: "products" },
{ text: "Reserve a Table", href: "contact" }
]}
/>
</div>
<div id="about" data-section="about">
<MetricSplitMediaAbout
tag="Our Story"
tagIcon={Sparkles}
title="Brewing Excellence Since Day One"
description="BrewHaven was founded on a simple belief: exceptional coffee should be accessible to everyone. We source single-origin beans from sustainable farms, roast them in small batches to preserve flavor complexity, and craft each cup with meticulous care. Our baristas are passionate professionals dedicated to creating not just drinks, but memorable experiences."
metrics={[
{ value: "5,000+", title: "Daily Coffee Lovers" },
{ value: "50+", title: "Origins & Varieties" }
]}
imageSrc="https://img.b2bpic.net/free-photo/confident-young-indian-man-black-shirt-standing-cafe_627829-5462.jpg"
imageAlt="Modern BrewHaven cafe interior with warm lighting"
useInvertedBackground="noInvert"
/>
</div>
<div id="products" data-section="products">
<ProductCardFour
title="Our Signature Menu"
description="From classic espresso shots to creative specialty drinks, each item is crafted to perfection"
tag="Popular Items"
tagIcon={Star}
products={[
{
id: "1", name: "Single-Shot Espresso", price: "$3.50", variant: "Rich Crema, Bold Flavor", imageSrc: "https://img.b2bpic.net/free-photo/cup-hot-espresso-coffee-takeaway-paper-cup-stone-table-cafe-monotone-background-with-copy-space-coffee-break_166373-3732.jpg", imageAlt: "Perfectly pulled single espresso shot"
},
{
id: "2", name: "Signature Latte", price: "$5.50", variant: "Silky Microfoam, Velvety Smooth", imageSrc: "https://img.b2bpic.net/free-photo/high-angle-buiscuits-muffin_23-2148297896.jpg", imageAlt: "Creamy latte with beautiful latte art"
},
{
id: "3", name: "Italian Cappuccino", price: "$5.00", variant: "1:1:1 Ratio, Classic Style", imageSrc: "https://img.b2bpic.net/free-photo/leftover-coffee-black-cup-window-sill_23-2147898257.jpg", imageAlt: "Traditional cappuccino with thick foam"
},
{
id: "4", name: "Pastry Pairing", price: "$8.50", variant: "Fresh Baked, House Special", imageSrc: "https://img.b2bpic.net/free-photo/breakfast-set-flat-lay-with-croissant-raspberry-jam-food-photography_53876-108045.jpg", imageAlt: "Fresh croissant perfect for coffee pairing"
}
]}
textboxLayout="default"
gridVariant="four-items-2x2-equal-grid"
animationType="slide-up"
useInvertedBackground="noInvert"
/>
</div>
<div id="features" data-section="features">
<FeatureProcessSteps
tag="How We Work"
tagIcon={Sparkles}
title="Our Coffee Crafting Process"
description="Every cup tells a story of careful sourcing, expert roasting, and precise brewing. Discover the steps that make our coffee truly special."
steps={[
{
number: "01", title: "Source & Select", tag: "Ethical Sourcing", description: "We partner directly with sustainable farms worldwide, carefully selecting beans based on altitude, climate, and flavor profile. Each origin brings unique characteristics to our roastery."
},
{
number: "02", title: "Small-Batch Roasting", tag: "In-House Roastery", description: "Our master roasters use traditional techniques to bring out the best in each batch. We roast to order, ensuring peak freshness and optimal flavor development for every shipment."
},
{
number: "03", title: "Expert Preparation", tag: "Craft & Care", description: "Our certified baristas use precision equipment and traditional methods to extract the perfect shot. Each drink is a craft beverage, prepared with passion and expertise."
}
]}
useInvertedBackground="noInvert"
/>
</div>
<div id="testimonial" data-section="testimonial">
<TestimonialCardTwo
title="What Our Coffee Lovers Say"
description="Real customers sharing their genuine experience at BrewHaven"
tag="Reviews"
tagIcon={Heart}
testimonials={[
{
id: "1", name: "Sarah Chen", role: "Regular Customer", testimonial: "The attention to detail is incredible. Every visit, my coffee tastes perfectly balanced. The baristas remember my order and always ask how their craft can improve my experience.", imageSrc: "https://img.b2bpic.net/free-photo/front-view-business-woman-suit_23-2148603018.jpg", imageAlt: "Portrait of Sarah Chen"
},
{
id: "2", name: "Marcus Rodriguez", role: "Coffee Enthusiast", testimonial: "I've tried coffee everywhere, and BrewHaven consistently delivers the best flavor profiles. Their single-origin selections are exceptional, and the pricing is fair for the quality.", imageSrc: "https://img.b2bpic.net/free-photo/close-up-confident-corporate-woman-professional-entrepreneur-smiling-cross-arms-chest-smiling-enthusiastic-standing-white-background_1258-85600.jpg", imageAlt: "Portrait of Marcus Rodriguez"
},
{
id: "3", name: "Emily Watson", role: "Daily Visitor", testimonial: "More than just great coffee, it's the warm atmosphere and community feel. I've made friends here, and the staff genuinely cares about sustainability and quality.", imageSrc: "https://img.b2bpic.net/free-photo/portrait-woman-with-curly-hair_23-2148728586.jpg", imageAlt: "Portrait of Emily Watson"
},
{
id: "4", name: "David Park", role: "Business Owner", testimonial: "I hold all my client meetings here. The ambiance is professional, the wifi is reliable, and the coffee keeps me sharp. It's become my second office.", imageSrc: "https://img.b2bpic.net/free-photo/happy-professional_1098-12931.jpg", imageAlt: "Portrait of David Park"
}
]}
textboxLayout="default"
animationType="slide-up"
useInvertedBackground="noInvert"
/>
</div>
<div id="faq" data-section="faq">
<FaqBase
title="Frequently Asked Questions"
description="Everything you need to know about BrewHaven, our coffee, and our cafe"
tag="Help"
tagIcon={HelpCircle}
textboxLayout="default"
useInvertedBackground="noInvert"
animationType="smooth"
faqs={[
{
id: "1", title: "What types of beans do you use?", content: "We source single-origin and blended specialty coffee beans from sustainable farms across Ethiopia, Kenya, Colombia, and Central America. Each bean is carefully selected for flavor profile, and we rotate seasonal offerings to showcase different origins and processing methods."
},
{
id: "2", title: "Do you offer decaf options?", content: "Yes! We offer Swiss water process decaffeinated beans that maintain exceptional flavor. We can prepare decaf espresso, cappuccino, latte, and all our specialty drinks. We roast decaf in small batches to ensure freshness."
},
{
id: "3", title: "Are there dairy-free milk alternatives?", content: "Absolutely. We offer oat milk, almond milk, coconut milk, and soy milk. All milk alternatives are premium quality and properly steamed to create the perfect microfoam for any espresso-based drink."
},
{
id: "4", title: "Can I buy whole beans to take home?", content: "Yes! We sell whole bean coffee in 12oz and 1lb bags. Each bag includes detailed tasting notes and brewing recommendations. Beans are roasted fresh multiple times weekly, and we offer subscription options for regular customers."
},
{
id: "5", title: "Do you provide catering services?", content: "We do offer coffee catering for corporate events, weddings, and private functions. Our team can provide espresso machines, trained baristas, and customized menu options. Please contact us for details and pricing."
},
{
id: "6", title: "What are your hours of operation?", content: "We're open Monday-Friday 6:30am-8:00pm, Saturday 8:00am-9:00pm, and Sunday 8:00am-7:00pm. Check our contact page for holiday hours and special closures."
}
]}
/>
</div>
<div id="contact" data-section="contact">
<ContactSplitForm
title="Get In Touch"
description="Have questions? Want to reserve a table or discuss catering? We'd love to hear from you. Fill out the form and our team will respond within 24 hours."
inputs={[
{
name: "name", type: "text", placeholder: "Your Name", required: true
},
{
name: "email", type: "email", placeholder: "Your Email", required: true
}
]}
textarea={{
name: "message", placeholder: "Tell us how we can help...", rows: 5,
required: true
}}
useInvertedBackground="noInvert"
imageSrc="https://img.b2bpic.net/free-photo/low-angle-friends-drinking-coffee_23-2148395430.jpg"
imageAlt="BrewHaven coffee shop storefront with welcoming entrance"
mediaPosition="right"
buttonText="Send Message"
/>
</div>
<div id="footer" data-section="footer">
<FooterSimple
columns={[
{
title: "Navigate", items: [
{ label: "Home", href: "#hero" },
{ label: "About", href: "#about" },
{ label: "Menu", href: "#products" },
{ label: "Contact", href: "#contact" }
]
},
{
title: "Visit Us", items: [
{ label: "123 Coffee Lane, Brew City, BC 45678" },
{ label: "Mon-Fri: 6:30am - 8:00pm" },
{ label: "Sat-Sun: 8:00am - 9:00pm" },
{ label: "(555) 123-4567" }
]
},
{
title: "Follow Us", items: [
{ label: "Instagram", href: "https://instagram.com" },
{ label: "Facebook", href: "https://facebook.com" },
{ label: "Twitter", href: "https://twitter.com" }
]
}
]}
bottomLeftText="© 2025 BrewHaven Coffee. All rights reserved."
bottomRightText="Roasting Excellence, Brewing Passion"
/>
</div>
</ThemeProvider>
);
}

5
vercel.json Normal file
View File

@@ -0,0 +1,5 @@
{
"installCommand": "npm ci",
"buildCommand": "npm run build",
"outputDirectory": ".next"
}