feat: demonstrate shared component architecture
This commit is contained in:
32
src/app/about/page.module.css
Normal file
32
src/app/about/page.module.css
Normal file
@@ -0,0 +1,32 @@
|
||||
@reference '../../styles/globals.css';
|
||||
|
||||
.page {
|
||||
@apply mx-auto max-w-3xl px-10 py-12;
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply font-display text-4xl text-fg;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
@apply mb-3 mt-10 font-display text-2xl text-fg;
|
||||
}
|
||||
|
||||
.lead {
|
||||
@apply mt-4 text-lg text-fg;
|
||||
}
|
||||
|
||||
.body {
|
||||
@apply mt-4 leading-relaxed text-muted;
|
||||
}
|
||||
|
||||
.inlineCode {
|
||||
@apply rounded bg-surface2 px-1 py-0.5 text-sm text-fg;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.code {
|
||||
@apply mt-2 overflow-x-auto rounded bg-surface2 p-4 text-sm text-fg;
|
||||
font-family: var(--font-mono);
|
||||
border: 1px solid var(--color-line);
|
||||
}
|
||||
63
src/app/about/page.tsx
Normal file
63
src/app/about/page.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { store } from '@config/store';
|
||||
import { stores } from '@config/stores';
|
||||
import styles from './page.module.css';
|
||||
|
||||
const exampleCss = `/* ProductCard.module.css — one file, every storefront */
|
||||
.cta {
|
||||
@apply mt-5 inline-block text-sm;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
@store executive { .cta { @apply text-[10px] uppercase tracking-[0.28em]; } }
|
||||
@store crypto { .cta { @apply w-full rounded-lg bg-accent py-3 font-bold; } }
|
||||
@store supper { .cta { @apply rounded-full bg-accent px-5 py-2.5; } }
|
||||
@store collector { .cta { @apply border px-4 py-2; } }`;
|
||||
|
||||
const examplePlugin = `// postcss-storestyles keeps only the active store's blocks.
|
||||
// shopSystem = NEXT_PUBLIC_STORE_CODE.split('_')[0] // e.g. "crypto"
|
||||
if (atRule.params !== shopSystem && !atRule.params.startsWith(shopSystem + '_')) {
|
||||
atRule.remove(); // dropped from the build
|
||||
} else {
|
||||
atRule.replaceWith(atRule.nodes); // unwrapped into plain CSS
|
||||
}`;
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<h1 className={styles.title}>One codebase, four storefronts</h1>
|
||||
|
||||
<p className={styles.lead}>
|
||||
You are looking at <strong>{store.brandName}</strong> — the{' '}
|
||||
<code className={styles.inlineCode}>{store.code}</code> storefront. The other three —{' '}
|
||||
{stores
|
||||
.filter((s) => s.code !== store.code)
|
||||
.map((s) => s.label)
|
||||
.join(', ')}{' '}
|
||||
— are <em>the same React components and the same CSS files</em>. No component knows which store it is.
|
||||
</p>
|
||||
|
||||
<p className={styles.body}>
|
||||
Every visual difference — colour, type, spacing, even layout — lives inside{' '}
|
||||
<code className={styles.inlineCode}>@store</code> blocks in the CSS. A small PostCSS plugin,{' '}
|
||||
<code className={styles.inlineCode}>postcss-storestyles</code>, runs at build time and keeps only the blocks
|
||||
that match <code className={styles.inlineCode}>NEXT_PUBLIC_STORE_CODE</code>. Every other store's styles
|
||||
are stripped before they reach the browser, so each build ships one lean stylesheet with zero runtime branching.
|
||||
</p>
|
||||
|
||||
<h2 className={styles.subtitle}>The styles</h2>
|
||||
<pre className={styles.code}>
|
||||
<code>{exampleCss}</code>
|
||||
</pre>
|
||||
|
||||
<h2 className={styles.subtitle}>The plugin that decides</h2>
|
||||
<pre className={styles.code}>
|
||||
<code>{examplePlugin}</code>
|
||||
</pre>
|
||||
|
||||
<p className={styles.body}>
|
||||
Each storefront is its own build, deployed to its own Cloudflare Worker. One codebase, four storefronts, each
|
||||
maintained in the file it belongs to.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
src/app/page.tsx
Normal file
15
src/app/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import Hero from '@/components/Hero/Hero';
|
||||
import ProductGrid from '@/components/ProductGrid/ProductGrid';
|
||||
import FeaturedBlock from '@/components/FeaturedBlock/FeaturedBlock';
|
||||
import Reviews from '@/components/Reviews/Reviews';
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<>
|
||||
<Hero />
|
||||
<ProductGrid />
|
||||
<FeaturedBlock />
|
||||
<Reviews />
|
||||
</>
|
||||
);
|
||||
}
|
||||
21
src/app/product/[slug]/page.module.css
Normal file
21
src/app/product/[slug]/page.module.css
Normal file
@@ -0,0 +1,21 @@
|
||||
@reference '../../../styles/globals.css';
|
||||
|
||||
.page {
|
||||
@apply mx-auto max-w-6xl px-10 pt-8;
|
||||
}
|
||||
|
||||
.back {
|
||||
@apply inline-block text-sm text-muted hover:text-fg;
|
||||
}
|
||||
|
||||
@store crypto {
|
||||
.back {
|
||||
@apply font-mono;
|
||||
}
|
||||
}
|
||||
|
||||
@store collector {
|
||||
.back {
|
||||
@apply font-mono uppercase tracking-[0.1em];
|
||||
}
|
||||
}
|
||||
33
src/app/product/[slug]/page.tsx
Normal file
33
src/app/product/[slug]/page.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { catalog, getProduct } from '@/data/catalog';
|
||||
import ProductDetail from '@/components/ProductDetail/ProductDetail';
|
||||
import styles from './page.module.css';
|
||||
|
||||
export function generateStaticParams() {
|
||||
return catalog.map((product) => ({ slug: product.slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params;
|
||||
const product = getProduct(slug);
|
||||
return { title: product?.name };
|
||||
}
|
||||
|
||||
export default async function ProductPage({ params }: { params: Promise<{ slug: string }> }) {
|
||||
const { slug } = await params;
|
||||
const product = getProduct(slug);
|
||||
|
||||
if (!product) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<Link href="/" className={styles.back}>
|
||||
← Back to shop
|
||||
</Link>
|
||||
<ProductDetail product={product} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user