Add ToDo-Karten product page, fluid design tokens, and Navbar/Hero fixes
New /todo-cards page (Hero, How-it-works, Focus, Testimonials, Pricing) built with the fluid clamp() token system (app/lib/fluid.ts) and scroll-reveal animations (app/components/Reveal.tsx), matching styling consistency with /challenge's testimonial section. Navbar: page-aware active state and anchor-link navigation from any route (not just "/"), fixed logo click to actually navigate instead of silently rewriting the URL, fluid nav text size, custom hash-scroll handling for cross-page anchor links. Hero: responsive breakpoint fix for the text/image split at Tablet widths, entrance animation for the brand's orange dot, removed a red dot artifact from hero.png. Also includes prior uncommitted work on About/Blog/Newsletter/Footer and the cart lib (app/lib/cart.ts). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const CART_KEY = "ep_cart";
|
||||
const CART_EVENT = "ep-cart-updated";
|
||||
|
||||
type CartItem = { id: string; qty: number };
|
||||
|
||||
function readCart(): CartItem[] {
|
||||
if (typeof window === "undefined") return [];
|
||||
try {
|
||||
const raw = window.localStorage.getItem(CART_KEY);
|
||||
return raw ? JSON.parse(raw) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function writeCart(items: CartItem[]) {
|
||||
window.localStorage.setItem(CART_KEY, JSON.stringify(items));
|
||||
window.dispatchEvent(new Event(CART_EVENT));
|
||||
}
|
||||
|
||||
export function getCartCount(): number {
|
||||
return readCart().reduce((sum, item) => sum + item.qty, 0);
|
||||
}
|
||||
|
||||
export function addToCart(id: string, qty = 1) {
|
||||
const items = readCart();
|
||||
const existing = items.find((i) => i.id === id);
|
||||
if (existing) existing.qty += qty;
|
||||
else items.push({ id, qty });
|
||||
writeCart(items);
|
||||
}
|
||||
|
||||
// Reactive cart item count — 0 until the client hydrates and reads
|
||||
// localStorage, then stays in sync across tabs ("storage") and same-tab
|
||||
// updates (the CART_EVENT dispatched by addToCart, which "storage" alone
|
||||
// doesn't fire for the tab that made the change).
|
||||
export function useCartCount(): number {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setCount(getCartCount());
|
||||
const onUpdate = () => setCount(getCartCount());
|
||||
window.addEventListener(CART_EVENT, onUpdate);
|
||||
window.addEventListener("storage", onUpdate);
|
||||
return () => {
|
||||
window.removeEventListener(CART_EVENT, onUpdate);
|
||||
window.removeEventListener("storage", onUpdate);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return count;
|
||||
}
|
||||
Reference in New Issue
Block a user