8

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

  • x items side by side on the left,
  • y items side by side on the middle,
  • z items side by side on the right.

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?

Cédric
  • 2,239
  • 3
  • 10
  • 28
  • 1
    Hello, can you explain what are you trying to achieve more? The snippet and explanation still didnt clear it out for me. A simple image will be more helpful. Do you want to show "radiobutton div" on the left, divider in the middle and "language" div on the right? Or do you want to even divide the "radiobutton div" into left middle and right? – Raunaq Patel Oct 31 '22 at 15:24
  • @RaunaqPatel I added an image – Cédric Oct 31 '22 at 15:46
  • will you be able to wrap inputs into their individual divs? EG left inputs in .left div, center inputs in .center div and so on... Since in your code snippet its all under .Container? – Raunaq Patel Oct 31 '22 at 16:03
  • No, that is the point of the question – Cédric Oct 31 '22 at 16:07
  • Sorry but I couldnt think about any good way to do this with that restriction. .container > .middle ~ .middle{ margin: unset !important; } .container > .left ~ .middle{ margin-left: auto; } remotely made things centered but then since left items and right items are not there, it couldnt do it – Raunaq Patel Oct 31 '22 at 17:50
  • @Cédric why can’t you add a container? – HackerFrosch Nov 02 '22 at 12:46
  • @HackerFrosch I use some of the items to change properties of elements below only with css. If I add a container to those "nav items", I won't be able to reach next elements because you can access to siblings or children but not parents in css, or it's [not well supported](https://caniuse.com/?search=has) – Cédric Nov 02 '22 at 12:57
  • @Cédric I know maybe you don’t want to, but just use JavaScript. There‘s no need to do something like this with css and it’s just complicated and unnecessary. – HackerFrosch Nov 02 '22 at 13:40
  • "complicated" is not a good reason to give up the idea, and "unecessary" is your opinion that I disagree with. It won't be usefull in most cases but sometimes it will be. – Cédric Nov 02 '22 at 13:49
  • @Cédric Well I would say „complicated“ is a very good reason to not do something if you can do it way easier otherwise. In your case why is it useful to do it with css instead of js? – HackerFrosch Nov 02 '22 at 14:14
  • As your previous question, I quickly explained it in my post "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.". This reason is a choice and a part of the constraints. – Cédric Nov 02 '22 at 14:41
  • possible guidance: https://stackoverflow.com/q/38948102/3597276 – Michael Benjamin Nov 14 '22 at 04:32

5 Answers5

3

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>
HackerFrosch
  • 344
  • 1
  • 9
  • It's a good start, I changed the grid template for `grid-template-columns: auto 1fr auto auto auto 1fr auto auto;` but as in your snippet, `.middle` elements aren't centered, and I'm not sure how next elements (after `.right` els) could take 100% width as usual with these rules. Anyway, I would like if possible to find a more flexible solution so I don't have to change css if I want to add or delete a `.left` , `.middle` or `.right` element. Thanks for your contribution. – Cédric Nov 02 '22 at 09:02
1

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>
Anton
  • 8,058
  • 1
  • 9
  • 27
0

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>
Jakob E
  • 4,476
  • 1
  • 18
  • 21
0

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>
Cédric
  • 2,239
  • 3
  • 10
  • 28
AtomicUs5000
  • 344
  • 1
  • 9
  • Thanks for you answer, maybe it lacks of clarity for this point, I need to access to all `.left`, `.middle` and `.right` elements to affect next elements – Cédric Nov 16 '22 at 09:02
  • Yes, it would probably help to add left and middle controlled divs to your sample code, even if it has no layout as well as what you just said in this comment. – AtomicUs5000 Nov 17 '22 at 03:13
-1

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>
MrPatel2021
  • 163
  • 8