Add logo wall transitions
This commit is contained in:
+34
-10
@@ -1,40 +1,45 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
const LogoWall = ({ logos = [] }) => {
|
const LogoWall = ({ logos = [] }) => {
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
// Default logos if none provided - ordered for mobile (3 rows × 2 columns)
|
// Default logos if none provided - ordered for mobile (3 rows × 2 columns)
|
||||||
const defaultLogos = [
|
const defaultLogos = [
|
||||||
{
|
{
|
||||||
src: "assets/Section/Logo_FoodNotBombs.png",
|
src: "/assets/Section/Logo_FoodNotBombs.png",
|
||||||
alt: "Food Not Bombs",
|
alt: "Food Not Bombs",
|
||||||
size: "h-11 lg:h-14 xl:h-[70px]",
|
size: "h-11 lg:h-14 xl:h-[70px]",
|
||||||
order: "order-1 sm:order-4", // Mobile: row 1 col 1, SM: row 2 col 1 (bottom left)
|
order: "order-1 sm:order-4", // Mobile: row 1 col 1, SM: row 2 col 1 (bottom left)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "assets/Section/Logo_StartCOOP.png",
|
src: "/assets/Section/Logo_StartCOOP.png",
|
||||||
alt: "Start COOP",
|
alt: "Start COOP",
|
||||||
size: "h-[42px] lg:h-[53px] xl:h-[66px]",
|
size: "h-[42px] lg:h-[53px] xl:h-[66px]",
|
||||||
order: "order-2 sm:order-2", // Mobile: row 1 col 2, SM: row 1 col 2 (top middle)
|
order: "order-2 sm:order-2", // Mobile: row 1 col 2, SM: row 1 col 2 (top middle)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "assets/Section/Logo_Metagov.png",
|
src: "/assets/Section/Logo_Metagov.png",
|
||||||
alt: "Metagov",
|
alt: "Metagov",
|
||||||
size: "h-6 lg:h-8 xl:h-[41px]",
|
size: "h-6 lg:h-8 xl:h-[41px]",
|
||||||
order: "order-3 sm:order-1", // Mobile: row 2 col 1, SM: row 1 col 1 (top left)
|
order: "order-3 sm:order-1", // Mobile: row 2 col 1, SM: row 1 col 1 (top left)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "assets/Section/Logo_OpenCivics.png",
|
src: "/assets/Section/Logo_OpenCivics.png",
|
||||||
alt: "Open Civics",
|
alt: "Open Civics",
|
||||||
size: "h-8 lg:h-10 xl:h-[50px]",
|
size: "h-8 lg:h-10 xl:h-[50px]",
|
||||||
order: "order-4 sm:order-5 md:order-6", // Mobile: row 2 col 2, SM: row 2 col 2, MD: swapped with Mutual Aid CO
|
order: "order-4 sm:order-5 md:order-6", // Mobile: row 2 col 2, SM: row 2 col 2, MD: swapped with Mutual Aid CO
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "assets/Section/Logo_MutualAidCO.png",
|
src: "/assets/Section/Logo_MutualAidCO.png",
|
||||||
alt: "Mutual Aid CO",
|
alt: "Mutual Aid CO",
|
||||||
size: "h-11 lg:h-14 xl:h-[70px]",
|
size: "h-11 lg:h-14 xl:h-[70px]",
|
||||||
order: "order-5 sm:order-6 md:order-5", // Mobile: row 3 col 1, SM: row 2 col 3, MD: swapped with OpenCivics
|
order: "order-5 sm:order-6 md:order-5", // Mobile: row 3 col 1, SM: row 2 col 3, MD: swapped with OpenCivics
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: "assets/Section/Logo_CUBoulder.png",
|
src: "/assets/Section/Logo_CUBoulder.png",
|
||||||
alt: "CU Boulder",
|
alt: "CU Boulder",
|
||||||
size: "h-10 lg:h-12 xl:h-[60px]",
|
size: "h-10 lg:h-12 xl:h-[60px]",
|
||||||
order: "order-6 sm:order-3", // Mobile: row 3 col 2, SM: row 1 col 3 (top right)
|
order: "order-6 sm:order-3", // Mobile: row 3 col 2, SM: row 1 col 3 (top right)
|
||||||
@@ -43,6 +48,15 @@ const LogoWall = ({ logos = [] }) => {
|
|||||||
|
|
||||||
const displayLogos = logos.length > 0 ? logos : defaultLogos;
|
const displayLogos = logos.length > 0 ? logos : defaultLogos;
|
||||||
|
|
||||||
|
// Simple fade-in effect after component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setIsVisible(true);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="p-[var(--spacing-scale-032)] md:px-[var(--spacing-scale-024)] md:py-[var(--spacing-scale-032)] lg:px-[var(--spacing-scale-064)] lg:py-[var(--spacing-scale-048)] xl:px-[160px] xl:py-[var(--spacing-scale-064)]">
|
<section className="p-[var(--spacing-scale-032)] md:px-[var(--spacing-scale-024)] md:py-[var(--spacing-scale-032)] lg:px-[var(--spacing-scale-064)] lg:py-[var(--spacing-scale-048)] xl:px-[160px] xl:py-[var(--spacing-scale-064)]">
|
||||||
<div className="flex flex-col gap-[var(--spacing-scale-032)] md:gap-[var(--spacing-scale-024)] xl:gap-[var(--spacing-scale-032)]">
|
<div className="flex flex-col gap-[var(--spacing-scale-032)] md:gap-[var(--spacing-scale-024)] xl:gap-[var(--spacing-scale-032)]">
|
||||||
@@ -52,19 +66,29 @@ const LogoWall = ({ logos = [] }) => {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Logo Grid Container */}
|
{/* Logo Grid Container */}
|
||||||
<div className="opacity-60">
|
<div
|
||||||
|
className={`transition-opacity duration-500 ${
|
||||||
|
isVisible ? "opacity-60" : "opacity-0"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<div className="grid grid-cols-2 grid-rows-3 sm:grid-cols-3 sm:grid-rows-2 md:flex md:justify-between md:items-center gap-x-[var(--spacing-scale-032)] gap-y-[var(--spacing-scale-032)] sm:gap-y-[var(--spacing-scale-048)]">
|
<div className="grid grid-cols-2 grid-rows-3 sm:grid-cols-3 sm:grid-rows-2 md:flex md:justify-between md:items-center gap-x-[var(--spacing-scale-032)] gap-y-[var(--spacing-scale-032)] sm:gap-y-[var(--spacing-scale-048)]">
|
||||||
{displayLogos.map((logo, index) => (
|
{displayLogos.map((logo, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`flex items-center justify-center ${
|
className={`flex items-center justify-center transition-opacity duration-500 hover:opacity-100 ${
|
||||||
logo.order || ""
|
logo.order || ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<img
|
<Image
|
||||||
src={logo.src}
|
src={logo.src}
|
||||||
alt={logo.alt}
|
alt={logo.alt}
|
||||||
className={`${logo.size || "h-8"} w-auto object-contain`}
|
width={120}
|
||||||
|
height={60}
|
||||||
|
className={`${
|
||||||
|
logo.size || "h-8"
|
||||||
|
} w-auto object-contain transition-transform duration-500 hover:scale-105`}
|
||||||
|
priority={index < 2} // Prioritize first 2 logos for above-the-fold loading
|
||||||
|
unoptimized // Skip optimization for local images
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ export default {
|
|||||||
- **LG**: Larger logo sizes and 64px horizontal padding
|
- **LG**: Larger logo sizes and 64px horizontal padding
|
||||||
- **XL**: Largest logo sizes, 160px horizontal padding, and 14px label text
|
- **XL**: Largest logo sizes, 160px horizontal padding, and 14px label text
|
||||||
|
|
||||||
|
## Animations & Transitions
|
||||||
|
|
||||||
|
- **Fade-in Effect**: Logos fade in from opacity 0 to 60% after component mounts (500ms transition)
|
||||||
|
- **Hover Interactions**: Individual logos respond to hover with:
|
||||||
|
- Opacity change from 60% to 100%
|
||||||
|
- Scale transform (105% zoom)
|
||||||
|
- 500ms smooth transitions for all effects
|
||||||
|
- **Loading States**: Progressive loading with fallback timer for reliable display
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
- **logos** (optional): Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos.
|
- **logos** (optional): Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos.
|
||||||
@@ -67,7 +76,7 @@ export const Default = {
|
|||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
story:
|
story:
|
||||||
"Default LogoWall with all partner logos. Displays in a 3×2 grid on mobile, 2×3 grid on small screens, single row on medium screens, and larger sizes on large screens.",
|
"Default LogoWall with all partner logos. Displays in a 3×2 grid on mobile, 2×3 grid on small screens, single row on medium screens, and larger sizes on large screens. Features smooth fade-in animations and hover interactions.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user