From c63b15d5d6e0f568806a73fab90ba118fc09e009 Mon Sep 17 00:00:00 2001 From: ethanglide Date: Sun, 2 Nov 2025 17:26:51 -0500 Subject: [PATCH] Add partners section --- package-lock.json | 20 +++++ package.json | 2 + src/app.tsx | 31 ++++---- src/components/navbar/navbar.tsx | 15 ++++ .../partners-marquee/partners-marquee.tsx | 48 ++++++++++++ src/components/ui/marquee.tsx | 74 +++++++++++++++++++ src/globals.d.ts | 3 + src/index.css | 32 ++++++++ src/main.tsx | 2 + 9 files changed, 212 insertions(+), 15 deletions(-) create mode 100644 src/components/navbar/navbar.tsx create mode 100644 src/components/partners-marquee/partners-marquee.tsx create mode 100644 src/components/ui/marquee.tsx create mode 100644 src/globals.d.ts diff --git a/package-lock.json b/package-lock.json index a0b9c28..d807d4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "bcdigital-challenge", "version": "0.0.0", "dependencies": { + "@fontsource-variable/merriweather": "^5.2.6", + "@fontsource/alex-brush": "^5.2.8", "@radix-ui/react-slot": "^1.2.3", "@tailwindcss/vite": "^4.1.16", "class-variance-authority": "^0.7.1", @@ -894,6 +896,24 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fontsource-variable/merriweather": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource-variable/merriweather/-/merriweather-5.2.6.tgz", + "integrity": "sha512-bHCDt99f/M48eUcFA86uh/oSPyn8r/ZxXR9l578wqLvjTwDzXx8A/XOAI05WfJ3LnH1rDufQX5RJwiZtbXUCkw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/alex-brush": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/alex-brush/-/alex-brush-5.2.8.tgz", + "integrity": "sha512-ShSsZkWN7O6H6x56Jc2c1d+YXDTCGIRBvLKiVIa7r3tyg6Ern56fN2xriAjNNBmvn37Y96XCkavrpERm/IRCEQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", diff --git a/package.json b/package.json index 09c430c..fee63ec 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "**/*": "prettier --write --ignore-unknown" }, "dependencies": { + "@fontsource-variable/merriweather": "^5.2.6", + "@fontsource/alex-brush": "^5.2.8", "@radix-ui/react-slot": "^1.2.3", "@tailwindcss/vite": "^4.1.16", "class-variance-authority": "^0.7.1", diff --git a/src/app.tsx b/src/app.tsx index 7fd6599..de0a002 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,3 +1,5 @@ +import { Navbar } from "./components/navbar/navbar"; +import { PartnersMarquee } from "./components/partners-marquee/partners-marquee"; import { AuroraText } from "./components/ui/aurora-text"; import { DotPattern } from "./components/ui/dot-pattern"; import { RainbowButton } from "./components/ui/rainbow-button"; @@ -5,18 +7,8 @@ import { TextAnimate } from "./components/ui/text-animate"; export function App() { return ( -
- +
+
- - Section 2 coming soon... +
+ + Our Partners + + + {/* Spacer for DotPattern */} +
diff --git a/src/components/navbar/navbar.tsx b/src/components/navbar/navbar.tsx new file mode 100644 index 0000000..99a2c77 --- /dev/null +++ b/src/components/navbar/navbar.tsx @@ -0,0 +1,15 @@ +export function Navbar() { + return ( + + ); +} diff --git a/src/components/partners-marquee/partners-marquee.tsx b/src/components/partners-marquee/partners-marquee.tsx new file mode 100644 index 0000000..9438620 --- /dev/null +++ b/src/components/partners-marquee/partners-marquee.tsx @@ -0,0 +1,48 @@ +import { Marquee } from "../ui/marquee"; + +export function PartnersMarquee() { + const partners = [ + { + name: "Conductor Orchestra", + logo: "https://t3.ftcdn.net/jpg/05/71/43/76/360_F_571437617_ZNppyF5qpbJn9dYifhjWEQkgjbZNBXP9.jpg", + }, + { + name: "Chicago Symphony Orchestra", + logo: "https://upload.wikimedia.org/wikipedia/commons/1/1f/Wiki-CSO_logo2024_stacked-whitebg.png", + }, + { + name: "Toronto Symphony Orchestra", + logo: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSfThsC3B2BGCKjnyr_IMiJmp-0jI9I9blZrw&s", + }, + { + name: "Los Angeles Chamber Orchestra", + logo: "https://admin.itsnicethat.com/images/TOHFR2DIydoDTaGM6NbfVRFZfJU=/168760/format-webp%7Cwidth-2880/5d947a047fa44cbd2200fd0f.jpg", + }, + { + name: "Richmond Symphony Orchestra", + logo: "https://richmondsymphony.org/wp-content/uploads/2021/07/rso-logo-2023.png", + }, + ]; + + return ( +
+ + {partners.map((partner) => ( +
+ {`${partner.name} +

{partner.name}

+
+ ))} +
+
+
+
+ ); +} diff --git a/src/components/ui/marquee.tsx b/src/components/ui/marquee.tsx new file mode 100644 index 0000000..0bd11ec --- /dev/null +++ b/src/components/ui/marquee.tsx @@ -0,0 +1,74 @@ +import type { ComponentPropsWithoutRef } from "react"; + +import { cn } from "@/lib/utils"; + +interface MarqueeProps extends ComponentPropsWithoutRef<"div"> { + /** + * Optional CSS class name to apply custom styles + */ + className?: string; + /** + * Whether to reverse the animation direction + * @default false + */ + reverse?: boolean; + /** + * Whether to pause the animation on hover + * @default false + */ + pauseOnHover?: boolean; + /** + * Content to be displayed in the marquee + */ + children: React.ReactNode; + /** + * Whether to animate vertically instead of horizontally + * @default false + */ + vertical?: boolean; + /** + * Number of times to repeat the content + * @default 4 + */ + repeat?: number; +} + +export function Marquee({ + className, + reverse = false, + pauseOnHover = false, + children, + vertical = false, + repeat = 4, + ...props +}: MarqueeProps) { + return ( +
+ {Array(repeat) + .fill(0) + .map((_, i) => ( +
+ {children} +
+ ))} +
+ ); +} diff --git a/src/globals.d.ts b/src/globals.d.ts new file mode 100644 index 0000000..e583f13 --- /dev/null +++ b/src/globals.d.ts @@ -0,0 +1,3 @@ +declare module "*.css"; +declare module "@fontsource/*" {} +declare module "@fontsource-variable/*" {} diff --git a/src/index.css b/src/index.css index 58878da..83a1246 100644 --- a/src/index.css +++ b/src/index.css @@ -4,6 +4,9 @@ @custom-variant dark (&:is(.dark *)); @theme inline { + --font-alex-brush: "Alex Brush", cursive; + --font-merriweather: "Merriweather Variable", serif; + --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); @@ -40,42 +43,70 @@ --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); --animate-aurora: aurora 8s ease-in-out infinite alternate; + @keyframes aurora { 0% { background-position: 0% 50%; transform: rotate(-5deg) scale(0.9); } + 25% { background-position: 50% 100%; transform: rotate(5deg) scale(1.1); } + 50% { background-position: 100% 50%; transform: rotate(-3deg) scale(0.95); } + 75% { background-position: 50% 0%; transform: rotate(3deg) scale(1.05); } + 100% { background-position: 0% 50%; transform: rotate(-5deg) scale(0.9); } } + --animate-rainbow: rainbow var(--speed, 2s) infinite linear; --color-color-5: var(--color-5); --color-color-4: var(--color-4); --color-color-3: var(--color-3); --color-color-2: var(--color-2); --color-color-1: var(--color-1); + @keyframes rainbow { 0% { background-position: 0%; } + 100% { background-position: 200%; } } + + --animate-marquee: marquee var(--duration) infinite linear; + + --animate-marquee-vertical: marquee-vertical var(--duration) linear infinite; + @keyframes marquee { + from { + transform: translateX(0); + } + to { + transform: translateX(calc(-100% - var(--gap))); + } + } + @keyframes marquee-vertical { + from { + transform: translateY(0); + } + to { + transform: translateY(calc(-100% - var(--gap))); + } + } } :root { @@ -161,6 +192,7 @@ * { @apply border-border outline-ring/50; } + body { @apply bg-background text-foreground; } diff --git a/src/main.tsx b/src/main.tsx index b160e7a..79328ed 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,6 +2,8 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; import { App } from "./app"; +import "@fontsource/alex-brush"; +import "@fontsource-variable/merriweather"; createRoot(document.getElementById("root")!).render(