From 6c5e630c81a548c1eb052c75fd9aba84b586a502 Mon Sep 17 00:00:00 2001 From: Alex Marasigan Date: Thu, 23 Apr 2026 16:15:04 -0700 Subject: [PATCH 01/19] light/dark icons --- .../starter-kit/resources/tabler_dark-brand-figma.svg | 5 +++++ .../resources/tabler_dark-presentation-analytics.svg | 3 +++ .../hackers/starter-kit/resources/tabler_dark-sparkles-2.svg | 3 +++ ...{tabler_writing-sign.svg => tabler_dark-writing-sign.svg} | 0 .../{tabler_brand-figma.svg => tabler_light-brand-figma.svg} | 0 ...analytics.svg => tabler_light-presentation-analytics.svg} | 0 .../{tabler_sparkles-2.svg => tabler_light-sparkles-2.svg} | 0 .../starter-kit/resources/tabler_light-writing-sign.svg | 3 +++ 8 files changed, 14 insertions(+) create mode 100644 public/hackers/starter-kit/resources/tabler_dark-brand-figma.svg create mode 100644 public/hackers/starter-kit/resources/tabler_dark-presentation-analytics.svg create mode 100644 public/hackers/starter-kit/resources/tabler_dark-sparkles-2.svg rename public/hackers/starter-kit/resources/{tabler_writing-sign.svg => tabler_dark-writing-sign.svg} (100%) rename public/hackers/starter-kit/resources/{tabler_brand-figma.svg => tabler_light-brand-figma.svg} (100%) rename public/hackers/starter-kit/resources/{tabler_presentation-analytics.svg => tabler_light-presentation-analytics.svg} (100%) rename public/hackers/starter-kit/resources/{tabler_sparkles-2.svg => tabler_light-sparkles-2.svg} (100%) create mode 100644 public/hackers/starter-kit/resources/tabler_light-writing-sign.svg diff --git a/public/hackers/starter-kit/resources/tabler_dark-brand-figma.svg b/public/hackers/starter-kit/resources/tabler_dark-brand-figma.svg new file mode 100644 index 00000000..02a6b64a --- /dev/null +++ b/public/hackers/starter-kit/resources/tabler_dark-brand-figma.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/hackers/starter-kit/resources/tabler_dark-presentation-analytics.svg b/public/hackers/starter-kit/resources/tabler_dark-presentation-analytics.svg new file mode 100644 index 00000000..ac81cc31 --- /dev/null +++ b/public/hackers/starter-kit/resources/tabler_dark-presentation-analytics.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/hackers/starter-kit/resources/tabler_dark-sparkles-2.svg b/public/hackers/starter-kit/resources/tabler_dark-sparkles-2.svg new file mode 100644 index 00000000..ee605018 --- /dev/null +++ b/public/hackers/starter-kit/resources/tabler_dark-sparkles-2.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/hackers/starter-kit/resources/tabler_writing-sign.svg b/public/hackers/starter-kit/resources/tabler_dark-writing-sign.svg similarity index 100% rename from public/hackers/starter-kit/resources/tabler_writing-sign.svg rename to public/hackers/starter-kit/resources/tabler_dark-writing-sign.svg diff --git a/public/hackers/starter-kit/resources/tabler_brand-figma.svg b/public/hackers/starter-kit/resources/tabler_light-brand-figma.svg similarity index 100% rename from public/hackers/starter-kit/resources/tabler_brand-figma.svg rename to public/hackers/starter-kit/resources/tabler_light-brand-figma.svg diff --git a/public/hackers/starter-kit/resources/tabler_presentation-analytics.svg b/public/hackers/starter-kit/resources/tabler_light-presentation-analytics.svg similarity index 100% rename from public/hackers/starter-kit/resources/tabler_presentation-analytics.svg rename to public/hackers/starter-kit/resources/tabler_light-presentation-analytics.svg diff --git a/public/hackers/starter-kit/resources/tabler_sparkles-2.svg b/public/hackers/starter-kit/resources/tabler_light-sparkles-2.svg similarity index 100% rename from public/hackers/starter-kit/resources/tabler_sparkles-2.svg rename to public/hackers/starter-kit/resources/tabler_light-sparkles-2.svg diff --git a/public/hackers/starter-kit/resources/tabler_light-writing-sign.svg b/public/hackers/starter-kit/resources/tabler_light-writing-sign.svg new file mode 100644 index 00000000..ad7468ff --- /dev/null +++ b/public/hackers/starter-kit/resources/tabler_light-writing-sign.svg @@ -0,0 +1,3 @@ + + + From f0b42a319a8123eb949d20f55d007405fcbc8cac Mon Sep 17 00:00:00 2001 From: Alex Marasigan Date: Thu, 23 Apr 2026 16:15:19 -0700 Subject: [PATCH 02/19] importing light/dark icons Co-authored-by: Copilot --- .../Resources/DesignDevResources.tsx | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx index 38b9fe4f..772da822 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx @@ -8,10 +8,15 @@ import { Responsibilities } from './Responsibilities'; import { Tips } from './Tips'; // Icons -import writing_sign from '@public/hackers/starter-kit/resources/tabler_writing-sign.svg'; -import presentation_analytics from '@public/hackers/starter-kit/resources/tabler_presentation-analytics.svg'; -import sparkles from '@public/hackers/starter-kit/resources/tabler_sparkles-2.svg'; -import figma from '@public/hackers/starter-kit/resources/tabler_brand-figma.svg'; +import dark_writing_sign from '@public/hackers/starter-kit/resources/tabler_dark-writing-sign.svg'; +import dark_presentation_analytics from '@public/hackers/starter-kit/resources/tabler_dark-presentation-analytics.svg'; +import dark_sparkles from '@public/hackers/starter-kit/resources/tabler_dark-sparkles-2.svg'; +import dark_figma from '@public/hackers/starter-kit/resources/tabler_dark-brand-figma.svg'; + +import light_writing_sign from '@public/hackers/starter-kit/resources/tabler_light-writing-sign.svg'; +import light_presentation_analytics from '@public/hackers/starter-kit/resources/tabler_light-presentation-analytics.svg'; +import light_sparkles from '@public/hackers/starter-kit/resources/tabler_light-sparkles-2.svg'; +import light_figma from '@public/hackers/starter-kit/resources/tabler_light-brand-figma.svg'; import github from '@public/hackers/starter-kit/resources/tabler_brand-github.svg'; import desktop_code from '@public/hackers/starter-kit/resources/tabler_device-desktop-code.svg'; @@ -42,25 +47,29 @@ export default function DesignDevResources() { // Data const designer_responsibilities = [ { - icon: writing_sign, + dark_icon: dark_writing_sign, + light_icon: light_writing_sign, title: 'Research problem statement', description: 'A problem statement should include: background, people affected, and the impact of the problem.', }, { - icon: figma, + dark_icon: dark_figma, + light_icon: light_figma, title: 'Craft UI/UX visuals', description: 'Create wireframes, mockups, and prototypes that bring the product vision to life.', }, { - icon: sparkles, + dark_icon: dark_sparkles, + light_icon: light_sparkles, title: 'Iterate on feedback & refine', description: 'Collaborate with your team, gather insights, and polish the design through multiple iterations.', }, { - icon: presentation_analytics, + dark_icon: dark_presentation_analytics, + light_icon: light_presentation_analytics, title: 'Create presentation & pitch', description: 'Be prepared to present design decisions, rationale, and final deliverables to the judges!', @@ -69,25 +78,29 @@ export default function DesignDevResources() { const developer_responsibilities = [ { - icon: writing_sign, + dark_icon: dark_writing_sign, + light_icon: light_writing_sign, title: 'Plan out system design', description: 'Figure out what tech stack and technologies you want to use for your product.', }, { - icon: github, + dark_icon: github, + light_icon: github, title: 'Set up codebase scaffolding', description: 'Create a GitHub repo and initialize the project so your team can collaborate.', }, { - icon: message_chatbot, + dark_icon: message_chatbot, + light_icon: message_chatbot, title: 'Divide and conquer', description: 'Split the product into features and assign tasks so teammates can build in parallel.', }, { - icon: desktop_code, + dark_icon: desktop_code, + light_icon: desktop_code, title: 'Build a functioning demo', description: 'Implement core features and ensure you have a working product ready for presentations.', From 217fb37e181c149c5bd0a71dad6b3a3a79917e9d Mon Sep 17 00:00:00 2001 From: Alex Marasigan Date: Thu, 23 Apr 2026 16:23:16 -0700 Subject: [PATCH 03/19] yay all the scrolling stuff!!! Co-authored-by: Copilot --- .../StarterKit/Resources/Responsibilities.tsx | 110 ++++++++++++------ 1 file changed, 75 insertions(+), 35 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 6d6bef11..dbe51ff4 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -1,14 +1,15 @@ +import next from 'next'; import Image from 'next/image'; import type { StaticImageData } from 'next/image'; -import { useRef, useState } from 'react'; +import { useRef, useState, useEffect } from 'react'; interface Responsibility { - icon: StaticImageData; + dark_icon: StaticImageData; + light_icon: StaticImageData; title: string; description: string; } -// shi figure out that bar!!!!!! export function Responsibilities({ image, title, @@ -23,41 +24,80 @@ export function Responsibilities({ const sectionRef = useRef(null); const scrollRef = useRef(null); - const [responsibilityIndex] = useState(0); - const [scrollPos] = useState(50); - - /*useEffect(() => { - const onPageScroll = () => { - const section = sectionRef.current.scrollTo(); - const scrollContainer = scrollRef.current; - if (!section) return; - - const rect = section.getBoundingClientRect(); - const anchorY = window.innerHeight * 0.35; - const traveled = anchorY - rect.top; - const rawProgress = traveled / Math.max(rect.height, 1); - const progress = Math.min(Math.max(rawProgress, 0), 0.999); - const nextIndex = Math.floor(progress * responsibilities.length); - const clampedIndex = Math.max(0, Math.min(responsibilities.length - 1, nextIndex)); - - setResponsibilityIndex(clampedIndex); - - if (scrollContainer) { - const maxHeight = scrollContainer.clientHeight; - const fillProgress = responsibilities.length > 1 ? clampedIndex / (responsibilities.length - 1) : 0; - setScrollPos(fillProgress * maxHeight); + const [responsibilityIndex, setResponsibilityIndex] = useState(0); + const [scrollPos, setScrollPos] = useState(50); + const scrollTimeout = useRef(null); + + useEffect(() => { + const snapToNearest = (pos: number) => { + const maxHeight = scrollRef.current?.clientHeight || 50; + const positions = [ + maxHeight * 1 / 8, + maxHeight * 3 / 8, + maxHeight * 21 / 32, + maxHeight, + ]; + + let closest = positions[0]; + + for (let p of positions) { + if (Math.abs(pos - p) < Math.abs(pos - closest)) { + closest = p; + } } + + setScrollPos(closest); }; - onPageScroll(); - window.addEventListener("scroll", onPageScroll, { passive: true }); - //window.addEventListener("resize", onPageScroll); + const onScroll = (e: WheelEvent) => { + if (!scrollRef.current) return; - return () => { - window.removeEventListener("scroll", onPageScroll); - //window.removeEventListener("resize", onPageScroll); + const midpoint = (scrollRef.current.getBoundingClientRect().top + scrollRef.current.getBoundingClientRect().bottom) / 2; + const minHeight = scrollRef.current?.clientHeight * 1 / 8 || 50; + + // Set scroll bar to whatever the user scrolled to + if (midpoint > window.innerHeight / 2 - 100 && midpoint < window.innerHeight / 2 + 100) { + const maxHeight = scrollRef.current ? scrollRef.current.clientHeight : minHeight; + + setScrollPos((prev) => { + const next = Math.min(Math.max(minHeight, prev + e.deltaY), maxHeight); + + if (scrollTimeout.current) clearTimeout(scrollTimeout.current); + + scrollTimeout.current = setTimeout(() => { + snapToNearest(next); + }, 10); + + if (next !== maxHeight && next !== minHeight) { + e.preventDefault(); + } + + return next; + }); + + } }; - }, );//[responsibilities.length]);*/ + + window.addEventListener("wheel", onScroll, { passive: false }); + + return () => { + window.removeEventListener("wheel", onScroll); + } + }); + + useEffect(() => { + const maxHeight = scrollRef.current?.clientHeight || 50; + const positions = [maxHeight * 1 / 8, maxHeight * 3 / 8, maxHeight * 21 / 32, maxHeight]; + + let min = 0; + for (let i = 1; i < positions.length; i++) { + if (Math.abs(scrollPos - positions[i]) < Math.abs(scrollPos - positions[min])) { + min = i; + } + } + + setResponsibilityIndex(min); + }, [scrollPos]); return (
@@ -92,9 +132,9 @@ export function Responsibilities({
{`${responsibility.title} {index == responsibilityIndex ? (

From 187ae812bf5d52dda1f18f3910d886e725d29fa0 Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Tue, 28 Apr 2026 05:00:40 -0700 Subject: [PATCH 04/19] ANIMATION YAYYYYY Co-authored-by: Copilot --- .../StarterKit/Resources/Responsibilities.tsx | 152 ++++++++++++++++-- 1 file changed, 142 insertions(+), 10 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index dbe51ff4..7e34ea63 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -1,3 +1,5 @@ +'use client'; + import next from 'next'; import Image from 'next/image'; import type { StaticImageData } from 'next/image'; @@ -23,12 +25,15 @@ export function Responsibilities({ }) { const sectionRef = useRef(null); const scrollRef = useRef(null); + const touchYRef = useRef(0); + const enterFromTop = useRef(true); const [responsibilityIndex, setResponsibilityIndex] = useState(0); const [scrollPos, setScrollPos] = useState(50); const scrollTimeout = useRef(null); useEffect(() => { + // go to nearest responsibility const snapToNearest = (pos: number) => { const maxHeight = scrollRef.current?.clientHeight || 50; const positions = [ @@ -49,39 +54,166 @@ export function Responsibilities({ setScrollPos(closest); }; + // scroll for computer const onScroll = (e: WheelEvent) => { if (!scrollRef.current) return; const midpoint = (scrollRef.current.getBoundingClientRect().top + scrollRef.current.getBoundingClientRect().bottom) / 2; - const minHeight = scrollRef.current?.clientHeight * 1 / 8 || 50; + const minScrollHeight = scrollRef.current?.clientHeight * 1 / 8 || 50; + const maxHeight = scrollRef.current ? scrollRef.current.clientHeight : minScrollHeight; + console.log(enterFromTop.current); - // Set scroll bar to whatever the user scrolled to - if (midpoint > window.innerHeight / 2 - 100 && midpoint < window.innerHeight / 2 + 100) { - const maxHeight = scrollRef.current ? scrollRef.current.clientHeight : minHeight; + // set scroll bar to whatever the user scrolled to + if (midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50) { // if component is within range... + //console.log("igieirfjerer"); setScrollPos((prev) => { - const next = Math.min(Math.max(minHeight, prev + e.deltaY), maxHeight); + // + const next = Math.min(Math.max(minScrollHeight, prev + e.deltaY), maxHeight); if (scrollTimeout.current) clearTimeout(scrollTimeout.current); + // makes it go to nearest responsibility scrollTimeout.current = setTimeout(() => { snapToNearest(next); - }, 10); + }, 100); + + return next; + }); - if (next !== maxHeight && next !== minHeight) { + if (enterFromTop.current == true && scrollPos != maxHeight) { + if (e.cancelable) { + e.preventDefault(); + } else { + const sectionElement = document.getElementById("sectionID"); + if (!sectionElement) return; + sectionElement.style.touchAction = 'none'; + } + } else if (enterFromTop.current == false && scrollPos != minScrollHeight) { + if (e.cancelable) { e.preventDefault(); - } + } else { + const sectionElement = document.getElementById("sectionID"); + if (!sectionElement) return; + sectionElement.style.touchAction = 'none'; + } + } else if (enterFromTop.current == true && scrollPos == maxHeight + 5) { + enterFromTop.current = false; + } else if (enterFromTop.current == false && scrollPos == minScrollHeight - 5) { + enterFromTop.current = true; + } + + /*if (scrollPos != minScrollHeight && scrollPos != maxHeight) { + if (e.cancelable) { + e.preventDefault(); + } else { + const sectionElement = document.getElementById("sectionID"); + if (!sectionElement) return; + sectionElement.style.touchAction = 'none'; + } + }*/ + + } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { // if it it somehow not in range but not at where its supposed to be + if (e.cancelable) { + e.preventDefault(); + } else { + const sectionElement = document.getElementById("sectionID"); + if (!sectionElement) return; + sectionElement.style.touchAction = 'none'; + } + setScrollPos((prev) => { + // + const next = Math.min(Math.max(minScrollHeight, prev + e.deltaY), maxHeight); + + if (scrollTimeout.current) clearTimeout(scrollTimeout.current); + + // makes it go to nearest responsibility + scrollTimeout.current = setTimeout(() => { + snapToNearest(next); + }, 100); return next; }); - + } else { + if (midpoint < window.innerHeight / 2 - 50) { + enterFromTop.current = false; + } else if (midpoint > window.innerHeight / 2 + 50) { + enterFromTop.current = true; + } else { + enterFromTop.current = null; + } } }; + // scroll for mobile + const onTouchStart = (e: TouchEvent) => { + touchYRef.current = e.touches[0].clientY + } + + const onTouchMove = (e: TouchEvent) => { + if (!scrollRef.current) return; + + const midpoint = (scrollRef.current.getBoundingClientRect().top + scrollRef.current.getBoundingClientRect().bottom) / 2; + const minScrollHeight = scrollRef.current?.clientHeight * 1 / 8 || 50; + + // set scroll bar to whatever the user scrolled to + if (midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50) { // if component is within range... + const maxHeight = scrollRef.current ? scrollRef.current.clientHeight : minScrollHeight; + + const deltaY = touchYRef.current - e.touches[0].clientY; + touchYRef.current = e.touches[0].clientY + + setScrollPos((prev) => { + // + const next = Math.min(Math.max(minScrollHeight, prev + deltaY), maxHeight); + + if (scrollTimeout.current) clearTimeout(scrollTimeout.current); + + // makes it go to nearest responsibility + scrollTimeout.current = setTimeout(() => { + snapToNearest(next); + }, 100); + + return next; + }); + + if (enterFromTop.current && scrollPos != maxHeight + 5) { + if (e.cancelable) { + e.preventDefault(); + } else { + const sectionElement = document.getElementById("sectionID"); + if (!sectionElement) return; + sectionElement.style.touchAction = 'none'; + } + } else if (enterFromTop.current && scrollPos != minScrollHeight - 5) { + if (e.cancelable) { + e.preventDefault(); + } else { + const sectionElement = document.getElementById("sectionID"); + if (!sectionElement) return; + sectionElement.style.touchAction = 'none'; + } + } + } + } + + const onTouchEnd = (e: TouchEvent) => { + touchYRef.current = 0; + const sectionElement = document.getElementById("sectionID"); + if (!sectionElement) return; + sectionElement.style.touchAction = 'auto'; + } + window.addEventListener("wheel", onScroll, { passive: false }); + window.addEventListener("touchstart", onTouchStart, { passive: false }); + window.addEventListener("touchmove", onTouchMove, { passive: false }); + window.addEventListener("touchend", onTouchEnd, { passive: false }); return () => { window.removeEventListener("wheel", onScroll); + window.removeEventListener("touchstart", onTouchStart); + window.removeEventListener("touchmove", onTouchMove); + window.removeEventListener("touchend", onTouchEnd); } }); @@ -100,7 +232,7 @@ export function Responsibilities({ }, [scrollPos]); return ( -

+

IDEATE

{title}

Date: Tue, 28 Apr 2026 05:00:57 -0700 Subject: [PATCH 05/19] new dark icons for dev responsibilities --- .../Resources/DesignDevResources.tsx | 21 +++++++++++-------- .../resources/tabler_dark-brand-github.svg | 3 +++ .../tabler_dark-device-desktop-code.svg | 3 +++ .../resources/tabler_dark-message-chatbot.svg | 3 +++ ...thub.svg => tabler_light-brand-github.svg} | 0 ...g => tabler_light-device-desktop-code.svg} | 0 ...t.svg => tabler_light-message-chatbot.svg} | 0 7 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 public/hackers/starter-kit/resources/tabler_dark-brand-github.svg create mode 100644 public/hackers/starter-kit/resources/tabler_dark-device-desktop-code.svg create mode 100644 public/hackers/starter-kit/resources/tabler_dark-message-chatbot.svg rename public/hackers/starter-kit/resources/{tabler_brand-github.svg => tabler_light-brand-github.svg} (100%) rename public/hackers/starter-kit/resources/{tabler_device-desktop-code.svg => tabler_light-device-desktop-code.svg} (100%) rename public/hackers/starter-kit/resources/{tabler_message-chatbot.svg => tabler_light-message-chatbot.svg} (100%) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx index 772da822..93568351 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx @@ -12,15 +12,18 @@ import dark_writing_sign from '@public/hackers/starter-kit/resources/tabler_dark import dark_presentation_analytics from '@public/hackers/starter-kit/resources/tabler_dark-presentation-analytics.svg'; import dark_sparkles from '@public/hackers/starter-kit/resources/tabler_dark-sparkles-2.svg'; import dark_figma from '@public/hackers/starter-kit/resources/tabler_dark-brand-figma.svg'; +import dark_github from '@public/hackers/starter-kit/resources/tabler_dark-brand-github.svg'; +import dark_desktop_code from '@public/hackers/starter-kit/resources/tabler_dark-device-desktop-code.svg'; +import dark_message_chatbot from '@public/hackers/starter-kit/resources/tabler_dark-message-chatbot.svg'; import light_writing_sign from '@public/hackers/starter-kit/resources/tabler_light-writing-sign.svg'; import light_presentation_analytics from '@public/hackers/starter-kit/resources/tabler_light-presentation-analytics.svg'; import light_sparkles from '@public/hackers/starter-kit/resources/tabler_light-sparkles-2.svg'; import light_figma from '@public/hackers/starter-kit/resources/tabler_light-brand-figma.svg'; +import light_github from '@public/hackers/starter-kit/resources/tabler_light-brand-github.svg'; +import light_desktop_code from '@public/hackers/starter-kit/resources/tabler_light-device-desktop-code.svg'; +import light_message_chatbot from '@public/hackers/starter-kit/resources/tabler_light-message-chatbot.svg'; -import github from '@public/hackers/starter-kit/resources/tabler_brand-github.svg'; -import desktop_code from '@public/hackers/starter-kit/resources/tabler_device-desktop-code.svg'; -import message_chatbot from '@public/hackers/starter-kit/resources/tabler_message-chatbot.svg'; import text_size from '@public/hackers/starter-kit/resources/tabler_text-size.svg'; import image_in_picture from '@public/hackers/starter-kit/resources/tabler_image-in-picture.svg'; @@ -85,22 +88,22 @@ export default function DesignDevResources() { 'Figure out what tech stack and technologies you want to use for your product.', }, { - dark_icon: github, - light_icon: github, + dark_icon: dark_github, + light_icon: light_github, title: 'Set up codebase scaffolding', description: 'Create a GitHub repo and initialize the project so your team can collaborate.', }, { - dark_icon: message_chatbot, - light_icon: message_chatbot, + dark_icon: dark_message_chatbot, + light_icon: light_message_chatbot, title: 'Divide and conquer', description: 'Split the product into features and assign tasks so teammates can build in parallel.', }, { - dark_icon: desktop_code, - light_icon: desktop_code, + dark_icon: dark_desktop_code, + light_icon: light_desktop_code, title: 'Build a functioning demo', description: 'Implement core features and ensure you have a working product ready for presentations.', diff --git a/public/hackers/starter-kit/resources/tabler_dark-brand-github.svg b/public/hackers/starter-kit/resources/tabler_dark-brand-github.svg new file mode 100644 index 00000000..ea74bbb9 --- /dev/null +++ b/public/hackers/starter-kit/resources/tabler_dark-brand-github.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/hackers/starter-kit/resources/tabler_dark-device-desktop-code.svg b/public/hackers/starter-kit/resources/tabler_dark-device-desktop-code.svg new file mode 100644 index 00000000..8eee34a9 --- /dev/null +++ b/public/hackers/starter-kit/resources/tabler_dark-device-desktop-code.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/hackers/starter-kit/resources/tabler_dark-message-chatbot.svg b/public/hackers/starter-kit/resources/tabler_dark-message-chatbot.svg new file mode 100644 index 00000000..1d3cf366 --- /dev/null +++ b/public/hackers/starter-kit/resources/tabler_dark-message-chatbot.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/hackers/starter-kit/resources/tabler_brand-github.svg b/public/hackers/starter-kit/resources/tabler_light-brand-github.svg similarity index 100% rename from public/hackers/starter-kit/resources/tabler_brand-github.svg rename to public/hackers/starter-kit/resources/tabler_light-brand-github.svg diff --git a/public/hackers/starter-kit/resources/tabler_device-desktop-code.svg b/public/hackers/starter-kit/resources/tabler_light-device-desktop-code.svg similarity index 100% rename from public/hackers/starter-kit/resources/tabler_device-desktop-code.svg rename to public/hackers/starter-kit/resources/tabler_light-device-desktop-code.svg diff --git a/public/hackers/starter-kit/resources/tabler_message-chatbot.svg b/public/hackers/starter-kit/resources/tabler_light-message-chatbot.svg similarity index 100% rename from public/hackers/starter-kit/resources/tabler_message-chatbot.svg rename to public/hackers/starter-kit/resources/tabler_light-message-chatbot.svg From 3524bd1814ff7c8a5952b1cf2e2311fbb35ebdb5 Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Tue, 28 Apr 2026 06:29:20 -0700 Subject: [PATCH 06/19] scroll works now --- .../StarterKit/Resources/Responsibilities.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 7e34ea63..36fe1ddd 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -29,13 +29,18 @@ export function Responsibilities({ const enterFromTop = useRef(true); const [responsibilityIndex, setResponsibilityIndex] = useState(0); - const [scrollPos, setScrollPos] = useState(50); + const [scrollPos, setScrollPos] = useState(null); const scrollTimeout = useRef(null); useEffect(() => { // go to nearest responsibility + if (!scrollRef.current) return; + if (!scrollPos) { + setScrollPos(scrollRef.current.clientHeight * 1 / 8); + } const snapToNearest = (pos: number) => { - const maxHeight = scrollRef.current?.clientHeight || 50; + if (!scrollRef.current) return; + const maxHeight = scrollRef.current.clientHeight; const positions = [ maxHeight * 1 / 8, maxHeight * 3 / 8, @@ -59,9 +64,10 @@ export function Responsibilities({ if (!scrollRef.current) return; const midpoint = (scrollRef.current.getBoundingClientRect().top + scrollRef.current.getBoundingClientRect().bottom) / 2; - const minScrollHeight = scrollRef.current?.clientHeight * 1 / 8 || 50; + const minScrollHeight = scrollRef.current.clientHeight * 1 / 8; const maxHeight = scrollRef.current ? scrollRef.current.clientHeight : minScrollHeight; - console.log(enterFromTop.current); + console.log("scroll pos" + scrollPos); + console.log("min" + minScrollHeight); // set scroll bar to whatever the user scrolled to if (midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50) { // if component is within range... @@ -69,7 +75,7 @@ export function Responsibilities({ setScrollPos((prev) => { // - const next = Math.min(Math.max(minScrollHeight, prev + e.deltaY), maxHeight); + const next = Math.min(Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), maxHeight); if (scrollTimeout.current) clearTimeout(scrollTimeout.current); @@ -123,7 +129,7 @@ export function Responsibilities({ } setScrollPos((prev) => { // - const next = Math.min(Math.max(minScrollHeight, prev + e.deltaY), maxHeight); + const next = Math.min(Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), maxHeight); if (scrollTimeout.current) clearTimeout(scrollTimeout.current); @@ -154,7 +160,7 @@ export function Responsibilities({ if (!scrollRef.current) return; const midpoint = (scrollRef.current.getBoundingClientRect().top + scrollRef.current.getBoundingClientRect().bottom) / 2; - const minScrollHeight = scrollRef.current?.clientHeight * 1 / 8 || 50; + const minScrollHeight = scrollRef.current.clientHeight * 1 / 8; // set scroll bar to whatever the user scrolled to if (midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50) { // if component is within range... @@ -218,6 +224,7 @@ export function Responsibilities({ }); useEffect(() => { + if (!scrollPos) return; const maxHeight = scrollRef.current?.clientHeight || 50; const positions = [maxHeight * 1 / 8, maxHeight * 3 / 8, maxHeight * 21 / 32, maxHeight]; From ab7e196dfacaee701007f7887b07f945308e409f Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Tue, 28 Apr 2026 06:30:23 -0700 Subject: [PATCH 07/19] transitions for the text --- .../StarterKit/Resources/Responsibilities.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 36fe1ddd..31c94373 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -273,23 +273,23 @@ export function Responsibilities({ {`${responsibility.title} {index == responsibilityIndex ? ( -

+

{responsibility.title}

) : ( -

+

{responsibility.title}

)}
- {index == responsibilityIndex && ( -

+ {index == responsibilityIndex ? ( +

{responsibility.description}

- )} + ) : (

)}
))}
From 98fb37298e902d35cad845ad9b46abf71d81f135 Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Tue, 28 Apr 2026 06:32:14 -0700 Subject: [PATCH 08/19] gets rid of the weird thingy when scrolling the text --- .../_components/StarterKit/Resources/Responsibilities.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 31c94373..baeb0b09 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -239,7 +239,7 @@ export function Responsibilities({ }, [scrollPos]); return ( -
+

IDEATE

{title}

Date: Tue, 28 Apr 2026 08:29:18 -0700 Subject: [PATCH 09/19] yay scrolling is now fixed :3 --- .../Resources/DesignDevResources.tsx | 1 - .../StarterKit/Resources/Responsibilities.tsx | 229 ++++++++++++------ 2 files changed, 158 insertions(+), 72 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx index 93568351..a15ead87 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx @@ -24,7 +24,6 @@ import light_github from '@public/hackers/starter-kit/resources/tabler_light-bra import light_desktop_code from '@public/hackers/starter-kit/resources/tabler_light-device-desktop-code.svg'; import light_message_chatbot from '@public/hackers/starter-kit/resources/tabler_light-message-chatbot.svg'; - import text_size from '@public/hackers/starter-kit/resources/tabler_text-size.svg'; import image_in_picture from '@public/hackers/starter-kit/resources/tabler_image-in-picture.svg'; import shape from '@public/hackers/starter-kit/resources/tabler_shape.svg'; diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index baeb0b09..71ea3146 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -1,6 +1,5 @@ 'use client'; -import next from 'next'; import Image from 'next/image'; import type { StaticImageData } from 'next/image'; import { useRef, useState, useEffect } from 'react'; @@ -35,22 +34,20 @@ export function Responsibilities({ useEffect(() => { // go to nearest responsibility if (!scrollRef.current) return; - if (!scrollPos) { - setScrollPos(scrollRef.current.clientHeight * 1 / 8); - } + const snapToNearest = (pos: number) => { if (!scrollRef.current) return; const maxHeight = scrollRef.current.clientHeight; const positions = [ - maxHeight * 1 / 8, - maxHeight * 3 / 8, - maxHeight * 21 / 32, + (maxHeight * 1) / 8, + (maxHeight * 3) / 8, + (maxHeight * 21) / 32, maxHeight, ]; let closest = positions[0]; - for (let p of positions) { + for (const p of positions) { if (Math.abs(pos - p) < Math.abs(pos - closest)) { closest = p; } @@ -63,19 +60,28 @@ export function Responsibilities({ const onScroll = (e: WheelEvent) => { if (!scrollRef.current) return; - const midpoint = (scrollRef.current.getBoundingClientRect().top + scrollRef.current.getBoundingClientRect().bottom) / 2; - const minScrollHeight = scrollRef.current.clientHeight * 1 / 8; - const maxHeight = scrollRef.current ? scrollRef.current.clientHeight : minScrollHeight; - console.log("scroll pos" + scrollPos); - console.log("min" + minScrollHeight); + const midpoint = + (scrollRef.current.getBoundingClientRect().top + + scrollRef.current.getBoundingClientRect().bottom) / + 2; + const minScrollHeight = (scrollRef.current.clientHeight * 1) / 8; + const maxHeight = scrollRef.current + ? scrollRef.current.clientHeight + : minScrollHeight; // set scroll bar to whatever the user scrolled to - if (midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50) { // if component is within range... - //console.log("igieirfjerer"); + if ( + midpoint > window.innerHeight / 2 - 50 && + midpoint < window.innerHeight / 2 + 50 + ) { + // if component is within range... setScrollPos((prev) => { - // - const next = Math.min(Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), maxHeight); + // + const next = Math.min( + Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), + maxHeight + ); if (scrollTimeout.current) clearTimeout(scrollTimeout.current); @@ -91,45 +97,44 @@ export function Responsibilities({ if (e.cancelable) { e.preventDefault(); } else { - const sectionElement = document.getElementById("sectionID"); + const sectionElement = document.getElementById('sectionID'); if (!sectionElement) return; sectionElement.style.touchAction = 'none'; } - } else if (enterFromTop.current == false && scrollPos != minScrollHeight) { + } else if ( + enterFromTop.current == false && + scrollPos != minScrollHeight + ) { if (e.cancelable) { e.preventDefault(); } else { - const sectionElement = document.getElementById("sectionID"); + const sectionElement = document.getElementById('sectionID'); if (!sectionElement) return; sectionElement.style.touchAction = 'none'; } } else if (enterFromTop.current == true && scrollPos == maxHeight + 5) { enterFromTop.current = false; - } else if (enterFromTop.current == false && scrollPos == minScrollHeight - 5) { + } else if ( + enterFromTop.current == false && + scrollPos == minScrollHeight - 5 + ) { enterFromTop.current = true; } - - /*if (scrollPos != minScrollHeight && scrollPos != maxHeight) { - if (e.cancelable) { - e.preventDefault(); - } else { - const sectionElement = document.getElementById("sectionID"); - if (!sectionElement) return; - sectionElement.style.touchAction = 'none'; - } - }*/ - - } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { // if it it somehow not in range but not at where its supposed to be + } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { + // if it it somehow not in range but not at where its supposed to be if (e.cancelable) { e.preventDefault(); } else { - const sectionElement = document.getElementById("sectionID"); + const sectionElement = document.getElementById('sectionID'); if (!sectionElement) return; sectionElement.style.touchAction = 'none'; } setScrollPos((prev) => { - // - const next = Math.min(Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), maxHeight); + // + const next = Math.min( + Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), + maxHeight + ); if (scrollTimeout.current) clearTimeout(scrollTimeout.current); @@ -153,25 +158,36 @@ export function Responsibilities({ // scroll for mobile const onTouchStart = (e: TouchEvent) => { - touchYRef.current = e.touches[0].clientY - } + touchYRef.current = e.touches[0].clientY; + }; const onTouchMove = (e: TouchEvent) => { if (!scrollRef.current) return; - const midpoint = (scrollRef.current.getBoundingClientRect().top + scrollRef.current.getBoundingClientRect().bottom) / 2; - const minScrollHeight = scrollRef.current.clientHeight * 1 / 8; + const midpoint = + (scrollRef.current.getBoundingClientRect().top + + scrollRef.current.getBoundingClientRect().bottom) / + 2; + const minScrollHeight = (scrollRef.current.clientHeight * 1) / 8; + const maxHeight = scrollRef.current + ? scrollRef.current.clientHeight + : minScrollHeight; + const deltaY = touchYRef.current - e.touches[0].clientY; + touchYRef.current = e.touches[0].clientY; // set scroll bar to whatever the user scrolled to - if (midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50) { // if component is within range... - const maxHeight = scrollRef.current ? scrollRef.current.clientHeight : minScrollHeight; - - const deltaY = touchYRef.current - e.touches[0].clientY; - touchYRef.current = e.touches[0].clientY + if ( + midpoint > window.innerHeight / 2 - 50 && + midpoint < window.innerHeight / 2 + 50 + ) { + // if component is within range... setScrollPos((prev) => { - // - const next = Math.min(Math.max(minScrollHeight, prev + deltaY), maxHeight); + // + const next = Math.min( + Math.max(minScrollHeight, prev == null ? 0 : prev + deltaY), + maxHeight + ); if (scrollTimeout.current) clearTimeout(scrollTimeout.current); @@ -183,54 +199,111 @@ export function Responsibilities({ return next; }); - if (enterFromTop.current && scrollPos != maxHeight + 5) { + if (enterFromTop.current == true && scrollPos != maxHeight) { if (e.cancelable) { e.preventDefault(); } else { - const sectionElement = document.getElementById("sectionID"); + const sectionElement = document.getElementById('sectionID'); if (!sectionElement) return; sectionElement.style.touchAction = 'none'; } - } else if (enterFromTop.current && scrollPos != minScrollHeight - 5) { + } else if ( + enterFromTop.current == false && + scrollPos != minScrollHeight + ) { if (e.cancelable) { e.preventDefault(); } else { - const sectionElement = document.getElementById("sectionID"); + const sectionElement = document.getElementById('sectionID'); if (!sectionElement) return; sectionElement.style.touchAction = 'none'; } + } else { + const sectionElement = document.getElementById('sectionID'); + if (!sectionElement) return; + sectionElement.style.touchAction = 'auto'; + } + } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { + // if it it somehow not in range but not at where its supposed to be + if (e.cancelable) { + e.preventDefault(); + } else { + const sectionElement = document.getElementById('sectionID'); + if (!sectionElement) return; + sectionElement.style.touchAction = 'none'; + } + setScrollPos((prev) => { + // + const next = Math.min( + Math.max(minScrollHeight, prev == null ? 0 : prev + deltaY), + maxHeight + ); + + if (scrollTimeout.current) clearTimeout(scrollTimeout.current); + + // makes it go to nearest responsibility + scrollTimeout.current = setTimeout(() => { + snapToNearest(next); + }, 100); + + return next; + }); + } else { + if (midpoint < window.innerHeight / 2 - 50) { + enterFromTop.current = false; + } else if (midpoint > window.innerHeight / 2 + 50) { + enterFromTop.current = true; + } else { + enterFromTop.current = null; } } - } + }; - const onTouchEnd = (e: TouchEvent) => { + const onTouchEnd = () => { touchYRef.current = 0; - const sectionElement = document.getElementById("sectionID"); + const sectionElement = document.getElementById('sectionID'); if (!sectionElement) return; sectionElement.style.touchAction = 'auto'; - } + }; - window.addEventListener("wheel", onScroll, { passive: false }); - window.addEventListener("touchstart", onTouchStart, { passive: false }); - window.addEventListener("touchmove", onTouchMove, { passive: false }); - window.addEventListener("touchend", onTouchEnd, { passive: false }); + window.addEventListener('wheel', onScroll, { passive: false }); + window.addEventListener('touchstart', onTouchStart, { passive: false }); + window.addEventListener('touchmove', onTouchMove, { passive: false }); + window.addEventListener('touchend', onTouchEnd, { passive: false }); return () => { - window.removeEventListener("wheel", onScroll); - window.removeEventListener("touchstart", onTouchStart); - window.removeEventListener("touchmove", onTouchMove); - window.removeEventListener("touchend", onTouchEnd); - } + window.removeEventListener('wheel', onScroll); + window.removeEventListener('touchstart', onTouchStart); + window.removeEventListener('touchmove', onTouchMove); + window.removeEventListener('touchend', onTouchEnd); + }; }); useEffect(() => { - if (!scrollPos) return; + if (!scrollRef.current) return; + if (!scrollPos) { + setScrollPos((scrollRef.current.clientHeight * 1) / 8); + } + const maxHeight = scrollRef.current?.clientHeight || 50; - const positions = [maxHeight * 1 / 8, maxHeight * 3 / 8, maxHeight * 21 / 32, maxHeight]; + const positions = [ + (maxHeight * 1) / 8, + (maxHeight * 3) / 8, + (maxHeight * 21) / 32, + maxHeight, + ]; + const minScrollHeight = (scrollRef.current.clientHeight * 1) / 8; let min = 0; for (let i = 1; i < positions.length; i++) { - if (Math.abs(scrollPos - positions[i]) < Math.abs(scrollPos - positions[min])) { + if ( + Math.abs( + (scrollPos != null ? scrollPos : minScrollHeight) - positions[i] + ) < + Math.abs( + (scrollPos != null ? scrollPos : minScrollHeight) - positions[min] + ) + ) { min = i; } } @@ -239,7 +312,11 @@ export function Responsibilities({ }, [scrollPos]); return ( -
+

IDEATE

{title}

{`${responsibility.title} {index == responsibilityIndex ? (

@@ -289,7 +374,9 @@ export function Responsibilities({

{responsibility.description}

- ) : (

)} + ) : ( +

+ )}
))}
From cf1a097e71c658d4c492a795f63ff5d4f8d6b08e Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Tue, 28 Apr 2026 08:38:56 -0700 Subject: [PATCH 10/19] okay no this fixes the weird thingy when scrolling and the text resizes --- .../_components/StarterKit/Resources/Responsibilities.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 71ea3146..b336a1fa 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -315,7 +315,7 @@ export function Responsibilities({

IDEATE

{title}

@@ -330,7 +330,7 @@ export function Responsibilities({ {`${title}
From dfefe99e587f60fa035fb8ca3a1380a4c18e0cb0 Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Wed, 29 Apr 2026 20:40:08 -0700 Subject: [PATCH 11/19] image no move --- .../_components/StarterKit/Resources/Responsibilities.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index b336a1fa..63cf256f 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -333,7 +333,7 @@ export function Responsibilities({ className="w-full min-[1250px]:w-[500px]" /> -
+
Date: Wed, 29 Apr 2026 21:05:45 -0700 Subject: [PATCH 12/19] changes what you are on when clicked --- .../StarterKit/Resources/Responsibilities.tsx | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 63cf256f..fae2d5b7 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -34,6 +34,22 @@ export function Responsibilities({ useEffect(() => { // go to nearest responsibility if (!scrollRef.current) return; + + const snapToIndex = (index: number) => { + if (!scrollRef.current) return; + const maxHeight = scrollRef.current.clientHeight; + let target = (maxHeight * 1) / 8; + if (index == 0) { + target = (maxHeight * 1) / 8; + } else if (index == 1) { + target = (maxHeight * 3) / 8; + } else if (index == 2) { + target = (maxHeight * 21) / 32; + } else if (index == 3) { + target = maxHeight; + } + setScrollPos(target); + } const snapToNearest = (pos: number) => { if (!scrollRef.current) return; @@ -270,12 +286,48 @@ export function Responsibilities({ window.addEventListener('touchstart', onTouchStart, { passive: false }); window.addEventListener('touchmove', onTouchMove, { passive: false }); window.addEventListener('touchend', onTouchEnd, { passive: false }); + + const responsibility0 = document.getElementById('responsibility-0'); + const responsibility0_onClick = () => { + snapToIndex(0); + }; + if (responsibility0) { + responsibility0.addEventListener("click", responsibility0_onClick); + } + + const responsibility1 = document.getElementById('responsibility-1'); + const responsibility1_onClick = () => { + snapToIndex(1); + }; + if (responsibility1) { + responsibility1.addEventListener("click", responsibility1_onClick); + } + + const responsibility2 = document.getElementById('responsibility-2'); + const responsibility2_onClick = () => { + snapToIndex(2); + }; + if (responsibility2) { + responsibility2.addEventListener("click", responsibility2_onClick); + } + + const responsibility3 = document.getElementById('responsibility-3'); + const responsibility3_onClick = () => { + snapToIndex(3); + }; + if (responsibility3) { + responsibility3.addEventListener("click", responsibility3_onClick); + } return () => { window.removeEventListener('wheel', onScroll); window.removeEventListener('touchstart', onTouchStart); window.removeEventListener('touchmove', onTouchMove); window.removeEventListener('touchend', onTouchEnd); + document.removeEventListener('click', responsibility0_onClick); + document.removeEventListener('click', responsibility1_onClick); + document.removeEventListener('click', responsibility2_onClick); + document.removeEventListener('click', responsibility3_onClick); }; }); @@ -345,7 +397,7 @@ export function Responsibilities({
{responsibilities.map((responsibility, index) => ( -
+
Date: Wed, 29 Apr 2026 21:06:05 -0700 Subject: [PATCH 13/19] better transition? --- .../_components/StarterKit/Resources/Responsibilities.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index fae2d5b7..9cdb0d3a 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -406,11 +406,7 @@ export function Responsibilities({ : responsibility.light_icon } alt={`${responsibility.title} icon`} - className={ - index == responsibilityIndex - ? `w-[32px] h-[32px] transition-all` - : `w-[24px] h-[24px] transition-all` - } + className={`transition-all duration-300 ease-out ${index == responsibilityIndex ? 'w-[32px] h-[32px]' : 'w-[24px] h-[24px]'}`} /> {index == responsibilityIndex ? (

From c2d59100628421b5874e6fce6a14fc063463b904 Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:12:08 -0700 Subject: [PATCH 14/19] woopsie i forgot to avoid duplicate ids --- .../StarterKit/Resources/Responsibilities.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 9cdb0d3a..d1d7cd84 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -287,7 +287,7 @@ export function Responsibilities({ window.addEventListener('touchmove', onTouchMove, { passive: false }); window.addEventListener('touchend', onTouchEnd, { passive: false }); - const responsibility0 = document.getElementById('responsibility-0'); + const responsibility0 = document.getElementById(title + 'responsibility-0'); const responsibility0_onClick = () => { snapToIndex(0); }; @@ -295,7 +295,7 @@ export function Responsibilities({ responsibility0.addEventListener("click", responsibility0_onClick); } - const responsibility1 = document.getElementById('responsibility-1'); + const responsibility1 = document.getElementById(title + 'responsibility-1'); const responsibility1_onClick = () => { snapToIndex(1); }; @@ -303,7 +303,7 @@ export function Responsibilities({ responsibility1.addEventListener("click", responsibility1_onClick); } - const responsibility2 = document.getElementById('responsibility-2'); + const responsibility2 = document.getElementById(title + 'responsibility-2'); const responsibility2_onClick = () => { snapToIndex(2); }; @@ -311,7 +311,7 @@ export function Responsibilities({ responsibility2.addEventListener("click", responsibility2_onClick); } - const responsibility3 = document.getElementById('responsibility-3'); + const responsibility3 = document.getElementById(title + 'responsibility-3'); const responsibility3_onClick = () => { snapToIndex(3); }; @@ -397,7 +397,7 @@ export function Responsibilities({

{responsibilities.map((responsibility, index) => ( -
+
Date: Thu, 30 Apr 2026 15:45:59 -0700 Subject: [PATCH 15/19] linting + fixes the sizing thingy (pretty sure) --- .../Resources/DesignDevResources.tsx | 2 +- .../StarterKit/Resources/Responsibilities.tsx | 38 +++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx index a15ead87..01208eed 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/DesignDevResources.tsx @@ -175,7 +175,7 @@ export default function DesignDevResources() { }; return ( -
+
{/** Section 1 */}
{ // go to nearest responsibility if (!scrollRef.current) return; - + const snapToIndex = (index: number) => { if (!scrollRef.current) return; const maxHeight = scrollRef.current.clientHeight; @@ -49,7 +49,7 @@ export function Responsibilities({ target = maxHeight; } setScrollPos(target); - } + }; const snapToNearest = (pos: number) => { if (!scrollRef.current) return; @@ -286,37 +286,37 @@ export function Responsibilities({ window.addEventListener('touchstart', onTouchStart, { passive: false }); window.addEventListener('touchmove', onTouchMove, { passive: false }); window.addEventListener('touchend', onTouchEnd, { passive: false }); - + const responsibility0 = document.getElementById(title + 'responsibility-0'); const responsibility0_onClick = () => { - snapToIndex(0); + snapToIndex(0); }; if (responsibility0) { - responsibility0.addEventListener("click", responsibility0_onClick); + responsibility0.addEventListener('click', responsibility0_onClick); } - + const responsibility1 = document.getElementById(title + 'responsibility-1'); const responsibility1_onClick = () => { - snapToIndex(1); + snapToIndex(1); }; if (responsibility1) { - responsibility1.addEventListener("click", responsibility1_onClick); + responsibility1.addEventListener('click', responsibility1_onClick); } const responsibility2 = document.getElementById(title + 'responsibility-2'); const responsibility2_onClick = () => { - snapToIndex(2); + snapToIndex(2); }; if (responsibility2) { - responsibility2.addEventListener("click", responsibility2_onClick); + responsibility2.addEventListener('click', responsibility2_onClick); } const responsibility3 = document.getElementById(title + 'responsibility-3'); const responsibility3_onClick = () => { - snapToIndex(3); + snapToIndex(3); }; if (responsibility3) { - responsibility3.addEventListener("click", responsibility3_onClick); + responsibility3.addEventListener('click', responsibility3_onClick); } return () => { @@ -367,7 +367,7 @@ export function Responsibilities({

IDEATE

{title}

@@ -397,7 +397,11 @@ export function Responsibilities({
{responsibilities.map((responsibility, index) => ( -
+
{`${responsibility.title} {index == responsibilityIndex ? (

From 1b5322f60bf739305757189ff90840d7e726cbf3 Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:21:39 -0700 Subject: [PATCH 16/19] deleted comments --- .../StarterKit/Resources/Responsibilities.tsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 6e3fd78e..293ca7d5 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -3,6 +3,7 @@ import Image from 'next/image'; import type { StaticImageData } from 'next/image'; import { useRef, useState, useEffect } from 'react'; +import useIdeateScroll from './useIdeateScroll'; interface Responsibility { dark_icon: StaticImageData; @@ -32,7 +33,6 @@ export function Responsibilities({ const scrollTimeout = useRef(null); useEffect(() => { - // go to nearest responsibility if (!scrollRef.current) return; const snapToIndex = (index: number) => { @@ -72,7 +72,6 @@ export function Responsibilities({ setScrollPos(closest); }; - // scroll for computer const onScroll = (e: WheelEvent) => { if (!scrollRef.current) return; @@ -85,15 +84,12 @@ export function Responsibilities({ ? scrollRef.current.clientHeight : minScrollHeight; - // set scroll bar to whatever the user scrolled to if ( midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50 ) { - // if component is within range... setScrollPos((prev) => { - // const next = Math.min( Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), maxHeight @@ -101,7 +97,6 @@ export function Responsibilities({ if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - // makes it go to nearest responsibility scrollTimeout.current = setTimeout(() => { snapToNearest(next); }, 100); @@ -137,7 +132,6 @@ export function Responsibilities({ enterFromTop.current = true; } } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { - // if it it somehow not in range but not at where its supposed to be if (e.cancelable) { e.preventDefault(); } else { @@ -146,7 +140,6 @@ export function Responsibilities({ sectionElement.style.touchAction = 'none'; } setScrollPos((prev) => { - // const next = Math.min( Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), maxHeight @@ -154,7 +147,6 @@ export function Responsibilities({ if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - // makes it go to nearest responsibility scrollTimeout.current = setTimeout(() => { snapToNearest(next); }, 100); @@ -172,7 +164,6 @@ export function Responsibilities({ } }; - // scroll for mobile const onTouchStart = (e: TouchEvent) => { touchYRef.current = e.touches[0].clientY; }; @@ -191,15 +182,12 @@ export function Responsibilities({ const deltaY = touchYRef.current - e.touches[0].clientY; touchYRef.current = e.touches[0].clientY; - // set scroll bar to whatever the user scrolled to if ( midpoint > window.innerHeight / 2 - 50 && midpoint < window.innerHeight / 2 + 50 ) { - // if component is within range... setScrollPos((prev) => { - // const next = Math.min( Math.max(minScrollHeight, prev == null ? 0 : prev + deltaY), maxHeight @@ -207,7 +195,6 @@ export function Responsibilities({ if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - // makes it go to nearest responsibility scrollTimeout.current = setTimeout(() => { snapToNearest(next); }, 100); @@ -240,7 +227,6 @@ export function Responsibilities({ sectionElement.style.touchAction = 'auto'; } } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { - // if it it somehow not in range but not at where its supposed to be if (e.cancelable) { e.preventDefault(); } else { @@ -249,7 +235,6 @@ export function Responsibilities({ sectionElement.style.touchAction = 'none'; } setScrollPos((prev) => { - // const next = Math.min( Math.max(minScrollHeight, prev == null ? 0 : prev + deltaY), maxHeight @@ -257,7 +242,6 @@ export function Responsibilities({ if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - // makes it go to nearest responsibility scrollTimeout.current = setTimeout(() => { snapToNearest(next); }, 100); From 5ed7ab9e827edbbfc4ac94216829fd1676d1b5a9 Mon Sep 17 00:00:00 2001 From: phlangiee <174984579+phlangiee@users.noreply.github.com> Date: Sun, 3 May 2026 17:34:32 -0700 Subject: [PATCH 17/19] use effect in different file --- .../StarterKit/Resources/Responsibilities.tsx | 329 +----------------- .../StarterKit/Resources/useIdeateScroll.tsx | 264 ++++++++++++++ 2 files changed, 269 insertions(+), 324 deletions(-) create mode 100644 app/(pages)/(hackers)/_components/StarterKit/Resources/useIdeateScroll.tsx diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index 293ca7d5..d0f9c957 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -2,7 +2,6 @@ import Image from 'next/image'; import type { StaticImageData } from 'next/image'; -import { useRef, useState, useEffect } from 'react'; import useIdeateScroll from './useIdeateScroll'; interface Responsibility { @@ -23,329 +22,11 @@ export function Responsibilities({ responsibilities: Responsibility[]; reverse: boolean; }) { - const sectionRef = useRef(null); - const scrollRef = useRef(null); - const touchYRef = useRef(0); - const enterFromTop = useRef(true); - - const [responsibilityIndex, setResponsibilityIndex] = useState(0); - const [scrollPos, setScrollPos] = useState(null); - const scrollTimeout = useRef(null); - - useEffect(() => { - if (!scrollRef.current) return; - - const snapToIndex = (index: number) => { - if (!scrollRef.current) return; - const maxHeight = scrollRef.current.clientHeight; - let target = (maxHeight * 1) / 8; - if (index == 0) { - target = (maxHeight * 1) / 8; - } else if (index == 1) { - target = (maxHeight * 3) / 8; - } else if (index == 2) { - target = (maxHeight * 21) / 32; - } else if (index == 3) { - target = maxHeight; - } - setScrollPos(target); - }; - - const snapToNearest = (pos: number) => { - if (!scrollRef.current) return; - const maxHeight = scrollRef.current.clientHeight; - const positions = [ - (maxHeight * 1) / 8, - (maxHeight * 3) / 8, - (maxHeight * 21) / 32, - maxHeight, - ]; - - let closest = positions[0]; - - for (const p of positions) { - if (Math.abs(pos - p) < Math.abs(pos - closest)) { - closest = p; - } - } - - setScrollPos(closest); - }; - - const onScroll = (e: WheelEvent) => { - if (!scrollRef.current) return; - - const midpoint = - (scrollRef.current.getBoundingClientRect().top + - scrollRef.current.getBoundingClientRect().bottom) / - 2; - const minScrollHeight = (scrollRef.current.clientHeight * 1) / 8; - const maxHeight = scrollRef.current - ? scrollRef.current.clientHeight - : minScrollHeight; - - if ( - midpoint > window.innerHeight / 2 - 50 && - midpoint < window.innerHeight / 2 + 50 - ) { - - setScrollPos((prev) => { - const next = Math.min( - Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), - maxHeight - ); - - if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - - scrollTimeout.current = setTimeout(() => { - snapToNearest(next); - }, 100); - - return next; - }); - - if (enterFromTop.current == true && scrollPos != maxHeight) { - if (e.cancelable) { - e.preventDefault(); - } else { - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'none'; - } - } else if ( - enterFromTop.current == false && - scrollPos != minScrollHeight - ) { - if (e.cancelable) { - e.preventDefault(); - } else { - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'none'; - } - } else if (enterFromTop.current == true && scrollPos == maxHeight + 5) { - enterFromTop.current = false; - } else if ( - enterFromTop.current == false && - scrollPos == minScrollHeight - 5 - ) { - enterFromTop.current = true; - } - } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { - if (e.cancelable) { - e.preventDefault(); - } else { - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'none'; - } - setScrollPos((prev) => { - const next = Math.min( - Math.max(minScrollHeight, prev == null ? 0 : prev + e.deltaY), - maxHeight - ); - - if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - - scrollTimeout.current = setTimeout(() => { - snapToNearest(next); - }, 100); - - return next; - }); - } else { - if (midpoint < window.innerHeight / 2 - 50) { - enterFromTop.current = false; - } else if (midpoint > window.innerHeight / 2 + 50) { - enterFromTop.current = true; - } else { - enterFromTop.current = null; - } - } - }; - - const onTouchStart = (e: TouchEvent) => { - touchYRef.current = e.touches[0].clientY; - }; - - const onTouchMove = (e: TouchEvent) => { - if (!scrollRef.current) return; - - const midpoint = - (scrollRef.current.getBoundingClientRect().top + - scrollRef.current.getBoundingClientRect().bottom) / - 2; - const minScrollHeight = (scrollRef.current.clientHeight * 1) / 8; - const maxHeight = scrollRef.current - ? scrollRef.current.clientHeight - : minScrollHeight; - const deltaY = touchYRef.current - e.touches[0].clientY; - touchYRef.current = e.touches[0].clientY; - - if ( - midpoint > window.innerHeight / 2 - 50 && - midpoint < window.innerHeight / 2 + 50 - ) { - - setScrollPos((prev) => { - const next = Math.min( - Math.max(minScrollHeight, prev == null ? 0 : prev + deltaY), - maxHeight - ); - - if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - - scrollTimeout.current = setTimeout(() => { - snapToNearest(next); - }, 100); - - return next; - }); - - if (enterFromTop.current == true && scrollPos != maxHeight) { - if (e.cancelable) { - e.preventDefault(); - } else { - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'none'; - } - } else if ( - enterFromTop.current == false && - scrollPos != minScrollHeight - ) { - if (e.cancelable) { - e.preventDefault(); - } else { - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'none'; - } - } else { - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'auto'; - } - } else if (scrollPos != maxHeight && scrollPos != minScrollHeight) { - if (e.cancelable) { - e.preventDefault(); - } else { - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'none'; - } - setScrollPos((prev) => { - const next = Math.min( - Math.max(minScrollHeight, prev == null ? 0 : prev + deltaY), - maxHeight - ); - - if (scrollTimeout.current) clearTimeout(scrollTimeout.current); - - scrollTimeout.current = setTimeout(() => { - snapToNearest(next); - }, 100); - - return next; - }); - } else { - if (midpoint < window.innerHeight / 2 - 50) { - enterFromTop.current = false; - } else if (midpoint > window.innerHeight / 2 + 50) { - enterFromTop.current = true; - } else { - enterFromTop.current = null; - } - } - }; - - const onTouchEnd = () => { - touchYRef.current = 0; - const sectionElement = document.getElementById('sectionID'); - if (!sectionElement) return; - sectionElement.style.touchAction = 'auto'; - }; - - window.addEventListener('wheel', onScroll, { passive: false }); - window.addEventListener('touchstart', onTouchStart, { passive: false }); - window.addEventListener('touchmove', onTouchMove, { passive: false }); - window.addEventListener('touchend', onTouchEnd, { passive: false }); - - const responsibility0 = document.getElementById(title + 'responsibility-0'); - const responsibility0_onClick = () => { - snapToIndex(0); - }; - if (responsibility0) { - responsibility0.addEventListener('click', responsibility0_onClick); - } - - const responsibility1 = document.getElementById(title + 'responsibility-1'); - const responsibility1_onClick = () => { - snapToIndex(1); - }; - if (responsibility1) { - responsibility1.addEventListener('click', responsibility1_onClick); - } - - const responsibility2 = document.getElementById(title + 'responsibility-2'); - const responsibility2_onClick = () => { - snapToIndex(2); - }; - if (responsibility2) { - responsibility2.addEventListener('click', responsibility2_onClick); - } - - const responsibility3 = document.getElementById(title + 'responsibility-3'); - const responsibility3_onClick = () => { - snapToIndex(3); - }; - if (responsibility3) { - responsibility3.addEventListener('click', responsibility3_onClick); - } - - return () => { - window.removeEventListener('wheel', onScroll); - window.removeEventListener('touchstart', onTouchStart); - window.removeEventListener('touchmove', onTouchMove); - window.removeEventListener('touchend', onTouchEnd); - document.removeEventListener('click', responsibility0_onClick); - document.removeEventListener('click', responsibility1_onClick); - document.removeEventListener('click', responsibility2_onClick); - document.removeEventListener('click', responsibility3_onClick); - }; - }); - - useEffect(() => { - if (!scrollRef.current) return; - if (!scrollPos) { - setScrollPos((scrollRef.current.clientHeight * 1) / 8); - } - - const maxHeight = scrollRef.current?.clientHeight || 50; - const positions = [ - (maxHeight * 1) / 8, - (maxHeight * 3) / 8, - (maxHeight * 21) / 32, - maxHeight, - ]; - const minScrollHeight = (scrollRef.current.clientHeight * 1) / 8; - - let min = 0; - for (let i = 1; i < positions.length; i++) { - if ( - Math.abs( - (scrollPos != null ? scrollPos : minScrollHeight) - positions[i] - ) < - Math.abs( - (scrollPos != null ? scrollPos : minScrollHeight) - positions[min] - ) - ) { - min = i; - } - } - - setResponsibilityIndex(min); - }, [scrollPos]); + const { sectionRef, scrollRef, responsibilityIndex, scrollPos } = + useIdeateScroll({ + title, + responsibilities, + }); return (

(null); + const scrollRef = useRef(null); + const touchYRef = useRef(0); + const enterFromTop = useRef(true); + const scrollPosRef = useRef(0); + const [responsibilityIndex, setResponsibilityIndex] = useState(0); + const [scrollPos, setScrollPos] = useState(null); + const scrollTimeout = useRef(null); + + useEffect(() => { + const getMaxHeight = () => scrollRef.current?.clientHeight ?? 0; + const getMinHeight = () => getMaxHeight() / 8; + const getCurrentScrollPos = () => scrollPosRef.current || getMinHeight(); + + const setTouchAction = (value: string) => { + if (!sectionRef.current) return; + sectionRef.current.style.touchAction = value; + }; + + const snapToIndex = (index: number) => { + const maxHeight = getMaxHeight(); + if (!maxHeight) return; + + const positions = [ + maxHeight / 8, + (maxHeight * 3) / 8, + (maxHeight * 21) / 32, + maxHeight, + ]; + + const target = positions[index] ?? positions[0]; + scrollPosRef.current = target; + setScrollPos(target); + }; + + const snapToNearest = (pos: number) => { + const maxHeight = getMaxHeight(); + if (!maxHeight) return; + + const positions = [ + maxHeight / 8, + (maxHeight * 3) / 8, + (maxHeight * 21) / 32, + maxHeight, + ]; + + let closest = positions[0]; + for (const p of positions) { + if (Math.abs(pos - p) < Math.abs(pos - closest)) { + closest = p; + } + } + + scrollPosRef.current = closest; + setScrollPos(closest); + }; + + const updateScrollPos = (delta: number) => { + const maxHeight = getMaxHeight(); + const next = Math.min( + Math.max(getMinHeight(), getCurrentScrollPos() + delta), + maxHeight + ); + + if (scrollTimeout.current) clearTimeout(scrollTimeout.current); + scrollTimeout.current = setTimeout(() => snapToNearest(next), 100); + + scrollPosRef.current = next; + setScrollPos(next); + }; + + const onScroll = (e: WheelEvent) => { + if (!scrollRef.current) return; + + const midpoint = + (scrollRef.current.getBoundingClientRect().top + + scrollRef.current.getBoundingClientRect().bottom) / + 2; + const minHeight = getMinHeight(); + const maxHeight = getMaxHeight(); + const currentScrollPos = getCurrentScrollPos(); + + if ( + midpoint > window.innerHeight / 2 - 50 && + midpoint < window.innerHeight / 2 + 50 + ) { + updateScrollPos(e.deltaY); + + if (enterFromTop.current === true && currentScrollPos !== maxHeight) { + if (e.cancelable) e.preventDefault(); + else setTouchAction('none'); + } else if ( + enterFromTop.current === false && + currentScrollPos !== minHeight + ) { + if (e.cancelable) e.preventDefault(); + else setTouchAction('none'); + } else if ( + enterFromTop.current === true && + currentScrollPos === maxHeight + 5 + ) { + enterFromTop.current = false; + } else if ( + enterFromTop.current === false && + currentScrollPos === minHeight - 5 + ) { + enterFromTop.current = true; + } + } else if ( + currentScrollPos !== maxHeight && + currentScrollPos !== minHeight + ) { + if (e.cancelable) e.preventDefault(); + else setTouchAction('none'); + updateScrollPos(e.deltaY); + } else { + if (midpoint < window.innerHeight / 2 - 50) { + enterFromTop.current = false; + } else if (midpoint > window.innerHeight / 2 + 50) { + enterFromTop.current = true; + } else { + enterFromTop.current = null; + } + } + }; + + const onTouchStart = (e: TouchEvent) => { + touchYRef.current = e.touches[0].clientY; + }; + + const onTouchMove = (e: TouchEvent) => { + if (!scrollRef.current) return; + + const midpoint = + (scrollRef.current.getBoundingClientRect().top + + scrollRef.current.getBoundingClientRect().bottom) / + 2; + const minHeight = getMinHeight(); + const maxHeight = getMaxHeight(); + const deltaY = touchYRef.current - e.touches[0].clientY; + touchYRef.current = e.touches[0].clientY; + const currentScrollPos = getCurrentScrollPos(); + + if ( + midpoint > window.innerHeight / 2 - 50 && + midpoint < window.innerHeight / 2 + 50 + ) { + updateScrollPos(deltaY); + + if (enterFromTop.current === true && currentScrollPos !== maxHeight) { + if (e.cancelable) e.preventDefault(); + else setTouchAction('none'); + } else if ( + enterFromTop.current === false && + currentScrollPos !== minHeight + ) { + if (e.cancelable) e.preventDefault(); + else setTouchAction('none'); + } else { + setTouchAction('auto'); + } + } else if ( + currentScrollPos !== maxHeight && + currentScrollPos !== minHeight + ) { + if (e.cancelable) e.preventDefault(); + else setTouchAction('none'); + updateScrollPos(deltaY); + } else { + if (midpoint < window.innerHeight / 2 - 50) { + enterFromTop.current = false; + } else if (midpoint > window.innerHeight / 2 + 50) { + enterFromTop.current = true; + } else { + enterFromTop.current = null; + } + } + }; + + const onTouchEnd = () => { + touchYRef.current = 0; + setTouchAction('auto'); + }; + + window.addEventListener('wheel', onScroll, { passive: false }); + window.addEventListener('touchstart', onTouchStart, { passive: false }); + window.addEventListener('touchmove', onTouchMove, { passive: false }); + window.addEventListener('touchend', onTouchEnd, { passive: false }); + + const listeners = responsibilities.map((_, index) => { + const id = `${title}responsibility-${index}`; + const element = document.getElementById(id); + const listener = () => snapToIndex(index); + if (element) element.addEventListener('click', listener); + return { element, listener }; + }); + + return () => { + window.removeEventListener('wheel', onScroll); + window.removeEventListener('touchstart', onTouchStart); + window.removeEventListener('touchmove', onTouchMove); + window.removeEventListener('touchend', onTouchEnd); + listeners.forEach(({ element, listener }) => { + element?.removeEventListener('click', listener); + }); + }; + }, [responsibilities, title]); + + useEffect(() => { + if (!scrollRef.current) return; + if (!scrollPos) { + const initial = (scrollRef.current.clientHeight * 1) / 8; + scrollPosRef.current = initial; + setScrollPos(initial); + } + + const maxHeight = scrollRef.current?.clientHeight || 50; + const positions = [ + (maxHeight * 1) / 8, + (maxHeight * 3) / 8, + (maxHeight * 21) / 32, + maxHeight, + ]; + const minScrollHeight = (scrollRef.current.clientHeight * 1) / 8; + + let min = 0; + for (let i = 1; i < positions.length; i += 1) { + if ( + Math.abs((scrollPos ?? minScrollHeight) - positions[i]) < + Math.abs((scrollPos ?? minScrollHeight) - positions[min]) + ) { + min = i; + } + } + + setResponsibilityIndex(min); + }, [scrollPos]); + + return { + sectionRef, + scrollRef, + responsibilityIndex, + scrollPos, + }; +} From 4f6bd226bdbede1db5b6dead16460d4bfbeed19a Mon Sep 17 00:00:00 2001 From: michelleyeoh Date: Sun, 3 May 2026 17:43:46 -0700 Subject: [PATCH 18/19] moved hook file location --- .../StarterKit/Resources => _hooks}/useIdeateScroll.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/(pages)/{(hackers)/_components/StarterKit/Resources => _hooks}/useIdeateScroll.tsx (100%) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/useIdeateScroll.tsx b/app/(pages)/_hooks/useIdeateScroll.tsx similarity index 100% rename from app/(pages)/(hackers)/_components/StarterKit/Resources/useIdeateScroll.tsx rename to app/(pages)/_hooks/useIdeateScroll.tsx From 1131662bb23ed5d1c326a42ad0bb664ba623d44b Mon Sep 17 00:00:00 2001 From: michelleyeoh Date: Sun, 3 May 2026 17:47:02 -0700 Subject: [PATCH 19/19] updated hook path --- .../_components/StarterKit/Resources/Responsibilities.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx index d0f9c957..db4635b3 100644 --- a/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx +++ b/app/(pages)/(hackers)/_components/StarterKit/Resources/Responsibilities.tsx @@ -2,7 +2,7 @@ import Image from 'next/image'; import type { StaticImageData } from 'next/image'; -import useIdeateScroll from './useIdeateScroll'; +import useIdeateScroll from '@hooks/useIdeateScroll'; interface Responsibility { dark_icon: StaticImageData;