From 1c15bd8a6e98102a51415d05ef582a9841ee8ee2 Mon Sep 17 00:00:00 2001 From: Ajay Dhangar Date: Sat, 12 Sep 2026 23:29:51 +0530 Subject: [PATCH 1/2] update UI of home page --- .../ContributorList/contributors.css | 968 ++++++++++++++++-- src/components/ContributorList/index.tsx | 343 +++++-- .../HomePage/Community/LandingCommunity.css | 933 +++++++++++++++-- src/components/HomePage/Community/index.tsx | 522 ++++++++-- src/components/HomePage/FinalCTA/FinalCTA.css | 414 ++++++++ src/components/HomePage/FinalCTA/index.tsx | 102 ++ src/components/HomePage/ResourcesSection.tsx | 197 ---- .../HomePage/ResourcesSection/index.tsx | 487 +++++++++ .../HomePage/ResourcesSection/style.css | 798 +++++++++++++++ src/components/HomePage/TweetsSection.tsx | 74 -- .../HomePage/TweetsSection/TweetsSection.css | 586 +++++++++++ .../HomePage/TweetsSection/index.tsx | 173 ++++ .../HomePage/WhyChooseCodeHarbor/index.tsx | 292 ++++++ .../why-choose-codeharbor.css | 761 ++++++++++++++ .../HomePage/organizations/index.tsx | 186 +++- .../HomePage/organizations/style.css | 761 +++++++++++++- src/components/HomePage/style.css | 228 ----- src/components/Tweet/Tweet.css | 371 +++++++ src/components/Tweet/index.tsx | 117 ++- src/components/WhyChooseCodeHarbor.tsx | 113 -- src/context/CommunityStats/index.tsx | 310 ++++-- src/pages/index.tsx | 44 +- 22 files changed, 7662 insertions(+), 1118 deletions(-) create mode 100644 src/components/HomePage/FinalCTA/FinalCTA.css create mode 100644 src/components/HomePage/FinalCTA/index.tsx delete mode 100644 src/components/HomePage/ResourcesSection.tsx create mode 100644 src/components/HomePage/ResourcesSection/index.tsx create mode 100644 src/components/HomePage/ResourcesSection/style.css delete mode 100644 src/components/HomePage/TweetsSection.tsx create mode 100644 src/components/HomePage/TweetsSection/TweetsSection.css create mode 100644 src/components/HomePage/TweetsSection/index.tsx create mode 100644 src/components/HomePage/WhyChooseCodeHarbor/index.tsx create mode 100644 src/components/HomePage/WhyChooseCodeHarbor/why-choose-codeharbor.css delete mode 100644 src/components/HomePage/style.css create mode 100644 src/components/Tweet/Tweet.css delete mode 100644 src/components/WhyChooseCodeHarbor.tsx diff --git a/src/components/ContributorList/contributors.css b/src/components/ContributorList/contributors.css index 74e997063..e5758c298 100644 --- a/src/components/ContributorList/contributors.css +++ b/src/components/ContributorList/contributors.css @@ -1,106 +1,952 @@ -.grid { - display: grid; - grid-template-rows: repeat(2, 1fr); +/* ========================================================= + CodeHarborHub + Open Source Contributors Section + ========================================================= */ + +.contributors-section { + --contributors-primary: var(--ifm-color-primary); + --contributors-primary-dark: var(--ifm-color-primary-dark); + + --contributors-background: var(--ifm-background-color); + --contributors-surface: var(--ifm-background-surface-color); + + --contributors-heading: var(--ifm-heading-color); + --contributors-text: var(--ifm-font-color-base); + --contributors-muted: var(--ifm-color-emphasis-700); + + --contributors-border: var(--ifm-color-emphasis-200); + + position: relative; + + width: 100%; + + padding: 2rem 0; + overflow: hidden; - place-items: center; + isolation: isolate; } -.grid-item { - display: flex; - justify-content: center; +/* ========================================================= + Background + ========================================================= */ + +.contributors-section::before { + content: ""; + + position: absolute; + inset: 0; + + z-index: -3; + + pointer-events: none; + + background: + radial-gradient( + circle at 12% 20%, + color-mix( + in srgb, + var(--contributors-primary) 10%, + transparent + ), + transparent 28% + ), + radial-gradient( + circle at 88% 75%, + color-mix( + in srgb, + var(--contributors-primary) 8%, + transparent + ), + transparent 30% + ); +} + +.contributors-section::after { + content: ""; + + position: absolute; + inset: 0; + + z-index: -2; + + pointer-events: none; + + opacity: 0.2; + + background-image: + linear-gradient( + color-mix( + in srgb, + var(--contributors-primary) 7%, + transparent + ) 1px, + transparent 1px + ), + linear-gradient( + 90deg, + color-mix( + in srgb, + var(--contributors-primary) 7%, + transparent + ) 1px, + transparent 1px + ); + + background-size: 42px 42px; + + mask-image: linear-gradient( + to bottom, + transparent, + black 20%, + black 80%, + transparent + ); +} + +.contributors-section__glow { + position: absolute; + + width: 240px; + height: 240px; + + border-radius: 50%; + + background: color-mix( + in srgb, + var(--contributors-primary) 9%, + transparent + ); + + filter: blur(80px); + + pointer-events: none; + + z-index: -1; +} + +.contributors-section__glow--one { + top: 10%; + left: -120px; +} + +.contributors-section__glow--two { + right: -120px; + bottom: 5%; +} + +/* ========================================================= + Container + ========================================================= */ + +.contributors-section__container { + /* width: min(1180px, 100%); */ + margin: 0 1.2rem; +} + +/* ========================================================= + Header + ========================================================= */ + +.contributors-section__header { + max-width: 760px; + + margin: 0 auto 3rem; + + text-align: center; +} + +.contributors-section__eyebrow { + display: inline-flex; align-items: center; - flex-wrap: nowrap; - width: 100%; + + gap: 0.5rem; + + margin-bottom: 1rem; + + padding: 0.45rem 0.8rem; + + border: 1px solid + color-mix( + in srgb, + var(--contributors-primary) 25%, + var(--contributors-border) + ); + + border-radius: 999px; + + color: var(--contributors-primary); + + background: color-mix( + in srgb, + var(--contributors-primary) 7%, + var(--contributors-background) + ); + + font-size: 0.72rem; + font-weight: 800; + + letter-spacing: 0.08em; +} + +.contributors-section__title { + margin: 0; + + color: var(--contributors-heading); + + font-size: clamp(2rem, 4vw, 3.25rem); + + line-height: 1.08; + + font-weight: 800; + + letter-spacing: -0.035em; +} + +.contributors-section__title span { + color: var(--contributors-primary); + + background: linear-gradient( + 120deg, + var(--contributors-primary), + var(--contributors-primary-dark) + ); + + -webkit-background-clip: text; + background-clip: text; + + -webkit-text-fill-color: transparent; +} + +.contributors-section__description { + max-width: 700px; + + margin: 1rem auto 0; + + color: var(--contributors-muted); + + font-size: 1rem; + + line-height: 1.75; +} + +/* ========================================================= + Main Panel + ========================================================= */ + +.contributors-section__panel { + position: relative; + overflow: hidden; + + border: 1px solid var(--contributors-border); + + border-radius: 1.5rem; + + background: + linear-gradient( + 145deg, + color-mix( + in srgb, + var(--contributors-primary) 4%, + var(--contributors-surface) + ), + var(--contributors-surface) + ); + + box-shadow: + 0 20px 60px rgba(0, 0, 0, 0.06), + inset 0 1px 0 rgba(255, 255, 255, 0.05); +} + +.contributors-section__panel::before { + content: ""; + + position: absolute; + + top: 0; + left: 10%; + right: 10%; + + height: 2px; + + border-radius: 0 0 999px 999px; + + background: linear-gradient( + 90deg, + transparent, + var(--contributors-primary), + transparent + ); } -.circle { +/* ========================================================= + Panel Header + ========================================================= */ + +.contributors-section__panel-header { display: flex; - justify-content: center; + align-items: center; - width: 35px; - height: 35px; - border-radius: 50%; - aspect-ratio: 1 / 1; - object-fit: cover; + justify-content: space-between; + + gap: 1.5rem; + + padding: 1.5rem 1.6rem; + + border-bottom: 1px solid var(--contributors-border); } -.circle img { - width: 100%; - height: 100%; - object-fit: cover; - border: 1px solid #ff8800; +.contributors-section__panel-title { + display: flex; + + align-items: center; + + gap: 0.9rem; +} + +.contributors-section__icon { + display: grid; + + place-items: center; + + width: 46px; + height: 46px; + + flex-shrink: 0; + + border-radius: 0.85rem; + + color: var(--contributors-primary); + + background: color-mix( + in srgb, + var(--contributors-primary) 9%, + var(--contributors-background) + ); } -.md-text { - font-size: large; +.contributors-section__panel-title h3 { + margin: 0; + + color: var(--contributors-heading); + + font-size: 1rem; } -.fade-horizontal { - position: relative; - color: black; +.contributors-section__panel-title p { + margin: 0.2rem 0 0; - -webkit-mask-image: linear-gradient( + color: var(--contributors-muted); + + font-size: 0.78rem; +} + +.contributors-section__count { + display: flex; + + align-items: baseline; + + gap: 0.4rem; + + white-space: nowrap; +} + +.contributors-section__count strong { + color: var(--contributors-primary); + + font-size: 1.25rem; +} + +.contributors-section__count span { + color: var(--contributors-muted); + + font-size: 0.72rem; +} + +/* ========================================================= + Contributor Avatar Area + ========================================================= */ + +.contributors-section__avatars { + display: flex; + + flex-direction: column; + + gap: 0.75rem; + + padding: 2rem 1.5rem; + + overflow: hidden; +} + +.contributors-section__avatar-row { + display: flex; + + align-items: center; + justify-content: center; + + gap: 0.35rem; + + width: 100%; + + overflow: hidden; + + mask-image: linear-gradient( to right, transparent, - black 20%, - black 80%, + black 10%, + black 90%, transparent ); - mask-image: linear-gradient( + + -webkit-mask-image: linear-gradient( to right, transparent, - black 20%, - black 80%, + black 10%, + black 90%, transparent ); +} + +/* Alternate subtle movement */ + +.contributors-section__avatar-row:nth-child(1) { + transform: translateX(-2%); +} + +.contributors-section__avatar-row:nth-child(2) { + transform: translateX(2%); +} + +.contributors-section__avatar-row:nth-child(3) { + transform: translateX(-1%); +} + +/* ========================================================= + Avatar + ========================================================= */ + +.contributor-avatar { + position: relative; + + display: block; + + width: 48px; + height: 48px; + + flex: 0 0 48px; + + border-radius: 50%; + + text-decoration: none; + + transition: + transform 180ms ease, + z-index 180ms ease; +} + +.contributor-avatar img { + display: block; + + width: 100%; + height: 100%; + + object-fit: cover; + + border: 2px solid var(--contributors-surface); - -webkit-mask-repeat: no-repeat; - mask-repeat: no-repeat; + border-radius: 50%; + + box-shadow: + 0 0 0 1px + color-mix( + in srgb, + var(--contributors-primary) 25%, + var(--contributors-border) + ), + 0 5px 15px rgba(0, 0, 0, 0.1); + + transition: + transform 180ms ease, + box-shadow 180ms ease; } -.father-flex-div { +.contributor-avatar:hover { + z-index: 10; + + transform: translateY(-6px) scale(1.15); +} + +.contributor-avatar:hover img { + box-shadow: + 0 0 0 2px var(--contributors-primary), + 0 10px 25px + color-mix( + in srgb, + var(--contributors-primary) 20%, + transparent + ); +} + +/* ========================================================= + Avatar Tooltip + ========================================================= */ + +.contributor-avatar__tooltip { + position: absolute; + + left: 50%; + bottom: calc(100% + 10px); + display: flex; + flex-direction: column; + + gap: 0.1rem; + + width: max-content; + + max-width: 180px; + + padding: 0.55rem 0.7rem; + + border: 1px solid var(--contributors-border); + + border-radius: 0.65rem; + + color: var(--contributors-text); + + background: var(--contributors-surface); + + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); + + opacity: 0; + + pointer-events: none; + + transform: + translate(-50%, 5px) + scale(0.96); + + transition: + opacity 160ms ease, + transform 160ms ease; +} + +.contributor-avatar:hover .contributor-avatar__tooltip { + opacity: 1; + + transform: + translate(-50%, 0) + scale(1); +} + +.contributor-avatar__tooltip strong { + color: var(--contributors-heading); + + font-size: 0.75rem; +} + +.contributor-avatar__tooltip small { + color: var(--contributors-muted); + + font-size: 0.65rem; +} + +/* ========================================================= + Loading Skeleton + ========================================================= */ + +.contributors-section__loading { + display: flex; + + flex-wrap: wrap; + justify-content: center; - flex-wrap: nowrap; + + gap: 0.45rem; + + padding: 2rem; +} + +.contributors-section__skeleton { + width: 48px; + height: 48px; + + border-radius: 50%; + + background: linear-gradient( + 90deg, + var(--contributors-border), + color-mix( + in srgb, + var(--contributors-primary) 10%, + var(--contributors-border) + ), + var(--contributors-border) + ); + + background-size: 200% 100%; + + animation: contributors-shimmer 1.5s infinite; +} + +@keyframes contributors-shimmer { + 0% { + background-position: 200% 0; + } + + 100% { + background-position: -200% 0; + } +} + +/* ========================================================= + Error + ========================================================= */ + +.contributors-section__error { + display: flex; + align-items: center; - width: 100%; - /* max-width: 100vw; */ + + gap: 1rem; + + margin: 1.5rem; + + padding: 1.2rem; + + border: 1px solid var(--contributors-border); + + border-radius: 1rem; + + background: var(--contributors-background); +} + +.contributors-section__error-icon { + display: grid; + + place-items: center; + + width: 42px; + height: 42px; + + flex-shrink: 0; + + border-radius: 0.7rem; + + color: var(--contributors-primary); + + background: color-mix( + in srgb, + var(--contributors-primary) 8%, + var(--contributors-surface) + ); } -.intro { - line-height: 1.6; - font-weight: 500; - margin: 1.5rem 1rem; - word-wrap: break-word; +.contributors-section__error strong { + color: var(--contributors-heading); + + font-size: 0.85rem; +} + +.contributors-section__error p { + margin: 0.2rem 0 0; + + color: var(--contributors-muted); + + font-size: 0.75rem; +} + +.contributors-section__error-link { + display: inline-flex; + + align-items: center; + + gap: 0.35rem; + + margin-left: auto; + + color: var(--contributors-primary); + + font-size: 0.78rem; + + font-weight: 700; + + white-space: nowrap; + + text-decoration: none; +} + +/* ========================================================= + Empty + ========================================================= */ + +.contributors-section__empty { + padding: 3rem 1rem; + + color: var(--contributors-muted); + text-align: center; + + font-size: 0.85rem; } -@media (max-width: 700px) { - .circle { - width: 14px; - height: 14px; - } +/* ========================================================= + Panel Footer + ========================================================= */ + +.contributors-section__panel-footer { + display: flex; + + align-items: center; + justify-content: space-between; + + gap: 1.5rem; + + padding: 1.2rem 1.5rem; + + border-top: 1px solid var(--contributors-border); + + background: color-mix( + in srgb, + var(--contributors-primary) 3%, + transparent + ); } -@media (min-width: 701px) and (max-width: 1024px) { - .circle { - width: 17px; - height: 17px; - } +.contributors-section__thanks { + display: flex; + + align-items: flex-start; + + gap: 0.55rem; + + color: var(--contributors-muted); + + font-size: 0.78rem; + + line-height: 1.5; +} + +.contributors-section__thanks svg { + flex-shrink: 0; + + margin-top: 0.1rem; + + color: var(--contributors-primary); +} + +.contributors-section__thanks a { + color: var(--contributors-primary); + + font-weight: 700; + + text-decoration: none; +} + +.contributors-section__thanks a:hover { + text-decoration: underline; +} + +.contributors-section__github-button { + display: inline-flex; + + align-items: center; + justify-content: center; + + gap: 0.45rem; + + flex-shrink: 0; + + padding: 0.7rem 0.9rem; + + border: 1px solid + color-mix( + in srgb, + var(--contributors-primary) 35%, + var(--contributors-border) + ); + + border-radius: 0.7rem; + + color: var(--contributors-primary); + + background: color-mix( + in srgb, + var(--contributors-primary) 6%, + var(--contributors-background) + ); + + font-size: 0.76rem; + + font-weight: 750; + + text-decoration: none; + + transition: + transform 180ms ease, + border-color 180ms ease, + background 180ms ease; } -@media (min-width: 1025px) and (max-width: 1440px) { - .circle { - width: 28px; - height: 28px; +.contributors-section__github-button:hover { + color: var(--contributors-primary); + + transform: translateY(-2px); + + border-color: color-mix( + in srgb, + var(--contributors-primary) 55%, + var(--contributors-border) + ); + + background: color-mix( + in srgb, + var(--contributors-primary) 11%, + var(--contributors-background) + ); +} + +.contributors-section__github-button:focus-visible, +.contributors-section__error-link:focus-visible, +.contributor-avatar:focus-visible { + outline: 3px solid + color-mix( + in srgb, + var(--contributors-primary) 30%, + transparent + ); + + outline-offset: 3px; +} + +/* ========================================================= + Tablet + ========================================================= */ + +@media screen and (max-width: 768px) { + .contributors-section { + padding: 4.5rem 1rem; + } + + .contributors-section__panel-header { + align-items: flex-start; + + flex-direction: column; + } + + .contributors-section__count { + padding-left: 3.35rem; + } + + .contributors-section__avatars { + padding-inline: 0.75rem; + } + + .contributors-section__panel-footer { + align-items: flex-start; + + flex-direction: column; + } + + .contributors-section__github-button { + width: 100%; } } -@media (min-width: 1441px) { - .circle { - width: 31px; - height: 31px; +/* ========================================================= + Mobile + ========================================================= */ + +@media screen and (max-width: 520px) { + .contributors-section { + padding: 3.5rem 0.75rem; + } + + .contributors-section__header { + margin-bottom: 2rem; + } + + .contributors-section__title { + font-size: 2rem; + } + + .contributors-section__description { + font-size: 0.9rem; + } + + .contributors-section__panel { + border-radius: 1.15rem; + } + + .contributors-section__panel-header { + padding: 1.15rem; + } + + .contributors-section__avatars { + gap: 0.55rem; + + padding: 1.5rem 0.25rem; + } + + .contributors-section__avatar-row { + gap: 0.2rem; + } + + .contributor-avatar { + width: 35px; + height: 35px; + + flex-basis: 35px; + } + + .contributor-avatar img { + border-width: 1.5px; + } + + .contributor-avatar__tooltip { + display: none; + } + + .contributors-section__error { + align-items: flex-start; + + flex-direction: column; + + margin: 1rem; + } + + .contributors-section__error-link { + margin-left: 0; + } + + .contributors-section__thanks { + font-size: 0.74rem; } } + +/* ========================================================= + Reduced Motion + ========================================================= */ + +@media (prefers-reduced-motion: reduce) { + .contributors-section__skeleton { + animation: none; + } + + .contributor-avatar, + .contributor-avatar img, + .contributor-avatar__tooltip, + .contributors-section__github-button { + transition: none; + } + + .contributor-avatar:hover { + transform: none; + } + + .contributors-section__github-button:hover { + transform: none; + } +} \ No newline at end of file diff --git a/src/components/ContributorList/index.tsx b/src/components/ContributorList/index.tsx index a5c9cbf4d..2e35a60c1 100644 --- a/src/components/ContributorList/index.tsx +++ b/src/components/ContributorList/index.tsx @@ -1,116 +1,325 @@ -import React, { useEffect, useState } from "react"; -import { Octokit } from "@octokit/rest"; -import "./contributors.css"; +import React, { useEffect, useMemo, useState } from "react"; import Translate from "@docusaurus/Translate"; +import { + ArrowUpRight, + GitBranch, + Github, + Heart, + Users, +} from "lucide-react"; + +import "./contributors.css"; type Contributor = { login: string; contributions: number; html_url: string; avatar_url: string; + type?: string; }; -// per page max 99 items -type ContibutorProps = { - contributorsPerPage: number; - contributorsIgnore: Array; +type ContributorProps = { + contributorsPerPage?: number; + contributorsIgnore?: string[]; }; -const defaultIgnoreContributors: string[] = [ +const DEFAULT_IGNORE_CONTRIBUTORS = [ "restyled-commits", "dependabot", "renovate", ]; -const ContributorList: React.FC = ({ + +const GITHUB_OWNER = "CodeHarborHub"; +const GITHUB_REPO = "codeharborhub.github.io"; + +const ContributorList: React.FC = ({ contributorsPerPage = 99, - contributorsIgnore = defaultIgnoreContributors, -}: ContibutorProps) => { - const [contributors, setContributors] = useState([]); + contributorsIgnore = DEFAULT_IGNORE_CONTRIBUTORS, +}) => { + const [contributors, setContributors] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + useEffect(() => { + const controller = new AbortController(); + const fetchContributors = async () => { - const octokit = new Octokit(); try { - const response = await octokit.request( - `GET /repos/{owner}/{repo}/contributors?per_page=${contributorsPerPage + contributorsIgnore.length}`, + setLoading(true); + setError(false); + + const perPage = Math.min( + Math.max(contributorsPerPage + contributorsIgnore.length, 1), + 100, + ); + + const response = await fetch( + `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/contributors?per_page=${perPage}`, { - owner: "codeharborhub", - repo: "codeharborhub.github.io", headers: { + Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28", }, + signal: controller.signal, }, ); - const filteredContributors = response.data.filter( - (contribuidor: Contributor) => - !contributorsIgnore.some((element) => - contribuidor.login.toLowerCase().includes(element.toLowerCase()), - ), + + if (!response.ok) { + throw new Error(`GitHub API error: ${response.status}`); + } + + const data: Contributor[] = await response.json(); + + const ignored = contributorsIgnore.map((login) => + login.toLowerCase(), ); - setContributors(filteredContributors); - } catch (error) { - console.error("Error fetching contributors:", error); + const filtered = data + .filter((contributor) => { + const login = contributor.login.toLowerCase(); + + return ( + !ignored.includes(login) && + !ignored.some((ignoredLogin) => + login.includes(ignoredLogin), + ) + ); + }) + .slice(0, contributorsPerPage); + + setContributors(filtered); + } catch (fetchError) { + if ( + fetchError instanceof DOMException && + fetchError.name === "AbortError" + ) { + return; + } + + console.error("Failed to fetch CodeHarborHub contributors:", fetchError); + setError(true); + } finally { + if (!controller.signal.aborted) { + setLoading(false); + } } }; fetchContributors(); - }, []); + + return () => { + controller.abort(); + }; + }, [contributorsPerPage, contributorsIgnore]); + + const contributorRows = useMemo(() => { + const rows: Contributor[][] = [[], [], []]; + + contributors.forEach((contributor, index) => { + rows[index % 3].push(contributor); + }); + + return rows; + }, [contributors]); + + const githubContributorsUrl = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/graphs/contributors`; return ( - <> -
-

- - CodeHarborHub - - ), - }} +

+
+ {/* Decorative background */} +
- +
); }; -export default ContributorList; +export default ContributorList; \ No newline at end of file diff --git a/src/components/HomePage/Community/LandingCommunity.css b/src/components/HomePage/Community/LandingCommunity.css index 12efcffd4..289f939a8 100644 --- a/src/components/HomePage/Community/LandingCommunity.css +++ b/src/components/HomePage/Community/LandingCommunity.css @@ -1,133 +1,880 @@ +/* ========================================================= + CodeHarborHub + Community / Social Proof Section + ========================================================= */ + .landing-community { + --chh-primary: var(--ifm-color-primary); + --chh-heading: var(--ifm-heading-color); + --chh-text: var(--ifm-font-color-base); + --chh-muted: var(--ifm-color-emphasis-600); + --chh-border: var(--ifm-color-emphasis-200); + --chh-surface: var(--ifm-background-surface-color); + + position: relative; width: 100%; - padding: 16px; + padding: 2rem 0; + overflow: hidden; + isolation: isolate; } -.landing-community .landing-community__header { - display: flex; - justify-content: space-between; - align-items: center; - max-width: 100%; - margin-bottom: 1rem; - padding: auto 1rem; +/* ========================================================= + Background + ========================================================= */ + +.landing-community__background { + position: absolute; + inset: 0; + z-index: -1; + pointer-events: none; + overflow: hidden; } -.landing-community .landing-community__header .landing-community__title { - font-size: 2rem; - line-height: 2rem; - text-align: start; - font-weight: 500; - padding: 0; +.landing-community__grid { + position: absolute; + inset: 0; + color: var(--chh-primary); + opacity: 0.035; } -.landing-community .landing-community__header .landing-community__highlight { - font-weight: 600; - color: var(--ifm-color-primary); - text-shadow: 0 0 1px var(--ifm-color-primary); +.landing-community__glow { + position: absolute; + width: 420px; + height: 420px; + border-radius: 50%; + background: var(--chh-primary); + filter: blur(130px); + opacity: 0.07; } -.landing-community .landing-community__content { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; - text-align: center; +.landing-community__glow--one { + left: -230px; + top: -220px; } -.landing-community .landing-community__content .landing-community__stats { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; +.landing-community__glow--two { + right: -220px; + bottom: -250px; } -.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding: 1rem; - border-radius: 1rem; - background-color: var(--ifm-color-background); - box-shadow: 0 0 1px var(--ifm-color-primary); +/* ========================================================= + Container + ========================================================= */ + +.landing-community__container { + /* width: min(1180px, 100%); */ + margin: 0 1.2rem; +} + +/* ========================================================= + Header + ========================================================= */ + +.landing-community__header { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 2rem; + margin-bottom: 3rem; } -.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item:hover { - cursor: pointer; - transform: scale(1.01); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4); +.landing-community__heading { + max-width: 760px; } -.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item .landing-community__stat-value { - font-size: 3.5rem; - font-weight: 600; - color: var(--ifm-color-primary); - text-shadow: 0 0 1px var(--ifm-color-primary); - padding-bottom: 0.5rem; +.landing-community__eyebrow { + width: fit-content; + + display: inline-flex; + align-items: center; + gap: 0.45rem; + + margin-bottom: 1rem; + padding: 0.45rem 0.75rem; + + color: var(--chh-primary); + + background: color-mix( + in srgb, + var(--chh-primary) 9%, + transparent + ); + + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 18%, + transparent + ); + + border-radius: 999px; + + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.13em; + text-transform: uppercase; } -.landing-community .landing-community__content .landing-community__stats .landing-community__stat-item .landing-community__stat-description { - font-size: 1rem; - text-shadow: 0 0 1px var(--ifm-color-primary); +.landing-community__heading h2 { + margin: 0; + + color: var(--chh-heading); + + font-size: clamp(2.25rem, 4.5vw, 3.6rem); + font-weight: 850; + line-height: 1.07; + letter-spacing: -0.045em; } -.landing-community .landing-community__content .landing-community__info { - width: 100%; - padding: 1rem; - border-radius: 1rem; - background-color: var(--ifm-color-background); - box-shadow: 0 0 1px var(--ifm-color-primary); +.landing-community__heading h2 span { + display: inline; + + background: linear-gradient( + 100deg, + var(--chh-primary), + #8b5cf6, + #ec4899 + ); + + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + color: transparent; } -.landing-community .landing-community__content .landing-community__info:hover { - cursor: pointer; - transform: scale(1.01); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4); +.landing-community__heading p { + max-width: 680px; + + margin: 1.2rem 0 0; + + color: var(--chh-muted); + + font-size: 1rem; + line-height: 1.75; } -.landing-community .landing-community__content .landing-community__info .landing-community__image { - width: 100%; - object-fit: cover; - border-radius: 1rem; +/* ========================================================= + GitHub Button + ========================================================= */ + +.landing-community__github-button { + flex: 0 0 auto; + + display: inline-flex; + align-items: center; + gap: 0.55rem; + + min-height: 46px; + + padding: 0.7rem 1rem; + + color: var(--chh-text); + + background: var(--chh-surface); + + border: 1px solid var(--chh-border); + border-radius: 12px; + + font-size: 0.8rem; + font-weight: 750; + + text-decoration: none; + + box-shadow: + 0 8px 25px rgb(0 0 0 / 0.04); + + transition: + transform 200ms ease, + color 200ms ease, + border-color 200ms ease, + box-shadow 200ms ease; +} + +.landing-community__github-button:hover { + color: var(--chh-primary); + border-color: color-mix( + in srgb, + var(--chh-primary) 35%, + var(--chh-border) + ); + + transform: translateY(-2px); + + text-decoration: none; + + box-shadow: + 0 12px 30px rgb(0 0 0 / 0.08); +} + +/* ========================================================= + Main Layout + ========================================================= */ + +.landing-community__layout { + display: grid; + grid-template-columns: minmax(0, 1.15fr) minmax(360px, 0.85fr); + gap: 1.25rem; + align-items: stretch; +} + +/* ========================================================= + Stats + ========================================================= */ + +.landing-community__stats { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1.25rem; +} + +.landing-community__stat { + position: relative; + + min-height: 230px; + + overflow: hidden; + + background: color-mix( + in srgb, + var(--chh-surface) 95%, + transparent + ); + + border: 1px solid var(--chh-border); + border-radius: 22px; + + box-shadow: + 0 12px 35px rgb(0 0 0 / 0.035), + inset 0 1px 0 rgb(255 255 255 / 0.04); + + transition: + transform 300ms cubic-bezier(0.22, 1, 0.36, 1), + border-color 300ms ease, + box-shadow 300ms ease; +} + +.landing-community__stat:hover { + transform: translateY(-6px); + + border-color: color-mix( + in srgb, + var(--chh-primary) 35%, + var(--chh-border) + ); + + box-shadow: + 0 22px 55px rgb(0 0 0 / 0.08), + 0 0 35px color-mix( + in srgb, + var(--chh-primary) 7%, + transparent + ); +} + +.landing-community__stat-link { + height: 100%; + + display: flex; + flex-direction: column; + + padding: 1.35rem; + + color: inherit; + + text-decoration: none; +} + +.landing-community__stat-link:hover { + color: inherit; + text-decoration: none; +} + +.landing-community__stat-top { + display: flex; + align-items: center; + justify-content: space-between; +} + +.landing-community__stat-icon { + width: 42px; + height: 42px; + + display: inline-flex; + align-items: center; + justify-content: center; + + color: var(--chh-primary); + + background: color-mix( + in srgb, + var(--chh-primary) 9%, + transparent + ); + + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 16%, + transparent + ); + + border-radius: 12px; + + transition: + color 220ms ease, + background 220ms ease, + transform 220ms ease; +} + +.landing-community__stat:hover + .landing-community__stat-icon { + color: #fff; + background: var(--chh-primary); + transform: scale(1.05); +} + +.landing-community__stat-arrow { + color: var(--chh-muted); + + transition: + color 200ms ease, + transform 200ms ease; +} + +.landing-community__stat:hover + .landing-community__stat-arrow { + color: var(--chh-primary); + transform: translate(2px, -2px); +} + +/* ========================================================= + Stat Content + ========================================================= */ + +.landing-community__stat-value { + margin-top: 1.5rem; + + color: var(--chh-heading); + + font-size: clamp(2.4rem, 4vw, 3.5rem); + font-weight: 850; + line-height: 1; + + letter-spacing: -0.055em; +} + +.landing-community__stat h3 { + margin: 0.6rem 0 0; + + color: var(--chh-heading); + + font-size: 0.95rem; + font-weight: 750; +} + +.landing-community__stat p { + margin: 0.45rem 0 0; + + color: var(--chh-muted); + + font-size: 0.75rem; + line-height: 1.6; +} + +/* ========================================================= + Visual Card + ========================================================= */ + +.landing-community__visual { + position: relative; + + min-height: 100%; + + display: flex; + align-items: stretch; +} + +.landing-community__visual-glow { + position: absolute; + + width: 260px; + height: 260px; + + top: 50%; + left: 50%; + + transform: translate(-50%, -50%); + + border-radius: 50%; + + background: var(--chh-primary); + + filter: blur(90px); + + opacity: 0.1; +} + +.landing-community__visual-card { + position: relative; + z-index: 1; + + width: 100%; + + display: flex; + flex-direction: column; + + overflow: hidden; + + background: + linear-gradient( + 145deg, + color-mix( + in srgb, + var(--chh-primary) 7%, + var(--chh-surface) + ), + var(--chh-surface) + ); + + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 18%, + var(--chh-border) + ); + + border-radius: 22px; + + box-shadow: + 0 20px 55px rgb(0 0 0 / 0.07); +} + +.landing-community__visual-image { + position: relative; + + flex: 1; + + min-height: 300px; + + display: flex; + align-items: center; + justify-content: center; + + padding: 2rem; +} + +.landing-community__visual-image::after { + content: ""; + + position: absolute; + inset: 0; + + background: + linear-gradient( + 180deg, + transparent 45%, + color-mix( + in srgb, + var(--chh-surface) 80%, + transparent + ) + ); + + pointer-events: none; +} + +.landing-community__visual-image img { + position: relative; + z-index: 1; + + width: min(90%, 430px); + height: auto; + + display: block; + + object-fit: contain; + + filter: + drop-shadow(0 25px 30px rgb(0 0 0 / 0.1)); +} + +/* ========================================================= + Visual Content + ========================================================= */ + +.landing-community__visual-content { + display: flex; + align-items: center; + gap: 0.9rem; + + padding: 1.2rem 1.35rem; + + border-top: 1px solid var(--chh-border); +} + +.landing-community__visual-icon { + width: 42px; + height: 42px; + + flex: 0 0 auto; + + display: inline-flex; + align-items: center; + justify-content: center; + + color: var(--chh-primary); + + background: color-mix( + in srgb, + var(--chh-primary) 10%, + transparent + ); + + border-radius: 11px; +} + +.landing-community__visual-content span { + display: block; + + margin-bottom: 0.2rem; + + color: var(--chh-primary); + + font-size: 0.62rem; + font-weight: 850; + letter-spacing: 0.13em; +} + +.landing-community__visual-content h3 { + margin: 0; + + color: var(--chh-heading); + + font-size: 1rem; + font-weight: 750; + line-height: 1.35; +} + +/* ========================================================= + Visual Footer + ========================================================= */ + +.landing-community__visual-footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + + padding: 0.9rem 1.35rem; + + border-top: 1px solid var(--chh-border); +} + +.landing-community__avatars { + display: flex; + align-items: center; +} + +.landing-community__avatars span { + width: 28px; + height: 28px; + + display: inline-flex; + align-items: center; + justify-content: center; + + margin-left: -6px; + + color: var(--chh-heading); + + background: var(--chh-surface); + + border: 2px solid var(--chh-surface); + border-radius: 50%; + + font-size: 0.58rem; + font-weight: 800; +} + +.landing-community__avatars span:first-child { + margin-left: 0; +} + +.landing-community__heart { + display: inline-flex; + align-items: center; + gap: 0.4rem; + + color: var(--chh-muted); + + font-size: 0.67rem; + font-weight: 650; +} + +.landing-community__heart svg { + color: var(--chh-primary); +} + +/* ========================================================= + Loading + ========================================================= */ + +.landing-community__stat--loading { + padding: 1.35rem; +} + +.landing-community__skeleton { + border-radius: 8px; + + background: color-mix( + in srgb, + var(--chh-primary) 7%, + var(--chh-surface) + ); + + animation: community-skeleton 1.5s ease-in-out infinite; +} + +.landing-community__skeleton--icon { + width: 42px; + height: 42px; +} + +.landing-community__skeleton--number { + width: 100px; + height: 48px; + margin-top: 1.5rem; +} + +.landing-community__skeleton--title { + width: 100px; + height: 16px; + margin-top: 0.7rem; +} + +.landing-community__skeleton--text { + width: 85%; + height: 32px; + margin-top: 0.6rem; +} + +@keyframes community-skeleton { + 0%, + 100% { + opacity: 0.5; + } + + 50% { + opacity: 1; + } +} + +/* ========================================================= + Footer + ========================================================= */ + +.landing-community__footer { + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + + margin-top: 1.25rem; + padding: 1.2rem 1.35rem; + + background: color-mix( + in srgb, + var(--chh-surface) 80%, + transparent + ); + + border: 1px solid var(--chh-border); + border-radius: 16px; +} + +.landing-community__footer p { + max-width: 750px; + + margin: 0; + + color: var(--chh-muted); + + font-size: 0.78rem; + line-height: 1.6; +} + +.landing-community__footer a { + flex: 0 0 auto; + + display: inline-flex; + align-items: center; + gap: 0.4rem; + + color: var(--chh-primary); + + font-size: 0.75rem; + font-weight: 750; + + text-decoration: none; +} + +.landing-community__footer a:hover { + color: var(--chh-primary); + text-decoration: underline; } -.landing-community .landing-community__content .landing-community__info .landing-community__info-text { - margin-top: 1rem; - padding: 0; - font-size: 1rem; - text-shadow: 0 0 1px var(--ifm-color-primary); +/* ========================================================= + Dark Theme + ========================================================= */ + +[data-theme="dark"] .landing-community { + --chh-border: rgb(255 255 255 / 0.09); + --chh-surface: rgb(15 23 42 / 0.6); +} + +[data-theme="dark"] .landing-community__stat { + box-shadow: + 0 18px 50px rgb(0 0 0 / 0.18), + inset 0 1px 0 rgb(255 255 255 / 0.025); +} + +[data-theme="dark"] .landing-community__stat:hover { + box-shadow: + 0 25px 65px rgb(0 0 0 / 0.3), + 0 0 40px color-mix( + in srgb, + var(--chh-primary) 8%, + transparent + ); +} + +[data-theme="dark"] + .landing-community__visual-card { + background: + linear-gradient( + 145deg, + rgb(255 255 255 / 0.045), + rgb(255 255 255 / 0.015) + ); } -.landing-community .landing-community__content .landing-community__info .landing-community__info-text .landing-community__link { - color: var(--ifm-color-primary); - text-shadow: 0 0 1px var(--ifm-color-primary); - text-decoration: none; +/* ========================================================= + Tablet + ========================================================= */ + +@media screen and (max-width: 1000px) { + .landing-community { + padding: 6rem 1.25rem; + } + + .landing-community__layout { + grid-template-columns: 1fr; + } + + .landing-community__visual { + min-height: 500px; + } + + .landing-community__visual-image { + min-height: 350px; + } } -.landing-community .landing-community__content .landing-community__info .landing-community__info-text .landing-community__link:hover { - text-decoration: underline; +/* ========================================================= + Mobile + ========================================================= */ + +@media screen and (max-width: 700px) { + .landing-community { + padding: 4.5rem 1rem; + } + + .landing-community__header { + align-items: flex-start; + flex-direction: column; + gap: 1.25rem; + } + + .landing-community__heading h2 { + font-size: 2.15rem; + } + + .landing-community__heading p { + font-size: 0.92rem; + } + + .landing-community__github-button { + width: 100%; + justify-content: center; + } + + .landing-community__stats { + grid-template-columns: 1fr; + } + + .landing-community__stat { + min-height: 215px; + } + + .landing-community__visual { + min-height: 420px; + } + + .landing-community__visual-image { + min-height: 280px; + } + + .landing-community__footer { + align-items: flex-start; + flex-direction: column; + gap: 1rem; + } } -@media screen and (max-width: 768px) { - .landing-community .landing-community__content { - grid-template-columns: 1fr; - } +/* ========================================================= + Small Mobile + ========================================================= */ - .landing-community .landing-community__content .landing-community__stats { - grid-template-columns: 1fr; - } +@media screen and (max-width: 420px) { + .landing-community__heading h2 { + font-size: 1.9rem; + } - .landing-community .landing-community__content .landing-community__info { - width: 100%; - } + .landing-community__eyebrow { + font-size: 0.65rem; + } - .landing-community .landing-community__content .landing-community__info .landing-community__image { - width: 100%; - } + .landing-community__stat-value { + font-size: 2.7rem; + } + + .landing-community__visual-content { + padding: 1rem; + } - .landing-community .landing-community__content .landing-community__info .landing-community__info-text { - margin-top: 1rem; - padding: 0; - font-size: 1rem; - text-shadow: 0 0 1px var(--ifm-color-primary); - } + .landing-community__visual-footer { + padding: 0.8rem 1rem; + } + .landing-community__heart span { + display: none; + } } + +/* ========================================================= + Reduced Motion + ========================================================= */ + +@media (prefers-reduced-motion: reduce) { + .landing-community__stat, + .landing-community__stat-icon, + .landing-community__stat-arrow, + .landing-community__github-button { + transition: none; + } + + .landing-community__stat:hover { + transform: none; + } + + .landing-community__skeleton { + animation: none; + } +} \ No newline at end of file diff --git a/src/components/HomePage/Community/index.tsx b/src/components/HomePage/Community/index.tsx index 43a9f0fd4..0de14f416 100644 --- a/src/components/HomePage/Community/index.tsx +++ b/src/components/HomePage/Community/index.tsx @@ -1,125 +1,449 @@ -import React, { type FC, useEffect, useState, useMemo } from "react"; +import React, { + type FC, + useEffect, + useState, +} from "react"; +import Link from "@docusaurus/Link"; +import { motion } from "framer-motion"; +import { + ArrowUpRight, + Code2, + GitFork, + Github, + Heart, + Star, + Users, +} from "lucide-react"; + import { useCommunityStatsContext } from "../../../context/CommunityStats"; + import "./LandingCommunity.css"; type Props = { className?: string; }; -export const LandingCommunity: FC = ({ className }) => { - const { githubStarCountText, githubContributorsCount, githubForksCount } = useCommunityStatsContext(); - const [state, setState] = useState({ - stat0: 0, - stat1: 0, - stat2: 0, - stat3: 0, - }); +interface CommunityStat { + value: number; + formattedValue: string; + label: string; + description: string; + href: string; + icon: React.ElementType; +} - const generateList = useMemo(() => [ - { - stat: githubStarCountText, - description: "Stars on our GitHub repository, showcasing our community's support and contribution.", - href: "https://github.com/CodeHarborHub/codeharborhub.github.io/stargazers", - }, +const GITHUB_REPO_URL = + "https://github.com/CodeHarborHub/codeharborhub.github.io"; + +const GITHUB_ORG_URL = + "https://github.com/CodeHarborHub"; + +const GITHUB_CONTRIBUTORS_URL = + `${GITHUB_REPO_URL}/graphs/contributors`; + +const GITHUB_STARS_URL = + `${GITHUB_REPO_URL}/stargazers`; + +const GITHUB_FORKS_URL = + `${GITHUB_REPO_URL}/network/members`; + +const formatNumber = (value: number) => + new Intl.NumberFormat("en-US").format(value); + +const useCountUp = ( + target: number, + duration = 1200 +) => { + const [count, setCount] = useState(0); + + useEffect(() => { + if (!Number.isFinite(target) || target <= 0) { + setCount(0); + return; + } + + let animationFrame = 0; + const startTime = performance.now(); + + const animate = (currentTime: number) => { + const elapsed = currentTime - startTime; + const progress = Math.min( + elapsed / duration, + 1 + ); + + // Ease-out cubic + const eased = + 1 - Math.pow(1 - progress, 3); + + setCount( + Math.round(target * eased) + ); + + if (progress < 1) { + animationFrame = + requestAnimationFrame(animate); + } + }; + + animationFrame = + requestAnimationFrame(animate); + + return () => { + cancelAnimationFrame(animationFrame); + }; + }, [target, duration]); + + return count; +}; + +const StatCard: FC<{ + stat: CommunityStat; + index: number; +}> = ({ stat, index }) => { + const Icon = stat.icon; + + const animatedValue = useCountUp( + stat.value, + 1000 + index * 120 + ); + + return ( + + +
+
+
+ +
+ +
+ {formatNumber(animatedValue)} +
+ +

{stat.label}

+ +

{stat.description}

+ +
+ ); +}; + +export const LandingCommunity: FC = ({ + className = "", +}) => { + const { + githubStarCount, + githubStarCountText, + githubContributorsCount, + githubContributorsCountText, + githubForksCount, + githubForksCountText, + loading, + } = useCommunityStatsContext(); + + const stats: CommunityStat[] = [ { - stat: 30, - description: "Live projects on CodeHarborHub, demonstrating the power of open-source collaboration.", + value: githubStarCount, + formattedValue: githubStarCountText, + label: "GitHub Stars", + description: + "Developers supporting and starring our open-source work.", + href: GITHUB_STARS_URL, + icon: Star, }, { - stat: githubContributorsCount, - description: "Contributors who have made our repository better.", - href: "https://github.com/CodeHarborHub/codeharborhub.github.io/graphs/contributors", + value: githubContributorsCount, + formattedValue: + githubContributorsCountText, + label: "Contributors", + description: + "Developers helping improve the CodeHarborHub ecosystem.", + href: GITHUB_CONTRIBUTORS_URL, + icon: Users, }, { - stat: githubForksCount, - description: "Forks of our repository, showing how our community extends our work.", - href: "https://github.com/CodeHarborHub/codeharborhub.github.io/network/members", + value: githubForksCount, + formattedValue: githubForksCountText, + label: "Forks", + description: + "Community members extending and experimenting with our work.", + href: GITHUB_FORKS_URL, + icon: GitFork, }, - ], [githubStarCountText, githubContributorsCount, githubForksCount]); - - const handleDynamicChange = (target: number, index: number) => { - let count = 0; - const increment = target / 100; - const interval = setInterval(() => { - count += increment; - setState(prev => ({ ...prev, [`stat${index}`]: Math.round(count) })); - if (count >= target) { - setState(prev => ({ ...prev, [`stat${index}`]: target })); - clearInterval(interval); - } - }, 20); - }; - - useEffect(() => { - generateList.forEach((item, index) => { - handleDynamicChange(Number(item.stat), index); - }); - }, [generateList]); + ]; return ( -
-
-

- Discover the strength of our{" "} - amazing community. -

+
+ {/* Background */} +
); }; + +export default LandingCommunity; \ No newline at end of file diff --git a/src/components/HomePage/FinalCTA/FinalCTA.css b/src/components/HomePage/FinalCTA/FinalCTA.css new file mode 100644 index 000000000..f40830215 --- /dev/null +++ b/src/components/HomePage/FinalCTA/FinalCTA.css @@ -0,0 +1,414 @@ +/* ========================================================= + CodeHarborHub โ€” Final CTA + ========================================================= */ + +.final-cta { + position: relative; + isolation: isolate; + overflow: hidden; + padding: 2rem 0; + background: + radial-gradient( + circle at 50% 0%, + color-mix(in srgb, var(--ifm-color-primary) 13%, transparent), + transparent 48% + ), + var(--ifm-background-color); +} + +.final-cta__container { + position: relative; + margin: 0 1rem; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 32px; + background: + linear-gradient( + 145deg, + color-mix(in srgb, var(--ifm-color-primary) 5%, var(--ifm-background-surface-color)), + var(--ifm-background-surface-color) + ); + box-shadow: + 0 30px 80px color-mix(in srgb, var(--ifm-color-primary) 8%, transparent), + inset 0 1px 0 color-mix(in srgb, white 8%, transparent); +} + +/* ========================================================= + Background + ========================================================= */ + +.final-cta__grid { + position: absolute; + inset: 0; + z-index: -3; + opacity: 0.4; + background-image: + linear-gradient( + to right, + color-mix(in srgb, var(--ifm-color-emphasis-300) 20%, transparent) 1px, + transparent 1px + ), + linear-gradient( + to bottom, + color-mix(in srgb, var(--ifm-color-emphasis-300) 20%, transparent) 1px, + transparent 1px + ); + background-size: 42px 42px; + mask-image: linear-gradient( + to bottom, + transparent, + black 20%, + black 80%, + transparent + ); +} + +.final-cta__glow { + position: absolute; + z-index: -2; + width: 320px; + height: 320px; + border-radius: 50%; + pointer-events: none; + filter: blur(80px); + opacity: 0.18; +} + +.final-cta__glow--one { + top: -150px; + left: 8%; + background: var(--ifm-color-primary); +} + +.final-cta__glow--two { + right: 5%; + bottom: -180px; + background: var(--ifm-color-primary-dark); +} + +/* ========================================================= + Content + ========================================================= */ + +.final-cta__content { + position: relative; + z-index: 2; + padding: 1rem; + text-align: center; +} + +.final-cta__eyebrow { + display: inline-flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 1.25rem; + padding: 0.45rem 0.8rem; + border: 1px solid color-mix( + in srgb, + var(--ifm-color-primary) 30%, + var(--ifm-color-emphasis-200) + ); + border-radius: 999px; + background: color-mix( + in srgb, + var(--ifm-color-primary) 7%, + transparent + ); + color: var(--ifm-color-primary); + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.12em; +} + +.final-cta__title { + margin: 0 auto; + font-size: clamp(1rem, 6vw, 5rem); + line-height: 1.02; + letter-spacing: -0.045em; + font-weight: 900; + color: var(--ifm-heading-color); +} + +.final-cta__highlight { + color: var(--ifm-color-primary); +} + +.final-cta__description { + max-width: 650px; + margin: 1.5rem auto 0; + color: var(--ifm-color-emphasis-700); + font-size: clamp(1rem, 1.5vw, 1.15rem); + line-height: 1.75; +} + +/* ========================================================= + Buttons + ========================================================= */ + +.final-cta__actions { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 0.85rem; + margin-top: 2rem; +} + +.final-cta__button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.65rem; + min-height: 50px; + padding: 0.8rem 1.2rem; + border-radius: 12px; + font-size: 0.95rem; + font-weight: 750; + text-decoration: none; + transition: + transform 180ms ease, + box-shadow 180ms ease, + background 180ms ease, + border-color 180ms ease; +} + +.final-cta__button:hover { + transform: translateY(-2px); + text-decoration: none; +} + +.final-cta__button--primary { + color: #fff; + background: var(--ifm-color-primary); + box-shadow: + 0 10px 30px color-mix( + in srgb, + var(--ifm-color-primary) 25%, + transparent + ); +} + +.final-cta__button--primary:hover { + color: #fff; + background: var(--ifm-color-primary-dark); + box-shadow: + 0 15px 35px color-mix( + in srgb, + var(--ifm-color-primary) 32%, + transparent + ); +} + +.final-cta__button--secondary { + border: 1px solid var(--ifm-color-emphasis-300); + color: var(--ifm-font-color-base); + background: color-mix( + in srgb, + var(--ifm-background-color) 75%, + transparent + ); +} + +.final-cta__button--secondary:hover { + color: var(--ifm-font-color-base); + border-color: var(--ifm-color-primary); + background: color-mix( + in srgb, + var(--ifm-color-primary) 6%, + var(--ifm-background-color) + ); +} + +/* ========================================================= + Trust line + ========================================================= */ + +.final-cta__trust { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 0.55rem; + margin-top: 1.5rem; + color: var(--ifm-color-emphasis-600); + font-size: 0.82rem; +} + +.final-cta__status { + width: 7px; + height: 7px; + border-radius: 50%; + background: #22c55e; + box-shadow: 0 0 0 4px color-mix( + in srgb, + #22c55e 12%, + transparent + ); +} + +.final-cta__separator { + opacity: 0.45; +} + +/* ========================================================= + Terminal + ========================================================= */ + +.final-cta__terminal { + position: absolute; + right: -80px; + bottom: -95px; + z-index: 1; + width: 330px; + overflow: hidden; + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 16px; + opacity: 0.35; + transform: rotate(-6deg); + background: var(--ifm-background-color); + box-shadow: 0 25px 60px rgb(0 0 0 / 18%); + pointer-events: none; +} + +.final-cta__terminal-header { + display: flex; + align-items: center; + gap: 6px; + padding: 0.7rem 0.8rem; + border-bottom: 1px solid var(--ifm-color-emphasis-200); +} + +.final-cta__terminal-header > span { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--ifm-color-emphasis-400); +} + +.final-cta__terminal-header code { + margin-left: auto; + color: var(--ifm-color-emphasis-600); + font-size: 0.65rem; +} + +.final-cta__terminal-body { + padding: 5rem; + font-family: var(--ifm-font-family-monospace); + font-size: 0.7rem; + line-height: 1.8; + text-align: left; +} + +.final-cta__terminal-prompt { + color: var(--ifm-color-primary); +} + +.final-cta__terminal-output { + color: var(--ifm-color-emphasis-500); +} + +.final-cta__terminal-success { + color: #22c55e; +} + +/* ========================================================= + Dark mode + ========================================================= */ + +[data-theme="dark"] .final-cta { + background: + radial-gradient( + circle at 50% 0%, + color-mix(in srgb, var(--ifm-color-primary) 16%, transparent), + transparent 50% + ), + var(--ifm-background-color); +} + +[data-theme="dark"] .final-cta__container { + border-color: color-mix( + in srgb, + var(--ifm-color-primary) 18%, + var(--ifm-color-emphasis-200) + ); +} + +/* ========================================================= + Responsive + ========================================================= */ + +@media (max-width: 996px) { + .final-cta { + padding: 2rem 1rem; + } + + .final-cta__container { + min-height: auto; + border-radius: 24px; + } + + .final-cta__content { + padding: 1rem; + } + + .final-cta__terminal { + display: none; + } +} + +@media (max-width: 640px) { + .final-cta { + padding: 0 0.5rem; + } + + .final-cta__container { + border-radius: 20px; + } + + .final-cta__content { + padding: 1rem; + } + + .final-cta__eyebrow { + font-size: 0.64rem; + } + + .final-cta__title { + font-size: clamp(2rem, 12vw, 3.5rem); + } + + .final-cta__description { + font-size: 0.9rem; + line-height: 1.65; + } + + .final-cta__actions { + flex-direction: column; + width: 100%; + } + + .final-cta__button { + width: 100%; + } + + .final-cta__trust { + font-size: 0.72rem; + } +} + +/* ========================================================= + Reduced motion + ========================================================= */ + +@media (prefers-reduced-motion: reduce) { + .final-cta__button { + transition: none; + } + + .final-cta__button:hover { + transform: none; + } +} \ No newline at end of file diff --git a/src/components/HomePage/FinalCTA/index.tsx b/src/components/HomePage/FinalCTA/index.tsx new file mode 100644 index 000000000..3f7572930 --- /dev/null +++ b/src/components/HomePage/FinalCTA/index.tsx @@ -0,0 +1,102 @@ +import React from "react"; +import Link from "@docusaurus/Link"; +import { ArrowRight, Github, Sparkles, Terminal } from "lucide-react"; +import "./FinalCTA.css"; + +const FinalCTA: React.FC = () => { + return ( +
+
+ {/* Decorative background */} +
+ ); +}; + +export default FinalCTA; \ No newline at end of file diff --git a/src/components/HomePage/ResourcesSection.tsx b/src/components/HomePage/ResourcesSection.tsx deleted file mode 100644 index a43e9e069..000000000 --- a/src/components/HomePage/ResourcesSection.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import React, { useState } from "react"; -import Link from "@docusaurus/Link"; -import { - ArrowRightFilled, - ChevronLeftRegular, - ChevronRightRegular, -} from "@fluentui/react-icons"; -import "./style.css"; - -// Interface defining the structure of a resource -interface Resource { - url: string; - type: string; - title: string; - description: string; - image: string; - duration: string; -} - -// Array containing all available resources -const ALL_RESOURCES: Resource[] = [ - { - url: "https://codeharborhub.github.io/docs/", - type: "tutorial", - title: "Tutorial: For Beginners", - description: - "Get started with CodeHarborHub and learn how to use it to manage your codebases. This tutorial is for beginners.", - image: "/img/resources/tutorials.jpg", - duration: "10 min", - }, - { - url: "https://codeharborhub.github.io/courses/", - type: "courses", - title: "Courses: For Advanced Users", - description: - "Learn advanced topics in CodeHarborHub and become a pro. This course is for advanced users.", - image: "/img/resources/courses.jpg", - duration: "3 min", - }, - { - url: "https://codeharborhub.github.io/blog/", - type: "blog", - title: "Blog: For All Users", - description: - "Read our blog to get the latest updates, news, and articles on CodeHarborHub.", - image: "/img/resources/blogs.jpg", - duration: "7 min", - }, - { - url: "https://codeharborhub.github.io/dsa/", - type: "dsa", - title: "DSA: For Competitive Programmers", - description: - "Prepare for your next coding interview with our Data Structures and Algorithms course.", - image: "/img/resources/dsa.jpg", - duration: "5 min", - }, -]; - -// Component to render each individual resource -function Resource({ - type, - url, - image, - title, - description, - duration, -}: Resource) { - return ( - -
-
- {title} -
-

{title}

-

{description}

-
-
-
- {`${duration} ${type === "video" ? "watch" : "read"}`} -
-
- - ); -} - -// Main component for displaying resources and managing pagination -export default function ResourcesSection() { - const [page, setPage] = useState(1); // State to track current page number - const [activeType, setActiveType] = useState< - "all" | "blog" | "tutorial" | "courses" | "dsa" - >("all"); // State to track currently active resource type filter - - // Filter resources based on activeType - const resources = - activeType === "all" - ? ALL_RESOURCES - : ALL_RESOURCES.filter((r) => r.type === activeType); - - // Determine the current set of resources to display based on pagination - const currentResources = resources.slice((page - 1) * 3, page * 3); - - // Calculate total number of pages based on the number of resources - const pages = Math.ceil(resources.length / 3); - - // Function to navigate to the next page of resources - const nextPage = () => { - if (page < pages) { - setPage(page + 1); - } - }; - - // Function to navigate to the previous page of resources - const prevPage = () => { - if (page > 1) { - setPage(page - 1); - } - }; - - return ( -
-
- {/* Section header */} -
-
- RESOURCES -

Want to know more?

-
- {/* Link to view all blogs */} - - All Blogs - -
- - {/* Filter buttons for resource types */} -
- - - - - -
- - {/* Displaying the current set of resources */} -
-
- {currentResources.map((resource, idx) => { - return ; - })} -
- - {/* Pagination controls */} -
- - - -
-
-
-
- ); -} diff --git a/src/components/HomePage/ResourcesSection/index.tsx b/src/components/HomePage/ResourcesSection/index.tsx new file mode 100644 index 000000000..78920988e --- /dev/null +++ b/src/components/HomePage/ResourcesSection/index.tsx @@ -0,0 +1,487 @@ +import React, { useEffect, useMemo, useState } from "react"; +import Link from "@docusaurus/Link"; +import { motion, AnimatePresence } from "framer-motion"; +import { + ArrowRight, + BookOpen, + ChevronLeft, + ChevronRight, + Code2, + FileText, + GraduationCap, + Layers3, + Sparkles, + Timer, + Trophy, +} from "lucide-react"; + +import "./style.css"; + +type ResourceType = "tutorial" | "courses" | "blog" | "dsa"; + +interface Resource { + id: string; + url: string; + type: ResourceType; + title: string; + description: string; + image: string; + duration: string; + level?: string; + featured?: boolean; +} + +interface ResourceCategory { + id: "all" | ResourceType; + label: string; + icon: React.ElementType; +} + +const ALL_RESOURCES: Resource[] = [ + { + id: "tutorials", + url: "/docs/", + type: "tutorial", + title: "Learn the Fundamentals", + description: + "Start your developer journey with structured tutorials, clear explanations, examples, and practical guidance.", + image: "/img/resources/tutorials.jpg", + duration: "10 min", + level: "Beginner", + featured: true, + }, + { + id: "courses", + url: "/courses/", + type: "courses", + title: "Master Modern Technologies", + description: + "Go deeper with structured courses designed to help you build practical skills and become job-ready.", + image: "/img/resources/courses.jpg", + duration: "3 min", + level: "Intermediate", + }, + { + id: "blog", + url: "/blog/", + type: "blog", + title: "Explore the Tech Blog", + description: + "Discover developer insights, technology trends, project ideas, engineering stories, and community updates.", + image: "/img/resources/blogs.jpg", + duration: "7 min", + level: "All Levels", + }, + { + id: "dsa", + url: "/dsa/", + type: "dsa", + title: "Build Strong DSA Skills", + description: + "Practice data structures and algorithms, improve problem-solving skills, and prepare for technical interviews.", + image: "/img/resources/dsa.jpg", + duration: "5 min", + level: "Intermediate", + }, +]; + +const CATEGORIES: ResourceCategory[] = [ + { + id: "all", + label: "All Resources", + icon: Layers3, + }, + { + id: "tutorial", + label: "Tutorials", + icon: BookOpen, + }, + { + id: "courses", + label: "Courses", + icon: GraduationCap, + }, + { + id: "blog", + label: "Blog", + icon: FileText, + }, + { + id: "dsa", + label: "DSA", + icon: Trophy, + }, +]; + +const ITEMS_PER_PAGE = 3; + +const getTypeLabel = (type: ResourceType) => { + const labels: Record = { + tutorial: "Tutorial", + courses: "Course", + blog: "Article", + dsa: "DSA", + }; + + return labels[type]; +}; + +const getTypeIcon = (type: ResourceType) => { + const icons: Record = { + tutorial: BookOpen, + courses: GraduationCap, + blog: FileText, + dsa: Code2, + }; + + return icons[type]; +}; + +const ResourceCard: React.FC<{ + resource: Resource; + index: number; +}> = ({ resource, index }) => { + const TypeIcon = getTypeIcon(resource.type); + + return ( + + + {/* Image */} +
+ + + + + {/* Body */} +
+
+ + + + + + {resource.level} +
+ +

{resource.title}

+ +

{resource.description}

+ +
+ + Explore Resource + + + + +
+
+ + + ); +}; + +const ResourcesSection: React.FC = () => { + const [activeType, setActiveType] = useState< + "all" | ResourceType + >("all"); + + const [page, setPage] = useState(1); + + const filteredResources = useMemo(() => { + if (activeType === "all") { + return ALL_RESOURCES; + } + + return ALL_RESOURCES.filter( + (resource) => resource.type === activeType + ); + }, [activeType]); + + const totalPages = Math.max( + 1, + Math.ceil(filteredResources.length / ITEMS_PER_PAGE) + ); + + const currentResources = useMemo(() => { + const start = (page - 1) * ITEMS_PER_PAGE; + + return filteredResources.slice( + start, + start + ITEMS_PER_PAGE + ); + }, [filteredResources, page]); + + useEffect(() => { + setPage(1); + }, [activeType]); + + const handlePrevious = () => { + setPage((current) => Math.max(1, current - 1)); + }; + + const handleNext = () => { + setPage((current) => + Math.min(totalPages, current + 1) + ); + }; + + return ( +
+ {/* Background */} +
+ ); +}; + +export default ResourcesSection; \ No newline at end of file diff --git a/src/components/HomePage/ResourcesSection/style.css b/src/components/HomePage/ResourcesSection/style.css new file mode 100644 index 000000000..cbc6715de --- /dev/null +++ b/src/components/HomePage/ResourcesSection/style.css @@ -0,0 +1,798 @@ +/* ========================================================= + CodeHarborHub Resources + ========================================================= */ + +.chh-resources { + --chh-primary: var(--ifm-color-primary); + --chh-heading: var(--ifm-heading-color); + --chh-text: var(--ifm-font-color-base); + --chh-muted: var(--ifm-color-emphasis-600); + --chh-border: var(--ifm-color-emphasis-200); + --chh-surface: var(--ifm-background-surface-color); + + position: relative; + width: 100%; + padding: 2rem 0; + overflow: hidden; + isolation: isolate; +} + +/* ========================================================= + Background + ========================================================= */ + +.chh-resources__background { + position: absolute; + inset: 0; + z-index: -1; + overflow: hidden; + pointer-events: none; +} + +.chh-resources__grid { + position: absolute; + inset: 0; + color: var(--chh-primary); + opacity: 0.035; +} + +.chh-resources__glow { + position: absolute; + width: 420px; + height: 420px; + border-radius: 50%; + background: var(--chh-primary); + filter: blur(130px); + opacity: 0.07; +} + +.chh-resources__glow--one { + top: -230px; + left: -200px; +} + +.chh-resources__glow--two { + right: -220px; + bottom: -250px; +} + +/* ========================================================= + Container + ========================================================= */ + +.chh-resources__container { + /* width: min(1180px, 100%); */ + margin: 0 1.2rem; +} + +/* ========================================================= + Header + ========================================================= */ + +.chh-resources__header { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 2rem; + margin-bottom: 2.5rem; +} + +.chh-resources__heading { + max-width: 760px; +} + +.chh-resources__eyebrow { + width: fit-content; + display: inline-flex; + align-items: center; + gap: 0.45rem; + + margin-bottom: 1rem; + padding: 0.45rem 0.75rem; + + color: var(--chh-primary); + + background: color-mix( + in srgb, + var(--chh-primary) 9%, + transparent + ); + + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 18%, + transparent + ); + + border-radius: 999px; + + font-size: 0.7rem; + font-weight: 800; + letter-spacing: 0.13em; + text-transform: uppercase; +} + +.chh-resources__heading h2 { + margin: 0; + + color: var(--chh-heading); + + font-size: clamp(2.2rem, 4vw, 3.4rem); + font-weight: 850; + line-height: 1.08; + letter-spacing: -0.045em; +} + +.chh-resources__heading h2 span { + display: inline; + + background: linear-gradient( + 100deg, + var(--chh-primary), + #8b5cf6, + #ec4899 + ); + + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + color: transparent; +} + +.chh-resources__heading p { + max-width: 680px; + margin: 1.1rem 0 0; + + color: var(--chh-muted); + + font-size: 1rem; + line-height: 1.75; +} + +/* ========================================================= + View All + ========================================================= */ + +.chh-resources__view-all { + flex: 0 0 auto; + + display: inline-flex; + align-items: center; + gap: 0.45rem; + + padding-bottom: 0.35rem; + + color: var(--chh-primary); + + border-bottom: 1px solid transparent; + + font-size: 0.86rem; + font-weight: 750; + + text-decoration: none; + + transition: + gap 200ms ease, + border-color 200ms ease; +} + +.chh-resources__view-all:hover { + color: var(--chh-primary); + gap: 0.7rem; + border-color: var(--chh-primary); + text-decoration: none; +} + +/* ========================================================= + Filters + ========================================================= */ + +.chh-resources__filters { + display: flex; + align-items: center; + gap: 0.55rem; + + margin-bottom: 1.75rem; + + overflow-x: auto; + + scrollbar-width: none; + -ms-overflow-style: none; +} + +.chh-resources__filters::-webkit-scrollbar { + display: none; +} + +.chh-resources__filter { + flex: 0 0 auto; + + display: inline-flex; + align-items: center; + gap: 0.5rem; + + min-height: 40px; + + padding: 0.55rem 0.9rem; + + color: var(--chh-muted); + + background: color-mix( + in srgb, + var(--chh-surface) 85%, + transparent + ); + + border: 1px solid var(--chh-border); + border-radius: 999px; + + font-size: 0.78rem; + font-weight: 700; + + cursor: pointer; + + transition: + color 200ms ease, + background 200ms ease, + border-color 200ms ease, + transform 200ms ease; +} + +.chh-resources__filter:hover { + color: var(--chh-primary); + border-color: color-mix( + in srgb, + var(--chh-primary) 35%, + var(--chh-border) + ); + + transform: translateY(-1px); +} + +.chh-resources__filter--active { + color: #fff; + background: var(--chh-primary); + border-color: var(--chh-primary); + + box-shadow: + 0 8px 22px color-mix( + in srgb, + var(--chh-primary) 20%, + transparent + ); +} + +.chh-resources__filter--active:hover { + color: #fff; +} + +/* ========================================================= + Cards Grid + ========================================================= */ + +.chh-resources__grid-cards { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.25rem; +} + +/* ========================================================= + Resource Card + ========================================================= */ + +.chh-resource-card { + position: relative; + min-width: 0; + overflow: hidden; + background: color-mix( + in srgb, + var(--chh-surface) 95%, + transparent + ); + border: 1px solid var(--chh-border); + border-radius: 0.9rem; + + box-shadow: + 0 12px 35px rgb(0 0 0 / 0.04), + inset 0 1px 0 rgb(255 255 255 / 0.04); + + transition: + transform 300ms cubic-bezier(0.22, 1, 0.36, 1), + border-color 300ms ease, + box-shadow 300ms ease; +} + +.chh-resource-card:hover { + transform: translateY(-7px); + + border-color: color-mix( + in srgb, + var(--chh-primary) 35%, + var(--chh-border) + ); + + box-shadow: + 0 22px 55px rgb(0 0 0 / 0.09), + 0 0 35px color-mix( + in srgb, + var(--chh-primary) 7%, + transparent + ); +} + +.chh-resource-card__link { + display: block; + height: 100%; + + color: inherit; + text-decoration: none; +} + +.chh-resource-card__link:hover { + color: inherit; + text-decoration: none; +} + +/* ========================================================= + Media + ========================================================= */ + +.chh-resource-card__media { + position: relative; + + aspect-ratio: 16 / 9; + + overflow: hidden; + + background: var(--chh-primary); +} + +.chh-resource-card__image { + width: 100%; + height: 100%; + + display: block; + + object-fit: cover; + + transition: + transform 600ms cubic-bezier(0.22, 1, 0.36, 1), + filter 400ms ease; +} + +.chh-resource-card:hover .chh-resource-card__image { + transform: scale(1.07); +} + +.chh-resource-card__image-overlay { + position: absolute; + inset: 0; + + background: + linear-gradient( + 180deg, + rgb(0 0 0 / 0.05) 0%, + rgb(0 0 0 / 0.12) 50%, + rgb(0 0 0 / 0.68) 100% + ); +} + +.chh-resource-card__category { + position: absolute; + left: 0.85rem; + bottom: 0.85rem; + + display: inline-flex; + align-items: center; + gap: 0.4rem; + + padding: 0.42rem 0.65rem; + + color: #fff; + + background: rgb(0 0 0 / 0.42); + + border: 1px solid rgb(255 255 255 / 0.16); + border-radius: 999px; + + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + + font-size: 0.68rem; + font-weight: 750; +} + +.chh-resource-card__featured { + position: absolute; + top: 0.85rem; + right: 0.85rem; + + display: inline-flex; + align-items: center; + gap: 0.35rem; + + padding: 0.4rem 0.6rem; + + color: #fff; + + background: var(--chh-primary); + + border-radius: 999px; + + font-size: 0.65rem; + font-weight: 800; + + box-shadow: + 0 8px 20px color-mix( + in srgb, + var(--chh-primary) 35%, + transparent + ); +} + +/* ========================================================= + Card Body + ========================================================= */ + +.chh-resource-card__body { + min-height: 250px; + + display: flex; + flex-direction: column; + + padding: 1.25rem; +} + +.chh-resource-card__meta { + display: flex; + align-items: center; + gap: 0.45rem; + + margin-bottom: 0.8rem; + + color: var(--chh-muted); + + font-size: 0.7rem; + font-weight: 650; +} + +.chh-resource-card__meta span { + display: inline-flex; + align-items: center; + gap: 0.3rem; +} + +.chh-resource-card__dot { + width: 3px; + height: 3px; + + border-radius: 50%; + + background: var(--chh-muted); +} + +.chh-resource-card__body h3 { + margin: 0; + + color: var(--chh-heading); + + font-size: 1.25rem; + font-weight: 780; + line-height: 1.3; + letter-spacing: -0.02em; +} + +.chh-resource-card__body p { + margin: 0.7rem 0 0; + + color: var(--chh-muted); + + font-size: 0.86rem; + line-height: 1.7; +} + +/* ========================================================= + Card Footer + ========================================================= */ + +.chh-resource-card__footer { + display: flex; + align-items: center; + justify-content: space-between; + + margin-top: auto; + padding-top: 1.3rem; +} + +.chh-resource-card__read { + color: var(--chh-primary); + + font-size: 0.76rem; + font-weight: 750; +} + +.chh-resource-card__arrow { + width: 34px; + height: 34px; + + display: inline-flex; + align-items: center; + justify-content: center; + + color: var(--chh-muted); + + background: color-mix( + in srgb, + var(--chh-primary) 7%, + transparent + ); + + border: 1px solid var(--chh-border); + border-radius: 10px; + + transition: + color 220ms ease, + background 220ms ease, + transform 220ms ease; +} + +.chh-resource-card:hover .chh-resource-card__arrow { + color: #fff; + background: var(--chh-primary); + transform: translateX(3px); +} + +/* ========================================================= + Bottom / Pagination + ========================================================= */ + +.chh-resources__bottom { + display: flex; + align-items: center; + justify-content: space-between; + + margin-top: 2rem; +} + +.chh-resources__count { + color: var(--chh-muted); + + font-size: 0.76rem; +} + +.chh-resources__count strong { + color: var(--chh-text); +} + +.chh-resources__pagination { + display: flex; + align-items: center; + gap: 0.7rem; +} + +.chh-resources__pagination-button { + width: 38px; + height: 38px; + + display: inline-flex; + align-items: center; + justify-content: center; + + color: var(--chh-text); + + background: var(--chh-surface); + + border: 1px solid var(--chh-border); + border-radius: 11px; + + cursor: pointer; + + transition: + color 180ms ease, + background 180ms ease, + border-color 180ms ease, + transform 180ms ease; +} + +.chh-resources__pagination-button:hover:not(:disabled) { + color: #fff; + background: var(--chh-primary); + border-color: var(--chh-primary); + transform: translateY(-2px); +} + +.chh-resources__pagination-button:disabled { + opacity: 0.35; + cursor: not-allowed; +} + +.chh-resources__page { + display: inline-flex; + align-items: center; + gap: 0.35rem; + + min-width: 45px; + justify-content: center; + + color: var(--chh-muted); + + font-size: 0.76rem; +} + +.chh-resources__page strong { + color: var(--chh-text); +} + +/* ========================================================= + Empty State + ========================================================= */ + +.chh-resources__empty { + min-height: 300px; + + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + text-align: center; + + color: var(--chh-muted); + + background: color-mix( + in srgb, + var(--chh-surface) 80%, + transparent + ); + + border: 1px dashed var(--chh-border); + border-radius: 22px; +} + +.chh-resources__empty svg { + margin-bottom: 1rem; + color: var(--chh-primary); +} + +.chh-resources__empty h3 { + margin: 0; + color: var(--chh-heading); +} + +.chh-resources__empty p { + margin: 0.4rem 0 0; + font-size: 0.85rem; +} + +/* ========================================================= + Dark Mode + ========================================================= */ + +[data-theme="dark"] .chh-resources { + --chh-border: rgb(255 255 255 / 0.09); + --chh-surface: rgb(15 23 42 / 0.6); +} + +[data-theme="dark"] .chh-resource-card { + box-shadow: + 0 18px 50px rgb(0 0 0 / 0.18), + inset 0 1px 0 rgb(255 255 255 / 0.025); +} + +[data-theme="dark"] .chh-resource-card:hover { + box-shadow: + 0 25px 65px rgb(0 0 0 / 0.3), + 0 0 40px color-mix( + in srgb, + var(--chh-primary) 8%, + transparent + ); +} + +[data-theme="dark"] .chh-resources__filter { + background: rgb(255 255 255 / 0.025); +} + +/* ========================================================= + Responsive + ========================================================= */ + +@media screen and (max-width: 1000px) { + .chh-resources { + padding: 6rem 1.25rem; + } + + .chh-resources__grid-cards { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .chh-resource-card:last-child { + grid-column: 1 / -1; + } +} + +@media screen and (max-width: 700px) { + .chh-resources { + padding: 4.5rem 1rem; + } + + .chh-resources__header { + align-items: flex-start; + flex-direction: column; + gap: 1.2rem; + } + + .chh-resources__view-all { + padding-bottom: 0; + } + + .chh-resources__grid-cards { + grid-template-columns: 1fr; + } + + .chh-resource-card:last-child { + grid-column: auto; + } + + .chh-resource-card__body { + min-height: 235px; + } + + .chh-resources__bottom { + align-items: center; + } +} + +@media screen and (max-width: 480px) { + .chh-resources__heading h2 { + font-size: 2rem; + } + + .chh-resources__heading p { + font-size: 0.9rem; + } + + .chh-resources__filters { + margin-right: -1rem; + padding-right: 1rem; + } + + .chh-resource-card { + border-radius: 18px; + } + + .chh-resource-card__body { + padding: 1rem; + } + + .chh-resource-card__body h3 { + font-size: 1.15rem; + } + + .chh-resource-card__body p { + font-size: 0.82rem; + } + + .chh-resources__count { + font-size: 0.7rem; + } +} + +/* ========================================================= + Reduced Motion + ========================================================= */ + +@media (prefers-reduced-motion: reduce) { + .chh-resource-card, + .chh-resource-card__image, + .chh-resource-card__arrow, + .chh-resources__filter, + .chh-resources__view-all, + .chh-resources__pagination-button { + transition: none; + } + + .chh-resource-card:hover { + transform: none; + } +} \ No newline at end of file diff --git a/src/components/HomePage/TweetsSection.tsx b/src/components/HomePage/TweetsSection.tsx deleted file mode 100644 index 85e295556..000000000 --- a/src/components/HomePage/TweetsSection.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { motion } from "framer-motion"; -import { Swiper, SwiperSlide } from "swiper/react"; -import { Navigation, Pagination, Autoplay } from "swiper/modules"; -import Tweet from "../Tweet"; -import Tweets from "../../data/tweets"; -import "swiper/css"; -import "swiper/css/navigation"; -import "swiper/css/pagination"; -import React from "react"; - -type TweetType = { - id: number; - showOnHomepage: boolean; -}; - -export const TweetsSection: React.FC = () => { - const tweetColumns = [[], [], []] as TweetType[][]; - - // Distribute the tweets into 3 columns - Tweets.filter((tweet) => tweet.showOnHomepage).forEach((tweet, i) => - tweetColumns[i % 3].push(tweet) - ); - - return ( -
- {/* Section Title */} - -

- Loved by Many Users -

-
- - - {/* Swiper Tweets */} - - {/* Map through tweetColumns and render SwiperSlides */} - {tweetColumns.map((tweetItems, columnIndex) => - tweetItems.map((tweet, tweetIndex) => ( - -
- -
-
- )) - )} -
-
- ); -}; - -export default TweetsSection; diff --git a/src/components/HomePage/TweetsSection/TweetsSection.css b/src/components/HomePage/TweetsSection/TweetsSection.css new file mode 100644 index 000000000..79ac75d84 --- /dev/null +++ b/src/components/HomePage/TweetsSection/TweetsSection.css @@ -0,0 +1,586 @@ +/* ========================================================= + CodeHarborHub - Community Voices + ========================================================= */ + +.tweets-section { + --tweets-primary: var(--ifm-color-primary); + --tweets-primary-dark: var(--ifm-color-primary-dark); + + --tweets-background: var(--ifm-background-color); + --tweets-surface: var(--ifm-background-surface-color); + + --tweets-heading: var(--ifm-heading-color); + --tweets-text: var(--ifm-font-color-base); + --tweets-muted: var(--ifm-color-emphasis-700); + + --tweets-border: var(--ifm-color-emphasis-200); + + position: relative; + width: 100%; + + padding: 2rem 0; + + overflow: hidden; + isolation: isolate; +} + +/* --------------------------------------------------------- + Background + --------------------------------------------------------- */ + +.tweets-section::before { + content: ""; + + position: absolute; + inset: 0; + + z-index: -3; + pointer-events: none; + + background: + radial-gradient( + circle at 10% 15%, + color-mix( + in srgb, + var(--tweets-primary) 11%, + transparent + ), + transparent 28% + ), + radial-gradient( + circle at 90% 80%, + color-mix( + in srgb, + var(--tweets-primary) 9%, + transparent + ), + transparent 30% + ); +} + +.tweets-section::after { + content: ""; + + position: absolute; + inset: 0; + + z-index: -2; + pointer-events: none; + + opacity: 0.22; + + background-image: + linear-gradient( + color-mix( + in srgb, + var(--tweets-primary) 7%, + transparent + ) 1px, + transparent 1px + ), + linear-gradient( + 90deg, + color-mix( + in srgb, + var(--tweets-primary) 7%, + transparent + ) 1px, + transparent 1px + ); + + background-size: 44px 44px; + + mask-image: linear-gradient( + to bottom, + transparent, + black 20%, + black 80%, + transparent + ); +} + +/* --------------------------------------------------------- + Container + --------------------------------------------------------- */ + +.tweets-section__container { + position: relative; + + /* width: min(1240px, 100%); */ + margin: 0 1.2rem; +} + +/* --------------------------------------------------------- + Glow + --------------------------------------------------------- */ + +.tweets-section__glow { + position: absolute; + + width: 240px; + height: 240px; + + border-radius: 50%; + + background: color-mix( + in srgb, + var(--tweets-primary) 8%, + transparent + ); + + filter: blur(80px); + + pointer-events: none; + + z-index: -1; +} + +.tweets-section__glow--one { + top: 10%; + left: -120px; +} + +.tweets-section__glow--two { + right: -120px; + bottom: 10%; +} + +/* --------------------------------------------------------- + Header + --------------------------------------------------------- */ + +.tweets-section__header { + max-width: 760px; + + margin: 0 auto 2rem; + + text-align: center; +} + +.tweets-section__eyebrow { + display: inline-flex; + align-items: center; + + gap: 0.5rem; + + margin-bottom: 1rem; + padding: 0.45rem 0.8rem; + + border: 1px solid + color-mix( + in srgb, + var(--tweets-primary) 25%, + var(--tweets-border) + ); + + border-radius: 999px; + + color: var(--tweets-primary); + + background: color-mix( + in srgb, + var(--tweets-primary) 7%, + var(--tweets-background) + ); + + font-size: 0.73rem; + font-weight: 800; + letter-spacing: 0.08em; +} + +.tweets-section__title { + margin: 0; + + color: var(--tweets-heading); + + font-size: clamp(2rem, 4vw, 3.2rem); + line-height: 1.08; + + font-weight: 800; + + letter-spacing: -0.035em; +} + +.tweets-section__title span { + color: var(--tweets-primary); + + background: linear-gradient( + 120deg, + var(--tweets-primary), + var(--tweets-primary-dark) + ); + + -webkit-background-clip: text; + background-clip: text; + + -webkit-text-fill-color: transparent; +} + +.tweets-section__description { + max-width: 680px; + + margin: 1rem auto 0; + + color: var(--tweets-muted); + + font-size: 1rem; + line-height: 1.75; +} + +/* --------------------------------------------------------- + Trust Badge + --------------------------------------------------------- */ + +.tweets-section__trust { + display: flex; + align-items: center; + + width: fit-content; + + margin: 0 auto 2.5rem; + padding: 0.7rem 1rem; + + gap: 0.75rem; + + border: 1px solid var(--tweets-border); + border-radius: 0.9rem; + + background: color-mix( + in srgb, + var(--tweets-surface) 85%, + transparent + ); + + box-shadow: + 0 8px 30px rgba(0, 0, 0, 0.04), + inset 0 1px 0 rgba(255, 255, 255, 0.05); +} + +.tweets-section__trust-icon { + display: grid; + place-items: center; + + width: 34px; + height: 34px; + + flex-shrink: 0; + + border-radius: 0.65rem; + + color: var(--tweets-primary); + + background: color-mix( + in srgb, + var(--tweets-primary) 10%, + var(--tweets-background) + ); +} + +.tweets-section__trust div:last-child { + display: flex; + flex-direction: column; + + gap: 0.1rem; +} + +.tweets-section__trust strong { + color: var(--tweets-heading); + + font-size: 0.82rem; +} + +.tweets-section__trust span { + color: var(--tweets-muted); + + font-size: 0.72rem; +} + +/* --------------------------------------------------------- + Slider + --------------------------------------------------------- */ + +.tweets-section__slider-wrapper { + position: relative; + + padding: 0.5rem 3.4rem 3.5rem; +} + +.tweets-section__swiper { + width: 100%; + + overflow: visible; +} + +.tweets-section__slide { + height: auto; + + display: flex; +} + +/* --------------------------------------------------------- + Swiper Navigation + --------------------------------------------------------- */ + +.tweets-section__swiper .swiper-button-prev, +.tweets-section__swiper .swiper-button-next { + width: 42px; + height: 42px; + + margin-top: -21px; + + border: 1px solid var(--tweets-border); + border-radius: 50%; + + color: var(--tweets-primary); + + background: var(--tweets-surface); + + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.08); + + transition: + transform 180ms ease, + border-color 180ms ease, + background 180ms ease; +} + +.tweets-section__swiper .swiper-button-prev:hover, +.tweets-section__swiper .swiper-button-next:hover { + transform: scale(1.06); + + border-color: color-mix( + in srgb, + var(--tweets-primary) 45%, + var(--tweets-border) + ); + + background: color-mix( + in srgb, + var(--tweets-primary) 7%, + var(--tweets-surface) + ); +} + +.tweets-section__swiper .swiper-button-prev { + left: 0; +} + +.tweets-section__swiper .swiper-button-next { + right: 0; +} + +.tweets-section__swiper .swiper-button-prev::after, +.tweets-section__swiper .swiper-button-next::after { + font-size: 0.9rem; + font-weight: 900; +} + +/* --------------------------------------------------------- + Pagination + --------------------------------------------------------- */ + +.tweets-section__swiper .swiper-pagination { + bottom: 0.5rem; +} + +.tweets-section__swiper + .swiper-pagination-bullet { + width: 7px; + height: 7px; + + opacity: 0.4; + + background: var(--tweets-muted); + + transition: + width 200ms ease, + opacity 200ms ease; +} + +.tweets-section__swiper + .swiper-pagination-bullet-active { + width: 22px; + + opacity: 1; + + border-radius: 999px; + + background: var(--tweets-primary); +} + +/* --------------------------------------------------------- + Footer + --------------------------------------------------------- */ + +.tweets-section__footer { + display: flex; + align-items: center; + justify-content: space-between; + + gap: 1rem; + + margin-top: 1rem; + padding: 1rem 1.15rem; + + border: 1px solid var(--tweets-border); + border-radius: 1rem; + + background: color-mix( + in srgb, + var(--tweets-surface) 80%, + transparent + ); +} + +.tweets-section__footer-quote { + display: flex; + align-items: center; + + gap: 0.6rem; + + color: var(--tweets-muted); + + font-size: 0.82rem; +} + +.tweets-section__footer-quote svg { + flex-shrink: 0; + + color: var(--tweets-primary); +} + +.tweets-section__footer-link { + display: inline-flex; + align-items: center; + + gap: 0.4rem; + + flex-shrink: 0; + + color: var(--tweets-primary); + + font-size: 0.82rem; + font-weight: 750; + + text-decoration: none; + + transition: gap 180ms ease; +} + +.tweets-section__footer-link:hover { + color: var(--tweets-primary-dark); + + gap: 0.65rem; +} + +.tweets-section__footer-link:focus-visible { + outline: 3px solid + color-mix( + in srgb, + var(--tweets-primary) 30%, + transparent + ); + + outline-offset: 3px; + + border-radius: 4px; +} + +/* --------------------------------------------------------- + Tablet + --------------------------------------------------------- */ + +@media screen and (max-width: 768px) { + .tweets-section { + padding: 4.5rem 1rem; + } + + .tweets-section__slider-wrapper { + padding-inline: 2.8rem; + } + + .tweets-section__trust { + margin-bottom: 2rem; + } + + .tweets-section__footer { + align-items: flex-start; + + flex-direction: column; + } +} + +/* --------------------------------------------------------- + Mobile + --------------------------------------------------------- */ + +@media screen and (max-width: 560px) { + .tweets-section { + padding: 3.5rem 0.75rem; + } + + .tweets-section__title { + font-size: 2rem; + } + + .tweets-section__description { + font-size: 0.92rem; + } + + .tweets-section__trust { + width: 100%; + + justify-content: flex-start; + + margin-bottom: 1.5rem; + } + + .tweets-section__slider-wrapper { + padding-inline: 0.25rem; + padding-bottom: 3rem; + } + + .tweets-section__swiper + .swiper-button-prev, + .tweets-section__swiper + .swiper-button-next { + display: none; + } + + .tweets-section__footer { + margin-top: 0.75rem; + } + + .tweets-section__footer-link { + width: 100%; + + justify-content: center; + + padding: 0.7rem; + + border: 1px solid var(--tweets-border); + border-radius: 0.7rem; + } +} + +/* --------------------------------------------------------- + Reduced Motion + --------------------------------------------------------- */ + +@media (prefers-reduced-motion: reduce) { + .tweets-section__swiper + .swiper-button-prev, + .tweets-section__swiper + .swiper-button-next, + .tweets-section__footer-link, + .tweets-section__swiper + .swiper-pagination-bullet { + transition: none; + } + + .tweets-section__swiper + .swiper-button-prev:hover, + .tweets-section__swiper + .swiper-button-next:hover { + transform: none; + } +} \ No newline at end of file diff --git a/src/components/HomePage/TweetsSection/index.tsx b/src/components/HomePage/TweetsSection/index.tsx new file mode 100644 index 000000000..3181ee1fc --- /dev/null +++ b/src/components/HomePage/TweetsSection/index.tsx @@ -0,0 +1,173 @@ +import React from "react"; +import { motion } from "framer-motion"; +import { + ArrowUpRight, + MessageCircleHeart, + Quote, + Sparkles, +} from "lucide-react"; + +import { Swiper, SwiperSlide } from "swiper/react"; +import { Autoplay, Navigation, Pagination } from "swiper/modules"; + +import Tweet from "../../Tweet"; +import Tweets from "../../../data/tweets"; + +import "swiper/css"; +import "swiper/css/navigation"; +import "swiper/css/pagination"; +import "./TweetsSection.css"; + +const TweetsSection: React.FC = () => { + const homepageTweets = Tweets.filter((tweet) => tweet.showOnHomepage); + + if (!homepageTweets.length) { + return null; + } + + return ( +
+
+ {/* Background decoration */} +
+ ); +}; + +export default TweetsSection; \ No newline at end of file diff --git a/src/components/HomePage/WhyChooseCodeHarbor/index.tsx b/src/components/HomePage/WhyChooseCodeHarbor/index.tsx new file mode 100644 index 000000000..50ed57658 --- /dev/null +++ b/src/components/HomePage/WhyChooseCodeHarbor/index.tsx @@ -0,0 +1,292 @@ +import React from "react"; +import Link from "@docusaurus/Link"; +import { motion } from "framer-motion"; +import { + BookOpen, + Code2, + Users2, + Zap, + Trophy, + Github, + ArrowRight, + Sparkles, +} from "lucide-react"; + +// @ts-expect-error Docusaurus resolves stylesheet imports at build time. +import "./why-choose-codeharbor.css"; + +type Reason = { + title: string; + description: string; + icon: React.ElementType; + number: string; + link: string; +}; + +const reasons: Reason[] = [ + { + number: "01", + title: "Free Education", + description: + "Learn modern programming and technology through structured, practical, and beginner-friendly resources โ€” completely free.", + icon: BookOpen, + link: "/tutorials", + }, + { + number: "02", + title: "Open Source", + description: + "Contribute to real-world projects, collaborate with developers, and build meaningful experience for your GitHub profile.", + icon: Github, + link: "/community/team", // /contributors + }, + { + number: "03", + title: "Hands-on Coding", + description: + "Move beyond theory with coding practice, interactive examples, projects, and developer-focused learning experiences.", + icon: Code2, + link: "#", // "/practice", + }, + { + number: "04", + title: "Global Community", + description: + "Learn with developers and contributors, share knowledge, collaborate on projects, and grow together.", + icon: Users2, + link: "/community", + }, + { + number: "05", + title: "DSA & Interview Prep", + description: + "Strengthen problem-solving skills with curated DSA topics, coding challenges, patterns, and interview preparation.", + icon: Zap, + link: "https://codeharborhub.github.io/dsa/", + }, + { + number: "06", + title: "Structured Roadmaps", + description: + "Follow clear learning paths from beginner to advanced with practical milestones designed around real-world skills.", + icon: Trophy, + link: "/roadmaps", + }, +]; + +const containerVariants = { + hidden: {}, + visible: { + transition: { + staggerChildren: 0.08, + }, + }, +}; + +const itemVariants = { + hidden: { + opacity: 0, + y: 30, + }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.6, + ease: [0.22, 1, 0.36, 1], + }, + }, +}; + +const WhyChooseCodeHarbor: React.FC = () => { + return ( +
+ {/* Background decoration */} +
+ ); +}; + +export default WhyChooseCodeHarbor; \ No newline at end of file diff --git a/src/components/HomePage/WhyChooseCodeHarbor/why-choose-codeharbor.css b/src/components/HomePage/WhyChooseCodeHarbor/why-choose-codeharbor.css new file mode 100644 index 000000000..2765bcb35 --- /dev/null +++ b/src/components/HomePage/WhyChooseCodeHarbor/why-choose-codeharbor.css @@ -0,0 +1,761 @@ +/* ========================================================= + CodeHarborHub โ€” Why Choose Section + ========================================================= */ + +.chh-why { + --chh-primary: var(--ifm-color-primary); + --chh-text: var(--ifm-font-color-base); + --chh-heading: var(--ifm-heading-color); + --chh-muted: var(--ifm-color-emphasis-600); + --chh-border: var(--ifm-color-emphasis-200); + --chh-surface: var(--ifm-background-surface-color); + + position: relative; + width: 100%; + padding: 2rem 0; + overflow: hidden; + isolation: isolate; + /* border-radius: 1rem; */ +} + +/* ========================================================= + Background + ========================================================= */ + +.chh-why__background { + position: absolute; + inset: 0; + z-index: -1; + pointer-events: none; + overflow: hidden; +} + +.chh-why__grid { + position: absolute; + inset: 0; + color: var(--chh-primary); + opacity: 0.035; +} + +.chh-why__orb { + position: absolute; + width: 420px; + height: 420px; + border-radius: 50%; + background: var(--chh-primary); + filter: blur(130px); + opacity: 0.07; +} + +.chh-why__orb--one { + top: -180px; + left: -180px; +} + +.chh-why__orb--two { + right: -180px; + bottom: -220px; +} + +/* ========================================================= + Container + ========================================================= */ + +.chh-why__container { + /* width: min(1180px, 100%); */ + margin: 0 1rem; +} + +/* ========================================================= + Header + ========================================================= */ + +.chh-why__header { + max-width: 780px; + margin: 0 auto 4.5rem; + text-align: center; +} + +.chh-why__eyebrow { + width: fit-content; + margin: 0 auto 1.25rem; + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.85rem; + color: var(--chh-primary); + font-size: 0.75rem; + font-weight: 800; + letter-spacing: 0.14em; + text-transform: uppercase; + background: color-mix( + in srgb, + var(--chh-primary) 9%, + transparent + ); + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 20%, + transparent + ); + border-radius: 999px; +} + +.chh-why__header h2 { + margin: 0; + color: var(--chh-heading); + font-size: clamp(1rem, 4.5vw, 3.6rem); + font-weight: 850; + line-height: 1.08; + letter-spacing: -0.045em; +} + +.chh-why__header h2 span { + display: inline; + + background: linear-gradient( + 100deg, + var(--chh-primary), + #8b5cf6, + #ec4899 + ); + + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + color: transparent; +} + +.chh-why__header p { + max-width: 690px; + margin: 1.4rem auto 0; + color: var(--chh-muted); + font-size: 1.05rem; + line-height: 1.8; +} + +/* ========================================================= + Feature Grid + ========================================================= */ + +.chh-why__grid-cards { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.25rem; +} + +/* ========================================================= + Card + ========================================================= */ + +.chh-why-card { + position: relative; + + min-height: 390px; + padding: 1.65rem; + + display: flex; + flex-direction: column; + + overflow: hidden; + + background: color-mix( + in srgb, + var(--chh-surface) 94%, + transparent + ); + + border: 1px solid var(--chh-border); + border-radius: 24px; + + box-shadow: + 0 10px 35px rgb(0 0 0 / 0.035), + inset 0 1px 0 rgb(255 255 255 / 0.04); + + transition: + transform 300ms cubic-bezier(0.22, 1, 0.36, 1), + border-color 300ms ease, + box-shadow 300ms ease, + background 300ms ease; +} + +.chh-why-card:hover { + transform: translateY(-8px); + + border-color: color-mix( + in srgb, + var(--chh-primary) 42%, + var(--chh-border) + ); + + background: color-mix( + in srgb, + var(--chh-primary) 3%, + var(--chh-surface) + ); + + box-shadow: + 0 25px 60px rgb(0 0 0 / 0.09), + 0 0 0 1px color-mix( + in srgb, + var(--chh-primary) 8%, + transparent + ); +} + +/* ========================================================= + Card Glow + ========================================================= */ + +.chh-why-card__glow { + position: absolute; + + width: 180px; + height: 180px; + + top: -100px; + right: -90px; + + border-radius: 50%; + + background: var(--chh-primary); + + filter: blur(65px); + + opacity: 0; + + transition: opacity 400ms ease; +} + +.chh-why-card:hover .chh-why-card__glow { + opacity: 0.14; +} + +/* ========================================================= + Card Top + ========================================================= */ + +.chh-why-card__top { + display: flex; + align-items: center; + justify-content: space-between; +} + +.chh-why-card__number { + color: var(--chh-muted); + + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, + Consolas, monospace; + + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.08em; +} + +.chh-why-card__arrow { + width: 34px; + height: 34px; + + display: inline-flex; + align-items: center; + justify-content: center; + + color: var(--chh-muted); + + border: 1px solid var(--chh-border); + border-radius: 10px; + + transition: + color 250ms ease, + background 250ms ease, + border-color 250ms ease, + transform 250ms ease; +} + +.chh-why-card:hover .chh-why-card__arrow { + color: #fff; + background: var(--chh-primary); + border-color: var(--chh-primary); + transform: rotate(-45deg); +} + +/* ========================================================= + Icon + ========================================================= */ + +.chh-why-card__icon { + width: 62px; + height: 62px; + + margin-top: 2.8rem; + margin-bottom: 1.5rem; + + display: inline-flex; + align-items: center; + justify-content: center; + + color: var(--chh-primary); + + background: color-mix( + in srgb, + var(--chh-primary) 9%, + transparent + ); + + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 15%, + transparent + ); + + border-radius: 16px; + + transition: + transform 300ms ease, + color 300ms ease, + background 300ms ease, + box-shadow 300ms ease; +} + +.chh-why-card:hover .chh-why-card__icon { + color: #fff; + + background: var(--chh-primary); + + transform: translateY(-3px) scale(1.05); + + box-shadow: + 0 12px 28px color-mix( + in srgb, + var(--chh-primary) 25%, + transparent + ); +} + +/* ========================================================= + Content + ========================================================= */ + +.chh-why-card__content { + position: relative; + z-index: 2; +} + +.chh-why-card__content h3 { + margin: 0; + + color: var(--chh-heading); + + font-size: 1.3rem; + font-weight: 750; + letter-spacing: -0.02em; +} + +.chh-why-card__content p { + margin: 0.75rem 0 0; + + color: var(--chh-muted); + + font-size: 0.92rem; + line-height: 1.7; +} + +/* ========================================================= + Card Link + ========================================================= */ + +.chh-why-card__link { + position: relative; + z-index: 2; + + width: fit-content; + + display: inline-flex; + align-items: center; + gap: 0.45rem; + + margin-top: auto; + padding-top: 1.4rem; + + color: var(--chh-primary); + + font-size: 0.84rem; + font-weight: 750; + + text-decoration: none; + + transition: + gap 200ms ease, + opacity 200ms ease; +} + +.chh-why-card__link:hover { + color: var(--chh-primary); + gap: 0.7rem; + text-decoration: none; +} + +/* ========================================================= + Bottom Accent + ========================================================= */ + +.chh-why-card__accent { + position: absolute; + + left: 0; + right: 0; + bottom: 0; + + height: 2px; + + background: linear-gradient( + 90deg, + transparent, + var(--chh-primary), + #8b5cf6, + transparent + ); + + transform: scaleX(0); + transform-origin: center; + + transition: transform 450ms ease; +} + +.chh-why-card:hover .chh-why-card__accent { + transform: scaleX(1); +} + +/* ========================================================= + CTA + ========================================================= */ + +.chh-why__cta { + position: relative; + + margin-top: 4rem; + padding: 1.5rem; + + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + + overflow: hidden; + + background: + linear-gradient( + 135deg, + color-mix( + in srgb, + var(--chh-primary) 8%, + var(--chh-surface) + ), + var(--chh-surface) + ); + + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 20%, + var(--chh-border) + ); + + border-radius: 24px; + + box-shadow: + 0 20px 50px rgb(0 0 0 / 0.05); +} + +.chh-why__cta::after { + content: ""; + + position: absolute; + + width: 260px; + height: 260px; + + right: -120px; + top: -160px; + + border-radius: 50%; + + background: var(--chh-primary); + + opacity: 0.08; + filter: blur(60px); + + pointer-events: none; +} + +.chh-why__cta-content { + position: relative; + z-index: 2; + + display: flex; + align-items: center; + gap: 1rem; +} + +.chh-why__cta-icon { + width: 52px; + height: 52px; + + flex: 0 0 auto; + + display: inline-flex; + align-items: center; + justify-content: center; + + color: var(--chh-primary); + + background: color-mix( + in srgb, + var(--chh-primary) 10%, + transparent + ); + + border: 1px solid color-mix( + in srgb, + var(--chh-primary) 18%, + transparent + ); + + border-radius: 14px; +} + +.chh-why__cta-label { + display: block; + + margin-bottom: 0.25rem; + + color: var(--chh-primary); + + font-size: 0.68rem; + font-weight: 850; + letter-spacing: 0.14em; +} + +.chh-why__cta h3 { + margin: 0; + + color: var(--chh-heading); + + font-size: 1.05rem; + font-weight: 750; +} + +.chh-why__cta p { + max-width: 650px; + + margin: 0.35rem 0 0; + + color: var(--chh-muted); + + font-size: 0.82rem; + line-height: 1.55; +} + +.chh-why__cta-button { + position: relative; + z-index: 2; + + flex: 0 0 auto; + + min-height: 48px; + + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.55rem; + + padding: 0.75rem 1.1rem; + + color: #fff; + + background: var(--chh-primary); + + border: 1px solid var(--chh-primary); + border-radius: 12px; + + font-size: 0.84rem; + font-weight: 750; + + text-decoration: none; + + box-shadow: + 0 10px 25px color-mix( + in srgb, + var(--chh-primary) 22%, + transparent + ); + + transition: + transform 200ms ease, + box-shadow 200ms ease, + filter 200ms ease; +} + +.chh-why__cta-button:hover { + color: #fff; + text-decoration: none; + + transform: translateY(-2px); + + filter: brightness(1.08); + + box-shadow: + 0 14px 32px color-mix( + in srgb, + var(--chh-primary) 30%, + transparent + ); +} + +/* ========================================================= + Dark Theme + ========================================================= */ + +[data-theme="dark"] .chh-why { + --chh-border: rgb(255 255 255 / 0.09); + --chh-surface: rgb(15 23 42 / 0.58); +} + +[data-theme="dark"] .chh-why-card { + box-shadow: + 0 15px 45px rgb(0 0 0 / 0.18), + inset 0 1px 0 rgb(255 255 255 / 0.025); +} + +[data-theme="dark"] .chh-why-card:hover { + box-shadow: + 0 25px 65px rgb(0 0 0 / 0.3), + 0 0 40px color-mix( + in srgb, + var(--chh-primary) 7%, + transparent + ); +} + +[data-theme="dark"] .chh-why__cta { + background: + linear-gradient( + 135deg, + rgb(255 255 255 / 0.045), + rgb(255 255 255 / 0.02) + ); +} + +/* ========================================================= + Responsive + ========================================================= */ + +@media screen and (max-width: 1000px) { + .chh-why { + padding: 6rem 1.25rem; + } + + .chh-why__grid-cards { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .chh-why-card { + min-height: 360px; + } + + .chh-why__cta { + align-items: flex-start; + flex-direction: column; + } + + .chh-why__cta-button { + width: 100%; + } +} + +@media screen and (max-width: 640px) { + .chh-why { + padding: 4.5rem 1rem; + } + + .chh-why__header { + margin-bottom: 3rem; + } + + .chh-why__header h2 { + font-size: 2.15rem; + } + + .chh-why__header p { + font-size: 0.94rem; + line-height: 1.7; + } + + .chh-why__grid-cards { + grid-template-columns: 1fr; + gap: 1rem; + } + + .chh-why-card { + min-height: 340px; + padding: 1.4rem; + border-radius: 20px; + } + + .chh-why-card__icon { + margin-top: 2.2rem; + } + + .chh-why__cta { + margin-top: 2.5rem; + padding: 1.25rem; + border-radius: 20px; + } + + .chh-why__cta-content { + align-items: flex-start; + } + + .chh-why__cta h3 { + font-size: 0.98rem; + } + + .chh-why__cta p { + font-size: 0.78rem; + } +} + +@media screen and (max-width: 420px) { + .chh-why__header h2 { + font-size: 1.9rem; + } + + .chh-why__eyebrow { + font-size: 0.67rem; + } + + .chh-why-card { + min-height: 320px; + } + + .chh-why__cta-content { + flex-direction: column; + } +} + +/* ========================================================= + Reduced Motion + ========================================================= */ + +@media (prefers-reduced-motion: reduce) { + .chh-why-card, + .chh-why-card__icon, + .chh-why-card__arrow, + .chh-why-card__link, + .chh-why__cta-button { + transition: none; + } + + .chh-why-card:hover { + transform: none; + } +} \ No newline at end of file diff --git a/src/components/HomePage/organizations/index.tsx b/src/components/HomePage/organizations/index.tsx index b7d51e4d1..d4766682c 100644 --- a/src/components/HomePage/organizations/index.tsx +++ b/src/components/HomePage/organizations/index.tsx @@ -1,4 +1,10 @@ import React from "react"; +import { + ArrowUpRight, + ExternalLink, + Github, + Sparkles, +} from "lucide-react"; import "./style.css"; interface Organization { @@ -6,76 +12,200 @@ interface Organization { logo: string; name: string; description: string; - link: string; + link?: string; + category?: string; } + const Orgs: Organization[] = [ { id: 1, logo: "https://img.icons8.com/?size=100&id=20909&format=png&color=000000", name: "HTML 5", + category: "Web Fundamentals", description: - "Join Our HTML organization to learn and build beautiful websites using the latest version of HTML, the language for creating web pages.", - link: "https://github.com/HTML-Mastery", + "Learn HTML from the fundamentals to advanced concepts and build accessible, semantic, and well-structured web pages.", + link: "https://github.com/html-mastery", }, { id: 2, logo: "https://img.icons8.com/?size=100&id=21278&format=png&color=000000", name: "CSS 3", + category: "Web Design", description: - "Join Our CSS organization to learn and build beautiful websites using the latest version of CSS, the language for styling web pages.", - link: "https://github.com/CSS3-Mastery", + "Master modern CSS, responsive layouts, animations, Flexbox, Grid, and the techniques behind beautiful web experiences.", + link: "#", }, { id: 3, logo: "https://img.icons8.com/?size=100&id=gYCTehfTlYk5&format=png&color=000000", name: "JavaScript", + category: "Programming", description: - "Join Our JavaScript organization to learn and build interactive websites using the most popular programming language for the web.", - link: "https://github.com/JavaScript-Mastery", + "Build interactive web applications and master JavaScript from core fundamentals to modern development patterns.", + link: "https://github.com/javascript-mastery", }, { id: 4, logo: "https://img.icons8.com/?size=100&id=wPohyHO_qO1a&format=png&color=000000", name: "React", + category: "Frontend Development", description: - "Join Our React organization to learn and build modern web applications using the most popular JavaScript library for building user interfaces.", - link: "https://github.com/React-js-Mastery", + "Learn React and build modern, component-driven interfaces using reusable architecture and industry-standard practices.", + link: "#", }, { id: 5, logo: "https://img.icons8.com/?size=100&id=hsPbhkOH4FMe&format=png&color=000000", - name: "Node", + name: "Node.js", + category: "Backend Development", description: - "Join Our Node organization to learn and build scalable network applications using the most popular JavaScript runtime environment.", - link: "https://github.com/Node-Js-Mastery", + "Learn Node.js and build scalable backend services, APIs, real-time applications, and production-ready web systems.", + link: "#", }, - { id: 6, logo: "https://img.icons8.com/?size=100&id=74402&format=png&color=000000", name: "MongoDB", + category: "Database", description: - "Join Our MongoDB organization to learn and build scalable network applications using the most popular NoSQL database program.", + "Explore MongoDB and learn how to design, query, and scale flexible NoSQL databases for modern applications.", link: "#", }, ]; + const Organizations: React.FC = () => { return ( -
-
- {Orgs.map((org) => ( -
- - {org.name} - -

{org.name}

-

{org.description}

+
+
+ {/* Section Header */} +
+
+
+ +

+ Learn. Build.{" "} + Master. +

+ +

+ Explore our technology-focused open-source organizations and + level up your skills through structured learning, projects, + documentation, and community collaboration. +

+
+ + {/* Organization Grid */} +
+ {Orgs.map((org) => { + const hasLink = Boolean(org.link); + + const cardContent = ( + <> +
+
+ {`${org.name} +
+ + {org.category && ( + + {org.category} + + )} +
+ +
+

{org.name}

+ +

+ {org.description} +

+
+ +
+ + {hasLink ? "Explore organization" : "Coming soon"} + + {hasLink && ( + + + {hasLink && ( + + + )} +
+ + ); + + if (hasLink) { + return ( + + {cardContent} + + ); + } + + return ( +
+ {cardContent} +
+ ); + })} +
+ + {/* Bottom CTA */} +
+
+
+
+ +
+

Build with the community

+

+ Explore CodeHarborHub on GitHub and contribute to the + open-source learning ecosystem. +

+
- ))} + + + Visit GitHub + +
); diff --git a/src/components/HomePage/organizations/style.css b/src/components/HomePage/organizations/style.css index c0cbfc39d..69b9c77ce 100644 --- a/src/components/HomePage/organizations/style.css +++ b/src/components/HomePage/organizations/style.css @@ -1,67 +1,750 @@ -#organization { - padding: 2rem 1rem; +/* ========================================================= + CodeHarborHub - Organizations / Mastery Ecosystem + ========================================================= */ + +.organizations { + --org-primary: var(--ifm-color-primary); + --org-primary-dark: var(--ifm-color-primary-dark); + --org-text: var(--ifm-font-color-base); + --org-heading: var(--ifm-heading-color); + --org-muted: var(--ifm-color-emphasis-700); + + --org-surface: var(--ifm-background-surface-color); + --org-background: var(--ifm-background-color); + + --org-border: var(--ifm-color-emphasis-200); + --org-border-hover: color-mix( + in srgb, + var(--org-primary) 45%, + var(--org-border) + ); + + --org-radius: 1.25rem; + + position: relative; + width: 100%; + padding: 2rem 0; + overflow: hidden; + isolation: isolate; +} + +/* --------------------------------------------------------- + Background Effects + --------------------------------------------------------- */ + +.organizations::before { + content: ""; + position: absolute; + inset: 0; + z-index: -2; + pointer-events: none; + + background: + radial-gradient( + circle at 15% 20%, + color-mix(in srgb, var(--org-primary) 12%, transparent), + transparent 30% + ), + radial-gradient( + circle at 85% 75%, + color-mix(in srgb, var(--org-primary) 9%, transparent), + transparent 32% + ); +} + +.organizations::after { + content: ""; + position: absolute; + inset: 0; + z-index: -1; + pointer-events: none; + + opacity: 0.25; + + background-image: + linear-gradient( + color-mix(in srgb, var(--org-primary) 7%, transparent) 1px, + transparent 1px + ), + linear-gradient( + 90deg, + color-mix(in srgb, var(--org-primary) 7%, transparent) 1px, + transparent 1px + ); + + background-size: 42px 42px; + + mask-image: linear-gradient( + to bottom, + transparent, + black 20%, + black 80%, + transparent + ); } -#organization h2 { +/* --------------------------------------------------------- + Container + --------------------------------------------------------- */ + +.organizations__container { + /* width: min(1180px, 100%); */ + margin: 0 1rem; +} + +/* --------------------------------------------------------- + Header + --------------------------------------------------------- */ + +.organizations__header { + max-width: 760px; + margin: 0 auto 3rem; text-align: center; - margin-bottom: 20px; } -#organization .organization-container { +.organizations__eyebrow { + display: inline-flex; + align-items: center; + gap: 0.5rem; + + margin-bottom: 1rem; + padding: 0.45rem 0.8rem; + + border: 1px solid + color-mix(in srgb, var(--org-primary) 25%, var(--org-border)); + + border-radius: 999px; + + color: var(--org-primary); + background: color-mix( + in srgb, + var(--org-primary) 7%, + var(--org-background) + ); + + font-size: 0.75rem; + font-weight: 800; + letter-spacing: 0.08em; +} + +.organizations__title { + margin: 0; + + color: var(--org-heading); + + font-size: clamp(2rem, 4vw, 3.25rem); + line-height: 1.08; + font-weight: 800; + letter-spacing: -0.035em; +} + +.organizations__highlight { + color: var(--org-primary); + + background: linear-gradient( + 120deg, + var(--org-primary), + var(--org-primary-dark) + ); + + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.organizations__subtitle { + max-width: 680px; + margin: 1rem auto 0; + + color: var(--org-muted); + + font-size: 1.05rem; + line-height: 1.75; +} + +/* --------------------------------------------------------- + Grid + --------------------------------------------------------- */ + +.organizations__grid { display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 1.5rem; + + gap: 1.25rem; } -#organization .organization-container .organization-card { - border-radius: 10px; - box-shadow: 0 0 10px rgba(5, 174, 216, 0.386); - padding: 25px; - text-align: center; - transition: transform 0.2s; +/* --------------------------------------------------------- + Organization Card + --------------------------------------------------------- */ + +.organization-card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + min-height: 330px; + padding: 1.4rem; + overflow: hidden; + border: 1px solid var(--org-border); + border-radius: var(--org-radius); + color: inherit; + text-decoration: none; + + background: + linear-gradient( + 145deg, + color-mix(in srgb, var(--org-primary) 4%, var(--org-surface)), + var(--org-surface) + ); + + box-shadow: + 0 8px 30px color-mix(in srgb, var(--org-primary) 3%, transparent), + inset 0 1px 0 color-mix(in srgb, white 8%, transparent); + + transition: + transform 220ms ease, + border-color 220ms ease, + box-shadow 220ms ease, + background 220ms ease; } -#organization .organization-container .organization-card:hover { - transform: scale(1); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6); - cursor: pointer; +.organization-card:hover { + text-decoration: none; } -#organization .organization-container .organization-card:hover img { - transform: scale(1.1); +/* Top gradient line */ + +.organization-card::before { + content: ""; + + position: absolute; + top: 0; + left: 1.25rem; + right: 1.25rem; + + height: 2px; + + border-radius: 0 0 999px 999px; + + background: linear-gradient( + 90deg, + transparent, + var(--org-primary), + transparent + ); + + opacity: 0; + + transition: opacity 220ms ease; } -#organization .organization-container .organization-card img { - width: 100px; - height: 100px; - margin-bottom: 20px; +/* Glow */ + +.organization-card::after { + content: ""; + + position: absolute; + + width: 160px; + height: 160px; + + top: -80px; + right: -80px; + + border-radius: 50%; + + background: color-mix(in srgb, var(--org-primary) 13%, transparent); + + filter: blur(35px); + + opacity: 0; + + pointer-events: none; + + transition: opacity 220ms ease; +} + +.organization-card:hover { + transform: translateY(-6px); + + border-color: var(--org-border-hover); + + box-shadow: + 0 18px 45px color-mix(in srgb, var(--org-primary) 10%, transparent), + 0 4px 12px rgba(0, 0, 0, 0.08); +} + +.organization-card:hover::before, +.organization-card:hover::after { + opacity: 1; +} + +.organization-card:focus-visible { + outline: 3px solid + color-mix(in srgb, var(--org-primary) 35%, transparent); + + outline-offset: 4px; +} + +/* --------------------------------------------------------- + Card Top + --------------------------------------------------------- */ + +.organization-card__top { + display: flex; + align-items: flex-start; + justify-content: space-between; + + gap: 1rem; +} + +/* --------------------------------------------------------- + Logo + --------------------------------------------------------- */ + +.organization-card__logo-wrapper { + display: grid; + place-items: center; + + width: 68px; + height: 68px; + + flex-shrink: 0; + + border: 1px solid var(--org-border); + border-radius: 1rem; + + background: var(--org-background); + + box-shadow: + 0 6px 20px rgba(0, 0, 0, 0.05), + inset 0 1px 0 rgba(255, 255, 255, 0.06); + + transition: + transform 220ms ease, + border-color 220ms ease, + box-shadow 220ms ease; +} + +.organization-card:hover .organization-card__logo-wrapper { + transform: translateY(-2px) scale(1.04); + + border-color: color-mix( + in srgb, + var(--org-primary) 35%, + var(--org-border) + ); + + box-shadow: + 0 10px 25px color-mix(in srgb, var(--org-primary) 10%, transparent); +} + +.organization-card__logo { + width: 45px; + height: 45px; + object-fit: contain; + + transition: transform 220ms ease; } -#organization .organization-container .organization-card h3 { - margin-bottom: 10px; +.organization-card:hover .organization-card__logo { + transform: scale(1.06); +} + +/* --------------------------------------------------------- + Category + --------------------------------------------------------- */ + +.organization-card__category { + display: inline-flex; + align-items: center; + + max-width: 150px; + + padding: 0.35rem 0.65rem; + + border: 1px solid + color-mix(in srgb, var(--org-primary) 20%, var(--org-border)); + + border-radius: 999px; + + color: var(--org-primary); + + background: color-mix( + in srgb, + var(--org-primary) 6%, + var(--org-background) + ); + + font-size: 0.7rem; + font-weight: 700; + line-height: 1.2; + text-align: center; +} + +/* --------------------------------------------------------- + Card Content + --------------------------------------------------------- */ + +.organization-card__content { + flex: 1; + + padding-top: 1.4rem; +} + +.organization-card__title { + margin: 0 0 0.65rem; + + color: var(--org-heading); + font-size: 1.3rem; + line-height: 1.25; + font-weight: 750; +} + +.organization-card__description { + margin: 0; + + color: var(--org-muted); + + font-size: 0.92rem; + line-height: 1.65; +} + +/* --------------------------------------------------------- + Card Footer + --------------------------------------------------------- */ + +.organization-card__footer { + display: flex; + align-items: center; + justify-content: space-between; + + gap: 1rem; + + margin-top: 1.5rem; + padding-top: 1rem; + + border-top: 1px solid var(--org-border); +} + +.organization-card__action { + display: inline-flex; + align-items: center; + gap: 0.35rem; + + color: var(--org-primary); + + font-size: 0.82rem; + font-weight: 750; +} + +.organization-card__action svg { + transition: transform 180ms ease; +} + +.organization-card:hover .organization-card__action svg { + transform: translate(2px, -2px); +} + +.organization-card__github { + display: grid; + place-items: center; + + width: 32px; + height: 32px; + + flex-shrink: 0; + + border: 1px solid var(--org-border); + border-radius: 50%; + + color: var(--org-muted); + background: var(--org-background); + + transition: + color 180ms ease, + border-color 180ms ease, + transform 180ms ease; +} + +.organization-card:hover .organization-card__github { + color: var(--org-primary); + + border-color: color-mix( + in srgb, + var(--org-primary) 30%, + var(--org-border) + ); + + transform: rotate(-5deg); +} + +/* --------------------------------------------------------- + Disabled / Coming Soon Card + --------------------------------------------------------- */ + +.organization-card--disabled { + opacity: 0.78; + cursor: default; +} + +.organization-card--disabled:hover { + transform: translateY(-2px); +} + +/* --------------------------------------------------------- + Bottom CTA + --------------------------------------------------------- */ + +.organizations__cta { + display: flex; + align-items: center; + justify-content: space-between; + + gap: 2rem; + + margin-top: 2rem; + padding: 1.35rem 1.5rem; + + border: 1px solid var(--org-border); + border-radius: var(--org-radius); + + background: + linear-gradient( + 120deg, + color-mix(in srgb, var(--org-primary) 8%, var(--org-surface)), + var(--org-surface) + ); + + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.04); +} + +.organizations__cta-content { + display: flex; + align-items: center; + + gap: 1rem; +} + +.organizations__cta-icon { + display: grid; + place-items: center; + + width: 46px; + height: 46px; + + flex-shrink: 0; + + border-radius: 0.85rem; + + color: var(--org-primary); + + background: color-mix( + in srgb, + var(--org-primary) 10%, + var(--org-background) + ); } -#organization .organization-container .organization-card p { +.organizations__cta h3 { + margin: 0 0 0.25rem; + + color: var(--org-heading); + font-size: 1rem; - text-align: justify; } +.organizations__cta p { + margin: 0; + + color: var(--org-muted); + + font-size: 0.85rem; + line-height: 1.5; +} + +.organizations__cta-button { + display: inline-flex; + align-items: center; + justify-content: center; + + gap: 0.5rem; + + flex-shrink: 0; + + padding: 0.7rem 1rem; + + border: 1px solid + color-mix(in srgb, var(--org-primary) 35%, var(--org-border)); + + border-radius: 0.75rem; + + color: var(--org-primary); -@media (max-width: 768px) { - #organization .organization-container { - grid-template-columns: repeat(2, 1fr); - } + background: color-mix( + in srgb, + var(--org-primary) 7%, + var(--org-background) + ); - #organization .organization-container .organization-card p { - display: none; - } + font-size: 0.85rem; + font-weight: 750; + + text-decoration: none; + + transition: + background 180ms ease, + border-color 180ms ease, + transform 180ms ease; +} + +.organizations__cta-button:hover { + color: var(--org-primary); + + background: color-mix( + in srgb, + var(--org-primary) 13%, + var(--org-background) + ); + + border-color: color-mix( + in srgb, + var(--org-primary) 55%, + var(--org-border) + ); + + transform: translateY(-2px); +} + +.organizations__cta-button:focus-visible { + outline: 3px solid + color-mix(in srgb, var(--org-primary) 30%, transparent); + + outline-offset: 3px; +} + +/* --------------------------------------------------------- + Tablet + --------------------------------------------------------- */ + +@media screen and (max-width: 996px) { + .organizations { + padding: 4rem 1rem; + } + + .organizations__grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); } - - @media (max-width: 480px) { - #organization .organization-container { - grid-template-columns: 1fr; - } + + .organization-card { + min-height: 315px; + } +} + +/* --------------------------------------------------------- + Mobile + --------------------------------------------------------- */ + +@media screen and (max-width: 640px) { + .organizations { + padding: 3.5rem 1rem; + } + + .organizations__header { + margin-bottom: 2rem; + } + + .organizations__subtitle { + font-size: 0.95rem; + line-height: 1.65; + } + + .organizations__grid { + grid-template-columns: 1fr; + + gap: 1rem; + } + + .organization-card { + min-height: auto; + + padding: 1.15rem; + } + + .organization-card__description { + font-size: 0.88rem; + } + + .organizations__cta { + align-items: flex-start; + flex-direction: column; + + gap: 1.25rem; + + padding: 1.2rem; + } + + .organizations__cta-content { + align-items: flex-start; + } + + .organizations__cta-button { + width: 100%; + } +} + +/* --------------------------------------------------------- + Small Mobile + --------------------------------------------------------- */ + +@media screen and (max-width: 420px) { + .organizations { + padding-inline: 0.75rem; + } + + .organization-card__category { + max-width: 125px; + + font-size: 0.65rem; + } + + .organization-card__logo-wrapper { + width: 60px; + height: 60px; + } + + .organization-card__logo { + width: 40px; + height: 40px; + } + + .organization-card__title { + font-size: 1.15rem; + } +} + +/* --------------------------------------------------------- + Reduced Motion + --------------------------------------------------------- */ + +@media (prefers-reduced-motion: reduce) { + .organization-card, + .organization-card::before, + .organization-card::after, + .organization-card__logo-wrapper, + .organization-card__logo, + .organization-card__action svg, + .organization-card__github, + .organizations__cta-button { + transition: none; + } + + .organization-card:hover, + .organization-card--disabled:hover, + .organizations__cta-button:hover { + transform: none; } - \ No newline at end of file +} \ No newline at end of file diff --git a/src/components/HomePage/style.css b/src/components/HomePage/style.css deleted file mode 100644 index c5588ba4c..000000000 --- a/src/components/HomePage/style.css +++ /dev/null @@ -1,228 +0,0 @@ -.resources-section { - text-decoration: none; - margin-top: 3.75rem 0; - padding: 0 1.5rem; -} - -.resources-section .resources-container { - margin-left: auto; - margin-right: auto; - max-width: 100%; -} - -.resources-section .resources-container .resources-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 2.5rem; -} - -.resources-section .resources-container .resources-header .codeharborhub-badge { - display: inline-block; - margin-top: 1rem; - margin-bottom: 1rem; - padding: 0.5rem 1rem; - background-color: var(--ifm-color-primary); - color: white; - border-radius: 5px; -} - -.resources-section .resources-container .resources-header .resources-title { - margin-bottom: 1.5rem; - font-size: 2.25rem; -} - -.resources-section .resources-container .resources-header .resources-all-blogs { - font-family: "Jakarta", sans-serif; - font-size: 0.875rem; - font-weight: 600; - color: var(--ifm-color-primary); -} - -.resources-section .resources-container .resources-header .arrow-icon { - margin-left: 0.25rem; -} - -.resources-section .resources-container .resources-filters { - display: grid; - grid-auto-flow: column; - grid-auto-columns: max-content; - gap: 10px; - margin: 1.5rem 0; - border-radius: 0.5rem; - padding: 0 0 30px 10px; - font-family: "Jakarta", sans-serif; - font-size: 0.875rem; - font-weight: 600; - scroll-behavior: smooth; - overflow-x: auto; - white-space: nowrap; - -ms-overflow-style: none; - scrollbar-width: none; -} - -.bg-secondary-700 { - background-color: var(--ifm-color-secondary); -} - -.dark\:bg-secondary-700 { - background-color: var(--ifm-color-secondary-dark); -} - -.resources-section .resources-container .resources-filters .filter-button { - padding: 0.5rem 1rem; - border: none; - cursor: pointer; - transition: background-color 0.2s, color 0.2s; - border-radius: 25px; - font-size: 0.875rem; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1), 0 1.5px 4px rgba(0, 0, 0, 0.06); -} - -.resources-section - .resources-container - .resources-filters - .filter-button.active { - background-color: var(--ifm-color-primary); - color: #f9f9f9; -} - -.resources-section .resources-container .resources-content { - position: relative; - display: flex; - flex-direction: column; -} - -.resources-section .resources-container .resources-content .resources-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 1.5rem; -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .resource { - text-decoration: none; - color: inherit; - border-radius: 0.5rem; - box-shadow: 0 0 10px rgba(5, 174, 216, 0.386); -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .fade-in { - animation: fadeIn 1s ease-in-out; -} - -@keyframes fadeIn { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .resource-image-container { - margin-bottom: 0.75rem; - overflow: hidden; - border-radius: 0.5rem; -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .resource-image { - aspect-ratio: 16 / 9; - width: 100%; - height: 100%; - object-fit: cover; - transition: transform 0.2s; -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .resource:hover - .resource-image { - transform: scale(1.1); -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .resource-title { - font-weight: 600; - margin-bottom: 8px; - padding: 0 0.5rem; -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .resource-description { - line-height: 1.375; - padding: 0 0.5rem; -} - -.resources-section - .resources-container - .resources-content - .resources-grid.resource-footer { - margin-top: 16px; - display: flex; - justify-content: space-between; -} - -.resources-section - .resources-container - .resources-content - .resources-grid - .resource-duration { - font-size: 14px; - padding: 0.5rem; - color: var(--ifm-color-primary); -} - -.resources-section .resources-container .pagination { - display: flex; - justify-content: center; - gap: 1rem; - margin-top: 2.5rem; -} - -.resources-section .resources-container .pagination .pagination-button { - background: none; - border: none; - cursor: pointer; - padding: 8px; -} - -.resources-section .resources-container .pagination .chevron-icon { - width: 24px; - height: 24px; -} - -@media (max-width: 768px) { - .resources-section .resources-container .resources-content .resources-grid { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (max-width: 480px) { - .resources-section .resources-container .resources-content .resources-grid { - grid-template-columns: 1fr; - } -} diff --git a/src/components/Tweet/Tweet.css b/src/components/Tweet/Tweet.css new file mode 100644 index 000000000..bacf5bfc1 --- /dev/null +++ b/src/components/Tweet/Tweet.css @@ -0,0 +1,371 @@ +/* ========================================================= + CodeHarborHub - Testimonial / Tweet Card + ========================================================= */ + +.tweet-card { + --tweet-primary: var(--ifm-color-primary); + + --tweet-background: var(--ifm-background-surface-color); + --tweet-page-background: var(--ifm-background-color); + + --tweet-heading: var(--ifm-heading-color); + --tweet-text: var(--ifm-font-color-base); + --tweet-muted: var(--ifm-color-emphasis-700); + + --tweet-border: var(--ifm-color-emphasis-200); + + position: relative; + + display: flex; + flex-direction: column; + + width: 100%; + min-height: 315px; + + padding: 1.35rem; + + overflow: hidden; + + border: 1px solid var(--tweet-border); + border-radius: 1.2rem; + + background: + linear-gradient( + 145deg, + color-mix( + in srgb, + var(--tweet-primary) 4%, + var(--tweet-background) + ), + var(--tweet-background) + ); + + box-shadow: + 0 8px 30px rgba(0, 0, 0, 0.035), + inset 0 1px 0 rgba(255, 255, 255, 0.05); + + transition: + transform 220ms ease, + border-color 220ms ease, + box-shadow 220ms ease; +} + +.tweet-card::before { + content: ""; + + position: absolute; + + top: 0; + left: 1.2rem; + right: 1.2rem; + + height: 2px; + + border-radius: 0 0 999px 999px; + + background: linear-gradient( + 90deg, + transparent, + var(--tweet-primary), + transparent + ); + + opacity: 0; + + transition: opacity 220ms ease; +} + +.tweet-card:hover { + transform: translateY(-5px); + + border-color: color-mix( + in srgb, + var(--tweet-primary) 35%, + var(--tweet-border) + ); + + box-shadow: + 0 18px 45px + color-mix( + in srgb, + var(--tweet-primary) 9%, + transparent + ), + 0 5px 15px rgba(0, 0, 0, 0.06); +} + +.tweet-card:hover::before { + opacity: 1; +} + +/* --------------------------------------------------------- + Quote + --------------------------------------------------------- */ + +.tweet-card__quote { + position: absolute; + + top: 1rem; + right: 1rem; + + display: grid; + place-items: center; + + width: 38px; + height: 38px; + + border-radius: 0.7rem; + + color: color-mix( + in srgb, + var(--tweet-primary) 75%, + var(--tweet-muted) + ); + + background: color-mix( + in srgb, + var(--tweet-primary) 7%, + var(--tweet-page-background) + ); +} + +/* --------------------------------------------------------- + Header + --------------------------------------------------------- */ + +.tweet-card__header { + display: flex; + align-items: center; + justify-content: space-between; + + gap: 1rem; + + padding-right: 2.5rem; +} + +.tweet-card__identity { + display: flex; + align-items: center; + + gap: 0.75rem; + + min-width: 0; +} + +.tweet-card__avatar { + width: 48px; + height: 48px; + + flex-shrink: 0; + + object-fit: cover; + + border: 2px solid + color-mix( + in srgb, + var(--tweet-primary) 22%, + var(--tweet-border) + ); + + border-radius: 50%; + + background: var(--tweet-page-background); +} + +.tweet-card__avatar--fallback { + display: grid; + place-items: center; + + color: var(--tweet-primary); + + background: color-mix( + in srgb, + var(--tweet-primary) 10%, + var(--tweet-page-background) + ); + + font-size: 0.8rem; + font-weight: 800; +} + +.tweet-card__user { + display: flex; + flex-direction: column; + + min-width: 0; +} + +.tweet-card__name { + overflow: hidden; + + color: var(--tweet-heading); + + font-size: 0.92rem; + line-height: 1.3; + + text-overflow: ellipsis; + white-space: nowrap; +} + +.tweet-card__handle { + color: var(--tweet-muted); + + font-size: 0.76rem; +} + +.tweet-card__github { + display: grid; + place-items: center; + + width: 34px; + height: 34px; + + flex-shrink: 0; + + border: 1px solid var(--tweet-border); + border-radius: 50%; + + color: var(--tweet-muted); + + background: var(--tweet-page-background); + + transition: + color 180ms ease, + border-color 180ms ease, + transform 180ms ease; +} + +.tweet-card__github:hover { + color: var(--tweet-primary); + + border-color: color-mix( + in srgb, + var(--tweet-primary) 35%, + var(--tweet-border) + ); + + transform: rotate(-5deg); +} + +/* --------------------------------------------------------- + Content + --------------------------------------------------------- */ + +.tweet-card__content { + flex: 1; + + margin-top: 1.4rem; + + color: var(--tweet-text); + + font-size: 0.92rem; + line-height: 1.75; +} + +.tweet-card__content p { + margin: 0; +} + +/* --------------------------------------------------------- + Footer + --------------------------------------------------------- */ + +.tweet-card__footer { + display: flex; + align-items: center; + justify-content: space-between; + + gap: 1rem; + + margin-top: 1.5rem; + padding-top: 1rem; + + border-top: 1px solid var(--tweet-border); +} + +.tweet-card__date { + display: inline-flex; + align-items: center; + + gap: 0.4rem; + + color: var(--tweet-muted); + + font-size: 0.72rem; +} + +.tweet-card__date svg { + color: var(--tweet-primary); +} + +.tweet-card__link { + display: inline-flex; + align-items: center; + + gap: 0.3rem; + + color: var(--tweet-primary); + + font-size: 0.76rem; + font-weight: 750; + + text-decoration: none; + + transition: gap 180ms ease; +} + +.tweet-card__link:hover { + color: var(--tweet-primary); + + gap: 0.55rem; +} + +.tweet-card__link:focus-visible, +.tweet-card__github:focus-visible { + outline: 3px solid + color-mix( + in srgb, + var(--tweet-primary) 30%, + transparent + ); + + outline-offset: 3px; +} + +/* --------------------------------------------------------- + Mobile + --------------------------------------------------------- */ + +@media screen and (max-width: 560px) { + .tweet-card { + min-height: 295px; + + padding: 1.15rem; + } + + .tweet-card__content { + font-size: 0.88rem; + line-height: 1.7; + } +} + +/* --------------------------------------------------------- + Reduced Motion + --------------------------------------------------------- */ + +@media (prefers-reduced-motion: reduce) { + .tweet-card, + .tweet-card::before, + .tweet-card__github, + .tweet-card__link { + transition: none; + } + + .tweet-card:hover { + transform: none; + } + + .tweet-card__github:hover { + transform: none; + } +} \ No newline at end of file diff --git a/src/components/Tweet/index.tsx b/src/components/Tweet/index.tsx index 2df3b6097..4702d50e2 100644 --- a/src/components/Tweet/index.tsx +++ b/src/components/Tweet/index.tsx @@ -1,12 +1,22 @@ import React, { type ReactNode } from "react"; +import { + ArrowUpRight, + CalendarDays, + Github, + Quote, +} from "lucide-react"; + import Link from "@docusaurus/Link"; + +import "./Tweet.css"; + export interface Props { url: string; handle: string; name: string; content: ReactNode; date: string; - githubUsername: string; + githubUsername?: string; } export default function Tweet({ @@ -17,36 +27,93 @@ export default function Tweet({ date, githubUsername, }: Props): JSX.Element { + const avatarUrl = githubUsername + ? `https://github.com/${githubUsername}.png?size=96` + : undefined; + + const initials = name + .split(" ") + .filter(Boolean) + .slice(0, 2) + .map((part) => part[0]) + .join("") + .toUpperCase(); + return ( -
-
-
- {name} -
- - {name} - - @{handle} +
+ {/* Decorative quote */} + + + {/* Header */} +
+
+ {avatarUrl ? ( + { + event.currentTarget.style.display = "none"; + }} + /> + ) : ( + + )} + +
+ {name} + + + @{handle} +
-
-
+ {githubUsername && ( + + + )} + + + {/* Content */} +
{content}
-
- - {date} + {/* Footer */} +
+
+
+ + + View post +
-
+ + ); -} +} \ No newline at end of file diff --git a/src/components/WhyChooseCodeHarbor.tsx b/src/components/WhyChooseCodeHarbor.tsx deleted file mode 100644 index cd341ec25..000000000 --- a/src/components/WhyChooseCodeHarbor.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import Link from "@docusaurus/Link"; -import { - BookOpen, - Code2, - Users2, - Zap, - Trophy, - Github -} from 'lucide-react'; - -const reasons = [ - { - title: "Free Education", - desc: "Comprehensive tech curriculum taught by industry masters, 100% free.", - icon: , - }, - { - title: "Open Source", - desc: "Contribute to real-world projects and build your GitHub profile.", - icon: , - }, - { - title: "Live Editor", - desc: "Practice coding in real-time with our integrated browser-based IDE.", - icon: , - }, - { - title: "Active Community", - desc: "Join over 100+ contributors and thousands of learners worldwide.", - icon: , - }, - { - title: "DSA Mastery", - desc: "Curated problems and solutions to help you crack technical interviews.", - icon: , - }, - { - title: "Expert Guidance", - desc: "Roadmaps and tutorials designed for both beginners and pros.", - icon: , - }, -]; - -const WhyChooseCodeHarbor = () => { - return ( -
- {/* Subtle Background Pattern */} -
- -
- -
- - {/* Header */} -
- - Why CodeHarborHub? - -

- Empowering Your Tech Journey -

-

- We shatter limitations by offering an exclusive open-source platform for mastering - in-demand skills and launching your dream career. -

-
- - {/* Feature Grid */} -
- {reasons.map((item, index) => ( -
- {/* Animated Glow Effect */} -
- - {/* Icon Container */} -
- {item.icon} -
- - {/* Content */} -

- {item.title} -

-

- {item.desc} -

- - {/* Bottom Accent Line */} -
-
- ))} -
- - {/* Call to Action */} -
- - - Join Our Open Source Mission - -
- -
-
- ); -}; - -export default WhyChooseCodeHarbor; \ No newline at end of file diff --git a/src/context/CommunityStats/index.tsx b/src/context/CommunityStats/index.tsx index 582c1dfbb..5b05fdf7c 100644 --- a/src/context/CommunityStats/index.tsx +++ b/src/context/CommunityStats/index.tsx @@ -11,81 +11,227 @@ import React, { interface ICommunityStatsContext { githubStarCount: number; githubStarCountText: string; + githubContributorsCount: number; + githubContributorsCountText: string; + githubForksCount: number; + githubForksCountText: string; + loading: boolean; + error: string | null; + refetch: () => Promise; } -export const CommunityStatsContext = createContext(undefined); +const GITHUB_REPO = + "CodeHarborHub/codeharborhub.github.io"; + +const GITHUB_API = + `https://api.github.com/repos/${GITHUB_REPO}`; + +export const CommunityStatsContext = createContext< + ICommunityStatsContext | undefined +>(undefined); + +interface GitHubRepositoryResponse { + stargazers_count?: number; + forks_count?: number; +} + +interface GitHubContributor { + login?: string; + contributions?: number; +} + +/** + * Fetch the total number of contributors. + * + * GitHub's repository endpoint doesn't directly expose a + * contributor count, so we use the contributors endpoint + * with `per_page=1` and read GitHub's pagination metadata. + */ +const fetchContributorCount = async ( + signal: AbortSignal +): Promise => { + const response = await fetch( + `${GITHUB_API}/contributors?per_page=1&anon=true`, + { + method: "GET", + headers: { + Accept: "application/vnd.github+json", + }, + signal, + } + ); + + if (!response.ok) { + throw new Error( + `Unable to fetch contributors (${response.status})` + ); + } + + const contributors: GitHubContributor[] = + await response.json(); + + const linkHeader = response.headers.get("Link"); -export const CommunityStatsProvider: FC = ({ children }) => { + if (linkHeader) { + const lastPageMatch = linkHeader.match( + /[?&]page=(\d+)>;\s*rel="last"/ + ); + + if (lastPageMatch) { + return Number(lastPageMatch[1]); + } + } + + return contributors.length; +}; + +export const CommunityStatsProvider: FC< + React.PropsWithChildren +> = ({ children }) => { const [loading, setLoading] = useState(true); - const [githubStarCount, setGithubStarCount] = useState(0); - const [githubContributorsCount, setGithubContributorsCount] = useState(0); - const [githubForksCount, setGithubForksCount] = useState(0); - - const fetchGithubCount = useCallback(async (signal: AbortSignal) => { - try { - setLoading(true); - - const response = await fetch( - "https://api.github.com/repos/CodeHarborHub/codeharborhub.github.io", - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - signal, + const [error, setError] = useState(null); + + const [githubStarCount, setGithubStarCount] = + useState(0); + + const [ + githubContributorsCount, + setGithubContributorsCount, + ] = useState(0); + + const [githubForksCount, setGithubForksCount] = + useState(0); + + const fetchGithubCount = useCallback( + async (signal?: AbortSignal) => { + const controller = + signal ? undefined : new AbortController(); + + const requestSignal = + signal ?? controller!.signal; + + try { + setLoading(true); + setError(null); + + const [repositoryResponse, contributorCount] = + await Promise.all([ + fetch(GITHUB_API, { + method: "GET", + headers: { + Accept: + "application/vnd.github+json", + }, + signal: requestSignal, + }), + + fetchContributorCount(requestSignal), + ]); + + if (!repositoryResponse.ok) { + throw new Error( + `Unable to fetch repository data (${repositoryResponse.status})` + ); } - ); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } + const repository: GitHubRepositoryResponse = + await repositoryResponse.json(); - const json = await response.json(); - setGithubStarCount(json.stargazers_count || 0); - setGithubContributorsCount(140 || 0); // Assuming this property exists - setGithubForksCount(json.forks_count || 0); - } catch (error) { - console.error("Error fetching GitHub data:", error); - } finally { - setLoading(false); - } - }, []); + setGithubStarCount( + repository.stargazers_count ?? 0 + ); + + setGithubForksCount( + repository.forks_count ?? 0 + ); + + setGithubContributorsCount( + contributorCount + ); + } catch (err) { + if ( + err instanceof DOMException && + err.name === "AbortError" + ) { + return; + } + + console.error( + "Failed to fetch CodeHarborHub GitHub statistics:", + err + ); + + setError( + err instanceof Error + ? err.message + : "Unable to load community statistics." + ); + } finally { + setLoading(false); + } + }, + [] + ); useEffect(() => { - const abortController = new AbortController(); - fetchGithubCount(abortController.signal); + const controller = new AbortController(); + + void fetchGithubCount(controller.signal); return () => { - abortController.abort(); + controller.abort(); }; }, [fetchGithubCount]); - const githubStarCountText = useMemo(() => { - return convertStatToText(githubStarCount); - }, [githubStarCount]); + const githubStarCountText = useMemo( + () => convertStatToText(githubStarCount), + [githubStarCount] + ); - const githubContributorsCountText = useMemo(() => { - return convertStatToText(githubContributorsCount); - }, [githubContributorsCount]); + const githubContributorsCountText = useMemo( + () => convertStatToText(githubContributorsCount), + [githubContributorsCount] + ); - const githubForksCountText = useMemo(() => { - return convertStatToText(githubForksCount); - }, [githubForksCount]); + const githubForksCountText = useMemo( + () => convertStatToText(githubForksCount), + [githubForksCount] + ); - const value = { - githubStarCount, - githubStarCountText, - githubContributorsCount, - githubContributorsCountText, - githubForksCount, - githubForksCountText, - loading, - refetch: fetchGithubCount, - }; + const value = useMemo( + () => ({ + githubStarCount, + githubStarCountText, + + githubContributorsCount, + githubContributorsCountText, + + githubForksCount, + githubForksCountText, + + loading, + error, + + refetch: async () => { + await fetchGithubCount(); + }, + }), + [ + githubStarCount, + githubStarCountText, + githubContributorsCount, + githubContributorsCountText, + githubForksCount, + githubForksCountText, + loading, + error, + fetchGithubCount, + ] + ); return ( @@ -94,26 +240,46 @@ export const CommunityStatsProvider: FC = ({ children }) => { ); }; -export const useCommunityStatsContext = () => { - const context = useContext(CommunityStatsContext); - if (context === undefined) { - throw new Error("useCommunityStatsContext must be used within a CommunityStatsProvider"); +export const useCommunityStatsContext = + (): ICommunityStatsContext => { + const context = useContext( + CommunityStatsContext + ); + + if (!context) { + throw new Error( + "useCommunityStatsContext must be used within CommunityStatsProvider" + ); + } + + return context; + }; + +export const convertStatToText = ( + num: number +): string => { + if (!Number.isFinite(num)) { + return "0"; } - return context; -}; -export const convertStatToText = (num: number) => { - const hasIntlSupport = - typeof Intl === "object" && Intl && typeof Intl.NumberFormat === "function"; + if ( + typeof Intl !== "undefined" && + typeof Intl.NumberFormat === "function" + ) { + return new Intl.NumberFormat("en-US", { + notation: "compact", + compactDisplay: "short", + maximumSignificantDigits: 3, + }).format(num); + } - if (!hasIntlSupport) { - return `${(num / 1000).toFixed(1)}k`; + if (num >= 1_000_000) { + return `${(num / 1_000_000).toFixed(1)}M`; } - const formatter = new Intl.NumberFormat("en-US", { - notation: "compact", - compactDisplay: "short", - maximumSignificantDigits: 3, - }); - return formatter.format(num); -}; + if (num >= 1_000) { + return `${(num / 1_000).toFixed(1)}K`; + } + + return String(num); +}; \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 85b452370..f7e0f719f 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,7 +2,7 @@ import clsx from "clsx"; import style from "./index.module.css"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import Layout from "@theme/Layout"; -import Heading from "@theme/Heading"; +// import Heading from "@theme/Heading"; import Head from "@docusaurus/Head"; import Header from "../components/HomePage/Header"; import ResourcesSection from "../components/HomePage/ResourcesSection"; @@ -10,12 +10,13 @@ import ScrollTopToButton from "../components/Buttons/bottom/ScrollTopToButton"; import ScrollBottomToTop from "../components/Buttons/top/ScrollBottomToTop"; import { LandingCommunity } from "../components/HomePage/Community"; import { CommunityStatsProvider } from "../context/CommunityStats"; -import Faq from "./Faq"; +// import Faq from "./Faq"; import Organizations from "../components/HomePage/organizations"; import TweetsSection from "../components/HomePage/TweetsSection"; -import WhyChooseCodeHarbor from "../components/WhyChooseCodeHarbor"; +import WhyChooseCodeHarbor from "../components/HomePage/WhyChooseCodeHarbor"; import ContributorList from "../components/ContributorList"; import AntiAdBlock from "../components/AntiAdBlock"; +import FinalCTA from "../components/HomePage/FinalCTA"; export default function Home() { const { siteConfig } = useDocusaurusContext(); @@ -36,7 +37,7 @@ export default function Home() { custom-element="amp-auto-ads" src="https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js" /> - +
@@ -47,15 +48,11 @@ export default function Home() {
-
- -
+
-
- -
+
@@ -65,11 +62,6 @@ export default function Home() {
-
- - Our GitHub Organizations - -

@@ -85,17 +77,25 @@ export default function Home() {
*/} + + -
- - Our Contributors - -
- +
+ + ); -} \ No newline at end of file +} From dcfc9af318b5440ec218cd682ac2eb9ca24e292d Mon Sep 17 00:00:00 2001 From: Ajay Dhangar Date: Sun, 13 Sep 2026 07:59:45 +0530 Subject: [PATCH 2/2] Fix broken link err... --- src/components/HomePage/FinalCTA/index.tsx | 4 ++-- src/components/HomePage/ResourcesSection/index.tsx | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/HomePage/FinalCTA/index.tsx b/src/components/HomePage/FinalCTA/index.tsx index 3f7572930..1dc6a7785 100644 --- a/src/components/HomePage/FinalCTA/index.tsx +++ b/src/components/HomePage/FinalCTA/index.tsx @@ -35,7 +35,7 @@ const FinalCTA: React.FC = () => { {/* CTAs */}
View all resources