DEV Community

Tomasz Kula
Tomasz Kula

Posted on

The Most Misunderstood Part of Styling Components πŸš€

The problem

Imagine you are tasked with rendering a list of articles. Seems easy enough, you've done this a lot. You quickly come up with this solution:

<div class="articles">
  <div *ngFor="let article of articles">
     {{ article.tittle }}
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode
.articles {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
Enter fullscreen mode Exit fullscreen mode

All is well in the world, the list is displayed correctly, and you move on with your life πŸ’ͺ

The Next Day

Next day, there is a new requirement. On a different page of the application you have to render the same articles, but now in a grid layout 🧱

We want to reuse the existing component, because the only difference is the layout of the list.

You might think of the following solutions:

  • create @Input which will drive the behaviour of dynamically styling the component to render either a list or a grid layout
  • create a <articles-grid> and <articles-list> components which will reuse the article card.

Both of this solutions will work, but they are needlessly complicated 😱
We either have to expand the component API to accommodate different layouts or create layout components that differ by just a couple lines of CSS.

The solution

What about a different approach? If we get rid of the wrapper .articles div and style the :host element directly, we will be able to override this layout styling as needed in the parent component.

Let's see it in action.

First, we remove the not needed wrapper div:

<div *ngFor="let article of articles" class="article">
   {{ article.tittle }}
</div>
Enter fullscreen mode Exit fullscreen mode

Next, we update the CSS to target the :host element of the component:

:host {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
Enter fullscreen mode Exit fullscreen mode

And that's it! With this simple change, we open up lost of possibilities for different layouts, which we can achieve with just CSS.

That's because the :host selector is the only part of the component which can be styled in the parent component.

Now this will continue to render the default flex layout:

<app-articles></app-articles>
Enter fullscreen mode Exit fullscreen mode

But this will override the flex layout with grid:

<app-articles class="grid"></app-articles>
Enter fullscreen mode Exit fullscreen mode
.grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}
Enter fullscreen mode Exit fullscreen mode

In the future if there is a new requirement to render the article list in a different layout, it's as easy as creating a new CSS class and styling it.

<app-articles class="two-column-grid"></app-articles>
<app-articles class="five-column-grid"></app-articles>
<app-articles class="responsive-grid"></app-articles>
...etc
Enter fullscreen mode Exit fullscreen mode

The recommendation

❌ Avoid using top level container elements just to style them
βœ… Style :host component when possible to make the component more reusable

Hope you're having a great one, and I'll see you for more web dev posts in the future πŸ₯³

In Case You Missed it

Top comments (0)