docs: update landing page (#4807)

This commit is contained in:
Kamil Moskała
2025-12-29 20:52:14 +01:00
committed by GitHub
parent 6aaeebce8a
commit 69f3dd9e31
39 changed files with 1034 additions and 138 deletions
+9
View File
@@ -37,6 +37,7 @@
"@widlarzgroup/docusaurus-ui": "^0.0.3",
"clsx": "^2.0.0",
"docusaurus-lunr-search": "^3.6.0",
"motion": "^12.23.26",
"prism-react-renderer": "^2.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@@ -1923,6 +1924,8 @@
"fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
"framer-motion": ["framer-motion@12.23.26", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA=="],
"fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="],
"fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
@@ -2673,6 +2676,12 @@
"mkdirp": ["mkdirp@0.3.0", "", {}, "sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew=="],
"motion": ["motion@12.23.26", "", { "dependencies": { "framer-motion": "^12.23.26", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Ll8XhVxY8LXMVYTCfme27WH2GjBrCIzY4+ndr5QKxsK+YwCtOi2B/oBi5jcIbik5doXuWT/4KKDOVAZJkeY5VQ=="],
"motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="],
"motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="],
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+1 -1
View File
@@ -86,7 +86,7 @@ const config: Config = {
navbar: {
title: 'React Native Video',
logo: {
alt: 'React Native Video Logo',
alt: 'TheWidlarzGroup Logo',
style: {
width: '60px',
height: '40px',
+1
View File
@@ -23,6 +23,7 @@
"@widlarzgroup/docusaurus-ui": "^0.0.3",
"clsx": "^2.0.0",
"docusaurus-lunr-search": "^3.6.0",
"motion": "^12.23.26",
"prism-react-renderer": "^2.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
@@ -0,0 +1,115 @@
.enterprise {
padding: 6rem 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
}
.textContent {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.label {
display: inline-block;
padding: 0.5rem 1rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--color-accent);
background: var(--color-accent-subtle);
border-radius: 100px;
width: fit-content;
}
.title {
margin: 0;
font-size: 2.5rem;
font-weight: 700;
line-height: 1.2;
color: var(--color-text-primary);
}
.description {
margin: 0;
font-size: 1.125rem;
line-height: 1.6;
color: var(--color-text-secondary);
}
.ctaButton {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.875rem 1.5rem;
font-size: 1rem;
font-weight: 600;
color: var(--color-text-primary);
background: var(--color-accent-bright);
border-radius: 8px;
text-decoration: none;
transition: all 0.2s ease;
width: fit-content;
}
[data-theme='dark'] .ctaButton {
color: var(--color-bg-primary);
}
.ctaButton:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.arrowIcon {
width: 20px;
height: 20px;
transition: transform 0.2s ease;
}
.ctaButton:hover .arrowIcon {
transform: translateX(4px);
}
.featuresList {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (max-width: 968px) {
.content {
grid-template-columns: 1fr;
gap: 3rem;
}
.title {
font-size: 2rem;
}
}
@media (max-width: 640px) {
.enterprise {
padding: 4rem 1.5rem;
}
.title {
font-size: 1.75rem;
}
.description {
font-size: 1rem;
}
}
@@ -0,0 +1,64 @@
import React from 'react';
import Link from '@docusaurus/Link';
import { FeatureCard, type FeatureCardItem } from './FeatureCard/FeatureCard';
import styles from './Enterprise.module.css';
const ArrowIcon = require('@site/static/img/arrow-icon.svg').default;
const LayersIcon =
require('@site/static/img/homepage/enterprise/layers-icon.svg').default;
const ShieldIcon =
require('@site/static/img/homepage/enterprise/shield-icon.svg').default;
const LightningIcon =
require('@site/static/img/homepage/enterprise/lightning-icon.svg').default;
const CONTACT_URL =
'https://www.thewidlarzgroup.com/?utm_source=rnv&utm_medium=docs&utm_campaign=landing-enterprise-solutions&utm_id=enterprise#Contact';
const FEATURE_LIST: FeatureCardItem[] = [
{
title: 'Custom Integration',
description: 'Custom solutions for your specific requirements',
Icon: LayersIcon,
},
{
title: 'Dedicated Support',
description: 'Priority support with direct access to our team',
Icon: ShieldIcon,
},
{
title: 'Expert Consultation',
description: 'Architecture guidance and best practices',
Icon: LightningIcon,
},
];
export function Enterprise() {
return (
<section className={styles.enterprise}>
<div className={styles.container}>
<div className={styles.content}>
<div className={styles.textContent}>
<span className={styles.label}>Enterprise Solutions</span>
<h2 className={styles.title}>
Need custom integration or support?
</h2>
<p className={styles.description}>
Our team provides enterprise-grade integration services, custom
development, and dedicated support to help you build the perfect
video solution for your business.
</p>
<Link to={CONTACT_URL} className={styles.ctaButton}>
Get in touch
<ArrowIcon className={styles.arrowIcon} />
</Link>
</div>
<div className={styles.featuresList}>
{FEATURE_LIST.map((feature) => (
<FeatureCard key={feature.title} {...feature} />
))}
</div>
</div>
</div>
</section>
);
}
@@ -0,0 +1,52 @@
.card {
display: flex;
gap: 1rem;
padding: 1.25rem;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: 12px;
transition: all 0.2s ease;
}
.card:hover {
border-color: var(--color-border-hover);
}
.iconWrapper {
flex-shrink: 0;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
border-radius: 10px;
}
.icon {
width: 24px;
height: 24px;
stroke: var(--color-accent);
}
.content {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.title {
margin: 0;
font-size: 1rem;
font-weight: 600;
color: var(--color-text-primary);
}
.description {
margin: 0;
font-size: 0.875rem;
color: var(--color-text-secondary);
line-height: 1.5;
}
@@ -0,0 +1,22 @@
import React from 'react';
import styles from './FeatureCard.module.css';
export interface FeatureCardItem {
title: string;
description: string;
Icon: React.ComponentType<React.ComponentProps<'svg'>>;
}
export function FeatureCard({ title, description, Icon }: FeatureCardItem) {
return (
<div className={styles.card}>
<div className={styles.iconWrapper}>
<Icon role="img" className={styles.icon} />
</div>
<div className={styles.content}>
<h3 className={styles.title}>{title}</h3>
<p className={styles.description}>{description}</p>
</div>
</div>
);
}
@@ -0,0 +1,70 @@
.card {
position: relative;
padding: 2rem;
border-radius: 16px;
background: var(--glass-bg);
border: 1px solid var(--color-border);
transition: all 0.3s ease;
backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
}
.card:hover {
border-color: rgba(150, 201, 235, 0.4);
background: var(--color-bg-elevated);
box-shadow: 0 4px 20px rgba(150, 201, 235, 0.15);
transform: translateY(-2px);
}
[data-theme='dark'] .card:hover {
box-shadow: none;
}
.iconWrapper {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
margin-bottom: 1.5rem;
border-radius: 12px;
background: var(--color-accent-subtle);
border: 1px solid rgba(150, 201, 235, 0.2);
}
[data-theme='dark'] .iconWrapper {
background: var(--color-bg-tertiary);
border: 1px solid var(--color-border);
}
.icon {
width: 24px;
height: 24px;
stroke: var(--color-accent);
}
.title {
font-size: 1.15rem;
font-weight: 600;
color: var(--color-text-primary);
margin-bottom: 0.75rem;
letter-spacing: -0.01em;
}
.description {
font-size: 0.95rem;
color: var(--color-text-secondary);
line-height: 1.7;
margin: 0;
}
.card:hover .iconWrapper {
background: rgba(150, 201, 235, 0.15);
border-color: rgba(150, 201, 235, 0.3);
}
[data-theme='dark'] .card:hover .iconWrapper {
background: rgba(150, 201, 235, 0.1);
border-color: rgba(150, 201, 235, 0.2);
}
@@ -0,0 +1,20 @@
import React from 'react';
import styles from './Feature.module.css';
export interface FeatureItem {
title: string;
Icon: React.ComponentType<React.ComponentProps<'svg'>>;
description: string;
}
export function Feature({ title, Icon, description }: FeatureItem) {
return (
<div className={styles.card}>
<div className={styles.iconWrapper}>
<Icon role="img" className={styles.icon} />
</div>
<h3 className={styles.title}>{title}</h3>
<p className={styles.description}>{description}</p>
</div>
);
}
@@ -0,0 +1,69 @@
.features {
padding: 6rem 2rem 8rem;
position: relative;
background: linear-gradient(180deg, transparent 0%, var(--color-bg-primary) 15%, var(--color-bg-primary) 100%);
margin-top: -1px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 4rem;
}
.label {
display: inline-block;
padding: 0.4rem 1rem;
background: var(--color-accent-subtle);
border: 1px solid rgba(150, 201, 235, 0.2);
border-radius: 100px;
font-size: 0.8rem;
font-weight: 600;
color: var(--color-accent);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 1.5rem;
}
.title {
font-size: clamp(1.75rem, 4vw, 2.5rem);
font-weight: 700;
color: var(--color-text-primary);
letter-spacing: -0.03em;
margin-bottom: 1rem;
line-height: 1.2;
}
.subtitle {
font-size: 1.1rem;
color: var(--color-text-muted);
max-width: 500px;
margin: 0 auto;
line-height: 1.7;
}
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
@media (max-width: 1024px) {
.grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 640px) {
.grid {
grid-template-columns: 1fr;
}
.features {
padding: 4rem 1.5rem 6rem;
}
}
@@ -0,0 +1,72 @@
import React from 'react';
import { Feature, type FeatureItem } from './Feature/Feature';
import styles from './Features.module.css';
const FEATURE_LIST: FeatureItem[] = [
{
title: 'React Native First',
Icon: require('@site/static/img/homepage/features/reactjs-icon.svg')
.default,
description:
'Built specifically for React Native with a native-first approach. Seamless integration with your existing React Native projects.',
},
{
title: 'Multi-Platform',
Icon: require('@site/static/img/homepage/features/platforms-icon.svg')
.default,
description:
'iOS, Android, tvOS, visionOS, Vega OS, Web, and Windows. One API across all platforms with platform-specific optimizations.',
},
{
title: 'Subtitles & Audio',
Icon: require('@site/static/img/homepage/features/subtitles-icon.svg')
.default,
description:
'Full support for multiple subtitle formats and audio tracks. Switch between languages on the fly.',
},
{
title: 'DRM Protected',
Icon: require('@site/static/img/homepage/features/shield-icon.svg').default,
description:
'Industry-standard DRM support including Widevine, FairPlay, and PlayReady for secure content delivery.',
},
{
title: 'High Performance',
Icon: require('@site/static/img/homepage/features/performance-icon.svg')
.default,
description:
'Optimized native players under the hood. Smooth playback with minimal memory footprint.',
},
{
title: 'Plugin System',
Icon: require('@site/static/img/homepage/features/plugins-icon.svg')
.default,
description:
'Extensible architecture with a powerful plugin system. Add custom functionality without forking.',
},
];
export function Features() {
return (
<section className={styles.features}>
<div className={styles.container}>
<div className={styles.header}>
<span className={styles.label}>Features</span>
<h2 className={styles.title}>
Everything you need for video playback
</h2>
<p className={styles.subtitle}>
A comprehensive video solution trusted by thousands of developers
worldwide
</p>
</div>
<div className={styles.grid}>
{FEATURE_LIST.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
);
}
@@ -0,0 +1,27 @@
.badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
margin-bottom: 1.5rem;
background: var(--color-secondary-subtle);
border: 1px solid rgba(250, 216, 91, 0.25);
border-radius: 100px;
font-size: 0.85rem;
font-weight: 500;
color: var(--color-secondary);
}
.dot {
width: 8px;
height: 8px;
background: var(--color-secondary-dim);
border-radius: 50%;
}
@media screen and (max-width: 480px) {
.badge {
font-size: 0.75rem;
padding: 0.4rem 0.8rem;
}
}
@@ -0,0 +1,21 @@
import React, { type ReactNode } from 'react';
import { motion } from 'motion/react';
import { itemVariants } from '../Hero';
import styles from './Badge.module.css';
interface BadgeProps {
children: ReactNode;
}
export function Badge({ children }: BadgeProps) {
return (
<motion.div className={styles.badge} variants={itemVariants}>
<motion.span
className={styles.dot}
animate={{ opacity: [0.5, 0.3, 0.5] }}
transition={{ duration: 2, ease: 'easeInOut', repeat: Infinity }}
/>
{children}
</motion.div>
);
}
@@ -0,0 +1,68 @@
.buttons {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
}
.primary,
.secondary {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 1rem 2rem;
font-size: 1rem;
font-weight: 600;
border-radius: 12px;
text-decoration: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.primary:hover,
.secondary:hover {
transform: translateY(-2px);
}
.primary {
background: var(--color-accent-bright);
color: var(--color-dark);
box-shadow: 0 0 0 0 rgba(150, 201, 235, 0);
}
.primary:hover {
background: var(--color-accent-bright-dim);
}
.secondary {
background: transparent;
color: var(--color-text-primary);
border: 1px solid var(--color-border);
}
.secondary:hover {
background: var(--color-accent-subtle);
border-color: var(--color-border-hover);
}
.icon {
width: 20px;
height: 20px;
transition: transform 0.3s ease;
}
.primary:hover .icon {
transform: translateX(4px);
}
@media screen and (max-width: 768px) {
.buttons {
flex-direction: column;
width: 100%;
}
.primary,
.secondary {
width: 100%;
justify-content: center;
}
}
@@ -0,0 +1,28 @@
import React from 'react';
import Link from '@docusaurus/Link';
import { motion } from 'motion/react';
import { itemVariants } from '../Hero';
import styles from './Buttons.module.css';
const ArrowIcon = require('@site/static/img/arrow-icon.svg').default;
const GitHubIcon = require('@site/static/img/github-icon.svg').default;
interface ButtonsProps {
docsLink: string;
githubLink: string;
}
export function Buttons({ docsLink, githubLink }: ButtonsProps) {
return (
<motion.div className={styles.buttons} variants={itemVariants}>
<Link to={docsLink} className={styles.primary}>
Get Started
<ArrowIcon role="img" className={styles.icon} />
</Link>
<Link to={githubLink} className={styles.secondary}>
<GitHubIcon role="img" className={styles.icon} />
View on GitHub
</Link>
</motion.div>
);
}
@@ -0,0 +1,118 @@
.hero {
position: relative;
min-height: calc(100vh - var(--ifm-navbar-height));
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 4rem 2rem 8rem;
overflow: visible;
background: var(--color-bg-primary);
}
.hero::before {
content: '';
position: absolute;
top: -50%;
right: -20%;
width: 80vw;
height: 80vw;
max-width: 1200px;
max-height: 1200px;
background: rgba(250, 216, 91, 0.05);
border-radius: 50%;
z-index: 0;
pointer-events: none;
display: none;
}
.hero::after {
content: '';
position: absolute;
bottom: -40%;
left: -20%;
width: 70vw;
height: 70vw;
max-width: 1000px;
max-height: 1000px;
background: rgba(150, 201, 235, 0.05);
border-radius: 50%;
z-index: 0;
pointer-events: none;
display: none;
}
[data-theme='dark'] .hero::before {
background: rgba(250, 216, 91, 0.008);
}
[data-theme='dark'] .hero::after {
background: rgba(150, 201, 235, 0.004);
}
.content {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
max-width: 900px;
}
.logo {
width: 120px;
height: 80px;
margin-bottom: 2rem;
}
.title {
font-size: clamp(2.5rem, 8vw, 5rem);
font-weight: 800;
letter-spacing: -0.04em;
line-height: 1.1;
margin-bottom: 1.5rem;
color: var(--color-text-primary);
}
.titleAccent {
background: linear-gradient(135deg, var(--color-accent-bright) 0%, var(--color-accent-bright-dim) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
padding-right: 0.05em;
}
.subtitle {
font-size: clamp(1.1rem, 2.5vw, 1.4rem);
font-weight: 400;
color: var(--color-text-secondary);
max-width: 600px;
line-height: 1.7;
margin-bottom: 3rem;
}
@media screen and (min-width: 769px) {
.hero::before,
.hero::after {
display: block;
}
}
@media screen and (max-width: 768px) {
.hero {
padding: 3rem 1.5rem;
min-height: auto;
}
.logo {
width: 100px;
height: 66px;
}
}
@media screen and (max-width: 480px) {
.hero {
padding: 2rem 1rem;
}
}
@@ -0,0 +1,98 @@
import React, { useMemo } from 'react';
import { useDocsPreferredVersion } from '@docusaurus/theme-common';
import { motion, type Variants } from 'motion/react';
import { Badge } from './Badge/Badge';
import { Buttons } from './Buttons/Buttons';
import { Stats } from './Stats/Stats';
import { ScrollIndicator } from './ScrollIndicator/ScrollIndicator';
import styles from './Hero.module.css';
const GITHUB_URL = 'https://github.com/TheWidlarzGroup/react-native-video';
const DEFAULT_DOCS_PATH = '/docs/v6/intro';
const STATS = [
{ value: '7k+', label: 'GitHub Stars' },
{ value: '350k+', label: 'Weekly Downloads' },
{ value: 'TWG', label: 'Company Backed' },
];
const containerVariants: Variants = {
hidden: {},
visible: {
transition: {
staggerChildren: 0.1,
delayChildren: 0.1,
},
},
};
export const itemVariants: Variants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: 'easeOut' },
},
};
const logoVariants: Variants = {
hidden: { opacity: 0, scale: 0.8 },
visible: {
opacity: 1,
scale: 1,
transition: { duration: 0.5, ease: 'easeOut' },
},
};
interface PreferredVersion {
preferredVersion: { label: string; mainDocId: string; path: string } | null;
}
function useDocsLink() {
const version = useDocsPreferredVersion() as PreferredVersion;
return useMemo(() => {
if (!version.preferredVersion) {
return DEFAULT_DOCS_PATH;
}
return `${version.preferredVersion.path}/${version.preferredVersion.mainDocId}`;
}, [version]);
}
export function Hero() {
const docsLink = useDocsLink();
return (
<header className={styles.hero}>
<motion.div
className={styles.content}
variants={containerVariants}
initial="hidden"
animate="visible"
>
<Badge>v7 Beta Available</Badge>
<motion.img
src="./img/twg-logo.png"
alt="TheWidlarzGroup logo"
className={styles.logo}
variants={logoVariants}
/>
<motion.h1 className={styles.title} variants={itemVariants}>
<span className={styles.titleAccent}>Video</span> for React Native
</motion.h1>
<motion.p className={styles.subtitle} variants={itemVariants}>
The most feature-rich video player for React Native. Multi-platform
support with DRM, subtitles, and audio tracks.
</motion.p>
<Buttons docsLink={docsLink} githubLink={GITHUB_URL} />
<Stats stats={STATS} />
</motion.div>
<ScrollIndicator />
</header>
);
}
@@ -0,0 +1,27 @@
.indicator {
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
color: var(--color-text-muted);
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.1em;
}
.line {
width: 1px;
height: 40px;
background: linear-gradient(to bottom, var(--color-text-muted), transparent);
}
@media screen and (max-width: 768px) {
.indicator {
display: none;
}
}
@@ -0,0 +1,29 @@
import React from 'react';
import { motion } from 'motion/react';
import styles from './ScrollIndicator.module.css';
interface ScrollIndicatorProps {
label?: string;
}
export function ScrollIndicator({ label = 'Explore' }: ScrollIndicatorProps) {
return (
<motion.div
className={styles.indicator}
initial={{ opacity: 0, y: 20 }}
animate={{
opacity: [0.5, 1, 0.5],
y: [0, 10, 0],
}}
transition={{
duration: 2,
repeat: Infinity,
ease: 'easeInOut',
delay: 0.6,
}}
>
<span>{label}</span>
<div className={styles.line} />
</motion.div>
);
}
@@ -0,0 +1,35 @@
.stats {
display: flex;
gap: 4rem;
margin-top: 5rem;
padding-top: 3rem;
border-top: 1px solid var(--color-border);
}
.stat {
text-align: center;
}
.value {
display: block;
font-size: 2.5rem;
font-weight: 700;
color: var(--color-accent-bright);
letter-spacing: -0.02em;
}
.label {
font-size: 0.9rem;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
@media screen and (max-width: 768px) {
.stats {
flex-direction: column;
gap: 2rem;
margin-top: 3rem;
padding-top: 2rem;
}
}
@@ -0,0 +1,26 @@
import React from 'react';
import { motion } from 'motion/react';
import { itemVariants } from '../Hero';
import styles from './Stats.module.css';
interface StatItem {
value: string;
label: string;
}
interface StatsProps {
stats: StatItem[];
}
export function Stats({ stats }: StatsProps) {
return (
<motion.div className={styles.stats} variants={itemVariants}>
{stats.map((stat) => (
<div key={stat.label} className={styles.stat}>
<span className={styles.value}>{stat.value}</span>
<span className={styles.label}>{stat.label}</span>
</div>
))}
</motion.div>
);
}
@@ -1,60 +0,0 @@
import Heading from '@theme/Heading';
import clsx from 'clsx';
import type { ReactNode } from 'react';
import styles from './styles.module.css';
type FeatureItem = {
title: string;
Svg: React.ComponentType<React.ComponentProps<'svg'>>;
description: ReactNode;
};
const FeatureList: FeatureItem[] = [
{
title: 'React Native First',
Svg: require('@site/static/img/reactjs-icon.svg').default,
description: (
<>React Native Video is a video player created for React Native.</>
),
},
{
title: 'Multi Subtitles & Audio Tracks',
Svg: require('@site/static/img/download-icon.svg').default,
description: (
<>React Native Video supports multiple subtitles and audio tracks.</>
),
},
{
title: 'DRM Protected Content',
Svg: require('@site/static/img/drm-content-icon.svg').default,
description: <>React Native Video supports DRM protected content.</>,
},
];
function Feature({ title, Svg, description }: FeatureItem) {
return (
<div className={clsx('col col--4')}>
<div className="text--center">
<Svg className={styles.featureSvg} role="img" />
</div>
<div className="text--center padding-horiz--md">
<Heading as="h3">{title}</Heading>
<p>{description}</p>
</div>
</div>
);
}
export default function HomepageFeatures(): ReactNode {
return (
<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
);
}
@@ -1,11 +0,0 @@
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}
+5 -1
View File
@@ -11,4 +11,8 @@
*/
@import '@widlarzgroup/docusaurus-ui/css/custom.css';
/* Add your custom overrides below */
/* Add your custom overrides below */
:root {
--color-dark: #0f0f0f;
}
+8 -49
View File
@@ -1,62 +1,21 @@
import Link from '@docusaurus/Link';
import { useDocsPreferredVersion } from '@docusaurus/theme-common';
import React from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import HomepageFeatures from '@site/src/components/HomepageFeatures';
import Heading from '@theme/Heading';
import { Enterprise } from '@site/src/components/Homepage/Enterprise/Enterprise';
import { Features } from '@site/src/components/Homepage/Features/Features';
import Layout from '@theme/Layout';
import clsx from 'clsx';
import { useMemo, type ReactNode } from 'react';
import styles from './index.module.css';
import { Hero } from '../components/Homepage/Hero/Hero';
interface PreferredVersion {
preferredVersion: { label: string; mainDocId: string; path: string } | null;
}
function HomepageHeader() {
const { siteConfig } = useDocusaurusContext();
const version = useDocsPreferredVersion() as PreferredVersion;
const link = useMemo(() => {
if (!version.preferredVersion) {
return '/docs/v6/intro'; // Default to v6 if no version is selected
}
return `${version.preferredVersion.path}/${version.preferredVersion.mainDocId}`;
}, [version]);
return (
<header className={clsx('hero hero--primary', styles.heroBanner)}>
<div className="container">
<img
src="./img/twg-logo.png"
alt="TheWidlarzGroup Logo"
className={styles.logo}
/>
<Heading as="h1" className="hero__title">
{siteConfig.title}
</Heading>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<div className={styles.buttons}>
<Link className="button button--secondary button--lg" to={link}>
Get Started
</Link>
</div>
</div>
</header>
);
}
export default function Home(): ReactNode {
export default function Home() {
const { siteConfig } = useDocusaurusContext();
return (
<Layout
title={`${siteConfig.title}`}
description="Video player for React Native."
>
<HomepageHeader />
<Hero />
<main>
<HomepageFeatures />
<Features />
<Enterprise />
</main>
</Layout>
);
+4
View File
@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 12h14M12 5l7 7-7 7" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 169 B

-3
View File
@@ -1,3 +0,0 @@
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="-60 -70 280 240">
<path fill="#96c9eb" d="M84.58 47a7.71 7.71 0 1 1 10.8 11L66.09 86.88a7.72 7.72 0 0 1-10.82 0L26.4 58.37a7.71 7.71 0 1 1 10.81-11L53.1 63.12l.16-55.47a7.72 7.72 0 0 1 15.43.13l-.15 55zM0 113.48.1 83.3a7.72 7.72 0 1 1 15.43.14l-.07 22q46 .09 91.91 0l.07-22.12a7.72 7.72 0 1 1 15.44.14l-.1 30h-.09a7.71 7.71 0 0 1-7.64 7.36q-53.73.1-107.38 0A7.7 7.7 0 0 1 0 113.48"/>
</svg>

Before

Width:  |  Height:  |  Size: 462 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="96.108" height="122.88" viewBox="-48.054 -61.44 192.216 245.76" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#96c9eb" d="M2.892 56.036h8.959V37.117c0-10.205 4.177-19.484 10.898-26.207v-.009C29.473 4.177 38.754 0 48.966 0 59.17 0 68.449 4.177 75.173 10.901l.01.009c6.721 6.723 10.898 16.002 10.898 26.207v18.919h7.136a2.9 2.9 0 0 1 2.892 2.891v61.062a2.9 2.9 0 0 1-2.892 2.891H2.892A2.9 2.9 0 0 1 0 119.989V58.927a2.9 2.9 0 0 1 2.892-2.891m23.379 0h45.387V36.911c0-6.24-2.554-11.917-6.662-16.03l-.005.004c-4.111-4.114-9.787-6.669-16.025-6.669-6.241 0-11.917 2.554-16.033 6.665-4.109 4.113-6.662 9.79-6.662 16.03zm22.878 33.412 4.581 21.139-12.557.053 3.685-21.423c-3.431-1.1-5.918-4.315-5.918-8.111a8.512 8.512 0 1 1 10.209 8.342"/>
</svg>

Before

Width:  |  Height:  |  Size: 817 B

+4
View File
@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>

After

Width:  |  Height:  |  Size: 783 B

+5
View File
@@ -0,0 +1,5 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z" />
<path d="M2 17l10 5 10-5M2 12l10 5 10-5" />
</svg>

After

Width:  |  Height:  |  Size: 172 B

@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
</svg>

After

Width:  |  Height:  |  Size: 132 B

+4
View File
@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
</svg>

After

Width:  |  Height:  |  Size: 144 B

@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 181 B

+5
View File
@@ -0,0 +1,5 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<rect x="2" y="3" width="20" height="14" rx="2" />
<path d="M8 21h8M12 17v4" stroke-linecap="round" />
</svg>

After

Width:  |  Height:  |  Size: 194 B

+4
View File
@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" stroke-linecap="round" />
</svg>

After

Width:  |  Height:  |  Size: 240 B

+6
View File
@@ -0,0 +1,6 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<circle cx="12" cy="12" r="2.5" />
<ellipse cx="12" cy="12" rx="10" ry="4" />
<ellipse cx="12" cy="12" rx="10" ry="4" transform="rotate(60 12 12)" />
<ellipse cx="12" cy="12" rx="10" ry="4" transform="rotate(120 12 12)" />
</svg>

After

Width:  |  Height:  |  Size: 323 B

+5
View File
@@ -0,0 +1,5 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 2l8 4v6c0 5.25-3.5 9.74-8 11-4.5-1.26-8-5.75-8-11V6l8-4z" />
<path d="M9 12l2 2 4-4" stroke-linecap="round" stroke-linejoin="round" />
</svg>

After

Width:  |  Height:  |  Size: 239 B

+4
View File
@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<rect x="2" y="4" width="20" height="16" rx="2" />
<path d="M6 14h4M6 18h8M14 14h4" stroke-linecap="round" />
</svg>

After

Width:  |  Height:  |  Size: 200 B

-10
View File
@@ -1,10 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="-8 -6 48 48">
<g transform="translate(.648 2.438)scale(.05696)">
<circle r="50.167" cy="237.628" cx="269.529" fill="#96c9eb"/>
<g stroke="#96c9eb" stroke-width="24" fill="none">
<path d="M269.53 135.628c67.356 0 129.928 9.665 177.107 25.907 56.844 19.57 91.794 49.233 91.794 76.093 0 27.99-37.04 59.503-98.083 79.728-46.15 15.29-106.88 23.272-170.818 23.272-65.554 0-127.63-7.492-174.3-23.44C36.184 297.006.62 265.085.62 237.628c0-26.642 33.37-56.076 89.415-75.616 47.355-16.51 111.472-26.384 179.486-26.384z"/>
<path d="M180.736 186.922c33.65-58.348 73.28-107.724 110.92-140.48C337.006 6.976 380.163-8.48 403.43 4.937c24.248 13.983 33.042 61.814 20.067 124.796-9.8 47.618-33.234 104.212-65.176 159.6-32.75 56.788-70.25 106.82-107.377 139.272-46.98 41.068-92.4 55.93-116.185 42.213-23.08-13.3-31.906-56.92-20.834-115.233 9.355-49.27 32.832-109.745 66.8-168.664z"/>
<path d="M180.82 289.482c-33.745-58.282-56.72-117.287-66.31-166.255-11.544-59-3.382-104.11 19.864-117.566 24.224-14.024 70.055 2.244 118.14 44.94 36.356 32.28 73.688 80.837 105.723 136.173 32.844 56.733 57.46 114.21 67.036 162.582 12.117 61.213 2.31 107.984-21.453 121.74-23.057 13.348-65.25-.784-110.24-39.5-38.013-32.71-78.682-83.253-112.76-142.115z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB