Search code examples
htmlcssflexboxcss-floatresponsive

A way to float inline elements *below* their container


I'm building a template for writing some technical documentation. I'd love to do this with as little Javascript as possible. I am including a minimal example

When the screen is wide, it floats references in span tags to the left margin. When the screen gets narrow (e.g., on mobile devices), there's not enough digital real estate to display a left margin, and I would like to display the references immediately after the paragraph (or, perhaps, at the end of the <section>). I have seen other people suggest doing this with some absolute positioning, but I don't think that works in my case, since there might be multiple references in a single paragraph. Another option that I've seen presented is to put all of the margin notes for a paragraph in a single <aside> block that gets floated to the margin. For various reasons, I'd love to be able to put the span tag as close as possible to the text that it refers to. In a perfect world, I want to keep the html as semantic as possible, so I don't want to wrap each paragraph in another div tag or table tag.

One near solution that I've been able to do (shown in the Codepen example) is to display the paragraph as a flexbox, and change the order of the reference spans so that they display after the main text. This almost works, except that by changing the paragraph to display as a flexbox, every continuous line of text between a span gets rendered as a block, which creates awkward line breaks. I've tried a number of CSS tricks to try to get the remaining text to wrap as it normally would if there were no span tags, but I'm beginning to be convinced that this is not supported (perhaps with some clever CSS selectors display: content)

article {
    margin-left: 25%;
    padding: 5px;
    
}

main {
    margin-left: auto;
    margin-right: auto;
    max-width: 800px;
    flex-wrap:wrap;
    
}

section {
    margin-top: 40px;
}

h1 {
  width: calc(125% - 10px);
  margin-left: -25%;
  text-align: center;
}

h2 {
  width: calc(125%);
  margin-left: calc(-33%);
  border-top: 1px solid gray;
  padding-top: 10px;
  padding-left: 40px;
}

span.ref {
  float: left;
  clear: both;
  width: calc(33% - 8px);
  margin-left: -33%;
  hyphens: auto;
  font-size: 14px;
  color: #666;
  text-align: left;
}

@media only screen and (max-width: 500px) {
  
  article {
    margin-left: 0px;
  }
  
  h1, h2 {
    margin-left: 0;
    padding-left: 0;
    width: 100%;
  }
  
  p {
    display: flex;
    flex-direction: column;
  }
  
  span.ref {
    order: 2;
    float: none;
    margin-left: 0px;
  }
  
 p > span.ref:first-of-type {margin-top: 10px;}
  
  
}
<body>
    <main>
        <article>
            <h1>Document Title</h1>
            <section>
                <h2>First section of the document</h2>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras fringilla porta est nec rhoncus. Vestibulum ultricies molestie ante, vel fringilla turpis tempor at. Maecenas eu leo justo. Aliquam scelerisque metus in massa hendrerit [1], <span class="ref">[1] Wikipedia. </span>non condimentum augue pharetra. Etiam in lorem eu nibh sagittis [2] <span class="ref">[2] A fancy textbook</span> lacinia sit amet eu odio. Quisque ultricies lacinia dignissim. Suspendisse blandit urna justo, et blandit metus tincidunt at. Quisque vel varius enim.
              </p>
              <p>
                Aenean cursus vestibulum libero, eu vulputate nisi efficitur sit amet. Praesent quis metus ac leo eleifend tempus. Praesent ac mauris eleifend, luctus ligula vel, interdum lectus. Donec iaculis in est non iaculis. Nullam vulputate, lorem quis placerat condimentum, risus lectus feugiat neque, id auctor augue elit ut enim. Donec vel rutrum massa. Nunc laoreet nulla et eleifend facilisis. Morbi scelerisque velit sit amet libero viverra mattis. Vivamus non porta neque, et rhoncus mi. In lectus nisl, faucibus nec sem sed, ullamcorper commodo ante. Aliquam ac massa imperdiet, viverra lacus eget, convallis ex. Phasellus tempus lacinia enim id tincidunt.
              </p>
            </section>
          <section>
            <h2>A second section</h2>
          </section>
        </article>
    </main>
</body>


Solution

  • I had another crack and was able to get it working with a single version (with some caveats). No need to duplicate the references or adjust the base HTML at all.

    Note: I'm using :has selector that is not currently supported in FireFox. How do you enable :has() selector on Firefox Firefox enable :has() selector

    Spans need to be 'yanked' right out of their default inline spot with position: absolute to have the residual paragraph text move up where the reference spans were previously.

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>References in left column for desktop. References below paragraphs for mobile.</title>
    <style>
    
    :root {
      --main-max-width:         800px;
      --citation-col-width:     180px;
      --span-reference-top:     1.3rem;
    }
    
    html {
      font-size: 16px;
    }
    
    body {
      margin: 0;
      padding-top: 1rem;
      padding-bottom: 1rem;
      font-family: sans-serif;
      font-size: 1rem;
      line-height: 1.35;
      background: #fafafa;
      color: #1a1a1a;
    }
    
    article {
      padding: 1rem;
      background: #fff;
      box-shadow: 0 2px 4px rgba(0,0,0,.2); 
    }
    
    main {
      margin-right: auto;  
      margin-left: auto;
      max-width: var(--main-max-width);
    }
    
    section.grid-2-col {
      display: grid;
      /* 2 COLUMNS: FIXED PX WIDTH THEN RESIDUAL IS TAKEN UP */
      grid-template-columns: var(--citation-col-width) 1fr;
      grid-column-gap: 1rem;
      margin-top: 2.5rem;
    }
    
    section.grid-2-col h2 {
      grid-column: span 2;
    }
    
    section.grid-2-col p {
      /* PARAS START IN GRID 2ND COLUMN */
      grid-column-start: 2;
      margin-top: 0;
      margin-bottom: .7rem;
      position: relative;
    }
    
    section.grid-2-col span.ref {
      /* HAD TO POSITION ABSOLUTE TO HIDE OVERFLOWING REFERENCES */
      position: absolute;
      display: block;
      max-width: var(--citation-col-width);
      /* NEGATIVE VALUE (* -1), -CITATION COLUMN WIDTH - COL GAP */
      left: calc((var(--citation-col-width) * -1) - 1rem);
      /* WHEN WRAPPING SPANS OVERLAPPED */
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      font-size: .8rem;
      color: #666;
    }
    
    /* UP TO 4 REFERENCES PER PARA (ADD MORE IF REQUIRED) */
    section.grid-2-col span.ref:nth-of-type(1) {
      top: 0;
    }
    
    section.grid-2-col span.ref:nth-of-type(2) {
      top: var(--span-reference-top);
    }
    
    section.grid-2-col span.ref:nth-of-type(3) {
      top: calc(var(--span-reference-top) * 2);
    }
    
    section.grid-2-col span.ref:nth-of-type(4) {
      top: calc(var(--span-reference-top) * 3);
    }
    
    h1, h2 {
      font-weight: 400;
      margin-top: 0;
      margin-bottom: .8rem;
    }
    
    h1 {
      font-size: 2.8rem;
      color: #9d9d9d;
      border-bottom: 1px solid #9d9d9d;
      margin-bottom: 2.5rem;
    }
    
    h2 {
      font-size: 1.5rem;
    }
    
    @media only screen and (max-width: 799px) {
      article {
    padding-right: .5rem;
    padding-left: .5rem;
      }
    
      section.grid-2-col {
    /* DON'T NEED GRID NOW */
    display: block;
      }
      
      section.grid-2-col span.ref {
    left: unset;
    max-width: 90vw;
      }
    
      /* UP TO 4 REFERENCES PER PARA (ADD MORE IF REQUIRED) */
      section.grid-2-col span.ref:nth-of-type(1) {
    top: 100%;
      }
    
      section.grid-2-col span.ref:nth-of-type(2) {
    top: calc(100% + var(--span-reference-top));
      }
    
      section.grid-2-col span.ref:nth-of-type(3) {
    top: calc(100% + var(--span-reference-top) * 2);
      }
    
      section.grid-2-col span.ref:nth-of-type(4) {
    top: calc(100% + var(--span-reference-top) * 3);
      }
    
      /* ALLOW DYNAMIC SPACE FOR ABSOLUTE REFERENCES */
      section.grid-2-col p:has(span.ref:nth-of-type(1)) {
    border-bottom: solid var(--span-reference-top) transparent;
      }
    
      section.grid-2-col p:has(span.ref:nth-of-type(2)) {
    border-bottom: solid calc(var(--span-reference-top) * 2) transparent;
      }
    
      section.grid-2-col p:has(span.ref:nth-of-type(3)) {
    border-bottom: solid calc(var(--span-reference-top) * 3) transparent;
      }
    
      section.grid-2-col p:has(span.ref:nth-of-type(4)) {
    border-bottom: solid calc(var(--span-reference-top) * 4) transparent;
      }
    }
    </style>
    </head>
    <body>
    <main>
        <article>
            <h1>Document title</h1>
            <section class="grid-2-col">
              <h2>First section of the document</h2>
              <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras fringilla porta est nec rhoncus. Vestibulum ultricies molestie ante, vel fringilla turpis tempor at. Maecenas eu leo justo. Aliquam scelerisque metus in massa hendrerit [1], <span class="ref">[1] Wikipedia, truncated long text length to reference</span>. Etiam [2] <span class="ref">[2] A fancy textbook</span> lacinia. Quisque [3] <span class="ref">[3] Another less fancy manuscript reference</span> ultricies lacinia dignissim. Suspendisse [4] <span class="ref">[4] Another far less fancy reference</span> et blandit metus tincidunt at. Quisque vel varius enim.</p>
              <p>Aenean cursus vestibulum libero, eu vulputate nisi efficitur sit amet. Praesent quis metus ac leo eleifend tempus. Praesent ac mauris eleifend, luctus ligula vel, interdum lectus. Donec iaculis in est non iaculis. Nullam vulputate, lorem quis placerat condimentum, risus lectus feugiat neque, id auctor augue elit ut enim. Donec vel rutrum massa. Nunc laoreet nulla et eleifend facilisis. Morbi scelerisque velit sit amet libero viverra mattis. Vivamus non porta neque, et rhoncus mi. In lectus nisl, faucibus nec sem sed, ullamcorper commodo ante. Aliquam ac massa imperdiet, viverra lacus eget, convallis ex. Phasellus tempus lacinia enim id tincidunt.</p>
            </section>
            <section class="grid-2-col">
              <h2>Second section of the document</h2>
              <p>Aenean cursus vestibulum libero, eu vulputate nisi efficitur sit amet. Praesent quis metus ac leo eleifend tempus. Praesent ac mauris eleifend, luctus ligula vel, interdum lectus. Donec iaculis in est non iaculis. Nullam vulputate, lorem quis placerat condimentum, risus lectus feugiat neque, id auctor augue elit ut enim. Donec vel rutrum massa. Nunc laoreet nulla et eleifend facilisis. Morbi scelerisque velit sit amet libero viverra mattis. Vivamus non porta neque, et rhoncus mi. In lectus nisl, faucibus nec sem sed, ullamcorper ex. Phasellus tempus lacinia enim id tincidunt.Aenean cursus vestibulum libero, eu vulputate nisi efficitur sit amet. Praesent quis metus ac leo eleifend tempus. Praesent ac mauris eleifend, luctus ligula vel, interdum lectus. Donec iaculis in est non iaculis. Nullam vulputate, lorem quis placerat condimentum, risus lectus feugiat neque, id auctor augue elit ut enim. Donec vel rutrum massa. Nunc laoreet nulla et eleifend facilisis. Morbi scelerisque velit [3] <span class="ref">[3] ChatGPT. </span> sit amet libero viverra mattis. Vivamus non porta neque, et rhoncus mi. In lectus nisl, faucibus nec sem sed, ullamcorper commodo ante. Aliquam ac massa imperdiet, viverra [4] <span class="ref">[4] Stack Overflow. </span> lacus eget, convallis ex. Phasellus tempus lacinia enim id tincidunt.Aenean cursus vestibulum libero, eu vulputate nisi efficitur sit amet. Praesent quis metus ac leo eleifend tempus. Praesent ac mauris eleifend, luctus ligula vel, [5] <span class="ref">[5] Bing. </span> interdum lectus. Donec iaculis in est non iaculis. Nullam vulputate, lorem quis placerat condimentum, risus lectus feugiat neque, id auctor augue elit ut enim. Donec vel rutrum massa. Nunc laoreet nulla et eleifend facilisis. Morbi scelerisque velit sit amet libero viverra mattis. Vivamus non porta neque, et rhoncus mi. In lectus nisl, faucibus nec sem sed, ullamcorper commodo ante. Aliquam ac massa imperdiet, viverra lacus eget, convallis ex. Phasellus tempus lacinia enim id tincidunt.</p>
            </section>
        </article>
    </main>
    </body>
    </html>