3

I am working on solving a conundrum, and I have it mostly figured out except for one big part:

Imagine if you will 5 divs. A Header and 4 Sections (a - d).

Each section has an open/close toggle I did using the CSS Checkbox Hack to toggle the value of open/close. When open, a section displays content inside of it and the button toggles to 'close' Clicking that hides the content and toggles the button back to 'open'. All that works just fine.

As part of the requirements: the Header should be constantly displaying which sections are open, in a grammatically correct form:

"all sections are closed"

"section A is open"

"sections A and C are open" <-- the plural is important here.

I am a javascript guy mostly and this would be a trivial problem in javascript. Problem is? Has to be pure CSS. Why? Because that's the requirement. I suspect I am actually instead being tested on whether or not I can find the resources necessary to solve the problem. I say this because it's a javascript development position - so odd they would test me on pure CSS, AND it's a 100% remote position. When I used to work remote, 80% of the job was googling or SO, so here I am. ;)

I'm assuming that the solution involves abusing the content: property along with a css counter or even multiple ones, but so far, I am at a loss as to how one might do it.

Any markup is allowed, and any CSS, but NO javascript. Also has to be cross-browser compatible, but I will be satisfied with it working on ONE to start with.

Can someone point me in the right direction?

TIA.

Edit: Code of what I've got so far:

input[type='checkbox'] {
  visibility: hidden;
}
label:after {
  color: blue;
  text-decoration: underline;
  display: inline;
  position: absolute;
  right: 10px;
  content: 'open';
}
#sectionA:checked ~ label:after {
  content: 'close';
}
.section {
  border: 1px solid black;
  border-radius: 4px;
  margin: 1% auto;
  position: relative;
  line-height: 35px;
  padding-left: 5px;
  width: 100%;
}
.header {
  background-color: #ddd;
}
.sectionName {
  color: #ffa500;
  font-style: italic;
  display: inline;
}
.sectionContent {
  max-height: 0;
  display: none;
  transition: all .4s;
}
#sectionA:checked + .sectionContent {
  max-height: 30px;
  display: block;
}
.clearfix:after {
  visibility: hidden;
  display: block;
  font-size: 0;
  content: " ";
  clear: both;
  height: 0;
}
.clearfix {
  display: inline-block;
}
#siblings {
  color: #333;
}
#siblings,
h2 ~ div {
  color: red;
}
<div class="section header">
  <span id="headerMessage">all sections are closed</span>
</div>
<div class="section closed clearfix">
  section <span class="sectionName">a</span>
  <label for="sectionA" id="labelA"></label>
  <input type=checkbox id="sectionA" />
  <div class="sectionContent">section <span class="sectionName">a</span> content</div>
</div>
<div class="section closed clearfix">
  section <span class="sectionName">b</span>
  <label for="sectionB"></label>
  <input type=checkbox id="sectionB" />
  <div class="sectionContent">section <span class="sectionName">b</span> content</div>
</div>
<div class="section closed">
  section <span class="sectionName">c</span>
</div>
<div class="section closed">
  section <span class="sectionName">d</span>
</div>

I haven't added the classes for sections b-d, hence them not working yet, but they will act the same as section a.

Joeytje50
  • 17,344
  • 12
  • 56
  • 87
MonkRocker
  • 269
  • 2
  • 9
  • Not possible without JavaScript. – Darkrum Oct 18 '16 at 00:08
  • Depends if this is possible. If the 'counter' is shown *below* the 4 sections (ie. `
    ...
    `), it is possible with 4^2=16 different styles, based on the states of the divs' input elements. If the header is above it, I'm very sorry, but that is not possible (you can't access previous siblings nor parents in CSS in any way). So my question is: could you edit your post with an example layout?
    – Joeytje50 Oct 18 '16 at 00:11
  • Perhaps the test is to see if you can see that this *is*, in fact, impossible without JavaScript, and also be able to explain *why*? – Chris Oct 18 '16 at 00:13
  • 1
    Trick question maybe? But you should really post whatever code you've tried so we can work off of that. Otherwise, you're kinda asking us to code for you from scratch. – Michael Benjamin Oct 18 '16 at 00:26
  • Some people in this comments section need a more open mind towards interactive CSS stuff. Anything's possible if you set your mind to it. Except for some stuff that is actually impossible, such as accessing parent/previous-sibling elements. – Joeytje50 Oct 18 '16 at 01:06
  • 1
    PS: http://stackoverflow.com/a/5239256/1256925 This might be interesting to read for @Chris. Considering the fact this was a situation where I could use HTML+CSS+User interaction, I was technically using a Turing Complete language, so it was certainly *possible* to do this. Like I said in my answer though, I did not have the guarante that the code would not be exponential with the amount of tabs (even though it happens to be linear in this case). – Joeytje50 Oct 18 '16 at 01:19
  • 3
    @Joeytje50: Heck, you *can* [style previous siblings](http://stackoverflow.com/questions/1817792/is-there-a-previous-sibling-css-selector/36118012#36118012). Well, depending on your layout. Does that make it *the* solution? No, for one it only proves that there is in fact no previous sibling selector. Likewise, the checkbox hack is called a hack for a reason. Having an open mind is great, but you also have to consider whether it's worth bending over and possibly breaking a bone just to reach that cookie jar on the counter behind you when you could just as easily turn around. – BoltClock Oct 18 '16 at 04:58
  • 1
    @Joeytje50 interesting read and app there! It is hard to determine without seeing the actual code, but by the sounds of it it would require previous parents and so on, which is not really possible. BoltClock makes a good point as well on what you would consider "possible" (e.g does a hack count?). Good topic though. – Chris Oct 18 '16 at 07:21
  • @BoltClock I do think that using checkbox-based tabs can have its advantages, and does make some stuff easier to do (or equally easy as turning around to reach for thr cookies). But indeed, things like what's described in this question does match your cookie-jar analogy quite well. I'd never have made this with css personally, since things like this are just so much easier with js. – Joeytje50 Oct 18 '16 at 11:46
  • @Chris I would say a 'hack' certainly counts, because this is actually all the intended functionality of this selector (even though it's not the intended application of the functionality). – Joeytje50 Oct 18 '16 at 11:57
  • @Joeytje50, oh I was only generally speaking, not specifically to your app. – Chris Oct 18 '16 at 12:44
  • I just got reminded of this SO question's existance and I'm really curious now: did you end up getting the job, and did your boss actually expect this to be possible, or was it actually a trick question in their mind? – Joeytje50 Feb 15 '21 at 03:57

1 Answers1

4

It is possible, but ONLY if the list of opened tabs is at the very bottom of the HTML code (so below the tabs). If it is, it is a simple matter of dealing with all situations one by one. I've done so in the following code:

/* Hide everything to start with */
.tab,
#T0,
#T1,
.plural,
.t {
  display: none;
}

/* Show the correct tab when needed */
#a:checked ~ #tab1,
#b:checked ~ #tab2,
#c:checked ~ #tab3,
#d:checked ~ #tab4 {
  display: block;
}

/* When nothing is checked, show #T0, otherwise, show #T1 */
#a:not(:checked) + #b:not(:checked) + #c:not(:checked) + #d:not(:checked) ~ #count #T0,
:checked ~ #count #T1 {
   display: inline;
}

/* Check if the message should be plural or singular */
:checked ~ :checked ~ #count .plural {
  display: inline;
}
:checked ~ :checked ~ #count .singular {
  display: none;
}

#a:checked ~ #count .taba,
#b:checked ~ #count .tabb,
#c:checked ~ #count .tabc,
#d:checked ~ #count .tabd {
  display: inline;
}

/* Show the required tabs,
   with proper comma and 'and' placement
*/
/* if ? and d are open, we need 'and' */
:checked ~ #d:checked ~ #count .andcd,
/* if ? and c are open but d is not, we need 'and' */
:checked ~ #c:checked ~ #d:not(:checked) ~ #count .andbc,
/* if a and b are open but c and d are not, we need 'and' */
#a:checked ~ #b:checked ~ #c:not(:checked) ~ #d:not(:checked) ~ #count .andab,
/* if a, b and ? are open, we need a comma */
#a:checked ~ #b:checked ~ :checked ~ #count .comab,
/* if ?, c and d are open, we need a comma */
:checked ~ #c:checked ~ #d:checked ~ #count .combc {
  display: inline;
}

/* make the counter appear as if it is at the very top of the page,
   even though it is not.
*/
/* Make room at the top, and force absolute positions to be relative
   to #container*/
#container {
  position: relative;
  padding-top: 2em;
}
/* move tab-counter to the top */
#count {
  position:absolute;
  top: 0;
}
  <div id="container">
    <input id="a" type="checkbox"/>
    <input id="b" type="checkbox"/>
    <input id="c" type="checkbox"/>
    <input id="d" type="checkbox"/>
    <div id="tab1" class="tab t1"><label for="a">Tab1</label></div>
    <div id="tab2" class="tab t2"><label for="a">Tab2</label></div>
    <div id="tab3" class="tab t3"><label for="a">Tab3</label></div>
    <div id="tab4" class="tab t4"><label for="a">Tab4</label></div>
    <br/>
    <div id="count">
      <span id="T0">None of the tabs are open</span>
      <span id="T1">
        Tab<span class="plural">s</span>
        <!--tab listing-->
        <span class="t taba">a</span><!--
        --><span class="t comab">, </span><!--
        --><span class="t andab"> and </span><!--
        --><span class="t tabb">b</span><!--
        --><span class="t combc">, </span><!--
        --><span class="t andbc"> and </span><!--
        --><span class="t tabc">c</span><!--
        --><span class="t andcd"> and </span><!--
        --><span class="t tabd">d</span><!--
        -->
        <span class="singular">is</span>
        <span class="plural">are</span>
        opened.
      </span>
    </div>
  </div>

I've tried to be as efficient as possible with the correct grammar, by simply toggling commas and and spans whenever needed, and also being as efficient as possible with it. I've tested all 16 scenarios, and this does work correctly for each of them.

As far as code-efficiency goes, I think the amount of conditions for the bottom display:inline style is linear with the amount of tabs, and the amount of interpunction-spans is also linear with the amount of tabs. That means this is inefficient code (compared to scripts), but still not exponential like I initially thought.

Edit: In reaction to you posting your JSFiddle, that is impossible. You simply can't access parent elements or previous-siblings in CSS. You'll have to put your header at the very bottom of your document. You can mess with absolute positioning if you want though. See my updated code for more.

PS: this almost feels like a code-golf question, so if this wouldn't have been a legit SO question, it'd still have been a darn good challenge on http://codegolf.stackexchange.com.

Joeytje50
  • 17,344
  • 12
  • 56
  • 87
  • Whoa. That is some Jedi stuff right there. But is there some reason I can't absolutely position the tabs to move them to the top of the page? The fiddle I posted is correct, as far as visual is concerned. – MonkRocker Oct 18 '16 at 00:54
  • @MonkRocker I noticed your edit; I've responded with an edit so that it also looks as if it is at the top of the page. PS: when you've got the job, send your boss a link to this showing how you got this answer :P – Joeytje50 Oct 18 '16 at 00:59
  • I'm fine with putting the header at the bottom of the document and using positioning to make it look like it's at the top. I'm gonna mark this as the answer, because I'm pretty sure based on what I had and what you have here I can get it done. Thanks heaps! @Joeytje: I'm definitely not going to claim this one as my own. I still suspect the test isn't to see if I'm a CSS wizard, but instead to see if I can self-start enough to solve the problem via whatever resource I can find. – MonkRocker Oct 18 '16 at 01:08
  • @MonkRocker Just a quick note: I was really in the mood for a distraction right now, and this seemed like a puzzle that was just what I liked. This doesn't mean StackOverflow is a site you can simply ask for some desired code and wait for others to do it for you. You can always try, but don't expect SO to always be able (or willing) to give you some code that works just for what you want right then. This was a relatively simple problem for me right now, but real-life problems might be much too difficult to do as a volunteer on this site. Just a disclaimer. Good luck with your job application. – Joeytje50 Oct 18 '16 at 01:15
  • Oh I'm well aware. Usually I'm just here looking for a nudge in the right direction, but you whipped that out CRAZY fast. One quick question: are the empty comments you have wrapping lines significant? That's not a trick I have seen before, but again - I'm a middle/back tier guy primarily. – MonkRocker Oct 18 '16 at 01:25
  • @MonkRocker The reason I have that is because otherwise there will be a space between the letters of the tabs and the commas, since any whitespace in the HTML at all would be interpreted as a space. Since there's enters and spaces between the span tags, you do need this to prevent the commas to be spaced off the preceding letter. Alternatively you could simply put each of those lines together (so remove the comments and don't leave enters) but that would be ugly code. – Joeytje50 Oct 18 '16 at 01:35
  • 1
    @Joeytje50 This is wizard level hacking right here. Didn't think this was possible but man does it go against the grain to achieve something JavaScript could have done easily while keeping your markup semantically correct. +1 for you and this beautiful atrocity. – Darkrum Oct 18 '16 at 06:00
  • Impressive stuff! – Chris Oct 18 '16 at 10:58