Skip to content

Commit 610cb1e

Browse files
feat : new-landing-page (#1203)
* Remove animation and refactor the page * Align vertically the content * Replace loattie * Add benchmarks animation to deploy at scale card * Update Deploy at scale href to add benchmarks * Remove sections from homepage * New wordings * Align mobile version to desktop * Add loattie performance for mobile version * Add Datadog-like product dropdowns * Change benchmarkes charts by performance loattie * Nits * Nits * Comment Video section * Improve wordings of Product section * Improve organization * Add Core principles section * Fix loatie * Fix case study button * Remove accordion * Add animation * Add animation in Develop and Iterate with instand feedback * Fix title font * Comment animation for now * Improve wordings * Improve animations * Improve wordings * Add video for first card * Improve product video section * Nits * Remove arrows * Align mobile section to desktop * Add landing videos * Add subtitles in each Build tabs * Improve core principles section * nits * Fix testimonial cards on mobile version * Improve wordings * Fix lottie animation * Add autoplay to all animations in core concepts * Change videos * Better wording * Better wording * Align mobile version * Nits * Add new video * Remove dead code * Align wordings * Improve UX of videos * Fix * Improve wordings * Improve wording * Nits * Fix link * Fix too much motion loatties * Replace mp4 by webm * Change monitoring video to webm * Remove glitches for tab videos * Improve wording * Temporary commit * Improve videos * Improve videos * Improve videos and remove data video * Add local dev in Build section * Change landing structure * Improve wordings * Nits * Improve title sizes * Add Youtube videos * Compress App video * The nittest of the nits * Hide Products dropdown while I improve the section * update * update --------- Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
1 parent 3778460 commit 610cb1e

24 files changed

+2596
-515
lines changed

src/css/custom.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,4 +596,19 @@ div.testimonials a:hover .wm-name {
596596

597597
video:fullscreen {
598598
object-fit: contain !important;
599+
}
600+
601+
/* Improve readability of BenchmarkVisualization chart text */
602+
.benchmark-chart-container canvas {
603+
filter: brightness(1.2) contrast(1.1);
604+
}
605+
606+
.benchmark-chart-container[data-theme="dark"] {
607+
--chart-text-color: #ffffff;
608+
--chart-axis-color: #e5e7eb;
609+
}
610+
611+
.benchmark-chart-container[data-theme="light"] {
612+
--chart-text-color: #1f2937;
613+
--chart-axis-color: #4b5563;
599614
}

src/landing/AppAnimation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export default function AppAnimation({ active, only }) {
131131
variant5: {
132132
top: 360,
133133
left: 300,
134-
text: 'Build complex apps with our 50+ atomic components, and add code only when needed.',
134+
//text: 'Build complex apps with our 50+ atomic components, and add code only when needed.',
135135
displayArrow: false
136136
}
137137
};

src/landing/CallToAction.jsx

Lines changed: 32 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,36 @@
11
import React, { useState } from 'react';
2-
import { useColorMode } from '@docusaurus/theme-common';
2+
import { ArrowRight } from 'lucide-react';
33
import BookDemoModal from '../components/BookDemoModal';
44

5-
export default function CallToAction({ color }) {
6-
const { colorMode } = useColorMode();
7-
const [bookDemoOpen, setBookDemoOpen] = useState(false);
5+
export default function CallToAction() {
6+
const [bookDemoOpen, setBookDemoOpen] = useState(false);
87

9-
const colorMap = {
10-
blue: {
11-
light: '#bfdbfe',
12-
dark: '#3b82f6'
13-
},
14-
orange: {
15-
light: '#fb923c',
16-
dark: '#7c2d12'
17-
},
18-
green: {
19-
light: '#bbf7d0',
20-
dark: '#10b981'
21-
}
22-
};
23-
24-
const c = colorMap[color ?? 'blue'][colorMode];
25-
26-
return (
27-
<div className="w-full py-16">
28-
<div className="">
29-
<div className="relative isolate overflow-hidden bg-gray-900 dark:bg-white px-6 py-24 text-center rounded-3xl sm:px-16">
30-
<h2 className="mx-auto max-w-4xl text-4xl font-bold tracking-tight text-white dark:text-gray-900">
31-
Build endpoints, workflows & ETLs, UIs with code only where it matters
32-
</h2>
33-
<p className="mx-auto mt-6 max-w-4xl text-lg leading-8 text-gray-300 dark:text-gray-600">
34-
Get started building your internal tool in under 10 minutes
35-
</p>
36-
<div className="mt-10 flex items-center justify-center gap-x-6">
37-
<a
38-
href="https://app.windmill.dev/user/login"
39-
onClick={() => window.plausible('try-cloud')}
40-
data-analytics='"try-cloud"'
41-
className="rounded-md bg-white px-3.5 py-1.5 text-base hover:text-blue-500 font-semibold leading-7 text-gray-900 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white !no-underline"
42-
rel="nofollow"
43-
>
44-
Get started for free
45-
</a>
46-
<button
47-
onClick={() => setBookDemoOpen(true)}
48-
className="text-base font-semibold leading-7 text-white !no-underline dark:text-gray-900 cursor-pointer bg-transparent border-none"
49-
>
50-
Schedule a demo <span aria-hidden="true"></span>
51-
</button>
52-
</div>
53-
<svg
54-
xmlns="http://www.w3.org/2000/svg"
55-
viewBox="0 0 1024 1024"
56-
className="absolute top-1/2 left-1/2 -z-10 h-[64rem] w-[64rem] -translate-x-1/2"
57-
aria-hidden="true"
58-
>
59-
<circle
60-
cx={512}
61-
cy={512}
62-
r={512}
63-
fill="url(#827591b1-ce8c-4110-b064-7cb85a0b12171)"
64-
fillOpacity="0.7"
65-
/>
66-
<defs>
67-
<radialGradient
68-
id="827591b1-ce8c-4110-b064-7cb85a0b12171"
69-
cx={0}
70-
cy={0}
71-
r={1}
72-
gradientUnits="userSpaceOnUse"
73-
gradientTransform="translate(512 512) rotate(90) scale(512)"
74-
>
75-
<stop stopColor={c} />
76-
<stop offset={1} stopColor={c} stopOpacity={0} />
77-
</radialGradient>
78-
</defs>
79-
</svg>
80-
</div>
81-
</div>
82-
<BookDemoModal open={bookDemoOpen} setOpen={setBookDemoOpen} />
83-
</div>
84-
);
85-
}
8+
return (
9+
<div className="w-full">
10+
<div className="dark:bg-gray-900 bg-gray-50 px-8 sm:px-12 py-10 text-center rounded-xl">
11+
<h2 className="mx-auto max-w-4xl text-3xl font-bold tracking-tight text-transparent bg-clip-text bg-gradient-to-br from-blue-500 to-blue-700 dark:from-blue-400 dark:to-blue-600 sm:text-4xl">
12+
Start building with Windmill
13+
</h2>
14+
<div className="mt-6 flex flex-col sm:flex-row items-center justify-center gap-4">
15+
<a
16+
href="https://app.windmill.dev/user/login"
17+
onClick={() => window.plausible('try-cloud')}
18+
data-analytics='"try-cloud"'
19+
className="rounded-lg bg-blue-600 hover:bg-blue-700 px-6 py-3 text-base font-semibold text-white shadow-sm transition-colors !no-underline"
20+
rel="nofollow"
21+
>
22+
Get started for free
23+
</a>
24+
<button
25+
onClick={() => setBookDemoOpen(true)}
26+
className="group flex items-center gap-2 text-base font-semibold text-gray-900 dark:text-white bg-transparent border-none cursor-pointer hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
27+
>
28+
Schedule a demo
29+
<ArrowRight size={20} className="group-hover:translate-x-1 transition-transform" />
30+
</button>
31+
</div>
32+
</div>
33+
<BookDemoModal open={bookDemoOpen} setOpen={setBookDemoOpen} />
34+
</div>
35+
);
36+
}

src/landing/CombinedAnimation.tsx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { motion, AnimatePresence } from 'framer-motion';
3+
4+
export default function CombinedAnimation() {
5+
const [currentIndex, setCurrentIndex] = useState(0);
6+
7+
const images = [
8+
{
9+
id: 'script',
10+
src: '/illustrations/animscripts.png',
11+
alt: 'VS Code editor with test panel and execution results',
12+
label: 'Scripts',
13+
subtitle: 'Write scripts in 20+ languages (Python, TS, Go, Bash...)'
14+
},
15+
{
16+
id: 'flow',
17+
src: '/illustrations/animflows.png',
18+
alt: 'Flow diagram with execution logs and performance metrics',
19+
label: 'Flows',
20+
subtitle: 'Orchestrate scripts into scalable workflows'
21+
},
22+
{
23+
id: 'app',
24+
src: '/illustrations/animapps.png',
25+
alt: 'App builder with button, table, and code editor',
26+
label: 'Apps',
27+
subtitle: 'Generate production-ready frontends with AI'
28+
}
29+
];
30+
31+
// Auto-switch every 4 seconds
32+
useEffect(() => {
33+
const interval = setInterval(() => {
34+
setDirection(1); // Always forward for auto-advance
35+
setCurrentIndex((prev) => (prev + 1) % images.length);
36+
}, 4000);
37+
38+
return () => clearInterval(interval);
39+
}, [images.length]);
40+
41+
const slideVariants = {
42+
enter: (direction: number) => ({
43+
x: direction > 0 ? '100%' : '-100%',
44+
opacity: 0
45+
}),
46+
center: {
47+
x: 0,
48+
opacity: 1
49+
},
50+
exit: (direction: number) => ({
51+
x: direction > 0 ? '-100%' : '100%',
52+
opacity: 0
53+
})
54+
};
55+
56+
const [direction, setDirection] = useState(0);
57+
58+
const goToSlide = (index: number) => {
59+
const newDirection = index > currentIndex ? 1 : -1;
60+
setDirection(newDirection);
61+
setCurrentIndex(index);
62+
};
63+
64+
return (
65+
<div className="relative w-full overflow-hidden rounded-lg px-0">
66+
{/* Title and Subtitle - Hidden on mobile, visible on desktop */}
67+
<div className="hidden md:block text-center">
68+
<AnimatePresence mode="wait">
69+
<motion.div
70+
key={currentIndex}
71+
initial={{ opacity: 0, y: -10 }}
72+
animate={{ opacity: 1, y: 0 }}
73+
exit={{ opacity: 0, y: 10 }}
74+
transition={{ duration: 0.3 }}
75+
>
76+
<div className="text-lg md:text-xl font-semibold text-gray-900 dark:text-white">
77+
{images[currentIndex].label}
78+
</div>
79+
<div className="text-sm md:text-base text-gray-600 dark:text-gray-400 mt-1">
80+
{images[currentIndex].subtitle}
81+
</div>
82+
</motion.div>
83+
</AnimatePresence>
84+
</div>
85+
{/* Image container */}
86+
<div className="relative w-full min-h-[250px] md:min-h-[400px]">
87+
<AnimatePresence mode="wait" custom={direction}>
88+
<motion.div
89+
key={currentIndex}
90+
custom={direction}
91+
variants={slideVariants}
92+
initial="enter"
93+
animate="center"
94+
exit="exit"
95+
transition={{
96+
x: { type: 'spring', stiffness: 300, damping: 30 },
97+
opacity: { duration: 0.2 }
98+
}}
99+
className="absolute inset-0 w-full flex items-center justify-center"
100+
>
101+
<img
102+
src={images[currentIndex].src}
103+
alt={images[currentIndex].alt}
104+
className="w-full h-auto max-w-full object-contain rounded-lg"
105+
loading="lazy"
106+
onError={(e) => {
107+
console.error('Failed to load image:', images[currentIndex].src);
108+
(e.target as HTMLImageElement).style.display = 'none';
109+
}}
110+
/>
111+
</motion.div>
112+
</AnimatePresence>
113+
</div>
114+
115+
{/* Navigation dots */}
116+
<div className="absolute bottom-2 md:bottom-4 left-1/2 transform -translate-x-1/2 flex gap-2 z-10">
117+
{images.map((_, index) => (
118+
<button
119+
key={index}
120+
onClick={() => goToSlide(index)}
121+
className={`h-1.5 md:h-2 rounded-full transition-all ${
122+
index === currentIndex
123+
? 'w-6 md:w-8 bg-white dark:bg-gray-200'
124+
: 'w-1.5 md:w-2 bg-white/50 dark:bg-gray-500/50 hover:bg-white/75 dark:hover:bg-gray-400/75'
125+
}`}
126+
aria-label={`Go to ${images[index].label}`}
127+
/>
128+
))}
129+
</div>
130+
</div>
131+
);
132+
}
133+

src/landing/CorePrinciple.tsx

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React from 'react';
2+
import LandingSection from './LandingSection';
3+
import { ArrowRight } from 'lucide-react';
4+
import BrowserOnly from '@docusaurus/BrowserOnly';
5+
// @ts-ignore
6+
import polyGlott from '/illustrations/polyglot.json';
7+
// @ts-ignore
8+
import secrets from '/illustrations/secrets.json';
9+
// @ts-ignore
10+
import thirdparty from '/illustrations/thirdparty.json';
11+
12+
// Client-only component that renders a static frame of a Lottie animation
13+
function StaticLottie({ animationData }: { animationData: unknown }) {
14+
const { useLottie } = require('lottie-react');
15+
const { View, goToAndStop, getDuration } = useLottie({
16+
animationData,
17+
loop: false,
18+
autoplay: false
19+
});
20+
21+
React.useEffect(() => {
22+
// Go to 90% of the animation
23+
const totalFrames = getDuration(true);
24+
if (totalFrames > 0) {
25+
goToAndStop(Math.floor(totalFrames * 0.90), true);
26+
}
27+
}, [goToAndStop, getDuration]);
28+
29+
return View;
30+
}
31+
32+
interface FeatureCardProps {
33+
title: string;
34+
description: string;
35+
actionLink: string;
36+
actionUrl?: string;
37+
lottieData?: unknown;
38+
}
39+
40+
function FeatureCard({ title, description, actionLink, actionUrl, lottieData }: FeatureCardProps) {
41+
return (
42+
<div className="flex flex-col h-full rounded-lg bg-gray-50 dark:bg-gray-800/50 backdrop-blur-sm p-6 shadow-lg border border-gray-200 dark:border-gray-700/50 group">
43+
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-3">{title}</h3>
44+
<p className="text-gray-600 dark:text-gray-300 mb-4 flex-grow text-base leading-relaxed">
45+
{description}
46+
</p>
47+
<a
48+
href={actionUrl || '#'}
49+
className="text-sm text-blue-600 dark:text-blue-400 flex flex-row items-center gap-2 group-hover:ml-2 transition-all mb-4 hover:text-blue-700 dark:hover:text-blue-300"
50+
target={actionUrl && actionUrl.startsWith('http') ? '_blank' : undefined}
51+
rel={actionUrl && actionUrl.startsWith('http') ? 'noopener noreferrer' : undefined}
52+
>
53+
{actionLink}
54+
<ArrowRight size={24} />
55+
</a>
56+
{lottieData && (
57+
<div className="mt-auto bg-gray-100 dark:bg-gray-700/80 rounded-md p-4 min-h-[180px] flex items-center justify-center overflow-hidden">
58+
<div className="w-full h-full flex items-center justify-center rounded-md">
59+
<BrowserOnly fallback={<div className="w-full h-full" />}>
60+
{() => <StaticLottie animationData={lottieData} />}
61+
</BrowserOnly>
62+
</div>
63+
</div>
64+
)}
65+
</div>
66+
);
67+
}
68+
69+
export default function CorePrinciple() {
70+
return (
71+
<LandingSection bgClass="py-16">
72+
<div className="w-full">
73+
<div className="mb-12 text-left">
74+
<h1 className="tracking-tight leading-tight text-left font-bold text-transparent bg-clip-text bg-gradient-to-br from-blue-500 to-blue-700 dark:from-blue-400 dark:to-blue-600">
75+
Our core principles
76+
</h1>
77+
<span className="text-lg text-gray-700 max-w-3xl dark:text-gray-200">
78+
The foundational beliefs that guide how we build Windmill.
79+
</span>
80+
</div>
81+
82+
{/* 3 cards in a row */}
83+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
84+
<FeatureCard
85+
title="Avoid vendor lock-in"
86+
description="Open source and self-hostable. Your code, your data, your infrastructure. Deploy anywhere—cloud, on-premises, or air-gapped."
87+
actionLink="View on GitHub"
88+
actionUrl="https://github.com/windmill-labs/"
89+
lottieData={polyGlott}
90+
/>
91+
<FeatureCard
92+
title="Seamless enterprise integration"
93+
description="Connect to databases (PostgreSQL, MySQL, Snowflake), cloud platforms (AWS, Azure, GCP), message queues (Kafka, SQS, NATS), and 100+ APIs."
94+
actionLink="Explore integrations"
95+
actionUrl="https://www.windmill.dev/docs/integrations/integrations_on_windmill"
96+
lottieData={thirdparty}
97+
/>
98+
<FeatureCard
99+
title="Enterprise-grade security"
100+
description="Granular RBAC, SSO, Secret Management, and comprehensive Audit Logs. Deploy in regulated industries with air-gapped support."
101+
actionLink="Explore enterprise features"
102+
actionUrl="https://www.windmill.dev/docs/misc/enterprise_onboarding"
103+
lottieData={secrets}
104+
/>
105+
</div>
106+
</div>
107+
</LandingSection>
108+
);
109+
}

0 commit comments

Comments
 (0)