Search code examples
htmlcsswcag

DL element with DT / DD as equal column widths on one row


So for specific reasons I'm trying to use the dl (Description List) html element. I wanted the elements contents to display each dt and dd on their own row which I accomplished as such..

div {
  padding: 1rem;
  color: #fff;
  background-color: rgba(0,0,0,.8);
}
  
dl dt, dl dd {
  display: inline;
  line-height: 1.75rem;
}
<div>
  <h3>Cryptids of Cornwall:</h3>
  <dl>
      <dt>Beast of Bodmin</dt>
      <dd>A large feline inhabiting Bodmin Moor.<br></dd>

      <dt>Morgawr</dt>
      <dd>A sea serpent.<br></dd>

      <dt>Owlman</dt>
      <dd>A giant owl-like creature.<br></dd>
  </dl>
</div>

Which is really close to what I'm after, but I'm wondering if anyone knows a way with maybe Flex or Grid for a more uniformed layout like a table while still using the dl the element so the output is more like this...

table {
  border-collapse: collapse;
  color: #fff;
  background-color: rgba(0,0,0,.8);
}

table th {
  text-align: left;
  padding-left: 1rem;
}

table td {
  padding: 1rem;
}

table tr td:first-of-type {
  text-align: right;
  border-right: lightgray 1px solid;
}
<table>
  <tr>
    <th colspan="2">
      <h3>Cryptids of Cornwall:</h3>
    </th>
  </tr>
  <tr>
      <td>Beast of Bodmin</td>
      <td>A large feline inhabiting Bodmin Moor.</td>
  </tr>
  <tr>
      <td>Morgawr</td>
      <td>A sea serpent.</td>
  </tr>
  <tr>
      <td>Owlman</td>
      <td>A giant owl-like creature.</td>
  </tr>
</table>

Is there a way to accomplish this with css and explicitly using the dl element? I've tried things display: table on the parent dl with the dt and dd as display: table-cell but then I can't seem to break the into new rows after the dd along with failed attempts at using flex and css grid so wondering if someone knows a trick to teach? Thanks!


Solution

  • Here's a quick-and-dirty display: grid; implementation:

    • The vertical tracks ("columns") are 1fr 1fr, so they're both 50% wide. I'm unsure of the best approach to make the tracks sized-to-content.
      • I ultimately got this working by simply doing grid-template-columns: auto auto;, phew!
    • There's no white border between the vertical tracks.
      • I did try to hack it by using dl::before (making it 1px wide with a border: 1px solid white but it seems like it's currently impossible to make a grid item span all horizontal tracks when there are implicit tracks.
      • UPDATE: After being inspired by this post, I was able to hack a vertical line by using an absolutely-positioned ::after element (in the <dt> so it has the correct horizontal position without an explicit left or right value).
    • To my surprise, it does actually work very well when a <dt> element has multiple <dd> siblings (I've added one for the Mermaid of Zennor as an example). I was originally apprehensive that multiple <dd> elements would break the layout.
    • BTW, you don't need the <br /> breaks in your HTML. I've removed them in my example below.

    div {
      padding: 1rem;
      color: #fff;
      background-color: #333;
    }
      
    dl, dt, dd {
      margin: 0;
      padding: 0;
    }
      
    dl {
      color: #eee;
      background-color: #666;
      border-radius: 5px;
      padding: 1rem;
      
      display: grid;
      grid-template-columns: 1fr 1fr;
      grid-template-columns: auto auto;
      grid-template-columns: auto 5px auto;
      grid-template-rows: auto [last-line];
      grid-auto-rows: min-content;
      
      grid-gap: 1rem;
      
      /* This is to allow the `dl > dt:first-child::after` to fill the height */
      position: relative;
    }
    
    /* Using this to make a vertical line won't work, see https://stackoverflow.com/questions/44052336/make-a-grid-item-span-to-the-last-row-column-in-implicit-grid
    dl::before {
      content: '';
      display: block;
      grid-column-start: 2;
      grid-column-end: 3;
      grid-row-start: 1;
      grid-row-end: -1;
      grid-row-end: last-line;
      
      background-color: red;
      border-left: 1px solid white;
      width: 1px;
    }
    */
    
    dl > dt:first-child::after {
      content: '';
      display: inline;
      border-left: 1px solid white;
      width: 1px;
      
      position: absolute;
      top: 0;
      bottom: 0;
      /* Offset it to the right a bit using margin: */
      margin-left: 1rem;
    }
    
    dl > dt {
      grid-column-start: 1;
      grid-column-end: 2;
    
      /*
      `align-self` is for vertical alignment in a grid cell.
      `justify-self` is for horizontal alignment in a grid cell.
      */
    
      align-self: center; 
      justify-self: end; /* i.e. right-align */
      text-align: right; /* This is needed so narrow-viewports don't break. */
    }
    dl > dd {
      grid-column-start: 2;
      grid-column-end: 3;
      
      grid-column-start: 3;
      grid-column-end: 4;
    
      align-self: center; 
      justify-self: start; /* i.e. left-align */
    }
    <div>
      <h3>Cryptids of Cornwall:</h3>
      <dl>
          
          <dt>Beast of Bodmin</dt>
          <dd>A large feline inhabiting Bodmin Moor.</dd>
    
          <dt>Morgawr</dt>
          <dd>A sea serpent.</dd>
    
          <dt>Mermaid of Zennor</dt>
          <dd>A popular Cornish folk tale</dd>
          <dd>The parishioners at St. Senara's commemorated the story by having one end of a bench carved in the shape of a mermaid</dd>
    
          <dt>Owlman</dt>
          <dd>A giant owl-like creature.</dd>
    
          
      </dl>
    </div>


    Screenshot proof:

    It looks good at a variety of viewport sizes:

    Very narrow:

    enter image description here

    Normal

    enter image description here

    Very wide

    enter image description here