refactor: Improve responsiveness of Features component layout

This commit is contained in:
Mauro Balades
2024-08-11 21:12:23 +02:00
parent 2b7f7fc9d2
commit 658ee9f1bc
2 changed files with 155 additions and 0 deletions

View File

@@ -17,6 +17,7 @@ import {
import { CheckIcon, XIcon } from "lucide-react"; import { CheckIcon, XIcon } from "lucide-react";
import { QuestionMarkIcon } from "@radix-ui/react-icons"; import { QuestionMarkIcon } from "@radix-ui/react-icons";
import ShineBorder from "./ui/shine-border"; import ShineBorder from "./ui/shine-border";
import SparklesText from "./ui/sparkles-text";
function Checkmark() { function Checkmark() {
return <CheckIcon className="mx-auto text-black mx-auto rounded-full bg-green-500 dark:bg-green-400 p-1 w-7 h-7" /> return <CheckIcon className="mx-auto text-black mx-auto rounded-full bg-green-500 dark:bg-green-400 p-1 w-7 h-7" />
@@ -34,6 +35,8 @@ export default function Features() {
return ( return (
<div> <div>
{/*<TextReveal text="Zen will change the way you browse the web. 🌟" />*/} {/*<TextReveal text="Zen will change the way you browse the web. 🌟" />*/}
<h1 className="text-5xl font-bold mt-20 text-center flex justify-center items-center mb-2">What does Zen offer <SparklesText className="mx-2" text="YOU" />?</h1>
<p className="text-muted-foreground text-center mb-24">Zen Browser is packed with features that will change the way you browse the web. Here are<br/> some of the features that Zen offers.</p>
<div className="flex flex-col lg:flex-row w-full mt-4 p-5 lg:p-0"> <div className="flex flex-col lg:flex-row w-full mt-4 p-5 lg:p-0">
<div className="w-full flex mx-auto lg:mx-0 flex-col lg:mb-24 lg:ml-4 lg:mt-24"> <div className="w-full flex mx-auto lg:mx-0 flex-col lg:mb-24 lg:ml-4 lg:mt-24">
<div className="bg-background relative mx-auto lg:mx-0 flex flex-col max-w-lg justify-center rounded-xl border-2 p-20 md:shadow-xl"> <div className="bg-background relative mx-auto lg:mx-0 flex flex-col max-w-lg justify-center rounded-xl border-2 p-20 md:shadow-xl">

View File

@@ -0,0 +1,152 @@
'use client'
import { motion } from 'framer-motion'
import type { CSSProperties, ReactElement } from 'react'
import { useEffect, useState } from 'react'
import { ny } from '@/lib/utils'
interface Sparkle {
id: string
x: string
y: string
color: string
delay: number
scale: number
lifespan: number
}
interface SparklesTextProps {
/**
* @default <div />
* @type ReactElement
* @description
* The component to be rendered as the text
*/
as?: ReactElement
/**
* @default ""
* @type string
* @description
* The className of the text
*/
className?: string
/**
* @required
* @type string
* @description
* The text to be displayed
*/
text: string
/**
* @default 10
* @type number
* @description
* The count of sparkles
*/
sparklesCount?: number
/**
* @default "{first: '#A07CFE', second: '#FE8FB5'}"
* @type string
* @description
* The colors of the sparkles
*/
colors?: {
first: string
second: string
}
}
const SparklesText: React.FC<SparklesTextProps> = ({
text,
colors = { first: '#A07CFE', second: '#FE8FB5' },
className,
sparklesCount = 10,
...props
}) => {
const [sparkles, setSparkles] = useState<Sparkle[]>([])
useEffect(() => {
const generateStar = (): Sparkle => {
const starX = `${Math.random() * 100}%`
const starY = `${Math.random() * 100}%`
const color = Math.random() > 0.5 ? colors.first : colors.second
const delay = Math.random() * 2
const scale = Math.random() * 1 + 0.3
const lifespan = Math.random() * 10 + 5
const id = `${starX}-${starY}-${Date.now()}`
return { id, x: starX, y: starY, color, delay, scale, lifespan }
}
const initializeStars = () => {
const newSparkles = Array.from({ length: sparklesCount }, generateStar)
setSparkles(newSparkles)
}
const updateStars = () => {
setSparkles(currentSparkles =>
currentSparkles.map((star) => {
if (star.lifespan <= 0)
return generateStar()
else return { ...star, lifespan: star.lifespan - 0.1 }
}),
)
}
initializeStars()
const interval = setInterval(updateStars, 100)
return () => clearInterval(interval)
}, [colors.first, colors.second])
return (
<div
className={ny('text-6xl font-bold', className)}
{...props}
style={
{
'--sparkles-first-color': `${colors.first}`,
'--sparkles-second-color': `${colors.second}`,
} as CSSProperties
}
>
<span className="relative inline-block">
{sparkles.map(sparkle => (
<Sparkle key={sparkle.id} {...sparkle} />
))}
<strong className="bg-gradient-to-r from-[var(--sparkles-first-color)] to-[var(--sparkles-second-color)] bg-clip-text text-transparent">
{text}
</strong>
</span>
</div>
)
}
const Sparkle: React.FC<Sparkle> = ({ id, x, y, color, delay, scale }) => {
return (
<motion.svg
key={id}
className="pointer-events-none absolute z-20"
initial={{ opacity: 0, left: x, top: y }}
animate={{
opacity: [0, 1, 0],
scale: [0, scale, 0],
rotate: [75, 120, 150],
}}
transition={{ duration: 0.8, repeat: Infinity, delay }}
width="21"
height="21"
viewBox="0 0 21 21"
>
<path
d="M9.82531 0.843845C10.0553 0.215178 10.9446 0.215178 11.1746 0.843845L11.8618 2.72026C12.4006 4.19229 12.3916 6.39157 13.5 7.5C14.6084 8.60843 16.8077 8.59935 18.2797 9.13822L20.1561 9.82534C20.7858 10.0553 20.7858 10.9447 20.1561 11.1747L18.2797 11.8618C16.8077 12.4007 14.6084 12.3916 13.5 13.5C12.3916 14.6084 12.4006 16.8077 11.8618 18.2798L11.1746 20.1562C10.9446 20.7858 10.0553 20.7858 9.82531 20.1562L9.13819 18.2798C8.59932 16.8077 8.60843 14.6084 7.5 13.5C6.39157 12.3916 4.19225 12.4007 2.72023 11.8618L0.843814 11.1747C0.215148 10.9447 0.215148 10.0553 0.843814 9.82534L2.72023 9.13822C4.19225 8.59935 6.39157 8.60843 7.5 7.5C8.60843 6.39157 8.59932 4.19229 9.13819 2.72026L9.82531 0.843845Z"
fill={color}
/>
</motion.svg>
)
}
export default SparklesText