From Requirements to Code: Implementing the Angular E-commerce Product Listing Page
Tags: angular, grid, tutorial, responsivedesign
Published: 2024-07-14 16:25:12

## Introduction

Welcome back to my series on building a scalable Angular e-commerce application! In the [previous article](https://dev.to/cezar-plescan/building-an-angular-e-commerce-app-a-step-by-step-guide-to-understanding-and-refining-requirements-hoe), I embarked on the crucial journey of understanding and refining the requirements for our Minimum Viable Product (MVP). Through collaborative discussions with stakeholders and careful analysis, we crafted a clear user story and acceptance criteria for the first feature: displaying a list of products.

### Overview 

In this article, I'll take the next crucial step: translating those requirements into tangible code. I'll delve into the initial implementation of the product listing page, exploring the thought process behind key decisions and showcasing practical techniques for creating a responsive and visually appealing layout.

### What I'll cover

**Component Design**: Identifying the core components needed for the product list and establishing their relationships.

**Data Modeling and Mock Data**: Creating a data structure to represent our products and utilizing mock data to simulate a real-world scenario.

**Angular Material Integration**: Leveraging the power of Angular Material components to create a visually appealing and user-friendly interface.

**Responsive Grid Layout**: Implementing a flexible grid layout that adapts to different screen sizes, ensuring optimal display across devices.

**Spacing and Layout Refinements**: Exploring different techniques for achieving the desired spacing and visual balance between product cards.

By the end of this article, you'll have a solid understanding of how to transform user stories and acceptance criteria into working Angular code, setting a strong foundation for building out the rest of our e-commerce application.

### Motivation

My aim is to provide a practical, step-by-step guide that goes beyond theoretical concepts. I want to share my thought process as a developer, highlighting the decisions I make and the challenges I encounter along the way. By following along, you'll gain valuable insights into how to approach frontend development in a real-world scenario.

The goal of this article is not just to show you _how_ to build a product list, but _why_ certain decisions are made, cultivating a deeper understanding of the architectural considerations involved in creating a successful e-commerce application.

Whether you're a beginner or an experienced Angular developer, this article will equip you with the knowledge and skills to build the foundation of a successful e-commerce application.

### A Quick Recap of the User Story and Acceptance Criteria

Let's revisit the refined user story and acceptance criteria that will guide the implementation:
> _**User Story**_:
>
> _As a potential customer, I want to see a clear and visually appealing list of products with their images, names, and prices, so that I can quickly browse and compare items before making a purchase decision._
>
> _**Acceptance Criteria**:_
> - _Layout_:
>     - _The product listing page displays products in a grid layout._
>     - _Each row of the grid contains a maximum of 4 product items._
>     - _The layout is responsive and adapts to different screen sizes._
> - _Product Card Content:_
>     - _Each product card displays a clear product image._
>     - _The product name is prominently displayed below the image._
>     - _The price is displayed clearly, using the appropriate currency symbol._
> - _Functionality:_
>     - _The product list is initially loaded when the user navigates to the product listing page._
> - _MVP Considerations:_
>     - _The product list does not include pagination or infinite scrolling in this initial version._
>     - _Error handling for data fetching issues will be addressed in a later iteration._
> - _Technical Notes:_
>     - _Data Source: Product data will be initially hardcoded on the frontend._
>     - _UI Framework: The Angular Material library will be used for styling and components._

### An overview of the path from requirements to code

Now, it's time to translate these requirements into actual code. While there's no one-size-fits-all process, I'll share the high-level approach I typically take:
1. **Project setup**: Create the Angular project and any essential initial configurations.
2. **Component identification and hierarchy**: Identify the visual components needed for the product listing page and define their hierarchical relationships. This involves deciding which components will contain other components and how they will interact.
3. **Data retrieval**: Determine the data source and how to fetch and manage the product data within the application.
4. **Layout and styling**: Create the visual layout of the product list and style the individual product cards to meet the design requirements. I'll discuss this in more detail later.

While these steps provide a solid starting point, it's important to remember that the development process is often iterative. We might discover new requirements, encounter unexpected challenges, or need to make adjustments based on user feedback. Therefore, it's crucial to maintain flexibility in our planning and be prepared to adapt as we go.

In the next section, I'll delve deeper into each step of the implementation, starting with the identification of components and their hierarchical structure.

## Identifying the component structure

### Create the Angular project

Before identifying the component structure I need to create a new  project with the Angular CLI command:
```bash
npx --yes --package @angular/cli@18 ng new Angular-Architecture-Guide --defaults --standalone=true
```
If you don't have npx installed, you can run:
```bash
npm install -g npx
```

_**Note**: You can also find the code for this project on Github at https://github.com/cezar-plescan/Angular-Architecture-Guide/. The `master` branch contains the initial project setup._

### Creating the main components

Analyzing the user story, I identify two main visual elements:
- **the product list container**, a single element which holds all visible products.
- **product elements**, each containing product details within the list container.

I immediately notice that there's a parent-child component relationship between the list and each product. Here are the two components I'll create:
- **`ProductListComponent`** - the container for the entire product list
- **`ProductCardComponent`** - a reusable component for displaying individual product information.

To create these components, I'll use the Angular CLI commands:
```bash
ng generate component product-list
ng generate component product-card
```

### Building the initial component structure

Now that I have a clear vision for the product listing page, I'll start sketching out the structure of the Angular components. The `ProductListComponent` will act as an orchestrator, managing the display of multiple `ProductCardComponent` instances, each representing a single product.

To get a better sense of the data flow and component interaction, I'll create a basic template for the product list:{% embed https://gist.github.com/cezar-plescan/68afb65852c1876cde48155c2edead30 %}

This template outlines my intent: I'll iterate over a `products` array and pass each `product` object as an input to a `ProductCardComponent`. However, there are some missing pieces:
- I haven't defined the `products` array and how I'll fetch the product data.
- The `ProductCardComponent` needs a `product` input property to receive the data from the parent component.
- I need a `Product` interface to define the structure of each product so that both the list and the card components can work with the data consistently.

To address these missing pieces, I'll start by defining the `Product` interface, as this is the foundation for working with product data in my components. Then, I'll create the input property in the `ProductCardComponent` and, finally, provide some initial product data to populate the list.

### Creating the `Product` interface

Since both components will be working with product data, it makes sense to define a common structure for this data. To achieve this, I'll create an interface called `Product`: {% embed https://gist.github.com/cezar-plescan/8c6c5695db1524060095636a445bab83 %}

Now, the question is: _where should I place this interface file?_ Since it's not specific to either component but will be used by both, a shared location makes the most sense.

Within the `src/app` directory, I'll create a new folder named `shared`. This will serve as a central repository for code that's used multiple times across the application. Inside `shared`, I'll create another folder called `types` to specifically house our data interfaces.

Therefore, the `product.interface.ts` file will reside in the `src/app/shared/types` folder. This approach promotes reusability and maintainability, as it makes the `Product` interface easily accessible from anywhere in our application.

### Add an input property to the `ProductCardComponent`

Now that I have the `Product` interface, I need a way for the `ProductListComponent` to pass the data for each individual product to the `ProductCardComponent`. To achieve this, I'll add an input property to the `ProductCardComponent`.

I'll call this input property `product` since it will be used to hold the data for a single product. Here's the updated code for `product-card.component.ts`: {% embed https://gist.github.com/cezar-plescan/bf311a37829864791577cbab4ad96eb2 %}

### Define the product catalog data

Since we don't yet have a backend API to provide product information, I'll use mock data for our initial implementation. This allows me to focus on building the frontend components and their interactions without being blocked by the backend development.

To keep my code organized, I'll create a separate file to store this mock data. I'll name it `product-data.ts` and place it within the `product-list` folder. This makes sense because the mock data is directly related to this specific feature and isn't currently needed elsewhere in the application.

Here is what the file will contain: {% embed https://gist.github.com/cezar-plescan/c8892090328bb7b80449ab3ed62ca48a %}

Now that I have the product mock data, I can load it into the `ProductListComponent` to be displayed on the product listing page. Here's the relevant code: {% embed https://gist.github.com/cezar-plescan/3ec636de5e488dbbccac8f32a865b681 %}

### Displaying the products

Now that I've defined the data models and loaded the mock product data, I'll bring the product listing page to life.

#### Rendering the product list

First, I need to tell the main application component `AppComponent` to display the `ProductListComponent`. This is where the product list will actually be rendered. I can do this by updating the `app.component.html` file: {% embed https://gist.github.com/cezar-plescan/8854bbbf986859739d678aad76a5f1e4 %}

#### Displaying individual products

Next, I need to populate the `ProductCardComponent` template to display the details of each individual product. For now, I'll keep it simple to verify that the data is loading correctly. Note that I'm using the `currency` pipe to format the price in Euros. {% embed https://gist.github.com/cezar-plescan/7a072511d2d361e7d28333e8d9c5d9b6 %}

#### Using `mat-card` from Angular Material for styling

As mentioned in the acceptance criteria, I'll be using Angular Material to style the product cards. I'll install the package using the Angular CLI. This command will prompt you to choose a pre-built theme and set up additional configuration options:
```bash
ng add @angular/material
```

Then I'll update the `ProductCardComponent` template to use the `mat-card` component for styling: {% embed https://gist.github.com/cezar-plescan/9b55b5ad4d24583c52f626d98cfd79fa %}

_**Note**: You can find more details about using the `mat-card` component in the official Angular Material documentation: https://v18.material.angular.io/components/card/overview._

Next, I run the app (using `ng serve` or `npm run start`) and see a list of basic product cards displayed on the page. However, the layout isn't quite right and the image is taking up the full width of the container, no matter the window size.
![Basic product list](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pa7uq5thx12aft915j3n.png)
_**Note**: You can see the code at this stage in the Github repository at this specific revision: https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/eda71069e44b6615267f289627f94d01a3e50d2b._

I now need to refine the layout of the product list and style the product cards to make them visually appealing and responsive. I'll cover this in the next section.

## Preparing the layout and styling the components

When I approach the design and layout of a component, I find it helpful to abstract away the specific content initially and focus on the underlying structure. This can save time and effort later on. 

I think of it like an architect designing the blueprint of a house before deciding on the interior decorations. The blueprint establishes the foundation and framework, while the decorations can be added and modified later.

In my case, I'll consider the product list as a container with multiple items. This abstract view allows me to create a flexible and adaptable layout that works well across different screen sizes.

Here's the step-by-step approach I typically follow:
1. **Abstraction**: define the core structure of the component without focusing on specific content.
2. **Choose a Design Approach**: select a layout strategy based on the design goals and responsiveness requirements.
3. **Implementation**: translate the chosen design approach into code, using CSS to create the layout structure and style the elements.

This methodical process ensures that the resulting layout is robust, adaptable, and visually appealing, regardless of the specific content it will hold.

_**Important Note**: In this initial implementation, I'm solely focused on creating the product list functionality. The examples and code snippets provided here only showcase the `ProductListComponent` and its child `ProductCardComponent`. In a real-world e-commerce application, we would typically have a more comprehensive layout that includes a header, navigation menu, page title, footer, and other elements. These elements would be incorporated into the overall application structure using additional components and routing. However, for the sake of clarity and to emphasize the core principles of building a responsive product list, I'm keeping the current implementation focused and minimal._

### The Power of Abstraction in Layout Design

Here's why abstracting away content initially is a valuable approach:

**Focus on Core Structure**: By ignoring the specific content initially, we can focus on the fundamental layout principles – how the items should be arranged, the spacing between them, and how the layout should respond to different screen sizes. This allows us to create a solid foundation for our design.

**Flexibility and Reusability**: Abstracting the layout makes it easier to reuse the same structure for different types of content. For example, the same grid layout we create for product cards could be used for blog posts, team member profiles, or other types of content.

**Simplified Testing**: With a basic layout structure in place, we can test the responsiveness and adaptability of the design without being distracted by the details of the content. This allows us to catch any layout issues early on and iterate on the design more effectively.

**Collaboration**:  An abstract layout is easier to communicate and discuss with designers and stakeholders. They can focus on the overall structure and flow of the content without getting bogged down in the specifics of each element.

**Gradual Refinement**: Once the basic layout is established, we can then gradually add and style the specific content elements. This allows for a more iterative and focused approach to design and development.

In our e-commerce application, I can abstract the product list as a container (`product-list`) with multiple items (`product-card`). My focus will be on creating a responsive layout that adapts to different screen sizes and ensures proper spacing between items. Once this foundation is established, I'll design and style the individual product cards, filling them with the relevant content (images, names, prices, descriptions).

### Mobile-First Design: Why it matters

Next, I need to choose a design approach. Knowing that I'm aiming for a responsive design that works well on various screen sizes, from mobile to desktop, I'll adopt the **Mobile-First Design** approach, a strategy that prioritizes designing and building the mobile version of a website or application first, then progressively enhancing it for larger screens.

Here is why Mobile-First Design is often the preferred approach in modern web development:

**Progressive Enhancement**: With a mobile-first approach, we start with a simpler, more streamlined layout and then progressively add enhancements for larger screens. This is generally easier and more efficient than trying to strip down a complex desktop design to fit mobile constraints. It also allows for more flexibility and scalability as we can easily add new features or design elements for larger screens without compromising the mobile experience.

**Mobile Usage Dominance**: The majority of web traffic now originates from mobile devices. By starting with a mobile-first design, we prioritize the experience for the largest segment of our audience. This ensures that the majority of our users have a smooth and optimized experience right from the start.

**Content Prioritization**: Mobile screens have limited real estate, forcing us to focus on the most essential content and interactions. This naturally leads to a cleaner and more streamlined design that translates well to larger screens. Desktop-first designs can sometimes feel cluttered or overwhelming when adapted to mobile, as they might include elements that are less relevant or usable on smaller screens.

**Performance Optimization**: Mobile devices often have slower connections and less processing power than desktops. Designing for mobile first forces us to optimize performance from the beginning. This optimization benefits users on all devices, as even desktop users appreciate fast-loading pages.

**SEO Benefits**: Search engines like Google prioritize mobile-friendly websites in their rankings. Adopting a mobile-first approach can improve our website's SEO and increase its visibility in search results.

_**Note**: If you're interested in learning more about Mobile-First Design, check out these resources:_
- _https://www.youtube.com/watch?v=W1dGYykSR-4: A video tutorial explaining the concepts and benefits of mobile-first design._
- _https://www.manypixels.co/blog/web-design/mobile-first-design: An article discussing the importance of mobile-first design and providing practical tips for implementation._
- _https://www.sanity.io/glossary/mobile-first-design: A concise definition and overview of mobile-first design principles._

With this mobile-first mindset, my approach will be to initially design and style the product list for smaller screens (mobile devices). Then, as I move to larger viewports (tablets and desktops), I'll progressively enhance the layout to accommodate more columns and potentially additional content.

### Implementing the layout for small screens

With the mobile-first approach in mind, I'll start implementing the layout for smaller screens. Currently, the product cards are stacked vertically, which is the correct behavior for mobile. However, they are lacking spacing between them. 

#### Determining where to apply spacing

The question is: where should I add this spacing? Remembering the principle of separation of concerns, it makes sense to apply the spacing within the `ProductListComponent`, as this component controls the overall layout of the product list. The `ProductCardComponent` should focus on presenting the individual product details, not the spacing between cards.

#### Choosing a spacing strategy

Now, I need to decide how to add this spacing. There are a few options:
- **margins on product cards**:  This involves adding a margin to the `product-card` elements, creating space around each card.
- **padding on the container**: This involves adding padding to the `product-list` container, creating space between the cards and the container's edges.
- **flexbox `gap` property**: If I use Flexbox for the layout, I can leverage the `gap` property to easily control the spacing between items.

I'll examine each approach to determine the best fit for the mobile-first layout.

#### Apply margins around list items

In the product list template, I'll wrap each `ProductCardComponent` element within a `div.product-card`: {% embed https://gist.github.com/cezar-plescan/80b47bfadc416e12872f207ba21a858f %}

This provides a target for applying margins directly to the product card wrapper elements, as shown below: {% embed https://gist.github.com/cezar-plescan/25de93916b5fcec0eeea858a875f640c %}

After reloading the app, I see a nice, even spacing around each product card. The vertical spacing between adjacent cards is exactly `1rem`, as expected.

Here are some screenshots:
1. the first one is the initial display, without any spacing
2. the list after applying the margins
3. the last card has no bottom margin
![apply margins](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ig9h2kgem8ngfytthlpo.png)

However, there's one important issue: the last product card doesn't have a visible bottom margin!

#### Understanding margin collapsing

This behavior is due to a CSS concept called **margin collapsing**. In essence, when vertical margins (top and bottom) are applied to adjacent elements, they sometimes combine into a single margin rather than adding up. This happens when there's no padding, border, or inline content to separate the elements.

In this case, each `div.product-card` element has a bottom margin. However, the last product card's bottom margin collapses with the bottom margin of its parent container. This is why we don't see a visible margin below the last card.

_**Note**: More details about margin collapsing can be found at:_
- _https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing The MDN Web Docs article provides a comprehensive guide to understanding margin collapsing, its rules, and how to work with it effectively._
- _https://www.joshwcomeau.com/css/rules-of-margin-collapse/ This interactive blog post by Josh Comeau offers a visual and engaging explanation of margin collapsing, making it easier to grasp the concept._

#### Fixing the margin collapsing issue

One of the fixes is to apply a bottom padding to the `.product-list` container element and remove the bottom margin from the last `.product-card` child: {% embed https://gist.github.com/cezar-plescan/7742228fcb73ba831391561b4da9b156 %} {% embed https://gist.github.com/cezar-plescan/ef84e7b17f6a1baf5be9fb9d913bd5d5 %}

Now, with these changes applied, the spacing between all product cards appears as expected, with even margins all around.

You can check out the code with these changes in the GitHub repository at this revision: https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/24ed3e51e3c988c452060c2b1cdd412c4d3786c4.

#### Apply padding around list items

Another solution for creating spacing around the cards is to use paddings on their wrappers: {% embed https://gist.github.com/cezar-plescan/f574b8e0ff2b3e6bface0008f0be64a4 %}

![product card paddings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ud9hpecrqu03zxbqh20k.png)

With this simple change, each product card now has `1rem` of padding on all sides, visually separating the content from the card's edges. 

However, as you can see in the screenshot, this approach leads to a new issue: The vertical spacing between adjacent cards is double the intended space because the bottom padding of one card is added to the top padding of the next. 

To resolve this double spacing issue, I can selectively remove the bottom padding from all cards except for the last one in the list. I'll achieve this using the `:not(:last-child)` pseudo-selector: {% embed https://gist.github.com/cezar-plescan/e3146bee1b7e92b73833cb800b49a943 %}

You can see the code with these changes in the GitHub repository at this revision: https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/1d9d60a6319dffee2453eaf4072f708c0d8212a6.

#### The philosophy of spacing: Parent vs Child responsibilities

In the previous solutions, spacing was defined within the `.product-card` selector (the child elements). However, let's revisit the fundamental question: Who should be responsible for defining spacing in a parent-child layout relationship?

**Parent's Role: Outer Spacing**

Consider an empty parent container. To create inner spacing, we intuitively apply padding to the parent. This is because padding defines space _within_ an element, pushing its content inwards. While we could achieve a similar visual effect with margins _outside_ the parent, it's not semantically accurate because margins create space _around_ an element, not within it.

**Child's Role: Inner Spacing**

Now, let's add child elements to the parent. These children will naturally reside within the boundaries defined by the parent's padding.

The question then becomes: Who controls the spacing _between_ these child elements? The answer lies in the concept of margin. Margins create empty space _around_ elements, so it's the responsibility of each child element to define its own margin to create separation from its siblings.

In our product list, applying `margin-bottom` to each product card (except the last) achieves this goal. If we used padding instead, we'd imply that the space between cards is part of the card itself, which is not semantically correct. 

Based on this reasoning, a more effective implementation would be: {% embed https://gist.github.com/cezar-plescan/d196f8cc68ed8c3e7fd7b2b3ffd5c17c %}

The code with these changes can be found at this specific revision: https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/e31fe48758da28321b65125033c9f3cdea151e24.

#### Using Flexbox for spacing

Another elegant approach to defining spacing is to leverage the power of CSS **Flexbox**. This method aligns perfectly with the principle of separation of concerns, distinguishing between spacing around the list and the space between its items. Here's the implementation: {% embed https://gist.github.com/cezar-plescan/5aa12c9c206b3912218a28325288d076 %}

The `gap` property does the heavy lifting here, handling the spacing between the cards. In our current vertical layout, this translates to vertical spacing only. With this approach, we eliminate concerns about margin collapsing, simplifying our code and ensuring consistent spacing between cards.

The simplicity of this CSS is a major advantage. Flexbox is a powerful layout tool, and in this case, it allows us to achieve the desired layout with minimal code.

For now, I'll refrain from declaring a definitive "best" approach. Let's continue exploring how both margins and Flexbox adapt when we introduce styling for larger screens. This will give us a more comprehensive understanding of their strengths and weaknesses in different scenarios.

You can see the changes up to this point in the GitHub repository: https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/ca8ab55b9529049da501f7191062651900d98e2c.

### Implementing the layout for larger screens

As I transition to designing the layout for larger screens, a key question arises: What should determine the number of product cards per row? Should it be based on the device's screen size, or the available width of the product list container itself?

While screen size is a factor, my experience has taught me that relying solely on it can be restrictive. Different devices have varying widths, and the width of our container might change due to the overall page layout or user preferences. Therefore, I believe it's crucial to base the number of cards per row on the available container width. This approach ensures a more flexible and adaptable layout.

#### Why container width is key for Responsive Design

Here's why prioritizing container width leads to a more adaptable and user-friendly layout:
- **Content containers can change**: The width of the product list container might change depending on the overall page layout, user preferences, or other design considerations. Basing the layout on the container width ensures it adapts to these changes dynamically.
- **Maximizing space utilization**: By considering the container width, we can maximize the use of available space, ensuring that as many products as possible are displayed without overcrowding or creating an overly sparse layout.
- **Graceful degradation**: Users might resize their browser windows or use devices with non-standard screen sizes. Basing the layout on container width allows the design to gracefully adjust and avoid breaking or looking odd.

#### Choosing the right implementation

Let's revisit the acceptance criteria to guide the implementation:
> - _The product listing page displays products in a grid layout._
> - _Each row of the grid contains a maximum of 4 product items._
> - _The layout is responsive and adapts to different screen sizes._

Since I need to accommodate more cards per row on larger screens, CSS **Grid** is the perfect tool for the job. It allows me to create a flexible, responsive grid layout that adapts to different screen sizes and content while maintaining a visually pleasing arrangement.

To create a dynamic layout while adhering to these constraints, I'll set both minimum and maximum widths for the product cards. This ensures the cards have enough space to display their content comfortably but don't become overly large on wide screens.

In the next section, I'll delve into the CSS code that brings this responsive grid layout to life.

### Implementing the grid layout

To achieve the desired design, I'll gradually build the styling, based on each requirement.

#### 1. Apply the grid layout

The first step is to establish the `.product-list` as a CSS grid container:
```CSS
.product-list {
  display: grid;
}
```
At this point, the product cards will simply stack vertically, as a single column is the default behavior for a grid without explicit column definitions.

#### 2. Set the minimum card width

Next, I'll define the minimum card width. A value that was agreed upon during the discussion with the PO was `240px`. The CSS property for this scenario is `grid-template-columns`, which controls the number and size of columns in the grid.

What value can the property take? For this case when we have a variable number of columns, we can use the **`repeat()`** function with the **`auto-fit`** value for the track count. The `auto-fit` keyword tells the grid to create as many columns as possible within the available space, respecting any minimum or maximum constraints.

Then, I need to define the second argument of the `repeat()` function, which specifies the set of tracks that will be repeated. I need to have equal columns and define a minimum width for them - this will get translated into `minmax(240px, 1fr)`:
- `240px` is the minimum width each column can be. It ensures that even on smaller screens, where we might only have one or two columns, the product cards will still have enough space to display their content comfortably.
- `1fr` is a fractional unit, meaning that any remaining space in the grid container, after allocating the minimum width to each column, will be distributed equally among the columns. This is what creates the equal-width behavior I want.

Here is the CSS for this: {% embed https://gist.github.com/cezar-plescan/de4ff6878b3d8078bc5a40eaa40619cb %}

When reloading the app and adjusting the viewport size, I notice that the columns are dynamically created, have a minimum width, but there could be more than the expected 4 columns on larger screens.
![5 items per row](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/834qcgtxiu7p0xyxd5ye.png)

#### 3. Set a maximum of 4 cards per row

I need to address the issue of displaying more columns than expected on larger screens. For this I need to calculate the maximum width each column can be. For this I'll adjust the first argument of the `minmax()` function to include the maximum value of the column width.

Now I have to determine what this maximum width is, relative to its parent container. I could say that the max width is 100% (the container width) / 4 (maximum columns per row) = 25%, but this is wrong because I didn't consider the gaps between the columns. Here is how I can include them:
- Calculate the total width of the gaps: there are 3 gaps between 4 columns), and each gap is `1rem`, so the result is **`(4 - 1) * 1rem`**
- Subtract the total gap width from the container width: `100% - (4 - 1) * 1rem`
- Divide the remaining width by the number of columns: `(100% - (4 - 1) * 1rem) / 4`

This is the maximum value of a column: `calc((100% - (4 - 1) * 1rem) / 4)`

To make the code more flexible and maintainable, I'll use CSS variables instead of hardcoded values. Here is the updated CSS code: {% embed https://gist.github.com/cezar-plescan/9f93f7e44a73123110a358b98891c023 %}

#### 4. Define a maximum width for product cards

When transitioning from 2 columns to one column layout, the product card gets very wide and looks a bit odd. I want to limit its width to, let's say, 380px. This will also prevent the cards from stretching excessively on very wide screens.

But where exactly should I apply this constraint? On the `.product-card` wrapper itself, or on its content?

When I apply it directly on `.product-card` elements, the columns in the grid layout could overlap at different container sizes.

A proper solution for this case is to apply the max width to the actual content of the `.product-card` wrapper using the flexbox display: {% embed https://gist.github.com/cezar-plescan/7d16243b376f026a24c0692055a4861c %}

_**Note**: To get a deeper understanding of CSS Grid layout and its properties, you can refer to the following resources:_
- _https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout This MDN Web Docs article provides a comprehensive overview of the fundamental concepts of CSS Grid, including grid containers, items, tracks, and lines._
- _https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns This MDN Web Docs article focuses specifically on the `grid-template-columns` property, explaining how it defines the columns in a grid layout._
- _https://css-tricks.com/almanac/properties/g/grid-template-columns/ This CSS-Tricks almanac entry offers a detailed explanation of grid-template-columns, including different ways to specify column sizes and examples of how to use it._
- _https://gridbyexample.com/examples/ This website provides a collection of interactive examples demonstrating various CSS Grid techniques, including responsive layouts, grid areas, and more._

#### Checkout the code

The changes I've made so far can be found in the `product-list` branch of the Github repository: https://github.com/cezar-plescan/Angular-Architecture-Guide/tree/product-list.

### See it in action

Here are some screenshots of different screen sizes:

![Two column per row](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tegccdy4ynwjcln68i2i.png)

![Three columns per row](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s6l7d2ewf7hz9a4scgz5.png)

![Four columns per row](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/houiv0lhbjozm0tv25cn.png)

## Wrapping up

With the current implementation, I've successfully met the acceptance criteria for the MVP product listing page. I've established a responsive grid layout, limited the maximum card and content widths, and created a visually appealing design using Angular Material. The product list now dynamically adjusts to different screen sizes, ensuring a user-friendly experience across devices.

#### Key takeaways

In this article, I've laid the groundwork for the e-commerce product listing page. Here's a recap of the key points I've covered.

**Topics:**
- Discussed the importance of abstracting layout design and choosing the right approach (CSS Grid in our case) for responsive design.
- Explored the nuances of `grid-template-columns`, `minmax()`, and other CSS properties for creating a flexible and visually appealing grid.
- Deliberated on how to handle spacing, considering both the container and individual product cards.
- Implemented a mobile-first design with a maximum of 4 cards per row, addressing potential issues like margin collapsing.
- Set up Angular Material to enhance styling and provide pre-built components.

**Architectural and Technical Decisions:**
- Mobile-First Design: prioritized the mobile experience, starting with a single-column layout and planning to progressively enhance for larger screens.
- CSS Grid: chose CSS Grid over Flexbox for its more powerful grid layout capabilities.
- `grid-template-columns`: used `auto-fit` and `minmax` to create a responsive grid that adjusts the number of columns based on the available space.
- Spacing: defined padding on the container for outer spacing and margins on the card wrappers for inner spacing.
- Card styling: styled the product cards using Angular Material and added custom CSS to limit the maximum card and content width for a visually balanced layout.

#### Next steps

The journey of building a robust e-commerce application doesn't end here. While the core functionality is in place, there are several areas for improvement that I'll address in future iterations:
- Content Overflow: I need to handle cases where product names or descriptions might be too long and overflow the card's boundaries. I'll explore strategies like truncating text or adding "Read More" functionality.
- Image Optimization: Angular is currently warning us about potentially oversized images. I'll look into image optimization techniques to ensure fast loading times and a smooth user experience.
- Web Accessibility: To make the product list accessible to all users, I'll follow WCAG (Web Content Accessibility Guidelines) and implement features like alternative text for images and keyboard navigation.

In the next article of this series, I'll tackle these enhancements and dive deeper into the technical details of styling and refining the product list.

______

Stay tuned for more insights and practical tips as I continue to build the e-commerce application step by step!