diff --git a/components/topbar.tsx b/components/topbar.tsx new file mode 100644 index 0000000..fe701ff --- /dev/null +++ b/components/topbar.tsx @@ -0,0 +1,49 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { InfiniteMovingCards } from "./ui/infinite-moving-cards"; + +export function Topbar() { + return ( +
+ +
+ ); +} + +const testimonials = [ + { + messages: 2110, + chain: "ICON", + role: "src", + }, + { + messages: 1926, + chain: "ICON", + role: "dest", + }, + { + messages: 1092, + chain: "Archway", + role: "src", + }, + { + messages: 1292, + chain: "Archway", + role: "dest", + }, + { + messages: 78, + chain: "Injective", + role: "src", + }, + { + messages: 54, + chain: "Injective", + role: "dest", + }, +]; diff --git a/components/ui/infinite-moving-cards.tsx b/components/ui/infinite-moving-cards.tsx new file mode 100644 index 0000000..cce8f89 --- /dev/null +++ b/components/ui/infinite-moving-cards.tsx @@ -0,0 +1,118 @@ +"use client"; + +import { cn } from "../../lib/utils/cn"; +import React, { useEffect, useState } from "react"; +import Image from "next/image"; +import { it } from "node:test"; + +export const InfiniteMovingCards = ({ + items, + direction = "left", + speed = "fast", + pauseOnHover = true, + className, +}: { + items: { + messages: number; + chain: string; + role: string; + }[]; + direction?: "left" | "right"; + speed?: "fast" | "normal" | "slow"; + pauseOnHover?: boolean; + className?: string; +}) => { + const containerRef = React.useRef(null); + const scrollerRef = React.useRef(null); + + useEffect(() => { + addAnimation(); + }, []); + const [start, setStart] = useState(false); + function addAnimation() { + if (containerRef.current && scrollerRef.current) { + const scrollerContent = Array.from(scrollerRef.current.children); + + scrollerContent.forEach((item) => { + const duplicatedItem = item.cloneNode(true); + if (scrollerRef.current) { + scrollerRef.current.appendChild(duplicatedItem); + } + }); + + getDirection(); + getSpeed(); + setStart(true); + } + } + const getDirection = () => { + if (containerRef.current) { + if (direction === "left") { + containerRef.current.style.setProperty( + "--animation-direction", + "forwards" + ); + } else { + containerRef.current.style.setProperty( + "--animation-direction", + "reverse" + ); + } + } + }; + const getSpeed = () => { + if (containerRef.current) { + if (speed === "fast") { + containerRef.current.style.setProperty("--animation-duration", "20s"); + } else if (speed === "normal") { + containerRef.current.style.setProperty("--animation-duration", "40s"); + } else { + containerRef.current.style.setProperty("--animation-duration", "80s"); + } + } + }; + return ( +
+
    + {items.map((item, idx) => ( +
  • +
    + +
    + {item.messages} messages {item.role === "src" ? "have left" : "have arrived on"} +
    + {item.chain} + + {item.chain} + +
    +
    + + + +
    +
  • + ))} +
+
+ ); +}; diff --git a/lib/utils/cn.ts b/lib/utils/cn.ts new file mode 100644 index 0000000..cec6ac9 --- /dev/null +++ b/lib/utils/cn.ts @@ -0,0 +1,6 @@ +import { ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/package-lock.json b/package-lock.json index 324cc96..e9cb1d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,15 +12,18 @@ "@mendable/search": "^0.0.183", "@tailwindcss/typography": "^0.5.9", "@vercel/analytics": "^1.0.1", + "clsx": "^2.1.1", "cookies-next": "^2.1.2", "feed": "^4.2.2", + "framer-motion": "^11.2.6", "globby": "^11.0.1", "next": "^13.0.6", "nextra": "^2.13.2", "nextra-theme-docs": "^2.13.2", "react": "^18.2.0", "react-dom": "^18.2.0", - "sharp": "^0.33.2" + "sharp": "^0.33.2", + "tailwind-merge": "^2.3.0" }, "devDependencies": { "@types/node": "18.11.10", @@ -45,9 +48,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.10.tgz", - "integrity": "sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", + "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1787,9 +1790,9 @@ } }, "node_modules/clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } @@ -3027,6 +3030,30 @@ "url": "https://www.patreon.com/infusion" } }, + "node_modules/framer-motion": { + "version": "11.2.6", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.6.tgz", + "integrity": "sha512-XUrjjBt57e5YoHQtjwc3eNchFBuHvIgN/cS8SC4oIaAn2J/0+bLanUxXizidJKZVeHJam/JrmMnPRjYMglVn5g==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7375,6 +7402,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwind-merge": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.3.0.tgz", + "integrity": "sha512-vkYrLpIP+lgR0tQCG6AP7zZXCTLc1Lnv/CCRT3BqJ9CZ3ui2++GPaGb1x/ILsINIMSYqqvrpqjUFsMNLlW99EA==", + "dependencies": { + "@babel/runtime": "^7.24.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", diff --git a/package.json b/package.json index 4c3bd14..1f0539f 100644 --- a/package.json +++ b/package.json @@ -21,15 +21,18 @@ "@mendable/search": "^0.0.183", "@tailwindcss/typography": "^0.5.9", "@vercel/analytics": "^1.0.1", + "clsx": "^2.1.1", "cookies-next": "^2.1.2", "feed": "^4.2.2", + "framer-motion": "^11.2.6", "globby": "^11.0.1", "next": "^13.0.6", "nextra": "^2.13.2", "nextra-theme-docs": "^2.13.2", "react": "^18.2.0", "react-dom": "^18.2.0", - "sharp": "^0.33.2" + "sharp": "^0.33.2", + "tailwind-merge": "^2.3.0" }, "devDependencies": { "@types/node": "18.11.10", diff --git a/pages/_app.js b/pages/_app.js index 876edec..362b94e 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -3,11 +3,13 @@ import '../globals.css'; import { Analytics } from '@vercel/analytics/react'; import Layout from '../components/layout'; import { MendableChatBubble } from "@mendable/search"; +import { Topbar } from '../components/topbar'; function MyApp({ Component, pageProps }) { return (
+ diff --git a/pages/index.mdx b/pages/index.mdx index 7900164..53cde6e 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -19,6 +19,7 @@ export const Homepage = () => { return (
+

Full Cross-Chain

Framework

diff --git a/tailwind.config.js b/tailwind.config.ts similarity index 65% rename from tailwind.config.js rename to tailwind.config.ts index 85fd53e..4c64c86 100644 --- a/tailwind.config.js +++ b/tailwind.config.ts @@ -1,3 +1,8 @@ +import defaultTheme from "tailwindcss/defaultTheme"; + +import colors from "tailwindcss/colors"; +import { default as flattenColorPalette } from "tailwindcss/lib/util/flattenColorPalette"; + /** @type {import('tailwindcss').Config} */ module.exports = { darkMode: 'class', @@ -48,14 +53,30 @@ module.exports = { '0%': { opacity: '1' }, '100%': { opacity: '0' }, }, + scroll: { + to: { + transform: "translate(calc(-50% - 0.5rem))", + }, + }, }, animation: { fadeIn: 'fadeIn 0.3s ease-in-out forwards', fadeOut: 'fadeOut 0.3s ease-in-out forwards', + scroll: "scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite", }, }, }, - plugins: [], + plugins: [addVariablesForColors], mode: 'jit', } +function addVariablesForColors({ addBase, theme }: any) { + let allColors = flattenColorPalette(theme("colors")); + let newVars = Object.fromEntries( + Object.entries(allColors).map(([key, val]) => [`--${key}`, val]) + ); + + addBase({ + ":root": newVars, + }); +}