Combining left-aligned and centered elements within a grid using flexbox

Looking to create a fluid responsive grid layout using flexbox, without the need for media queries. The number of elements in the grid can vary and each item should have a fixed, equal width with left alignment. The entire group should have equal left and right margins.

Here's an example of what I'm aiming for:

https://i.sstatic.net/z99HV.png

This is how I attempted to achieve it:

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  margin: auto;
}
.item {
  height: 200px;
  width: 200px;
  background-color: purple;
  padding: 10px;
  margin: 10px;
}
<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">Flex item 4</div>
  <div class="item">Flex item 5</div>
</div>

Unfortunately, this approach did not yield the desired results:

https://i.sstatic.net/XLJdk.png

I had hoped that by setting margin: auto on the container, it would size itself to accommodate an optimal number of items per row.

While solutions like Bootstrap or Foundation would make this task easier, I am curious if it can be achieved using flexbox alone.

Answer №1

CSS Grid

Implementing CSS Grid is a stylish and adaptable solution for layout design.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, 200px);
  grid-auto-rows: 200px;
  grid-gap: 10px;
  justify-content: center;
}

.item {
  background-color: purple;
  padding: 10px;
}
<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">Flex item 4</div>
  <div class="item">Flex item 5</div>
</div>

However, it may lack support in older browsers like IE, requiring the use of Javascript to achieve similar functionality due to flexbox issues with width distribution.

Javascript Approach

The Javascript method calculates the necessary margin-left value to centrally align flexbox items, ensuring compatibility despite initial markup settings.

// get width of element with margins
function getOuterWidth(el) {
  var styles = window.getComputedStyle(el);
  var margins = parseFloat(styles["marginLeft"]) +
    parseFloat(styles["marginRight"]);

  return Math.ceil(el.getBoundingClientRect().width + margins);
}

// get width of element without paddings
function getContentWidth(el) {
  var styles = window.getComputedStyle(el);
  var paddings = parseFloat(styles["paddingLeft"]) +
    parseFloat(styles["paddingRight"]);

  return Math.ceil(el.getBoundingClientRect().width - paddings);
}

// Get top of element
function getTopOfElement(el) {
  return el.getBoundingClientRect().top;
}

var container = document.querySelector(".container");
var initialMarginLeft = parseFloat(window.getComputedStyle(container)["marginLeft"]);
// getting array of items
var items = Array.prototype.slice.call(document.querySelectorAll(".item"));

function centerItems() {
  if (items.length === 0) return 0;

  // set margin-left to initial value to recalculate it
  container.style.marginLeft = initialMarginLeft + "px";

  var topOfFirstItem = getTopOfElement(items[0]);
  var spaceTakenByElementsOnFirstLine = getOuterWidth(items[0]);

  for (var i = 1; i < items.length; i++) {
    // Break in case we are in second line
    if (getTopOfElement(items[i]) > topOfFirstItem)
      break;
    spaceTakenByElementsOnFirstLine += getOuterWidth(items[i]);
  }

  // Set margin-left to center elements
  var marginLeft = initialMarginLeft + (getContentWidth(container) - spaceTakenByElementsOnFirstLine) / 2;

  container.style.marginLeft = marginLeft + "px";
};

window.addEventListener("resize", centerItems);

centerItems();
.container {
  display: flex;
  flex-wrap: wrap;
}

.item {
  height: 200px;
  width: 200px;
  background-color: purple;
  padding: 10px;
  margin: 10px;
}
<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">Flex item 4</div>
  <div class="item">Flex item 5</div>
</div>

jQuery Implementation

// get width of element with margins
function getOuterWidth(el) {
  return $(el).outerWidth(true);
}

// get width of element without paddings
function getContentWidth(el) {
  return parseFloat($(el).css("width"));
}

function getTopOfElement(el) {
  return $(el).position().top;
}

var $container = $(".container");
var $items = $(".item");
var initialMarginLeft = parseFloat($container.css("margin-left"));

function centerItems() {
  if ($items.length === 0) return 0;

  // set margin-left to initial value to recalculate it
  $container.css("margin-left", initialMarginLeft + "px");

  var topOfFirstItem = getTopOfElement($items[0]);
  var spaceTakenByElementsOnFirstLine = getOuterWidth($items[0]);

  for (var i = 1; i < $items.length; i++) {
    // Break in case we are in second line
    if (getTopOfElement($items[i]) > topOfFirstItem)
      break;
    spaceTakenByElementsOnFirstLine += getOuterWidth($items[i]);
  }

  // Set margin left to center elements
  var marginLeft = initialMarginLeft + (getContentWidth($container) - spaceTakenByElementsOnFirstLine) / 2;

  $container.css("margin-left", marginLeft + "px");
};

$(window).resize(centerItems);

centerItems();
.container {
  display: flex;
  flex-wrap: wrap;
}

.item {
  height: 200px;
  width: 200px;
  background-color: purple;
  padding: 10px;
  margin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="container">
  <div class="item">Flex item 1</div>
  <div class="item">Flex item 2</div>
  <div class="item">Flex item 3</div>
  <div class="item">...etc...</div>
  <div class="item">Flex item 5</div>
</div>

Answer №2

If you're looking for a simple solution, one approach is to include a series of invisible, zero-height items at the end of your regular items:

<div class="parent">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="dummyItem"></div>
  <div class="dummyItem"></div>
  <div class="dummyItem"></div>
  <div class="dummyItem"></div>
</div>

Additionally,

.parent {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
}

.parent .item,
.parent .dummyItem{
  width: 50px;
  height: 50px;
  background: white;
  margin: 5px;
}

.parent .dummyItem {
  height: 0;
}

If the longest row in your layout can accommodate n visible items, you will need at least n-1 dummy items for this method to be effective.

The only drawback to this technique is that if there is only one row of items, they may not be perfectly centered but rather aligned mostly to the left.

View Codepen Example.

Answer №3

Unable to achieve desired layout using flexbox right away (I couldn't figure it out myself). One option is to use justify-content: center; but this will center all children resulting in a layout like:

https://i.sstatic.net/fD8BD.png

The workaround I found is to introduce another parent element and wrap everything inside it.

Check out this CodePen example: http://codepen.io/justd/pen/rOeMGZ

You'll likely discover a solution that works for you by experimenting with various CSS techniques.

Answer №4

Although I tried using flexbox, I couldn't achieve this specific behavior. However, it can easily be done with CSS Grid. Simply add justify-content: center to the container.

Check out this example on CodePen

Answer №5

To achieve the desired layout, set a specific width for the container and then apply margin: 0 auto to the containing element. This will center it horizontally on the page instead of spanning the full width.

For a visual example, you can check out this Codepen link: http://codepen.io/janedoe/pen/RzKlja

Answer №6

If you're struggling with getting your grid container to work properly, try adding a max-width to it. This will allow the auto margins to function correctly. Currently, your container is expanding 100% to both sides, leaving no room for the auto margin to take effect.

To ensure that your container expands to a maximum of 3 items per row, set the max-width to 660 (item width + item margin):

.container {
  max-width: 660px;
  margin: 0 auto;
  display: flex;
  flex-flow: row-wrap;
}
.item {
  width: 200px;
  height: 200px;
  margin: 10px;
}

Check out this Codepen example for reference: http://codepen.io/kgrote/pen/qbpGWZ

With this setup, the container will fluidly expand until reaching its maximum width, then center itself while maintaining a left-aligned grid layout for its children. As the container shrinks, the children will stack accordingly.

Answer №7

It's essential to structure your page with default margins on the left and right using percentages instead of pixel widths. This approach ensures that your page will be responsive and display well on all devices without needing Javascript. If you desire, you can also set a maximum width for added control over the layout. By making these adjustments in the CSS code provided below, you should achieve the desired result effortlessly.

.pageLayout {
    margin: 0 auto;
    width: 80%;
    /* add optional max width  */
}

.container {
  display: flex;
  flex-wrap: wrap;
}
.item {
  height: 200px;
  width: 200px;
  background-color: purple;
  padding: 10px;
  margin: 10px;
}
<div class="pageLayout">
  <div class="container>
    <div class="item">Flex item 1</div>
    <div class="item">Flex item 2</div>
    <div class="item">Flex item 3</div>
    <div class="item">Flex item 4</div>
    <div class="item">Flex item 5</div>
  </div>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The issue arises when trying to implement CSS keyframes with a CSS image slideshow

I'm currently troubleshooting a CSS slideshow that isn't functioning properly. The issue lies within the keyframes. I'm attempting to make the images come down from the top mask and then have the previous image go back up into the mask. This ...

What is the best way to "leap" a sole transform value within an animation?

I need to create an animation that makes a transform function value "jump" in an animation. Within my @keyframes rule are three percentages with the transform property in each one. I specifically want only the scale to transition from 0.5 to 1 between the ...

Require this form to be centered flawlessly

<form class="form-horizontal" role="form" id="contactf" action="http://titan.csit.rmit.edu.au/~e54061/wp/form-tester.php" method="post"> <div class="form-group"> <label class="control-label col-sm-2 lb" for="email">Email : </ ...

Arranging my HTML text fields for optimal organization

I am struggling to format my text fields the way I envision. Here's what I want: - Username field at the top - First and last name underneath each other - Password and confirm password next to each other below names As a newcomer to HTML, I'm fi ...

Ways to cap the height of elements at predetermined values ("stepped") depending on the content within

After posting a question on this topic here, I am seeking advice on how to implement "step-based heights" for a div element. To further clarify, my goal is to have each box with a height that is a multiple of 400px. For example, if the height is 345px, it ...

Retrieve the value of a PHP array within a for loop and transfer it to JQuery

*edited format I came across a PHP code for a calendar on the internet and I am currently working on implementing an onclick event that captures the date selected by a user as a variable (which will be used later in a database query). At this point, my g ...

Display scroll bars over the position:absolute header

My container has content that exceeds its size in both directions. To see the issue, try scrolling horizontally and vertically on the table available here: The vertical scrollbar is visible as desired, except that it gets hidden behind the table header un ...

The function is defined, but it cannot be set to null

Having trouble understanding this error message "Cannot set properties of null." I'm attempting to update the innerHTML with the output text from four functions that my button triggers. However, it seems to be stopping at the first function now even t ...

The placement of text appears to be incorrect when applying the float:left property in CSS

My issue is that I want #text1 to display on the right side of #video1, and #text2 to be positioned next to #video2. However, currently #text2 is also appearing beside #video1. Can someone please explain why this is happening and provide a solution to fi ...

Hide elements forever once the form is submitted

I'm seeking help to figure out how to make certain elements disappear after a form submission on my website's dashboard page. Specifically, I need to hide three elements once the user has submitted a form. Elements that need to be hidden: .vc_t ...

Achieving nested classes in CSS: a comprehensive guide

I am encountering an issue with my CSS while trying to create a dropdown on hover. The problem seems to be related to nested classes inside a div structure. It appears that the dropdown list is not displaying properly, leading me to believe there might be ...

What is the best way to rearrange the order of divs when the screen is being resized?

My task involves rendering a page with react-bootstrap on desktop, structured like this: <Row> <Col xs={12} md={8} className="a"> </Col> <Col xs={6} md={4} className="b"> </Col> <Col xs={6} md={4} className ...

Customizing the appearance of a radio button within a form

I need assistance with styling a form that contains a radio button. I'm looking to customize the appearance of the radio button by adding a background image and also changing the default view of the check image. Additionally, I want the form to be cen ...

Challenge with CSS selectors

Here is the HTML code I am working with: <label for="tx_alterneteducationcatalog_subscriberadd[newSubscriber][gender]" class="error" id="tx_alterneteducationcatalog_subscriberadd[newSubscriber] [gender]-error">This field is required.</label> ...

The issue arises when trying to use ::v-deep in conjunction with v-dialog's content-class when using scoped scss

I've been working on styling the content of the Vuetify dialog component and have been using the content-class prop along with scoped styles to achieve this. Can someone explain the difference between the styles provided below? And also, any tips on h ...

List with pulldown options

I am trying to implement a drop-down list with bullets using Angular 2, JavaScript, and CSS. Although I have managed to create the drop-down list, I am facing difficulty in adding bullets to the list items. Unfortunately, I have found that jQuery and Boot ...

The jQuery toggleClass() function is not being applied successfully to content that is dynamically generated from

I've created an awesome collection of CSS-generated cards containing icons and text with a cool animation that expands upon tapping to reveal more icons and options. I carefully crafted the list to look and behave exactly how I wanted it to. But now, ...

Modify the class of the dropdown and heading 2 elements if they meet specific conditions using Animate.css

Is it possible to add a class using jQuery when two specific conditions are met? 1) If the dropdown selection is either "acting" or "backstage" 2) If the membership status is set to "Non-member" If both of these requirements are fulfilled, I would like ...

Styling a div element in CSS so that it adjusts its size to fit its

How can I make a div element adjust to its content and also break the line so that the next element appears below it? I attempted setting the display property to inline-block, but then the div no longer breaks the line... Normally, it breaks the line, but ...

What is the best way to create a flexible Ul-LI menu?

Is it possible to create a flexible menu where clicking on an item does not cover other items in the menu? #demo { margin: 30px 0 50px 0; font-family: sans-serif; } #demo .wrapper { display: inline-block; width: 180px; height: 20px; position ...