From 0a72e12f5b9b3ef31d54f8a2fa14c8c9f4a18934 Mon Sep 17 00:00:00 2001 From: idncod Date: Fri, 13 Feb 2026 19:10:30 +0000 Subject: [PATCH 1/5] chore: update README --- README.md | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a984ca7..9c43636 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,30 @@ -![CI](https://github.com/idncod/snappycart/actions/workflows/ci.yml/badge.svg) -![Conventional Commits](https://img.shields.io/badge/commits-conventional-brightgreen.svg) +[![SnappyCart](https://img.shields.io/badge/SnappyCart-snappycart.idncod.com-F97316.svg?labelColor=111827)](https://snappycart.idncod.com/) · [![License](https://img.shields.io/badge/license-MIT-F97316.svg?labelColor=111827)](./LICENSE) [![npm](https://img.shields.io/npm/v/snappycart.svg?label=npm&color=F97316&labelColor=111827)](https://www.npmjs.com/package/snappycart) [![Build and Test](https://github.com/idncod/snappycart/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/idncod/snappycart/actions/workflows/ci.yml) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-22C55E.svg?labelColor=111827)](./CONTRIBUTING.md) [![Conventional Commits](https://img.shields.io/badge/commits-conventional-22C55E.svg?labelColor=111827)](https://www.conventionalcommits.org/) - ๐Ÿ›’ SnappyCart + + + # ๐Ÿ›’ Snappycart **SnappyCart** is a modern, headless React cart system designed for plug-and-play use in any React app. With full support for local state and future SaaS syncing, it's the perfect cart foundation โ€” open-source and Pro-ready. --- +## Why SnappyCart? + +Most cart solutions are either too basic or too opinionated. +SnappyCart gives you the core cart engine and UI building blocks, so you can ship your own cart UX without fighting a framework. + +- **Headless by default:** bring your own UI, or use the included drawer and components. +- **Type-safe:** first-class TypeScript types, predictable APIs, fewer runtime surprises. +- **Composable:** works with any React app architecture, from tiny SPAs to serious storefronts. +- **Built for growth:** structured to support future sync, analytics, and checkout flows without rewrites. + ## Features -- Headless cart logic (`add`, `remove`, `clear`, etc.) -- React context & hook (`useCart`) -- Customizable sliding cart drawer -- TypeScript support -- Built-in persistence (localStorage) -- Designed for extension (Pro syncing, analytics, checkout) +- Cart primitives: `add`, `remove`, `updateQuantity`, `clear` +- React context + hook: `CartProvider`, `useCart` +- Optional UI: sliding cart drawer you can theme +- Persistence: localStorage support out of the box +- Works with any product model (you own the schema) --- @@ -22,3 +32,74 @@ ```bash npm install snappycart +``` + +## Quick Start +**1. Wrap your app once:** +```javascript +import { CartProvider } from "snappycart"; + +export function App() { + return ( + + {/* your app */} + + ); +} +``` + +**2. Use the hook anywhere:** +```tsx +import { useCart } from "snappycart"; + +export function AddToCartButton() { + const { addItem } = useCart(); + + return ( + + ); +} +``` +**3. Open the cart UI (if you use the provided drawer):** +```tsx +import { useCart } from "snappycart"; + +export function CartButton() { + const { openCart, itemsCount } = useCart(); + return ; +} +``` + +## Documentation +Docs live here: + +Getting Started: https://snappycart.idncod.com/docs + +Theming: https://snappycart.idncod.com/docs/theming + +## Contributing + +--- + +SnappyCart is built to keep improving the core cart engine so it stays simple to adopt, reliable in production, and easy to extend. Development happens in the open on GitHub, and community contributions make a real difference, from bug fixes to new features and docs improvements. Use the sections below to learn how to contribute and help shape the project. + +### Start here: [Your first pull request](https://snappycart.idncod.com/docs/contributing/your-first-pr) + +### Issues: [Bug reports and feature requests are welcome](https://github.com/idncod/snappycart/issues) + +### [Good First Issues](https://github.com/idncod/snappycart/labels/good%20first%20issue) + +If you are new here and want an easy first win, start with our list of [good first issues](https://github.com/idncod/snappycart/labels/good%20first%20issue). These are small, well-scoped bugs and improvements that are a solid way to learn the contribution flow and get your first PR merged. +### License +Snappycart is [MIT licensed](./LICENSE). From 7e94463a458d1ff6663521407a0d20a38f4c9864 Mon Sep 17 00:00:00 2001 From: idncod Date: Sun, 8 Mar 2026 17:37:21 +0000 Subject: [PATCH 2/5] feat: add demo for snappyart on the landing --- .github/workflows/ci.yml | 24 +- src/App.tsx | 159 +++++++++++-- src/demo/DemoHome.module.scss | 221 ++++++++++++++++++ .../ProductCard/ProductCard.module.scss | 74 ++++++ .../components/ProductCard/ProductCard.tsx | 34 +++ .../ProductGrid/ProductGrid.module.scss | 18 ++ .../components/ProductGrid/ProductGrid.tsx | 19 ++ .../Quickstart/Quickstart.module.scss | 35 +++ src/demo/components/Quickstart/Quickstart.tsx | 37 +++ .../SiteFooter/SiteFooter.module.scss | 28 +++ src/demo/components/SiteFooter/SiteFooter.tsx | 15 ++ .../SiteHeader/SiteHeader.module.scss | 89 +++++++ src/demo/components/SiteHeader/SiteHeader.tsx | 36 +++ .../components/UseCases/UseCases.module.scss | 43 ++++ src/demo/components/UseCases/UseCases.tsx | 40 ++++ src/globals.scss | 56 +++++ src/main.tsx | 2 +- src/styles/_variables.scss | 64 ++--- 18 files changed, 943 insertions(+), 51 deletions(-) create mode 100644 src/demo/DemoHome.module.scss create mode 100644 src/demo/components/ProductCard/ProductCard.module.scss create mode 100644 src/demo/components/ProductCard/ProductCard.tsx create mode 100644 src/demo/components/ProductGrid/ProductGrid.module.scss create mode 100644 src/demo/components/ProductGrid/ProductGrid.tsx create mode 100644 src/demo/components/Quickstart/Quickstart.module.scss create mode 100644 src/demo/components/Quickstart/Quickstart.tsx create mode 100644 src/demo/components/SiteFooter/SiteFooter.module.scss create mode 100644 src/demo/components/SiteFooter/SiteFooter.tsx create mode 100644 src/demo/components/SiteHeader/SiteHeader.module.scss create mode 100644 src/demo/components/SiteHeader/SiteHeader.tsx create mode 100644 src/demo/components/UseCases/UseCases.module.scss create mode 100644 src/demo/components/UseCases/UseCases.tsx create mode 100644 src/globals.scss diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aeee8c7..4572b2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,10 @@ name: CI on: - push: - branches: [master] pull_request: - branches: [master] + push: + branches: + - $default-branch jobs: build-and-test: @@ -12,18 +12,28 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '18' + cache: npm - name: Install dependencies - run: npm install + run: npm ci + + - name: Changesets status + env: + BASE_REF: ${{ github.base_ref || github.event.repository.default_branch }} + run: | + git fetch origin "$BASE_REF" + npx changeset status --since="origin/$BASE_REF" - name: Run tests - run: npm run test + run: npm test - name: Run linter run: npm run lint diff --git a/src/App.tsx b/src/App.tsx index 5a462e8..36d59b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,17 @@ import { useMemo, useState } from 'react'; -import CartDrawer from './cart/components/CartDrawer'; -import CartIcon from './cart/components/CartIcon/CartIcon'; import { useCart } from './cart/hooks/useCart'; import type { CartProduct } from './cart/types/types'; +import CartDrawer from './cart/components/CartDrawer'; +import CartIcon from './cart/components/CartIcon/CartIcon'; + +import styles from './demo/DemoHome.module.scss'; +import { SiteHeader } from './demo/components/SiteHeader/SiteHeader'; +import { ProductGrid } from './demo/components/ProductGrid/ProductGrid'; +import { Quickstart } from './demo/components/Quickstart/Quickstart'; +import { UseCases } from './demo/components/UseCases/UseCases'; +import { SiteFooter } from './demo/components/SiteFooter/SiteFooter'; + export default function App() { const { addItem } = useCart(); const [open, setOpen] = useState(false); @@ -12,25 +20,148 @@ export default function App() { () => [ { id: 'apple', name: 'Apple', price: 0.6, imageUrl: 'apple.png' }, { id: 'banana', name: 'Banana', price: 0.4, imageUrl: 'banana.png' }, + { id: 'orange', name: 'Orange', price: 0.55, imageUrl: 'orange.png' }, + { id: 'strawberry', name: 'Strawberries', price: 1.25, imageUrl: 'strawberry.png' }, ], [], ); + const handleAddStarterSet = () => { + addItem(products[0]); + addItem(products[1], 2); + addItem(products[2]); + }; + return ( - <> -

Demo eCommerce

- -
- - -
+
+ setOpen(true)} + links={[ + { label: 'Quickstart', href: '#quickstart' }, + { label: 'Use cases', href: '#use-cases' }, + { label: 'Demo shop', href: '#demo-shop' }, + { label: 'GitHub', href: 'https://github.com/your-org/snappycart', external: true }, + { label: 'npm', href: 'https://www.npmjs.com/package/snappycart', external: true }, + ]} + /> + +
+
+
+
+

Integration starter

+

snappycart

+

+ Add items, open the drawer, ship. Use the snippets below to integrate into React or + Next.js in minutes. +

+ +
+ + +
+ +
+ React + TypeScript + SCSS + Headless-friendly +
+
+ +
+
+
Step 1: Trigger cart actions
+
+
+ + + + +
+
+ Tip: add items, then open the drawer. Everything here should mirror how users + integrate it. +
+
+
+ +
+
What you get
+
+
+
+ CartProvider +
+
+ useCart() +
+
+ CartIcon + CartDrawer +
+
+ Types exported +
+
+
+
+
+
+
+ +
+
+

Quickstart

+

Copy paste, no ceremony.

+
+ +
+ +
+
+

Use cases

+

Buttons trigger real cart behavior.

+
+ setOpen(true)} /> +
+ +
+
+

Demo shop

+

This is the โ€œintegration UIโ€ people expect.

+
+ addItem(p, qty)} /> +
+
+ + setOpen(true)} /> setOpen(false)} /> - +
); } diff --git a/src/demo/DemoHome.module.scss b/src/demo/DemoHome.module.scss new file mode 100644 index 0000000..447bf90 --- /dev/null +++ b/src/demo/DemoHome.module.scss @@ -0,0 +1,221 @@ +@use "../styles/variables" as *; + +.appShell { + min-height: 100vh; +} + +.page { + width: min(1120px, calc(100% - 48px)); + margin: 0 auto; + padding: 34px 0 90px; + display: flex; + flex-direction: column; + gap: 56px; +} + +.hero { + padding: 34px 0 10px; +} + +.heroGrid { + display: grid; + grid-template-columns: 1.12fr 0.88fr; + gap: 28px; + align-items: start; +} + +@media (max-width: 900px) { + .heroGrid { + grid-template-columns: 1fr; + } +} + +.heroLeft { + max-width: 62ch; +} + +.kicker { + margin: 0 0 10px; + font-size: 12px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: $color-muted; +} + +.h1 { + margin: 0 0 10px; + font-size: 56px; + line-height: 0.98; + letter-spacing: -0.05em; + font-weight: 900; + color: $color-text; +} + +.p { + margin: 0 0 18px; + color: $color-muted; + max-width: 70ch; + font-size: 16px; +} + +.heroActions { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-bottom: 18px; +} + +.primaryBtn, +.secondaryBtn { + border-radius: 999px; + padding: 11px 14px; + font-weight: 750; + transition: $transition-default; + cursor: pointer; +} + +.primaryBtn { + border: 0; + background: linear-gradient(135deg, rgba(124, 92, 255, 1), rgba(166, 177, 255, 1)); + color: $color-text; + box-shadow: 0 16px 36px rgba(124, 92, 255, 0.18); +} + +.primaryBtn:hover { + filter: brightness(1.04); +} + +.secondaryBtn { + border: 1px solid $color-border; + background: $color-surface; + color: $color-text; +} + +.secondaryBtn:hover { + background: $color-surface2; +} + +.badges { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.badge { + border: 1px solid $color-border; + background: $color-surface; + padding: 7px 10px; + border-radius: 999px; + font-size: 13px; + color: $color-muted; +} + +.heroRight { + display: grid; + gap: 14px; +} + +.card { + background: $color-surface; + border: 1px solid $color-border; + border-radius: 18px; + overflow: hidden; + box-shadow: $shadow-lg; +} + +.cardTitle { + padding: 14px 16px; + font-weight: 850; + color: $color-text; +} + +.cardBody { + padding: 16px; +} + +.miniGrid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; +} + +@media (max-width: 900px) { + .miniGrid { + grid-template-columns: 1fr; + } +} + +.miniBtn { + border: 1px solid $color-border; + background: rgba(255, 255, 255, 0.04); + color: $color-text; + border-radius: 999px; + padding: 11px 12px; + font-weight: 720; + cursor: pointer; + transition: $transition-default; +} + +.miniBtn:hover { + background: rgba(255, 255, 255, 0.07); +} + +.tip { + margin-top: 12px; + font-size: 13px; + color: $color-muted; +} + +.bullets { + display: grid; + gap: 10px; + padding-bottom: 2px; +} + +.bullet { + display: flex; + align-items: center; + gap: 10px; + color: $color-muted; +} + +.dot { + width: 10px; + height: 10px; + border-radius: 999px; + background: rgba(166, 177, 255, 1); + box-shadow: 0 0 0 3px rgba(124, 92, 255, 0.18); +} + +.section { + padding: 0; +} + +.sectionHeader { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 14px; + margin: 0 0 14px; +} + +@media (max-width: 720px) { + .sectionHeader { + flex-direction: column; + align-items: flex-start; + } +} + +.h2 { + margin: 0; + font-size: 18px; + letter-spacing: -0.01em; + font-weight: 850; + color: $color-text; +} + +.note { + margin: 0; + font-size: 13px; + color: $color-muted; +} \ No newline at end of file diff --git a/src/demo/components/ProductCard/ProductCard.module.scss b/src/demo/components/ProductCard/ProductCard.module.scss new file mode 100644 index 0000000..eb60a62 --- /dev/null +++ b/src/demo/components/ProductCard/ProductCard.module.scss @@ -0,0 +1,74 @@ +.card { + grid-column: span 4; + border: 1px solid var(--border); + background: var(--surface); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; +} + +@media (max-width: 980px) { + .card { + grid-column: span 4; + } +} + +@media (max-width: 680px) { + .card { + grid-column: span 4; + } +} + +.top { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.emoji { + font-size: 34px; + line-height: 1; +} + +.tag { + font-size: 12px; + color: var(--muted); + border: 1px solid var(--border); + background: rgba(0, 0, 0, 0.25); + padding: 6px 10px; + border-radius: 999px; +} + +.name { + font-weight: 760; + letter-spacing: -0.01em; +} + +.bottom { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.price { + font-weight: 700; + color: rgba(255, 255, 255, 0.86); +} + +.button { + border: 1px solid var(--border); + background: rgba(255, 255, 255, 0.08); + color: var(--text); + border-radius: var(--radius-sm); + padding: 10px 12px; + cursor: pointer; +} + +.button:hover { + background: rgba(255, 255, 255, 0.14); +} diff --git a/src/demo/components/ProductCard/ProductCard.tsx b/src/demo/components/ProductCard/ProductCard.tsx new file mode 100644 index 0000000..d61e72f --- /dev/null +++ b/src/demo/components/ProductCard/ProductCard.tsx @@ -0,0 +1,34 @@ +import type { CartProduct } from '../../../cart/types/types'; +import styles from './ProductCard.module.scss'; + +function formatMoneyGBP(value: number): string { + return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' }).format(value); +} + +export function ProductCard({ + product, + onAdd, +}: { + product: CartProduct; + onAdd: (product: CartProduct, qty?: number) => void; +}) { + return ( +
+
+ +
Demo
+
+ +
{product.name}
+ +
+
{formatMoneyGBP(product.price)}
+ +
+
+ ); +} diff --git a/src/demo/components/ProductGrid/ProductGrid.module.scss b/src/demo/components/ProductGrid/ProductGrid.module.scss new file mode 100644 index 0000000..0b313cb --- /dev/null +++ b/src/demo/components/ProductGrid/ProductGrid.module.scss @@ -0,0 +1,18 @@ +.grid { + margin-top: 12px; + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: var(--gap); +} + +@media (max-width: 980px) { + .grid { + grid-template-columns: repeat(8, 1fr); + } +} + +@media (max-width: 680px) { + .grid { + grid-template-columns: repeat(4, 1fr); + } +} diff --git a/src/demo/components/ProductGrid/ProductGrid.tsx b/src/demo/components/ProductGrid/ProductGrid.tsx new file mode 100644 index 0000000..6764762 --- /dev/null +++ b/src/demo/components/ProductGrid/ProductGrid.tsx @@ -0,0 +1,19 @@ +import type { CartProduct } from '../../../cart/types/types'; +import styles from './ProductGrid.module.scss'; +import { ProductCard } from '../ProductCard/ProductCard'; + +export function ProductGrid({ + products, + onAdd, +}: { + products: CartProduct[]; + onAdd: (product: CartProduct, qty?: number) => void; +}) { + return ( +
+ {products.map((p) => ( + + ))} +
+ ); +} diff --git a/src/demo/components/Quickstart/Quickstart.module.scss b/src/demo/components/Quickstart/Quickstart.module.scss new file mode 100644 index 0000000..eec06a9 --- /dev/null +++ b/src/demo/components/Quickstart/Quickstart.module.scss @@ -0,0 +1,35 @@ +@use "../../../styles/variables" as *; + +.wrap { + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: $spacing-md; +} + +.block { + grid-column: span 4; + border: 1px solid $color-border; + border-radius: $radius-md; + background: $color-bg; + box-shadow: $shadow-sm; + overflow: hidden; +} + +.title { + padding: 12px 14px; + font-weight: 800; + border-bottom: 1px solid $color-border; +} + +.code { + margin: 0; + padding: 14px; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; + font-size: 13px; + line-height: 1.5; + background: rgba(255, 255, 255, 0.7); +} + +@media (max-width: 980px) { + .block { grid-column: span 12; } +} \ No newline at end of file diff --git a/src/demo/components/Quickstart/Quickstart.tsx b/src/demo/components/Quickstart/Quickstart.tsx new file mode 100644 index 0000000..730ac85 --- /dev/null +++ b/src/demo/components/Quickstart/Quickstart.tsx @@ -0,0 +1,37 @@ +import styles from './Quickstart.module.scss'; + +export function Quickstart() { + return ( +
+
+
Install
+
+          npm i snappycart
+        
+
+ +
+
Wire it
+
+          {`import { CartProvider } from "snappycart";
+
+root.render(
+  
+    
+  
+);`}
+        
+
+ +
+
Use it
+
+          {`import { useCart } from "snappycart";
+
+const { addItem } = useCart();
+addItem({ id: "apple", name: "Apple", price: 0.6 });`}
+        
+
+
+ ); +} diff --git a/src/demo/components/SiteFooter/SiteFooter.module.scss b/src/demo/components/SiteFooter/SiteFooter.module.scss new file mode 100644 index 0000000..60370f8 --- /dev/null +++ b/src/demo/components/SiteFooter/SiteFooter.module.scss @@ -0,0 +1,28 @@ +.footer { + border-top: 1px solid var(--border); + background: rgba(255, 255, 255, 0.03); +} + +.inner { + width: min(var(--max), calc(100% - 40px)); + margin: 0 auto; + padding: 18px 0; + display: flex; + justify-content: space-between; + gap: 14px; + flex-wrap: wrap; +} + +.title { + font-weight: 750; +} + +.muted { + color: var(--muted); + font-size: 13px; +} + +.left { + display: grid; + gap: 4px; +} diff --git a/src/demo/components/SiteFooter/SiteFooter.tsx b/src/demo/components/SiteFooter/SiteFooter.tsx new file mode 100644 index 0000000..cc06c57 --- /dev/null +++ b/src/demo/components/SiteFooter/SiteFooter.tsx @@ -0,0 +1,15 @@ +import styles from './SiteFooter.module.scss'; + +export function SiteFooter() { + return ( +
+
+
+
Demo Shop
+
Built to be cloned and integrated fast.
+
+
Next.js + TypeScript + SCSS Modules
+
+
+ ); +} diff --git a/src/demo/components/SiteHeader/SiteHeader.module.scss b/src/demo/components/SiteHeader/SiteHeader.module.scss new file mode 100644 index 0000000..abc5986 --- /dev/null +++ b/src/demo/components/SiteHeader/SiteHeader.module.scss @@ -0,0 +1,89 @@ +@use "../../../styles/variables" as *; + +.header { + position: sticky; + top: 0; + z-index: $z-index-header; + background: rgba(11, 13, 18, 0.72); + backdrop-filter: blur(12px); +} + +.inner { + width: min(1120px, calc(100% - 48px)); + margin: 0 auto; + padding: 14px 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; +} + +.left { + display: flex; + align-items: center; + gap: 12px; + min-width: 0; +} + +.logo { + width: 34px; + height: 34px; + border-radius: 12px; + background: radial-gradient(circle at 30% 30%, rgba(166, 177, 255, 0.95), rgba(124, 92, 255, 0.9)); + box-shadow: 0 16px 36px rgba(0, 0, 0, 0.35); + flex: none; +} + +.brand { + display: grid; + gap: 2px; + min-width: 0; +} + +.name { + font-weight: 900; + letter-spacing: -0.01em; + color: $color-text; +} + +.sub { + font-size: 12px; + color: $color-muted; +} + +.nav { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.link { + font-size: 14px; + color: $color-muted; + padding: 8px 10px; + border-radius: 999px; + border: 1px solid transparent; + transition: $transition-default; +} + +.link:hover { + color: $color-text; + background: rgba(255, 255, 255, 0.04); + border-color: $color-border; +} + +.cta { + border: 1px solid $color-border; + background: $color-surface; + color: $color-text; + border-radius: 999px; + padding: 10px 12px; + font-weight: 800; + cursor: pointer; + transition: $transition-default; +} + +.cta:hover { + background: $color-surface2; +} \ No newline at end of file diff --git a/src/demo/components/SiteHeader/SiteHeader.tsx b/src/demo/components/SiteHeader/SiteHeader.tsx new file mode 100644 index 0000000..bd4db3c --- /dev/null +++ b/src/demo/components/SiteHeader/SiteHeader.tsx @@ -0,0 +1,36 @@ +import styles from './SiteHeader.module.scss'; + +type Link = { label: string; href: string; external?: boolean }; + +export function SiteHeader({ links, onOpenCart }: { links: Link[]; onOpenCart: () => void }) { + return ( +
+
+
+ + + +
+
+ ); +} diff --git a/src/demo/components/UseCases/UseCases.module.scss b/src/demo/components/UseCases/UseCases.module.scss new file mode 100644 index 0000000..d057d7c --- /dev/null +++ b/src/demo/components/UseCases/UseCases.module.scss @@ -0,0 +1,43 @@ +@use "../../../styles/variables" as *; + +.grid { + display: grid; + grid-template-columns: repeat(12, 1fr); + gap: $spacing-md; +} + +.action { + grid-column: span 4; + border: 1px solid $color-border; + background: $color-surface; + color: $color-text; + border-radius: $radius-md; + padding: 14px 16px; + min-height: 56px; + text-align: left; + font-weight: 700; + cursor: pointer; + transition: $transition-default; +} + +.action:hover:not(:disabled) { + background: $color-surface2; + transform: translateY(-1px); +} + +.action:disabled { + opacity: 0.48; + cursor: not-allowed; +} + +@media (max-width: 980px) { + .action { + grid-column: span 6; + } +} + +@media (max-width: 680px) { + .action { + grid-column: span 12; + } +} \ No newline at end of file diff --git a/src/demo/components/UseCases/UseCases.tsx b/src/demo/components/UseCases/UseCases.tsx new file mode 100644 index 0000000..0ee256c --- /dev/null +++ b/src/demo/components/UseCases/UseCases.tsx @@ -0,0 +1,40 @@ +import type { CartProduct } from '../../../cart/types/types'; +import { useCart } from '../../../cart/hooks/useCart'; +import styles from './UseCases.module.scss'; + +export function UseCases({ + products, + onOpenCart, +}: { + products: CartProduct[]; + onOpenCart: () => void; +}) { + const { addItem } = useCart(); + + const apple = products.find((p) => p.id === 'apple') ?? products[0]; + const banana = products.find((p) => p.id === 'banana') ?? products[1]; + + return ( +
+ + + + + + + + + +
+ ); +} diff --git a/src/globals.scss b/src/globals.scss new file mode 100644 index 0000000..3a39d8c --- /dev/null +++ b/src/globals.scss @@ -0,0 +1,56 @@ +@use "./styles/variables" as *; + +:root { + --text: #{$color-text}; + --muted: #{$color-muted}; + --border: #{$color-border}; + --surface: #{$color-surface}; + --surface2: #{$color-surface2}; + --radius: #{$radius-md}; + --radius-sm: #{$radius-sm}; + --shadow: #{$shadow-md}; + --gap: #{$spacing-md}; + --max: 1120px; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body, +#root { + min-height: 100%; + margin: 0; +} + +body { + background: + radial-gradient(1200px 700px at 20% -10%, rgba(124, 92, 255, 0.22), transparent 60%), + radial-gradient(1000px 700px at 85% 0%, rgba(166, 177, 255, 0.14), transparent 55%), + #0b0d12; + color: $color-text; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + line-height: 1.4; + overflow-x: hidden; +} + +a { + color: inherit; + text-decoration: none; +} + +button, +input, +textarea, +select { + font: inherit; +} + +img, +svg { + display: block; + max-width: 100%; +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index c9f98e8..ecb4354 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,7 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.tsx'; import { CartProvider } from './cart/context/CartProvider'; -import './cart/components/snappycart.scss'; +import '../src/globals.scss'; ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 1f86a39..38f7a85 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -1,45 +1,51 @@ // ---------------- -// Snappycart Theme +// snappycart theme // ---------------- // Colors -$color-primary: #2F6846; // Rich botanical green -$color-accent: #FFD56B; // Soft honey yellow -$color-bg: #FAFAF7; // Clean off-white -$color-text: #2B2B2B; // Elegant charcoal -$color-muted: #D8D2C2; // Neutral taupe -$color-border: #E5E5E0; // Light border grey -$color-success: #30A46C; // Action green -$color-danger: #D9534F; // Warning red +$color-bg: #0b0d12; + +$color-text: rgba(255, 255, 255, 0.92); +$color-muted: rgba(255, 255, 255, 0.62); +$color-border: rgba(255, 255, 255, 0.12); + +$color-surface: rgba(255, 255, 255, 0.06); +$color-surface2: rgba(255, 255, 255, 0.10); + +$color-primary: #7c5cff; +$color-accent: #a6b1ff; + +$color-success: #2bd4a6; +$color-danger: #ff4d6d; // Typography -$font-size-base: 1rem; -$font-size-sm: 0.875rem; -$font-size-lg: 1.25rem; -$font-weight-normal: 400; -$font-weight-bold: 600; +$font-size-base: 1rem; +$font-size-sm: 0.875rem; +$font-size-lg: 1.25rem; +$font-weight-normal: 400; +$font-weight-bold: 600; // Spacing -$spacing-xs: 0.25rem; -$spacing-sm: 0.5rem; -$spacing-md: 1rem; -$spacing-lg: 1.5rem; -$spacing-xl: 2rem; +$spacing-xs: 0.25rem; +$spacing-sm: 0.5rem; +$spacing-md: 1rem; +$spacing-lg: 1.5rem; +$spacing-xl: 2rem; // Border radius -$radius-sm: 6px; -$radius-md: 12px; -$radius-lg: 20px; +$radius-sm: 6px; +$radius-md: 12px; +$radius-lg: 20px; // Shadows -$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06); -$shadow-md: 0 4px 10px rgba(0, 0, 0, 0.1); -$shadow-lg: 0 8px 20px rgba(0, 0, 0, 0.12); +$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.18); +$shadow-md: 0 8px 22px rgba(0, 0, 0, 0.26); +$shadow-lg: 0 16px 44px rgba(0, 0, 0, 0.34); // Transitions -$transition-default: 0.2s ease; +$transition-default: 0.2s ease; // Z-index -$z-index-header: 1000; -$z-index-drawer: 1100; -$z-index-modal: 1200; +$z-index-header: 1000; +$z-index-drawer: 1100; +$z-index-modal: 1200; \ No newline at end of file From 0d0916ff92d6a2e527e8a94d972578192375d2c3 Mon Sep 17 00:00:00 2001 From: idncod Date: Sun, 8 Mar 2026 17:39:14 +0000 Subject: [PATCH 3/5] chore: changeset --- .changeset/floppy-bears-kneel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/floppy-bears-kneel.md diff --git a/.changeset/floppy-bears-kneel.md b/.changeset/floppy-bears-kneel.md new file mode 100644 index 0000000..3ebc157 --- /dev/null +++ b/.changeset/floppy-bears-kneel.md @@ -0,0 +1,5 @@ +--- +'snappycart': major +--- + +Getting ready for release by prepping the demo for users (developers). From 130e66cdccbd8e34a39f8971015f4610169cc5d7 Mon Sep 17 00:00:00 2001 From: idncod Date: Sun, 8 Mar 2026 18:01:40 +0000 Subject: [PATCH 4/5] feat: add cartIcon --- src/cart/components/CartIcon/CartIcon.tsx | 7 ++++++- src/main.tsx | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cart/components/CartIcon/CartIcon.tsx b/src/cart/components/CartIcon/CartIcon.tsx index 022f615..f3a6ccc 100644 --- a/src/cart/components/CartIcon/CartIcon.tsx +++ b/src/cart/components/CartIcon/CartIcon.tsx @@ -16,7 +16,12 @@ export default function CartIcon({ position = 'bottom-right', onClick }: CartIco : 'sc-bottom-right'; return ( - diff --git a/src/main.tsx b/src/main.tsx index ecb4354..ed050f1 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client'; import App from './App.tsx'; import { CartProvider } from './cart/context/CartProvider'; import '../src/globals.scss'; +import './cart/components/snappycart.scss'; ReactDOM.createRoot(document.getElementById('root')!).render( From af4cf99ca16fdc3d235ef569c298be16e063b062 Mon Sep 17 00:00:00 2001 From: idncod Date: Sun, 8 Mar 2026 18:06:23 +0000 Subject: [PATCH 5/5] feat: add correct github link --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 36d59b0..1a58978 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -40,7 +40,7 @@ export default function App() { { label: 'Quickstart', href: '#quickstart' }, { label: 'Use cases', href: '#use-cases' }, { label: 'Demo shop', href: '#demo-shop' }, - { label: 'GitHub', href: 'https://github.com/your-org/snappycart', external: true }, + { label: 'GitHub', href: 'https://github.com/idncod/snappycart', external: true }, { label: 'npm', href: 'https://www.npmjs.com/package/snappycart', external: true }, ]} />