Merge pull request 'Logo Wall' (#9) from adilallo/component/LogoWall into adilallo/HeroBanner
Reviewed-on: #9
This commit was merged in pull request #9.
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
# [PR Title]
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Brief description of what this PR does and why.
|
||||||
|
Mention any context, background, or goals.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- High-level summary of key changes
|
||||||
|
- List of components, features, or files added/modified
|
||||||
|
- Reference design tokens, breakpoints, or accessibility improvements if relevant
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
<!-- Add image tags if visual changes exist, otherwise leave this section empty -->
|
||||||
|
|
||||||
|
## How to Test
|
||||||
|
|
||||||
|
1. Steps to run locally or in Storybook
|
||||||
|
2. List what to verify (e.g., responsive layout, accessibility, functional links)
|
||||||
|
3. Include breakpoints, browsers, or devices if relevant
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Any future follow-ups, TODOs, or known limitations
|
||||||
|
- Temporary setups or workarounds (e.g., personal deployments, placeholder assets)
|
||||||
|
- External dependencies (e.g., design tokens, API connections)
|
||||||
@@ -42,3 +42,7 @@ next-env.d.ts
|
|||||||
|
|
||||||
*storybook.log
|
*storybook.log
|
||||||
storybook-static
|
storybook-static
|
||||||
|
|
||||||
|
# storybook config files (to avoid git changes when switching between local and production)
|
||||||
|
.storybook/main.js
|
||||||
|
.storybook/preview.js
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/** @type { import('@storybook/nextjs-vite').StorybookConfig } */
|
||||||
|
const config = {
|
||||||
|
stories: [
|
||||||
|
"../stories/**/*.mdx",
|
||||||
|
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
||||||
|
],
|
||||||
|
addons: [
|
||||||
|
"@chromatic-com/storybook",
|
||||||
|
"@storybook/addon-docs",
|
||||||
|
"@storybook/addon-onboarding",
|
||||||
|
"@storybook/addon-a11y",
|
||||||
|
"@storybook/addon-vitest",
|
||||||
|
],
|
||||||
|
framework: {
|
||||||
|
name: "@storybook/nextjs-vite",
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
staticDirs: ["../public"],
|
||||||
|
managerHead: (head) => `${head}<base href="/communityrulestorybook/">`,
|
||||||
|
previewHead: (head) => `${head}<base href="/communityrulestorybook/">`,
|
||||||
|
async viteFinal(cfg) {
|
||||||
|
// IMPORTANT: Set base path for GitHub Pages sub-path hosting
|
||||||
|
cfg.base = "/communityrulestorybook/";
|
||||||
|
// Ensure esbuild treats .js as JSX during dep pre-bundling
|
||||||
|
cfg.optimizeDeps ??= {};
|
||||||
|
cfg.optimizeDeps.esbuildOptions ??= {};
|
||||||
|
cfg.optimizeDeps.esbuildOptions.loader = {
|
||||||
|
...(cfg.optimizeDeps.esbuildOptions.loader || {}),
|
||||||
|
".js": "jsx",
|
||||||
|
".ts": "tsx",
|
||||||
|
};
|
||||||
|
return cfg;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@@ -21,7 +21,6 @@ const config = {
|
|||||||
async viteFinal(cfg) {
|
async viteFinal(cfg) {
|
||||||
// IMPORTANT: Set base path for GitHub Pages sub-path hosting
|
// IMPORTANT: Set base path for GitHub Pages sub-path hosting
|
||||||
cfg.base = "/communityrulestorybook/";
|
cfg.base = "/communityrulestorybook/";
|
||||||
|
|
||||||
// Ensure esbuild treats .js as JSX during dep pre-bundling
|
// Ensure esbuild treats .js as JSX during dep pre-bundling
|
||||||
cfg.optimizeDeps ??= {};
|
cfg.optimizeDeps ??= {};
|
||||||
cfg.optimizeDeps.esbuildOptions ??= {};
|
cfg.optimizeDeps.esbuildOptions ??= {};
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/** @type { import('@storybook/nextjs-vite').StorybookConfig } */
|
||||||
|
const config = {
|
||||||
|
stories: [
|
||||||
|
"../stories/**/*.mdx",
|
||||||
|
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
||||||
|
],
|
||||||
|
addons: [
|
||||||
|
"@chromatic-com/storybook",
|
||||||
|
"@storybook/addon-docs",
|
||||||
|
"@storybook/addon-onboarding",
|
||||||
|
"@storybook/addon-a11y",
|
||||||
|
"@storybook/addon-vitest",
|
||||||
|
],
|
||||||
|
framework: {
|
||||||
|
name: "@storybook/nextjs-vite",
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
staticDirs: ["../public"],
|
||||||
|
async viteFinal(cfg) {
|
||||||
|
// Ensure esbuild treats .js as JSX during dep pre-bundling
|
||||||
|
cfg.optimizeDeps ??= {};
|
||||||
|
cfg.optimizeDeps.esbuildOptions ??= {};
|
||||||
|
cfg.optimizeDeps.esbuildOptions.loader = {
|
||||||
|
...(cfg.optimizeDeps.esbuildOptions.loader || {}),
|
||||||
|
".js": "jsx",
|
||||||
|
".ts": "tsx",
|
||||||
|
};
|
||||||
|
return cfg;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import "../app/globals.css";
|
||||||
|
|
||||||
|
/** @type { import('@storybook/react').Preview } */
|
||||||
|
const preview = {
|
||||||
|
parameters: {
|
||||||
|
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/i,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default preview;
|
||||||
+2
-49
@@ -1,62 +1,15 @@
|
|||||||
import "../app/globals.css";
|
import "../app/globals.css";
|
||||||
|
|
||||||
/** @type { import('@storybook/nextjs-vite').Preview } */
|
/** @type { import('@storybook/react').Preview } */
|
||||||
const preview = {
|
const preview = {
|
||||||
parameters: {
|
parameters: {
|
||||||
nextjs: { appDirectory: true },
|
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||||
controls: {
|
controls: {
|
||||||
matchers: {
|
matchers: {
|
||||||
color: /(background|color)$/i,
|
color: /(background|color)$/i,
|
||||||
date: /Date$/i,
|
date: /Date$/i,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
a11y: {
|
|
||||||
// 'todo' - show a11y violations in the test UI only
|
|
||||||
// 'error' - fail CI on a11y violations
|
|
||||||
// 'off' - skip a11y checks entirely
|
|
||||||
test: "todo",
|
|
||||||
},
|
|
||||||
|
|
||||||
backgrounds: {
|
|
||||||
default: "dark",
|
|
||||||
values: [
|
|
||||||
{
|
|
||||||
name: "dark",
|
|
||||||
value: "#000000",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "light",
|
|
||||||
value: "#ffffff",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
|
|
||||||
viewport: {
|
|
||||||
defaultViewport: "md",
|
|
||||||
viewports: {
|
|
||||||
xsm: {
|
|
||||||
name: "XSmall (≤429px)",
|
|
||||||
styles: { width: "429px", height: "800px" },
|
|
||||||
},
|
|
||||||
sm: {
|
|
||||||
name: "Small (≥430px)",
|
|
||||||
styles: { width: "430px", height: "800px" },
|
|
||||||
},
|
|
||||||
md: {
|
|
||||||
name: "Medium (≥640px)",
|
|
||||||
styles: { width: "640px", height: "800px" },
|
|
||||||
},
|
|
||||||
lg: {
|
|
||||||
name: "Large (≥1024px)",
|
|
||||||
styles: { width: "1024px", height: "800px" },
|
|
||||||
},
|
|
||||||
xl: {
|
|
||||||
name: "XLarge (≥1440px)",
|
|
||||||
styles: { width: "1440px", height: "900px" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import "../app/globals.css";
|
||||||
|
|
||||||
|
/** @type { import('@storybook/react').Preview } */
|
||||||
|
const preview = {
|
||||||
|
parameters: {
|
||||||
|
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/i,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default preview;
|
||||||
@@ -1,36 +1,79 @@
|
|||||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
# Community Rule
|
||||||
|
|
||||||
|
A Next.js application for community decision-making and governance documentation.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
First, run the development server:
|
Run the development server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
npm run dev
|
||||||
# or
|
|
||||||
yarn dev
|
|
||||||
# or
|
|
||||||
pnpm dev
|
|
||||||
# or
|
|
||||||
bun dev
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
|
## Storybook Development
|
||||||
|
|
||||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
This project includes Storybook for component development and documentation. The setup supports both local development and GitHub Pages deployment.
|
||||||
|
|
||||||
## Learn More
|
### Local Development
|
||||||
|
|
||||||
To learn more about Next.js, take a look at the following resources:
|
For local Storybook development (no base path):
|
||||||
|
|
||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
```bash
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
npm run storybook:local
|
||||||
|
```
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
This will:
|
||||||
|
|
||||||
## Deploy on Vercel
|
- Copy local configuration files (without GitHub Pages base path)
|
||||||
|
- Start Storybook at `http://localhost:6006`
|
||||||
|
- Ignore configuration changes in git
|
||||||
|
|
||||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
### Production Deployment
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
When ready to deploy to GitHub Pages:
|
||||||
|
|
||||||
|
1. **Restore GitHub Pages configuration:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run storybook:restore
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Build Storybook:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build-storybook
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Deploy to GitHub Pages repository:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy the build to your GitHub Pages repository
|
||||||
|
cp -r storybook-static/* /path/to/communityrulestorybook/
|
||||||
|
|
||||||
|
# Or if you have it as a git submodule:
|
||||||
|
cp -r storybook-static/* communityrulestorybook/
|
||||||
|
cd communityrulestorybook
|
||||||
|
git add .
|
||||||
|
git commit -m "Update Storybook build"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Switching Between Configurations
|
||||||
|
|
||||||
|
- **Local Development:** `npm run storybook:local`
|
||||||
|
- **Production Build:** `npm run storybook:restore` then `npm run build-storybook`
|
||||||
|
- **Back to Local:** `npm run storybook:local`
|
||||||
|
|
||||||
|
The gitignore is configured to prevent configuration file changes from triggering git changes during local development.
|
||||||
|
|
||||||
|
### Available Scripts
|
||||||
|
|
||||||
|
- `npm run dev` - Start Next.js development server
|
||||||
|
- `npm run build` - Build Next.js application for production
|
||||||
|
- `npm run start` - Start Next.js production server
|
||||||
|
- `npm run storybook:local` - Start Storybook with local configuration
|
||||||
|
- `npm run storybook:restore` - Restore GitHub Pages configuration
|
||||||
|
- `npm run build-storybook` - Build Storybook for production
|
||||||
|
- `npm run storybook` - Start Storybook with current configuration
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
const LogoWall = ({ logos = [] }) => {
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
|
// Default logos if none provided - ordered for mobile (3 rows × 2 columns)
|
||||||
|
const defaultLogos = [
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_FoodNotBombs.png",
|
||||||
|
alt: "Food Not Bombs",
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_StartCOOP.png",
|
||||||
|
alt: "Start COOP",
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_Metagov.png",
|
||||||
|
alt: "Metagov",
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_OpenCivics.png",
|
||||||
|
alt: "Open Civics",
|
||||||
|
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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_MutualAidCO.png",
|
||||||
|
alt: "Mutual Aid CO",
|
||||||
|
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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_CUBoulder.png",
|
||||||
|
alt: "CU Boulder",
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<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)]">
|
||||||
|
{/* Label */}
|
||||||
|
<p className="font-inter font-medium text-[10px] leading-[12px] xl:text-[14px] xl:leading-[12px] uppercase text-[var(--color-content-default-secondary)] text-center">
|
||||||
|
Trusted by leading cooperators
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Logo Grid Container */}
|
||||||
|
<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)]">
|
||||||
|
{displayLogos.map((logo, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`flex items-center justify-center transition-opacity duration-500 hover:opacity-100 ${
|
||||||
|
logo.order || ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={logo.src}
|
||||||
|
alt={logo.alt}
|
||||||
|
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
|
||||||
|
width={0}
|
||||||
|
height={0}
|
||||||
|
sizes="100vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LogoWall;
|
||||||
+1
-1
@@ -11,7 +11,7 @@ const inter = Inter({
|
|||||||
|
|
||||||
const bricolageGrotesque = Bricolage_Grotesque({
|
const bricolageGrotesque = Bricolage_Grotesque({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
weight: ["400"],
|
weight: ["400", "500"],
|
||||||
variable: "--font-bricolage-grotesque",
|
variable: "--font-bricolage-grotesque",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import NumberedCards from "./components/NumberedCards";
|
import NumberedCards from "./components/NumberedCards";
|
||||||
import HeroBanner from "./components/HeroBanner";
|
import HeroBanner from "./components/HeroBanner";
|
||||||
|
import LogoWall from "./components/LogoWall";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const heroBannerData = {
|
const heroBannerData = {
|
||||||
@@ -36,6 +37,7 @@ export default function Page() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<HeroBanner {...heroBannerData} />
|
<HeroBanner {...heroBannerData} />
|
||||||
|
<LogoWall />
|
||||||
<NumberedCards {...numberedCardsData} />
|
<NumberedCards {...numberedCardsData} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -9,6 +9,8 @@
|
|||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"postinstall": "npm rebuild lightningcss",
|
"postinstall": "npm rebuild lightningcss",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
|
"storybook:local": "cp .storybook/main.local.js .storybook/main.js && cp .storybook/preview.local.js .storybook/preview.js && storybook dev -p 6006",
|
||||||
|
"storybook:restore": "cp .storybook/main.github.js .storybook/main.js && cp .storybook/preview.github.js .storybook/preview.js",
|
||||||
"build-storybook": "storybook build"
|
"build-storybook": "storybook build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -31,7 +33,7 @@
|
|||||||
"@vitest/browser": "^3.2.4",
|
"@vitest/browser": "^3.2.4",
|
||||||
"@vitest/coverage-v8": "^3.2.4",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.2.4",
|
"eslint-config-next": "15.2.0",
|
||||||
"eslint-plugin-storybook": "^9.1.2",
|
"eslint-plugin-storybook": "^9.1.2",
|
||||||
"playwright": "^1.54.2",
|
"playwright": "^1.54.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,83 @@
|
|||||||
|
import LogoWall from "../app/components/LogoWall";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/LogoWall",
|
||||||
|
component: LogoWall,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: `A responsive logo wall component that displays partner/sponsor logos in a grid layout. Features responsive breakpoints with different layouts and sizing for mobile, tablet, and desktop views.
|
||||||
|
|
||||||
|
## Responsive Behavior
|
||||||
|
|
||||||
|
- **Mobile**: 3 rows × 2 columns grid with 32px gaps
|
||||||
|
- **SM**: 2 rows × 3 columns grid with 48px row gap and 32px column gap
|
||||||
|
- **MD**: Single row with space-between layout and 24px gap between text and logos
|
||||||
|
- **LG**: Larger logo sizes and 64px horizontal padding
|
||||||
|
- **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
|
||||||
|
|
||||||
|
- **logos** (optional): Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos.
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Custom Logos
|
||||||
|
\`\`\`jsx
|
||||||
|
<LogoWall
|
||||||
|
logos={[
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_CUBoulder.png",
|
||||||
|
alt: "CU Boulder",
|
||||||
|
size: "h-10 lg:h-12 xl:h-[60px]",
|
||||||
|
order: "order-1 sm:order-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "assets/Section/Logo_FoodNotBombs.png",
|
||||||
|
alt: "Food Not Bombs",
|
||||||
|
size: "h-11 lg:h-14 xl:h-[70px]",
|
||||||
|
order: "order-2 sm:order-1"
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Empty State
|
||||||
|
\`\`\`jsx
|
||||||
|
<LogoWall logos={[]} />
|
||||||
|
\`\`\`
|
||||||
|
This will fall back to the default partner logos.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
argTypes: {
|
||||||
|
logos: {
|
||||||
|
control: "object",
|
||||||
|
description:
|
||||||
|
"Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {},
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
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. Features smooth fade-in animations and hover interactions.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user