Skip to content
Advertisement

Precisely formatting a cast list for narrow browser windows

I need to format a “cast list” (a series of person/role pairs) on a web page in a way that approximates professional-looking results when a line’s text is wider than the available space in the browser window. But I’m not sure if I should be looking at CSS flexbox, grid, JavaScript, or something else, or if any of these are even capable of generating anything close to what I want.

Here are some examples of the same cast list, manually formatted for different widths. (I’m using a fixed-width font here to make it easier to show alignment, but would prefer a method that works with variable-width fonts.)

Wolfgang Amadeus Mozart ................. Court Composer
Antonio Salieri ........ Assistant to the Court Composer
Tim ... Assistant to the Assistant to the Court Composer
John Jacob Jingleheimer Schmidt ................. Intern
John Jacob Jingleheimer Schmidt Jr. ....... Secretary to
                     the Assistant to the Court Composer

Wolfgang Amadeus Mozart ..... Court Composer
Antonio Salieri ..... Assistant to the Court
                                    Composer
Tim ...... Assistant to the Assistant to the
                              Court Composer
John Jacob Jingleheimer Schmidt ..... Intern
John Jacob Jingleheimer
   Schmidt Jr. ............ Secretary to the
             Assistant to the Court Composer

Wolfgang Amadeus Mozart ... Court       ; not ideal, better to break the longer name
                         Composer
Antonio Salieri ....... Assistant
            to the Court Composer
Tim ............ Assistant to the 
  Assistant to the Court Composer
John Jacob Jingleheimer
   Schmidt ............... Intern
John Jacob Jingleheimer
   Schmidt Jr. ..... Secretary to
             the Assistant to the 
                   Court Composer

Wolfgang Amadeus
   Mozart .... Court Composer
Antonio Salieri ... Assistant
        to the Court Composer
Tim ........ Assistant to the
       Assistant to the Court
                     Composer
John Jacob Jingleheimer
   Schmidt ........... Intern
John Jacob Jingleheimer
   Schmidt Jr. .... Secretary
      to the Assistant to the
               Court Composer

One way to express the logic behind this layout scheme might be:

  1. For each person/role pair, try to set the text in a single line. If it’s too wide for that:
  2. Compute the width of the last word in the “person” part, and the first word in the “role” part.
  3. Break the “person” part into multiple lines, so that each line is no wider than 70% of the available width, and the last line is no wider than the width of the first word in the “role” part (plus a constant to account for indenting and dots).
  4. Break the “role” part into multiple lines, so that each line is no wider than 70% of the available width, and the first line is no wider than the width of the last word in the “person” part (plus a constant).
  5. Set the “person” part, with subsequent lines indented.
  6. After the final line of the “person” part, set the first line of the “role” part, flush right, filling in the extra space with dots.
  7. Set the remaining lines of the “role” part, flush right.

Is it possible to get results like the above, using just CSS? Or is it possible to do this kind of thing in JavaScript, measuring words and manually positioning line breaks? Has anyone done anything similar? Any pointers appreciated.

Advertisement

Answer

While you can get almost there with just CSS, there is a need to calculate the indent needed on the second column first line which has to be done by JavaScript.

This snippet has two spans within each of the first column divs containing the text. The first span allows a white background to just the text and the second allows calculation of the end of the actual text. From there the indent required on the first line of the second column can be found.

As the dots are just a visual clue they can be included through a pseudo element, repeated radial gradients making the dots.

function resize() {
  const cast = document.querySelector('.cast');
  const castX = cast.getBoundingClientRect().x;
  const leftCols = document.querySelectorAll('.cast > *:nth-child(odd)');
  leftCols.forEach(leftCol => {
    const lastEl = leftCol.querySelector('*:last-child');
    const x = lastEl.getBoundingClientRect().x + lastEl.getBoundingClientRect().width - castX;
    const rightCol = leftCol.nextElementSibling;
    const indent = x - (cast.getBoundingClientRect().width * 30 / 100);
    rightCol.style.setProperty('--indent', indent + 'px');
  });
}
window.onload = resize;
window.onresize = resize;
.cast {
  width: 50vw;
  position: relative;
  --lineheight: 1.1em;
  overflow: hidden;
}

.cast>* {
  max-width: 70%;
  margin: 0;
  padding: 0;
  line-height: var(--lineheight);
}

.cast> :nth-child(odd) {
  text-indent: -2em;
  padding-left: 2em;
  text-align: left;
}

.cast>*> :first-child {
  background: white;
}

.cast> :nth-child(odd) :first-child {
  position: relative;
}

.cast> :nth-child(odd) :first-child::after {
  width: 100vw;
  height: var(--lineheight);
  content: '';
  position: absolute;
  bottom: 0;
  z-index: -1;
  background-image: radial-gradient(gray 0 20%, transparent 20% 100%);
  background-repeat: repeat no-repeat;
  background-size: calc(var(--lineheight) / 2) calc(var(--lineheight) / 2);
  background-position: 0 bottom;
}

.cast> :nth-child(even) {
  --indent: 6em;
  text-align: right;
  position: relative;
  left: 30%;
  text-indent: var(--indent);
  margin-top: calc(-1 * var(--lineheight));
}
<div class="cast">
  <div><span>Wolfgang Amadeus Mozart</span><span></span></div>
  <div><span>Court Composer</span></div>
  <div><span>Antonio Salieri</span><span></span></div>
  <div><span>Assistant to the Court Composer</span></div>
  <div><span>Tim</span><span></span></div>
  <div><span>Assistant to the Assistant to the Court Composer</span></div>
  <div><span>John Jacob Jingleheimer Schmidt</span><span></span></div>
  <div><span>Intern</span></div>
  <div><span>John Jacob Jingleheimer Schmidt Jr.</span><span></span></div>
  <div><span>Secretary to the Assistant to the Court Composer</span></div>
</div>
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement