Skip to content

Repository files navigation

Digikala — rebuilt on Next.js 16

A pixel-for-pixel rebuild of the original static Digikala-style home page (page/index.html + style/style.css + script/script.js) on Next.js 16 (App Router), React 19, TypeScript, and Tailwind CSS v4 — plus a new /category/[slug] section so the site has real, multi-page routing.

Getting started

Requires Node.js ≥ 20.9 (Next.js 16's minimum).

npm install
npm run dev

Open http://localhost:3000. Other scripts:

npm run build       # production build (Turbopack, on by default in Next 16)
npm run start       # serve the production build
npm run lint        # eslint .
npm run typecheck   # tsc --noEmit

No manual setup steps are required beyond npm install.

What's here

Route Description
/ The original home page: hero slider, deals carousel, category grids, DigiPlus/DigiClub, bestsellers, blog, etc.
/products New: a full listing page — search, category/brand/price/rating/availability filters (desktop sidebar, mobile slide-over), sort, grid/list toggle, pagination, loading skeleton, empty state. 32 sample products spanning all 12 categories.
/cart New: a fully functional shopping cart — quantity controls, save-for-later, coupon + gift-card codes, shipping estimate, order summary, empty/loading states, recommended & recently-viewed rows. Client-side state only (React Context + localStorage), seeded with sample items — see "The cart has no backend" below.
/category/<slug> 12 statically-generated category pages — mobile, digital-goods, home-kitchen, fashion, supermarket-goods, books-stationery, toys, beauty-health, sport-travel, cars-motorcycles, local-products, gift-card — one per tile in the "دسته‌بندی‌های دیجی‌کالا" grid.
/sitemap.xml Generated from src/app/sitemap.ts, listing the home page and all 12 category pages.

Project structure

src/
  app/                    # Routes, root layout, global styles, favicon, sitemap
    category/<slug>/       # 12 static category route folders (see table above)
  components/
    layout/               # Header, Footer, TopPromoBanner (global site chrome)
    ui/                    # Small reusable primitives (SiteImage, PriceTag, MainSection, ...)
  features/
    home/                  # Every home-page-only section component + HomePage.tsx
    category/              # CategoryPageView + the shared route-page factory
  constants/               # Literal source data (typed, one file per content area)
  services/                # Thin functions in front of constants/ (the seam for a real API later)
  types/                   # Shared TypeScript interfaces
  hooks/                   # useCarousel (hero slider), useScrollToTop
  lib/                     # next/font/local config, generated image-dimensions manifest
  utils/                   # asset(), cn(), chunk() helpers
public/assets/             # All images, icons (fonts are self-hosted via next/font instead)

Enhancement phase: new dependencies, cart, and products listing

This project is being extended (not rebuilt) into a fuller e-commerce experience per an ongoing brief, in phases. Phases 1 (cart) and 2 (products listing) are done; see the roadmap at the end of this section for what's next.

Dependencies added beyond the original rebuild, because this phase's brief specifically calls for them:

  • framer-motion — item add/remove/save transitions on the cart page.
  • shadcn/ui-style primitives (class-variance-authority, clsx, tailwind-merge, lucide-react, and Radix UI primitives — react-slot, react-separator, react-checkbox, react-dialog) — Button, Input, Select, Checkbox, Separator, Badge, Skeleton, Sheet, Pagination in src/components/ui/, alongside the existing hand-built primitives (SiteImage, PriceTag, MainSection, ...) — same folder, no conflicts. They're themed via a @theme block at the top of globals.css that maps --color-primary etc. to colors already used elsewhere in the site (#ef4056 brand red, #eeeeee borders, #8a8a8a muted text) instead of shadcn's default zinc palette, so new components look native rather than bolted on. components.json documents the convention, even though it was hand-written here rather than run through the actual shadcn CLI (no network access in this environment to fetch it). The Sheet's open/close animation is hand-written CSS keyframes rather than the tailwindcss-animate plugin, to avoid a dependency for two rules.

The cart and product catalog have no backend, since none exists in this project:

  • src/features/cart/CartContext.tsx is a client-side React Context + useReducer, persisted to localStorage, seeded with 4 sample items. Coupon codes (DIGI10, WELCOME300) and gift-card validation (any 8+ character code) are intentionally simple stand-ins for what a real checkout/payments API would do.
  • src/constants/catalog.ts holds 32 sample products across all 12 categories, reusing photography already in /public/assets rather than introducing new images. src/services/productCatalogService.ts does the filtering/sorting client-side over that static array; swapping in a real product API means replacing that service's implementation, not the UI components that call it.
  • The products page's price filter is a plain min/max input pair rather than a slider — this matches how the real Digikala site's own filter UI works, and avoids taking on a dependency for a dual-thumb range control.

Neither page's header/nav links are new — /cart is reachable via the header's cart icon (now a real link with a live item-count badge), and /products doesn't yet have a nav entry point of its own. Wiring up fuller header navigation is part of the "Improve Existing Components" phase below, not bolted on ad hoc here.

Roadmap (later phases from the same brief, not yet built): an About page, Login/Register, a Framer Motion rewrite of the hero slider, and a broader visual-polish pass across the header/footer/dropdowns/inputs.

Bug fix: oversized, overlapping category-menu images

Reported with screenshots showing /category/* pages with huge, overlapping product photos instead of a clean icon grid. Root cause: .meno-item (the "دسته‌بندی‌های دیجی‌کالا" tiles) never had an img sizing rule in the original stylesheet — every sibling section (category-item, digikala-segustions-item) sizes its icons to 60×60px, but this one rule was missing, and the source photos are 718–1436px square. This was a bug in the original site itself, faithfully (if unfortunately) preserved during the pixel-perfect rebuild rather than caught. Fixed by adding the missing .meno-item img { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; } rule, matching its siblings.

While auditing for the same class of issue, a few other product-image sections had a related but milder problem — sized by width only, with height: auto or no object-fit, so cards in the same grid could end up different heights or (if a photo's aspect ratio didn't match its box) slightly stretched. Added object-fit: cover and, where the box's height was implicit, a fixed aspect-ratio to: the amazing-deals carousel, the discounted-products grid, DigiPlus's product row, and the bestselling lists.

Also added: a first pass of hover animations (soft lift via transform: translateY, shadow transitions, ~180ms ease-out) across category tiles, product cards, the DigiClub challenges, blog cards, the cart icon, and the shadcn Button component — the original stylesheet had exactly one hover rule in the whole site, so this was a real gap. The new /products page's ProductCard uses Framer Motion (whileHover) instead of CSS, per the brief's ask to use it "where appropriate" for product cards specifically.

Design decisions worth knowing about

Styling is the original stylesheet, not a Tailwind rewrite. The brief allowed CSS Modules or Tailwind "only if it reproduces the design exactly." The source stylesheet uses very specific pixel values, calc() grids, and custom breakpoints (1150/950/750/650/420px) that don't map onto Tailwind's default scale — rewriting ~1500 lines of it into utility classes by hand risked subtle visual drift. Instead, globals.css is the original stylesheet ported verbatim (same selectors, same values), with Tailwind v4 layered underneath it for any future work: its Preflight reset is intentionally skipped (via the @import "tailwindcss/theme.css" layer(theme) / utilities.css split) so it can never fight with the site's own reset, and the ported CSS is left unlayered so it always wins the cascade. The new /category pages do use a few plain CSS classes rather than Tailwind utilities too, just to stay visually consistent with the rest of the site — Tailwind's utilities layer is there and ready if you want to use it for new work.

Images. Every raster photo (jpg/png/webp) goes through next/image with its real intrinsic size (captured once into src/lib/image-manifest.ts), so you get lazy-loading, AVIF/WebP, and no layout shift — while the rendered size is still fully controlled by CSS, exactly as before. SVG icons and the one animated GIF banner (gif-disprice-banner.gif) intentionally bypass next/image (see SiteImage.tsx): SVGs gain nothing from raster optimization, and optimizing the GIF would strip its animation.

Fonts. The custom Yekan typeface is now self-hosted via next/font/local (src/lib/fonts.ts) instead of a hand-written @font-face. Only the .woff file is used — every browser Next.js 16 officially supports (Chrome/Edge/Firefox 111+, Safari 16.4+) already supports WOFF, so the legacy .eot/.ttf/.svg font files were dropped as genuine dead weight.

Filenames with spaces. Four source images had spaces in their filenames (e.g. iphone 13pro green.webp). They were renamed with hyphens (iphone-13pro-green.webp) for URL safety; nothing about how they look or where they're used changed.

The 12 category pages are new, and are static folders rather than a dynamic route. The original was a single HTML page, so there was nothing to "preserve" here — this section exists because the project needed real, reachable multi-page routing. It's implemented as 12 plain folders (app/category/mobile/, app/category/digital-goods/, ...) sharing one factory (src/features/category/categoryPageFactory.tsx) rather than a single dynamic app/category/[slug]/ route: there's a small, fixed, known set of exactly 12 categories, so a dynamic segment wasn't buying anything, and avoiding bracket characters in the file tree sidesteps a real compatibility problem — several Windows/mobile zip tools mishandle [/] in archive entries. Rather than inventing new product photography, each page reuses real images and price strings that already exist elsewhere on the site (the same way the original itself reuses the same placeholder prices across many unrelated products, and reuses the "دفتر" view-category group twice). Three categories (home & kitchen, toys, local products) have no matching product photos in the original asset set at all, so those honestly show an empty state instead of forcing an unrelated image into the grid.

Bugs and quirks preserved on purpose

The brief called for an exact rebuild, which sometimes means keeping things that look like mistakes rather than fixing them:

  • The footer's newsletter submit button has class .my-btn in the source (a literal typo — the dot is inside the class name). It has no matching CSS rule either way, and the page's one .my-btn click handler binds to the other .my-btn element (the back-to-top button) via querySelector, which only ever grabs the first match. The reproduction is faithful: the newsletter button renders unstyled-by-class (it's still styled by a parent selector) and does nothing on click.
  • In the first "بر اساس بازدیدهای شما" row, the "کیف و کاور گوشی" group's 4th image isn't wrapped in a link, unlike its siblings — kept as-is.
  • The "دفتر" (notebook) group appears twice, identically, across the two "بر اساس بازدیدهای شما" rows — a copy/paste artifact in the source, kept as-is.
  • In the discounted-products grid, only 2 of the 3 rows' last tiles get a border-left-no modifier (row 3's doesn't) — an inconsistency in the source markup, reproduced exactly rather than "fixed" with clean modular arithmetic.
  • All the header's inline category links (شگفت‌انگیزها, سوپرمارکت, ...) are plain, unlinked text in the source, not anchors — reproduced as bare <li> text.

Accessibility / metadata additions

These are additive and don't change anything visible, so they were made without hesitation per the brief's own accessibility section:

  • <html lang="fa" dir="rtl"> (the source had lang="en" despite being an entirely Persian, RTL page).
  • A <main> landmark around routed content.
  • Full Metadata (title template, description, Open Graph, Twitter card) and a generated SVG favicon — the original page had none of these at all.
  • generateMetadata per category page.

metadataBase in src/app/layout.tsx is currently a placeholder (https://dgikala-nextjs.example.com) — update it to your real domain before deploying, and consider adding a proper 1200×630 Open Graph image (none of the source's existing images are that shape, so one wasn't forced in).

About

A production-inspired Digikala clone built with Next.js 16, TypeScript, and Tailwind CSS, showcasing modern frontend architecture, responsive layouts, and reusable UI components.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages