โ† Back to Projects Live Demo โ†—

QLess

Web Development

Campus Pre-Order Web App

The QLess ordering flow started as a Figma prototype. This is what happened when it got built out as an actual web app: menu, cart, slot booking and OTP pickup, running in the browser with no framework and no backend to hide behind.

HTML5CSS3Vanilla JSNo framework
QLess project placeholder

01

Problem

What problem were you solving?

A Figma prototype can hide a lot of assumptions: every screen looks reachable, every state looks handled, because someone drew it that way. The question was whether the pre-order flow designed in the QLess UX case study would hold up once it had to run on real state instead of pre-wired hotspots.

02

Solution

What did you build?

A deliberately tight-scope build, no server, no database, no login, just the core ordering loop a single-shop canteen needs, running entirely in the browser off nothing but JavaScript state and the DOM.

03

Key Features

๐Ÿ›’

Menu & cart

One shared state object drives every re-render.

๐Ÿ•’

Slot capacity

Pickup times can actually fill up and lock out.

๐Ÿ”

OTP pickup

A real, live countdown, not a static screenshot.

๐Ÿ“„

Single-file app

View-switching via class toggles, no page reloads.

04

Architecture

How does it work?

MenuItem list + cart state
โ†’
CartSticky bar, live totals
โ†’
Slot PickerCapacity-checked times
โ†’
ConfirmSimulated payment
โ†’
OTP PickupLive countdown

A plain JS object keyed by item ID tracks quantities. Every add, remove or quantity change re-renders the menu list and cart bar from that single source of truth, no separate DOM state to fall out of sync.

05

Tech Stack & Tools

LayerTools
Markup & stylingHTML5, CSS3
LogicVanilla JavaScript, no framework
StateIn-memory cart state, slot capacity model

06

Technical Decisions

Why these technologies?

Why no framework?

A single cart object and a handful of render functions were enough to keep five views in sync, React would have added ceremony without solving a problem that existed.

Why cap slot capacity?

The zero-queue promise only means anything if slots can actually run out: an unlimited list of times would have made the demo hollow.

Why a real countdown?

A live timer, instead of a static screenshot of one, is what makes the collect-by-code pickup pattern feel true.

Why one HTML file?

Toggling a single active class between views keeps the whole flow, menu, cart, slots, OTP, running without a page reload breaking state.

07

Results

0dependencies, no framework, no build step
5connected views sharing one state object
10 minlive OTP countdown, not a mocked screen
1 filethe entire app ships as a single page

The design case study's "guaranteed pickup" promise only became a real engineering problem once slots needed actual capacity limits: a prototype can't run out of anything, an app has to decide what happens when it does.

08

Challenges

What difficulties did you face?

  • Keeping five views in sync off one state object without a framework
  • Modelling slot capacity so it fails gracefully once full
  • Making a backend-free demo still feel like a real system

09

Limitations

What could be better

  • No real backend, payment, or persistence across sessions
  • Single-shop menu only, not multi-vendor
  • State resets on page reload by design

10

What I Learned

  • A working build finds what a mockup hides
  • State management doesn't need a framework at this scale
  • Constraints make a demo feel true

11

Future Improvements

  • A real backend for persistence across sessions
  • Multi-vendor menu support
  • Actual payment integration in place of the simulated step