Mulaomerović Mahir
Web developer

Improving individual projects presentation in the lab section

2024-02-10

In this post, I presented an enhancement to a part of the blog. This is a single step that suffices for the current phase of the blog. I hope to write more similar posts in the future.

LAB represents a page in the page router in Next.js. Each project is a separate component. Although they have a similar form, all projects are unique and have their own distinct characteristics. This is to avoid a sense of copy-paste dominance, which is common in React applications, as well as sibling Next.js applications.


Each component also includes a child component called ImageCounter. A simple component that allows image changes through clicks, functioning on the principle of a counter.


Here is old code of a component:


const Section = ({ title, imageSrc, description }) => { return ( <div className="section" style={{ alignContent: "left" }}> <div className="content" style={{ padding: "30px" }}> <div className="image" style={{ border: "2px solid black", width: "300px", height: "200px", borderRadius: "5px", }} > <ImageC imgArray={imageSrc} /> </div> <div className="description" style={{ marginLeft: "20px" }}> <h2 style={{ marginBottom: "5px", fontWeight: "bolder" }}>{title}</h2> <p className="font-light text-xl leading-8">{description}</p> </div> </div> </div> ); };

Here is the rendered look of this component


Image 1: Oldview of sampled project.

As you can see, the old view of the project is not very appealing. It is just a simple image with a title and a description. It is not very interactive and does not provide a good user experience. So, I decided to improve the presentation of individual projects in the lab section by adding animations and effects.


Here is the new code of the structure of an element:


import React, { useState } from "react"; import ImageCounter from "../utils/ImageCounter"; export default function RestApiProject() { const title = "REST API for a web application"; const imageSrc = [ "/whatisrest1.png", "/httpmethods1.png", "/restprinciples1.png", ]; const description = "This API provides a comprehensive suite of endpoints designed to facilitate user management within a web application. Built upon REST principles, each route is carefully crafted to ensure security, efficiency, and scalability. "; const [showMore, setShowMore] = useState(false); // State to control the visibility of additional content const [isImageHovered, setIsImageHovered] = useState(false); const handleImageClick = () => { // Add your logic here for image click }; const toggleShowMore = () => { setShowMore(!showMore); }; return ( <div className="section flex flex-col items-center"> <div className="content w-full max-w-screen-lg p-8 flex flex-col md:flex-row"> <div className="md:w-1/3 md:order-1"> <div className={`image h-48 relative overflow-hidden ${ isImageHovered ? "shadow-lg" : "shadow" } transition duration-300`} onMouseEnter={() => setIsImageHovered(true)} onMouseLeave={() => setIsImageHovered(false)} onClick={handleImageClick} style={{ cursor: "pointer" }} // Change cursor style > <ImageCounter imgArray={imageSrc} /> </div> </div> <div className="description ml-0 mt-4 md:ml-8 md:w-2/3 md:mt-0"> <h2 className="mb-2 font-bold">{title}</h2> <p className="font-light text-xl leading-8">{description}</p> <div className={`overflow-hidden transition-max-h ease-out duration-1000 ${ showMore ? "max-h-full" : "max-h-0" }`} > {showMore && ( <ol className="list-decimal list-inside"> <p style={{ marginBottom: "1em" }}></p> <h1> This is removed in order to reduce the length of the content </h1> </ol> )} </div> <div className="mt-4 flex items-center"> <button onClick={toggleShowMore} className="px-4 py-2 bg-gray-200 rounded-md text-gray-800 hover:bg-gray-300 transition duration-1000" > {showMore ? "See Less" : "See More"} </button> <a href="https://github.com/mmahE96/UserManagementAPI" target="_blank" rel="noopener noreferrer" className="flex items-center px-4 py-2 ml-4 bg-gray-200 rounded-md text-gray-800 hover:bg-gray-300 transition duration-300" > <svg className="h-5 w-5 mr-2" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > <path fillRule="evenodd" clipRule="evenodd" d="M12 0C5.373 0 0 5.373 0 12c0 5.303 3.438 9.801 8.206 11.385.6.11.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.582-4.042-1.582-.547-1.385-1.334-1.756-1.334-1.756-1.089-.743.083-.728.083-.728 1.205.085 1.838 1.238 1.838 1.238 1.07 1.834 2.807 1.303 3.49.997.108-.776.42-1.303.763-1.603-2.665-.305-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.223-.124-.304-.535-1.526.117-3.176 0 0 1.007-.322 3.3 1.23.96-.267 1.98-.4 3-.405 1.02.005 2.04.138 3 .405 2.29-1.552 3.295-1.23 3.295-1.23.654 1.65.243 2.872.12 3.176.77.843 1.23 1.913 1.23 3.223 0 4.61-2.805 5.622-5.475 5.92.43.372.815 1.102.815 2.22 0 1.605-.015 2.896-.015 3.286 0 .32.215.694.825.576C20.565 21.795 24 17.296 24 12c0-6.627-5.373-12-12-12z" /> </svg> View on GitHub </a> </div> </div> </div> </div> ); }

Here is the rendered look of this component


Image 2: New view of sampled project.

Image 3: New view of sampled project, when hovering over image, subtle shadow is shown below image.

Image 4: New view of sampled project, when clicked on image, new image will show.

As you can see, the new view of the project is much more appealing. It is interactive and provides a good user experience. The image has a subtle shadow when hovered over, and when clicked, a new image will show. The description is also more interactive, with a "See More" button that will show more content when clicked. The "View on GitHub" button is also interactive and will take the user to the GitHub repository when clicked.


Now, wil go through code and explain the changes made to improve the presentation of individual projects in the lab section.


Here is a list with an explanation of all Tailwind classes used in this component.
  1. section: Defines a section of content.
  2. flex: Establishes a flex container to enable flex layout.
  3. flex-col: Sets the flex direction to column, arranging items vertically.
  4. items-center: Aligns flex items along the vertical center of the container.
  5. content: Styles for the content section.
  6. w-full: Sets the width to 100%.
  7. max-w-screen-lg: Sets the maximum width of the container to a large screen size.
  8. p-8: Adds padding of 8 units to all sides of the container.
  9. md:flex-row: Specifies that the flex layout should change to row direction for medium-sized screens and above.
  10. md:w-1/3: Sets the width to one-third of the container width for medium-sized screens and above.
  11. md:order-1: Changes the order of the element to 1 (second position) for medium-sized screens and above.
  12. h-48: Sets the height to 12rem (48px).
  13. relative: Establishes a positioning context for child elements with absolute positioning.
  14. overflow-hidden: Clips content that overflows the element's box.
  15. shadow: Adds a shadow effect to the element.
  16. transition: Applies a transition effect to property changes.
  17. duration-300: Sets the duration of the transition to 300ms.
  18. onMouseEnter: Event handler for mouse enter.
  19. onMouseLeave: Event handler for mouse leave.
  20. onClick: Event handler for click.
  21. cursor-pointer: Changes the cursor to a pointer when hovering over the element.
  22. ml-0: Sets the left margin to 0.
  23. mt-4: Sets the top margin to 1rem (4px).
  24. md:ml-8: Sets the left margin to 2rem (8px) for medium-sized screens and above.
  25. md:w-2/3: Sets the width to two-thirds of the container width for medium-sized screens and above.
  26. md:mt-0: Sets the top margin to 0 for medium-sized screens and above.
  27. mb-2: Sets the bottom margin to 0.5rem (2px).
  28. font-bold: Applies bold font weight to the text.
  29. list-decimal: Sets the list style to decimal numbers.
  30. list-inside: Positions the list marker (decimal number) inside the list item.
  31. overflow-hidden: Clips content that overflows the element's box.
  32. transition-max-h: Applies a transition effect to maximum height changes.
  33. ease-out: Specifies the easing function for the transition.
  34. duration-1000: Sets the duration of the transition to 1000ms.
  35. max-h-full: Sets the maximum height to full height.
  36. max-h-0: Sets the maximum height to 0.
  37. px-4: Sets the horizontal padding to 1rem (4px).
  38. py-2: Sets the vertical padding to 0.5rem (2px).
  39. bg-gray-200: Sets the background color to a shade of gray.
  40. rounded-md: Applies medium border radius to the element.
  41. text-gray-800: Sets the text color to a shade of gray.
  42. hover:bg-gray-300: Changes the background color to a lighter shade of gray when hovered.
  43. transition: Applies a transition effect to property changes.
  44. h-5: Sets the height to 1.25rem (5px).
  45. w-5: Sets the width to 1.25rem (5px).
  46. mr-2: Sets the right margin to 0.5rem (2px).
  47. fill-current: Fills the current text color for SVG elements.
  48. viewBox: Defines the aspect ratio, width, and height of the SVG view box.
  49. xmlns: Specifies the XML namespace.
  50. h-48: Sets the height to 12rem (48px).

First, I created a new component called RestApiProject.js. This component is a functional component that returns the new structure of the project element. The component uses the useState hook to manage the state of the selected image and the visibility of additional content. The component also uses the useState hook to manage the state of the image hover effect. The handleImageClick function is used to handle the image click event, and the toggleShowMore function is used to toggle the visibility of additional content.


<div className={`overflow-hidden transition-max-h ease-out duration-1000 ${ showMore ? "max-h-full" : "max-h-0" }`} > {showMore && ( <ol className="list-decimal list-inside"> <p style={{ marginBottom: "1em" }}></p> <h1>This is removed in order to reduce the length of the content</h1> </ol> )} </div>

Here, I added a new div element to the description section of the project element. This div element contains the additional content that will be shown when the "See More" button is clicked. The div element has a transition-max-h class that will animate the height of the div element when the visibility changes. The transition-max-h class uses the ease-out timing function and a duration of 1000ms to create a smooth animation effect.


<div className="mt-4 flex items-center"> <button onClick={toggleShowMore} className="px-4 py-2 bg-gray-200 rounded-md text-gray-800 hover:bg-gray-300 transition duration-1000" > {showMore ? 'See Less' : 'See More'} </button> <a href="https://github.com/mmahE96/UserManagementAPI" target="_blank" rel="noopener noreferrer" className="flex items-center px-4 py-2 ml-4 bg-gray-200 rounded-md text-gray-800 hover:bg-gray-300 transition duration-300" > <svg className="h-5 w-5 mr-2" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > <path fillRule="evenodd" clipRule="evenodd" d="M12 0C5.373 0 0 5.373 0 12c0 5.303 3.438 9.801 8.206 11.385.6.11.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.582-4.042-1.582-.547-1.385-1.334-1.756-1.334-1.756-1.089-.743.083-.728.083-.728 1.205.085 1.838 1.238 1.838 1.238 1.07 1.834 2.807 1.303 3.49.997.108-.776.42-1.303.763-1.603-2.665-.305-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.223-.124-.304-.535-1.526.117-3.176 0 0 1.007-.322 3.3 1.23.96-.267 1.98-.4 3-.405 1.02.005 2.04.138 3 .405 2.29-1.552 3.295-1.23 3.295-1.23.654 1.65.243 2.872.12 3.176.77.843 1.23 1.913 1.23 3.223 0 4.61-2.805 5.622-5.475 5.92.43.372.815 1.102.815 2.22 0 1.605-.015 2.896-.015 3.286 0 .32.215.694.825.576C20.565 21.795 24 17.296 24 12c0-6.627-5.373-12-12-12z" /> </svg> View on GitHub </a> </div> </div>

Here, I added a new div element to the description section of the project element. This div element contains the "See More" button and the "View on GitHub" button. The "See More" button is used to toggle the visibility of additional content, and the "View on GitHub" button is used to take the user to the GitHub repository when clicked. Both buttons have a hover effect that changes the background color when hovered over. The transition duration is set to 1000ms to create a smooth animation effect.


<path fillRule="evenodd" clipRule="evenodd" d="M12 0C5.373 0 0 5.373 0 12c0 5.303 3.438 9.801 8.206 11.385.6.11.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.582-4.042-1.582-.547-1.385-1.334-1.756-1.334-1.756-1.089-.743.083-.728.083-.728 1.205.085 1.838 1.238 1.838 1.238 1.07 1.834 2.807 1.303 3.49.997.108-.776.42-1.303.763-1.603-2.665-.305-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.223-.124-.304-.535-1.526.117-3.176 0 0 1.007-.322 3.3 1.23.96-.267 1.98-.4 3-.405 1.02.005 2.04.138 3 .405 2.29-1.552 3.295-1.23 3.295-1.23.654 1.65.243 2.872.12 3.176.77.843 1.23 1.913 1.23 3.223 0 4.61-2.805 5.622-5.475 5.92.43.372.815 1.102.815 2.22 0 1.605-.015 2.896-.015 3.286 0 .32.215.694.825.576C20.565 21.795 24 17.296 24 12c0-6.627-5.373-12-12-12z" />

Path element are used to show the images in the project. I added the new images to the public folder and updated the image paths in the RestApiProject component. FillRule and clipRule are used to fill the svg element with the current color and clip the path of the svg element, respectively.d="numbers" is used to define the path of the svg element.SVG is used to define the path of the svg element.


<button onClick={toggleShowMore} className="px-4 py-2 bg-gray-200 rounded-md text-gray-800 hover:bg-gray-300 transition duration-1000"> {showMore ? "See Less" : "See More"} </button>

This part "{showMore ? 'See Less' : 'See More'}" is used to toggle the text of the "See More" button based on the visibility of additional content. If the additional content is visible, the text of the button will be "See Less", and if the additional content is hidden, the text of the button will be "See More".


import RestApiProject from "../components/lab-components/Projects/RestApiProject"; export default function Lab() { return ( <Layout> <div className="min-h-screen"> <RestApiProject /> </div> </Layout> ); }

Next, I added the new component to the Lab page. I imported the RestApiProject component and added it to the Lab page. I also removed the old structure of the project element and replaced it with the new component.


Finally, I added the new images to the public folder and updated the image paths in the RestApiProject component.

By adding animations and effects to the individual projects in the lab section, I was able to improve the presentation of the projects and provide a better user experience. The new presentation is more interactive and engaging, and it makes the lab section more visually appealing.


That's all for now. I will continue to work on improving the site and adding new features, so stay tuned for more updates!


Mahirnextjs tailwindjs architecture guide mardown blog