diff --git a/docs/5.x/_nav.json b/docs/5.x/_nav.json index 0099eb059f..3634f9864d 100644 --- a/docs/5.x/_nav.json +++ b/docs/5.x/_nav.json @@ -4,6 +4,11 @@ "link": "/docs/guides/getting-started", "activeMatch": "^/docs/guides/" }, + { + "text": "Components", + "link": "/docs/components/ActivityIndicator", + "activeMatch": "^/docs/components/" + }, { "text": "Showcase", "link": "/docs/showcase", diff --git a/docs/5.x/docs/_meta.json b/docs/5.x/docs/_meta.json index 1a81e3fb19..9e4ac38659 100644 --- a/docs/5.x/docs/_meta.json +++ b/docs/5.x/docs/_meta.json @@ -4,6 +4,11 @@ "name": "guides", "label": "Guides" }, + { + "type": "dir-section-header", + "name": "components", + "label": "Components" + }, { "type": "file", "name": "showcase", diff --git a/docs/5.x/index.mdx b/docs/5.x/index.mdx index 91f12670f9..4e5fa36102 100644 --- a/docs/5.x/index.mdx +++ b/docs/5.x/index.mdx @@ -1,38 +1,7 @@ --- -pageType: home -hero: - name: Cross-platform Material Design for React Native - tagline: Paper is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. - actions: - - text: Get started - link: /docs/guides/getting-started - theme: brand - - text: Try on Snack - link: https://snack.expo.dev/@react-native-paper/react-native-paper-example_v5 - theme: alt -features: - - title: Production-ready components - details: Buttons, inputs, lists, cards, menus, dialogs, navigation, and feedback patterns built for day-to-day product work. - icon: /react-native-paper/images/home/components.svg - span: 4 - - title: Material You theming - details: Adopt Material 3 color, typography, elevation, and state layers with a theme system designed for real apps. - icon: /react-native-paper/images/home/palette.svg - span: 4 - - title: Accessible by default - details: Common interactions, contrast, and state handling are already baked into the component surface instead of left to each screen. - icon: /react-native-paper/images/home/accessibility.svg - span: 4 - - title: Cross-platform parity - details: Keep design language and component behavior aligned across iOS, Android, and React Native Web from one codebase. - icon: /react-native-paper/images/home/devices.svg - span: 4 - - title: Dark mode support - details: Ship light and dark experiences with the same primitives, including adaptive surfaces and semantic color roles. - icon: /react-native-paper/images/home/moon.svg - span: 4 - - title: Migration-friendly docs - details: Browse stable 5.x, preview 6.x, and legacy static docs from one version selector without splitting the docs experience. - icon: /react-native-paper/images/home/versions.svg - span: 4 +pageType: custom --- + +import Landing from '@docs/components/landing'; + + diff --git a/docs/6.x/index.mdx b/docs/6.x/index.mdx index 91f12670f9..4e5fa36102 100644 --- a/docs/6.x/index.mdx +++ b/docs/6.x/index.mdx @@ -1,38 +1,7 @@ --- -pageType: home -hero: - name: Cross-platform Material Design for React Native - tagline: Paper is a collection of customizable and production-ready components for React Native, following Google’s Material Design guidelines. - actions: - - text: Get started - link: /docs/guides/getting-started - theme: brand - - text: Try on Snack - link: https://snack.expo.dev/@react-native-paper/react-native-paper-example_v5 - theme: alt -features: - - title: Production-ready components - details: Buttons, inputs, lists, cards, menus, dialogs, navigation, and feedback patterns built for day-to-day product work. - icon: /react-native-paper/images/home/components.svg - span: 4 - - title: Material You theming - details: Adopt Material 3 color, typography, elevation, and state layers with a theme system designed for real apps. - icon: /react-native-paper/images/home/palette.svg - span: 4 - - title: Accessible by default - details: Common interactions, contrast, and state handling are already baked into the component surface instead of left to each screen. - icon: /react-native-paper/images/home/accessibility.svg - span: 4 - - title: Cross-platform parity - details: Keep design language and component behavior aligned across iOS, Android, and React Native Web from one codebase. - icon: /react-native-paper/images/home/devices.svg - span: 4 - - title: Dark mode support - details: Ship light and dark experiences with the same primitives, including adaptive surfaces and semantic color roles. - icon: /react-native-paper/images/home/moon.svg - span: 4 - - title: Migration-friendly docs - details: Browse stable 5.x, preview 6.x, and legacy static docs from one version selector without splitting the docs experience. - icon: /react-native-paper/images/home/versions.svg - span: 4 +pageType: custom --- + +import Landing from '@docs/components/landing'; + + diff --git a/docs/package.json b/docs/package.json index 50d5a6c934..683f33f452 100644 --- a/docs/package.json +++ b/docs/package.json @@ -20,6 +20,7 @@ "camelcase": "^7.0.1", "color": "^4.2.3", "marked": "^4.1.1", + "prism-react-renderer": "^2.4.1", "react": "19.2.6", "react-color": "^2.19.3", "react-dom": "19.2.6", diff --git a/docs/public/landing/images/appstore.svg b/docs/public/landing/images/appstore.svg new file mode 100644 index 0000000000..072b425a1a --- /dev/null +++ b/docs/public/landing/images/appstore.svg @@ -0,0 +1,46 @@ + + Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/arrow-left.svg b/docs/public/landing/images/arrow-left.svg new file mode 100644 index 0000000000..85356a2c0d --- /dev/null +++ b/docs/public/landing/images/arrow-left.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/public/landing/images/arrow-right.svg b/docs/public/landing/images/arrow-right.svg new file mode 100644 index 0000000000..fb048183cb --- /dev/null +++ b/docs/public/landing/images/arrow-right.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/public/landing/images/brent.jpg b/docs/public/landing/images/brent.jpg new file mode 100755 index 0000000000..014357ac50 Binary files /dev/null and b/docs/public/landing/images/brent.jpg differ diff --git a/docs/public/landing/images/finger.svg b/docs/public/landing/images/finger.svg new file mode 100644 index 0000000000..3ed68221a9 --- /dev/null +++ b/docs/public/landing/images/finger.svg @@ -0,0 +1,19 @@ + + + + + + + + + + 👆 + + + + + + + + + \ No newline at end of file diff --git a/docs/public/landing/images/google-play.svg b/docs/public/landing/images/google-play.svg new file mode 100644 index 0000000000..a01a8d876b --- /dev/null +++ b/docs/public/landing/images/google-play.svg @@ -0,0 +1,113 @@ + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/hero-image.png b/docs/public/landing/images/hero-image.png new file mode 100644 index 0000000000..e7f3e23be3 Binary files /dev/null and b/docs/public/landing/images/hero-image.png differ diff --git a/docs/public/landing/images/icons/accessibility-icon.svg b/docs/public/landing/images/icons/accessibility-icon.svg new file mode 100644 index 0000000000..f1b11f668f --- /dev/null +++ b/docs/public/landing/images/icons/accessibility-icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/callstack-dark.svg b/docs/public/landing/images/icons/callstack-dark.svg new file mode 100644 index 0000000000..47d16e88eb --- /dev/null +++ b/docs/public/landing/images/icons/callstack-dark.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/public/landing/images/icons/callstack.svg b/docs/public/landing/images/icons/callstack.svg new file mode 100644 index 0000000000..25ccb16dec --- /dev/null +++ b/docs/public/landing/images/icons/callstack.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/commit-icon.svg b/docs/public/landing/images/icons/commit-icon.svg new file mode 100644 index 0000000000..908cfcf887 --- /dev/null +++ b/docs/public/landing/images/icons/commit-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/discord.svg b/docs/public/landing/images/icons/discord.svg new file mode 100644 index 0000000000..435d4dc8f2 --- /dev/null +++ b/docs/public/landing/images/icons/discord.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/public/landing/images/icons/download-icon.svg b/docs/public/landing/images/icons/download-icon.svg new file mode 100644 index 0000000000..b62640c144 --- /dev/null +++ b/docs/public/landing/images/icons/download-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/live-icon.svg b/docs/public/landing/images/icons/live-icon.svg new file mode 100644 index 0000000000..054d0642d9 --- /dev/null +++ b/docs/public/landing/images/icons/live-icon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/paper-white.svg b/docs/public/landing/images/icons/paper-white.svg new file mode 100644 index 0000000000..5885b48f45 --- /dev/null +++ b/docs/public/landing/images/icons/paper-white.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/platform-icon.svg b/docs/public/landing/images/icons/platform-icon.svg new file mode 100644 index 0000000000..73eae8787a --- /dev/null +++ b/docs/public/landing/images/icons/platform-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/play.svg b/docs/public/landing/images/icons/play.svg new file mode 100644 index 0000000000..acc9be14f1 --- /dev/null +++ b/docs/public/landing/images/icons/play.svg @@ -0,0 +1,15 @@ + + + + Combined Shape + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/docs/public/landing/images/icons/star-icon.svg b/docs/public/landing/images/icons/star-icon.svg new file mode 100644 index 0000000000..0f47721a68 --- /dev/null +++ b/docs/public/landing/images/icons/star-icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/theming-icon.svg b/docs/public/landing/images/icons/theming-icon.svg new file mode 100644 index 0000000000..94ec174099 --- /dev/null +++ b/docs/public/landing/images/icons/theming-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/icons/twitter.svg b/docs/public/landing/images/icons/twitter.svg new file mode 100644 index 0000000000..bb69f89bb5 --- /dev/null +++ b/docs/public/landing/images/icons/twitter.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/public/landing/images/ip13.png b/docs/public/landing/images/ip13.png new file mode 100644 index 0000000000..48d0a6a7b3 Binary files /dev/null and b/docs/public/landing/images/ip13.png differ diff --git a/docs/public/landing/images/kurt.jpg b/docs/public/landing/images/kurt.jpg new file mode 100755 index 0000000000..bc9c750218 Binary files /dev/null and b/docs/public/landing/images/kurt.jpg differ diff --git a/docs/public/landing/images/material-badge.svg b/docs/public/landing/images/material-badge.svg new file mode 100644 index 0000000000..c8b0a62f3f --- /dev/null +++ b/docs/public/landing/images/material-badge.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + MATERIAL DESIGN + + + COMPLIANT + + + + + + react-native-paper + + + + + + + \ No newline at end of file diff --git a/docs/public/landing/images/material-you.png b/docs/public/landing/images/material-you.png new file mode 100644 index 0000000000..1ba8491e7f Binary files /dev/null and b/docs/public/landing/images/material-you.png differ diff --git a/docs/public/landing/images/ornament-small.svg b/docs/public/landing/images/ornament-small.svg new file mode 100644 index 0000000000..c765def134 --- /dev/null +++ b/docs/public/landing/images/ornament-small.svg @@ -0,0 +1,24 @@ + + + + Ornament + Created with Sketch. + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/landing/images/osadnik.jpg b/docs/public/landing/images/osadnik.jpg new file mode 100755 index 0000000000..332c29744c Binary files /dev/null and b/docs/public/landing/images/osadnik.jpg differ diff --git a/docs/public/landing/videos/paper-1-poster.png b/docs/public/landing/videos/paper-1-poster.png new file mode 100644 index 0000000000..6e2a0477d1 Binary files /dev/null and b/docs/public/landing/videos/paper-1-poster.png differ diff --git a/docs/public/landing/videos/paper-1.mp4 b/docs/public/landing/videos/paper-1.mp4 new file mode 100644 index 0000000000..efd5fe3376 Binary files /dev/null and b/docs/public/landing/videos/paper-1.mp4 differ diff --git a/docs/public/landing/videos/paper-1.webm b/docs/public/landing/videos/paper-1.webm new file mode 100644 index 0000000000..8a8f68edfb Binary files /dev/null and b/docs/public/landing/videos/paper-1.webm differ diff --git a/docs/public/landing/videos/paper-2-poster.png b/docs/public/landing/videos/paper-2-poster.png new file mode 100644 index 0000000000..33486c47fd Binary files /dev/null and b/docs/public/landing/videos/paper-2-poster.png differ diff --git a/docs/public/landing/videos/paper-2.mp4 b/docs/public/landing/videos/paper-2.mp4 new file mode 100644 index 0000000000..85bac5eb7e Binary files /dev/null and b/docs/public/landing/videos/paper-2.mp4 differ diff --git a/docs/public/landing/videos/paper-2.webm b/docs/public/landing/videos/paper-2.webm new file mode 100644 index 0000000000..855b12a3a7 Binary files /dev/null and b/docs/public/landing/videos/paper-2.webm differ diff --git a/docs/src/assets/landing/dots.png b/docs/src/assets/landing/dots.png new file mode 100755 index 0000000000..da0c2a0b5c Binary files /dev/null and b/docs/src/assets/landing/dots.png differ diff --git a/docs/src/assets/landing/fonts/PlatformWeb-Medium.woff b/docs/src/assets/landing/fonts/PlatformWeb-Medium.woff new file mode 100644 index 0000000000..f88150dc6a Binary files /dev/null and b/docs/src/assets/landing/fonts/PlatformWeb-Medium.woff differ diff --git a/docs/src/assets/landing/fonts/PlatformWeb-Medium.woff2 b/docs/src/assets/landing/fonts/PlatformWeb-Medium.woff2 new file mode 100644 index 0000000000..50f7975c01 Binary files /dev/null and b/docs/src/assets/landing/fonts/PlatformWeb-Medium.woff2 differ diff --git a/docs/src/assets/landing/fonts/roboto-300.woff2 b/docs/src/assets/landing/fonts/roboto-300.woff2 new file mode 100644 index 0000000000..52c5845a7c Binary files /dev/null and b/docs/src/assets/landing/fonts/roboto-300.woff2 differ diff --git a/docs/src/assets/landing/fonts/roboto-400.woff2 b/docs/src/assets/landing/fonts/roboto-400.woff2 new file mode 100644 index 0000000000..7e854e669b Binary files /dev/null and b/docs/src/assets/landing/fonts/roboto-400.woff2 differ diff --git a/docs/src/assets/landing/fonts/roboto-500.woff2 b/docs/src/assets/landing/fonts/roboto-500.woff2 new file mode 100644 index 0000000000..5ab8a6561a Binary files /dev/null and b/docs/src/assets/landing/fonts/roboto-500.woff2 differ diff --git a/docs/src/assets/landing/fonts/roboto-700.woff2 b/docs/src/assets/landing/fonts/roboto-700.woff2 new file mode 100644 index 0000000000..e327dc95b6 Binary files /dev/null and b/docs/src/assets/landing/fonts/roboto-700.woff2 differ diff --git a/docs/src/assets/landing/fonts/roboto-mono-400.woff2 b/docs/src/assets/landing/fonts/roboto-mono-400.woff2 new file mode 100644 index 0000000000..0a6a0df147 Binary files /dev/null and b/docs/src/assets/landing/fonts/roboto-mono-400.woff2 differ diff --git a/docs/src/assets/landing/hero-image2.png b/docs/src/assets/landing/hero-image2.png new file mode 100644 index 0000000000..7de3d61d51 Binary files /dev/null and b/docs/src/assets/landing/hero-image2.png differ diff --git a/docs/src/assets/landing/patterns/dark-pattern.png b/docs/src/assets/landing/patterns/dark-pattern.png new file mode 100644 index 0000000000..fad931ad8e Binary files /dev/null and b/docs/src/assets/landing/patterns/dark-pattern.png differ diff --git a/docs/src/assets/landing/patterns/light-pattern.png b/docs/src/assets/landing/patterns/light-pattern.png new file mode 100644 index 0000000000..ff809f012b Binary files /dev/null and b/docs/src/assets/landing/patterns/light-pattern.png differ diff --git a/docs/src/components/landing/LandingBanner.tsx b/docs/src/components/landing/LandingBanner.tsx new file mode 100644 index 0000000000..92b07265b4 --- /dev/null +++ b/docs/src/components/landing/LandingBanner.tsx @@ -0,0 +1,22 @@ +import LandingSection from './LandingSection'; + +/** + * Header.js in the source. The paper logo and GitHub link that flank it there + * are already provided by the docs navbar, so only the call to action remains. + */ +export default function LandingBanner() { + return ( + +
+
+
+

+ Need a customized UI kit for Android & iOS?{' '} + Get started +

+
+
+
+
+ ); +} diff --git a/docs/src/components/landing/LandingButton.tsx b/docs/src/components/landing/LandingButton.tsx new file mode 100644 index 0000000000..ec762f9e13 --- /dev/null +++ b/docs/src/components/landing/LandingButton.tsx @@ -0,0 +1,58 @@ +import type { ReactNode } from 'react'; + +import type { CSSVars } from './cssVars'; + +const PRIMARY = 'rgba(33, 0, 93, 1)'; +const WHITE = '#ffffff'; +const GREY = '#d8d8d8'; +const NEAR_BLACK = '#414757'; + +/** components/Button.js — `b1qn7xxj` reads six custom properties. */ +const variants: Record<'dark' | 'light', CSSVars> = { + dark: { + '--b1qn7xxj-0': PRIMARY, + '--b1qn7xxj-1': WHITE, + '--b1qn7xxj-2': WHITE, + '--b1qn7xxj-3': WHITE, + '--b1qn7xxj-4': PRIMARY, + '--b1qn7xxj-5': PRIMARY, + }, + light: { + '--b1qn7xxj-0': WHITE, + '--b1qn7xxj-1': GREY, + '--b1qn7xxj-2': NEAR_BLACK, + '--b1qn7xxj-3': GREY, + '--b1qn7xxj-4': GREY, + '--b1qn7xxj-5': NEAR_BLACK, + }, +}; + +type Props = { + children: ReactNode; + href: string; + dark?: boolean; + external?: boolean; +}; + +export default function LandingButton({ + children, + href, + dark, + external, +}: Props) { + return ( + + + + ); +} diff --git a/docs/src/components/landing/LandingCallstack.tsx b/docs/src/components/landing/LandingCallstack.tsx new file mode 100644 index 0000000000..9a7f043d59 --- /dev/null +++ b/docs/src/components/landing/LandingCallstack.tsx @@ -0,0 +1,25 @@ +import LandingButton from './LandingButton'; +import LandingSection, { asset } from './LandingSection'; + +export default function LandingCallstack() { + return ( + +
+

Paper is created by

+ Callstack logo +

React Native Core Contributors and official Facebook partners

+

+ Callstack cooperates with many clients from various industries, + including big enterprises, helping them to improve their React Native + products and achieve business goals. +

+ + Visit Callstack + +
+
+ ); +} diff --git a/docs/src/components/landing/LandingCode.tsx b/docs/src/components/landing/LandingCode.tsx new file mode 100644 index 0000000000..baa56643ee --- /dev/null +++ b/docs/src/components/landing/LandingCode.tsx @@ -0,0 +1,488 @@ +import * as React from 'react'; +import { Linking, ScrollView, StyleSheet, View } from 'react-native'; + +import { BrowserOnly } from '@rspress/core/runtime'; +import { + Appbar, + Button, + Chip, + DarkTheme, + Divider, + LightTheme, + Provider, + Snackbar, + Text, + TextInput, + useTheme, +} from 'react-native-paper'; + +import { grayTheme } from './editorTheme'; +import LandingCodeSnippet from './LandingCodeSnippet'; +import LandingSection, { asset, docsHref } from './LandingSection'; +import { prepareThemes } from '../../utils/themes'; + +// The original sample referenced a non-existent `TouchableWithNativeFeedback` +// and was missing a comma in the StyleSheet object; both corrected here. +const standardReact = ` +const Touchable = Platform.OS === 'ios' ? + TouchableOpacity : + TouchableNativeFeedback + +const Button = ({ onPress }) => + + + Press me + + + +const styles = StyleSheet.create({ + button: { + // Styles here + }, + text: { + // Styles here + } +}); + +`; + +const withPaper = ` + + +`; + +const snippet = ` + + Sign up to our newsletter! + + + Get a monthly dose of fresh React Native Paper news straight to your mailbox. Just sign up to our newsletter and enjoy! + + + + + Dark theme + + + Material You + + + + + + + + { + // Do side magic + }, + }} + duration={ + Snackbar.DURATION_LONG + } +> + Hey there! I'm a Snackbar. +`; + +/** + * The source's second chip switches the demo between MD2 and MD3. Paper 6 + * renders MD3 components only, so the chip switches the palette instead: + * on, a Material You scheme derived from the page's own purple; off, the MD2 + * colours the source hands its editor (`#6200ee` light, `#64DAC5` dark). + * Component shapes stay MD3 either way - that part needs Paper 5. + */ +const materialYouColors = prepareThemes({ primary: '#21005d' }); + +const md2Colors = { + light: { + primary: '#6200ee', + onPrimary: '#ffffff', + secondary: '#03dac4', + secondaryContainer: '#e0e0e0', + onSecondaryContainer: 'rgba(0, 0, 0, 0.87)', + background: '#ffffff', + surface: '#ffffff', + surfaceVariant: '#f0f0f0', + surfaceContainerHighest: '#f0f0f0', + onSurface: 'rgba(0, 0, 0, 0.87)', + onSurfaceVariant: 'rgba(0, 0, 0, 0.54)', + outline: 'rgba(0, 0, 0, 0.38)', + }, + dark: { + primary: '#64dac5', + onPrimary: '#000000', + secondary: '#64dac5', + secondaryContainer: '#3a3a3a', + onSecondaryContainer: '#ffffff', + background: '#202c32', + surface: '#202c32', + surfaceVariant: '#2b383f', + surfaceContainerHighest: '#2b383f', + onSurface: '#ffffff', + onSurfaceVariant: '#aaaaaa', + outline: 'rgba(255, 255, 255, 0.38)', + }, +}; + +/** + * MD2 squared most surfaces off at 4dp and kept chips as 16dp pills, where + * MD3 rounds everything much further. `TextInput`'s radius is a constant in + * Paper 6 rather than a token, so the fields stay MD3-round either way. + */ +/** + * MD2 fills its Appbar with the primary colour in both modes and picks the + * contrasting content colour: white on the light theme's `#6200ee`, black on + * the dark theme's `#64DAC5`. + */ +const md2OnAppbar = '#ffffff'; +const md2OnAppbarMuted = 'rgba(255, 255, 255, 0.7)'; +const md2OnAppbarDark = '#000000'; +const md2OnAppbarDarkMuted = 'rgba(0, 0, 0, 0.7)'; + +const md2AppbarContent = (isDarkTheme: boolean) => + isDarkTheme ? md2OnAppbarDark : md2OnAppbar; + +const md2Corners = { + extraSmall: 4, + small: 16, + medium: 4, + large: 4, + largeIncreased: 4, + extraLarge: 4, + extraLargeIncreased: 4, + extraExtraLarge: 4, +}; + +// `customStyle` and the transformCode wrapper from the source's exampleCode.js. +const styles = StyleSheet.create({ + box: { + padding: 15, + }, + row: { + marginTop: 15, + marginBottom: 15, + flexDirection: 'row', + }, + field: { + marginTop: 15, + }, + chip: { + marginRight: 8, + }, + // The ScrollContainer the source nests under the Appbar: a fixed 600px + // viewport, so the demo scrolls inside the phone instead of overflowing it. + scroll: { + height: 600, + marginBottom: -30, + }, + placeholder: { + minHeight: 480, + }, + md2Appbar: { + backgroundColor: md2Colors.light.primary, + }, + md2AppbarDark: { + backgroundColor: md2Colors.dark.primary, + }, + // The preset's `body *` would stretch these to 1.5; MD2 runs them at 24/16. + // MD2's `Title` is 20px where Paper 6's closest variant, titleLarge, is 22. + md2Heading: { + fontSize: 20, + }, + md2Title: { + color: md2OnAppbar, + fontSize: 20, + lineHeight: 24, + fontWeight: '500', + }, + md2TitleDark: { + color: md2OnAppbarDark, + fontSize: 20, + lineHeight: 24, + fontWeight: '500', + }, + md2Subtitle: { + color: md2OnAppbarMuted, + fontSize: 14, + lineHeight: 16, + }, + md2SubtitleDark: { + color: md2OnAppbarDarkMuted, + fontSize: 14, + lineHeight: 16, + }, +}); + +type DemoProps = { + isDarkTheme: boolean; + onToggleTheme: () => void; + isMaterialYou: boolean; + onToggleMaterialYou: () => void; +}; + +const Demo = ({ + isDarkTheme, + onToggleTheme, + isMaterialYou, + onToggleMaterialYou, +}: DemoProps) => { + const theme = useTheme(); + const [showSnack, setShowSnack] = React.useState(false); + + const toggleSnack = () => setShowSnack((visible) => !visible); + + return ( + + {/* `statusBarHeight: 47` leaves the frame's notch clear of the title. */} + + + + react-native-paper + + + Read more in the docs + + + ) + } + /> + + void Linking.openURL(docsHref('docs/components/Appbar/')) + } + /> + + + + + Sign up to our newsletter! + + + Get a monthly dose of fresh React Native Paper news straight to your + mailbox. Just sign up to our newsletter and enjoy! + + + + + Dark theme + + + Material You + + + + + + + + + + + + + {/* Anchored to the outer View rather than the scroll content: the + source's container carries `marginBottom: -30`, so the box the + Snackbar sits at the bottom of is 681px, not the 600px viewport. */} + + Hey there! I'm a Snackbar. + + + ); +}; + +const ThemedDemo = () => { + const [isDarkTheme, setIsDarkTheme] = React.useState(false); + const [isMaterialYou, setIsMaterialYou] = React.useState(true); + + const base = isDarkTheme ? DarkTheme : LightTheme; + const palette = isMaterialYou ? materialYouColors : md2Colors; + const theme = { + ...base, + colors: { + ...base.colors, + ...(isDarkTheme ? palette.dark : palette.light), + }, + shapes: isMaterialYou + ? base.shapes + : { ...base.shapes, corner: { ...base.shapes.corner, ...md2Corners } }, + }; + + return ( + + setIsMaterialYou((value) => !value)} + onToggleTheme={() => setIsDarkTheme((value) => !value)} + /> + + ); +}; + +export default function LandingCode() { + return ( + +
+

+ Achieve more in less time +

+

+ Don't waste your time writing complex components from scratch. +

+
+ +
+
+

Standard React code

+
+            
+          
+
+
+

With react-native-paper

+
+            
+          
+ + 👆 + +

+ Interactions, animations and accessibility. +
+ React-native-paper takes care of the details and your UI logic, so + you can focus on your users. +

+
+
+ +
+
+
+ +

+ Try it live! +

+

+ Try our live demo and check it out for yourself! When you are + done, make sure to copy the code and drop it straight into a React + Native app! Yes, Paper is cross-platform and works on both web and + mobile. +

+
+ +
+ +
+ +
+
+
+ }> + {() => } + +
+
+ +
+
+
+
+ ); +} diff --git a/docs/src/components/landing/LandingCodeSnippet.tsx b/docs/src/components/landing/LandingCodeSnippet.tsx new file mode 100644 index 0000000000..b7b5c6e765 --- /dev/null +++ b/docs/src/components/landing/LandingCodeSnippet.tsx @@ -0,0 +1,57 @@ +import type { CSSProperties } from 'react'; + +import { Highlight, type PrismTheme } from 'prism-react-renderer'; + +import editorTheme from './editorTheme'; + +/** + * `customStyles` from the source, plus the typographic half of its theme's + * `plain` - prism's own theme type carries colour only. + */ +const customStyles: CSSProperties = { + padding: 20, + overflowY: 'auto', + fontFamily: '"Roboto Mono", monospace', + fontSize: '16px', + borderRadius: 5, +}; + +type Props = { + code: string; + theme?: PrismTheme; + /** + * Renders as the live editor rather than a snippet: a line-number gutter, + * wrapped long lines, and none of `seb0k0g`'s dots ornament. + */ + numbered?: boolean; +}; + +/** + * editor/CodeSnippet.js. `seb0k0g` carries the dots ornament the source draws + * through a `::before`; prism applies `theme.plain` as an inline style and + * `customStyles` adds the padding on top. + */ +export default function LandingCodeSnippet({ code, theme, numbered }: Props) { + return ( + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+          {tokens.map((line, index) => (
+            
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ )} +
+ ); +} diff --git a/docs/src/components/landing/LandingConcepts.tsx b/docs/src/components/landing/LandingConcepts.tsx new file mode 100644 index 0000000000..da1f2c666e --- /dev/null +++ b/docs/src/components/landing/LandingConcepts.tsx @@ -0,0 +1,49 @@ +import LandingSection, { asset } from './LandingSection'; + +const concepts = [ + { + icon: 'platform-icon.svg', + title: 'Platform adaptation', + body: 'React-native-paper meets high expectations set by iOS and Android ecosystems. Your users will appreciate this choice.', + }, + { + icon: 'theming-icon.svg', + title: 'Full theming support', + body: 'Every app is different - that’s why themes are first-class citizens in React-native-paper. Switch between dark and light modes, customise default colours or make your own. It’s never been that easy.', + }, + { + icon: 'accessibility-icon.svg', + title: 'Accessibility and RTL support', + body: 'React-native-paper is fully compatible with screen readers, readability tools and right-to-left languages. Make your app inclusive by default.', + }, +]; + +export default function LandingConcepts() { + return ( + +
+
+

Key concepts

+

+ react-native-paper focuses in important concepts that are hard to + build from scratch. +

+
+
+ {concepts.map((concept) => ( +
+
+ {concept.title} +

{concept.title}

+

{concept.body}

+
+
+ ))} +
+
+
+ ); +} diff --git a/docs/src/components/landing/LandingDiscord.tsx b/docs/src/components/landing/LandingDiscord.tsx new file mode 100644 index 0000000000..a529f567f3 --- /dev/null +++ b/docs/src/components/landing/LandingDiscord.tsx @@ -0,0 +1,21 @@ +import LandingButton from './LandingButton'; +import LandingSection, { asset } from './LandingSection'; + +export default function LandingDiscord() { + return ( + +
+ Discord logo +

We are in Discord

+

Join us in the #react-native-paper channel

+

+ Community where people discuss and supports others in building + react-native-paper apps. +

+ + Join us on Discord + +
+
+ ); +} diff --git a/docs/src/components/landing/LandingFooter.tsx b/docs/src/components/landing/LandingFooter.tsx new file mode 100644 index 0000000000..f288b0c476 --- /dev/null +++ b/docs/src/components/landing/LandingFooter.tsx @@ -0,0 +1,75 @@ +import LandingSection, { asset } from './LandingSection'; + +type LinkProps = { to: string; children: React.ReactNode }; + +/** components/Link.js in the source. */ +const Link = ({ to, children }: LinkProps) => ( + + {children} + +); + +/** + * components/Footer.js. The copy is carried over verbatim, outdated year and + * docs URL included - refreshing the landing copy is a separate task. + */ +export default function LandingFooter() { + return ( + +
+
+ +

+ react-native-paper is a high-quality standard-compliant Material + Design library developed by the engineers of{' '} + Callstack. +

+
+
+
info
+
    +
  • + + Github + +
  • +
  • + + Docs + +
  • +
  • + Twitter +
  • +
  • + Discord +
  • +
+
+
+
callstack
+
    +
  • + Careers +
  • +
  • + Our work +
  • +
  • + Contact +
  • +
+
+
+
+
© 2021 Callstack, All rights reserved
+
+ Created with love by: + + + +
+
+
+ ); +} diff --git a/docs/src/components/landing/LandingHero.tsx b/docs/src/components/landing/LandingHero.tsx new file mode 100644 index 0000000000..5778a5a3e8 --- /dev/null +++ b/docs/src/components/landing/LandingHero.tsx @@ -0,0 +1,161 @@ +import { useEffect, useState } from 'react'; + +import { useVersion, withBase } from '@rspress/core/dist/runtime/index.js'; + +import LandingButton from './LandingButton'; +import LandingSection, { asset } from './LandingSection'; +import { getNextRoute } from '../../utils/versionRoutes'; + +const REPO = 'https://github.com/callstack/react-native-paper'; +const GETTING_STARTED = '/docs/guides/getting-started'; +const STARS_FALLBACK = '3000+'; + +/** Mirrors fetchStars.js from the landing page source. */ +function useGithubStars() { + const [stars, setStars] = useState(() => { + try { + return localStorage.getItem('cachedGithubStars') ?? STARS_FALLBACK; + } catch { + return STARS_FALLBACK; + } + }); + + useEffect(() => { + let cancelled = false; + + const load = async () => { + try { + const response = await fetch( + 'https://api.github.com/repos/callstack/react-native-paper' + ); + const json = await response.json(); + + if (cancelled || typeof json?.stargazers_count !== 'number') { + return; + } + + const count = String(json.stargazers_count); + + setStars(count); + + try { + localStorage.setItem('cachedGithubStars', count); + } catch { + // Storage can be unavailable; the count is non-essential. + } + } catch { + // Keep whatever value is already rendered. + } + }; + + void load(); + + return () => { + cancelled = true; + }; + }, []); + + return stars; +} + +export default function LandingHero() { + const version = useVersion() || '5.x'; + const stars = useGithubStars(); + const docsHref = withBase( + version === '6.x' ? getNextRoute(GETTING_STARTED) : GETTING_STARTED + ); + + return ( + +
+
+

+ Making your React Native apps look and feel native +

+ + + +

+ React Native Paper is a high-quality, standard-compliant Material + Design library that has you covered in all major use-cases. +

+ +
+ + Learn more + + Docs +
+ +
+

Try out components in our demo apps:

+ +
+
+ +
+ mobile mockup +
+
+
+ + ); +} diff --git a/docs/src/components/landing/LandingLookAndFeel.tsx b/docs/src/components/landing/LandingLookAndFeel.tsx new file mode 100644 index 0000000000..e8372448fa --- /dev/null +++ b/docs/src/components/landing/LandingLookAndFeel.tsx @@ -0,0 +1,112 @@ +import { useRef, useState } from 'react'; + +import type { CSSVars } from './cssVars'; +import LandingSection, { asset } from './LandingSection'; + +type PlayerProps = { name: string }; + +/** LookAndFeel.js `PlayerContainer` — capped at 297x630 to hold the ratio. */ +const poster = (name: string): CSSVars => ({ + '--p626bjh-0': `url(${asset(`videos/${name}-poster.png`)})`, +}); + +const Player = ({ name }: PlayerProps) => { + const videoRef = useRef(null); + const [playing, setPlaying] = useState(false); + + const handleBegin = () => { + void videoRef.current?.play(); + setPlaying(true); + }; + + return ( +
+ + {playing ? null : ( +
+ Play +
+ )} +
+ ); +}; + +export default function LandingLookAndFeel() { + return ( + +
+
+
+
+

Look

+

What makes an app look native?

+

+ Its interface. It is responsive, fast and works reliably on both + platforms. When building a React component, you have to style + each of them yourself, according to the guidelines of the + platform you are targeting. This can be overwhelming and + non-trivial to do right. +

+ +
+ point up emoji + + It's not a real badge, actually, but you can take our + word for it ;) + +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+

Feel

+

What makes an app feel native?

+

+ Snappy interface that behaves just like any other app. It supports + accessibility standards - in other words, it does everything a + native app would do. Interactions in React Native are easy to do, + but getting to 100% polish requires extra effort. Implementing + exact platform-specific animations, making sure it runs under + heavy load are just two examples of what we have to think when + writing first-class interface. +

+

+ React-native-paper ships with a lot of components and interactions + that are there to satisfy every single use case you might have. + See it for yourself. +

+
+
+
+
+ ); +} diff --git a/docs/src/components/landing/LandingMaterialYou.tsx b/docs/src/components/landing/LandingMaterialYou.tsx new file mode 100644 index 0000000000..d674526fc0 --- /dev/null +++ b/docs/src/components/landing/LandingMaterialYou.tsx @@ -0,0 +1,32 @@ +import LandingSection, { asset } from './LandingSection'; + +export default function LandingMaterialYou() { + return ( + +
+
+

+ Paper supports Material You! +

+

+ React Native Paper library officially supports the new Material + Design generation called Material You - a brand new and the most + expressive design system by Google. +

+

+ All of the Paper’s components have been adjusted to the latest + standards of visual by changes in colors, typography and animations + so you can build your apps according to the latest trends. +

+
+
+ Material You components +
+
+
+ ); +} diff --git a/docs/src/components/landing/LandingNumbers.tsx b/docs/src/components/landing/LandingNumbers.tsx new file mode 100644 index 0000000000..7fbce34871 --- /dev/null +++ b/docs/src/components/landing/LandingNumbers.tsx @@ -0,0 +1,88 @@ +import { useEffect, useState } from 'react'; + +import LandingSection, { asset } from './LandingSection'; + +const PAPER_STATS_API = + 'https://paper-github-stats-h6ah15j9p.vercel.app/api/github-stats'; + +type Stats = { stars: number; commits: number; weeklyDownloads: number }; + +/** Shown until the API responds, same values as the landing page source. */ +const fallbackData: Stats = { + stars: 7371, + commits: 1306, + weeklyDownloads: 55300, +}; + +export default function LandingNumbers() { + const [data, setData] = useState(fallbackData); + + useEffect(() => { + let cancelled = false; + + const load = async () => { + try { + const response = await fetch(PAPER_STATS_API); + const json = await response.json(); + + if (!cancelled) { + setData(json); + } + } catch (error) { + console.error('Error while fetching numbers', error); + } + }; + + void load(); + + return () => { + cancelled = true; + }; + }, []); + + const items = [ + { + icon: 'download-icon.svg', + value: data.weeklyDownloads, + label: ( + <> + Downloads weekly on{' '} + npm + + ), + }, + { + icon: 'star-icon.svg', + value: data.stars, + label: ( + <> + Stars on{' '} + GitHub + + ), + }, + { + icon: 'commit-icon.svg', + value: data.commits, + label: 'Number of commits', + }, + ]; + + return ( + +

+ It's free, it's Open Source! +

+ +
    + {items.map((item) => ( +
  • + +

    {item.value.toLocaleString('en-US')}

    +

    {item.label}

    +
  • + ))} +
+
+ ); +} diff --git a/docs/src/components/landing/LandingSection.tsx b/docs/src/components/landing/LandingSection.tsx new file mode 100644 index 0000000000..b6766bf730 --- /dev/null +++ b/docs/src/components/landing/LandingSection.tsx @@ -0,0 +1,56 @@ +import type { ReactNode } from 'react'; + +import { withBase } from '@rspress/core/dist/runtime/index.js'; + +import type { CSSVars } from './cssVars'; + +/** Resolves a `docs/public/landing` asset against the configured base path. */ +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) => + typeof window === 'undefined' + ? withBase(`/${path}`) + : new URL(withBase(`/${path}`), window.location.origin).toString(); + +type Props = { + children: ReactNode; + /** The original's per-section linaria class, e.g. `wwffi0l` for the hero. */ + variant: string; + dark?: boolean; + /** Extra class on the inner 940px container. */ + innerClassName?: string; + id?: string; +}; + +/** + * layout/Section.js in the source. `sq6c5at` carries the padding and reads its + * colours from two custom properties; `l1qehd68` / `d1pafg9o` supply the light + * and dark dot patterns. + */ +export default function LandingSection({ + children, + variant, + dark, + innerClassName, + id, +}: Props) { + const colours: CSSVars = { + '--sq6c5at-0': dark ? 'rgba(33, 0, 93, 1)' : '#ffffff', + '--sq6c5at-1': dark ? '#ffffff' : '#000000', + }; + + return ( +
+
+ {children} +
+
+ ); +} diff --git a/docs/src/components/landing/LandingSlider.tsx b/docs/src/components/landing/LandingSlider.tsx new file mode 100644 index 0000000000..984cd8c17c --- /dev/null +++ b/docs/src/components/landing/LandingSlider.tsx @@ -0,0 +1,110 @@ +import { useEffect, useRef, useState, type ReactNode } from 'react'; + +import type { CSSVars } from './cssVars'; +import { asset } from './LandingSection'; + +type Props = { + children: ReactNode; + slidesAmount: number; + initialSlide: number; +}; + +/** Below this, the source hides the arrows and the track is swipe-only. */ +const SWIPE_THRESHOLD = 40; + +/** + * components/Slider.js. The track's width and offset are responsive, so the + * source feeds linaria six interpolations - one width and one translate per + * breakpoint - which compile to the custom properties `i15993hv` reads. + */ +export default function LandingSlider({ + children, + slidesAmount, + initialSlide, +}: Props) { + const [currentSlide, setCurrentSlide] = useState(0); + const [windowWidth, setWindowWidth] = useState(0); + + // The source sets the initial slide on mount rather than in state, so the + // track starts centred only once the window width is known. + useEffect(() => { + setCurrentSlide(initialSlide); + + const handleResize = () => setWindowWidth(document.body.clientWidth); + + handleResize(); + window.addEventListener('resize', handleResize); + + return () => window.removeEventListener('resize', handleResize); + }, [initialSlide]); + + const prevSlide = () => + setCurrentSlide((slide) => (slide === 1 ? slide : slide - 1)); + const nextSlide = () => + setCurrentSlide((slide) => (slide === slidesAmount ? slide : slide + 1)); + + // Stands in for react-swipeable, which the docs site does not depend on. + const swipeStart = useRef(null); + + const handlePointerDown = (event: React.PointerEvent) => { + swipeStart.current = event.clientX; + }; + + const handlePointerUp = (event: React.PointerEvent) => { + const start = swipeStart.current; + + swipeStart.current = null; + + if (start === null) { + return; + } + + const travelled = event.clientX - start; + + if (travelled <= -SWIPE_THRESHOLD) { + nextSlide(); + } else if (travelled >= SWIPE_THRESHOLD) { + prevSlide(); + } + }; + + const offset = (slideWidth: number) => + currentSlide * -slideWidth + windowWidth / 2 + slideWidth / 2; + + const track: CSSVars = { + '--i15993hv-0': `${slidesAmount * 320}px`, + '--i15993hv-1': `${offset(320)}px`, + '--i15993hv-2': `${slidesAmount * 450}px`, + '--i15993hv-3': `${offset(450)}px`, + '--i15993hv-4': `${slidesAmount * 620}px`, + '--i15993hv-5': `${offset(620)}px`, + }; + + return ( +
+
+
+ {children} +
+
+ {currentSlide > 1 ? ( + Previous + ) : null} + {currentSlide < slidesAmount ? ( + Next + ) : null} +
+
+
+ ); +} diff --git a/docs/src/components/landing/LandingTestimonials.tsx b/docs/src/components/landing/LandingTestimonials.tsx new file mode 100644 index 0000000000..9fee7a3c13 --- /dev/null +++ b/docs/src/components/landing/LandingTestimonials.tsx @@ -0,0 +1,95 @@ +import type { ReactNode } from 'react'; + +import LandingSection, { asset } from './LandingSection'; +import LandingSlider from './LandingSlider'; + +type Testimonial = { + avatar: string; + name: string; + role: string; + quote: ReactNode; + href: string; +}; + +const testimonials: Testimonial[] = [ + { + avatar: 'kurt.jpg', + name: 'Kurt Kemple', + role: 'Co-organizer of @NYCGraphQL', + quote: + "Paper w/ RNW is a glorious thing! Universal design systems are where it's at. #alltheplatforms", + href: 'https://twitter.com/kurtiskemple/status/1089866560641093632', + }, + { + avatar: 'brent.jpg', + name: 'brent', + role: 'Developer @ Expo / React Native', + quote: ( + <> + react-native-paper is{' '} + + 😎 + {' '} + ios + android + web, and exports typescript types. + + ), + href: 'https://twitter.com/notbrent/status/1108492804978667520', + }, + { + avatar: 'osadnik.jpg', + name: 'Michał Osadnik', + role: 'Software Engineer @SWMansion', + quote: + 'Still cannot believe how actively react-native-paper is maintained! Is it resurrection of Material Design on iOS in React Native?', + href: 'https://twitter.com/mosdnk/status/1106184194458374149', + }, +]; + +/** + * homepage/Testimonials.js, across two bands: a heading section and a + * full-bleed track holding components/Testimonial.js cards in the Slider. + */ +export default function LandingTestimonials() { + return ( + <> + +
+

+ They are already using +
+ react-native-paper +

+
+
+ + + + {testimonials.map((testimonial) => ( +
+
+ +
+

{testimonial.name}

+

{testimonial.role}

+
+ + + +
+
{testimonial.quote}
+
+ ))} +
+
+ + ); +} diff --git a/docs/src/components/landing/cssVars.ts b/docs/src/components/landing/cssVars.ts new file mode 100644 index 0000000000..2920ca022a --- /dev/null +++ b/docs/src/components/landing/cssVars.ts @@ -0,0 +1,7 @@ +import type { CSSProperties } from 'react'; + +/** + * The vendored stylesheet reads its colours and images from linaria-generated + * custom properties, which React's CSSProperties does not model. + */ +export type CSSVars = CSSProperties & Record<`--${string}`, string>; diff --git a/docs/src/components/landing/editorTheme.ts b/docs/src/components/landing/editorTheme.ts new file mode 100644 index 0000000000..77a9ca350d --- /dev/null +++ b/docs/src/components/landing/editorTheme.ts @@ -0,0 +1,91 @@ +import type { PrismTheme } from 'prism-react-renderer'; + +/** utils/editorTheme.js. `plain` is applied inline by prism-react-renderer. */ +const editorTheme: PrismTheme = { + // The source also puts the font, size and radius here; `PrismThemeEntry` + // only carries colour, so they live in the snippet's own styles instead. + plain: { + backgroundColor: '#202C32', + color: 'rgb(170, 176, 179)', + }, + styles: [ + { + types: [ + 'prolog', + 'doctype', + 'cdata', + 'variable', + 'function-variable', + 'tag', + 'operator', + ], + style: { color: '#F92F5F' }, + }, + { types: ['property', 'attr-name'], style: { color: '#d7ad58' } }, + { types: ['comment'], style: { color: '#8295b6' } }, + { types: ['punctuation'], style: { color: 'rgb(170, 176, 179)' } }, + { + types: ['attr-value', 'script', 'function'], + style: { color: 'rgb(100, 172, 144)' }, + }, + { + types: [ + 'tag-id', + 'selector', + 'atrule-id', + 'keyword', + 'boolean', + 'string', + 'entity', + 'url', + 'control', + 'directive', + 'unit', + 'statement', + 'regex', + 'at-rule', + 'placeholder', + 'number', + ], + style: { color: 'rgb(173, 119, 215)' }, + }, + { types: ['deleted'], style: { textDecorationLine: 'line-through' } }, + { types: ['inserted'], style: { textDecorationLine: 'underline' } }, + { types: ['italic'], style: { fontStyle: 'italic' } }, + { types: ['important', 'bold'], style: { fontWeight: 'bold' } }, + ], +}; + +/** The monochrome variant the "Standard React code" panel uses. */ +export const grayTheme: PrismTheme = { + plain: { ...editorTheme.plain }, + styles: [ + { + types: [ + 'prolog', + 'doctype', + 'cdata', + 'variable', + 'function-variable', + 'tag', + 'operator', + 'boolean', + 'string', + 'entity', + 'url', + 'attr-value', + 'keyword', + 'control', + 'directive', + 'unit', + 'statement', + 'regex', + 'at-rule', + 'placeholder', + ], + style: { color: 'rgb(170, 176, 179)' }, + }, + ], +}; + +export default editorTheme; diff --git a/docs/src/components/landing/index.tsx b/docs/src/components/landing/index.tsx new file mode 100644 index 0000000000..d2229148f9 --- /dev/null +++ b/docs/src/components/landing/index.tsx @@ -0,0 +1,30 @@ +import LandingBanner from './LandingBanner'; +import LandingCallstack from './LandingCallstack'; +import LandingCode from './LandingCode'; +import LandingConcepts from './LandingConcepts'; +import LandingDiscord from './LandingDiscord'; +import LandingFooter from './LandingFooter'; +import LandingHero from './LandingHero'; +import LandingLookAndFeel from './LandingLookAndFeel'; +import LandingMaterialYou from './LandingMaterialYou'; +import LandingNumbers from './LandingNumbers'; +import LandingTestimonials from './LandingTestimonials'; + +/** Section order follows src/pages/index.js in the landing page source. */ +export default function Landing() { + return ( +
+ + + + + + + + + + + +
+ ); +} diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 03288b6bcd..ec1e4b6ae2 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -1,3 +1,5 @@ +@import './landing.css'; + :root { --paper-nav-link-hover: var(--rp-c-bg-soft, rgba(148, 163, 184, 0.16)); --paper-nav-link-active: var(--rp-c-bg-mute, rgba(148, 163, 184, 0.24)); diff --git a/docs/src/css/landing-vendor.css b/docs/src/css/landing-vendor.css new file mode 100644 index 0000000000..55db7b6624 --- /dev/null +++ b/docs/src/css/landing-vendor.css @@ -0,0 +1,232 @@ +/* + * Extracted verbatim from the compiled stylesheet served by + * reactnativepaper.com, the linaria output of + * callstack-internal/paper-landing-page. Class names are the generated + * linaria hashes, so the markup in src/components/landing mirrors the + * original DOM in order to use them. + * + * Only class-scoped rules are included; the original's global `html`, `body` + * and `*` resets are reproduced scoped to `.landing` in landing.css so they + * cannot leak into the docs site. The one image URL has been repointed at our + * vendored copy. + * + * Do not hand-edit: regenerate from the source site if the design changes. + */ +.sq6c5at{padding:60px 30px;background:var(--sq6c5at-0);color:var(--sq6c5at-1);background-position:-5px 10px} +@media (min-width:992px){.sq6c5at{padding:60px 15px}} +.l1qehd68{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAMklEQVRYhe3PoQ0AIBRDwQbNEszAOuw/B/kGg0WRu+SZugYAgA/1sWb14lm7FgAAOJJsjOUBe5fO1bAAAAAASUVORK5CYII=)} +.d1pafg9o{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAMUlEQVRYhe3PoQ0AIBRDwQbNEuzFYmxLvsFgUeQueaauAQDgQ33MVb141q4FAACOJBvlyQJH/J3AHgAAAABJRU5ErkJggg==)} +.s1o99i3g{max-width:940px;margin:0 auto} +.sl968z4{padding:0} +.sdarceo{max-width:unset} +.g1s6pewu{-webkit-align-self:center;align-self:center} +.hv0shhd{background:var(--hv0shhd-0);box-shadow:var(--hv0shhd-1);color:var(--hv0shhd-2);padding:15px;position:relative;transition:background .4s,color .4s,box-shadow .4s;z-index:50;display:-webkit-flex;display:flex} +@media (min-width:900px){.hv0shhd{height:80}} +.hv0shhd .contact-link--mobile{position:absolute;top:0;left:0;width:100%;height:100%} +@media (min-width:900px){.hv0shhd .contact-link--mobile{display:none}} +.h12yetm2{-webkit-align-items:center;align-items:center;display:-webkit-flex;display:flex;-webkit-flex:1;flex:1 1;-webkit-flex-flow:row;flex-flow:row;-webkit-justify-content:center;justify-content:center;margin:0 auto;max-width:940px;padding:0 15px} +@media (min-width:900px){.h12yetm2{-webkit-justify-content:space-between;justify-content:space-between}} +.hubqjat{display:none;width:140px} +@media (min-width:900px){.hubqjat{display:block}} +.hfwrvff{font-size:14px!important;margin:0} +@media (min-width:600px){.hfwrvff{font-size:medium!important}} +.hfwrvff a{cursor:pointer;font-family:Platform Web!important;-webkit-text-decoration:underline;text-decoration:underline;color:inherit} +.cf3elge{display:-webkit-flex;display:flex;-webkit-justify-content:space-between;justify-content:space-between;-webkit-flex-wrap:wrap;flex-wrap:wrap} +.cf3elge h5{font-size:12px!important;-webkit-letter-spacing:2px!important;-moz-letter-spacing:2px!important;-ms-letter-spacing:2px!important;letter-spacing:2px!important;line-height:24px!important;text-transform:uppercase} +.cf3elge ul{list-style:none;margin:0;padding:0} +.cf3elge a{color:#fff;font-size:14px!important;line-height:24px!important;-webkit-text-decoration:none;text-decoration:none} +.d1cbpza3{width:100%} +@media (min-width:768px){.d1cbpza3{width:70%}} +.d1cbpza3 img{height:50px} +.d1cbpza3 p{margin-top:16px;font-size:14px!important;line-height:24px!important;max-width:330px;font-weight:300!important;-webkit-letter-spacing:2!important;-moz-letter-spacing:2!important;-ms-letter-spacing:2!important;letter-spacing:2!important} +.d1cbpza3 a{-webkit-text-decoration:underline;text-decoration:underline} +.i7d7zjg{width:50%} +@media (min-width:768px){.i7d7zjg{width:15%}} +.i7d7zjg li{font-weight:300!important} +.i7d7zjg h5{margin-top:0} +.cn49emr{display:-webkit-flex;display:flex;font-size:12px!important;-webkit-flex-flow:column;flex-flow:column;line-height:24px!important;margin-top:40px;-webkit-align-items:center;align-items:center} +@media (min-width:768px){.cn49emr{-webkit-flex-flow:row;flex-flow:row;-webkit-justify-content:space-between;justify-content:space-between;margin-top:100px}} +.cn49emr div{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center} +.cn49emr div img{margin-left:5px} +.c1od1uwn a{height:18px} +.s7sbtf9{padding-top:60px;padding-bottom:16px} +.b1qn7xxj{background:var(--b1qn7xxj-0);border-radius:23px;border:2px solid var(--b1qn7xxj-1);color:var(--b1qn7xxj-2);cursor:pointer;height:46px;-webkit-justify-content:center;justify-content:center;font-size:16px!important;padding:0 24px;text-align:center;-webkit-text-decoration:none;text-decoration:none;transition:color .3s ease-in-out,background .3s ease-in-out,border .3s ease-in-out;width:auto} +.b1qn7xxj:disabled{cursor:auto} +.b1qn7xxj:hover{background:var(--b1qn7xxj-3);border:2px solid var(--b1qn7xxj-4);color:var(--b1qn7xxj-5)} +.wwffi0l{position:relative;padding-bottom:0;padding-top:0;min-height:700px} +@media (min-width:401px){.wwffi0l{min-height:420px}} +@media (min-width:901px){.wwffi0l{min-height:700px}} +.cztnm0o{display:-webkit-flex;display:flex;-webkit-flex-flow:row;flex-flow:row;padding-bottom:30px} +@media (min-width:901px){.cztnm0o{padding-bottom:0}} +.hmtzm5j{font-size:52px!important;line-height:120%!important;margin-top:0;margin-bottom:25px} +.hmtzm5j span{color:#21005d} +.bia8az9{font-size:18px!important;color:#414757;line-height:28px!important;margin-top:0;margin-bottom:10px} +.hyn0pf{width:auto;position:relative;width:320px;height:670px;bottom:-70px;left:30px} +.h1w9fa70{display:none;background-image:url(../assets/landing/hero-image2.png);background-size:contain;background-repeat:no-repeat;overflow-x:hidden} +@media (min-width:1200px){.h1w9fa70{display:block;position:absolute;width:100%;height:100%;max-height:700px;bottom:-140px;right:-370px}} +.mlu3qb8{display:none} +@media (min-width:900px){.mlu3qb8{position:relative;display:-webkit-flex;display:flex;-webkit-flex:1;flex:1 1}} +.r17y5eu1{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;margin-top:30px} +.r17y5eu1 a{margin-right:20px} +.g1n6np2q{display:block;height:28px;margin-bottom:25px;margin-left:0;white-space:nowrap} +.s5mrc6w{display:inline-block;vertical-align:middle;margin-right:5px} +.g1cwq15n{color:#24292e;-webkit-text-decoration:none;text-decoration:none;outline:0} +.gwvv8a1{display:inline-block;font-weight:600!important;vertical-align:bottom;cursor:pointer;border:1px solid #c5c9cc;border-radius:.25em;padding:5px 10px;font-size:12px!important;line-height:16px!important;height:auto;background-color:#eff3f6} +.dqnumg8{margin:-5px 0 0 -7px;border:solid transparent;border-right:solid #c5c9cc} +.dqnumg8,.lmbp5un{position:absolute;top:50%;left:0;display:block;width:0;height:0} +.lmbp5un{margin:-5px 0 0 -6px;border:solid transparent;border-right:solid #fff} +.s152rxo6{display:inline-block;font-weight:600!important;vertical-align:bottom;cursor:pointer;border:1px solid #c5c9cc;border-radius:.25em;height:auto;padding:7px 10px;font-size:12px!important;line-height:16px!important;position:relative;margin-left:6px;background-color:#fff} +.d1g530h0{background:#fff;border:1px solid #d8d8d8;color:#000} +.hrrzndk{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-flex:1;flex:1 1;padding-top:30px} +@media (min-width:600px){.hrrzndk{padding:75px 35px 0}} +@media (min-width:900px){.hrrzndk{max-width:495px}} +.smvkndv{margin-top:40px} +.skghjfv{height:42px;margin-right:20px} +.hn5vo52{font-size:48px!important;text-align:center} +@media (min-width:768px){.hn5vo52{font-size:64px!important}} +.hn5vo52 span{color:#21005d} +.h15lhj08{margin-bottom:60px;margin-top:0} +.o16ish7f{padding-bottom:0} +@media (min-width:992px){.o16ish7f{padding-top:150px}} +.n1himknf{display:-webkit-flex;display:flex;-webkit-flex-flow:column;flex-flow:column;-webkit-align-items:center;align-items:center} +.n1himknf:not(:last-of-type){margin-bottom:60px} +.n1himknf h3{font-family:Platform Web!important;font-size:72px!important;margin-top:30px;margin-bottom:0} +@media (min-width:768px){.n1himknf h3{margin-bottom:15px}} +.n1himknf h4{font-size:12px!important;text-transform:uppercase;font-weight:400!important;-webkit-letter-spacing:2.57px!important;-moz-letter-spacing:2.57px!important;-ms-letter-spacing:2.57px!important;letter-spacing:2.57px!important;margin-bottom:0} +.n1himknf h4 a{color:#fff} +.n5g0et3{padding:0;display:-webkit-flex;display:flex;-webkit-flex-flow:column;flex-flow:column;-webkit-justify-content:space-between;justify-content:space-between;list-style:none;margin:0} +@media (min-width:768px){.n5g0et3{-webkit-flex-flow:row;flex-flow:row}} +.r1c5jsvp{-webkit-flex-wrap:wrap;flex-wrap:wrap} +.c1r5rfba,.r1c5jsvp{display:-webkit-flex;display:flex} +.c1r5rfba{width:100%;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-order:var(--c1r5rfba-0);order:var(--c1r5rfba-0);padding-left:20px} +@media (min-width:768px){.c1r5rfba{-webkit-flex:1;flex:1 1;-webkit-order:unset;order:unset;-webkit-justify-content:center;justify-content:center;padding-left:0}} +@media (max-width:899px){.c1r5rfba{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center}} +.c17p1dxj{max-width:400px} +@media (min-width:768px){.c17p1dxj{max-width:300px}} +@media (max-width:899px){.c17p1dxj{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center}} +.bpqw886{font-family:Roboto!important;margin-top:58px;margin-bottom:30px} +.t1n6sdu9{font-size:40px!important;border-bottom:3px solid #fff;display:inline-block;padding-bottom:10px;margin:0} +.h1irmz7o{font-size:30px!important;margin-top:35px} +.p626bjh{-webkit-align-items:center;align-items:center;background-image:var(--p626bjh-0);background-size:contain;height:auto;margin-top:45px;max-height:630px;max-width:297px;position:relative;width:100%} +@media (min-width:768px){.p626bjh{margin-top:unset}} +.p2eysq8,.py956vk{width:100%;height:100%} +.p2eysq8{-webkit-align-items:center;align-items:center;background:rgba(0,0,0,.5);display:-webkit-flex;display:flex;-webkit-justify-content:center;justify-content:center;left:0;opacity:var(--p2eysq8-0);pointer-events:var(--p2eysq8-1);position:absolute;top:0;transition:opacity .4s;border:3px solid #21005d;box-sizing:border-box} +.p18nt66k{cursor:pointer;position:absolute} +.c1ofgfit{line-height:24px!important} +.c1ofgfit:last-of-type{margin-bottom:0} +.hz1a1q4{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;font-size:12px!important;width:100%} +.hz1a1q4 img{margin-right:12px} +@media (min-width:768px){.hz1a1q4{width:400px}} +.o1dlsbjg{position:absolute;-webkit-transform:rotate(var(--o1dlsbjg-0));transform:rotate(var(--o1dlsbjg-0))} +.n15i0mrj textarea{outline:none} +.n15i0mrj pre{counter-reset:token-line} +.n15i0mrj .token-line:before{counter-increment:token-line;content:counter(token-line);position:absolute;left:-10px;overflow:visible;color:#545e68} +.o1et4rx6{top:470px;left:425px} +.seb0k0g:before{content:"";width:60px;height:20px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGkAAAAZCAMAAAAFfvDJAAAAXVBMVEUAAAD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwD/QEH/sQMBxwBpmW/PAAAAHHRSTlMA8/Pz6Ojo1NTUycnJoaGheHh4WVlZMDAwRUVFzgnkzgAAAP5JREFUSMd8z8eVwzAMBcBPUjlnOQDov8w9rPUsUJCng8FhbnNHLm9n/LZ0hWdfdAt+W/sySCj7FdqQ0CEZcG9M+ZCOuDdlcsgmfG0VnVUbbHvNZ/UO26ORs+aBj6cjzT1heXnW/AuWdxAtvD8jRzG3WSPPMb9boyCx8L+q6KrCVc1XNa4auWoAYCDLgNjIlhGxSSwTgIQsCWIpW1LEMrFkwEy2GdrCtgXaKrYVLdlaaB3bOmi92HrkZMuhFWwroJViK+HI5qB5tnloQWwBdAca3/kbmjo5XIB+fqJfPNEv7dEvP9GtjKBfuUe3spx+9RPd6ly6tSPo2zaiX3sPABnWoKjKvF6bAAAAAElFTkSuQmCC) top/contain no-repeat;display:block;margin-top:-10px;margin-left:-10px} +.t1ol3fh0{color:#414757;font-size:30px!important;line-height:35px!important;margin:0 auto 70px;max-width:500px;text-align:center} +.cmn45yp{display:-webkit-flex;display:flex;-webkit-flex-flow:row;flex-flow:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:space-between;justify-content:space-between;padding-bottom:50px} +.cmn45yp h2{font-family:Platform Web!important;font-size:24px!important} +.cmn45yp p{color:#414757;font-size:18px!important;line-height:28px!important} +.e1wvjlx7{display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;position:relative} +.e1wvjlx7 h1{text-align:left;margin-top:30px} +.e1wvjlx7 p{color:#414757;font-size:18px!important;line-height:28px!important;width:400px} +.e7mdwhd{display:-webkit-flex;display:flex;-webkit-flex:1;flex:1 1;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-items:flex-end;align-items:flex-end;min-width:380px} +@media (min-width:840px){.e7mdwhd{-webkit-justify-content:flex-end;justify-content:flex-end}} +.e1tqzesn{width:380px;position:relative;bottom:-80px} +.c11ps6c6{max-width:460px;border-radius:6px;width:100%!important} +.c11ps6c6 .token,.c11ps6c6 code{font-family:Roboto Mono,monospace!important;line-height:1.3!important;-webkit-letter-spacing:.5px!important;-moz-letter-spacing:.5px!important;-ms-letter-spacing:.5px!important;letter-spacing:.5px!important} +.c16bevq1{-webkit-flex:1;flex:1 1;width:100%;min-width:unset} +@media (min-width:500px){.c16bevq1{max-width:380px;padding-left:5px}} +@media (max-width:878px){.c16bevq1{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;margin:auto}} +.c1wrkpdf{-webkit-flex:1;flex:1 1;width:100%;min-width:unset} +@media (min-width:500px){.c1wrkpdf{min-width:460px}} +@media (max-width:878px){.c1wrkpdf{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center}} +.ezl4rww{max-width:450px;-webkit-flex:1;flex:1 1} +.shegl68{padding-bottom:0} +.i1vg0a03{-webkit-justify-content:center;justify-content:center} +.sirtpml{-webkit-align-items:center;align-items:center;display:none;-webkit-flex-flow:column;flex-flow:column} +@media (min-width:992px){.sirtpml{display:-webkit-flex;display:flex}} +.sirtpml img{margin-bottom:30px} +.sirtpml h1{margin-top:0} +.sirtpml p{color:#414757;font-size:18px!important;line-height:28px!important;margin-bottom:45px} +.hhlgf0c{padding-bottom:40px} +.hhlgf0c h1{margin-top:0} +.hhlgf0c p{margin-bottom:0} +.esf2wzt{display:none} +@media (min-width:960px){.esf2wzt{display:block}} +.cg6sb30{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;padding:46px 20px 20px;width:300px;height:360px;background-color:#fff;box-shadow:10px 10px 0 0 rgba(98,0,238,.3);border:2px solid #e6eaf2;border-radius:4px;color:#414757;text-align:center;margin-bottom:15px} +@media (min-width:1000px){.cg6sb30{margin-bottom:unset}} +.cg6sb30 h4{font-size:20px!important;font-weight:300!important;margin-top:30px;margin-bottom:15px} +.cg6sb30 p{margin-top:0;font-size:16px!important;font-weight:300!important;text-align:center;line-height:24px!important} +.cg6sb30 img{height:64px} +.r1an33qj{display:-webkit-flex;display:flex;width:100%;-webkit-flex-flow:column;flex-flow:column} +@media (min-width:1000px){.r1an33qj{-webkit-flex-flow:row;flex-flow:row}} +.caka61c{-webkit-flex:1;flex:1 1;-webkit-justify-content:center;justify-content:center} +.c1e6p7u3,.caka61c{display:-webkit-flex;display:flex} +.c1e6p7u3{color:#fff;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;text-align:center} +.c1e6p7u3 h2{font-size:4rem!important;margin-bottom:1.5rem;margin-top:0} +.c1e6p7u3 p{max-width:460px;margin-bottom:4rem} +.ce9bm9u{-webkit-align-items:center;align-items:center;display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column} +.h1j92dro{margin-top:0;margin-bottom:0} +.wibeo8m{overflow-x:hidden;width:100%;position:relative;height:350px} +.i15993hv{display:-webkit-flex;display:flex;-webkit-flex-flow:row;flex-flow:row;-webkit-flex-wrap:no-wrap;flex-wrap:no-wrap;width:var(--i15993hv-0);-webkit-transform:translate(var(--i15993hv-1));transform:translate(var(--i15993hv-1));transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out} +@media (min-width:450px){.i15993hv{width:var(--i15993hv-2);-webkit-transform:translate(var(--i15993hv-3));transform:translate(var(--i15993hv-3))}} +@media (min-width:650px){.i15993hv{width:var(--i15993hv-4);-webkit-transform:translate(var(--i15993hv-5));transform:translate(var(--i15993hv-5))}} +.awvlien{-webkit-align-items:center;align-items:center;background:transparent;display:none;height:100%;left:0;margin-left:auto;margin-right:auto;position:absolute;right:0;top:0;width:350px} +@media (min-width:650px){.awvlien{display:-webkit-flex;display:flex;width:630px}} +.a1fnk6ch{cursor:pointer;width:40px} +.a19n5qcz.a1fnk6ch{margin-right:auto} +.a19nei0v.a1fnk6ch{margin-left:auto} +.c1pd6wp7{background:#fff;border-radius:5px;border:1px solid #d8d8d8;height:320px;width:320px;margin:0 5px} +@media (min-width:450px){.c1pd6wp7{width:450px}} +@media (min-width:650px){.c1pd6wp7{width:620px;margin:0 50px}} +.cjmn5uj{-webkit-align-items:center;align-items:center;background-color:#f5f7fa;border-bottom:1px solid #d8d8d8;border-top-right-radius:5px;border-top-left-radius:5px;color:#000;display:-webkit-flex;display:flex;-webkit-flex-flow:row;flex-flow:row;height:110px;padding:15px;width:100%} +@media (min-width:650px){.cjmn5uj{padding:25px}} +.cjmn5uj h3{font-size:16px!important;line-height:20px!important;margin-top:0;margin-bottom:5px} +@media (min-width:650px){.cjmn5uj h3{font-size:18px!important;line-height:35px!important;margin-bottom:0}} +.cjmn5uj h4{font-size:16px!important;font-weight:400!important;margin:0} +@media (min-width:650px){.cjmn5uj h4{font-size:18px!important}} +.cjmn5uj .twitter{margin-left:auto} +.a1r8fups{border-radius:50%;height:50px;margin-right:10px;width:50px} +@media (min-width:650px){.a1r8fups{height:60px;margin-right:20px;width:60px}} +.c1htk8me{color:#000;font-size:18px!important;line-height:28px!important;padding:15px 30px;text-align:center} +@media (min-width:650px){.c1htk8me{font-size:24px!important;padding:40px 80px}} +.o18vdt8l{padding:0;height:400px} +.i1olqn3s{max-width:unset;overflow:hidden} +.s1k6gqjz{min-height:500px} +@media (max-width:992px){.s1k6gqjz{padding:60px 15px}} +.c1idk4n3{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;color:#fff;text-align:center} +.c1idk4n3 h2{font-size:3rem!important;margin-bottom:16px;margin-top:40px} +@media (min-width:768px){.c1idk4n3 h2{font-size:4rem!important}} +.c1idk4n3 h2 .brand{color:#21005d} +.c1idk4n3 h3{margin-bottom:1rem;font-size:30px!important} +.c1idk4n3 h3,.c1idk4n3 p{margin-top:0;font-weight:300!important} +.c1idk4n3 p{margin-bottom:40px;width:100%;line-height:24px!important} +@media (min-width:768px){.c1idk4n3 p{width:450px}} +.c1idk4n3 img{width:120px} +@media (min-width:768px){.c1idk4n3 img{width:175px}} +.s1xiom6p{min-height:400px} +@media (max-width:992px){.s1xiom6p{padding:60px 15px}} +.ct77mvk{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:center;align-items:center;color:#000;text-align:center} +.ct77mvk h2{font-size:3rem!important;margin-bottom:40px} +@media (min-width:768px){.ct77mvk h2{font-size:4rem!important}} +.ct77mvk h2 .brand{color:#21005d} +.ct77mvk h3{margin-top:40px;margin-bottom:1rem;font-size:30px!important;font-weight:300!important} +.ct77mvk p{margin-top:0;margin-bottom:40px;font-weight:300!important;width:100%;line-height:24px!important} +@media (min-width:768px){.ct77mvk p{width:450px}} +.ct77mvk img{width:250px} +@media (min-width:768px){.ct77mvk img{width:300px}} +.w1xuvb7e{position:relative;padding-bottom:0;padding-top:0;min-height:700px} +@media (min-width:401px){.w1xuvb7e{min-height:420px}} +@media (min-width:901px){.w1xuvb7e{min-height:700px}} +.c1i5490c{display:-webkit-flex;display:flex;-webkit-flex-flow:row;flex-flow:row;padding-bottom:0} +@media (min-width:901px){.c1i5490c{padding-bottom:0}} +.h1knnu89{font-size:52px!important;line-height:120%!important;margin-top:0;margin-bottom:25px} +.h1knnu89 span{color:#21005d} +.b1tutycc{font-size:18px!important;color:#414757;line-height:28px!important;margin-top:0;margin-bottom:10px} +.m6ymebm{width:auto;position:absolute;height:720px;top:20px;left:0} +.mbij43c{display:none} +@media (min-width:900px){.mbij43c{position:relative;display:-webkit-flex;display:flex;-webkit-flex:1;flex:1 1}} +.h1c4x4n0{display:-webkit-flex;display:flex;-webkit-flex-direction:column;flex-direction:column;-webkit-flex:1;flex:1 1;padding-top:30px} +@media (min-width:600px){.h1c4x4n0{padding:75px 35px 0}} +@media (min-width:900px){.h1c4x4n0{max-width:495px}} diff --git a/docs/src/css/landing.css b/docs/src/css/landing.css new file mode 100644 index 0000000000..c126997d38 --- /dev/null +++ b/docs/src/css/landing.css @@ -0,0 +1,550 @@ +/* + * Landing page ported from reactnativepaper.com + * (callstack-internal/paper-landing-page). + * + * The visual rules live in landing-vendor.css, lifted verbatim from the + * original's compiled stylesheet. This file holds only what that cannot + * provide: the webfonts, and the overrides needed because the docs preset + * styles headings, `body *`, `pre`/`code` and font synthesis with !important. + * + * Roboto and Roboto Mono are Apache-2.0 and vendored. Platform Web, which the + * original sets its headings in, is a Commercial Type retail face: the files + * come from the source repo's src/fonts/PlatformWeb and carry "Not to be used + * for anything other than web font use!". Callstack's licence with Commercial + * Type needs to cover both the domain this is served from and redistribution + * through this repo, which is public where the source repo is not. + * + * Only the Medium cut exists, declared at `normal` as the original does, so + * the bold headings are the browser synthesising weight from it - hence the + * `font-synthesis` reset further down. + */ + +@import './landing-vendor.css'; + +@font-face { + font-family: 'Platform Web'; + font-style: normal; + font-weight: normal; + font-display: swap; + src: + url('../assets/landing/fonts/PlatformWeb-Medium.woff2') format('woff2'), + url('../assets/landing/fonts/PlatformWeb-Medium.woff') format('woff'); +} + +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url('../assets/landing/fonts/roboto-300.woff2') format('woff2'); +} + +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('../assets/landing/fonts/roboto-400.woff2') format('woff2'); +} + +/* Paper's typescale leans on Medium: labelLarge, titleMedium and the rest of + the demo ask for 500, and without this face CSS font matching drops them to + 400, which is why the phone's body copy rendered light. */ +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('../assets/landing/fonts/roboto-500.woff2') format('woff2'); +} + +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('../assets/landing/fonts/roboto-700.woff2') format('woff2'); +} + +@font-face { + font-family: 'Roboto Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('../assets/landing/fonts/roboto-mono-400.woff2') format('woff2'); +} + +/* typography.css in the original: Roboto body, Platform Web for h1/h2. */ +/* The original's global resets, scoped so they cannot leak into the docs. */ +.landing, +.landing *, +.landing *::before, +.landing *::after { + box-sizing: border-box; +} + +.landing { + font-family: Roboto, sans-serif; + color: #000; + background: #fff; + + /* The preset disables weight synthesis and opts into antialiasing; the + original runs with the browser defaults. */ + font-synthesis: weight style small-caps; + -webkit-font-smoothing: auto; + text-rendering: auto; +} + +/* The live demo renders Paper's Appbar title as an `h1`, so every heading rule + below carries `:where(:not(.landing-editor__preview *))` to leave the phone's + contents to Paper. Wrapping the `:not()` in `:where()` keeps it specificity + free, so each rule still sits exactly where the cascade needs it. */ + +/* Excluding the landing rules is only half of it: the preset's own `h1..h6` + rules reach in too, and Paper applies its typescale through RN Web classes, + which cannot outrank an !important. The demo's only heading is the Appbar + title, which Paper sets as titleLarge - 22/28 at 400 in the theme stack. */ +.landing .landing-editor__preview :is(h1, h2, h3, h4, h5, h6) { + font-family: Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif !important; + font-weight: 400 !important; + line-height: 28px !important; +} + +/* Paper draws its icons as glyphs in a Text with `lineHeight` equal to + `fontSize`, set inline - which `body *`'s !important 1.5 overrides, giving + the glyph a line box half a line too tall and dropping it below the text it + sits beside. `1` restores the relationship at every icon size. */ +.landing .landing-editor__preview [role='img'] { + line-height: 1 !important; +} + +/* typography.css in the original: Platform Web for h1/h2, Roboto elsewhere. + The preset sets its own face on h1-h6 with !important, so these have to + outrank it. h3-h6 stay inside `:where()` so the one heading the original + does set in Platform Web, `.n1himknf h3`, still wins. */ +.landing h1:where(:not(.landing-editor__preview *)), +.landing h2:where(:not(.landing-editor__preview *)) { + font-family: 'Platform Web', Roboto, sans-serif !important; +} + +.landing :where(h3, h4, h5, h6):where(:not(.landing-editor__preview *)) { + font-family: Roboto, sans-serif !important; +} + +/* Nested spans and links fall back to the preset's face and to `body *`'s + leading otherwise, which leaves a heading set in two fonts and stretches + its line box - the stat labels wrap a link and were running 18px to 14. */ +.landing :is(h1, h2, h3, h4, h5, h6):where(:not(.landing-editor__preview *)) * { + font-family: inherit; + line-height: inherit !important; +} + +/* --- Overrides for the docs preset's !important declarations --- + The vendored rules carry !important on the typographic properties, so they + already outrank the preset. What remains is the elements the original + styles globally rather than by class. */ + +.landing h1, +.landing h2, +.landing h1 * { + font-feature-settings: normal !important; + font-variant-numeric: normal !important; +} +/* The preset tightens every descendant of `body` to -.02em, which narrows the + body copy enough to drop a line from most paragraphs. The original runs at + the font's own tracking. `.landing *` is a single class - above the preset's + `body *`, below every vendored rule that sets its own tracking, such as + `.cf3elge h5` or `.c11ps6c6 .token`. */ +.landing, +.landing * { + letter-spacing: normal !important; +} + +/* Same story for weight: the preset puts every heading at 500, the original + leaves them at the UA's bold unless a vendored rule says otherwise. Keeping + the element names inside `:where()` holds this at one class of specificity, + so `.cjmn5uj h4`, `.ct77mvk h3` and the other 300/400 rules still win. */ +.landing :where(h1, h2, h3, h4, h5, h6):where(:not(.landing-editor__preview *)) { + font-weight: bold !important; +} + +/* `.cn49emr` sets its own 24px leading, but the preset's `body *` overrides it + on the two rows inside, which then collapse to 12px * 1.5. */ +.landing .cn49emr * { + line-height: inherit !important; +} + +/* Same for the GitHub star widget: both boxes are 32px in the original, held + there by the 16px leading its two halves declare. `body *` overrides that on + their contents (12px * 1.5 = 18), stretching the count box to 34. */ +.landing .gwvv8a1 *, +.landing .s152rxo6 * { + line-height: inherit !important; +} + +/* And the star itself is an inline svg sitting on the baseline; the preset's + svg reset makes it a block, which drops the descender space under it and + leaves the button 2px short of the count beside it. */ +.landing .s5mrc6w svg { + display: inline; + vertical-align: baseline; +} + + +/* The preset resets heading margins (`blockquote, ..., h1, h2, ... { margin: 0 }`) + and forces `line-height: 1.2`, both at element specificity. The original + leaves headings at the UA defaults unless a vendored class says otherwise - + its `normal` leading is exactly 1.0 in Platform Web, so a 64px heading + occupies 64px. Re-assert both through `:where()`, which contributes no + specificity: these outrank the preset's element rules while still losing to + any vendored class, so `.h15lhj08`'s margin and `.hmtzm5j`'s 120% survive. */ +:where(.landing) h1 { + margin-block: 0.67em; +} + +:where(.landing) h2 { + margin-block: 0.83em; +} + +:where(.landing) h3 { + margin-block: 1em; +} + +:where(.landing) h4 { + margin-block: 1.33em; +} + +:where(.landing) h5 { + margin-block: 1.67em; +} + +:where(.landing) h6 { + margin-block: 2.33em; +} + +:where(.landing) p { + margin-block: 1em; +} + +/* Leading needs to outrank the preset's `h1..h6 { line-height: 1.2 }`, which + is !important, so it cannot ride at `:where()` specificity. `.hmtzm5j` and + `.h1knnu89` are the two headings the original gives an explicit 120%; they + are excluded by name rather than by restating their value here. */ +.landing h1:not(.hmtzm5j):not(.h1knnu89):where(:not(.landing-editor__preview *)), +.landing h2:where(:not(.landing-editor__preview *)) { + line-height: normal !important; +} + +/* h3-h6 are Roboto in the original and take its natural leading; the preset + forces 1.2 (and `body *` 1.5 on the smaller ones). Holding this at one class + of specificity leaves vendored leading, such as `.cf3elge h5`'s 24px or + `.cjmn5uj h3`'s 35px, in charge where the original sets it. */ +.landing :where(h3, h4, h5, h6):where(:not(.landing-editor__preview *)) { + line-height: normal !important; +} + +/* The one h3 the original sets in Platform Web, so it takes that face's own + leading rather than Roboto's. */ +.landing .n1himknf h3 { + line-height: normal !important; +} + +/* Header.js sets these inline from its sticky state; this is the unscrolled + state. The original spaces three children apart, we only carry the copy. */ +.landing .hv0shhd { + --hv0shhd-0: transparent; + --hv0shhd-1: none; + --hv0shhd-2: #000000; +} + +.landing .h12yetm2 { + justify-content: center; +} + +/* rspress decorates the page wrapper with the docs footer artwork - a + background image plus an `::after` holding the Callstack mark - and reserves + 181px of padding for it. That sits below the landing page's own footer, + where the original ends. Keyed to the wrapper that holds `.landing`, so + every other docs page keeps it. */ +/* The theme's own rule is `div#__rspress_root:not(:has(div[class^="rp-doc"]))`, + which outranks anything without the id, hence matching it here. */ +div#__rspress_root:has(> main.landing) { + padding-bottom: 0 !important; + background-image: none !important; +} + +div#__rspress_root:has(> main.landing)::after { + content: none !important; +} + +/* --- The two blocks the original builds in JS rather than CSS --- */ + +/* Editor.js lays the live demo out with inline React Native Web styles, so + there are no linaria classes to lift. These reproduce those values. */ +.landing-editor { + display: flex; + flex-direction: row; + justify-content: space-between; + flex-wrap: wrap; + width: 100%; + padding-bottom: 50px; +} + +.landing-editor__col { + position: relative; + flex: 0 0 50%; + max-width: 460px; +} + +.landing-editor__icon { + align-self: start; + width: 65px; + height: auto; +} + +/* The source leaves the h1 at the UA default `margin: .67em 0`, which the + preset zeroes; its leading is handled by the global heading rule above. */ +.landing .landing-editor__heading { + margin-top: 30px; + margin-bottom: 0.67em; + text-align: left; +} + +/* `line-height: 28` against the preset's `body *` rule, which would otherwise + reflow the copy from five lines to four. */ +.landing .landing-editor__lede { + max-width: 400px; + margin-bottom: 50px; + color: #414757; + font-size: 18px !important; + line-height: 28px !important; +} + +/* Editor.js `liveEditorWrapper`: a fixed 400x400 scroll box. Without the cap + the panel runs to the full height of the snippet (1426px). */ +.landing-editor__codebox { + height: 400px; + max-width: 400px; + overflow: auto; + border-radius: 5px; + background: #202c32; +} + +/* `styles.editor` - the pane is 380px inside the 400px box, offset by 20px. */ +.landing .landing-editor__codebox > pre { + width: 100%; + max-width: 380px; + margin: 0 0 0 20px; + overflow: visible !important; +} + +/* `cssStyles.numericLine` from Editor.js: prism emits one `.token-line` per + line, and the counter renders into the 20px gutter the offset above opens. */ +.landing .landing-code--numbered { + position: relative; + counter-reset: token-line; + /* react-simple-code-editor, which the source's LiveEditor renders through, + wraps long lines; prism's own `.prism-code` would let them overflow. */ + white-space: pre-wrap !important; +} + +/* The counter is absolute with only `left` set, so each number keeps its + line's static vertical position and lands 10px outside the pane - inside + the 400px box, in the gutter the 20px offset opens. */ +.landing .landing-code--numbered .token-line::before { + counter-increment: token-line; + content: counter(token-line); + position: absolute; + left: -10px; + overflow: visible; + color: rgb(84, 94, 104); +} + +/* The preset's `body *` would set these lines to 24px against the source's + 21px; the pane is a scroll box, so this only shows as misaligned numbers. */ +.landing .landing-code--numbered, +.landing .landing-code--numbered * { + line-height: normal !important; +} + +/* `ornamentStyles` - the gradient line and dot bridging the code box and the + phone. `` resolves to rotate(0deg). */ +.landing-editor__ornament { + position: absolute; + top: 470px; + left: 425px; + display: block !important; + transform: rotate(0deg); +} + +/* `previewWrapper`: offset so the phone starts 80px below the row top. */ +.landing-editor__phone { + position: relative; + flex: 0 0 auto; + width: 380px; + height: 793px; + bottom: -80px; +} + +/* `mobileImg` carries zIndex 3: the frame paints over the screen, so the + bezel and notch mask the demo and only the PNG's transparent cutout shows. + ip13.png is 1570x2932 (0.536) against a 380x793 box (0.479), and the source + draws it through a React Native , whose default `cover` crops 22px a + side. Stretching it to 380 instead walks the cutout in to 48-331 and leaves + the demo 282px wide; with `cover` it lands at 32-348, as on the original. */ +.landing-editor__frame { + position: relative; + z-index: 3; + display: block; + width: 380px; + height: 793px; + object-fit: cover; + pointer-events: none; +} + +.landing-editor__screen { + position: absolute; + top: 37px; + left: 30px; + width: 320px; + height: 710px; + background: #fff; +} + +.landing-editor__preview { + width: 100%; + height: 100%; + overflow: hidden; + border: 1px solid rgba(33, 0, 93, 1); + border-radius: 50px; +} + +/* Testimonials render through a drag Slider in the source; these cards stand + in for it while keeping the original's proportions. */ +.landing-testimonials { + display: grid; + gap: 32px; + grid-template-columns: repeat(3, minmax(0, 1fr)); + width: 100%; +} + +.landing-testimonials__item { + display: flex; + flex-direction: column; + gap: 20px; + margin: 0; +} + +.landing-testimonials__item blockquote { + margin: 0; + font-size: 18px; + line-height: 28px; +} + +.landing-testimonials__item figcaption { + display: flex; + align-items: center; + gap: 14px; + margin-top: auto; +} + +.landing-testimonials__item figcaption img { + width: 60px; + height: 60px; + border-radius: 50%; + object-fit: cover; +} + +.landing-testimonials__item figcaption a { + display: block; + color: inherit; + font-weight: 600; +} + +.landing-testimonials__item figcaption small { + display: block; + color: #414757; + font-size: 14px; +} + +/* LookAndFeel.js spaces its second row with an inline marginTop. */ +.landing-lookfeel__row--second { + margin-top: 60px; +} + +/* Only the Paper snippet is syntax-coloured in the source (the standard-React + one uses grayTheme and stays monochrome). These mirror editorTheme's token + colours for the hand-marked spans. */ +.landing .tok-tag { + color: #f92f5f; +} + +.landing .tok-attr { + color: #d7ad58; +} + +.landing .tok-value { + color: rgb(100, 172, 144); +} + +/* The preset forces `img { display: block }`; the original leaves these at the + browser default, which is what puts the store badges on one row. */ +.landing .skghjfv, +.landing .bpqw886, +.landing .hz1a1q4 img, +.landing .d1cbpza3 img { + display: inline !important; +} + +/* The footer logo sits on the text baseline in the original, so its line box + carries the descender space below it; the preset's `img` reset centres it + and `body *` gives the block a 24px strut, which together cost 4px. */ +.landing .d1cbpza3 { + line-height: normal !important; +} + +/* The vendored `.d1cbpza3 p` carries `letter-spacing: 2`, which is unitless + and so invalid: the original drops it and the copy runs at normal tracking. + Our CSS pipeline normalises it to 2px instead, which costs the paragraph a + line. Overridden here rather than in the extract, so a regeneration of + landing-vendor.css cannot quietly bring it back. */ +.landing .d1cbpza3 p { + letter-spacing: normal !important; +} + +.landing .d1cbpza3 img { + vertical-align: baseline; +} + +/* `body * { line-height: 1.5 }` from the preset inflates the code panels; the + original renders the pane and its lines at Roboto Mono's natural leading. + The tokens are left out on purpose - the vendored `.c11ps6c6 .token` rule + puts them at 1.3, and the line box ends up the union of the two. */ +.landing .c11ps6c6, +.landing .c11ps6c6 .token-line { + line-height: normal !important; +} + +/* The preset's `code, kbd, samp, pre { font-family }` is !important, so it + outranks the font prism applies inline and swaps in Fira Code. */ +.landing .seb0k0g, +.landing .landing-code--numbered { + font-family: 'Roboto Mono', monospace !important; +} + +/* The same preset rule sets `font-weight: 500` on code, where the original + runs at 400. It only looked right while no Roboto Mono Medium was loaded to + fall back from. */ +.landing .seb0k0g, +.landing .seb0k0g *, +.landing .landing-code--numbered, +.landing .landing-code--numbered * { + font-weight: 400 !important; +} + +/* Code.js nests prism's own
 inside a .c11ps6c6 wrapper; the wrapper is
+   just the 460px frame, the inner pre carries the dark surface. */
+.landing .c11ps6c6 > pre {
+  margin: 0;
+  width: 100%;
+}
diff --git a/docs/theme/index.tsx b/docs/theme/index.tsx
index 3cd7557c48..0d9acbae17 100644
--- a/docs/theme/index.tsx
+++ b/docs/theme/index.tsx
@@ -1,10 +1,8 @@
-import { Fragment, type ComponentProps } from 'react';
+import { type ComponentProps } from 'react';
 
-import { HomeBanner } from '@callstack/rspress-theme';
 import { Layout as BasicLayout } from '@rspress/core/dist/theme/index.js';
 
 import Outline from './Outline';
-import PaperHomeShowcase from '../src/components/PaperHomeShowcase';
 import PaperVersionSelector from '../src/components/PaperVersionSelector';
 import VersionedPrereleaseNotice from '../src/components/VersionedPrereleaseNotice';
 
@@ -13,17 +11,6 @@ type LayoutProps = ComponentProps;
 const Layout = (props: LayoutProps) => (
   
-        
-        
-      
-    }
     afterNavMenu={}
     afterOutline={false}
     beforeDocContent={}
diff --git a/yarn.lock b/yarn.lock
index ee4558c745..99180174de 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5454,6 +5454,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/prismjs@npm:^1.26.0":
+  version: 1.26.6
+  resolution: "@types/prismjs@npm:1.26.6"
+  checksum: 10c0/152a27500cb32b114edfb77f9d0dccd03bebc84828d1e92abacaf212b22d3ccdde041ce421dd58b6ec8461bbec7cd76ed5ee773cae4be7ca36a6dd4ddcf0f9e7
+  languageName: node
+  linkType: hard
+
 "@types/react-color@npm:^3.0.6":
   version: 3.0.13
   resolution: "@types/react-color@npm:3.0.13"
@@ -7811,7 +7818,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"clsx@npm:2.1.1":
+"clsx@npm:2.1.1, clsx@npm:^2.0.0":
   version: 2.1.1
   resolution: "clsx@npm:2.1.1"
   checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839
@@ -9093,6 +9100,7 @@ __metadata:
     color: "npm:^4.2.3"
     component-docs: "npm:^0.24.0"
     marked: "npm:^4.1.1"
+    prism-react-renderer: "npm:^2.4.1"
     react: "npm:19.2.6"
     react-color: "npm:^2.19.3"
     react-dom: "npm:19.2.6"
@@ -17623,6 +17631,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"prism-react-renderer@npm:^2.4.1":
+  version: 2.4.1
+  resolution: "prism-react-renderer@npm:2.4.1"
+  dependencies:
+    "@types/prismjs": "npm:^1.26.0"
+    clsx: "npm:^2.0.0"
+  peerDependencies:
+    react: ">=16.0.0"
+  checksum: 10c0/ebbe8feb975224344bbdd046b3a937d121592dbe4b8f22ba0be31f5af37b9a8219f441138ef6cab1c5b96f2aa6b529015200959f7e5e85b60ca69c81d35edcd4
+  languageName: node
+  linkType: hard
+
 "prismjs@npm:~1.27.0":
   version: 1.27.0
   resolution: "prismjs@npm:1.27.0"