Search code examples
cssflexboxcss-grid

How to align multiline headings to heading baseline in CSS


I am having a problem trying to align the baselines of headings inside of a mega menu.

/* Presentational bits */

.add-divider {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
  padding-bottom: 0.65em;
  -webkit-box-align: baseline;
  -ms-flex-align: baseline;
  align-items: baseline;
}

.standard-list {
  list-style-type: none;
  padding-left: 0;
  list-style: none;
}


/* Core Styling */

.mega-menu-wrapper {
  list-style-type: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(23%, 1fr));
  grid-gap: 10px;
  align-items: baseline;
}
<nav>
  <ul class="mega-menu-wrapper">
    <li class="mega-menu-item">
      <h5 class="add-divider">Typical Section Header</h5>
      <ul class="standard-list">
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
      </ul>
    </li>
    <li>
      <h5 class="add-divider">Atypical Section Header with much longer content in it that breaks to another line</h5>
      <ul class="standard-list">
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
      </ul>
    </li>
    <li>
      <h5 class="add-divider">How to accomodate different sized sections Headers?</h5>
      <ul class="standard-list">
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
      </ul>
    </li>
    <li><h5 class="add-divider">The section dividers should line up</h5>
      <ul class="standard-list">
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
      </ul></li>
    <li>
      <h5 class="add-divider">No matter the content of the headers</h5>
      <ul class="standard-list">
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
        <li><a href="#">Link to nowhere</a></li>
      </ul>
    </li>
  </ul>
</nav>

As seen, I have a navigation, inside that navigation is an unordered list, and inside each list item is a heading and it's own unordered list of navigation items. Each of the headings have their own bottom border, for aesthetic purposes, and to divide the header from the links.

The problem is that I am unable to align these bottom borders, which are styles on the headings. I thought it would be a pretty simple task with CSS Grid, but perhaps I'm using the wrong thing to style this menu, or I am ignorant of some CSS Grid features.

All in all I just want to align the base lines of these headings, even if they are breaking to multiple lines.

I have looked at the following resources, and none of them line up with my use-case, but I can't be the only one having this problem, or it's easier to solve than I thought and I'm overlooking something. Either way, here's what I've tried to no avail:

Any help is much appreciated.

CodePen Example

Update

I'd like to be able to support a variable number of links, as this is a mega-menu, I don't know how many links the user will add into it.

I realize this may not be able to be done with the way my HTML is structured now. I am open to exploring new ways of structuring the HTML as long as I can have 4 columns of menu items before the 5th breaks to a new line.


Solution

  • Here you are

    CSS updated

    /* Presentational bits */
    .add-divider {
      margin-bottom: 7px;
      margin-top: auto; 
    }
    
    .standard-list {
      border-top: 1px solid rgba(0, 0, 0, 0.1);
      padding-top: 5px;
      padding-left: 0;
    }
    
    /* Core Styling */
    .mega-menu-wrapper {
      display: flex;
      width: 600px;
      flex-wrap: wrap;
      align-items: stretch;
    }
    
    .mega-menu-wrapper li {
      width: 180px;
      flex-grow: 1;
      margin: 2px 10px;
      display:flex;
      flex-direction: column;
      justify-content: space-between;
    }
    
    .mega-menu-wrapper li ul{
      margin-bottom: auto;
    }
    

    Answer to comment #2

    I'm afraid it is not 100% possible to have this layout in this "edge" case. But here are things you can do:

    • Use JS

      • target longest string
      • get it's height
      • set this height to other h tags
    • Still css but can be ugly

      • to any long h add class="long" and add provided css .mega-menu-wrapper li.long {flex-basis: 100%;}
    • Kinda "hack" - get count of longest list items and add empty ones (probably good if you use frontend framework - if you use one) Angular example:

    list.component.html

    ...
      <li class="mega-menu-item">
          <h5 class="add-divider">Typical Section Header</h5>
          <ul class="standard-list">
            <li *ngFor="let i of arrayOfNumbers0toMax">
                <a href="{{linksAtN[i].link}}">{{linksAtN[i].linkName}}</a>
            </li>
          </ul>
        </li>
    ...
    

    I also updated CSS in this answer and added

    .mega-menu-wrapper li ul{
      margin-bottom: auto;
    }
    

    And removed display: flex of .add-divider

    Also I'm appending link to CSS fixed example.