Add Container component and fix radius/maxWidth theming
- Drop buttonRadius prop; button now uses --radius via rounded-md - Inject @theme radius mappings into ThemeProvider so rounded-* utilities pick up --radius inside the Tailwind CDN iframe - Add shared Container that consumes --container-max-width set from the global maxWidth prop, replacing ad-hoc "container mx-auto max-w-7xl px-6" wrappers across commerce, landing, footer, navigation, and others - Simplify maxWidth options to Small/Medium/Large/X-Large/Full bleed and shift the scale up so Large (1280px) matches the previous default Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,8 @@ export type ThemeProps = {
|
|||||||
mutedForegroundColor?: string;
|
mutedForegroundColor?: string;
|
||||||
borderColor?: string;
|
borderColor?: string;
|
||||||
radius?: "none" | "sm" | "md" | "lg" | "xl";
|
radius?: "none" | "sm" | "md" | "lg" | "xl";
|
||||||
buttonRadius?: "none" | "sm" | "md" | "lg" | "xl";
|
|
||||||
shadow?: "none" | "sm" | "md" | "lg" | "xl";
|
shadow?: "none" | "sm" | "md" | "lg" | "xl";
|
||||||
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
|
maxWidth?: "sm" | "md" | "lg" | "xl" | "full";
|
||||||
};
|
};
|
||||||
|
|
||||||
const radiusMap: Record<NonNullable<ThemeProps["radius"]>, string> = {
|
const radiusMap: Record<NonNullable<ThemeProps["radius"]>, string> = {
|
||||||
@@ -27,12 +26,12 @@ const radiusMap: Record<NonNullable<ThemeProps["radius"]>, string> = {
|
|||||||
xl: "1rem",
|
xl: "1rem",
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonRadiusMap: Record<NonNullable<ThemeProps["buttonRadius"]>, string> = {
|
const maxWidthMap: Record<NonNullable<ThemeProps["maxWidth"]>, string> = {
|
||||||
none: "0px",
|
sm: "64rem",
|
||||||
sm: "0.25rem",
|
md: "72rem",
|
||||||
md: "0.5rem",
|
lg: "80rem",
|
||||||
lg: "0.75rem",
|
xl: "96rem",
|
||||||
xl: "1rem",
|
full: "100%",
|
||||||
};
|
};
|
||||||
|
|
||||||
const shadowMap: Record<NonNullable<ThemeProps["shadow"]>, string> = {
|
const shadowMap: Record<NonNullable<ThemeProps["shadow"]>, string> = {
|
||||||
@@ -67,8 +66,8 @@ export function ThemeProvider({
|
|||||||
mutedForegroundColor,
|
mutedForegroundColor,
|
||||||
borderColor,
|
borderColor,
|
||||||
radius,
|
radius,
|
||||||
buttonRadius,
|
|
||||||
shadow,
|
shadow,
|
||||||
|
maxWidth,
|
||||||
children,
|
children,
|
||||||
}: ThemeProps & { children?: React.ReactNode }) {
|
}: ThemeProps & { children?: React.ReactNode }) {
|
||||||
// Recompute CSS-variable map only when a relevant prop changes.
|
// Recompute CSS-variable map only when a relevant prop changes.
|
||||||
@@ -84,8 +83,8 @@ export function ThemeProvider({
|
|||||||
if (mutedForegroundColor) vars["--muted-foreground"] = mutedForegroundColor;
|
if (mutedForegroundColor) vars["--muted-foreground"] = mutedForegroundColor;
|
||||||
if (borderColor) vars["--border"] = borderColor;
|
if (borderColor) vars["--border"] = borderColor;
|
||||||
if (radius) vars["--radius"] = radiusMap[radius];
|
if (radius) vars["--radius"] = radiusMap[radius];
|
||||||
if (buttonRadius) vars["--button-radius"] = buttonRadiusMap[buttonRadius];
|
|
||||||
if (shadow) vars["--shadow"] = shadowMap[shadow];
|
if (shadow) vars["--shadow"] = shadowMap[shadow];
|
||||||
|
if (maxWidth) vars["--container-max-width"] = maxWidthMap[maxWidth];
|
||||||
if (headerFont) vars["--font-header"] = `"${headerFont}", system-ui, sans-serif`;
|
if (headerFont) vars["--font-header"] = `"${headerFont}", system-ui, sans-serif`;
|
||||||
if (bodyFont) vars["--font-body"] = `"${bodyFont}", system-ui, sans-serif`;
|
if (bodyFont) vars["--font-body"] = `"${bodyFont}", system-ui, sans-serif`;
|
||||||
return vars;
|
return vars;
|
||||||
@@ -102,8 +101,8 @@ export function ThemeProvider({
|
|||||||
mutedForegroundColor,
|
mutedForegroundColor,
|
||||||
borderColor,
|
borderColor,
|
||||||
radius,
|
radius,
|
||||||
buttonRadius,
|
|
||||||
shadow,
|
shadow,
|
||||||
|
maxWidth,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Imperatively push every CSS var onto :root inside the host document
|
// Imperatively push every CSS var onto :root inside the host document
|
||||||
@@ -157,6 +156,12 @@ export function ThemeProvider({
|
|||||||
@theme {
|
@theme {
|
||||||
--font-family-heading: var(--font-header), system-ui, -apple-system, sans-serif;
|
--font-family-heading: var(--font-header), system-ui, -apple-system, sans-serif;
|
||||||
--font-family-body: var(--font-body), system-ui, -apple-system, sans-serif;
|
--font-family-body: var(--font-body), system-ui, -apple-system, sans-serif;
|
||||||
|
--radius-sm: calc(var(--radius) * 0.5);
|
||||||
|
--radius-md: calc(var(--radius) * 0.75);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) * 1.5);
|
||||||
|
--radius-2xl: calc(var(--radius) * 2);
|
||||||
|
--radius-3xl: calc(var(--radius) * 3);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Link } from "react-router";
|
|||||||
import { shopifyFetch } from "@/services/shopify/client";
|
import { shopifyFetch } from "@/services/shopify/client";
|
||||||
import { GET_COLLECTIONS_QUERY } from "@/graphql/collections";
|
import { GET_COLLECTIONS_QUERY } from "@/graphql/collections";
|
||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type CollectionGridProps = {
|
export type CollectionGridProps = {
|
||||||
tagline: string;
|
tagline: string;
|
||||||
@@ -45,7 +46,7 @@ export function CollectionGrid({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-20 md:py-28">
|
<section className="bg-background py-20 md:py-28">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
<div className="mx-auto mb-12 max-w-2xl text-center">
|
<div className="mx-auto mb-12 max-w-2xl text-center">
|
||||||
{tagline ? (
|
{tagline ? (
|
||||||
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||||
@@ -110,7 +111,7 @@ export function CollectionGrid({
|
|||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
|||||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
|
||||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, SheetFooter } from '@/components/ui/sheet';
|
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, SheetFooter } from '@/components/ui/sheet';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Container } from '@/components/layout/Container';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type FilterOption = { label: string };
|
type FilterOption = { label: string };
|
||||||
@@ -425,7 +426,7 @@ export function CollectionView(props: CollectionProps) {
|
|||||||
if (!selected && !paramHandle) {
|
if (!selected && !paramHandle) {
|
||||||
return (
|
return (
|
||||||
<section className="bg-background pb-24 pt-12 md:pt-20">
|
<section className="bg-background pb-24 pt-12 md:pt-20">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
<header className="mx-auto mb-14 flex max-w-2xl flex-col items-center gap-3 text-center">
|
<header className="mx-auto mb-14 flex max-w-2xl flex-col items-center gap-3 text-center">
|
||||||
<Skeleton className="h-3 w-24" />
|
<Skeleton className="h-3 w-24" />
|
||||||
<Skeleton className="h-10 w-3/4" />
|
<Skeleton className="h-10 w-3/4" />
|
||||||
@@ -435,14 +436,14 @@ export function CollectionView(props: CollectionProps) {
|
|||||||
<Skeleton key={i} className="aspect-[4/5] w-full" />
|
<Skeleton key={i} className="aspect-[4/5] w-full" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-background pb-24 pt-12 md:pt-20">
|
<section className="bg-background pb-24 pt-12 md:pt-20">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
{/* Cover image */}
|
{/* Cover image */}
|
||||||
{showCoverImage === 'yes' && collectionImage && (
|
{showCoverImage === 'yes' && collectionImage && (
|
||||||
<div className="mb-10 overflow-hidden rounded-lg">
|
<div className="mb-10 overflow-hidden rounded-lg">
|
||||||
@@ -545,7 +546,7 @@ export function CollectionView(props: CollectionProps) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useProduct } from "@/hooks/use-shopify-products";
|
|||||||
import { useShopifyCart } from "@/hooks/use-shopify-cart";
|
import { useShopifyCart } from "@/hooks/use-shopify-cart";
|
||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export type FeaturedProductProps = {
|
export type FeaturedProductProps = {
|
||||||
@@ -33,7 +34,7 @@ export function FeaturedProductView({
|
|||||||
tone === "muted" ? "bg-muted/40" : "bg-background",
|
tone === "muted" ? "bg-muted/40" : "bg-background",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="container mx-auto grid max-w-7xl grid-cols-1 items-center gap-10 px-6 md:grid-cols-2 md:gap-16">
|
<Container className="grid grid-cols-1 items-center gap-10 md:grid-cols-2 md:gap-16">
|
||||||
<div className={cn(align === "right" && "md:order-2")}>
|
<div className={cn(align === "right" && "md:order-2")}>
|
||||||
<Skeleton className="aspect-[4/5] w-full" />
|
<Skeleton className="aspect-[4/5] w-full" />
|
||||||
</div>
|
</div>
|
||||||
@@ -51,7 +52,7 @@ export function FeaturedProductView({
|
|||||||
<Skeleton className="h-11 w-32 rounded-md" />
|
<Skeleton className="h-11 w-32 rounded-md" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -75,7 +76,7 @@ export function FeaturedProductView({
|
|||||||
: "bg-background py-20 md:py-28"
|
: "bg-background py-20 md:py-28"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="container mx-auto grid max-w-7xl grid-cols-1 items-center gap-10 px-6 md:grid-cols-2 md:gap-16">
|
<Container className="grid grid-cols-1 items-center gap-10 md:grid-cols-2 md:gap-16">
|
||||||
<div className={align === "right" ? "md:order-2" : ""}>
|
<div className={align === "right" ? "md:order-2" : ""}>
|
||||||
{image ? (
|
{image ? (
|
||||||
<img
|
<img
|
||||||
@@ -123,7 +124,7 @@ export function FeaturedProductView({
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { Typography } from "@/components/Typography";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { Loader } from "@/components/ui/loader";
|
import { Loader } from "@/components/ui/loader";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type ProductDetailsProps = {
|
export type ProductDetailsProps = {
|
||||||
product: ShopifyProduct | null;
|
product: ShopifyProduct | null;
|
||||||
@@ -31,7 +32,7 @@ export function ProductDetailsView({ product: selected }: ProductDetailsProps) {
|
|||||||
if (!handle || loading || !product) {
|
if (!handle || loading || !product) {
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-12 md:py-20">
|
<section className="bg-background py-12 md:py-20">
|
||||||
<div className="container mx-auto grid max-w-7xl grid-cols-1 gap-10 px-6 md:grid-cols-2 md:gap-16">
|
<Container className="grid grid-cols-1 gap-10 md:grid-cols-2 md:gap-16">
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<Skeleton className="aspect-[4/5] w-full" />
|
<Skeleton className="aspect-[4/5] w-full" />
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
@@ -62,7 +63,7 @@ export function ProductDetailsView({ product: selected }: ProductDetailsProps) {
|
|||||||
<Skeleton className="h-4 w-4/6" />
|
<Skeleton className="h-4 w-4/6" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -90,7 +91,7 @@ export function ProductDetailsView({ product: selected }: ProductDetailsProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-12 md:py-20">
|
<section className="bg-background py-12 md:py-20">
|
||||||
<div className="container mx-auto grid max-w-7xl grid-cols-1 gap-10 px-6 md:grid-cols-2 md:gap-16">
|
<Container className="grid grid-cols-1 gap-10 md:grid-cols-2 md:gap-16">
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div className="aspect-[4/5] w-full overflow-hidden rounded-md bg-muted">
|
<div className="aspect-[4/5] w-full overflow-hidden rounded-md bg-muted">
|
||||||
{main ? (
|
{main ? (
|
||||||
@@ -214,7 +215,7 @@ export function ProductDetailsView({ product: selected }: ProductDetailsProps) {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
CarouselNext,
|
CarouselNext,
|
||||||
CarouselPrevious,
|
CarouselPrevious,
|
||||||
} from "@/components/ui/carousel";
|
} from "@/components/ui/carousel";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type ProductsCarouselProps = {
|
export type ProductsCarouselProps = {
|
||||||
collection: ShopifyCollection | null;
|
collection: ShopifyCollection | null;
|
||||||
@@ -71,7 +72,7 @@ export function ProductsCarousel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-20 md:py-28">
|
<section className="bg-background py-20 md:py-28">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
<div className="mb-10 flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
<div className="mb-10 flex flex-col gap-6 md:flex-row md:items-end md:justify-between">
|
||||||
<div className="max-w-xl">
|
<div className="max-w-xl">
|
||||||
{tagline ? (
|
{tagline ? (
|
||||||
@@ -120,7 +121,7 @@ export function ProductsCarousel({
|
|||||||
<CarouselPrevious className="hidden md:inline-flex" />
|
<CarouselPrevious className="hidden md:inline-flex" />
|
||||||
<CarouselNext className="hidden md:inline-flex" />
|
<CarouselNext className="hidden md:inline-flex" />
|
||||||
</Carousel>
|
</Carousel>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { getProducts } from "@/hooks/use-shopify-products";
|
|||||||
import { getCollectionProducts } from "@/hooks/use-shopify-collections";
|
import { getCollectionProducts } from "@/hooks/use-shopify-collections";
|
||||||
import { ProductCard } from "./product-card";
|
import { ProductCard } from "./product-card";
|
||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type ProductsGridProps = {
|
export type ProductsGridProps = {
|
||||||
collection: ShopifyCollection | null;
|
collection: ShopifyCollection | null;
|
||||||
@@ -59,7 +60,7 @@ export function ProductsGrid({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-20 md:py-28">
|
<section className="bg-background py-20 md:py-28">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
<div className="mb-12 flex flex-col items-end justify-between gap-6 md:flex-row md:items-end">
|
<div className="mb-12 flex flex-col items-end justify-between gap-6 md:flex-row md:items-end">
|
||||||
<div className="max-w-xl">
|
<div className="max-w-xl">
|
||||||
{tagline ? (
|
{tagline ? (
|
||||||
@@ -94,7 +95,7 @@ export function ProductsGrid({
|
|||||||
))
|
))
|
||||||
: products.map((p) => <ProductCard key={p.id} product={p} />)}
|
: products.map((p) => <ProductCard key={p.id} product={p} />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
import { ProductCard } from "./product-card";
|
import { ProductCard } from "./product-card";
|
||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type RecommendedProductsProps = {
|
export type RecommendedProductsProps = {
|
||||||
product: ShopifyProduct | null;
|
product: ShopifyProduct | null;
|
||||||
@@ -27,7 +28,7 @@ export function RecommendedProductsView({
|
|||||||
if (!selected) {
|
if (!selected) {
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-20 md:py-28">
|
<section className="bg-background py-20 md:py-28">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
<div className="mb-12 flex max-w-xl flex-col gap-3">
|
<div className="mb-12 flex max-w-xl flex-col gap-3">
|
||||||
{tagline ? <Skeleton className="h-3 w-24" /> : null}
|
{tagline ? <Skeleton className="h-3 w-24" /> : null}
|
||||||
<Skeleton className="h-8 w-2/3" />
|
<Skeleton className="h-8 w-2/3" />
|
||||||
@@ -37,14 +38,14 @@ export function RecommendedProductsView({
|
|||||||
<Skeleton key={i} className="aspect-[4/5] w-full" />
|
<Skeleton key={i} className="aspect-[4/5] w-full" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-20 md:py-28">
|
<section className="bg-background py-20 md:py-28">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
<div className="mb-12 max-w-xl">
|
<div className="mb-12 max-w-xl">
|
||||||
{tagline ? (
|
{tagline ? (
|
||||||
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||||
@@ -61,7 +62,7 @@ export function RecommendedProductsView({
|
|||||||
))
|
))
|
||||||
: items.map((p: any) => <ProductCard key={p.id} product={p} />)}
|
: items.map((p: any) => <ProductCard key={p.id} product={p} />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Skeleton } from '@/components/ui/skeleton';
|
|||||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, SheetFooter } from '@/components/ui/sheet';
|
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, SheetFooter } from '@/components/ui/sheet';
|
||||||
|
import { Container } from '@/components/layout/Container';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
type FilterOption = { label: string };
|
type FilterOption = { label: string };
|
||||||
@@ -397,7 +398,7 @@ export function SearchProductsView(props: SearchProductsProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-12 md:py-16">
|
<section className="bg-background py-12 md:py-16">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
|
|
||||||
{/* Page header */}
|
{/* Page header */}
|
||||||
<div className="mb-10">
|
<div className="mb-10">
|
||||||
@@ -523,7 +524,7 @@ export function SearchProductsView(props: SearchProductsProps) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type FeaturesProps = {
|
export type FeaturesProps = {
|
||||||
tagline: string;
|
tagline: string;
|
||||||
@@ -17,7 +18,7 @@ const colClass: Record<FeaturesProps["columns"], string> = {
|
|||||||
export function Features({ tagline, heading, subheading, columns, items }: FeaturesProps) {
|
export function Features({ tagline, heading, subheading, columns, items }: FeaturesProps) {
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-20 md:py-28">
|
<section className="bg-background py-20 md:py-28">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
<div className="mx-auto mb-16 max-w-2xl text-center">
|
<div className="mx-auto mb-16 max-w-2xl text-center">
|
||||||
{tagline ? (
|
{tagline ? (
|
||||||
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
<p className="mb-3 text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||||
@@ -45,7 +46,7 @@ export function Features({ tagline, heading, subheading, columns, items }: Featu
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type FooterProps = {
|
export type FooterProps = {
|
||||||
brand: string;
|
brand: string;
|
||||||
@@ -45,7 +46,7 @@ export function Footer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="border-t border-border bg-background">
|
<footer className="border-t border-border bg-background">
|
||||||
<div className="container mx-auto max-w-7xl px-6 py-20 md:py-24">
|
<Container className="py-20 md:py-24">
|
||||||
<div className="grid grid-cols-1 gap-12 md:grid-cols-12">
|
<div className="grid grid-cols-1 gap-12 md:grid-cols-12">
|
||||||
<div className="md:col-span-4">
|
<div className="md:col-span-4">
|
||||||
<Typography variant="h5" as="p">
|
<Typography variant="h5" as="p">
|
||||||
@@ -123,7 +124,7 @@ export function Footer({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type ImageGalleryProps = {
|
export type ImageGalleryProps = {
|
||||||
tagline: string;
|
tagline: string;
|
||||||
@@ -12,7 +13,7 @@ export type ImageGalleryProps = {
|
|||||||
export function ImageGallery({ tagline, heading, subheading, layout, items }: ImageGalleryProps) {
|
export function ImageGallery({ tagline, heading, subheading, layout, items }: ImageGalleryProps) {
|
||||||
return (
|
return (
|
||||||
<section className="bg-background py-20 md:py-28">
|
<section className="bg-background py-20 md:py-28">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
{(tagline || heading || subheading) && (
|
{(tagline || heading || subheading) && (
|
||||||
<div className="mx-auto mb-12 max-w-2xl text-center">
|
<div className="mx-auto mb-12 max-w-2xl text-center">
|
||||||
{tagline ? (
|
{tagline ? (
|
||||||
@@ -86,7 +87,7 @@ export function ImageGallery({ tagline, heading, subheading, layout, items }: Im
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Typography } from "@/components/Typography";
|
import { Typography } from "@/components/Typography";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type EmailProvider = "none" | "mailchimp" | "klaviyo";
|
export type EmailProvider = "none" | "mailchimp" | "klaviyo";
|
||||||
|
|
||||||
@@ -131,7 +132,7 @@ export function NewsletterCta({
|
|||||||
if (layout === "split") {
|
if (layout === "split") {
|
||||||
return (
|
return (
|
||||||
<section className="bg-background">
|
<section className="bg-background">
|
||||||
<div className="container mx-auto max-w-7xl px-6 py-16 md:py-24">
|
<Container className="py-16 md:py-24">
|
||||||
<div className="grid grid-cols-1 items-center gap-12 md:grid-cols-2">
|
<div className="grid grid-cols-1 items-center gap-12 md:grid-cols-2">
|
||||||
<div>
|
<div>
|
||||||
{imageUrl ? (
|
{imageUrl ? (
|
||||||
@@ -165,7 +166,7 @@ export function NewsletterCta({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
21
components/layout/Container.tsx
Normal file
21
components/layout/Container.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
export type ContainerProps = React.HTMLAttributes<HTMLElement> & {
|
||||||
|
as?: React.ElementType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Container({
|
||||||
|
as: Comp = "div",
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
...props
|
||||||
|
}: ContainerProps) {
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn("mx-auto w-full px-6", className)}
|
||||||
|
style={{ maxWidth: "var(--container-max-width, 80rem)", ...style }}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
|
|
||||||
export type LogosProps = {
|
export type LogosProps = {
|
||||||
tagline: string;
|
tagline: string;
|
||||||
items: Array<{ src: string; alt: string }>;
|
items: Array<{ src: string; alt: string }>;
|
||||||
@@ -7,7 +9,7 @@ export type LogosProps = {
|
|||||||
export function Logos({ tagline, items, layout }: LogosProps) {
|
export function Logos({ tagline, items, layout }: LogosProps) {
|
||||||
return (
|
return (
|
||||||
<section className="border-y border-border bg-muted/40 py-12">
|
<section className="border-y border-border bg-muted/40 py-12">
|
||||||
<div className="container mx-auto max-w-7xl px-6">
|
<Container>
|
||||||
{tagline ? (
|
{tagline ? (
|
||||||
<p className="mb-8 text-center text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
<p className="mb-8 text-center text-xs uppercase tracking-[0.2em] text-muted-foreground">
|
||||||
{tagline}
|
{tagline}
|
||||||
@@ -38,7 +40,7 @@ export function Logos({ tagline, items, layout }: LogosProps) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useState } from "react";
|
|||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
import { useShopifyCart } from "@/hooks/use-shopify-cart";
|
import { useShopifyCart } from "@/hooks/use-shopify-cart";
|
||||||
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
|
||||||
|
import { Container } from "@/components/layout/Container";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export type NavigationProps = {
|
export type NavigationProps = {
|
||||||
@@ -49,7 +50,7 @@ export function Navigation({
|
|||||||
toneClass[tone],
|
toneClass[tone],
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="container mx-auto flex h-16 max-w-7xl items-center justify-between px-6 md:h-20">
|
<Container className="flex h-16 items-center justify-between md:h-20">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="inline-flex items-center font-semibold tracking-tight"
|
className="inline-flex items-center font-semibold tracking-tight"
|
||||||
@@ -110,7 +111,7 @@ export function Navigation({
|
|||||||
<MenuIcon size={20} strokeWidth={1.5} />
|
<MenuIcon size={20} strokeWidth={1.5} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Container>
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius-button)] text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|||||||
@@ -32,9 +32,8 @@ export const Root: RootConfig<{
|
|||||||
fgColor: "#0a0a0a",
|
fgColor: "#0a0a0a",
|
||||||
mutedColor: "#f5f5f5",
|
mutedColor: "#f5f5f5",
|
||||||
radius: "md",
|
radius: "md",
|
||||||
buttonRadius: "md",
|
|
||||||
shadow: "sm",
|
shadow: "sm",
|
||||||
maxWidth: "xl",
|
maxWidth: "lg",
|
||||||
},
|
},
|
||||||
fields: {
|
fields: {
|
||||||
title: { label: "Page title", type: "text" },
|
title: { label: "Page title", type: "text" },
|
||||||
@@ -59,17 +58,6 @@ export const Root: RootConfig<{
|
|||||||
{ label: "Extra large", value: "xl" },
|
{ label: "Extra large", value: "xl" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
buttonRadius: {
|
|
||||||
label: "Button radius",
|
|
||||||
type: "select",
|
|
||||||
options: [
|
|
||||||
{ label: "None (square)", value: "none" },
|
|
||||||
{ label: "Small", value: "sm" },
|
|
||||||
{ label: "Medium", value: "md" },
|
|
||||||
{ label: "Large", value: "lg" },
|
|
||||||
{ label: "Extra large", value: "xl" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
shadow: {
|
shadow: {
|
||||||
label: "Shadow",
|
label: "Shadow",
|
||||||
type: "select",
|
type: "select",
|
||||||
@@ -89,7 +77,6 @@ export const Root: RootConfig<{
|
|||||||
{ label: "Medium", value: "md" },
|
{ label: "Medium", value: "md" },
|
||||||
{ label: "Large", value: "lg" },
|
{ label: "Large", value: "lg" },
|
||||||
{ label: "Extra large", value: "xl" },
|
{ label: "Extra large", value: "xl" },
|
||||||
{ label: "2X large", value: "2xl" },
|
|
||||||
{ label: "Full bleed", value: "full" },
|
{ label: "Full bleed", value: "full" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -105,8 +92,8 @@ export const Root: RootConfig<{
|
|||||||
fgColor,
|
fgColor,
|
||||||
mutedColor,
|
mutedColor,
|
||||||
radius,
|
radius,
|
||||||
buttonRadius,
|
|
||||||
shadow,
|
shadow,
|
||||||
|
maxWidth,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
@@ -119,8 +106,8 @@ export const Root: RootConfig<{
|
|||||||
fgColor={fgColor}
|
fgColor={fgColor}
|
||||||
mutedColor={mutedColor}
|
mutedColor={mutedColor}
|
||||||
radius={radius}
|
radius={radius}
|
||||||
buttonRadius={buttonRadius}
|
|
||||||
shadow={shadow}
|
shadow={shadow}
|
||||||
|
maxWidth={maxWidth}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
--radius-md: calc(var(--radius) * 0.75);
|
--radius-md: calc(var(--radius) * 0.75);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) * 1.5);
|
--radius-xl: calc(var(--radius) * 1.5);
|
||||||
--radius-button: var(--button-radius, var(--radius));
|
|
||||||
|
|
||||||
--animate-marquee: marquee var(--duration) infinite linear;
|
--animate-marquee: marquee var(--duration) infinite linear;
|
||||||
--animate-marquee-vertical: marquee-vertical var(--duration) linear infinite;
|
--animate-marquee-vertical: marquee-vertical var(--duration) linear infinite;
|
||||||
|
|||||||
Reference in New Issue
Block a user