DECKED
Ride. Style. Repeat.
What it is
DECKED is a Venice Beach skate shop for a brand that only exists in my head. You can browse forty-two products across six categories, filter the catalogue, view product pages, fill a cart, create an account, check out and come back later to find your past orders waiting for you.
Built with plain HTML, a single stylesheet and vanilla JavaScript, it has no framework, no build step and no backend.
What makes it interesting is that every feature a real shop would normally rely on a server for, from the cart to user accounts and order history, runs entirely in the browser.
The problem
Making it look like a skate shop wasn't the difficult part. Making it behave like a real shop without anything running behind it was.
A real store remembers your cart between pages, keeps you logged in and stores your orders after you close the browser. I wanted all of that while still deploying as a collection of static files on GitHub Pages.
The challenge is that DECKED is a true multi-page site. Every page navigation starts a fresh JavaScript environment, so nothing survives in memory. The cart on the catalogue page and the cart at checkout aren't the same running application, they're separate pages that have to rebuild the same state every time they load.
The decisions
One place for the truth
Everything the shop remembers lives in localStorage under four keys. Every page reads them when it loads and writes back immediately when something changes, making localStorage the single source of truth.
| Key | What it holds |
|---|---|
| decked_cart | Product IDs and quantities |
| decked_user | Name and email, or nothing |
| decked_orders | Number, date, items, total |
| decked_stock | Any stock levels I overrode |
The cart stores only product IDs and quantities rather than complete product data. Prices, names and images always come from the catalogue itself, ensuring every page shows the latest information without duplicating it.
Keeping every page in sync
A shared script runs across the entire site. On every page load it rebuilds the cart drawer, updates the cart counter and decides whether the navigation greets the user by name or shows a login link.
That lets each page focus on its own job rather than worrying about how the application's shared state was created.
A login that plays its part
Registration stores a name directly. Logging in only asks for an email address, then generates a display name from it, so jordan.vega@email.com becomes Jordan Vega.
Checkout and the account page both require a user. Visitors who aren't logged in are redirected to the login page before being returned to where they originally intended to go.
Turning a cart into an order
Completing checkout generates a DKD reference number, calculates shipping, saves the order, clears the cart and displays a confirmation without leaving the page.
Because the order is written to localStorage first, it remains in the account history even after the browser is closed.
What I'd do differently
The biggest limitation is that the login only plays the part. Passwords are never stored or verified, so any password works. Since users and orders both live in localStorage, anyone with browser developer tools can modify them. The checkout accepts payment details without processing them, and stock levels can be edited just as easily.
None of that is accidental. It reflects the limits of a project designed to run entirely as static files. Supporting real payments would mean moving carts, accounts and orders somewhere visitors couldn't access or change.
As a demonstration of how much of an e-commerce experience can live entirely in the browser, the approach achieved exactly what I wanted. As the foundation of a production store, it would be the first thing I'd replace.