Skip to content

Haven

Live preview ->

Haven is a storefront for furniture and homewares. The first screen is a photograph of a furnished room, and markers on four pieces in it open a small card with the product’s picture, price and a link. Below it come shop benefits, category tiles, new arrivals with load more, two promotional banners, trending products in tabs, the latest journal articles, a newsletter band and a gallery that leads to the lookbook.

The whole catalog flow is there: collection pages with sorting, filters, grid density and pagination, a search drawer, quick view, product pages with colour options and stock, and a cart, wishlist and comparison that are saved in the browser. It all runs on demo data in one file, so no store account is needed to try it.

PropertyValue
TierPro ($49)
CategoryEcommerce / Homewares
FontInter (self-hosted)
PaletteInk (#222) and deep teal (#254b4e) on white, with a sale red (#a6534d)
MotionGSAP ScrollTrigger reveals, room parallax, Lenis smooth scrolling on a mouse or trackpad, page fades
StylesPlain CSS with variables in src/styles/global.css, no CSS framework
Catalog28 products, 5 categories, defined entirely in site.ts
ConfigurationEvery product, label, page and article lives in src/config/site.ts
  • / - room hero, benefits, category tiles, new arrivals, promotions, trending products, journal preview, newsletter and gallery
  • /collections/all/ and /collections/[slug]/ - all products and the five collections, with sorting, filters, grid density and pagination
  • /products/[slug]/ - one page per product
  • /search/, /cart/, /wishlist/
  • /lookbook/
  • /journal/ and /journal/[slug]/ - the journal and its four articles
  • /about/, /contact/, /faq/, /care-guide/, /size-guide/, /shipping/, /returns/, /terms/, /privacy/, /account/
  • /404.html

The production build generates 55 static pages.

  • A room you can shop - markers on the room photograph open product cards, and you choose which products they point to and where they sit.
  • The whole catalog flow - filters, sorting and pagination stay in the address bar, so links can be shared and back and forward work.
  • Saved between visits - the cart, wishlist and comparison live in the browser and recover from stale or broken saved data.
  • Products in one file - prices, variants, stock, pictures and descriptions sit in site.ts with the rest of the copy.

Checkout in Haven is a demo. It shows the flow but takes no payments and creates no orders, and the stock numbers are not real inventory. To sell, connect your own payment provider or commerce backend. The catalog sits behind one module, src/lib/catalog.ts: keep the Product and Variant shapes the pages expect, map your backend’s data into them, and replace the local cart with your provider’s cart and checkout links.

The newsletter and contact forms check what is typed and then say plainly that nothing was sent. Point them at your own endpoint before you rely on them.

SITE_URL sets the canonical address for production. Set it when you build:

Terminal window
SITE_URL=https://shop.example.com npm run build

Then deploy the dist/ folder to any static host.

Each entry in products (at the end of src/config/site.ts) has a title, slug, category, brand, images, description, material, rating, dates and a list of variants. Prices live on the variants and are whole cents, so 3515 is $35.15:

{
id: "ribbed-oak-pendant",
slug: "ribbed-oak-pendant",
title: "Ribbed Oak Pendant",
category: "lighting",
variants: [
{ id: "ribbed-oak-pendant-1", title: "Natural", color: "#dedbd1", price: 3515, ... },
{ id: "ribbed-oak-pendant-2", title: "Black", color: "#222222", price: 3515, ... },
],
// ...
},

Keep every product and variant id unique and stable once the shop is live, because saved carts and wishlists refer to them. The slug sets the product’s address. A product with a tag containing extended stays out of the default all-products list but can still be found by search. The brand filter builds itself from the brands you use.

hero.hotspots places each marker by percentage of the room photograph and names the product it opens:

hero: {
image: "/images/generated/hero.webp",
hotspots: [
{ x: 79, y: 22, product: "ribbed-oak-pendant" },
// ...
],
},

On phones the markers follow the cropped photograph, markers that fall outside the crop are hidden, and an open card stays inside the screen.

  • categories - collection slugs, titles and pictures; each one gets its own collection page.
  • navigation, categoryNavigation, footerNavigation, footer - the menus.
  • hero, benefits, promotions, instagram - the homepage blocks.
  • catalog - the all-products title, banner, products per page (pageSize) and products on the homepage (homeCount).
  • pages - the about, contact, policy, guide and account pages; adding a key adds a page.
  • labels - every heading, button, form message and accessible label. Some contain placeholders such as {count} or {title}; keep those when you edit the words around them.

articles near the top of site.ts lists each article’s slug, title, date, picture and body. The four included articles share one body, articleBody; give an article its own body array of { tag: "p" | "h2", text: "..." } blocks to write it separately.

The colours are CSS variables at the top of src/styles/global.css (--ink, --teal, --sale and the rest), and themeColor in site.ts sets the browser bar colour.

Reveals, distances, timing, Lenis and the room parallax are set in src/scripts/motion.ts, and the easing and transition styles in src/styles/motion.css. The page fade between routes is set in src/layouts/Layout.astro. Reduced-motion settings are respected.

credit in site.ts holds the “Created by aerolaunch.app” line in the footer. Edit it, or set it to null to remove it.