{product.category}
+{product.name}
+{product.description}
+diff --git a/package-lock.json b/package-lock.json
index 3357cc6..34b05ff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,8 +10,7 @@
"license": "ISC",
"dependencies": {
"react": "^19.2.0",
- "react-dom": "^19.2.0",
- "react-router-dom": "^7.9.6"
+ "react-dom": "^19.2.0"
},
"devDependencies": {
"@commitlint/cli": "^20.1.0",
@@ -22,6 +21,7 @@
"@semantic-release/npm": "^13.1.2",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
+ "@testing-library/user-event": "^14.6.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.6",
"@types/react-dom": "^19.2.3",
@@ -2913,6 +2913,20 @@
}
}
},
+ "node_modules/@testing-library/user-event": {
+ "version": "14.6.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
+ "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": ">=7.21.4"
+ }
+ },
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
@@ -4086,15 +4100,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/cookie": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
- "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -9738,44 +9743,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/react-router": {
- "version": "7.9.6",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz",
- "integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==",
- "license": "MIT",
- "dependencies": {
- "cookie": "^1.0.1",
- "set-cookie-parser": "^2.6.0"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
- },
- "peerDependenciesMeta": {
- "react-dom": {
- "optional": true
- }
- }
- },
- "node_modules/react-router-dom": {
- "version": "7.9.6",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.6.tgz",
- "integrity": "sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==",
- "license": "MIT",
- "dependencies": {
- "react-router": "7.9.6"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "react": ">=18",
- "react-dom": ">=18"
- }
- },
"node_modules/read-package-up": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
@@ -10571,12 +10538,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/set-cookie-parser": {
- "version": "2.7.2",
- "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
- "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
- "license": "MIT"
- },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
diff --git a/package.json b/package.json
index ad7cb32..5b68bbd 100644
--- a/package.json
+++ b/package.json
@@ -16,8 +16,7 @@
},
"dependencies": {
"react": "^19.2.0",
- "react-dom": "^19.2.0",
- "react-router-dom": "^7.9.6"
+ "react-dom": "^19.2.0"
},
"devDependencies": {
"@commitlint/cli": "^20.1.0",
@@ -28,6 +27,7 @@
"@semantic-release/npm": "^13.1.2",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
+ "@testing-library/user-event": "^14.6.1",
"@types/node": "^24.10.1",
"@types/react": "^19.2.6",
"@types/react-dom": "^19.2.3",
diff --git a/src/App.css b/src/App.css
index b9d355d..cfd784a 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,42 +1,470 @@
-#root {
- max-width: 1280px;
+.shop {
+ max-width: 1200px;
margin: 0 auto;
- padding: 2rem;
- text-align: center;
+ padding: 3rem clamp(1.25rem, 4vw, 3rem) 4rem;
+ display: flex;
+ flex-direction: column;
+ gap: 3rem;
+}
+
+.hero {
+ background: radial-gradient(circle at top left, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.1)),
+ var(--card);
+ border-radius: 32px;
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ overflow: hidden;
+ box-shadow: 0 25px 80px rgba(15, 23, 42, 0.1);
+}
+
+.hero__content {
+ padding: clamp(2rem, 5vw, 4rem);
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.hero__media img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ min-height: 320px;
+ filter: saturate(1.05);
+}
+
+.hero__eyebrow,
+.eyebrow {
+ font-size: 0.85rem;
+ letter-spacing: 0.24em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+
+.hero__lead {
+ font-size: 1.1rem;
+ color: var(--muted-strong);
+ max-width: 32ch;
+}
+
+.hero__actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+}
+
+.hero__meta {
+ display: flex;
+ gap: 1rem;
+ font-weight: 600;
+}
+
+.btn {
+ border: none;
+ border-radius: 999px;
+ padding: 0.85rem 1.6rem;
+ font-size: 0.95rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: transform 200ms ease, box-shadow 200ms ease, background 200ms ease;
+}
+
+.btn--primary {
+ background: var(--accent);
+ color: #fff;
+ box-shadow: 0 12px 24px rgba(241, 84, 53, 0.25);
+}
+
+.btn--ghost {
+ background: transparent;
+ border: 1px solid rgba(15, 23, 42, 0.15);
+ color: var(--text);
+}
+
+.btn:hover {
+ transform: translateY(-2px);
+}
+
+.perks {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+ gap: 1.25rem;
+}
+
+.perks article {
+ background: var(--card);
+ padding: 1.5rem;
+ border-radius: 20px;
+ border: 1px solid var(--border);
+ min-height: 150px;
+}
+
+.perks h3 {
+ margin-bottom: 0.35rem;
+}
+
+.categories {
+ background: var(--card);
+ border-radius: 32px;
+ padding: clamp(2rem, 5vw, 3.5rem);
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ border: 1px solid var(--border);
+}
+
+.section-heading {
+ max-width: 520px;
+}
+
+.categories__grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
+ gap: 1rem;
+}
+
+.categories__grid article {
+ background: rgba(15, 23, 42, 0.04);
+ border-radius: 20px;
+ padding: 1.25rem;
+ min-height: 130px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+}
+
+.product-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 2.5rem;
+}
+
+.product-grid__items {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: 2rem;
+}
+
+.product-card {
+ background: var(--card);
+ border-radius: 28px;
+ overflow: hidden;
+ border: 1px solid var(--border);
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+ transition: transform 200ms ease, box-shadow 200ms ease;
+}
+
+.product-card__media {
+ position: relative;
+ overflow: hidden;
+ aspect-ratio: 4 / 3;
+}
+
+.product-card__media img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ transition: transform 300ms ease;
+}
+
+.product-card:hover .product-card__media img {
+ transform: scale(1.04);
+}
+
+.product-card__badge {
+ position: absolute;
+ top: 1rem;
+ left: 1rem;
+ background: rgba(0, 0, 0, 0.65);
+ color: #fff;
+ padding: 0.35rem 0.75rem;
+ border-radius: 999px;
+ font-size: 0.75rem;
+}
+
+.product-card__body {
+ padding: 1.75rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.8rem;
+ flex: 1;
+}
+
+.product-card__category {
+ text-transform: uppercase;
+ letter-spacing: 0.2em;
+ font-size: 0.7rem;
+ color: var(--muted);
+}
+
+.product-card__description {
+ color: var(--muted-strong);
+ margin: 0;
+ flex: 1;
+}
+
+.product-card__meta {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-weight: 600;
+}
+
+.product-card__price {
+ font-size: 1.1rem;
+}
+
+.product-card__colors {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ font-size: 0.85rem;
+ color: var(--muted-strong);
}
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
+.product-card__cta {
+ align-self: flex-start;
+ border-radius: 999px;
+ border: none;
+ background: rgba(15, 23, 42, 0.85);
+ color: #fff;
+ padding: 0.55rem 1.4rem;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ font-size: 0.75rem;
+ transition: transform 200ms ease, box-shadow 200ms ease;
}
-.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
+
+.product-card--highlight {
+ animation: cardPulse 500ms ease;
+ box-shadow: 0 10px 20px rgba(241, 84, 53, 0.08);
}
-.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafbaa);
+
+.product-card__cta--pulse {
+ animation: ctaPulse 500ms ease;
+ background: linear-gradient(120deg, rgba(241, 84, 53, 0.95), rgba(241, 84, 53, 0.8));
+ box-shadow: 0 8px 18px rgba(241, 84, 53, 0.18);
}
-@keyframes logo-spin {
- from {
- transform: rotate(0deg);
+@keyframes cardPulse {
+ 0% {
+ transform: scale(1);
+ opacity: 1;
+ }
+ 30% {
+ transform: scale(1.004);
+ opacity: 0.98;
}
- to {
- transform: rotate(360deg);
+ 100% {
+ transform: scale(1);
+ opacity: 1;
}
}
-@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
+@keyframes ctaPulse {
+ 0% {
+ transform: scale(1);
}
+ 35% {
+ transform: scale(1.03);
+ }
+ 100% {
+ transform: scale(1);
+ }
+}
+
+.editorial {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
+ gap: 2rem;
+ align-items: center;
+ background: var(--card);
+ border-radius: 32px;
+ padding: clamp(2rem, 5vw, 3.5rem);
+ border: 1px solid var(--border);
+}
+
+.editorial__media img {
+ width: 100%;
+ border-radius: 24px;
+ object-fit: cover;
+ min-height: 320px;
+}
+
+.editorial__content ul {
+ padding-left: 1.2rem;
+ color: var(--muted-strong);
+ line-height: 1.8;
+}
+
+.editorial__content button {
+ margin-top: 1rem;
+}
+
+.cart-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: var(--card);
+ border-radius: 28px;
+ padding: 1.5rem 2rem;
+ border: 1px solid var(--border);
+}
+
+.cart-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.65rem;
+ background: #0f172a;
+ color: #fff;
+ border: none;
+ border-radius: 999px;
+ padding: 0.75rem 1.3rem;
+ font-weight: 600;
+ cursor: pointer;
+}
+
+.cart-pill {
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.15);
+}
+
+.cart-panel {
+ background: var(--card);
+ border-radius: 32px;
+ border: 1px solid var(--border);
+ padding: clamp(1.5rem, 4vw, 3rem);
+ display: grid;
+ gap: 1.5rem;
+}
+
+.cart-panel--open {
+ box-shadow: 0 20px 60px rgba(15, 23, 42, 0.12);
+}
+
+.cart-panel__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.cart-panel__note {
+ color: var(--muted-strong);
+}
+
+.cart-panel__empty {
+ padding: 1.5rem;
+ border-radius: 20px;
+ background: rgba(15, 23, 42, 0.04);
+ text-align: center;
}
-.card {
- padding: 2em;
+.cart-panel__lines {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
}
-.read-the-docs {
- color: #888;
+.cart-line {
+ border-radius: 24px;
+ border: 1px solid var(--border);
+ padding: 1.25rem 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.cart-line__info {
+ display: flex;
+ justify-content: space-between;
+ font-weight: 600;
+}
+
+.cart-line__controls {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+}
+
+.quantity {
+ display: inline-flex;
+ align-items: center;
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ overflow: hidden;
+}
+
+.quantity button {
+ border: none;
+ background: transparent;
+ padding: 0.35rem 0.9rem;
+ font-size: 1.2rem;
+ cursor: pointer;
+}
+
+.quantity span {
+ min-width: 2rem;
+ text-align: center;
+ font-weight: 600;
+}
+
+.cart-line__total {
+ font-weight: 600;
+}
+
+.cart-panel__summary {
+ border-top: 1px solid var(--border);
+ padding-top: 1rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.cart-panel__summary-row {
+ display: flex;
+ justify-content: space-between;
+ color: var(--muted-strong);
+}
+
+.cart-panel__summary-row--total {
+ font-size: 1.1rem;
+ color: var(--text);
+ font-weight: 600;
+}
+
+.btn--small {
+ padding: 0.5rem 1.1rem;
+ font-size: 0.85rem;
+}
+
+.btn--full {
+ width: 100%;
+ text-align: center;
+}
+
+@media (max-width: 720px) {
+ .hero__actions {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .cart-line__info,
+ .cart-line__controls {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .cart-panel {
+ padding: 1.5rem;
+ }
}
diff --git a/src/App.test.tsx b/src/App.test.tsx
index afe527d..8b1dd6d 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -1,19 +1,43 @@
import { describe, it, expect } from 'vitest'
+import '@testing-library/jest-dom/vitest'
import { render, screen } from '@testing-library/react'
-import { MemoryRouter } from 'react-router-dom'
+import userEvent from '@testing-library/user-event'
import App from './App'
+import products from './data/products'
describe('App', () => {
- it('renders main navigation links', () => {
- render(
-
This is the home page.
-{product.category}
+{product.description}
+- This starter uses React Router, TypeScript, Vitest, ESLint, and Prettier configured for a - modern workflow. -
-{item.product.name}
+ {currency.format(item.product.price)} +Your bag
+{cartCount ? `${cartCount} item${cartCount > 1 ? 's' : ''}` : 'No items yet'}
+{text}
Shopping bag
+{freeShippingMessage}
+ {cartItems.length === 0 ? ( +Your basket is empty – add your favorite finds.
+ ) : ( +New season edit
++ Curated furniture, lighting, and objects crafted in small batches and ready to ship. +
+{perk.detail}
+Shop by room
+Refresh a single corner or rethink your whole home with designer-backed palettes.
+{category.count} curated pieces
+Featured pieces
++ Mix tactile fabrics, natural woods, and sculptural silhouettes for your signature look. +
+From the studio
++ We partner with small-batch workshops to produce timeless staples. Every stitch, weave, + and finishing touch is considered so you can style once and enjoy for years. +
+