Skip to content

Improve Open SaaS (mobile + desktop) lighthouse page rank score #353

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 51 additions & 10 deletions template/app/src/landing-page/components/Clients.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,64 @@
import logo from '../../client/static/logo.webp';
import AstroLogo from "../logos/AstroLogo";
import OpenAILogo from "../logos/OpenAILogo";
import PrismaLogo from "../logos/PrismaLogo";
import SalesforceLogo from "../logos/SalesforceLogo";

export default function Clients() {
return (
<div className='mt-12 mx-auto max-w-7xl px-6 lg:px-8 flex flex-col items-between gap-y-6'>
<h2 className='mb-6 text-center font-semibold tracking-wide text-gray-500 dark:text-white'>
Built with / Used by:
<h2 className='mb-6 text-center font-semibold tracking-wide text-gray-500'>
Built and Ships with
</h2>

<div className='mx-auto grid max-w-lg grid-cols-2 items-center gap-x-8 gap-y-12 sm:max-w-xl md:grid-cols-4 sm:gap-x-10 sm:gap-y-14 lg:mx-0 lg:max-w-none'>
{
[<SalesforceLogo />, <PrismaLogo />, <AstroLogo />, <OpenAILogo />].map((logo, index) => (
<div key={index} className='flex justify-center col-span-1 max-h-12 w-full object-contain dark:opacity-80'>
{logo}
</div>
))
}

<img
className='col-span-1 max-h-12 w-full object-contain grayscale opacity-100'
src='https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/512px-React-icon.svg.png'
alt='React'
height={48}
/>

<img
className='col-span-1 max-h-12 w-full object-contain grayscale opacity-60 dark:filter dark:brightness-0 dark:invert'
src='https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Node.js_logo.svg/590px-Node.js_logo.svg.png'
alt='NodeJS'
height={48}
/>

<img
className='col-span-1 max-h-12 w-full object-contain grayscale opacity-80'
src={logo}
alt='Wasp'
height={48}
/>

<div className='flex justify-center col-span-1 max-h-12 w-full object-contain grayscale opacity-80'>
<PrismaLogo />
</div>

<img
className='col-span-1 max-h-12 w-full object-contain grayscale '
src='https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Tailwind_CSS_Logo.svg/512px-Tailwind_CSS_Logo.svg.png'
alt='Tailwind CSS'
height={48}
/>

<img
className='col-span-1 max-h-12 w-full object-contain grayscale opacity-80 dark:opacity-60 dark:filter dark:brightness-0 dark:invert'
src='https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/Stripe_Logo%2C_revised_2016.svg/512px-Stripe_Logo%2C_revised_2016.svg.png'
alt='Stripe'
height={48}
/>

<div className='flex justify-center col-span-1 w-full max-h-12 object-contain grayscale opacity-75'>
<AstroLogo />
</div>

<div className='flex justify-center col-span-1 w-full max-h-12 object-contain grayscale opacity-80'>
<OpenAILogo />
</div>

</div>
</div>
)
10 changes: 5 additions & 5 deletions template/app/src/landing-page/components/FAQ.tsx
Original file line number Diff line number Diff line change
@@ -7,20 +7,20 @@ interface FAQ {

export default function FAQ({ faqs }: { faqs: FAQ[] }) {
return (
<div className='mt-32 mx-auto max-w-2xl divide-y divide-gray-900/10 dark:divide-gray-200/10 px-6 pb-8 sm:pb-24 sm:pt-12 lg:max-w-7xl lg:px-8 lg:py-32'>
<div className='mt-32 mx-auto max-w-2xl divide-y divide-gray-900/10 px-6 pb-8 sm:pb-24 sm:pt-12 lg:max-w-7xl lg:px-8 lg:py-32'>
<h2 className='text-2xl font-bold leading-10 tracking-tight text-gray-900 dark:text-white'>
Frequently asked questions
</h2>
<dl className='mt-10 space-y-8 divide-y divide-gray-900/10'>
<dl className='mt-10 space-y-8 divide-y divide-gray-900/10 dark:divide-gray-100/10'>
{faqs.map((faq) => (
<div key={faq.id} className='pt-8 lg:grid lg:grid-cols-12 lg:gap-8'>
<dt className='text-base font-semibold leading-7 text-gray-900 lg:col-span-5 dark:text-white'>
{faq.question}
</dt>
<dd className='flex items-center justify-start gap-2 mt-4 lg:col-span-7 lg:mt-0'>
<p className='text-base leading-7 text-gray-600 dark:text-white'>{faq.answer}</p>
<dd className='mt-4 lg:col-span-7 lg:mt-0'>
<p className='text-base leading-7 text-gray-600 dark:text-gray-400'>{faq.answer}</p>
{faq.href && (
<a href={faq.href} className='text-base leading-7 text-yellow-500 hover:text-yellow-600'>
<a href={faq.href} className='mt-4 text-base leading-7 text-yellow-500 hover:text-yellow-600'>
Learn more →
</a>
)}
28 changes: 15 additions & 13 deletions template/app/src/landing-page/components/Features.tsx
Original file line number Diff line number Diff line change
@@ -10,25 +10,27 @@ export default function Features({ features }: { features: Feature[] }) {
<div id='features' className='mx-auto mt-48 max-w-7xl px-6 lg:px-8'>
<div className='mx-auto max-w-2xl text-center'>
<p className='mt-2 text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl dark:text-white'>
The <span className='text-yellow-500'>Best</span> Features
<span className='text-yellow-500'>100%</span> Open-Source
</p>
<p className='mt-6 text-lg leading-8 text-gray-600 dark:text-white'>
Don't work harder.
<br /> Work smarter.
<p className='mt-6 text-lg leading-8 text-gray-600 dark:text-gray-400'>
No vendor lock-in.
<br /> Deploy anywhere.
</p>
</div>
<div className='mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-4xl'>
<dl className='grid max-w-xl grid-cols-1 gap-x-8 gap-y-10 lg:max-w-none lg:grid-cols-2 lg:gap-y-16'>
{features.map((feature) => (
<div key={feature.name} className='relative pl-16'>
<dt className='text-base font-semibold leading-7 text-gray-900 dark:text-white'>
<div className='absolute left-0 top-0 flex h-10 w-10 items-center justify-center border border-yellow-400 bg-yellow-100/50 dark:bg-boxdark rounded-lg'>
<div className='text-2xl'>{feature.icon}</div>
</div>
{feature.name}
</dt>
<dd className='mt-2 text-base leading-7 text-gray-600 dark:text-white'>{feature.description}</dd>
</div>
<a href={feature.href} className='group'>
<div key={feature.name} className='relative pl-16'>
<dt className='text-base font-semibold leading-7 text-gray-900 dark:text-white group-hover:underline'>
<div className='absolute left-0 top-0 flex h-10 w-10 items-center justify-center border border-yellow-400 bg-yellow-100/50 dark:bg-boxdark rounded-lg group-hover:border-yellow-500'>
<div className='text-2xl group-hover:opacity-80 '>{feature.icon}</div>
</div>
{feature.name}
</dt>
<dd className='mt-2 text-base leading-7 text-gray-600 dark:text-gray-400'>{feature.description}</dd>
</div>
</a>
))}
</dl>
</div>
2 changes: 1 addition & 1 deletion template/app/src/landing-page/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ export default function Footer({ footerNavigation }: {
<div className='mx-auto mt-6 max-w-7xl px-6 lg:px-8 dark:bg-boxdark-2'>
<footer
aria-labelledby='footer-heading'
className='relative border-t border-gray-900/10 dark:border-gray-200/10 py-24 sm:mt-32'
className='relative border-t border-gray-900/10 dark:border-gray-100/10 py-24 sm:mt-32'
>
<h2 id='footer-heading' className='sr-only'>
Footer
67 changes: 49 additions & 18 deletions template/app/src/landing-page/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,70 @@
import openSaasBannerWebp from '../../client/static/open-saas-banner.webp';
import { DocsUrl } from '../../shared/common';
import { useState, useEffect } from 'react';
import { AiFillGithub } from 'react-icons/ai';
import { DocsUrl, GithubUrl } from '../../shared/common';

export default function Hero() {
const [repoInfo, setRepoInfo] = useState<null | any>(null);

useEffect(() => {
const fetchRepoInfo = async () => {
try {
const response = await fetch(
'https://api.github.com/repos/wasp-lang/open-saas'
);
const data = await response.json();
setRepoInfo(data);
} catch (error) {
console.error('Error fetching repo info', error);
}
};
fetchRepoInfo();
}, []);

return (
<div className='relative pt-14 w-full'>
<TopGradient />
<BottomGradient />
<div className='py-24 sm:py-32'>
<div className='mx-auto max-w-8xl px-6 lg:px-8'>
<div className='lg:mb-18 mx-auto max-w-3xl text-center'>
<h1 className='text-4xl font-bold text-gray-900 sm:text-6xl dark:text-white'>
Some <span className='italic'>cool</span> words about your product
<h1 className='text-4xl font-bold text-gray-900 sm:text-6xl dark:text-white tracking-tight'>
The <span className='italic'>free</span> SaaS template with
superpowers
</h1>
<p className='mt-6 mx-auto max-w-2xl text-lg leading-8 text-gray-600 dark:text-white'>
With some more exciting words about your product!
<p className='mt-6 mx-auto max-w-2xl text-lg leading-8 text-gray-600 dark:text-gray-400'>
An open-source, feature-rich, full-stack React + NodeJS
template that manages features for you.
</p>
<div className='mt-10 flex items-center justify-center gap-x-6'>
<a
href={DocsUrl}
className='rounded-md px-3.5 py-2.5 text-sm font-semibold text-gray-700 ring-1 ring-inset ring-gray-200 hover:ring-2 hover:ring-yellow-300 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 dark:text-white'
className='rounded-md px-6 py-4 text-sm font-semibold text-gray-700 ring-1 ring-inset ring-gray-200 hover:ring-2 hover:ring-yellow-300 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 dark:text-white'
>
Get Started <span aria-hidden='true'>→</span>
</a>
<a
href={GithubUrl}
className='group relative flex items-center justify-center rounded-md bg-gray-100 px-6 py-4 text-sm font-semibold shadow-sm ring-1 ring-inset ring-gray-200 dark:bg-gray-700 hover:ring-2 hover:ring-yellow-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
>
View the Repo
{repoInfo!! && (
<>
<span className='absolute -top-3 -right-7 inline-flex items-center gap-x-1 rounded-full ring-1 group-hover:ring-2 ring-inset ring-yellow-300 bg-yellow-100 px-2 py-1 text-sm font-medium text-yellow-800'>
<AiFillGithub size='1rem' />
{repoInfo.stargazers_count}
</span>
</>
)}
</a>
</div>
</div>
<div className='mt-14 flow-root sm:mt-14'>
<div className='-m-2 flex justify-center rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
<img
src={openSaasBannerWebp}
alt='App screenshot'
width={1000}
height={530}
loading='lazy'
className='rounded-md shadow-2xl ring-1 ring-gray-900/10'
/>
<div className='mt-14 flow-root sm:mt-14 '>
<div className='-m-2 mx-auto rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
<iframe
className=' mx-auto w-full md:w-[85%] aspect-[4/3] shadow-2xl'
src='https://cards.producthunt.com/cards/posts/436467?v=1'
allowFullScreen
></iframe>
</div>
</div>
</div>
@@ -72,4 +103,4 @@ function BottomGradient() {
/>
</div>
);
}
}
121 changes: 89 additions & 32 deletions template/app/src/landing-page/contentSections.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,132 @@
import type { NavigationItem } from '../client/components/NavBar/NavBar';
import { routes } from 'wasp/client/router';
import { DocsUrl, BlogUrl } from '../shared/common';
import daBoiAvatar from '../client/static/da-boi.webp';
import avatarPlaceholder from '../client/static/avatar-placeholder.webp';
import { DocsUrl, BlogUrl, GithubUrl } from '../shared/common';

export const landingPageNavigationItems: NavigationItem[] = [
{ name: 'Features', to: '#features' },
{ name: 'Pricing', to: routes.PricingPageRoute.to },
{ name: 'Documentation', to: DocsUrl },
{ name: 'Blog', to: BlogUrl },
];
export const features = [
{
name: 'Cool Feature #1',
description: 'Describe your cool feature here.',
name: 'Open-Source Philosophy',
description:
'The repo and framework are 100% open-source, and so are the services wherever possible. Still missing something? Contribute!',
icon: '🤝',
href: DocsUrl,
},
{
name: 'Cool Feature #2',
description: 'Describe your cool feature here.',
name: 'DIY Auth, Done For You',
description: 'Pre-configured full-stack Auth that you own. No 3rd-party services or hidden fees.',
icon: '🔐',
href: DocsUrl,
href: DocsUrl + '/guides/authentication/',
},
{
name: 'Cool Feature #3',
description: 'Describe your cool feature here.',
name: 'Full-stack Type Safety',
description:
'Full support for TypeScript with auto-generated types that span the whole stack. Nothing to configure!',
icon: '🥞',
href: DocsUrl,
},
{
name: 'Cool Feature #4',
description: 'Describe your cool feature here.',
name: 'Stripe / Lemon Squeezy Integration',
description: "No SaaS is complete without payments. We've pre-configured checkout and webhooks. Just choose a provider and start cashing out.",
icon: '💸',
href: DocsUrl + '/guides/payments-integration/',
},
{
name: 'Admin Dashboard',
description: 'Graphs! Tables! Analytics w/ Plausible or Google! All in one place. Ooooooooooh.',
icon: '📈',
href: DocsUrl + '/general/admin-dashboard/',
},
{
name: 'Email Sending',
description:
'Email sending built-in. Combine it with the cron jobs feature to easily send emails to your customers.',
icon: '📧',
href: DocsUrl + '/guides/email-sending/',
},
{
name: 'OpenAI API Implemented',
description: 'Have a sweet AI-powered app concept? Get your idea shipped to potential customers in days!',
icon: '🤖',
href: DocsUrl,
},
{
name: 'File Uploads with AWS',
description: 'File upload examples with AWS S3 presigned URLs are included and fully documented!',
icon: '📁',
href: DocsUrl + '/guides/file-uploading/',
},
{
name: 'Blog w/ Astro',
description:
'Built-in blog with the Astro framework. Write your posts in Markdown, and watch your SEO performance take off.',
icon: '📝',
href: DocsUrl + '/start/guided-tour/',
},
{
name: 'Deploy Anywhere. Easily.',
description:
'No vendor lock-in because you own all your code. Deploy yourself, or let Wasp deploy it for you with a single command.',
icon: '🚀 ',
href: DocsUrl + '/guides/deploying/',
},
{
name: 'E2E Tests w/ Playwright',
description: 'Move fast without breaking too much. Tests and a CI pipeline w/ GitHub Actions are set up for you.',
icon: '🧪',
href: DocsUrl + '/guides/tests/',
},
{
name: 'Complete Documentation & Support',
description: "We don't leave you hanging. We have detailed docs and a Discord community to help!",
icon: '🫂',
href: DocsUrl,
},
];
export const testimonials = [
{
name: 'Da Boi',
role: 'Wasp Mascot',
avatarSrc: daBoiAvatar,
socialUrl: 'https://twitter.com/wasplang',
quote: "I don't even know how to code. I'm just a plushie.",
name: 'Max Khamrovskyi',
role: 'Senior Eng @ Red Hat',
avatarSrc: 'https://pbs.twimg.com/profile_images/1719397191205179392/V_QrGPSO_400x400.jpg',
socialUrl: 'https://twitter.com/maksim36ua',
quote: 'I used Wasp to build and sell my AI-augmented SaaS app for marketplace vendors within two months!',
},
{
name: 'Mr. Foobar',
role: 'Founder @ Cool Startup',
avatarSrc: avatarPlaceholder,
socialUrl: '',
quote: 'This product makes me cooler than I already am.',
name: 'Tim Skaggs',
role: 'Founder @ Antler US',
avatarSrc: 'https://pbs.twimg.com/profile_images/1802196804236091392/ZG0OE_fO_400x400.jpg',
socialUrl: 'https://twitter.com/tskaggs',
quote: 'Nearly done with a MVP in 3 days of part-time work... and deployed on Fly.io in 10 minutes.',
},
{
name: 'Jamie',
role: 'Happy Customer',
avatarSrc: avatarPlaceholder,
socialUrl: '#',
quote: 'My cats love it!',
name: 'Jonathan Cocharan',
role: 'Entrepreneur',
avatarSrc: 'https://pbs.twimg.com/profile_images/926142421653753857/o6Hmcbr7_400x400.jpg',
socialUrl: 'https://twitter.com/jonathancocharan',
quote:
'In just 6 nights... my SaaS app is live 🎉! Huge thanks to the amazing @wasplang community 🙌 for their guidance along the way. These tools are incredibly efficient 🤯!',
},
];

export const faqs = [
{
id: 1,
question: 'Whats the meaning of life?',
answer: '42.',
href: 'https://en.wikipedia.org/wiki/42_(number)',
question: 'Why is this SaaS Template free and open-source?',
answer:
'We believe the best product is made when the community puts their heads together. We also believe a quality starting point for a web app should be free and available to everyone. Our hope is that together we can create the best SaaS template out there and bring our ideas to customers quickly.',
},
{
id: 2,
question: "What's Wasp?",
href: 'https://wasp-lang.dev',
answer: "It's the fastest way to develop full-stack React + NodeJS + Prisma apps and it's what gives this template superpowers. Wasp relies on React, NodeJS, and Prisma to define web components and server queries and actions. Wasp's secret sauce is its compiler which takes the client, server code, and config file and outputs the client app, server app and deployment code, supercharging the development experience. Combined with this template, you can build a SaaS app in record time.",
},
];
export const footerNavigation = {
app: [
{ name: 'Github', href: GithubUrl },
{ name: 'Documentation', href: DocsUrl },
{ name: 'Blog', href: BlogUrl },
],