Skip to content
Codeino
Work2026 · Client project · demo build, no backend

CERNY STORE

An Arabic-first storefront for an Egyptian hardware importer — built from the shop's own PDF price list, with product photography generated rather than licensed, and a checkout that runs end to end without a server.

213 products across 11 categories, 221 pages pre-rendered at build time, and zero environment variables needed to run any of it.

The CERNY STORE home page — Arabic right-to-left, on a limestone ground
Role
Brand, design & build, end to end
Scope
Storefront + admin — 8 routes, 221 pages
Stack
Next.js 14 · TypeScript · Tailwind 3
Supplied
An 80-page PDF price list
01

The brief

The client had an existing site with a dead Supabase project behind it, an empty catalogue, and a name they wanted to change. What they did have was an 80-page PDF: their own counter price list, rows of stock with photographs and hand-typed prices in Egyptian pounds.

The ask was a storefront that reads as a real importer rather than a template — and that could be shown to people without a database, a dashboard login, or a support call when a demo server goes to sleep.

Two constraints shaped everything. No backend, which is a scoping decision rather than a gap, and turns the interesting question into which parts of a shop you can make genuinely work without one. And no stock photography budget and no product shoot — which turned photography from a purchasing problem into an engineering one.

02

What shipped

  • Home, shop, product, cart, checkout, payment, order confirmation, admin and 404
  • 213 products across 11 categories, with search, brand filter, a price ceiling and four sort orders
  • Cart with free-shipping progress and quantity limits bound to real stock
  • Validated checkout — Egyptian mobile-number format, all 27 governorates
  • Four payment paths: Vodafone Cash, InstaPay, bank transfer and cash on delivery
  • Admin: order moderation with receipt preview, status filters and a live revenue tally
  • 221 pages pre-rendered at build time, and no environment variables to run any of it
  • A 10-step Playwright walk covering cart, checkout validation, payment, confirmation and admin moderation — passing against the live deployment with no console errors
products
213
pages pre-rendered
221
product images, none licensed
287
env vars to run it
0
The shop grid with its filter sidebar on the right
Search, brand filter, a price ceiling and four sort orders across 213 products. The filter rail sits on the right because that is where the eye starts.
A product page for a 34-inch ultrawide monitor, its curve visible in the render
Not a photograph. The panel bows because a clip-path arc puts the ends nearer the eye and drops the centre away — and ultrawides are drawn at 21:9 rather than cropped to it.
A contact sheet of rendered products, all on the same lit backdrop
111 of the 287 product images, drawn in the browser. One light setup across the whole catalogue, and re-rendering the set after a palette change is one command.
The admin panel reviewing an order, with a live revenue tally
Order moderation with receipt preview, status filters and a running total. The customer details here are seeded demo data — the shop has never taken a real order.
03

Decisions, and why

Anyone can list a stack. The useful part of a case study is the calls that had a real alternative — so each of these says what the tradeoff was, not just what was chosen.

  1. 01

    No backend at all

    The catalogue is a TypeScript module; orders live in the browser.

    The previous build depended on Supabase for products, orders, receipt storage and admin auth. The project was dead, so the site rendered empty. Moving the catalogue into a typed module and orders into the browser made the whole purchase path work with zero configuration — clone it, install, and the shop is running.

    Tradeoff · Orders do not survive across devices, and the admin sees only what that browser created. The swap-in point is one file — the demo order store already exposes the exact interface an API would need.

  2. 02

    Product photography is rendered, not licensed

    111 studio shots drawn in CSS 3D and captured with Playwright.

    Stock libraries do not have a photograph of the specific case this shop sells. The alternative was mismatched images — a white mid-tower with four ARGB fans represented by someone else's black tower. Instead each product carries a render spec — body colour, fan count, glass, mesh, curvature, ultrawide — and a script draws it as a real 3D box on a lit backdrop and screenshots it at 1200×900.

    Tradeoff · They are illustrations, not photographs, and the page says so. In exchange every shot matches the product it names, the whole catalogue shares one light setup, and re-rendering the set after a palette change is one command.

  3. 03

    The client's PDF became half the catalogue

    176 products extracted from an 80-page price list, photographs included.

    The PDF was the only real inventory data in existence. A three-stage pipeline reads it, matches each embedded photograph to its row by vertical position on the page, recovers the Arabic — which the file stored in reversed visual order — infers a category, and emits typed catalogue entries.

    Tradeoff · Names come from a source with inconsistent spelling, so a handful of the 176 read awkwardly and want hand-editing. The pipeline is committed, so running it against a newer price list is one command rather than a re-type.

  4. 04

    The shop's own photos were moved onto the catalogue backdrop

    154 of the 176 photographs had their studio white flood-filled away.

    Real counter photos arrived on white; the rendered products sit on a warm sand gradient. Mixed together, the white plates read as pasted-in boxes. A flood fill from the image borders removes the background where it is genuinely uniform, and the product is composited onto the same backdrop with the same contact shadow.

    Tradeoff · The fill refuses itself when it would eat more than 82% of the frame — a white product on white would lose its edges. Those 22 images keep their original background behind a softened rounded plate, which reads as an honest “shot in the shop” photo rather than a failed cutout.

  5. 05

    A light sand ground, not the default dark gaming look

    Limestone #FDFBF7, Egyptian faience blue #1D4E89, gold on hairlines only.

    Every design recommendation for “gaming hardware store” returns near-black with a neon accent. It is a defensible look and it is also what every competitor already has. The palette here is drawn from Egyptian material culture instead — limestone ground, faience blue as the brand colour, gold leaf restricted to rules and small marks — which lets the dark product shots carry the contrast.

    Tradeoff · Less immediately “gamer” than a neon build. In exchange the products are the darkest thing on the page, and long Arabic body copy stays readable.

  6. 06

    Arabic-first, but prices in Western digits

    The interface is Arabic; 2,950 ج.م is not ٢٩٥٠ ج.م.

    The whole site is right-to-left with Egyptian-dialect copy — error messages, empty states and button labels all read like a person wrote them. Prices break the rule deliberately: Egyptian price tags, invoices and WhatsApp quotes use Western digits, so matching the market beats internal consistency. Specification values keep Arabic-Indic digits, because that is how spec sheets read.

    Tradeoff · Two numbering systems on one page look inconsistent in a screenshot. They look normal to the person actually buying.

  7. 07

    Cash on delivery is a first-class path, not a fallback

    Four payment methods, and the one most customers pick needs no receipt.

    Wallet transfers require the customer to upload a receipt image, which the admin then reviews — a real two-sided flow. Cash on delivery skips that entirely and moves the order straight to review. Both paths land on the same confirmation receipt.

    Tradeoff · The uploaded receipt is held in the browser rather than object storage, so it is bounded in size and never leaves the device. That is the demo boundary, and the upload component is the only piece that changes.

  8. 08

    The catalogue is typed, and the type is enforced

    Two sources, one product interface, one array.

    Imported stock and counter stock come from different pipelines and genuinely have different shapes — one carries a render spec, the other a photograph path. Rather than branch the interface, both satisfy one type with the render spec optional, and the app sees a single products array. Adding a third source later touches no component.

    Tradeoff · The generated catalogue file is several thousand lines checked into the repository. In exchange the build catches a malformed product at compile time instead of at render time, and every product page is static.

04

Making the photography

Two hundred and eighty-seven product images ship with this site and not one was licensed.

The 111 imported-hardware shots are drawn in the browser. A PC case is built as an actual CSS 3D box — front, side and top faces with their own gradients — with a tempered-glass overlay, a motherboard and a graphics card behind it, and fans whose rings are a conic rainbow when the product is ARGB and dark when it is not. Monitors use a clip-path arc so a curved panel genuinely bows: the ends sit nearer the eye and the centre falls away. Ultrawides are drawn at 21:9 rather than cropped to it. Playwright loads each spec, waits for paint, and screenshots a 1200×900 frame.

The first pass had every case floating. The camera was below the object, so the render showed its underside and the contact shadow sat in open space. Inverting the X rotation and anchoring the shadow to the projected base fixed both. The mice were worse — rounded rectangles that read as eggs — and were rebuilt from a clip-path silhouette with separated left and right buttons, a recessed scroll wheel and a side thumb rest.

The 176 counter photographs took the opposite journey: out of the client's PDF, through border trimming, background knockout and re-compositing, onto the same lit backdrop as everything else. The two halves of the catalogue were shot in different decades by different people, and on the page they look like one session.

05

What was deliberately left out

Scope discipline is part of the work. Each of these was considered and cut for a stated reason.

Customer accounts
Nothing on this site needs an identity. Orders are found by reference number.
A real payment gateway
Needs a live merchant account. The four methods model the flow honestly instead.
Product editing in the admin
The catalogue is a typed module; editing it in a browser would mean shipping a database.
Review text
The rating figures are seeded placeholders. Publishing invented review text on a shop that takes orders is deceptive advertising.
A second language
The customers are Egyptian. An English toggle would be scope, not reach.

Ratings and review counts are deterministic placeholder values, and the catalogue file says so. Prices and product names are real — researched against the Egyptian import market for the imported stock, and taken from the shop's own price list for the rest. There are no real customers, no real orders and no payment integration: this is a demo, and the site says so in its own footer.

Stack

  • Next.js 14
  • React 18
  • TypeScript
  • Tailwind CSS 3
  • Playwright
  • Python
  • Vercel

Services

  • Brand identity
  • Product design
  • Frontend engineering
  • Data pipeline
  • Generated imagery

Need something built to this standard?

We design and build complete systems — a purchase path that works, roles, and an admin panel that actually runs the business. And Arabic here is never a translation layer bolted on at the end.