Jonas
Jonas

Reputation: 417

How can I spread out my flex items to more than the width of a container?

I'd like to use flex to create something similar to this with CSS:

Click to see the image in fullscreen


What I've tried so far is this code:

#dot-container {
  position: absolute;
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: center;
  align-items: center;
  display: flex;
}

.dot {
  border-radius: 100%;
  width: 2vw;
  height: 2vw;
  margin: 3.2%; /*(30-2*7) / (7-2)*/
  background: green;
}
<div id="dot-container">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

But I cannot really get it working such that the leftmost and rightmost dots are exactly at the left and the right corner like you can see it in my image above.

Note: also justify-content: space-between; seems not to work because the dots themselves are most left but not dots center!

Upvotes: 5

Views: 1823

Answers (4)

Temani Afif
Temani Afif

Reputation: 272779

You can rely on overflow by having the width of elements and the margin exceed the total width. From your figure we have half a circle overflowing from left and right thus a full circle is overflowing

Considering this, the total width is 6*width_of_circle + total_margin. We can divide this margin to 6 parts (between our 7 cirles) and we will have 6*width_of_circle + 6*small_margin so each margin will be total_width/6 - width_of_circle that we split on each side:

#dot-container {
  position: absolute;
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: center;
  align-items: center;
  display: flex;
  margin:10px;
}

.dot {
  border-radius: 100%;
  width: 2vw;
  height: 2vw;
  margin: 0 calc((100%/6 - 2vw)/2); 
  background: green;
  flex-shrink:0; /* Don't shrink*/
}
<div id="dot-container">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

You can express everything in percentage if you want:

#dot-container {
  position: absolute;
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: center;
  align-items: center;
  display: flex;
  margin:10px;
}

.dot {
  border-radius: 100%;
  width: calc(100%/15);
  margin: 0 calc((100%/6 - 100%/15)/2); 
  background: green;
  flex-shrink:0; /* Don't shrink*/
}

/* To keep the square ratio*/
.dot:before {
  content:"";
  display:block;
  padding-top:100%;
}
<div id="dot-container">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

And we can add CSS variable to easily control the width of the dots by keeping the same configuration and the first/last circle overflowing:

#dot-container {
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: center;
  align-items: center;
  display: flex;
  margin:10px;
}

.dot {
  border-radius: 100%;
  width: calc(100%/var(--d));
  margin: 0 calc((100%/6 - 100%/var(--d))/2); 
  background: green;
  flex-shrink:0; /* Don't shrink*/
}

/* To keep the square ratio instead of setting height*/
.dot:before {
  content:"";
  display:block;
  padding-top:100%;
}
<div id="dot-container" style="--d:10">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

<div id="dot-container" style="--d:8">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>
<div id="dot-container" style="--d:20">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>


If you will add/remove dots simply adjust the value 6 which is the number of dots minus one:

#dot-container {
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: center;
  align-items: center;
  display: flex;
  margin:10px;
}

.dot {
  border-radius: 100%;
  width: calc(100%/var(--d));
  margin: 0 calc((100%/(var(--n) - 1) - 100%/var(--d))/2); 
  background: green;
  flex-shrink:0; /* Don't shrink*/
}

/* To keep the square ratio*/
.dot:before {
  content:"";
  display:block;
  padding-top:100%;
}
<div id="dot-container" style="--d:10;--n:5">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

<div id="dot-container" style="--d:8;--n:7">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>
<div id="dot-container" style="--d:20;--n:9">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>


As a side note, the left margin of the first element and the right margin of the last element doesn't need to be equal to the calculated value since they overflowing. They simply need to the be the same value even 0:

Equal to 0:

#dot-container {
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: center;
  align-items: center;
  display: flex;
  margin:10px;
}

.dot {
  border-radius: 100%;
  width: calc(100%/var(--d));
  margin: 0 calc((100%/(var(--n) - 1) - 100%/var(--d))/2); 
  background: green;
  flex-shrink:0; /* Don't shrink*/
}
.dot:first-child {
 margin-left:0;
}
.dot:last-child {
 margin-right:0;
}

/* To keep the square ratio*/
.dot:before {
  content:"";
  display:block;
  padding-top:100%;
}
<div id="dot-container" style="--d:10;--n:5">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

<div id="dot-container" style="--d:8;--n:7">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>
<div id="dot-container" style="--d:20;--n:9">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

Equal to a random value:

#dot-container {
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: center;
  align-items: center;
  display: flex;
  margin:10px;
}

.dot {
  border-radius: 100%;
  width: calc(100%/var(--d));
  margin: 0 calc((100%/(var(--n) - 1) - 100%/var(--d))/2); 
  background: green;
  flex-shrink:0; /* Don't shrink*/
}
.dot:first-child {
 margin-left:658624px;
}
.dot:last-child {
 margin-right:658624px;
}

/* To keep the square ratio*/
.dot:before {
  content:"";
  display:block;
  padding-top:100%;
}
<div id="dot-container" style="--d:10;--n:5">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

<div id="dot-container" style="--d:8;--n:7">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>
<div id="dot-container" style="--d:20;--n:9">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

Upvotes: 1

kukkuz
kukkuz

Reputation: 42352

If possible, you can try multiple backgrounds instead of flexboxes. The idea is to use a radial gradient for the circles and a linear gradient for the background - see the demo below:

div{
  width: 30vw;
  height: 8vw;
  margin: 10px;
  background: radial-gradient(circle, green calc(1vw - 2px) calc(1vw - 1px), transparent 1vw) center / calc(100% / var(--n)) 2vw repeat-x,         
              linear-gradient(black, black) center / calc(100% - 100% / var(--n)) 100% no-repeat;  
}
<div style="--n:5"></div>
<div style="--n:9"></div>


Now you can have a fixed width for the black background by changing the width of the div based on the number of circles - see demo below with a red background to show the actual boundaries:

div {
  height: 8vw;
  margin: 10px calc(-15vw / var(--n));
  position: relative;
  width: calc(30vw + 30vw / var(--n));
  background: radial-gradient(circle, green calc(1vw - 2px) calc(1vw - 1px), transparent 1vw) 0 50% / calc(30vw / var(--n)) 2vw repeat-x,
              linear-gradient(black, black) center / calc(100% - 30vw / var(--n)) 100% no-repeat, red;
}
<div style="--n:3"></div>
<div style="--n:5"></div>
<div style="--n:9"></div>


Finish it up using a pseudo element to apply the background and having the width specified on the div for generality - final result below:

div {
  --w: 30vw;
  height: 8vw;
  width: var(--w);
  margin: 10px;
  position: relative;
}

div:after {
  content: '';
  position: absolute;
  height: 100%;
  width: calc(100% + 100% / var(--n));
  margin: 0 calc(-1 * var(--w) / var(--n) / 2);
  background: radial-gradient(circle, green calc(1vw - 2px) calc(1vw - 1px), transparent 1vw) 0 50% / calc(var(--w) / var(--n)) 2vw repeat-x,
              linear-gradient(black, black) center / calc(100% - var(--w) / var(--n)) 100% no-repeat;
}
<div style="--n:3"></div>
<div style="--n:7"></div>
<div style="--n:9"></div>

Upvotes: 2

Philip Sole
Philip Sole

Reputation: 58

You might go for a pseudo element that has the black background. Increase the width of your container a bit so the dots can spread out 1vw more. With justify-content: space-between you don't need the margin on the dots. The dots have position relative so they display on top of the black background.

#dot-container {
  position: absolute;
  width: 32vw;
  height: 8vw;
  justify-content: space-between;
  align-items: center;
  display: flex;
}

#dot-container:before {
  content: '';
  display: block;
  position: absolute;
  width: 30vw;
  height: 8vw;
  margin: 0 1vw;
  background: black;
}

.dot {
  border-radius: 100%;
  width: 2vw;
  height: 2vw;
  background: green;
  position: relative;
}

Upvotes: 0

Anurag Srivastava
Anurag Srivastava

Reputation: 14413

This can be done using justify-content: space-between and using a negative margin.

#dot-container {
  position: absolute;
  width: 30vw;
  background: black;
  height: 8vw;
  justify-content: space-between;
  align-items: center;
  display: flex;
}

.dot {
  border-radius: 100%;
  width: 2vw;
  height: 2vw;
  margin: -3.2%; /*(30-2*7) / (7-2)*/
  background: green;
}
<div id="dot-container">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

Or, if you do not want to calculate margins you can add a child element with absolute positioning, and change number of div.dot elements like you wish:

#dot-container {
  position: relative;
  width: 30vw;
  height: 6vw;
  justify-content: space-between;
  align-items: center;
  display: flex;
}

#dot-container-inner {
  position: absolute;
  left: 3%;
  top: 0;
  z-index: -1;
  height: 100%;
  width: 95%;
  background: black;
}

.dot {
  border-radius: 100%;
  width: 2vw;
  height: 2vw;
  background: green;
}
<div id="dot-container">
  <div id="dot-container-inner">
  </div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>

Upvotes: 6

Related Questions