1

Is it possible, without adjusting the markup and using either CSS Grid or Flexbox, to achieve the following layout?

+-------------------------+
||           ||          ||
|+-+         +-----------+|
|                         |
|                         |
|                         |
+-------------------------+

There are two items in the row, the first aligned left, the second aligned to the center of the page and filling the remaining width to the right. I have tried various ways, such as an empty 3rd grid item with no width etc, but with no luck. This was my last attempt and shows the issue well.

header {
  display: grid;
  grid-template-columns: auto auto;
  justify-items: stretch;
}

ul {
  display: flex;
}

li:last-child {
  margin-left: auto;
}


/* for demo only */

ul {
  padding: 0;
  margin: 0;
  list-style: none;
}

li {
  padding: 5px;
  background: #aaa;
}

p {
  text-align: center;
}
<header>
  <span>Title</span>
  <nav>
    <ul>
      <li>Link 1</li>
      <li>Link 2</li>
      <li>Link 3</li>
    </ul>
  </nav>
</header>
<p>| true center |</p>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Dan
  • 1,536
  • 4
  • 17
  • 31
  • 1
    `the second aligned to the center of the page and filling the remaining width, with a right aligned child` --> sorry but this has no meaing... align to center and fill remaining width ? – Temani Afif Mar 24 '18 at 14:26
  • @TemaniAfif Yes, I have updated the description to make it clearer. – Dan Mar 24 '18 at 15:16
  • well it's still confusing :) `the second aligned to the center of the page and filling the remaining width to the right` --> how to align something to the center and at the same time fill the width ? it's not logic – Temani Afif Mar 24 '18 at 15:27
  • @TemaniAfif Well, that is the question. If you run the snippet the example should be self evident. – Dan Mar 24 '18 at 15:33
  • 1
    Not possible with the Grid/Flexbox alone, possible with the positioning or JS. – VXp Mar 24 '18 at 16:04

4 Answers4

2

This layout would be a perfect use case scenario for the CSS Grid subgrid feature, which allows descendants of a grid container beyond the children to recognize top-level grid container lines.

Unfortunately, none of the major browsers support this feature yet. Technical development has been deferred and is currently under consideration for CSS Grid Layout Module Level 2.

More here: Positioning content of grid items in primary container (subgrid feature)

Without subgrid, the layout is still possible with Flex and Grid, but it can become somewhat complex and may break in certain scenarios. For example, with Flex/Grid, the two outer items can be pinned to the edges, no problem. But the two inner items may be centered, near-center or off-center depending on screen size and other factors.

If you're okay with a bit of off-centering in certain cases, then Flex/Grid may work for you. In fact, you may be able to resolve the issue with media queries, negative margins, the translate() function of the transform property, or a combination thereof. Again, it gets complicated.

Using only CSS, the simplest solution uses absolute positioning.

(No changes to the HTML.)

header, nav {
  position: relative;
}

ul {
  display: flex;
  justify-content: center;
}

li:last-child {
  position: absolute;
  right: 0;
}

span {
  position: absolute;
  left: 0;
}


/* for demo only */
ul {
  padding: 0;
  margin: 0;
  list-style: none;
}
li {
  padding: 5px;
  background: #aaa;
}
p {
  text-align: center;
}
<header>
  <span>Title</span>
  <nav>
    <ul>
      <li>Link 1</li>
      <li>Link 2</li>
      <li>Link 3</li>
    </ul>
  </nav>
</header>
<p>| true center |</p>

As an aside, since you're using the semantically valuable nav element, there's really no need to use an unordered list. Here's an even simpler version:

header, nav {
  position: relative;
}

nav {
  display: flex;
  justify-content: center;
}

nav > div:last-child {
  position: absolute;
  right: 0;
}

header > div {
  position: absolute;
  left: 0;
}


/* for demo only */
nav > div {
  padding: 5px;
  background: #aaa;
}
p {
  text-align: center;
}
<header>
  <div>Title</div>
  <nav>
      <a>Link 1</a>
      <a>Link 2</a>
      <a>Link 3</a>
  </nav>
</header>
<p>| true center |</p>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 1
    Thanks for mentioning `subgrid`, after reading into it that seems the correct way to achieve this in the future. – Dan Mar 28 '18 at 09:28
0

I added a class .spacer on the second li in order to give it a flex-grow and let it take all the empty space automatically and therefore pushes the last element to the right.

also changed the header styling to grid-template-columns: 1fr 1fr; such that both elements take half the width.

header {
  display: grid;
  grid-template-columns: 0.71fr 1fr;
  justify-items: stretch;
}

ul { display: flex; }
li.spacer{flex-grow: 1;} /* or li:nth-child(2) if you cannot add a class to the html tags */

/* for demo only */
ul { padding: 0; margin: 0; list-style: none; }
li a{ padding: 5px; background: #aaa; }
p  { text-align: center; }
<header>
 <span>Title</span>
 <nav>
  <ul>
   <li><a href="#">Link 1</a></li>
   <li class="spacer"><a href="#">Link 2</a></li>
   <li><a href="#">Link 3</a></li>
  </ul>
 </nav>
</header>
<p>| true center |</p>
Omar Tanti
  • 1,368
  • 1
  • 14
  • 29
  • Unfortunately not. The links need to be left aligned apart from the last child which is right aligned. – Dan Mar 24 '18 at 15:15
  • I updated the code. I added css for `li.spacer` - in order to give it a flex-grow in order to take all the empty space automatically and therefore pushes the last element to the right. – Omar Tanti Mar 24 '18 at 15:43
  • This is no different to my initial example. The links aren't centred to the page, which is the initial problem. – Dan Mar 24 '18 at 16:59
  • Its better you give us an image on how you would like the links to be rendered and add it to your answer – Omar Tanti Mar 24 '18 at 17:01
  • Link 1 and Link 2 should be aligned with the centre of the page, which is represented by the "true centre" paragraph tag in the snippet. – Dan Mar 24 '18 at 17:03
  • So your end result should be something to the current version? I don't think that this would be the final solution since it will depend on the text length in link 1 and link 2 and also the screen size. You would have to update the `grid-template-columns: 0.71fr 1fr;` according to your text in links 1 and 2 – Omar Tanti Mar 25 '18 at 06:06
0

Here is a hacky idea:

header {
  display: flex;
}
header span {
  min-width:0;
  width:0;
  flex-basis:0%;
  white-space:nowrap;
}
nav {
   margin-left: auto;
   flex-basis:100%;
}
ul {
  display: flex;
}

li:first-child {
  margin-left: auto;
}
li:nth-child(2) {
  margin-right:auto;
}

li:last-child {
  position:absolute;
  right:0;
}

/* for demo only */

ul {
  padding: 0;
  margin: 0;
  list-style: none;
  position:relative;
}

li {
  padding: 5px;
  background: #aaa;
}

p {
  text-align: center;
}
<header>
  <span>Title Title</span>
  <nav>
    <ul>
      <li>Link 1</li>
      <li>Link 2</li>
      <li>Link 3</li>
    </ul>
  </nav>
</header>
<p>| true center |</p>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • This is no different to my initial example. The links aren't centred to the page, which is the initial problem. – Dan Mar 24 '18 at 17:00
  • @DanChristian i told it's confusin :) ... so in yout question you need to say that you want to center sub item withing the container of their parent element – Temani Afif Mar 24 '18 at 18:49
  • @DanChristian i updated my code, you can check again – Temani Afif Mar 24 '18 at 21:05
0

I have found this solution the easiest and most clear.

header {
  display: grid;
  grid-template-columns: 1fr auto 1fr; /* magic here */
  align-items: center; /* optional */
}

ul { display: flex; }

span { justify-self: start; } /* magic here */
.right-link { justify-self: end; } /* magic here */

/* for demo only */
ul { padding: 0; margin: 0; list-style: none; }
a{ padding: 5px; background: #aaa; }
p  { text-align: center; }
<header>
    <span>Title</span>
    <nav>
        <ul>
            <li><a href="#">Link 1</a></li>
            <li><a href="#">Link 2</a></li>
        </ul>
    </nav>
  <a href="#" class="right-link">Link 3</a>
</header>
<p>| true center |</p>