Creating a Horizontal Bar-Chart

Now I am trying to create a responsive, horizontal-only, bar-chart.

For this horizontal bar-chart, the HTML is slightly different, however, I am trying to use your CSS code for the Y-axis Title and Labels.

Unfortunately, my Title and Labels aren’t behaving.

Here is what I have so far…

<!DOCTYPE HTML>
<html lang="en">

<!-- *************************  HTML HEAD  ********************************* -->
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">

  <title>sp_bar-chart-horizontal_v03.html</title>

  <!-- CSS STYLES -->
  <style media="screen">
    /**************************************************************************/
    /* GENERIC                                                                */
    /**************************************************************************/
    *{
      margin: 0;
      padding: 0;
    }
    
    *,
    *:before,
    *:after{
      box-sizing: border-box;
    }

    body{
      font-family: Helvetica, Arial, Sans-Serif;
      font-weight: normal;
      line-height: 1.4em;
      font-size: 0.9em;
      color: #000;
    }

    
    /**************************************************************************/
    /* DESKTOP                                                                */
    /**************************************************************************/
    
    /* FIGURE */
    figure{
      min-width: 200px;
      max-width: 600px;
      margin: 2rem;
      padding: 1.5rem 2rem 2rem;
      border: 1px solid #000;
    }
    
    /* TABLE */
    .barChart_h{
      display: block;
      height: auto;                         /* Do I need this? */
      width: 100%;
      overflow-wrap: break-word;
      border-spacing: 0;
    }
    
    /* CAPTION */
    .barChart_h caption{
      display: block;
      padding: 0 0 1rem 0;
      line-height: 1.1;
      font-size: 1.1rem;
      font-weight: bold;
      text-align: left;
    }
    
    /* TBODY */    
    .barChart_h tbody{
      display: block                        /* All HTML elements have a "display" value assigned by the User-Agent (UA).
                                               Default-values assigned:
                                                  tbody ==> (display: table-row-group)
                                                  tr    ==> (display: table-row)
                                                  td    ==> (display: table-cell)
                                               Assigning (display: block) allows us to turn a table into a horizontal display. */
    }

    .barChart_h tbody:after{                /* For IE9 and under, need to enclose floats... */
      content: "";
      display: block;
      clear: both;
      height: 0;
    }
    
    /* TH */
    .barChart_h tbody th{
      width: 25%;
      font-weight: normal;
      text-align: right;
    }

    /* TD */
    .barChart_h tbody td{
      border-left: 2px solid #F00;          /* X-AXIS. (vertical) */
      
      border-right: 1px solid #DDD;         /* Finish out repeating vertical-gridlines. */
      
      background-image: linear-gradient(to right, #DDD 1px, transparent 1px);
                                            /* Create black-transparent gradient for 1px, then remainder of gradient is transparent.
                                               This creates the illusion of a 1px vertical-line. */
      background-size: 10% 100%;            /* Go right in 10% increments. */
    }

    /* TH+TD */
    .barChart_h tbody th,
    .barChart_h tbody td{
      padding: 0.5rem 0 0.4rem 0;           /* Space around Bars. */
    }
                                            
    .barChart_h tbody tr.firstRow th,
    .barChart_h tbody tr.firstRow td{
      padding: 1rem 0 0.5rem 0;             /* Add spacing. */
    }
    
    .barChart_h tbody tr.lastRow th,
    .barChart_h tbody tr.lastRow td{
      padding: 0.5rem 0 1rem 0;             /* Add spacing. */
    }
    
    /* BARS */
    .barChart_h tbody td span{
      position: relative;                   /* Needed for absolute-positioning of Bar-value. */
      display: block;                       /* Expands <span> to fill <td> */
      height: 20px;
      background: #99FFFF;
      border-top: 1px solid #000;
      border-right: 1px solid #000;
      border-bottom: 1px solid #000;
      box-shadow: 5px 0px 5px rgba(0, 0, 0, 0.3);
    }
    
    /* BAR-VALUES */
    .barChart_h tbody td span b{
      position: absolute;
      left: 100%;
      top: 0;
      right: auto;
      bottom: 0;
      display: block;
      padding: 0 0 0 0.5rem;
    }
        
    /* Y-AXIS TITLE+LABELS */
    .barChart_h tbody th.y-axis{
      display: block;
      width: 100%;
/*      width: calc(100% + 2px);            /* Hide right border. /**/

      display: flex;                        /* Create Flexbox (Flex-Container). */
      padding-bottom: 1.4rem;
      font-weight: bold;
      border-bottom: 2px solid #F00;        /* Y-AXIS. (horizontal) */
      background-color: #FFF;
    }

    
    /* NEED HELP WITH STYLES BELOW... */

    /* Y-AXIS TITLE */
    .barChart_h tbody .rotate{
      display: block;
    }
/*    
      -ms-writing-mode: initial;
      writing-mode: initial;
      white-space: nowrap;
      transform: none;
      margin: 0;
      line-height: normal;
      font-size: 0.9rem;
    }
/**/

    /* Y-AXIS LABELS */
    .barChart_h tbody th.y-axis ol.segments{
      position: absolute;
      top: auto;
      bottom: 0;
      right: 0;
      left: 0;
      display: flex;                        /* Create Flexbox (Flex-Container). */
      flex-direction: row;
      font-size: 0.9rem;
    }

    .barChart_h tbody ol.segments li{
      flex: 1 0 0;
      text-align: right;
    }

    .barChart_h tbody ol.segments li b{
      display: inline-flex;
      transform: translate(50%, 0%);
    }

    .barChart_h tbody ol.segments li.zero{
      left: 0;
      right: auto;
      bottom: auto;
      top: 0;
    }

    .barChart_h tbody ol.segments li.zero b{
      transform: translate(-50%, 0%);
    }

    /**************************************************************************/
    /* MOBILE                                                                 */
    /**************************************************************************/
    @media screen and (max-width: 414px){
      body{
        font-size: 0.8em;
      }
      
      /* CAPTION */
      .barChart_h caption{
        font-size: 1rem;
      }

      /* FIGURE */
      figure{
        margin: 0;
        padding: 1rem;
      }
      
      /* TD */
      .barChart_h tbody td{
        width: 60%;
      }
    }
        
  </style>
</head>

<!-- *************************  HTML BODY  ********************************* -->
<body>
  <figure>
    <table class="barChart_h">
      <caption>Q1: My waiter provided good service tonight.</caption>

      <tbody>
        <!-- Y-axis -->
        <tr>
          <th class="blankCell"></th>
          <th class="y-axis">
            <div class="rotate">Responses</div>
            <ol class="segments">
              <li><b>100</b></li>
              <li><b>80</b></li>
              <li><b>60</b></li>
              <li><b>40</b></li>
              <li><b>20</b></li>
              <li class="zero"><b>0</b></li>
            </ol>
          </th>
        </tr>
        
        <!-- Data Rows -->
        <tr class="firstRow">
          <th scope="row">Stongly Disagree:</th>
          <td><span style="width:10%"><b>10</b></span></td>
        </tr>
        <tr>
          <th scope="row">Disagree:</th>
          <td><span style="width:30%"><b>30</b></span></td>
        </tr>
        <tr>
          <th scope="row">Neither:</th>
          <td><span style="width:20%"><b>20</b></span></td>
        </tr>
        <tr>
          <th scope="row">Strongly Agree:</th>
          <td><span style="width:70%"><b>70</b></span></td>
        </tr>
        <tr class="lastRow">
          <th scope="row">Strongly Agree:</th>
          <td><span style="width:40%"><b>40</b></span></td>
        </tr>
      </tbody>

    </table>
  </figure>
  
</body>
</html>

Not sue what I am doing wrong, but am hoping you can help me fix things.

Thanks!

I’m not around much this week (and its best not to tag specific people because if they are away then no one will answer) but here’s a few pointers.

You need to remove the display:flex from the y-axis element and add then add position:relative to it as a stacking context for the absolute ol.

Then you need to remove margin and padding and list-marker from the ol.segments and set it flex-direction to row-reverse.

You then need to add position:absolute to the last list element (.zero) to remove it from the flow and position it to the left (otherwise you have 6 in-flow items when you need to split the space into 5 equal parts).

If you do that correctly it will look like this:

Screen Shot 2021-04-05 at 13.26.49

I am not going to post the full solution as you are pretty close and you need to work on the finer details a little more. The reasoning was explained in the last thread anyway so you can always refer to that.

6 Likes

@PaulOB ,

Thank you for the tips above!

I was able to get things working and match your example, and below is my updated code…

<!DOCTYPE HTML>
<html lang="en">

<!-- *************************  HTML HEAD  ********************************* -->
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1">

  <title>sp_bar-chart-horizontal_v05.html</title>

  <!-- CSS STYLES -->
  <style media="screen">
    /**************************************************************************/
    /* GENERIC                                                                */
    /**************************************************************************/
    *{
      margin: 0;
      padding: 0;
    }
    
    *,
    *:before,
    *:after{
      box-sizing: border-box;
    }

    body{
      font-family: Helvetica, Arial, Sans-Serif;
      font-weight: normal;
      line-height: 1.4em;
      font-size: 0.9em;
      color: #000;
    }

    
    /**************************************************************************/
    /* DESKTOP                                                                */
    /**************************************************************************/
    
    /* FIGURE */
    figure{
      min-width: 200px;
      max-width: 780px;
/*      max-width: 600px;                     /**/
      margin: 2rem;
      padding: 1.5rem 2rem 2rem;
      border: 1px solid #000;
    }
    
    /* TABLE */
    .barChart_h{
      display: block;
      height: auto;                         /* Do I need this? */
      width: 100%;
      overflow-wrap: break-word;
      border-spacing: 0;
    }
    
    /* CAPTION */
    .barChart_h caption{
      display: block;
      padding: 0 0 1rem 0;
      line-height: 1.1;
      font-size: 1.1rem;
      font-weight: bold;
      text-align: left;
    }
    
    /* TBODY */    
    .barChart_h tbody{
      display: block                        /* All HTML elements have a "display" value assigned by the User-Agent (UA).
                                               Default-values assigned:
                                                  tbody ==> (display: table-row-group)
                                                  tr    ==> (display: table-row)
                                                  td    ==> (display: table-cell)
                                               Assigning (display: block) allows us to turn a table into a horizontal display. */
    }

    .barChart_h tbody:after{                /* For IE9 and under, need to enclose floats... */
      content: "";
      display: block;
      clear: both;
      height: 0;
    }
    
    /* TH+TD */
    .barChart_h tbody tr.firstRow th,
    .barChart_h tbody tr.firstRow td{
      padding: 1rem 0 0.5rem 0;             /* Add extra vertical padding. */
    }
    
    .barChart_h tbody th,
    .barChart_h tbody td{
      padding: 0.5rem 0 0.4rem 0;           /* Space around Bars. */
    }
                                            
    .barChart_h tbody tr.lastRow th,
    .barChart_h tbody tr.lastRow td{
      padding: 0.5rem 0 1rem 0;             /* Add extra vertical padding. */
    }
    
    /* TH */
    .barChart_h tbody th{
/*      width: 25%;     /**/
      font-weight: normal;
      text-align: right;
    }

    /* TD */
    .barChart_h tbody td{
      border-left: 2px solid #F00;          /* X-AXIS. (vertical) */
      
      border-right: 1px solid #DDD;         /* Finish out repeating vertical-gridlines. */
      
      background-image: linear-gradient(to right, #DDD 1px, transparent 1px);
                                            /* Create grey-transparent gradient for 1px, then remainder of gradient is transparent.
                                               This creates the illusion of a 1px vertical-line. */
      background-size: 10% 100%;            /* Go right in 10% increments. */
    }

    /* BARS */
    .barChart_h tbody td span{
      position: relative;                   /* Needed for absolute-positioning of Bar-value. */
      display: block;                       /* Expands <span> to fill <td> */
      height: 20px;
      background: #99FFFF;                  /* Aqua (default) */
      border-top: 1px solid #000;
      border-right: 1px solid #000;
      border-bottom: 1px solid #000;
      box-shadow: 5px 0px 5px rgba(0, 0, 0, 0.3);
    }
    
    /* BAR-VALUES */
    .barChart_h tbody td span b{
      position: absolute;
      left: 100%;
      top: 0;
      right: auto;
      bottom: 0;
      display: block;
      padding: 0 0 0 0.5rem;
      font-weight: normal;
    }

    /* Y-AXIS */
    .barChart_h tbody th.blankCell{
      width: 25%;
    }

    .barChart_h tbody th.y-axis{
      position: relative;                   /* New */
      padding-bottom: 1.4rem;
      border-bottom: 2px solid #F00;        /* Y-AXIS. (horizontal) */
      background-color: #FFF;
    }
    
    /* Y-AXIS TITLE */
    .barChart_h tbody div.y-axis-title{
      display: block;
      text-align: center;
      font-weight: bold;
    }    
    
    /* Y-AXIS LABELS */
    .barChart_h tbody ol.y-axis-labels{
      position: absolute;
      top: auto;
      bottom: 0;
      right: 0;
      left: 0;
      display: flex;                        /* Create Flexbox (Flex-Container). */
      flex-direction: row;
      margin: 0;                            /* New */
      padding: 0;                           /* New */
      list-style: none;                     /* New */
      font-size: 0.9rem;
    }

    .barChart_h tbody ol.y-axis-labels li.zero{
      position: absolute;                   /* Remove 0 from Flexbox flow. */ /* New */
      left: 0;
      right: auto;
      bottom: auto;
      top: 0;
    }

    .barChart_h tbody ol.y-axis-labels li.zero b{
      transform: translate(-50%, 0%);
    }

    .barChart_h tbody ol.y-axis-labels li{
      flex: 1 0 0;
      text-align: right;
    }

    .barChart_h tbody ol.y-axis-labels li b{
      display: inline-flex;
      transform: translate(50%, 0%);
      font-weight: normal;
    }
    
    
    /**************************************************************************/
    /* MOBILE                                                                 */
    /**************************************************************************/
    @media screen and (max-width: 414px){
      body{
        font-size: 0.8em;
      }
      
      /* CAPTION */
      .barChart_h caption{
        font-size: 1rem;
      }

      /* FIGURE */
      figure{
        margin: 0;
        padding: 1rem;
      }
      
      /* TD */
      .barChart_h tbody td{
        width: 60%;
      }
    }
        
  </style>
</head>

<!-- *************************  HTML BODY  ********************************* -->
<body>
  <figure>
    <table class="barChart_h">
      <caption>Q1: My waiter provided good service tonight.</caption>

      <tbody>
        <!-- Y-axis -->
        <tr>
          <th class="blankCell"></th>
          <th class="y-axis">
            <div class="y-axis-title"># of Responses</div>
            <ol class="y-axis-labels">
              <li class="zero"><b>0</b></li>
              <li><b>20</b></li>
              <li><b>40</b></li>
              <li><b>60</b></li>
              <li><b>80</b></li>
              <li><b>100</b></li>
            </ol>
          </th>
        </tr>
        
        <!-- Data Rows -->
        <tr class="firstRow">
          <th scope="row">Stongly Disagree:</th>
          <td><span style="width:10%"><b>10</b></span></td>
        </tr>
        <tr>
          <th scope="row">Disagree:</th>
          <td><span style="width:30%"><b>30</b></span></td>
        </tr>
        <tr>
          <th scope="row">Neither:</th>
          <td><span style="width:20%"><b>20</b></span></td>
        </tr>
        <tr>
          <th scope="row">Strongly Agree:</th>
          <td><span style="width:70%"><b>70</b></span></td>
        </tr>
        <tr class="lastRow">
          <th scope="row">Strongly Agree:</th>
          <td><span style="width:40%"><b>40</b></span></td>
        </tr>
        
      </tbody>

    </table>
  </figure>
  
</body>
</html>

I have some follow-up questions…

Q1.) Why is it that when I hover over <table> in Firefox Dev Tools, it is expanded to the full-width of <figure>, but when I hover over <table> <tbody> <tr> and more specifically <table> <tbody> <tr> <th class="y-axis">, it seems stuck width-wise?

Furthermore, if I remove the style with: 25%;[code] from [code]<tr> <th class="blankCell"> then everything scrunches up?!

I would like the X-axis labels to be 25% wide, and thus the bar area should be 75% wide.


Q2.) Do I need .barChart_h{ height: auto?


Q3.) Would it be okay to put the Y-axis on the botom of the bar chart like you would traditionally see, or is it better to leave it on top so that mobile users can see the Y-axis without needing to scroll down? (I was able to figure out how to put the Y-axis in ither place, but am unsure of which is better UI design.)


Q4.) Would it be more semantically correct to put the Y-axis in <thead> and leave the bars in <tbody>?

Thanks.

Most of the problems stem from the fact that you are setting some table components to display:block and some left to their default. That means that elements like a tr are still display:table-row and effectively become anonymous tables and tables by default are shrink to fit and have only content width. That’s why when you remove that width the element scrunches up.

As I mentioned in the previous thread that for a vertical chart like this with two cells you don’t need all the trickery that was employed to turn a horizontal chart into a vertical one. For this ‘use case’ you can use the standard table behaviour as you simply have two horizontal cells and the table components can remain exactly as they were designed.

I quickly removed all the display blocks from your code so that we get a natural table display and pasted into this codepen:

The elements now behave naturally and take up their full width of the parent. I added a few comments in the code but did not fully document as I’m short of time today.

It certainly looks like a header row to me as it defines the vertical content below. Table semantics can be difficult to achieve or understand when doing complex layouts. The main thing to ask is whether this column header refers to the whole column underneath. If so then it is clearly a table header.

No that looks unnecessary to me.

1 Like

@PaulOB

Thanks for the code tweaks and the comments as well - things look much better now.

Not to split hairs, but the proportions seem to be off a bit.

For example, in Firefox Dev Tools, Responsive Design Mode, if I remove the .barChart_h tbody th.blankCell{ width: 25%;} on the iPhone 6/7/8 Plus, tbody comes in at 606px but .blankCell is 312.933px and .y-axis is 293.067px.

Likewise, with the 25% in place, the bar portion looks like maybe 2 1/2 times as wide versus the 3 times you’d expect.

Not a major issue, but sort of trading one problem for another. :wink:

Widths on table-cells are only a suggestion in the default auto layout mode and ultimately its the content that will decide how wide the cells will be when push comes to shove.

If you want an exact percentage or px width then you need the fixed algorithm instead.

i.e.

.barChart_h {
  table-layout: fixed; /* enforce cell widths*/
}

That now means that the width will be honoured but should data not fit in that width the cell will overflow unlike the auto algorithm. Therefore be sure than any long words can break and not overflow the cell.

1 Like

@PaulOB ,

Thanks for all of the help!! :slight_smile:

1 Like