diff --git a/bun.lock b/bun.lock
index fb95158..b8d055f 100644
--- a/bun.lock
+++ b/bun.lock
@@ -10,7 +10,7 @@
"dedent": "^1.6.0",
"framer-motion": "^12.23.0",
"lucide-react": "^0.523.0",
- "next": "16.0.1",
+ "next": "16.0.10",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-syntax-highlighter": "^15.6.1",
@@ -283,21 +283,21 @@
"@next/env": ["@next/env@16.0.3", "", {}, "sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ=="],
- "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-R0YxRp6/4W7yG1nKbfu41bp3d96a0EalonQXiMe+1H9GTHfKxGNCGFNWUho18avRBPsO8T3RmdWuzmfurlQPbg=="],
+ "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg=="],
- "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-kETZBocRux3xITiZtOtVoVvXyQLB7VBxN7L6EPqgI5paZiUlnsgYv4q8diTNYeHmF9EiehydOBo20lTttCbHAg=="],
+ "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw=="],
- "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-hWg3BtsxQuSKhfe0LunJoqxjO4NEpBmKkE+P2Sroos7yB//OOX3jD5ISP2wv8QdUwtRehMdwYz6VB50mY6hqAg=="],
+ "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw=="],
- "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-UPnOvYg+fjAhP3b1iQStcYPWeBFRLrugEyK/lDKGk7kLNua8t5/DvDbAEFotfV1YfcOY6bru76qN9qnjLoyHCQ=="],
+ "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw=="],
- "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Et81SdWkcRqAJziIgFtsFyJizHoWne4fzJkvjd6V4wEkWTB4MX6J0uByUb0peiJQ4WeAt6GGmMszE5KrXK6WKg=="],
+ "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA=="],
- "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qBbgYEBRrC1egcG03FZaVfVxrJm8wBl7vr8UFKplnxNRprctdP26xEv9nJ07Ggq4y1adwa0nz2mz83CELY7N6Q=="],
+ "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g=="],
- "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.0.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-cPuBjYP6I699/RdbHJonb3BiRNEDm5CKEBuJ6SD8k3oLam2fDRMKAvmrli4QMDgT2ixyRJ0+DTkiODbIQhRkeQ=="],
+ "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.0.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg=="],
- "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.1", "", { "os": "win32", "cpu": "x64" }, "sha512-XeEUJsE4JYtfrXe/LaJn3z1pD19fK0Q6Er8Qoufi+HqvdO4LEPyCxLUt4rxA+4RfYo6S9gMlmzCMU2F+AatFqQ=="],
+ "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.10", "", { "os": "win32", "cpu": "x64" }, "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="],
@@ -967,7 +967,7 @@
"neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
- "next": ["next@16.0.1", "", { "dependencies": { "@next/env": "16.0.1", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.1", "@next/swc-darwin-x64": "16.0.1", "@next/swc-linux-arm64-gnu": "16.0.1", "@next/swc-linux-arm64-musl": "16.0.1", "@next/swc-linux-x64-gnu": "16.0.1", "@next/swc-linux-x64-musl": "16.0.1", "@next/swc-win32-arm64-msvc": "16.0.1", "@next/swc-win32-x64-msvc": "16.0.1", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-e9RLSssZwd35p7/vOa+hoDFggUZIUbZhIUSLZuETCwrCVvxOs87NamoUzT+vbcNAL8Ld9GobBnWOA6SbV/arOw=="],
+ "next": ["next@16.0.10", "", { "dependencies": { "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.10", "@next/swc-darwin-x64": "16.0.10", "@next/swc-linux-arm64-gnu": "16.0.10", "@next/swc-linux-arm64-musl": "16.0.10", "@next/swc-linux-x64-gnu": "16.0.10", "@next/swc-linux-x64-musl": "16.0.10", "@next/swc-win32-arm64-msvc": "16.0.10", "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA=="],
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
@@ -1291,7 +1291,7 @@
"jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
- "next/@next/env": ["@next/env@16.0.1", "", {}, "sha512-LFvlK0TG2L3fEOX77OC35KowL8D7DlFF45C0OvKMC4hy8c/md1RC4UMNDlUGJqfCoCS2VWrZ4dSE6OjaX5+8mw=="],
+ "next/@next/env": ["@next/env@16.0.10", "", {}, "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang=="],
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
diff --git a/src/app/(main)/home.tsx b/src/app/(main)/home.tsx
index 831aacb..25f49f9 100644
--- a/src/app/(main)/home.tsx
+++ b/src/app/(main)/home.tsx
@@ -16,72 +16,55 @@ const sections: Section[] = [
export default function HomeComponent() {
const router = useRouter();
- const totalItems = sections.length;
- const columns = 3;
-
- const colSpanClasses: { [key: number]: string } = {
- 2: 'md:col-span-2',
- 3: 'md:col-span-3',
- 6: 'md:col-span-6',
- };
-
return (
-
-
-
+
+
+
+ Interview preparation
+
+
Any interview prep
+
+ Choose a topic to start preparing
+
-
- {sections.map((section, index) => {
- const rowNumber = Math.floor(index / columns);
- const isLastRow =
- rowNumber === Math.floor((totalItems - 1) / columns);
-
- const itemsOnLastRow = totalItems % columns;
- const itemsOnThisRow =
- isLastRow && itemsOnLastRow > 0 ? itemsOnLastRow : columns;
-
- const colSpan = Math.round(6 / itemsOnThisRow);
-
- const className = `rounded-lg border border-zinc-800 bg-zinc-900/90 p-6 shadow-sm transition-colors duration-200 hover:border-zinc-600 ${
- colSpanClasses[colSpan]
- }`;
- return (
-
- );
- })}
+
+ {sections.map((section) => (
+
+ ))}
);
diff --git a/src/app/frontend/junior/frontend-junior.tsx b/src/app/frontend/junior/frontend-junior.tsx
index 86969bd..bce1b1b 100644
--- a/src/app/frontend/junior/frontend-junior.tsx
+++ b/src/app/frontend/junior/frontend-junior.tsx
@@ -39,65 +39,56 @@ const sections: Section[] = [
export default function FrontendJunior() {
const router = useRouter();
- const totalItems = sections.length;
- const columns = 3;
-
- const colSpanClasses: { [key: number]: string } = {
- 2: 'md:col-span-2',
- 3: 'md:col-span-3',
- 6: 'md:col-span-6',
- };
-
return (
-
-
-
- Junior Frontend Developer Preparation
+
+
+
+ Frontend
+
+
+ Junior Developer Preparation
-
- (in development, 'in progress' parts are not completed)
+
+ Topics marked as “in progress” are still being written
-
- {sections.map((section, index) => {
- const rowNumber = Math.floor(index / columns);
- const isLastRow =
- rowNumber === Math.floor((totalItems - 1) / columns);
-
- const itemsOnLastRow = totalItems % columns;
- const itemsOnThisRow =
- isLastRow && itemsOnLastRow > 0 ? itemsOnLastRow : columns;
- const colSpan = Math.round(6 / itemsOnThisRow);
-
- const className = `rounded-lg border border-zinc-800 bg-zinc-900/90 p-6 shadow-sm transition-colors duration-200 hover:border-zinc-600 ${
- colSpanClasses[colSpan]
- }`;
-
- return (
-
);
}
diff --git a/src/components/layout/page-container.tsx b/src/components/layout/page-container.tsx
index b7ca7df..7386c9d 100644
--- a/src/components/layout/page-container.tsx
+++ b/src/components/layout/page-container.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useEffect, useMemo, useState } from 'react';
+import { useMemo, useState } from 'react';
import { WidthSwitcher } from '@/components/width-switcher';
import type { PageContainerProps, WidthPreset } from '@/types';
@@ -30,26 +30,6 @@ export function PageContainer({
return initialWidth;
});
- const [headerHeight, setHeaderHeight] = useState
(120);
-
- useEffect(() => {
- if (typeof window === 'undefined') {
- return;
- }
- const header = document.getElementById('page-header');
- if (!header) {
- return;
- }
-
- const update = () => setHeaderHeight(header.getBoundingClientRect().height);
- update();
- const observer = new ResizeObserver(update);
- observer.observe(header);
- return () => {
- observer.disconnect();
- };
- }, []);
-
const applyWidthPreference = (next: WidthPreset) => {
setWidth(next);
try {
@@ -89,23 +69,23 @@ export function PageContainer({
const containerClasses = useMemo(() => {
return [
- 'content-container mx-0 sm:mx-auto px-4 py-8',
+ 'content-container w-full px-4 sm:px-6 py-8',
'transition-[max-width] duration-200 ease-in-out',
className,
].join(' ');
}, [className]);
return (
-
- {allowWidthToggle && (
-
- )}
-
-
{children}
-
+
+
+ {allowWidthToggle && (
+
+ )}
+ {children}
+
+
);
}
diff --git a/src/components/notes-area/notes-area.tsx b/src/components/notes-area/notes-area.tsx
index 6644a05..aad8c34 100644
--- a/src/components/notes-area/notes-area.tsx
+++ b/src/components/notes-area/notes-area.tsx
@@ -6,9 +6,9 @@ export const NotesArea = ({
}: NotesAreaProps) => {
return (
-
{placeholder}
+
{placeholder}
);
};
diff --git a/src/components/page-header/page-header.tsx b/src/components/page-header/page-header.tsx
index 219d2b2..ab4bd91 100644
--- a/src/components/page-header/page-header.tsx
+++ b/src/components/page-header/page-header.tsx
@@ -1,9 +1,7 @@
'use client';
-import { AnimatePresence, motion } from 'framer-motion';
import { BookOpenCheck, House } from 'lucide-react';
import { useRouter } from 'next/navigation';
-import { useCallback, useEffect, useRef, useState } from 'react';
import type { PageHeaderProps } from '@/types';
export const PageHeader = ({
@@ -11,258 +9,47 @@ export const PageHeader = ({
title,
topicHome,
}: PageHeaderProps) => {
- type MediaQueryListWithLegacy = MediaQueryList & {
- addListener: (listener: (e: MediaQueryListEvent) => void) => void;
- removeListener: (listener: (e: MediaQueryListEvent) => void) => void;
- };
-
const router = useRouter();
- const [isScrolled, setIsScrolled] = useState(false);
- const [isInitialLoad, setIsInitialLoad] = useState(true);
- const lastScrollY = useRef(0);
- const [isHiddenOnMobile, setIsHiddenOnMobile] = useState(false);
- const [isMobileScreen, setIsMobileScreen] = useState(false);
- const isMobile = useRef(false);
- const downwardAccumPx = useRef(0);
- const upwardAccumPx = useRef(0);
- const isHiddenOnMobileRef = useRef(false);
- const lastCssHeaderHeight = useRef(null);
-
- // Threshold constants for mobile hysteresis
- const JITTER_PX = 5;
- const HIDE_THRESHOLD_PX = 24;
- const SHOW_THRESHOLD_PX = 64;
-
- useEffect(() => {
- isHiddenOnMobileRef.current = isHiddenOnMobile;
- }, [isHiddenOnMobile]);
-
- const resetNonMobileState = useCallback(() => {
- downwardAccumPx.current = 0;
- upwardAccumPx.current = 0;
- if (isHiddenOnMobileRef.current) setIsHiddenOnMobile(false);
- }, []);
-
- const accumulateDown = useCallback((delta: number) => {
- downwardAccumPx.current += delta;
- upwardAccumPx.current = 0;
- }, []);
-
- const accumulateUp = useCallback((delta: number) => {
- upwardAccumPx.current += -delta;
- downwardAccumPx.current = 0;
- }, []);
-
- const maybeHideOnMobile = useCallback((currentScrollY: number) => {
- if (
- !isHiddenOnMobileRef.current &&
- currentScrollY > 16 &&
- downwardAccumPx.current > HIDE_THRESHOLD_PX
- ) {
- setIsHiddenOnMobile(true);
- }
- }, []);
-
- const maybeShowOnMobile = useCallback(() => {
- if (
- isHiddenOnMobileRef.current &&
- upwardAccumPx.current > SHOW_THRESHOLD_PX
- ) {
- setIsHiddenOnMobile(false);
- }
- }, []);
-
- const maybeResetAtTop = useCallback((currentScrollY: number) => {
- if (currentScrollY < 2 && isHiddenOnMobileRef.current) {
- setIsHiddenOnMobile(false);
- }
- }, []);
-
- const handleMobileScroll = useCallback(
- (currentScrollY: number, delta: number) => {
- if (!isMobile.current) {
- resetNonMobileState();
- return;
- }
-
- const absDelta = Math.abs(delta);
- if (absDelta <= JITTER_PX) {
- maybeResetAtTop(currentScrollY);
- return;
- }
-
- if (delta > 0) {
- accumulateDown(delta);
- maybeHideOnMobile(currentScrollY);
- } else if (delta < 0) {
- accumulateUp(delta);
- maybeShowOnMobile();
- }
-
- maybeResetAtTop(currentScrollY);
- },
- [
- accumulateDown,
- accumulateUp,
- maybeHideOnMobile,
- maybeResetAtTop,
- maybeShowOnMobile,
- resetNonMobileState,
- ]
- );
-
- // Track mobile breakpoint to enable full hide behavior on small screens
- useEffect(() => {
- if (typeof window === 'undefined') return;
- const mq: MediaQueryList = window.matchMedia('(max-width: 639px)');
- const setMobileFlag = () => {
- isMobile.current = mq.matches;
- setIsMobileScreen(mq.matches);
- };
- setMobileFlag();
- const hasModernListener = typeof mq.addEventListener === 'function';
- if (hasModernListener) {
- mq.addEventListener('change', setMobileFlag);
- } else {
- (mq as MediaQueryListWithLegacy).addListener(setMobileFlag);
- }
- return () => {
- if (hasModernListener) {
- mq.removeEventListener('change', setMobileFlag);
- } else {
- (mq as MediaQueryListWithLegacy).removeListener(setMobileFlag);
- }
- };
- }, []);
-
- // Scroll listener with rAF batching
- useEffect(() => {
- if (typeof window === 'undefined') return;
- let rafId: number | null = null;
- const onScroll = () => {
- if (rafId !== null) return;
- rafId = window.requestAnimationFrame(() => {
- rafId = null;
- const currentScrollY = Math.max(0, window.scrollY);
- const delta = currentScrollY - lastScrollY.current;
- // Avoid toggling collapse state on mobile to reduce flicker; rely on full-hide
- if (!isMobile.current) {
- setIsScrolled(currentScrollY > 20 && delta > 0);
- }
- handleMobileScroll(currentScrollY, delta);
- lastScrollY.current = currentScrollY;
- });
- };
-
- setIsInitialLoad(false);
- onScroll();
- window.addEventListener('scroll', onScroll, { passive: true });
- return () => {
- window.removeEventListener('scroll', onScroll);
- if (rafId !== null) cancelAnimationFrame(rafId);
- };
- }, [handleMobileScroll]);
-
- // Expose current header height as a CSS variable for other components
- useEffect(() => {
- if (typeof document === 'undefined') {
- return;
- }
- const expandedHeight = '128px';
- const collapsedHeight = '72px';
- let current: string;
- if (isHiddenOnMobile) {
- current = '0px';
- } else if (isMobileScreen) {
- // On mobile, when visible, keep the header at full height for clear tap targets
- current = expandedHeight;
- } else if (isInitialLoad || !isScrolled) {
- current = expandedHeight;
- } else {
- current = collapsedHeight;
- }
- if (lastCssHeaderHeight.current !== current) {
- document.documentElement.style.setProperty(
- '--page-header-height',
- current
- );
- lastCssHeaderHeight.current = current;
- }
- }, [isInitialLoad, isScrolled, isHiddenOnMobile, isMobileScreen]);
return (
- {
- if (isHiddenOnMobile) return '0px';
- if (isMobileScreen) return '128px';
- if (isInitialLoad || !isScrolled) return '128px';
- return '72px';
- })(),
- }}
+