Back

MyStie

September 2024
MyStie
MyRole
I did everything.
Team
Euichan Lee
Timeline
1 Week, starts September 2024
Tech Keywords
Next.js, TypeScript, Figma, Tailwindcss, Redux, middleware, i18n, Framer
Overview
My Site is a portfolio website. It was implemented using Next.js, with all designs implemented in Tailwindcss and designed with responsiveness in mind. Component movements were implemented using Framer Motion.
Redux is used to globally manage the dark mode state. Korean-English switching functionality was implemented using the i18n library along with dynamic routing through middleware.
HIGHLIGHTS
A personal portfolio website providing project descriptions and a personal resume.
Project highlight
Project highlight
Project highlight
DESIGN
Elements considered during design.
Componentization
From the beginning of the design process, we applied componentization considering reusability. Through componentization, we aimed for optimization in the development process.
Responsive Design
We applied designs that change according to screen size. In the code, we used Tailwindcss to apply the designs.
For parts where animations change according to the design, we detected this using window.addEventListener.
  useEffect(() => {
    const checkScreenSize = () => {
      setIsSmallScreen(window.innerWidth < 640); // 640px is the 'sm' breakpoint in Tailwind
    };

    checkScreenSize();
    window.addEventListener('resize', checkScreenSize);

    return () => window.removeEventListener('resize', checkScreenSize);
  }, []);
For components, descriptions are displayed when the mouse hovers over them. However, since you can't hover on a phone, we checked the size to apply different animations.
TableOfContents.tsx
ProjectBox.tsx
LANGUAGE
How to implement Korean-English switching?
Middleware
We use dynamic routing to detect 'ko' or 'en' according to the locale. When the language mode switch is pressed, it sets a new path, and the middleware intercepts this signal and changes it to the appropriate path.
i18n
We used the i18n library, which enables internationalization processing such as language conversion. Different message files are loaded according to the locale settings.
import createMiddleware from 'next-intl/middleware';
 
export default createMiddleware({
  locales: ['en', 'ko'],
 
  defaultLocale: 'en'
});
 
export const config = {
  matcher: ['/((?!api|_next|.*\..*).*)']
};
The locale value is detected through middleware.
middleware.ts
i18n.ts
layout.tsx
HomePage.tsx
TABLE OF CONTENTS
How to navigate to the table of contents across numerous pages.
Section
We used IntersectionObserver to detect each section. Clicking scrolls to each section using scrollIntoView.
  useEffect(() => {
    const observerOptions = {
      root: null,
      rootMargin: '-20% 0px -20% 0px',
      threshold: 0.1
    };

    const observerCallback = (entries: IntersectionObserverEntry[]) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          setActiveSection(entry.target.id);
        }
      });
    };

    const observer = new IntersectionObserver(observerCallback, observerOptions);

    document.querySelectorAll('section[id]').forEach((section) => {
      observer.observe(section);
    });

    const handleScroll = () => {
	    //...
    };

    window.addEventListener('scroll', handleScroll);

    return () => {
      observer.disconnect();
      window.removeEventListener('scroll', handleScroll);
    };
  }, [sections]);
Check the position of each section and determine which section is currently visible on the screen.
TableOfContents.tsx
scroll
STATE
How to manage state using Redux on pages.
Dark mode
When the darkModeSwitch is pressed, we use dispatch to store the value in Redux. Through the provider, we check the locale value and change the dark mode value.
export default function DarkModeSwitch() {
  const dispatch = useDispatch();
  const isDarkMode = useSelector((state: RootState) => state.theme.isDarkMode);
  
  const handleToggle = () => {
    dispatch(toggleTheme());
  };

  return (
    <motion.button
      onClick={handleToggle}
      className="w-14 h-8 p-1 bg-white/5 rounded-full shadow border-2 border-white/30 backdrop-blur-[15px] relative flex items-center"
      transition={{ duration: 0.3 }}
    >
	    //...
      </motion.div>
    </motion.button>
  );
}
When the dark mode button is pressed, the value is updated through dispatch.
DarkModeSwitch.tsx
ANIMATION
Increasing smooth user experience.
Framer
We used Framer to smoothly implement movements such as components slightly rising on mouse hover, navigation bar movement, and popup component display.
<motion.div
	initial={{ opacity: 1, scale: 0.9 }}
	animate={{ opacity: 1, scale: 1 }}
	exit={{ opacity: 0, scale: 0.9 }}
	transition={{ duration: 0.3 }}
	className='fixed inset-0 z-50 flex items-center justify-center p-4'
	onClick={onClose}
>
	//...
</motion.div>
We applied animations for components appearing and disappearing.
ProjectBox.tsx