# Marketly – Trading platform landing page

> A market-data landing page template with its dependent pages, built to be re-skinned: every color, type size, radius and shadow is a design token, every section is a data-driven component fed by typed mock JSON, and all copy lives in one file.

- Kind: Landing page
- Price: comes with the Pro plan
- License: UpFork Standard License. Use it in unlimited personal and commercial projects. Don't resell or redistribute it on its own.
- Developer: UpFork
- Tags: trading, fintech, stock-market, crypto, 3d-globe, nextjs, threejs, dark-mode
- npm packages: @base-ui/react, @react-three/drei, @react-three/fiber, @react-three/postprocessing, class-variance-authority, d3-geo, embla-carousel-react, framer-motion, lucide-react, simple-icons, three, topojson-client, world-atlas
- Published: 2026-10-15
- Updated: 2026-10-15
- Page: https://upfork.dev/products/marketly-trading-landing

## How to get it

Install it into a React + Tailwind project with the shadcn CLI:

```bash
npx shadcn@latest add @upfork/marketly-trading-landing
```

It installs with the member's personal CLI token (UPFORK_TOKEN), made on their UpFork account page. It comes with UpFork's Pro plan and the plans after it.

## About

A market-data **landing page template with its dependent pages**, built to be re-skinned: every color,
type size, radius and shadow is a design token, every section is a data-driven component fed by typed mock JSON,
and all copy lives in one file.

| | |
| --- | --- |
| **Stack** | Next.js 16 (App Router, TypeScript strict) · React 19 · Tailwind CSS 4 · shadcn/ui (Base UI) · framer-motion · three + @react-three/fiber + @react-three/drei · embla-carousel · d3-geo + world-atlas · lucide-react · flag-icons |
| **Pages** | `/` landing · `/pricing` · `/markets` · `/ideas` · `/brokers` · `/features` · `/news` · `/signin` · 404 |
| **Hero** | A pinned scrollytelling block: a procedurally rendered Earth (day/night textures, city lights, sunrise flare, atmosphere, stars, animated "price lines"), the headline scene parallaxing away while the Earth blurs into a warm bokeh and the showcase title and a glass video frame pin |
| **Header** | Transparent with white text over dark surfaces (the hero, the sub-pages' black bands); turns white with dark text the moment the page's white sheet scrolls under it |
| **Themes** | Light and dark (`data-theme` on `<html>`, persisted, no flash); the hero stays black in both |
| **Responsive** | 390px phones to 1920px desktops; header collapses to a full-screen drawer below 1024px |
| **A11y** | Keyboard-navigable menus and carousels, `aria-expanded`/`role="menu"`, visible focus rings, `prefers-reduced-motion` honoured (no parallax, no rotation), RTL-ready logical properties |

## Run it

```bash
pnpm install
pnpm dev          # http://localhost:3000
pnpm lint && pnpm build && pnpm start
```

Node.js 20.9+ (22 recommended) and pnpm. The build fetches the Plus Jakarta Sans font from Google Fonts once; to
go fully offline, switch `app/layout.tsx` to `next/font/local`.

## Project layout

```
app/                  routes: layout (fonts, theme, header, footer), the landing, the sub-pages, not-found
components/
  layout/             Header, HeaderBackdrop, MegaMenu, MobileDrawer, LangMenu, UserMenu, Search*, Footer, Logo, ThemeProvider
  hero/               HeroScroller (scroll choreography), EarthScene (R3F), Starfield, FloatingPill, PlatformVideo, …
  ui/                 the kit: SlashButton, Card, SymbolRow, SymbolLogo, TickerChip, PillTabs, Carousel, Sparkline, bars, Skeleton, Empty, …
  sections/           MarketSummary, FeaturedIpos, IdeasSection, NewsList, MarketCategory, GainersLosers, EarningsCalendar,
                      PerformanceBars, Economy (WorldMap, EconomicCalendar), TradingBrokers
  pages/              the sub-pages' interactive parts (pricing toggle, ideas grid, brokers filters, news tabs, markets anchors)
data/                 types.ts, copy.ts (all text), nav.ts (menus, languages, footer), *.json (mock data), markets/*.json
lib/                  api.ts (data layer stubs), format.ts (numbers, %, time ago), hooks.ts, cn.ts
styles/               globals.css (tokens + utilities + the slash button), themes.css (light/dark aliases)
scripts/              generators for the idea thumbnails, the platform poster and the market summary's series
public/               textures/, ideas/, hero/, avatars/
```

## Re-skin it

1. **Brand.** `data/copy.ts` → `brand` (name, tagline, legal name). The logo mark is `components/layout/Logo.tsx`
   (`LogoMark`); the browser icon is `app/icon.svg`.
2. **Palette.** `styles/globals.css` → the `@theme` block holds the raw scales (`--color-cold-gray-*`,
   `--color-brand-*`, market greens and reds). `styles/themes.css` maps them onto roles (`--bg-page`,
   `--fg-primary`, `--link`, `--border-subtle` …) for light and dark. Change the roles to re-color the whole site;
   change a scale to swap the brand hue. Components never carry a hex.
3. **Fonts.** `app/layout.tsx` loads the display font (`--font-plus-jakarta`); the body stack is `--font-body` in
   `globals.css`. Type sizes are `--text-*` tokens (`text-display`, `text-h1`, `text-h2`, `text-body`, `text-meta` …).
4. **Spacing and shape.** Radii (`--radius-card`, `--radius-chip`, `--radius-pill` …), the gutter
   (`--spacing-gutter`, 40px desktop / 16px phones) and shadows are tokens too.
5. **Copy.** Everything a visitor reads is in `data/copy.ts`; menus, languages and footer columns in `data/nav.ts`.
   The footer's legal lines (`footer.legal`) are placeholders: name your own data providers there.
6. **Direction.** The layout uses logical properties (`ps-`, `pe-`, `start-`, `end-`): set `dir="rtl"` on `<html>`
   in `app/layout.tsx` for a right-to-left language.

## Replace the mock data

`lib/api.ts` exposes the data the way a backend would (`getMarketSummary()`, `getMarketCategory("crypto")`,
`getNews("top")`, `getBrokers()` …) and currently returns the JSON under `data/`. Replace the function bodies
with fetches (CoinGecko, Binance, Finnhub …) and keep the return types from `data/types.ts`; no component changes.
The Market summary's charts read plain number lists: the big one is a trading day (`hero.spark` with
`hero.session`: the time of the first point and the minutes between points, from which the hourly axis and the
tooltip times are derived), the three small ones a month each. `node scripts/gen-market-series.mjs` regenerates the
mock curves. Every section also takes a `loading` prop that renders its skeleton at the same height, and shows "No data here
yet" when a list is empty.

Logos are described, not uploaded: `{ kind: "text", text: "500", bg: "#cc2f3c" }`, `{ kind: "icon", icon: "gold" }`,
`{ kind: "flag", country: "us" }`, `{ kind: "flags", countries: ["eu", "us"] }` or `{ kind: "image", src }`.

## Media placeholders

| What | Where | Replace with |
| --- | --- | --- |
| Platform demo (the glass frame) | `public/hero/platform-poster.png` (rendered from the SVG `scripts/gen-platform-poster.mjs` writes) | Your `platform.mp4` / `.webm` + poster: set the paths in `data/copy.ts` → `hero.platformDemo` |
| Earth fallback (no WebGL / low memory) | `public/hero/earth-poster.jpg` (a render of the 3D scene) | Any earth-from-orbit clip: `hero.earthFallback` |
| Story pill portrait | `public/avatars/placeholder.svg` | A 44×44 photo: `hero.avatar` |
| Idea thumbnails | `public/ideas/idea-0*.png` (generated by `scripts/gen-idea-thumbs.mjs`) | Real chart screenshots, referenced from the ideas JSON |

## The Earth

`components/hero/EarthScene.tsx`. Camera, sun direction, the spin (axis latitude, speed, starting point), atmosphere
colors, flare size and the line colors are constants at the top of the file. The planet turns about its own axis,
laid across the frame with north to the right: the camera hangs over 37°N looking west, the way an evening is seen
from orbit. The ground comes over the horizon in the evening light (land, deserts and clouds showing through a blue
veil of air), runs down the frame, crosses the terminator, and its towns light up one by one as they pass into the
night: each cell of NASA's Black Marble switches on at its own moment in the dusk, and up close the lights break
into points of different brightness. Along 37°N the frame sweeps the Mediterranean, Europe, the United States, Japan,
China, India and the Middle East; it starts over the Middle East. The sun sits just behind the horizon with rays all
round, two sets turning slowly against each other.

The "price lines" are charts standing up from the planet: each leaves one lit town, climbs in a jagged rising run
and drops onto another town's lights, which flare up as it lands. The lit spots are read from the night texture
itself; pairs are picked on the visible night side and across the frame, two lines at a time, clear of each other;
each draws in, holds, fades and starts again between two other towns. The scene pauses when the hero leaves the
viewport, renders a single still with `prefers-reduced-motion`, and hands over to `EarthFallbackVideo` when WebGL is
unavailable or `navigator.deviceMemory < 4`. Textures are in `public/textures/` (day at 2k, clouds at 1k, night
lights at 4k). `EarthSceneLazy` loads the three.js chunk separately and mounts the scene after the first idle moment,
so the headline and the rest of the page come first.

## Licenses

- **Earth textures** (`public/textures/`): the day and clouds maps from the
  [three.js](https://github.com/mrdoob/three.js) examples (MIT), themselves derived from NASA's Blue Marble; the
  night lights downscaled from NASA's [Black Marble 2016](https://earthobservatory.nasa.gov/features/NightLights)
  (public domain).
- **Country flags**: [flag-icons](https://github.com/lipis/flag-icons) (MIT). **Brand marks**:
  [simple-icons](https://simpleicons.org) (CC0). **Icons**: [lucide](https://lucide.dev) (ISC).
- **World map**: [world-atlas](https://github.com/topojson/world-atlas) (ISC), Natural Earth data (public domain).
- **Font**: Plus Jakarta Sans (SIL Open Font License), via `next/font/google`.
- Everything else in this folder is the template itself. The brand name "Marketly", its mark, the mock data and the
  generated images are placeholders for you to replace.
- The mock data names real companies, tickers and brokers only as sample content; it implies no affiliation or
  endorsement, and its prices, ratings, headlines and ideas are made up.
