Technology Stack
Next.js 16, React 19, TypeScript, Tailwind CSS 4, Framer Motion, HugeIcons, ESLint, Prettier, Vercel
Overview
Duration: 5/2026 - 6/2026
scrape.ly sells web scraping infrastructure — scrapers, proxies, an API, prebuilt templates, and a template store — to developers and data teams. The site covers the product lines, the resources, the company, and the legal surface.
The dependency list is deliberately short: Next, React, Tailwind, one animation library, one icon set, and nothing else. No UI kit, no CSS-in-JS. Everything visual is built on a design system defined in a single stylesheet and a small set of layout primitives — and that is the actual deliverable here. The pages are the easy part; the foundation is what determines whether the site still looks coherent after a year of other people adding to it.
The Grid System
Every page is composed from two primitives, and the whole site inherits its rhythm from them.
SiteGrid establishes a twelve-column grid. GridRow then splits each row identically every time — two columns of decor, eight of content, two of decor — as three components: a left decor cell, a content cell, and a right decor cell.
What makes it hold is the CSS underneath. Below the breakpoint, the grid is twelve equal columns. Above it, the template becomes fluid margin, gutter, eight content columns, gutter, fluid margin — so the content band is a fixed measure and all the extra space is pushed to the outside where it belongs. Rows stretch, which is why the decorative side cells always match the height of whatever content sits between them, with no manual sizing anywhere.
The decor cells are then configurable rather than fixed:
decordisables the side cells entirely for full-bleed rowsdecorMergedfuses each pair of outer columns into a single tile per side, removing the seamdecorGutterswitches the inner tile between its default and a dot motif used beside the navigation
The result is an editorial, visibly-gridded layout — the kind that is normally hand-built per page and starts drifting within a week — expressed as one row component that every page calls.
Token Architecture
The stylesheet opens by declaring its own layer order, then holds to it:
- A primitive palette of raw values, annotated as never to be referenced from components
- Semantic tokens — page, background, surface, border — mapped into the framework so they become utilities
- Typography, spacing, and radius scales feeding the corresponding utilities
- Base styles last
The discipline is the deliverable. A component reaches for bg-surface or border-border, never a hex value, and never the primitive behind the token either. Retheming the entire site is an edit to layer two, and nothing below it needs to be opened. That is the difference between a design system and a stylesheet with good intentions.
Routing & Link Integrity
Routes are organised with route groups — marketing, products, resources, company, legal — so the URL structure stays flat while the source tree groups by concern.
A typed routes module is the counterpart on the link side:
- A typed route map mirroring the application tree, so no destination is ever written as a bare string in a component — a renamed route becomes a type error rather than a 404 discovered in production
- Path builders composing URLs through one shared slug function rather than each caller formatting its own
- A single legal route that switches between terms, privacy, refund, and SLA by query rather than four near-identical pages
- A separate external map for off-site destinations, each entry annotated with which controls use it
The A/B Variant
The landing page ships in two versions with a shared component folder and a small variant folder for each.
The B hero needs a different grid treatment from every other row on the site. Rather than threading a variant flag down through the grid primitives — which would put a test concern permanently into the layout API — the stylesheet handles it directly:
.grid-row:has(.landing-b-hero) { /* … */ }
.grid-row:has(.landing-b-hero) .content-cell { /* … */ }
.grid-row:has(.landing-b-hero) .decor-cell { /* … */ }The row reshapes itself because of what it contains. The layout primitives stay unaware that variants exist, and adding a third would not touch them either. Marketing can test layouts without engineering paying for it in permanent API surface.
What makes this build stand apart from the other marketing work is that almost none of the value is in the pages. It is in a grid that can be described in one sentence, a token layer with an enforced rule about what may reference what, and a variant mechanism that adds nothing to the component API.