10 Commits

14 changed files with 215 additions and 5 deletions

View File

@@ -1,7 +1,9 @@
import { motion } from "motion/react";
import { useState } from "react";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import { getRandomFact } from "@/lib/facts";
type AboutMediaOverlayProps = {
tag: string;
@@ -20,6 +22,7 @@ const AboutMediaOverlay = ({
imageSrc,
videoSrc,
}: AboutMediaOverlayProps) => {
const [fact] = useState(getRandomFact);
return (
<section aria-label="About section" className="py-20">
<div className="relative flex items-center justify-center py-8 md:py-12 mx-auto w-content-width rounded overflow-hidden">
@@ -47,6 +50,16 @@ const AboutMediaOverlay = ({
className="text-6xl font-medium text-balance"
/>
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.4, ease: "easeOut", delay: 0.2 }}
className="backdrop-blur-md bg-white/30 border border-white/20 rounded-lg p-4 my-6 text-center max-w-md"
>
<p className="text-sm italic text-foreground">"{fact}"</p>
</motion.div>
<TextAnimation
text={description}
variant="slide-up"

View File

@@ -1,8 +1,9 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { motion } from "motion/react";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import { sendContactEmail } from "@/lib/api/email";
import { companyFacts } from "../../../data/facts";
type InputField = {
name: string;
@@ -39,6 +40,12 @@ const ContactSplitForm = ({
imageSrc,
videoSrc,
}: ContactSplitFormProps) => {
const [currentFact, setCurrentFact] = useState("");
useEffect(() => {
setCurrentFact(companyFacts[Math.floor(Math.random() * companyFacts.length)]);
}, []);
const [formData, setFormData] = useState<Record<string, string>>(() => {
const initial: Record<string, string> = {};
inputs.forEach((input) => {
@@ -88,6 +95,18 @@ const ContactSplitForm = ({
className="text-4xl font-medium text-balance"
/>
{currentFact && (
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.4, ease: "easeOut", delay: 0.2 }}
className="backdrop-blur-md bg-white/30 border border-white/20 rounded-lg p-4 my-6 text-center max-w-md"
>
<p className="text-sm italic text-foreground">"{currentFact}"</p>
</motion.div>
)}
<TextAnimation
text={description}
variant="slide-up"

View File

@@ -5,6 +5,7 @@ import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import { cls } from "@/lib/utils";
import GlassmorphicBadge from "@/components/ui/GlassmorphicBadge";
type FaqItem = {
question: string;
@@ -56,6 +57,8 @@ const FaqSplitMedia = ({
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
<GlassmorphicBadge />
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}

View File

@@ -3,6 +3,7 @@ import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import GridOrCarousel from "@/components/ui/GridOrCarousel";
import FunnyFactBadge from "@/components/ui/FunnyFactBadge";
type FeatureItem = {
title: string;
@@ -46,6 +47,8 @@ const FeaturesMediaCards = ({
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
<FunnyFactBadge />
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}

View File

@@ -1,4 +1,5 @@
import { useButtonClick } from "@/hooks/useButtonClick";
import { useState, useEffect } from "react";
type FooterLink = {
label: string;
@@ -33,9 +34,28 @@ const FooterBasic = ({
leftText: string;
rightText: string;
}) => {
const funnyFacts = [
"Our office coffee machine is sentient and demands tribute in the form of spare USB cables.",
"We once solved a major server outage by turning it off and on again. The client thinks we're wizards.",
"The company's first server was a modified gaming console. It ran surprisingly well.",
"Our AI assistant has developed a passion for writing poetry about database schemas.",
"Legend says our lead developer codes in binary. We haven't disproven it."
];
const [randomFact, setRandomFact] = useState<string | null>(null);
useEffect(() => {
setRandomFact(funnyFacts[Math.floor(Math.random() * funnyFacts.length)]);
}, []);
return (
<footer aria-label="Site footer" className="w-full pt-20 pb-10">
<div className="w-content-width mx-auto">
{randomFact && (
<div className="backdrop-blur-md bg-white/30 border border-white/20 rounded-xl shadow-lg p-4 text-foreground mb-8 text-center">
<p className="italic">"{randomFact}"</p>
</div>
)}
<div className="w-full flex flex-wrap justify-between gap-y-10 mb-10">
{columns.map((column) => (
<div key={column.title} className="w-1/2 md:w-auto flex flex-col items-start gap-3">

View File

@@ -2,6 +2,8 @@ import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import AutoFillText from "@/components/ui/AutoFillText";
import GlassmorphicBadge from "@/components/ui/GlassmorphicBadge";
import { motion } from "motion/react";
type HeroBrandProps = {
brand: string;
@@ -35,7 +37,14 @@ const HeroBrand = ({
/>
<div className="relative z-10 w-content-width mx-auto pb-5">
<div className="flex flex-col">
<div className="flex flex-col gap-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: "easeOut" }}
>
<GlassmorphicBadge />
</motion.div>
<div className="w-full flex flex-col md:flex-row md:justify-between items-start md:items-end gap-3 md:gap-5">
<TextAnimation
text={description}

View File

@@ -1,9 +1,13 @@
"use client";
import { useState } from "react";
import { motion } from "motion/react";
import type { LucideIcon } from "lucide-react";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import GridOrCarousel from "@/components/ui/GridOrCarousel";
import { resolveIcon } from "@/utils/resolve-icon";
import { getRandomFact } from "@/lib/facts";
type Metric = {
value: string;
@@ -26,7 +30,9 @@ const MetricsGradientCards = ({
primaryButton?: { text: string; href: string };
secondaryButton?: { text: string; href: string };
metrics: Metric[];
}) => (
}) => {
const [fact] = useState(getRandomFact);
return (
<section aria-label="Metrics section" className="py-20">
<div className="flex flex-col gap-8">
<div className="flex flex-col items-center gap-3 md:gap-2 w-content-width mx-auto">
@@ -46,6 +52,16 @@ const MetricsGradientCards = ({
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.4, ease: "easeOut", delay: 0.2 }}
className="backdrop-blur-md bg-white/30 border border-white/20 rounded-lg p-4 my-6 text-center max-w-md"
>
<p className="text-sm italic text-foreground">"{fact}"</p>
</motion.div>
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}
@@ -88,6 +104,7 @@ const MetricsGradientCards = ({
</motion.div>
</div>
</section>
);
);
};
export default MetricsGradientCards;

View File

@@ -1,6 +1,8 @@
import { motion } from "motion/react";
import { useState } from "react";
import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import { getRandomFact } from "@/lib/facts";
const SocialProofMarquee = ({
tag,
@@ -17,6 +19,8 @@ const SocialProofMarquee = ({
secondaryButton?: { text: string; href: string };
names: string[];
}) => {
const [fact] = useState(getRandomFact);
return (
<section aria-label="Social proof section" className="py-20">
<div className="flex flex-col gap-8">
@@ -37,6 +41,16 @@ const SocialProofMarquee = ({
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.4, ease: "easeOut", delay: 0.2 }}
className="backdrop-blur-md bg-white/30 border border-white/20 rounded-lg p-4 my-4 text-center max-w-md mx-auto"
>
<p className="text-sm italic text-foreground">"{fact}"</p>
</motion.div>
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}

View File

@@ -3,6 +3,7 @@ import Button from "@/components/ui/Button";
import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import GridOrCarousel from "@/components/ui/GridOrCarousel";
import GlassmorphicBadge from "@/components/ui/GlassmorphicBadge";
type Testimonial = {
name: string;
@@ -44,6 +45,8 @@ const TestimonialQuoteCards = ({
className="md:max-w-6/10 text-lg leading-tight text-center"
/>
<GlassmorphicBadge />
{(primaryButton || secondaryButton) && (
<div className="flex flex-wrap justify-center gap-3 mt-1 md:mt-2">
{primaryButton && <Button text={primaryButton.text} href={primaryButton.href} variant="primary" animate />}

View File

@@ -0,0 +1,32 @@
"use client";
import { useState } from "react";
import { motion } from "motion/react";
const funnyFacts = [
"Our office coffee machine is sentient and occasionally offers career advice.",
"We once solved a major bug by turning the server off and on again. It's now our official policy.",
"The company mascot is a rubber duck named 'Sir Quacks-a-Lot'. He has his own email address.",
"All our code is written in Comic Sans for 'improved readability'.",
"Our CTO's cat is listed as a senior developer on our internal directory.",
];
const getRandomFact = () => funnyFacts[Math.floor(Math.random() * funnyFacts.length)];
const FunnyFactBadge = () => {
const [fact] = useState(getRandomFact);
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.4, ease: "easeOut", delay: 0.2 }}
className="backdrop-blur-md bg-white/30 border border-white/20 rounded-lg p-4 my-4 text-center max-w-md mx-auto"
>
<p className="text-sm italic text-foreground">"{fact}"</p>
</motion.div>
);
};
export default FunnyFactBadge;

View File

@@ -0,0 +1,37 @@
"use client";
import { useState, useEffect } from "react";
import { motion } from "motion/react";
const funnyFacts = [
"Our office coffee machine is rumored to be sentient. It only makes good coffee for its favorites.",
"We once named a server 'Zeus' and it was promptly struck by a lightning-induced power surge. We stick to boring names now.",
"The office plant has been promoted more times than some of our interns.",
"Our first 'office' was a garage that we shared with a surprisingly musical raccoon.",
"We settled a heated debate on tabs vs. spaces with a company-wide rock-paper-scissors tournament.",
"The company mascot is a rubber duck named 'Kernel Panic'. He oversees all code deployments.",
];
const GlassmorphicBadge = () => {
const [fact, setFact] = useState("");
useEffect(() => {
setFact(funnyFacts[Math.floor(Math.random() * funnyFacts.length)]);
}, []);
if (!fact) return null;
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-15%" }}
transition={{ duration: 0.4, ease: "easeOut", delay: 0.2 }}
className="backdrop-blur-md bg-white/30 border border-white/20 rounded-lg p-4 my-6 text-center max-w-md mx-auto"
>
<p className="text-sm italic text-foreground">"{fact}"</p>
</motion.div>
);
};
export default GlassmorphicBadge;

View File

@@ -45,9 +45,22 @@ const NavLink = ({
);
};
const funnyFacts = [
"Our office coffee is powered by pure optimism.",
"We once named a server 'Titanic'. It was unsinkable.",
"Our first 'office' was a garage. We still have oil stains.",
"The office plant is our longest-serving employee.",
"Our mascot is a rubber duck named 'Sir Quacks-a-lot'.",
];
const NavbarCentered = ({ logo, navItems, ctaButton }: NavbarCenteredProps) => {
const [isScrolled, setIsScrolled] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [fact, setFact] = useState("");
useEffect(() => {
setFact(funnyFacts[Math.floor(Math.random() * funnyFacts.length)]);
}, []);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 50);
@@ -64,7 +77,14 @@ const NavbarCentered = ({ logo, navItems, ctaButton }: NavbarCenteredProps) => {
)}
>
<div className="relative flex items-center justify-between h-full w-content-width mx-auto">
<Link to="/" className="text-xl font-medium text-foreground">{logo}</Link>
<div className="flex items-center gap-4">
<Link to="/" className="text-xl font-medium text-foreground">{logo}</Link>
{fact && (
<div className="hidden lg:block backdrop-blur-md bg-white/20 border border-white/10 rounded-full px-3 py-1 text-xs shadow-lg text-foreground whitespace-nowrap">
{fact}
</div>
)}
</div>
<div className="hidden md:flex absolute left-1/2 items-center gap-6 -translate-x-1/2">
{navItems.map((item) => (

7
src/data/facts.ts Normal file
View File

@@ -0,0 +1,7 @@
export const companyFacts = [
"Our office coffee machine is rumored to be sentient. We haven't confirmed it, but it makes a great latte.",
"We once held a meeting entirely in pirate speak. Productivity was questionable, but morale was high.",
"The office plant, 'Fern-ando', has been promoted to Senior Vice President of Photosynthesis.",
"Our first server was a potato. It was surprisingly reliable.",
"We have a rubber duck for debugging. It's our most senior developer.",
];

13
src/lib/facts.ts Normal file
View File

@@ -0,0 +1,13 @@
export const facts = [
"Our office coffee machine is rumored to be sentient. It only makes good coffee for its favorites.",
"We once held a meeting entirely in emojis. It was surprisingly productive.",
"The company's first server was a modified gaming console. It still holds the high score in Tetris.",
"Our CEO's dog is an official board member with the title 'Chief Morale Officer'.",
"We have a rubber duck for debugging. It's been promoted twice.",
"The office plant has more followers on Instagram than most of our employees.",
"Our team-building exercise involved assembling IKEA furniture without instructions. We're still not talking about it.",
];
export const getRandomFact = () => {
return facts[Math.floor(Math.random() * facts.length)];
};