skip to Main Content

I’m working on a Frequently Asked Questions section for a project I’m making up. I’m going to make a list of common questions a potential buyer may have. I want it so when the user clicks the + button right after the question, a new paragraph opens up right below to answer the question. What I’m focused on now is trying to make it so all of the + buttons are vertically aligned with each other. If you look how it is now, each + is aligned in a different position depending on the length of the question. I’m using justify-content: space-between to have a reasonable amount of distance between the question and the + but it looks so tacky that the all the + aren’t vertically aligned together.

section {
  background-color: #fff;
  height: 100vh;
  height: fill-available;
  min-height: -moz-available;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  overflow-x: hidden;
}

.faq {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.container {
  display: flex;
  width: 60vw;
}

.left {
  width: 100%;
}

.get-started {
  font-family: "Poppins", sans-serif;
  font-size: 28px;
}

.question {
  display: flex;
  justify-content: space-between;
}
<section class="faq">
        <div class="container">
          <div class="left">
            <div class="get-started">
              <p>Learn How To Get Started</p>
            </div>
            <div class="question">
              <p>How do I create a website?</p>
              <button>+</button>
              <div>
                <p></p>
              </div>
            </div>
            <div class="question">
              <p>Is this website right for me?</p>
              <button>+</button>
              <div>
                <p></p>
              </div>
            </div>
           <div class="question">
             <p>How much will web storage cost me?</p>
             <button>+</button>
             <div>
               <p></p>
             </div>
           </div>
          </div>
        </div>
      </section>

2

Answers


  1. If you want to use justify-content: space-between; to right align the button, you should only have 2 elements in the container, otherwise, there will be the same space between all elements of that container (in your example, after the button). In the snippet below, I created a div (.label) to wrap the question paragraph and the button, an then I used flexbox on it.

    section {
        background-color: #fff;
        height: 100vh;
        height: fill-available;
        min-height: -moz-available;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        overflow-x: hidden;
    }
    
    .faq {
        display: flex;
        flex-direction: column;
        justify-content: center;
    }
    
    .container {
        display: flex;
        width: 60vw;
    }
    
    .left {
        width: 100%;
    }
    
    .get-started {
        font-family: "Poppins", sans-serif;
        font-size: 28px;
    }
    
    /* Use flex on the label div */
        .question .label {
        display: flex;
        justify-content: space-between;
    }
    <section class="faq">
        <div class="container">
            <div class="left">
                <div class="get-started">
                    <p>Learn How To Get Started</p>
                </div>
                <div class="question">
                    <div class="label">
                        <p>How do I create a website?</p>
                        <button>+</button>
                    </div>
                    <div>
                        <p></p>
                    </div>
                </div>
                <div class="question">
                    <div class="label">
                        <p>Is this website right for me?</p>
                        <button>+</button>
                    </div>
                    <div>
                        <p></p>
                    </div>
                </div>
                <div class="question">
                    <div class="label">
                        <p>How much will web storage cost me?</p>
                        <button>+</button>
                    </div>
                    <div>
                        <p></p>
                    </div>
                </div>
            </div>
        </div>
    </section>
    Login or Signup to reply.
  2. /* simple demo, I didn't want to put a lot of time into creating a "pretty" demo, though */
    
    // retrieving all elements matching the selector:
    const questions = document.querySelectorAll('.question'),
        buttons = document.querySelectorAll('.question button'),
        // defining the toggle() function, using Arrow syntax:
        toggle = (evt) => {
          // destructuring assignment to retrieve the value of
          // the currentTarget property from the Event Object
          // (passed automatically from EventTarget.addEventListener())
          // to a variable of the same name:
          let {currentTarget} = evt,
              // navigation to the closest '.question' ancestor, and
              // from there, finding the first <div> element within
              // that '.question' element; this allows for some
              // reorganisation of the HTML:
              answer = currentTarget.closest('.question').querySelector('div');
    
          // we set the hidden property of the found answer to be the inverse
          // of the current state of the hidden property; 'hidden' is a Boolean
          // property so it will either be true or false. If it's currently
          // true we invert that to false, and vice-versa:
          answer.hidden = !answer.hidden;
          
          // we update the text-content of the <button> to either
          // '-' if the answer is now visible (hidden: false) and
          // to '+' if the element is not visible (hidden: true):
          currentTarget.textContent = !answer.hidden ? '-' : '+';
          
        }
     
     // iterating over the '.question' elements and setting hidden: true, to
     // hide them on page load:
     questions.forEach((q) => q.querySelector('div').hidden = true);
     
     // iterating over the <button> elements and, using an Arrow function
     buttons.forEach(
      // we bind the toggle() function as the event-handler for the
      // 'click' event on each <button> in the NodeList:
      (btn) => btn.addEventListener('click', toggle)
     );
    /* reorganised the CSS to follow an order that makes sense to me,
       basically following the HTML structure from the top 'section.faq'
       inwards through the hierarchy of elements in order to make it
       - for me, personally - easier to follow the cascade: */
    section {
      background-color: #fff;
      height: 100vh;
      height: fill-available;
      min-height: -moz-available;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      overflow-x: hidden;
    }
    
    .faq {
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
    
    .container {
      display: flex;
      width: 60vw;
    }
    
    .left {
      width: 100%;
    }
    
    .get-started {
      font-family: "Poppins", sans-serif;
      font-size: 28px;
    }
    
    .question {
      display: flex;
      /* using shorthand to explicitly set:
           flex-direction: row;
           flex-wrap: wrap; */
      flex-flow: row wrap;
      justify-content: space-between;
    }
    
    /* because of the way that flex-items are sized,
       changing the <button> text (see the JS) causes
       the <button> to have different sizing depending
       on the text, and the open/closed state of the
       answer text's visibility; this is set simply
       to have the browser maintain the size consistently: */
    .question button {
      flex-basis: 2rem;
    }
    
    .question div {
      /* to have the <div> containing the answer take
         up the full available space, so forcing it
         to wrap to the next line (thanks to flex-wrap: wrap
         overriding the default value of flex: nowrap): */
      flex-basis: 100%;
    }
    <section class="faq">
      <div class="container">
        <div class="left">
          <div class="get-started">
            <p>Learn How To Get Started</p>
          </div>
          <div class="question">
            <p>How do I create a website?</p>
            <button>+</button>
            <div>
              <p>You can create a website following our simple <a href="#">guidelines</a>, <a href="#">wizards</a>, or <a href="#">intuitive UI</a>.</p>
            </div>
          </div>
          <div class="question">
            <p>Is this website right for me?</p>
            <button>+</button>
            <div>
              <p>Do you like it? That's your answer!</p>
            </div>
          </div>
          <div class="question">
            <p>How much will web storage cost me?</p>
            <button>+</button>
            <div>
              <p>Time, money, blood, sweat, and tears in convenient monthly installments.</p>
            </div>
          </div>
        </div>
      </div>
    </section>

    JS Fiddle demo.

    As for how it would be better handled, taking advantage of existing HTML elements and native functionality, I’d suggest the following (as already mentioned in the comments to the question):

    /* reorganised the CSS to follow an order that makes sense to me,
       basically following the HTML structure from the top 'section.faq'
       inwards through the hierarchy of elements in order to make it
       - for me, personally - easier to follow the cascade: */
    section {
      background-color: #fff;
      height: 100vh;
      height: fill-available;
      min-height: -moz-available;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      overflow-x: hidden;
    }
    
    .faq {
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
    
    .container {
      display: flex;
      width: 60vw;
    }
    
    .left {
      width: 100%;
    }
    
    .get-started {
      font-family: "Poppins", sans-serif;
      font-size: 28px;
    }
    
    /* hiding the default marker, the triangle (this doesn't
       affect functionality, I just assumed you'd rather
       retain a <button>-like interactivity/state element: */
    .faq ::marker {
      content: '';
    }
    
    summary {
      /* to indicate interactivity: */
      cursor: pointer;
      display: flex;
      justify-content: space-between;
    }
    
    /* styling the <span> element: */
    details span {
      /* ensuring that the element is equally wide as it is
         tall: */
      aspect-ratio: 1;
      background-color: lightskyblue;
      /* to create a round indicator: */
      clip-path: circle(50%);
      /* setting the dimensions of the element in concert
         with aspect-ratio: */
      inline-size: 2rem;
      /* large line-height to place the text in vertical, or
         block-axis, center of the element: */
      line-height: 2rem;
      /* aligning the text centrally on the inline-axis,
         horizontal in English, European languages: */
      text-align: center;
    }
    
    /* styling the <span> when the ancestor <details> element
       has the "open" attribute (adjust to taste, please): */
    details[open] span {
      background-color: palegreen;
    }
    
    /* setting the default text of the <span> element's
       ::before pseudo element: */
    details span::before {
      content: '+';
    }
    
    /* setting the text of the <span> element's
       ::before element when its ancestor <details>
       element has the "open" attribute, which is
       set and handled by the browser by default 
       (although the user can also write the attribute
       in the HTML to influence the default state): */
    details[open] span::before {
      content: '-';
    }
    <section class="faq">
      <div class="container">
        <div class="left">
          <div class="get-started">
            <p>Learn How To Get Started</p>
          </div>
    
          <!-- using the <details> element to contain both the question and the answer: -->
          <details>
            <!-- the <summary> element is visible on the page by default, and should the
                 user click on that element, the 'answer' (any child element(s) which are
                 not the <summary> will be toggled between visible/invisible based on
                 the presence, or absence, of the "open" attribute on the ancestor
                 <details>. The <button> element - with no text or child content - serves
                 as the wrapper for the indicator '+'/'-' text which indicates the
                 functionality of clicking-->
            <summary>How do I create a website? <span></span></summary>
            <p>You can create a website following our simple <a href="#">guidelines</a>, <a href="#">wizards</a>, or <a href="#">intuitive UI</a>.</p>
          </details>
          <details>
            <summary>Is this website right for me? <span></span></summary>
            <p>Do you like it? That's your answer!</p>
          </details>
    
          <details>
            <summary>How do I create a website? <span></span></summary>
            <p>You can create a website following our simple <a href="#">guidelines</a>, <a href="#">wizards</a>, or <a href="#">intuitive UI</a>.</p>
          </details>
          <details>
            <summary>How much will web storage cost me?</summary>
            <p>Time, money, blood, sweat, and tears in convenient monthly installments.</p>
          </details>
    
    
        </div>
      </div>
    </section>

    JS Fiddle demo.

    References:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search