How to make flex component with 1 element bottom aligned

Hello,

For AntennaPod (the open source podcast app for Android) I’m trying to implement Material Design 3 primary tabs as a component.

I’m struggling to get a (flex) layout, however, which:

  • takes the width of the label + a padding (which is not defined in the spec but we can infer)
  • gets a set height (specs say: 48dp) and place the label vertically centred
  • places the ‘active indicator’ at the bottom, taking the width of the label (but with a padding of 2dp vis-a-vis the label width)

I do have the ‘normal’ tab look nice:

However, I don’t see how I can make the ‘active indicator’ ‘float’ on top of the tab container but bottom aligned. With a flex layout it would push the active indicator to below the label, which would then go beyond the maximum height.

Any suggestions?

1 Like

I managed to do it like this - but if anyone has better suggestions please let me know :slight_smile:

This is the element structure:

  • tab board:
    flex layout: fixed height (48), fit content width, column layout, justify content method doesn’t really matter since flex elements take 100% of the height
    • tab container (not sure I need this actually, but didn’t manage to make it work without):
      flex element: 100% height → this is the part that makes the indicator be pushed to the bottom of the tab; fit content width
      flex layout
      • tab label:
        flex element: fixed width and height, with the text field being ‘Auto width’, horizontal margin set to 16px via md.tab.margin.horizontal token
    • active indicator:
      flex element:
      no height option selected but I suppose it’s ‘fixed’ - in any case it’s set to 3px via md.sys.state.focus-indicator.thickness token;
      width set to 100%;
      horizontal margin set to 18px via a custom token with the calculation {md.tab.margin.horizontal}+2;
      margin-top set to -3px via a custom token with the calculation {md.sys.state.focus-indicator.thickness}*-1 → this is the part that makes the indicator not drop outside the tab container

Of course this can be done also without tokens, but then if you want to change the horizontal margin of the tab label you’d also have to adjust each time the horizontal margin of the active indicator.

2 Likes

I think this is a smart solution! Another option might be to put the Active indicator inside the Tab container with the Label. (With the flex layout of the Tab container being set from top to bottom.) Then use the md.tab.margin.horizontal token on the Tab container padding, rather than on the margins for the Label and the Active Indicator. Using padding on the container, rather than margins on the inline elements, means you wouldn’t need to update both if you changed the value. (Even though tokens is a nice solution for that!)

1 Like

Hi @LauraKalbag

Thanks for chipping in with that suggestion!

Applied that - works nicely. It allowed me to get rid of the calculated token {md.tab.margin.horizontal}+2 and just have a direct margin directly.

Then figured (how) I can get rid of the tab container and apply the top-to-bottom flex layout to the board directly. So it’s even cleaner now.

However, I no longer manage to set ‘Auto width’ on the label. When clicking, it resizes and then immediately jumps back to ‘Fixed’. This means that if I make the label text longer, it jumps over two lines. And if I remove label text the tab stays too wide.

I’m not sure when ‘Auto width’ stopped working, but I suspect that it’s when I removed the intermediary ‘Tab container’. Is it a bug? (If so, happy to report it on GitHub.) Or is ‘Auto width’ not expected to work, given that the text element is a flex layout element that has ‘fixed width’?

PS. Love your video tutorials! (And the work of the other half of the Small Tech foundation - only just realised :smiley: )

2 Likes

I think this might be a place where Penpot’s layout behaviour can be confusing. I think the auto width isn’t working because the Active tab container is set to a fixed width, and so the text element can’t be independently set to auto width. You may not have set this fixed width option on the container, it can happen when you set a flex item to full width. As a flex item can’t be full width while its container is set to fit content (neither is dictating what the widths should be) then Penpot will adjust the container’s settings to better suit the latest setting you’ve applied.

TL;DR if you switch the Active tab container to fit content width, you should be able to set the text layer to auto width :sweat_smile:

Let me know if that doesn’t work as it may be a bug and I couldn’t see the settings on Active tab in your video!

Thank you! I was sitting alongside Aral when I read your post and he really appreciated it :smiling_face:

2 Likes

Allright, cool. Then I’m understanding it correctly :sweat_smile: And there’s a bug :cry:

Here’s a video where the settings of the container has the right setting:

2 Likes

Hi!

Looking at the MD guidelines, I think padding the tabs isn’t correct because their width isn’t determined by their content but by their distribution within the tab group.
I also don’t think adding very long text is a good practice in this type of component.

I played around a bit and did this. I also added badges. The active indicator adapts well to the text length, and I set a minimum size of 24. I didn’t use libraries or tokens, but I imagine they’re easily implemented.
I hope it’s useful and that there aren’t any major mistakes!

tabs.penpot (238.1 KB)

1 Like

Allright, bug report created: bug: Cannot apply '' to text flex element in flex container with 'fit content width' · Issue #7227 · penpot/penpot · GitHub

The rule you’re describing applies indeed to fixed tabs.

But we have more tabs than fit in the container, so we need scrollable tabs, and that tab width rule doesn’t apply here. Material design specs (sic - spelling error isn’t mine):

Padding should remain the same when using scrolllable tabs and long labels

However, what that padding should be is not defined. Hence I picked a margin/padding of 16px/dp.

I can’t check right now, but I will later on. (And yes, tokens are quite easily added after the fact.) Thanks for sharing!

1 Like

looking here, it seems you’re right!!

1 Like