cssflexboxvertical-alignmentbaseline

Vertically align text of same-height blocks along a shared baseline (supporting mixed font size)


I have a single-line/row navigation/menu where some items have bigger font size but I want all items' text to be aligned with harmony, along the base line.

I also need the items to react to hover on the full height of the menu so user don't have to aim the smaller text (this is for sub-menu but not part of this question).

I tried with Flexbox but I can't mix align-items: stretch (full height) and align-items: baseline (text alignment)

Note: Text of menu items are in menu-item-label wrappers because I will add more stuff to the item (dropdown arrows, picto...) but it's the text alignment that counts.

body {
  font-size: 24px; /* Big value to ease highlighting of mis-alignemnt */
}

.some-existing-container {
  /* This container has some dimensions: I don't think it would cause conflict. */
  width: 100%;
  height: 4em;

  display: flex;
  align-items: center; /* Not mandatory */

  border: 1px solid blue; /* Highlighting */
}

.menu {
  display: flex;
  align-items: baseline;

  border: 1px solid green; /* Highlighting */
}

.menu-item {
  border: 1px dotted brown; /* Highlighting */
}

.menu-item:hover {
  background-color: grey; /* Highlighting */
}

.menu-item-label {
  border: 1px dotted pink; /* Highlighting */
}

.other {
  margin-left: auto; /* Places this block to the right in .some-existing-container */

  border: 1px solid purple; /* Highlighting */
}

.bigger {
  font-size: 4rem;
}

.stretched {
  align-items: stretch;
}
<p>Variant 1: Texts are aligned but items does not occupies the whole height:</p>
<div class="main">
  <div class="some-existing-container">
    <div class="menu">
      <div class="menu-item">
        <div class="menu-item-label">
          Home
        </div>
      </div>
      <div class="bigger menu-item">
        <div class="menu-item-label">
          Item1
        </div>
      </div>
      <div class="menu-item">
        <div class="menu-item-label">
          Item2
        </div>
      </div>
      <div class="menu-item">
        <div class="menu-item-label">
          Item3
        </div>
      </div>
    </div>
    <div class="other">
      Other
    </div>
  </div>
</div>
<p>Variant 2: Items of same-and-full height but texts are not aligned:</p>
<div class="main">
  <div class="some-existing-container">
    <div class="menu stretched">
      <div class="menu-item">
        <div class="menu-item-label">
          Home
        </div>
      </div>
      <div class="bigger menu-item">
        <div class="menu-item-label">
          Item1
        </div>
      </div>
      <div class="menu-item">
        <div class="menu-item-label">
          Item2
        </div>
      </div>
      <div class="menu-item">
        <div class="menu-item-label">
          Item3
        </div>
      </div>
    </div>
    <div class="other">
      Other
    </div>
  </div>
</div>

I would like to mix the two as the montage below:

Example of desired render

Is it even possible (with or without JavaScript)?

I have also tried with Grid layout without success (see this JSFiddle).


Solution

  • I would keep the baseline alignment and consider a pseudo elemen trick to increase the hoverable area:

    body {
      font-size: 24px; /* Big value to ease highlighting of mis-alignemnt */
    }
    
    .some-existing-container {
      height: 4em;
      display: flex;
      align-items: center; 
      border: 1px solid blue; 
    }
    
    .menu {
      display: flex;
      align-items: baseline;
      border: 1px solid green; 
      overflow:hidden;
    }
    
    .menu-item {
      border: 1px dotted brown; 
      position:relative;
      z-index:0;
    }
    .menu-item::before {
      content:"";
      position:absolute;
      z-index:-1;
      top:-200px;
      bottom:-200px;
      left:0;
      right:0;
    }
    
    .menu-item:hover,
    .menu-item:hover::before{
      background-color: grey;
    }
    
    .menu-item-label {
      border: 1px dotted pink; 
    }
    
    .other {
      margin-left: auto; 
      border: 1px solid purple; 
    }
    
    .bigger {
      font-size: 4rem;
    }
    <div class="main">
      <div class="some-existing-container">
        <div class="menu">
          <div class="menu-item">
            <div class="menu-item-label">
              Home
            </div>
          </div>
          <div class="bigger menu-item">
            <div class="menu-item-label">
              Item1
            </div>
          </div>
          <div class="menu-item">
            <div class="menu-item-label">
              Item2
            </div>
          </div>
          <div class="menu-item">
            <div class="menu-item-label">
              Item3
            </div>
          </div>
        </div>
        <div class="other">
          Other
        </div>
      </div>
    </div>