Skip to content

docs: replace the homepage with the reactnativepaper.com landing page - #5138

Draft
rjmreis wants to merge 5 commits into
callstack:mainfrom
rjmreis:docs/landing-rework
Draft

rjmreis wants to merge 5 commits into
callstack:mainfrom
rjmreis:docs/landing-rework

Conversation

@rjmreis

@rjmreis rjmreis commented Oct 9, 2026 •

Copy link
Copy Markdown

Motivation

The project has two sites: the marketing landing page at reactnativepaper.com and the documentation site on GitHub Pages. This replaces the documentation site's homepage with the landing page, so there is one site to maintain.

Per Satyajit's direction: swap the whole homepage for the landing page plus the docs navbar links, rather than merging the two pages' content. Refreshing the landing copy is deliberately out of scope — it was written by marketing and should be a separate task.

Related issue

Notion: Merge reactnativepaper.com + oss.callstack.com/react-native-paper

Screenshots / Videos

Walkthrough of the ported homepage — hero through footer, with the live demo driven through its Snackbar, dark theme and Material You states, and the testimonial slider.

rn-native-paper-small-2.mp4

Test plan

yarn docs generate:5x
yarn docs build
yarn docs preview

Open the site root and compare against https://reactnativepaper.com. Both the 5.x and 6.x homepages render the landing page, so any future version inherits it.

In the phone demo: the Sign me up button raises the Snackbar at the bottom of the screen, the Dark theme and Material You chips both toggle and change the demo's palette, and the Appbar's external-link icon opens the Appbar docs.

The docs footer artwork should still appear on other custom pages — /showcase/ is the one to check — and be suppressed only on the homepage.


How it was ported

The visual rules are lifted verbatim from the original's compiled linaria stylesheet into docs/src/css/landing-vendor.css, and the components mirror the original DOM so they can use those class names. docs/src/css/landing.css holds only the webfonts and the overrides needed because the docs preset styles headings, body *, pre/code and font synthesis with !important.

Needs a decision before merge

Platform Web. The original sets its headings in Platform Web, a Commercial Type retail face. The files here come from the source repo's src/fonts/PlatformWeb and carry "Not to be used for anything other than web font use!". That repo is private; this one is public, so merging redistributes the font to anyone who clones or forks. Whoever holds Callstack's licence with Commercial Type needs to confirm it covers both this repo and the domain the site is served from. If the answer is no, reverting is two files and the @font-face — the fallback chain already drops to Roboto.

New dependency. prism-react-renderer in the docs workspace. It is what the original uses, and nothing else reproduces its editor theme, line numbers and colouring.

Deployment

This is the first of two PRs. The second flips base from /react-native-paper/ to / and adds the CNAME, paired with the DNS move: custom domain set in the repo settings, Cloudflare records added DNS-only so GitHub can issue the certificate, then Enforce HTTPS, then re-proxy with SSL set to Full (strict). The ordering matters.

Ported from callstack-internal/paper-landing-page ahead of merging
reactnativepaper.com and oss.callstack.com/react-native-paper onto a
single domain.

Both versioned homepages become `pageType: custom` rendering a shared
<Landing /> component, so any future version picks up the same page.
Section order follows src/pages/index.js in the source: hero, numbers,
look and feel, Material You, code, key concepts, testimonials, Discord,
Callstack. The docs navbar is kept; the rspress feature grid, screenshot
gallery and Callstack home banner are removed rather than merged.

Stats and the GitHub star count stay live, via the same endpoints the
original uses. Roboto and Roboto Mono are vendored to match the
original's body and code type.

Headings are set in Platform Web on the original. That is a Commercial
Type retail face whose webfont licence is domain-scoped and does not
allow redistribution, so the file is not committed and headings fall
back to Roboto. landing.css documents how to restore it once the
licensing is settled.

The docs preset styles headings, `body *`, and `pre`/`code` with
!important, and disables weight synthesis via `font-synthesis: style`.
The landing scope overrides these so the page renders with the
original's metrics.

Two fixes to the source's snippet: `TouchableWithNativeFeedback` does
not exist, and its StyleSheet object was missing a comma.
Measured both pages element by element with Playwright instead of
comparing screenshots by eye, and corrected what differed.

Sizing, all of which had been guessed: the demo videos are capped at
297x630 to hold the movie ratio rather than filling their column, the
Material You art is 720px tall and positioned out of flow, the Material
Design badge renders at its natural 225x195, play badges are 60px,
testimonial avatars 60px, the Discord mark 175px and the Callstack
wordmark 300px.

Hero and Material You run flush with a 700px floor, and Numbers and Code
drop their bottom padding. These needed to outrank .landing-section,
whose padding shorthand in the 992px media query was winning on order.

Section headings take the font's natural leading, not the preset's 1.2.
Numbers keeps the 60px item margin the source never clears in row
layout. Key concepts' subtitle is body copy at 460px, not the 30px
tagline used by Code.

Restores the header band above the hero and the headings dropped from
Discord and Callstack, and reorders both to match the source.

Remaining gaps are tracked: the hero wraps to four lines rather than
three because Platform Web is absent for licensing reasons and Roboto
is wider, and testimonials render as a grid rather than the source's
slider.
Cap the code panel at the source's 400x400 scroll box, draw the phone
frame with object-fit: cover so the demo fills the real 320x710 cutout,
give the demo its Appbar so content clears the notch, and add the
ornament between the panel and the phone.

Port the footer section, which had been left out entirely.

Set the comparison panels in Roboto Mono with the source's line
padding, and take the editor column's heading and lede metrics from the
original.
Port the testimonial cards and their slider, add the footer section and the
Callstack wordmark, and render all three code panels through
prism-react-renderer so they carry the original's colours and line numbers.

Restore Platform Web for the headings, add Roboto Medium, and undo the preset
overrides that were reaching the page: heading margins, leading and weight,
letter-spacing, and the rules that leaked into the live demo.

Make the Material You chip switch palettes rather than sit inert, and stop the
docs footer artwork painting below the landing page.
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Found potential problems with the pull request:

  • The description is too long. Please keep it under 1000 characters.

Drops the vendored Platform Web, which we cannot ship from a public repo,
and takes the face the rest of the docs site already uses.

Declares Alliance No. 2 here rather than relying on the theme: the copies
on assets.callstack.com are served without Access-Control-Allow-Origin,
so the theme's own declaration is CORS-blocked.

@satya164 satya164 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI workflow (lint, typecheck, tests) hasn't run on this PR yet, only the description check. It needs to pass before merge.

The "Needs a decision before merge" section in the description still talks about Platform Web, but the branch no longer ships it since the headings moved to Alliance. Can you update the description?

<Snackbar
visible={showSnack}
onDismiss={toggleSnack}
action={{ label: 'Dismiss', onPress: toggleSnack }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pressing "Dismiss" leaves the Snackbar open. Snackbar calls action.onPress and then onDismiss on an action press, so toggleSnack runs twice and the state flips back to true.

onDismiss={() => setShowSnack(false)}
action={{ label: 'Dismiss', onPress: () => {} }}

color={isMaterialYou ? undefined : md2AppbarContent(isDarkTheme)}
icon="open-in-new"
onPress={() =>
void Linking.openURL(docsHref('docs/components/Appbar/'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs/components/Appbar/ has no page, so this opens a 404. The build only has Appbar/Appbar, Appbar/AppbarAction etc. It also ignores the version, so on the 6.x homepage it opens the 5.x docs. LandingHero already handles this with getNextRoute.

Comment thread docs/5.x/_nav.json
"activeMatch": "^/docs/guides/"
},
{
"text": "Components",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the docs/5.x/docs/_meta.json change are the output of generate-5x-docs.ts. #5094 removed these entries on purpose, and the deploy workflow regenerates them before building. In production this is a no-op, and locally without generate:5x it adds a Components link that 404s. Please revert both files.

function useGithubStars() {
const [stars, setStars] = useState<string>(() => {
try {
return localStorage.getItem('cachedGithubStars') ?? STARS_FALLBACK;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading localStorage in the useState initializer causes a hydration mismatch. The prerendered HTML has 3000+, while the client's first render has the cached count on any repeat visit. Start from the fallback and read the cache in the effect.

const json = await response.json();

if (!cancelled) {
setData(json);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The response is stored without checking response.ok or its shape. If the API returns a JSON error body, item.value is undefined and toLocaleString throws during render, which takes down the homepage.

The endpoint is also a deployment URL of the old landing page's Vercel project, which the domain move is meant to retire. The fallback numbers are outdated too (7371 stars, it's 14474 now).

preload="none"
ref={videoRef}
>
<source src={asset(`videos/${name}.webm`)} type="video/webm" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WebM files are larger than the MP4s (3.4 MB vs 0.84 MB and 4.2 MB vs 1.9 MB), and listing WebM first makes browsers that support it download the larger one. Either swap the order or drop the WebM sources. material-you.png is also 3.1 MB and loads eagerly, which could use loading="lazy" or compression.

export const asset = (path: string) => withBase(`/landing/${path}`);

/** An absolute URL to a page of this docs site, for links opened via Linking. */
export const docsHref = (path: string) =>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react-native-web's Linking.openURL already resolves relative URLs with new URL(url, window.location), and the window check never matters since this only runs in onPress. withBase in LandingCode should be enough, and this helper can go.

Comment thread docs/src/css/landing.css

/* Testimonials render through a drag Slider in the source; these cards stand
in for it while keeping the original's proportions. */
.landing-testimonials {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing renders .landing-testimonials* anymore since the testimonials use the slider. The same goes for .tok-* further down. Both blocks and their comments can go.

src/assets/landing/dots.png and src/assets/landing/patterns/*.png are also unused, since the vendored CSS embeds the dot patterns as data URIs.

Comment thread docs/src/css/landing.css
src: url('../assets/landing/fonts/roboto-mono-400.woff2') format('woff2');
}

/* typography.css in the original: Roboto body, Alliance for h1/h2. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't belong to anything, the rule it describes is further down.

Comment thread docs/theme/index.tsx
import { Layout as BasicLayout } from '@rspress/core/dist/theme/index.js';

import Outline from './Outline';
import PaperHomeShowcase from '../src/components/PaperHomeShowcase';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this removed, docs/src/components/PaperHomeShowcase.tsx and docs/public/images/home/*.svg are unused. Can you delete them too?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants