remixed v0

This commit is contained in:
m5r
2022-05-14 12:22:06 +02:00
parent 9275d4499b
commit 98b89ae0f7
338 changed files with 22549 additions and 44628 deletions

View File

@ -0,0 +1,20 @@
import type { FunctionComponent, PropsWithChildren } from "react";
import Header from "./header";
import Footer from "./footer";
const BaseLayout: FunctionComponent<PropsWithChildren<{}>> = ({ children }) => (
<>
<section className="font-inter antialiased bg-white text-gray-900 tracking-tight">
<section className="flex flex-col min-h-screen overflow-hidden">
<Header />
<main className="flex-grow">{children}</main>
<Footer />
</section>
</section>
</>
);
export default BaseLayout;

View File

@ -0,0 +1,31 @@
import { useState } from "react";
export default function CTAForm() {
const [{ isSubmitted }, setState] = useState({ isSubmitted: false });
const onSubmit = () => setState({ isSubmitted: true });
return (
<form onSubmit={onSubmit}>
{isSubmitted ? (
<p className="text-center md:text-left mt-2 opacity-75 text-green-900 text-md">
You&#39;re on the list! We will be in touch soon
</p>
) : (
<div className="flex flex-col sm:flex-row justify-center w-full md:max-w-md md:mx-0">
<input
name="email"
type="email"
className="form-input w-full mb-2 sm:mb-0 sm:mr-2 focus:outline-none focus:ring-rebeccapurple-500 focus:border-rebeccapurple-500"
placeholder="Enter your email address"
/>
<button
type="submit"
className="btn text-white bg-rebeccapurple-500 hover:bg-rebeccapurple-400 flex-shrink-0"
>
Request access
</button>
</div>
)}
</form>
);
}

View File

@ -0,0 +1,86 @@
import type { FunctionComponent, PropsWithChildren } from "react";
import { Disclosure, Transition } from "@headlessui/react";
import clsx from "clsx";
export default function FAQs() {
return (
<section className="max-w-6xl mx-auto px-4 sm:px-6">
<div className="py-12 md:py-20">
<div className="max-w-3xl mx-auto text-center pb-20">
<h2 className="h2 font-mackinac">Questions & Answers</h2>
</div>
<ul className="max-w-3xl mx-auto pl-12">
<Accordion title="How does it work?">
Shellphone is your go-to app to use your phone number over the internet. It integrates
seamlessly with Twilio to provide the best experience for your personal cloud phone.
</Accordion>
<Accordion title="What do I need to use Shellphone?">
Shellphone is still in its early stages and we&#39;re working hard to make it as easy-to-use as
possible. Currently, you must have a Twilio account to set up your personal cloud phone with
Shellphone.
</Accordion>
<Accordion title="Why would I use this over an eSIM?">
Chances are you&#39;re currently using an eSIM-compatible device. eSIMs are a reasonable way of
using a phone number internationally but they are still subject to some irky limitations. For
example, you can only use an eSIM on one device at a time and you are still subject to
exorbitant rates from your carrier.
</Accordion>
<span className="block border-t border-gray-200" aria-hidden="true" />
</ul>
</div>
</section>
);
}
const Accordion: FunctionComponent<PropsWithChildren<{ title: string }>> = ({ title, children }) => {
return (
<Disclosure>
{({ open }) => (
<>
<Disclosure.Button className="flex items-center w-full text-lg font-medium text-left py-5 border-t border-gray-200">
<svg
className="w-4 h-4 fill-current text-rebeccapurple-500 flex-shrink-0 mr-8 -ml-12"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<rect
y="7"
width="16"
height="2"
rx="1"
className={clsx("transform origin-center transition duration-200 ease-out", {
"rotate-180": open,
})}
/>
<rect
y="7"
width="16"
height="2"
rx="1"
className={clsx("transform origin-center transition duration-200 ease-out", {
"rotate-90": !open,
"rotate-180": open,
})}
/>
</svg>
<span>{title}</span>
</Disclosure.Button>
<Transition
enter="transition duration-300 ease-in-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Disclosure.Panel className="text-gray-600 overflow-hidden">
<p className="pb-5">{children}</p>
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
);
};

View File

@ -0,0 +1,40 @@
import type { FunctionComponent } from "react";
import { Link, type LinkProps } from "@remix-run/react";
export default function Footer() {
// TODO
const isDisabled = true;
if (isDisabled) {
return null;
}
return (
<footer className="bg-white">
<div className="max-w-7xl mx-auto py-12 px-4 overflow-hidden sm:px-6 lg:px-8">
<nav className="-mx-5 -my-2 flex flex-wrap justify-center" aria-label="Footer">
<FooterLink to="/blog" name="Blog" />
<FooterLink to="/privacy" name="Privacy Policy" />
<FooterLink to="/terms" name="Terms of Service" />
<FooterLink to="mailto:support@shellphone.app" name="Email Us" />
</nav>
<p className="mt-8 text-center text-base text-gray-400">
&copy; 2021 Capsule Corp. Dev Pte. Ltd. All rights reserved.
{/*&copy; 2021 Mokhtar Mial All rights reserved.*/}
</p>
</div>
</footer>
);
}
type Props = {
to: LinkProps["to"];
name: string;
};
const FooterLink: FunctionComponent<Props> = ({ to, name }) => (
<div className="px-5 py-2">
<Link to={to} className="text-base text-gray-500 hover:text-gray-900">
{name}
</Link>
</div>
);

View File

@ -0,0 +1,240 @@
import { Fragment, useState, useRef, useEffect } from "react";
import { Link, type LinkProps } from "@remix-run/react";
import { Dialog, Transition } from "@headlessui/react";
import { IoClose } from "react-icons/io5";
function Header() {
return (
<header className="absolute inset-x-0 top-0 z-10 w-full">
<div className="px-4 mx-auto sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16 lg:h-20">
<div className="hidden lg:flex lg:items-center lg:justify-center lg:ml-10 lg:mr-auto lg:space-x-10">
<a
href="#"
title=""
className="text-base text-black transition-all duration-200 hover:text-opacity-80"
>
{" "}
Features{" "}
</a>
<a
href="#"
title=""
className="text-base text-black transition-all duration-200 hover:text-opacity-80"
>
{" "}
Solutions{" "}
</a>
<a
href="#"
title=""
className="text-base text-black transition-all duration-200 hover:text-opacity-80"
>
{" "}
Resources{" "}
</a>
<a
href="#"
title=""
className="text-base text-black transition-all duration-200 hover:text-opacity-80"
>
{" "}
Pricing{" "}
</a>
</div>
</div>
</div>
</header>
);
}
function Headerold() {
return (
<header className="absolute w-full z-30 inset-x-0 top-0">
<div className="px-4 mx-auto sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-20">
<div className="flex-shrink-0 mr-5">
<Link to="/" className="block">
<img className="w-10 h-10" src="/shellphone.png" alt="Shellphone logo" />
</Link>
</div>
<nav className="hidden md:flex md:flex-grow">
<ul className="flex items-center justify-center ml-10 mr-auto space-x-10">
<li>
<DesktopNavLink to="/features" label="Features" />
</li>
<li>
<DesktopNavLink to="/roadmap" label="Roadmap" />
</li>
<li>
<DesktopNavLink to="/open" label="Open Metrics" />
</li>
<li>
<DesktopNavLink to="/pricing" label="Pricing" />
</li>
</ul>
</nav>
<MobileNav />
</div>
</div>
</header>
);
}
type NavLinkProps = {
to: LinkProps["to"];
label: string;
};
function DesktopNavLink({ to, label }: NavLinkProps) {
return (
<Link to={to} className="text-base text-gray-600 hover:text-gray-900 transition duration-150 ease-in-out">
{label}
</Link>
);
}
function MobileNav() {
const [mobileNavOpen, setMobileNavOpen] = useState(false);
const trigger = useRef<HTMLButtonElement>(null);
const mobileNav = useRef<HTMLDivElement>(null);
// close the mobile menu on click outside
useEffect(() => {
const clickHandler = ({ target }: MouseEvent) => {
if (!mobileNav.current || !trigger.current) {
return;
}
console.log(mobileNav.current.contains(target as Node));
if (
!mobileNavOpen ||
mobileNav.current.contains(target as Node) ||
trigger.current.contains(target as Node)
) {
return;
}
setMobileNavOpen(false);
};
document.addEventListener("click", clickHandler);
return () => document.removeEventListener("click", clickHandler);
});
// close the mobile menu if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }: KeyboardEvent) => {
if (!mobileNavOpen || keyCode !== 27) return;
setMobileNavOpen(false);
};
document.addEventListener("keydown", keyHandler);
return () => document.removeEventListener("keydown", keyHandler);
});
return (
<div className="inline-flex md:hidden">
<button
ref={trigger}
className={`hamburger ${mobileNavOpen && "active"}`}
aria-controls="mobile-nav"
aria-expanded={mobileNavOpen}
onClick={() => setMobileNavOpen(!mobileNavOpen)}
>
<span className="sr-only">Menu</span>
<svg
className="w-6 h-6 fill-current text-gray-900 hover:text-gray-900 transition duration-150 ease-in-out"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<rect y="4" width="24" height="2" rx="1" />
<rect y="11" width="24" height="2" rx="1" />
<rect y="18" width="24" height="2" rx="1" />
</svg>
</button>
<Transition.Root show={mobileNavOpen} as={Fragment}>
<Dialog as="div" className="fixed z-40 inset-0 overflow-hidden" onClose={setMobileNavOpen}>
<div className="absolute inset-0 overflow-hidden">
<Transition.Child
as={Fragment}
enter="ease-in-out duration-500"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in-out duration-500"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Overlay className="absolute inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>
<div className="fixed inset-y-0 right-0 pl-10 max-w-full flex">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-700"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<div ref={mobileNav} className="w-screen max-w-[16rem] sm:max-w-sm">
<div className="h-full flex flex-col py-6 bg-white shadow-xl overflow-y-scroll">
<div className="px-4 sm:px-6">
<div className="flex items-start justify-between">
<Dialog.Title className="text-lg font-medium text-gray-900">
Shellphone
</Dialog.Title>
<div className="ml-3 h-7 flex items-center">
<button
type="button"
className="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-rebeccapurple-500"
onClick={() => setMobileNavOpen(false)}
>
<span className="sr-only">Close panel</span>
<IoClose className="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
<div className="mt-6 relative flex-1 px-4 sm:px-6">
<div className="absolute inset-0 px-4 sm:px-6">
<ul className="space-y-4">
<li>
<MobileNavLink to="/features" label="Features" />
</li>
<li>
<MobileNavLink to="/roadmap" label="Roadmap" />
</li>
<li>
<MobileNavLink to="open" label="Open Metrics" />
</li>
<li>
<MobileNavLink to="/pricing" label="Pricing" />
</li>
</ul>
</div>
</div>
</div>
</div>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
</div>
);
function MobileNavLink({ to, label }: NavLinkProps) {
return (
<Link to={to} onClick={() => setMobileNavOpen(false)} className="text-base flex text-gray-600 hover:text-gray-900">
{label}
</Link>
);
}
}
export default Headerold;

View File

@ -0,0 +1,51 @@
import CTAForm from "./cta-form";
import mockupImage from "../images/phone-mockup.png";
export default function Hero() {
return (
<div className="relative bg-gradient-to-b from-rebeccapurple-100 to-rebeccapurple-200">
<section className="overflow-hidden">
<div className="flex flex-col lg:flex-row lg:items-stretch lg:min-h-screen lg:max-h-[900px]">
<div className="flex items-center justify-center w-full lg:order-2 lg:w-7/12">
<div className="h-full px-4 pt-24 pb-16 sm:px-6 lg:px-24 2xl:px-32 lg:pt-40 lg:pb-14">
<div className="flex flex-col flex-1 justify-center h-full space-y-8">
<h1 className="font-heading text-4xl leading-none lg:leading-tight xl:text-5xl xl:leading-tight">
<span className="bg-gradient-to-br from-rebeccapurple-500 to-indigo-600 bg-clip-text decoration-clone text-transparent">
Take your phone number
</span>{" "}
<span className="text-[#24185B]">anywhere you dgo</span>
</h1>
<p className="text-base lg:text-lg xl:text-xl text-black">
Coming soon! &#128026; Keep your phone number and pay less for your communications,
even abroad.
</p>
<CTAForm />
<div className="max-w-lg mx-auto md:mx-0">
<span className="block md:inline mx-2">
<em> </em>Free trial
</span>
<span className="block md:inline mx-2">
<em> </em>No credit card required
</span>
<span className="block md:inline mx-2">
<em> </em>Cancel anytime
</span>
</div>
</div>
</div>
</div>
<div className="relative w-full overflow-hidden lg:w-5/12 lg:order-1">
<div className="lg:absolute lg:bottom-0 lg:left-0">
<img className="w-full" src={mockupImage} alt="App screenshot on a phone" />
</div>
</div>
</div>
</section>
</div>
);
}

View File

@ -0,0 +1,25 @@
import type { FunctionComponent, PropsWithChildren } from "react";
import BaseLayout from "./base-layout";
type Props = {
title?: string;
};
const Layout: FunctionComponent<PropsWithChildren<Props>> = ({ children, title }) => (
<BaseLayout>
<section className="max-w-6xl mx-auto px-4 sm:px-6">
<div className="pt-32 pb-10 md:pt-34 md:pb-16">
{title ? (
<div className="max-w-5xl mx-auto">
<h1 className="h1 mb-16 text-navy font-extrabold font-mackinac">{title}</h1>
</div>
) : null}
<div className="max-w-3xl mx-auto text-lg xl:text-xl flow-root">{children}</div>
</div>
</section>
</BaseLayout>
);
export default Layout;

View File

@ -0,0 +1,36 @@
import { IoClose } from "react-icons/io5";
export default function ReferralBanner() {
// TODO
const isDisabled = true;
if (isDisabled) {
return null;
}
return (
<div className="relative bg-rebeccapurple-600 z-40">
<div className="max-w-7xl mx-auto py-3 px-3 sm:px-6 lg:px-8">
<div className="pr-16 sm:text-center sm:px-16">
<p className="font-medium text-white">
<span>&#127881; New: Get one month free for every friend that joins and subscribe!</span>
<span className="block sm:ml-2 sm:inline-block">
<a href="#" className="text-white font-bold underline">
{" "}
Learn more <span aria-hidden="true">&rarr;</span>
</a>
</span>
</p>
</div>
<div className="absolute inset-y-0 right-0 pt-1 pr-1 flex items-start sm:pt-1 sm:pr-2 sm:items-start">
<button
type="button"
className="flex p-2 rounded-md hover:bg-rebeccapurple-500 focus:outline-none focus:ring-2 focus:ring-white"
>
<span className="sr-only">Dismiss</span>
<IoClose className="h-6 w-6 text-white" aria-hidden="true" />
</button>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,20 @@
export default function Testimonials() {
return (
<div className="bg-rebeccapurple-600">
<div className="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:py-16 lg:px-8">
<p className="text-xl text-white text-center text-base font-semibold uppercase text-gray-600 tracking-wider">
Trusted by digital nomads in
<div className="h-[2rem] relative flex">
<span className="location">Bali</span>
<span className="location">Tulum</span>
<span className="location">Tbilissi</span>
<span className="location">Bansko</span>
<span className="location">Zanzibar</span>
<span className="location">Mauritius</span>
<span className="location">Amsterdam</span>
</div>
</p>
</div>
</div>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@ -0,0 +1,23 @@
import Header from "../components/header";
import Footer from "../components/footer";
import ReferralBanner from "../components/referral-banner";
import Hero from "../components/hero";
import FAQs from "../components/faqs";
export default function IndexPage() {
return (
<section className="font-inter antialiased bg-white text-gray-900 tracking-tight">
<section className="flex flex-col min-h-screen overflow-hidden">
<Header />
<main className="flex-grow">
<ReferralBanner />
<Hero />
<FAQs />
</main>
<Footer />
</section>
</section>
);
}