Rebrand store as Pulse with athletic theme and shared typography
- Pulse theme tokens in app.schema.json: Archivo Black headings (weight 400) + Inter body, white bg / black pill buttons, xl radius, AI-generated athletic imagery - Add headerFontWeight theme prop so single-weight fonts (Archivo Black) load and render correctly; ThemeProvider applies font-family + weight inline so Typography works regardless of `as` element - New shared Heading component (tagline / title / subtitle with size + align + tone variants) and Typography caption variant for taglines; refactor features, faq, cta, testimonials, products-carousel, products-grid, collection-grid, recommended-products, image-gallery, newsletter-cta to use them - Hero accepts a `buttons` array (label / href / variant) replacing primaryCta/secondaryCta; cover-image component removed and existing cover blocks migrated to Hero blocks with `buttons: []` - Newsletter CTA uses shadcn Button + Input so it inherits theme radius; stacked layout fixed to keep the image - Product/collection card titles use Typography subtitle variants (font-body), heading font weight is theme-controlled - Remove orphan commerce/shop-header.tsx and commerce/shop-footer.tsx; the editor-driven navigation/footer are the live chrome Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Typography } from '@/components/Typography';
|
||||
|
||||
interface CollectionImage {
|
||||
url: string;
|
||||
@@ -40,9 +41,12 @@ const CollectionCard: React.FC<CollectionCardProps> = ({ collection }) => {
|
||||
|
||||
{/* Collection Info */}
|
||||
<CardContent className="p-6">
|
||||
<h3 className="text-2xl font-bold text-foreground mb-3 group-hover:text-muted-foreground transition-colors font-heading">
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
className="mb-3 font-semibold tracking-tight text-foreground transition-colors group-hover:text-muted-foreground"
|
||||
>
|
||||
{collection.title}
|
||||
</h3>
|
||||
</Typography>
|
||||
|
||||
{collection.description && (
|
||||
<p className="text-muted-foreground">
|
||||
|
||||
@@ -2,8 +2,9 @@ import { useEffect, useState } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { shopifyFetch } from "@/services/shopify/client";
|
||||
import { GET_COLLECTIONS_QUERY } from "@/graphql/collections";
|
||||
import { Typography } from "@/components/Typography";
|
||||
import { Container } from "@/components/layout/Container";
|
||||
import { Typography } from "@/components/Typography";
|
||||
import { Heading } from "@/components/Heading";
|
||||
|
||||
export type CollectionGridProps = {
|
||||
tagline: string;
|
||||
@@ -47,19 +48,15 @@ export function CollectionGrid({
|
||||
return (
|
||||
<section className="bg-background py-20 md:py-28">
|
||||
<Container>
|
||||
<div className="mx-auto mb-12 max-w-2xl text-center">
|
||||
{tagline ? (
|
||||
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||
{tagline}
|
||||
</p>
|
||||
) : null}
|
||||
<Typography variant="h2">{heading}</Typography>
|
||||
{subheading ? (
|
||||
<Typography variant="subtitle1" className="mt-3">
|
||||
{subheading}
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
<Heading
|
||||
tagline={tagline}
|
||||
title={heading}
|
||||
subtitle={subheading}
|
||||
align="center"
|
||||
size="lg"
|
||||
className="mx-auto mb-12"
|
||||
maxWidth="max-w-2xl"
|
||||
/>
|
||||
|
||||
<div
|
||||
className={
|
||||
@@ -90,22 +87,27 @@ export function CollectionGrid({
|
||||
{isEditorial ? (
|
||||
<div className="absolute inset-0 flex items-end bg-gradient-to-t from-black/60 via-transparent to-transparent p-8">
|
||||
<div>
|
||||
<Typography variant="h4" className="text-white">
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
className="font-semibold tracking-tight text-white"
|
||||
>
|
||||
{c.title}
|
||||
</Typography>
|
||||
<span className="mt-2 inline-flex text-xs uppercase tracking-[0.2em] text-white/80">
|
||||
Shop now →
|
||||
Shop now
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{!isEditorial ? (
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<h3 className="text-sm font-medium tracking-tight">{c.title}</h3>
|
||||
<span className="text-xs text-muted-foreground transition-opacity group-hover:opacity-100">
|
||||
→
|
||||
</span>
|
||||
<div className="mt-4">
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
className="font-medium tracking-tight text-foreground"
|
||||
>
|
||||
{c.title}
|
||||
</Typography>
|
||||
</div>
|
||||
) : null}
|
||||
</Link>
|
||||
|
||||
@@ -90,9 +90,7 @@ export function FeaturedProductView({
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-5">
|
||||
{tagline ? (
|
||||
<p className="text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||
{tagline}
|
||||
</p>
|
||||
<Typography variant="caption">{tagline}</Typography>
|
||||
) : null}
|
||||
<Typography variant="h2">{product.title}</Typography>
|
||||
{formatted ? (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { Link } from "react-router";
|
||||
import { Typography } from "@/components/Typography";
|
||||
|
||||
type ProductImage = { url: string; altText?: string };
|
||||
type ProductPrice = { amount: string; currencyCode: string };
|
||||
@@ -53,7 +54,12 @@ export function ProductCard({
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-4 flex items-start justify-between gap-3">
|
||||
<h3 className="text-sm font-medium tracking-tight">{product.title}</h3>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
className="font-medium tracking-tight text-foreground"
|
||||
>
|
||||
{product.title}
|
||||
</Typography>
|
||||
{price ? (
|
||||
<div className="flex flex-col items-end text-sm">
|
||||
{onSale && compare ? (
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { ShopifyCollection } from "@reacteditor/field-shopify";
|
||||
import { getProducts } from "@/hooks/use-shopify-products";
|
||||
import { getCollectionProducts } from "@/hooks/use-shopify-collections";
|
||||
import { ProductCard } from "./product-card";
|
||||
import { Typography } from "@/components/Typography";
|
||||
import { Heading } from "@/components/Heading";
|
||||
import {
|
||||
Carousel,
|
||||
CarouselContent,
|
||||
@@ -74,19 +74,14 @@ export function ProductsCarousel({
|
||||
<section className="bg-background py-20 md:py-28">
|
||||
<Container>
|
||||
<div className="mb-10 flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
||||
<div className="max-w-xl">
|
||||
{tagline ? (
|
||||
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||
{tagline}
|
||||
</p>
|
||||
) : null}
|
||||
<Typography variant="h2">{heading}</Typography>
|
||||
{subheading ? (
|
||||
<Typography variant="subtitle1" className="mt-3">
|
||||
{subheading}
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
<Heading
|
||||
tagline={tagline}
|
||||
title={heading}
|
||||
subtitle={subheading}
|
||||
align="left"
|
||||
size="lg"
|
||||
maxWidth="max-w-xl"
|
||||
/>
|
||||
{ctaLabel ? (
|
||||
<Link
|
||||
to={
|
||||
|
||||
@@ -4,8 +4,8 @@ import type { ShopifyCollection } from "@reacteditor/field-shopify";
|
||||
import { getProducts } from "@/hooks/use-shopify-products";
|
||||
import { getCollectionProducts } from "@/hooks/use-shopify-collections";
|
||||
import { ProductCard } from "./product-card";
|
||||
import { Typography } from "@/components/Typography";
|
||||
import { Container } from "@/components/layout/Container";
|
||||
import { Heading } from "@/components/Heading";
|
||||
|
||||
export type ProductsGridProps = {
|
||||
collection: ShopifyCollection | null;
|
||||
@@ -62,19 +62,14 @@ export function ProductsGrid({
|
||||
<section className="bg-background py-20 md:py-28">
|
||||
<Container>
|
||||
<div className="mb-12 flex flex-col items-end justify-between gap-6 md:flex-row md:items-end">
|
||||
<div className="max-w-xl">
|
||||
{tagline ? (
|
||||
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||
{tagline}
|
||||
</p>
|
||||
) : null}
|
||||
<Typography variant="h2">{heading}</Typography>
|
||||
{subheading ? (
|
||||
<Typography variant="subtitle1" className="mt-3">
|
||||
{subheading}
|
||||
</Typography>
|
||||
) : null}
|
||||
</div>
|
||||
<Heading
|
||||
tagline={tagline}
|
||||
title={heading}
|
||||
subtitle={subheading}
|
||||
align="left"
|
||||
size="lg"
|
||||
maxWidth="max-w-xl"
|
||||
/>
|
||||
{ctaLabel ? (
|
||||
<Link
|
||||
to={ctaHref || (collection?.handle ? `/collections/${collection.handle}` : "/collections")}
|
||||
|
||||
@@ -4,9 +4,9 @@ import {
|
||||
useProductRecommendations,
|
||||
} from "@/hooks/use-shopify-products";
|
||||
import { ProductCard } from "./product-card";
|
||||
import { Typography } from "@/components/Typography";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Container } from "@/components/layout/Container";
|
||||
import { Heading } from "@/components/Heading";
|
||||
|
||||
export type RecommendedProductsProps = {
|
||||
product: ShopifyProduct | null;
|
||||
@@ -46,14 +46,14 @@ export function RecommendedProductsView({
|
||||
return (
|
||||
<section className="bg-background py-20 md:py-28">
|
||||
<Container>
|
||||
<div className="mb-12 max-w-xl">
|
||||
{tagline ? (
|
||||
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||
{tagline}
|
||||
</p>
|
||||
) : null}
|
||||
<Typography variant="h3">{heading}</Typography>
|
||||
</div>
|
||||
<Heading
|
||||
tagline={tagline}
|
||||
title={heading}
|
||||
align="left"
|
||||
size="md"
|
||||
className="mb-12"
|
||||
maxWidth="max-w-xl"
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 gap-x-6 gap-y-12 md:grid-cols-4">
|
||||
{items.length === 0
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
return (
|
||||
<footer className="bg-foreground text-background py-12">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h3
|
||||
className="text-2xl font-bold mb-4"
|
||||
style={{fontFamily: 'Space Grotesk, sans-serif'}}
|
||||
>
|
||||
Store
|
||||
</h3>
|
||||
<p className="text-background/70 mb-6">
|
||||
Your premium shopping destination
|
||||
</p>
|
||||
<div className="mt-8 pt-8 border-t border-background/20 text-background/70">
|
||||
<p>© 2025 Store. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
@@ -1,68 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { useShopifyCart } from '@/hooks/use-shopify-cart';
|
||||
import config from '@/lib/config.json';
|
||||
|
||||
const CartIcon: React.FC = () => {
|
||||
const { toggleCart, itemCount } = useShopifyCart();
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggleCart}
|
||||
className="relative p-1 text-foreground hover:text-muted-foreground transition-colors"
|
||||
>
|
||||
<i className="ri-shopping-cart-line text-xl"></i>
|
||||
{itemCount > 0 && (
|
||||
<span className="absolute -top-1 -right-1 bg-primary text-primary-foreground text-[10px] rounded-full w-4 h-4 flex items-center justify-center font-semibold">
|
||||
{itemCount > 99 ? '99+' : itemCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const Header: React.FC = () => {
|
||||
return (
|
||||
<nav className="bg-background shadow-sm sticky top-0 z-30 h-14">
|
||||
<div className="container mx-auto px-4 h-full">
|
||||
<div className="flex justify-between items-center h-full">
|
||||
{/* Logo */}
|
||||
<Link to="/" className="text-lg font-bold text-foreground font-heading">
|
||||
{config.brand.logo.url ? (
|
||||
<img
|
||||
src={config.brand.logo.url}
|
||||
alt={config.brand.logo.alt || 'Store'}
|
||||
className="h-8"
|
||||
/>
|
||||
) : (
|
||||
'Store'
|
||||
)}
|
||||
</Link>
|
||||
|
||||
{/* Navigation Links */}
|
||||
<div className="flex items-center space-x-6">
|
||||
<Link
|
||||
to="/"
|
||||
className="text-sm text-foreground hover:text-muted-foreground font-medium transition-colors"
|
||||
>
|
||||
Products
|
||||
</Link>
|
||||
<Link
|
||||
to="/collections"
|
||||
className="text-sm text-foreground hover:text-muted-foreground font-medium transition-colors"
|
||||
>
|
||||
Collections
|
||||
</Link>
|
||||
|
||||
{/* Cart Icon */}
|
||||
<CartIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
Reference in New Issue
Block a user