Skip to content
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

Refactor(homescreen, navbar): hero section changed #441

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"date-fns": "^4.1.0",
"free-astro-components": "^1.1.1",
"lucide-astro": "^0.460.0",
"lucide-react": "^0.474.0",
"motion": "^11.13.5",
"postcss": "8.4.21",
"prettier": "^3.3.3",
Expand Down
6 changes: 3 additions & 3 deletions src/animations.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
export function getTitleAnimation(delay = 0) {
export function getTitleAnimation(delay = 0, duration = 0.3, once = true) {
return {
initial: { opacity: 0, translateY: 20, filter: 'blur(4px)' },
whileInView: {
opacity: 1,
translateY: 0,
filter: 'blur(0px)',
transition: { duration: 0.3, delay },
transition: { duration, delay },
},
viewport: { once: true },
viewport: { once: once },
}
}

Expand Down
78 changes: 50 additions & 28 deletions src/components/Hero.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,84 @@ import { ArrowRight } from 'lucide-astro'
import { motion } from 'motion/react'
import { getTitleAnimation } from '../animations'

export function splitTextIntoWords(text: string) {
return text.split(' ').map((word, wordIndex) => ({
word,
characters: word.split('').map((char, charIndex) => ({
char,
key: `${wordIndex}-${charIndex}`,
})),
}))
}

let titleAnimationCounter = 0
function getNewAnimationDelay() {
function getNewAnimationDelay(customDelay = 0.15) {
titleAnimationCounter++
return titleAnimationCounter * 0.15
return titleAnimationCounter * customDelay
}

function getHeroTitleAnimation() {
return getTitleAnimation(getNewAnimationDelay())
function getHeroTitleAnimation(customDelay = 0.15, customDuration = 0.3) {
return getTitleAnimation(getNewAnimationDelay(customDelay), customDuration)
}

const titleText = 'Welcome to a calmer internet'
const splitTitleText = splitTextIntoWords(titleText)
---

<header
id="header"
class="flex w-full flex-col items-center gap-[20%] py-32 pb-16 text-center md:pb-32 lg:gap-[15%] lg:pb-16"
class="flex h-screen w-full flex-col items-center gap-[20%] pb-16 text-center md:pb-32 lg:gap-[15%] lg:pb-16"
>
<div class="flex h-full flex-col items-center justify-center">
<Title
class="relative px-12 text-left !font-normal leading-[108px] md:text-center md:!text-7xl lg:px-0 lg:!text-9xl"
class="relative whitespace-normal break-words px-12 text-left font-instrument font-normal leading-tight md:text-center md:!text-6xl lg:px-0 lg:!text-8xl"
>
<motion.span client:load {...getHeroTitleAnimation()}>
welcome
</motion.span>
<motion.span client:load {...getHeroTitleAnimation()}> to </motion.span>
<br class="hidden md:block" />
<motion.span client:load {...getHeroTitleAnimation()}> a </motion.span>
<motion.span
client:load
{...getHeroTitleAnimation()}
className="italic text-coral"
>
calmer
</motion.span>
<motion.span client:load {...getHeroTitleAnimation()}>
internet
</motion.span>
{
splitTitleText.map(({ word, characters }, wordIndex) => (
<span
class={
word === 'calmer'
? 'inline-block text-nowrap font-instrument italic text-coral'
: 'inline-block text-nowrap'
}
>
{characters.map(({ char, key }) => (
<motion.span
key={key}
className="inline-block"
client:load
{...getHeroTitleAnimation(0.05)}
>
{char}
</motion.span>
))}
{wordIndex < splitTitleText.length - 1 && (
<span class="inline-block" />
)}
</span>
))
}
</Title>
<motion.span client:load {...getHeroTitleAnimation()}>
<motion.span client:load {...getHeroTitleAnimation(0.07)}>
<Description class="px-12 text-left md:text-center lg:px-0"
>Beautifully designed, privacy-focused, and packed with features.<br
/>We care about your experience, not your data.</Description
>
</motion.span>
<div class="mt-6 flex w-2/3 flex-col gap-3 sm:gap-6 md:w-fit md:flex-row">
<motion.span client:load {...getHeroTitleAnimation()}>
<motion.span client:load {...getHeroTitleAnimation(0.07)}>
<Button class="w-full" href="/download" isPrimary>
Beta is now available!
<ArrowRight class="size-4" />
</Button>
</motion.span>
<motion.span client:load {...getHeroTitleAnimation()}>
<Button href="#features">Explore Zen</Button>
<motion.span client:load {...getHeroTitleAnimation(0.07)}>
<Button href="#features">Explore</Button>
</motion.span>
</div>
</div>
</header>
<motion.span client:load {...getHeroTitleAnimation()}>
<!-- <motion.span client:load {...getHeroTitleAnimation()}>
<Image
src={myImage}
alt="Zen browser"
Expand All @@ -77,4 +99,4 @@ function getHeroTitleAnimation() {
class="mx-auto mb-24 block dark:hidden"
loading="eager"
/>
</motion.span>
</motion.span> -->
5 changes: 5 additions & 0 deletions src/components/Logo.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
---
const { class: className } = Astro.props
---

<svg
class={className}
width="32"
height="32"
viewBox="0 0 64 64"
Expand Down
155 changes: 155 additions & 0 deletions src/components/MobileNavBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import React, { useState } from 'react'
import MyLogo from './Mylogo'
import { Menu } from 'lucide-react'

const MobileNavbar: React.FC = () => {
const [isMenuOpen, setMenuOpen] = useState(false)

const menuTransformClass = isMenuOpen ? 'translate-x-0' : 'translate-x-full'

return (
<div className="lg:hidden">
{/* Mobile Header */}
<header className="flex items-center justify-between bg-paper px-4 py-2 dark:shadow">
<a className="flex items-center gap-2" href="/">
<MyLogo className="h-8 w-8 text-coral" />
<span className="text-lg font-bold">zen browser</span>
</a>
<button
id="burger-btn"
className="p-2 text-dark"
aria-label="Open menu"
onClick={() => setMenuOpen(true)}
>
<Menu className="h-6 w-6" />
</button>
</header>

{/* Mobile Slide Menu (slides in from the right) */}
<div
id="mobile-menu"
className={`fixed inset-y-0 right-0 z-40 w-64 transform bg-paper shadow-lg transition-transform duration-300 ${menuTransformClass}`}
>
<div className="flex items-center justify-between border-b border-dark px-4 py-2">
<div className="text-lg font-bold">Menu</div>
<button
id="close-btn"
className="p-2 text-dark"
aria-label="Close menu"
onClick={() => setMenuOpen(false)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<nav className="px-4 py-2">
<ul className="space-y-4">
{/* Getting Started Links */}
<li>
<div className="mb-2 font-bold">Getting Started</div>
<ul className="ml-4 space-y-2">
<li>
<a href="/mods" className="block text-dark hover:text-coral">
Zen Mods
</a>
</li>
<li>
<a
href="/release-notes"
className="block text-dark hover:text-coral"
>
Release Notes
</a>
</li>
<li>
<a
href="https://discord.gg/zen-browser"
className="block text-dark hover:text-coral"
>
Discord
</a>
</li>
</ul>
</li>
{/* Useful Links */}
<li>
<div className="mb-2 font-bold">Useful Links</div>
<ul className="ml-4 space-y-2">
<li>
<a
href="/donate"
className="block text-dark hover:text-coral"
>
Donate ❤️
</a>
</li>
<li>
<a href="/about" className="block text-dark hover:text-coral">
About Us 🌟
</a>
</li>
<li>
<a
href="https://docs.zen-browser.app"
className="block text-dark hover:text-coral"
>
Documentation
</a>
</li>
<li>
<a
href="https://github.com/zen-browser"
target="_blank"
className="block text-dark hover:text-coral"
>
GitHub
</a>
</li>
</ul>
</li>
{/* Extra Links */}
<li>
<a
href="/mods"
className="block font-bold text-dark hover:text-coral"
>
Mods
</a>
</li>
<li>
<a
href="/download"
className="block font-bold text-dark hover:text-coral"
>
Download
</a>
</li>
</ul>
</nav>
</div>

{/* Overlay for Mobile Menu */}
{isMenuOpen && (
<div
id="overlay"
className="fixed inset-0 z-30 bg-black opacity-50"
onClick={() => setMenuOpen(false)}
/>
)}
</div>
)
}

export default MobileNavbar
37 changes: 37 additions & 0 deletions src/components/Mylogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'

interface MyLogoProps {
className?: string
}

const MyLogo: React.FC<MyLogoProps> = ({ className }) => (
<svg
className={className}
width="32"
height="32"
viewBox="0 0 64 64"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M32 44.3077C38.7974 44.3077 44.3077 38.7974 44.3077 32C44.3077 25.2027 38.7974 19.6923 32 19.6923C25.2027 19.6923 19.6923 25.2027 19.6923 32C19.6923 38.7974 25.2027 44.3077 32 44.3077ZM41.8462 32C41.8462 37.4379 37.4379 41.8462 32 41.8462C26.5621 41.8462 22.1538 37.4379 22.1538 32C22.1538 26.5621 26.5621 22.1538 32 22.1538C37.4379 22.1538 41.8462 26.5621 41.8462 32Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M53.3333 32C53.3333 43.7821 43.7821 53.3333 32 53.3333C20.2179 53.3333 10.6667 43.7821 10.6667 32C10.6667 20.2179 20.2179 10.6667 32 10.6667C43.7821 10.6667 53.3333 20.2179 53.3333 32ZM32 49.2308C41.5163 49.2308 49.2308 41.5163 49.2308 32C49.2308 22.4837 41.5163 14.7692 32 14.7692C22.4837 14.7692 14.7692 22.4837 14.7692 32C14.7692 41.5163 22.4837 49.2308 32 49.2308Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M64 32C64 49.6731 49.6731 64 32 64C14.3269 64 0 49.6731 0 32C0 14.3269 14.3269 0 32 0C49.6731 0 64 14.3269 64 32ZM32 58.2564C46.501 58.2564 58.2564 46.501 58.2564 32C58.2564 17.499 46.501 5.74359 32 5.74359C17.499 5.74359 5.74359 17.499 5.74359 32C5.74359 46.501 17.499 58.2564 32 58.2564Z"
fill="currentColor"
/>
</svg>
)

export default MyLogo
Loading