Dan
Dan

Reputation: 1596

Center-align an item in a row of two items with CSS Grid or Flexbox

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>

Upvotes: 1

Views: 1327

Answers (4)

Vukadin Nedeljkovic
Vukadin Nedeljkovic

Reputation: 1

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>

Upvotes: 0

Michael Benjamin
Michael Benjamin

Reputation: 371251

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>

Upvotes: 2

Omar Tanti
Omar Tanti

Reputation: 1448

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>

Upvotes: 0

Temani Afif
Temani Afif

Reputation: 272947

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>

Upvotes: 0

Related Questions