Section
Studio Footer
A tall dark footer with the studio mark as a faint silhouette behind it, a huge call to action in the middle, and links that nudge sideways on hover.
Hover anywhere in it — the mark behind everything swells and brightens
import StudioFooter from "@/components/ui/studio-footer";
export default function StudioFooterDemo({ fullscreen }: { fullscreen?: boolean }) {
if (fullscreen) return <StudioFooter />;
return (
<div className="w-full min-w-0 space-y-3">
<div className="w-full overflow-hidden rounded-card border border-line">
<StudioFooter />
</div>
<p className="text-center text-xs text-faint">
Hover anywhere in it — the mark behind everything swells and brightens
</p>
</div>
);
}Installation
1. Install the dependencies.
terminal npm install2. Add the
cnhelper, if you don't already have it.lib/utils.ts import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }3. Copy the component into your project.
components/ui/studio-footer.tsx type FooterLink = { label: string; href: string } const FOOTER_LINKS: FooterLink[] = [ { label: 'Home', href: '/' }, { label: 'Work', href: '/work' }, { label: 'Services', href: '/services' }, { label: 'About', href: '/about' }, { label: 'Contact', href: '/contact' }, ] const LEGAL_LINKS: FooterLink[] = [ { label: 'Privacy Policy', href: '/privacy-policy' }, { label: 'Terms and Conditions', href: '/terms-of-service' }, { label: 'Cookie Policy', href: '/cookie-policy' }, { label: 'Disclaimer', href: '/disclaimer' }, ] // public/images/Group.svg's three path shapes, flattened into one combined // path (same technique/coordinates as GROUP_PATH_D in components/WorkSection.tsx) // so the mark can be recolored freely — the source file bakes in a fixed dark // fill, which is unreadable against this footer's own dark background. const GROUP_PATH_D = 'M0,286.39L116.72,177.43L233.45,286.39L164.61,286.39L164.61,394.4L68.53,394.4L68.53,286.39L0,286.39Z ' + 'M0,103.66L0.02,159.51L116.52,55.26L233.01,159.35L233.45,102.8L116.52,0Z ' + 'M0,188.37L0.02,244.22L116.52,139.97L233.01,244.06L233.45,187.51L116.52,84.71Z' function LogoMark({ className }: { className?: string }) { return ( <svg viewBox="0 0 234 395" className={className} aria-hidden> <path d={GROUP_PATH_D} fill="currentColor" /> </svg> ) } type StudioFooterProps = { /** Shown beside the mark, top left. */ wordmark?: string tagline?: string /** Used by the email link and the big call to action. */ email?: string links?: FooterLink[] legalLinks?: FooterLink[] /** Who the copyright line names. */ owner?: string location?: string } export default function StudioFooter({ wordmark = 'kinematics.', tagline = 'Web agency in Kuwait building Awwwards-level, motion-first websites.', email = 'hello@kinematicstudio.com', links = FOOTER_LINKS, legalLinks = LEGAL_LINKS, owner = 'Kinematic Studio', location = 'Kuwait', }: StudioFooterProps) { return ( <footer className="group relative flex min-h-[80vh] w-full flex-col justify-between overflow-hidden bg-kine-bg px-6 py-12 md:px-10 md:py-16"> {/* Large background silhouette of the logo — centered, faint, purely decorative. Reacts to hovering anywhere in the footer (via the group class above) by growing slightly and brightening, rather than sitting completely inert. */} <LogoMark className="pointer-events-none absolute left-1/2 top-1/2 h-[85vh] w-auto -translate-x-1/2 -translate-y-1/2 text-white/[0.04] transition-all duration-700 ease-out group-hover:scale-105 group-hover:text-white/[0.07]" /> <div className="relative z-10 mx-auto flex w-full max-w-6xl flex-1 flex-col justify-between gap-10"> <div className="flex flex-col items-start justify-between gap-8 md:flex-row md:items-end"> <div className="flex flex-col gap-4"> <a href="/" className="flex items-center gap-4 transition-opacity duration-200 hover:opacity-80"> <LogoMark className="h-10 w-auto text-white md:h-12" /> <span className="font-kine-display text-4xl uppercase tracking-tight text-white md:text-5xl"> {wordmark} </span> </a> <p className="max-w-sm font-kine-body text-base text-kine-accent/60 md:text-lg"> {tagline} </p> </div> <nav className="flex flex-wrap gap-x-10 gap-y-4"> {links.map((link) => ( <a key={link.label} href={link.href} className="font-kine-body text-xl uppercase tracking-wide text-kine-accent/70 transition-all duration-200 hover:translate-x-1 hover:text-kine-primary md:text-2xl" > {link.label} </a> ))} </nav> <a href={`mailto:${email}`} className="font-kine-body text-base text-kine-accent/70 transition-all duration-200 hover:translate-x-1 hover:text-kine-primary" > {email} </a> </div> {/* Big central statement — fills the footer's own generous height (min-h-[80vh]) rather than leaving that middle space empty. */} <div className="flex flex-1 items-center justify-center py-10"> <a href={`mailto:${email}`} className="group/cta flex items-center gap-4 text-center font-kine-display uppercase leading-none tracking-tight text-white transition-all duration-300 hover:scale-[1.03] hover:text-kine-primary" style={{ fontSize: 'clamp(2.5rem, 9vw, 9rem)' }} > <span> Let's Build <br /> Something Great </span> <svg width="0.6em" height="0.6em" viewBox="0 0 16 16" fill="none" className="shrink-0 transition-transform duration-300 ease-out group-hover/cta:translate-x-2 group-hover/cta:-translate-y-2" > <path d="M4 12L12 4M12 4H5M12 4V11" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round" /> </svg> </a> </div> <div className="flex flex-col gap-6 border-t border-kine-accent/10 pt-6"> <nav className="flex flex-wrap gap-x-6 gap-y-2"> {legalLinks.map((link) => ( <a key={link.label} href={link.href} className="font-sans text-xs uppercase tracking-widest text-kine-accent/40 transition-colors duration-200 hover:text-kine-primary" > {link.label} </a> ))} </nav> <div className="flex flex-col-reverse items-start justify-between gap-4 md:flex-row md:items-center"> <span className="font-kine-body text-sm text-kine-accent/40"> © {new Date().getFullYear()} {owner}. All rights reserved. </span> <span className="font-kine-body text-sm uppercase tracking-widest text-kine-accent/40"> {location} </span> </div> </div> </div> </footer> ) }styles/kinematics.css /* Kinematic Studio's palette and type, for the components ported from its site. Namespaced as kine-* so they sit beside this library's own tokens — Kinematics calls its off-white "accent", which here is already the blue. Import this from the stylesheet that imports Tailwind, so the @theme block turns into utilities: bg-kine-bg, text-kine-primary/70, font-kine-display and so on. */ @theme { --color-kine-bg: #0f1113; --color-kine-primary: #ceff00; --color-kine-surface: #2b2e34; --color-kine-accent: #e2e8f0; /* Audiowide for headlines, Geo for body copy. Geo breaks up at small sizes, so tiny labels use the plain sans instead. */ --font-kine-display: "Audiowide", ui-sans-serif, system-ui, sans-serif; --font-kine-body: "Geo", ui-sans-serif, system-ui, sans-serif; /* Morni, the restaurant client in the Emoji Cursor demo. */ --font-morni-brand: "Modak", ui-sans-serif, system-ui, sans-serif; --font-morni-body: "Poppins", ui-sans-serif, system-ui, sans-serif; } /* Manifesto emoji markers — the box grows from square to rectangle as scroll reaches it; once mostly grown, the emoji starts rolling, paused until then via animation-play-state set inline. */ @keyframes manifesto-emoji-roll { to { transform: rotate(360deg); } } /* Expertise cards — small, always-looping visuals tying each card's animation to its own subject (motion, layout, load speed, ranking, language). CSS keyframes rather than GSAP, since they run for as long as the card is mounted. */ @keyframes expertise-dash { to { stroke-dashoffset: -24; } } .expertise-flow-line { stroke-dasharray: 6 6; animation: expertise-dash 1s linear infinite; } @keyframes expertise-block-a { 0%, 100% { width: 85%; } 50% { width: 45%; } } @keyframes expertise-block-b { 0%, 100% { width: 55%; } 50% { width: 90%; } } @keyframes expertise-block-c { 0%, 100% { width: 70%; } 50% { width: 30%; } } .expertise-block-a { animation: expertise-block-a 3.2s ease-in-out infinite; } .expertise-block-b { animation: expertise-block-b 3.2s ease-in-out infinite 0.3s; } .expertise-block-c { animation: expertise-block-c 3.2s ease-in-out infinite 0.6s; } @keyframes expertise-fill { 0% { width: 6%; opacity: 0.5; } 70% { width: 100%; opacity: 1; } 85% { width: 100%; opacity: 1; } 100% { width: 6%; opacity: 0.5; } } .expertise-fill-bar { animation: expertise-fill 2.2s cubic-bezier(0.65, 0, 0.35, 1) infinite; } @keyframes expertise-rank-bar-1 { 0%, 100% { height: 30%; } 50% { height: 80%; } } @keyframes expertise-rank-bar-2 { 0%, 100% { height: 55%; } 50% { height: 25%; } } @keyframes expertise-rank-bar-3 { 0%, 100% { height: 40%; } 50% { height: 95%; } } @keyframes expertise-rank-bar-4 { 0%, 100% { height: 70%; } 50% { height: 50%; } } .expertise-rank-1 { animation: expertise-rank-bar-1 2.4s ease-in-out infinite; } .expertise-rank-2 { animation: expertise-rank-bar-2 2.4s ease-in-out infinite 0.15s; } .expertise-rank-3 { animation: expertise-rank-bar-3 2.4s ease-in-out infinite 0.3s; } .expertise-rank-4 { animation: expertise-rank-bar-4 2.4s ease-in-out infinite 0.45s; } @keyframes expertise-lang-cross { 0%, 45% { opacity: 1; transform: translateY(0); } 50%, 95% { opacity: 0; transform: translateY(-6px); } 100% { opacity: 1; transform: translateY(0); } } @keyframes expertise-lang-cross-alt { 0%, 45% { opacity: 0; transform: translateY(6px); } 50%, 95% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(6px); } } .expertise-lang-a { animation: expertise-lang-cross 3.6s ease-in-out infinite; } .expertise-lang-b { animation: expertise-lang-cross-alt 3.6s ease-in-out infinite; }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| wordmark | string | "kinematics." | Beside the mark, top left. |
| tagline | string | — | Line under the wordmark. |
| string | "hello@kinematicstudio.com" | Used by the email link and the big call to action. | |
| links | { label: string; href: string }[] | 5 links | Main navigation. |
| legalLinks | { label: string; href: string }[] | 4 links | Small links along the bottom. |
| owner | string | "Kinematic Studio" | Named in the copyright line. |
| location | string | "Kuwait" | Bottom right. |