Cédric
Cédric

Reputation: 2629

How to display x items on left, y on middle, and z on right in a row without wrapping elements?

I would like to do something similar to justify-content:space-around or justify-content:space-between, but with :

enter image description here

It would be simple by wrap elements but I can't because these items (.left, .middle, .right) would be checkboxes influencing the styles of elements below (and there is no well-supported parent selector).

I found this answer to emulate first-of-class for the right side, but didn't find something similar to emulate last-of-class.

/* emulate first-of-class */
.container>.right {
  margin-left: auto;
}

.container>.right~.right {
  margin-left: unset;
}

Here is a snippet of my current attempt :

.container {
  display: flex;
  flex-wrap: wrap;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
}

.container>.right {
  margin-left: auto;
}

.container>.right~.right {
  margin-left: unset;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang ] {
  display: none;
}

[ name="language" ]:checked+label {
  background-color: pink;
}

[ value="en" ]:checked ~  [ lang="en" ],
[ value="en" ]:checked ~* [ lang="en" ],
[ value="fr" ]:checked ~  [ lang="fr" ],
[ value="fr" ]:checked ~* [ lang="fr" ],
[ value="es" ]:checked ~  [ lang="es" ],
[ value="es" ]:checked ~* [ lang="es" ] {
  display: block;
}

/*for codepen*/
html[ lang ] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>

  <div id="check-center">|</div>

  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>

I could cheat by adding a specific class to the last left element to apply margin-right:auto; and the first right element to apply margin-left:auto; in a display:flex container but it's not the best and even then, the middle items wouldn't be centered if the left and right parts have not the same width.

.container {
  display: flex;
  flex-wrap: wrap;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
}

.cheat.left {
  margin-right: auto;
}

.cheat.right {
  margin-left: auto;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang ] {
  display: none;
}

[ name="language" ]:checked+label {
  background-color: pink;
}

[ value="en" ]:checked ~  [ lang="en" ],
[ value="en" ]:checked ~* [ lang="en" ],
[ value="fr" ]:checked ~  [ lang="fr" ],
[ value="fr" ]:checked ~* [ lang="fr" ],
[ value="es" ]:checked ~  [ lang="es" ],
[ value="es" ]:checked ~* [ lang="es" ] {
  display: block;
}

/*for codepen*/
html[ lang ] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="cheat left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="cheat right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>

  <div id="check-center">|</div>

  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>

My current goal is to display elements depending on the selected language even if JavaScript is disabled, so I'm looking for a solution without JavaScript.

As HackerFrosch suggested, I tried to solve it by using a grid but I'm not used to it, the .middle items are not centered and I did not manage to make the elements below .right divs 100% width as by default.

.container {
  display: grid;
  grid-template-columns: auto 1fr auto auto auto 1fr auto auto;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: fit-content;
}

.cheat.left {
  margin-right: auto;
}

.cheat.right {
  margin-left: auto;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang] {
  display: none;
}

[ name="language"]:checked+label {
  background-color: pink;
}

[ value="en"]:checked~[ lang="en"],
[ value="en"]:checked~* [ lang="en"],
[ value="fr"]:checked~[ lang="fr"],
[ value="fr"]:checked~* [ lang="fr"],
[ value="es"]:checked~[ lang="es"],
[ value="es"]:checked~* [ lang="es"] {
  display: block;
}


/*for codepen*/

html[ lang] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="cheat left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="cheat right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>
  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>
<div id="check-center">|</div>

Is there a way to achieve this?

Upvotes: 8

Views: 368

Answers (5)

AtomicUs5000
AtomicUs5000

Reputation: 378

This does well to maintain the center while still allowing css selectors. It will maintain center adding more elements to the left, middle, or right containers. The middle container can have odd or even elements and it will still maintain center, provided there is adequate room.

The limitations of this approach is that your header height must be fixed, and the absolutely positioned #dynamic_children div top property must reflect that value. Also, due to IE 11's poor flex implementation, this will not work properly and obviously they will never fix that since IE 11 is no longer supported. I am unsure if that matters for you.

body {
  padding: 0;
  margin: 0;
}

.container {
  display: flex;
  flex-wrap: nowrap;
  height: 30px;
  max-height: 30px;
}

#left_container {
  display: flex;
  justify-content: left;
  flex: 1;
  flex-wrap: nowrap;
  align-items: center;
}

#middle_container {
  display: flex;
  justify-content: center;
  flex: 0;
  flex-wrap: nowrap;
  align-items: center;
}

#right_container {
  display: flex;
  justify-content: right;
  flex: 1;
  flex-wrap: wrap;
  align-items: center;
  margin-left: auto;
}

#check-center {
  display: block;
  width: 100%;
  height: 20px;
  text-align: center;
  background-color: rgb(255, 255, 0);
}

#dynamic_children {
  width: 100%;
  display: block;
  position: absolute;
  top: 30px;
  left: 0;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
}

[ lang] {
  display: none;
}

[ name="language"]:checked+label {
  background-color: pink;
}

[ value="en"]:checked~[ lang="en"],
[ value="en"]:checked~* [ lang="en"],
[ value="fr"]:checked~[ lang="fr"],
[ value="fr"]:checked~* [ lang="fr"],
[ value="es"]:checked~[ lang="es"],
[ value="es"]:checked~* [ lang="es"] {
  display: block;
}
<body>
  <div class="container">
    <div id="left_container">
      <input type="radio" name="left-field" id="A" />
      <label class="left" for="A">A</label>
      <input type="radio" name="left-field" id="B" />
      <label class="left" for="B">B</label>
    </div>
    <div id="middle_container">
      <input type="radio" name="middle-field" id="C" />
      <label class="middle" for="C">C</label>
      <input type="radio" name="middle-field" id="D" />
      <label class="middle" for="D">D</label>
      <input type="radio" name="middle-field" id="E" />
      <label class="middle" for="E">E</label>
    </div>
    <div id="right_container">
      <input type="radio" name="language" id="enLang" value="en" />
      <label class="right" for="enLang">en</label>
      <input type="radio" name="language" id="frLang" value="fr" />
      <label class="right" for="frLang">fr</label>
      <input type="radio" name="language" id="esLang" value="es" />
      <label class="right" for="esLang">es</label>
      <div id="dynamic_children">
        <div id="check-center">-|-</div>
        <div>
          <div lang="en">EN selected div 1</div>
          <div lang="fr">FR selected div 1</div>
          <div lang="es">ES selected div 1</div>

          <div lang="en">EN selected div 2</div>
          <div lang="fr">FR selected div 2</div>
          <div lang="es">ES selected div 2</div>

          <div lang="en">EN selected div 3</div>
          <div lang="fr">FR selected div 3</div>
          <div lang="es">ES selected div 3</div>
        </div>
      </div>
    </div>
  </div>
</body>

Upvotes: 0

Jakob E
Jakob E

Reputation: 4936

Something like this - using left margin auto on the first center and right classes:

/* flex container */
.container { display: flex; }

/* left margin auto on all center/right classes */
.center, .right { margin-left: auto; }

/* remove auto margin on non firsts */
.center~.center, .right~.right { margin-left: initial; }



/* just a bit of styling */
div { padding: .5rem; }
.left { background: tomato; }
.center { background: orange; }
.right { background: yellowgreen; }
<div class="container">
  <div class="left">Left 1</div>
  <div class="left">Left 2</div>
  <div class="left">Left 3</div>

  <div class="center">Center 1</div>
  <div class="center">Center 2</div>
  <div class="center">Center 3</div>

  <div class="right">Right 1</div>
  <div class="right">Right 2</div>
  <div class="right">Right 3</div>
</div>

Upvotes: 0

MrPatel2021
MrPatel2021

Reputation: 211

As per your requirements, you need to use grid property.

<!DOCTYPE html>
<html>
   <style>
      .container {
      display: grid;
      grid-template-columns: 50px auto 50px 50px auto 50px 50px 50px;
      }
      input {
      display: none;
      }
      label {
      padding: 0 10px;
      background-color: orange;
      width: 12px;
      }
      #check-center {
      width: 100%;
      text-align: center;
      }
      [ lang ] {
      display: none;
      }
      [ name="language" ]:checked+label {
      background-color: pink;
      }
   </style>
   <body>
      <div class="container">
         <input type="radio" name="left-field" id="A" />
         <label class="left" for="A">A</label>
         <input type="radio" name="left-field" id="B" />
         <label class="left" for="B">B</label>
         <input type="radio" name="middle-field" id="C" />
         <label class="middle" for="C">C</label>
         <input type="radio" name="middle-field" id="D" />
         <label class="middle" for="D">D</label>
         <input type="radio" name="middle-field" id="E" />
         <label class="middle" for="E">E</label>
         <input type="radio" name="language" id="enLang" value="en" />
         <label class="right" for="enLang">en</label>
         <input type="radio" name="language" id="frLang" value="fr" />
         <label class="right" for="frLang">fr</label>
         <input type="radio" name="language" id="esLang" value="es" />
         <label class="right" for="esLang">es</label>
         <div lang="en">EN selected</div>
         <div lang="fr">FR selected</div>
         <div lang="es">ES selected</div>
         <div>
            <div lang="en">EN selected</div>
            <div lang="fr">FR selected</div>
            <div lang="es">ES selected</div>
         </div>
      </div>
   </body>
</html>

Upvotes: -1

Anton
Anton

Reputation: 8538

You can try to solve this with flex and use the calc() function to manage the left or right margins. So, I created four classes to use one of them to center the middle classes.

/* adjust for left side, put to the first middle label */
.one-left.middle {
  margin-inline-start: calc((4 * var(--block-width) / 2));
}
.two-left.middle {
  margin-inline-start: calc((3 * var(--block-width) / 3));
}

/* adjust for right side, set to the last meddle label */
.one-right.middle {
  margin-inline-end: calc((4 * var(--block-width) / 2));
}
.two-right.middle {
  margin-inline-end: calc((3 * var(--block-width) / 3));
}

Usage .two-left.middle

*,
::after,
::before {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  --block-width: 50px;
  display: flex;
  justify-content: center;
}

.gap.left {
  margin-inline-end: auto;
}

.gap.right {
  margin-inline-start: auto;
}


/* adjust for left side, put to the first middle label */
.one-left.middle {
  margin-inline-start: calc((4 * var(--block-width) / 2));
}

.two-left.middle {
  margin-inline-start: calc((3 * var(--block-width) / 3));
}


/* adjust for right side, set to the last meddle label */
.one-right.middle {
  margin-inline-end: calc((4 * var(--block-width) / 2));
}

.two-right.middle {
  margin-inline-end: calc((3 * var(--block-width) / 3));
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: var(--block-width);
}

#check-center {
  width: 100%;
  text-align: center;
}

[lang] {
  display: none;
}

[name='language']:checked+label {
  background-color: pink;
}

[value='en']:checked~[lang='en'],
[value='en']:checked~* [lang='en'],
[value='fr']:checked~[lang='fr'],
[value='fr']:checked~* [lang='fr'],
[value='es']:checked~[lang='es'],
[value='es']:checked~* [lang='es'] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <!-- <input type="radio" name="left-field" id="A1" />
      <label class="left" for="A1">A1</label> -->
  <input type="radio" name="left-field" id="B" />
  <label class="gap left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="two-left middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="gap right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>
  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>
<div id="check-center">|</div>

Usage .one-right.middle

*,
::after,
::before {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  --block-width: 50px;
  display: flex;
  justify-content: center;
}

.gap.left {
  margin-inline-end: auto;
}

.gap.right {
  margin-inline-start: auto;
}


/* adjust for left side, put to the first middle label */
.one-left.middle {
  margin-inline-start: calc((4 * var(--block-width) / 2));
}

.two-left.middle {
  margin-inline-start: calc((3 * var(--block-width) / 3));
}


/* adjust for right side, set to the last meddle label */
.one-right.middle {
  margin-inline-end: calc((4 * var(--block-width) / 2));
}

.two-right.middle {
  margin-inline-end: calc((3 * var(--block-width) / 3));
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: var(--block-width);
}

#check-center {
  width: 100%;
  text-align: center;
}

[lang] {
  display: none;
}

[name='language']:checked+label {
  background-color: pink;
}

[value='en']:checked~[lang='en'],
[value='en']:checked~* [lang='en'],
[value='fr']:checked~[lang='fr'],
[value='fr']:checked~* [lang='fr'],
[value='es']:checked~[lang='es'],
[value='es']:checked~* [lang='es'] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="A1" />
  <label class="left" for="A1">A1</label> 
  <input type="radio" name="left-field" id="B" />
  <label class="gap left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="one-right middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="gap right" for="enLang">en</label>
  <!--<input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label> 
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label> -->
  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>
<div id="check-center">|</div>

Upvotes: 1

HackerFrosch
HackerFrosch

Reputation: 364

A solution to your problem is the css grid layout. You can customize the positioning in this example if you want. I hope that helps.

.container {
  display: grid;
  grid-template-columns: 50px auto 50px 50px auto 50px 50px 50px;
}

input {
  display: none;
}

label {
  padding: 0 10px;
  background-color: orange;
  width: 20px;
}

#check-center {
  width: 100%;
  text-align: center;
}

[ lang ] {
  display: none;
}

[ name="language" ]:checked+label {
  background-color: pink;
}

[ value="en" ]:checked ~  [ lang="en" ],
[ value="en" ]:checked ~* [ lang="en" ],
[ value="fr" ]:checked ~  [ lang="fr" ],
[ value="fr" ]:checked ~* [ lang="fr" ],
[ value="es" ]:checked ~  [ lang="es" ],
[ value="es" ]:checked ~* [ lang="es" ] {
  display: block;
}

/*for codepen*/
html[ lang ] {
  display: block;
}
<div class="container">
  <input type="radio" name="left-field" id="A" />
  <label class="left" for="A">A</label>
  <input type="radio" name="left-field" id="B" />
  <label class="left" for="B">B</label>

  <input type="radio" name="middle-field" id="C" />
  <label class="middle" for="C">C</label>
  <input type="radio" name="middle-field" id="D" />
  <label class="middle" for="D">D</label>
  <input type="radio" name="middle-field" id="E" />
  <label class="middle" for="E">E</label>

  <input type="radio" name="language" id="enLang" value="en" />
  <label class="right" for="enLang">en</label>
  <input type="radio" name="language" id="frLang" value="fr" />
  <label class="right" for="frLang">fr</label>
  <input type="radio" name="language" id="esLang" value="es" />
  <label class="right" for="esLang">es</label>

  <div id="check-center">|</div>

  <div lang="en">EN selected</div>
  <div lang="fr">FR selected</div>
  <div lang="es">ES selected</div>
  <div>
    <div lang="en">EN selected</div>
    <div lang="fr">FR selected</div>
    <div lang="es">ES selected</div>
  </div>
</div>

Upvotes: 3

Related Questions