Man overlooking lake

Photosnap

This is a solution to the Photosnap Website challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.

Skip to

Inspiration

My motivation for taking on this project was twofold: the clean, elegant design and the inherent challenge it presented. The project's aesthetics instantly appealed to me, and I saw it as a canvas to hone my skills in building reusable components, optimizing images efficiently, and upholding accessibility standards. Furthermore, embracing a mobile-first workflow was a natural step.

photosnap home page
For the alternating image heroes, I built a reusable component where the image orientation can be set to either left or right.

Stack

Next.js (App Directory): I wanted to learn how to use the new app directory and React Server Components(RSC).

TypeScript: For type safety and makes working with React fun by helping avoid runtime errors like passing the wrong data via props.

Tailwind CSS: I appreciated the utility-first approach of Tailwind CSS and its built-in utilities for colors, spacing, and media queries, making it feel like a design system that was ready to use with only minor adjustments required for customization.

Radix-UI: I took accessibility seriously, so I chose Radix UI, a collection of un-styled, composable, and accessible UI components that allowed me to build modern web applications with essential components like forms, dropdowns, and alert dialogs.

Framer Motion: I choose FM for it's spring animation engine and easy API.

photosnap style system page
My process for this project started by translating design tokens and components into code and capture them in a live design system page.
photosnap stories page
Next.js Image component was used to manage image resolution at specific viewport widths and lazy loading.

Challenges

The challenge I encountered while working on the pricing page's Compare table was to balance preserving the inherent accessibility features of tables and rows with the need for a responsive design. My goal was to ensure that users with disabilities could effectively navigate the table, but traditional HTML tables posed difficulties in adapting to mobile views without sacrificing accessibility.

To overcome this challenge, I took several steps. First, in the mobile view, I hid the headers "BASIC," "PROP," and "BUSINESS" to reduce clutter and improve readability. Then, to provide screen reader users with clear labels for the check marks, I utilized the data attribute on each check mark cell and assigned it the respective label from the data source.

To visually display these labels above the check marks, I employed the "::before" pseudo-element on each check mark cell and set its content property to the label obtained from the data attribute. This approach allowed me to maintain the table's accessibility while adapting its design to various screen sizes, ensuring a seamless user experience for all.

photosnap compare plans table
The layout table was a challenge because I wanted to preserve table and row accessibility on mobile, without using divs due to aria label limitations.

What I've Learned

React Server Components I delved into the realm of React Server Components (RSC) and got hands-on experience with both RSC and client-side components, thanks to the 'use client' directive. One key takeaway was that RSC mounts once on the server and doesn't trigger re-renders. Additionally, I discovered that client-side components can't serve as parents to RSC, but they can certainly function as children within the context of RSC.

Next.js App Routing I learned how to use the new App directory to set up pages and dynamic routes.

Framer Motion I learned how to use the motion api to orchestrate animations, and how to tweak Spring animations for smooth and realistic motion. I also leverage the AnimatePresence components allowing me to animate components when they are removed from the React tree.

TailwindCSS I learned how to transfer all of the design tokens in the design system into the Tailwind config file. While it took a little time to set up, I think it improve my development experience and made sure that my code was very consistent with the figma designs.

photosnap pricing tier toggle
For the toggle button animation I used Framer Motion Spring animation to add a touch of realism to the movement by experimenting with the stiffness and damping properties.

What's Next?

I've decided to make the switch to Cloudinary for image optimization. I've always been curious about it, and after running some tests, it's clear that Cloudinary brings several valuable advantages over the Next.js Image component. With Cloudinary, I can tap into advanced image transformations, leverage a global Content Delivery Network (CDN) for quicker load times, benefit from automatic format conversion, robust image optimization, and compression, and even perform real-time image adjustments. Plus, it's not limited to images, offering support for various other media types as well.