Dale
Dale

Reputation: 669

Constrain body text width while allowing pre elements to be wider than their parent if necessary

I have a blog article with code samples. For readability, the article's text is constrained to a certain maximum width. The code samples, however, may need to be wider than the maximum width of the text. What's the best way to accomplish this with HTML/CSS?

Here's a basic example illustrating the layout I'm trying to achieve as well as my problem.

body {
  max-width: 650px;
  border: 1px solid red;
  margin: auto;
}

pre {
  border: 1px solid green;
  background-color: #eee;
}
<body>
  <p>This is a sufficiently long paragraph.  Lorem ipsum dolor
  sit amet, consectetur adipiscing elit. Quae hic rei publicae
  vulnera inponebat, eadem ille sanabat. Ut id aliis narrare
  gestiant? Cur iustitia laudatur? Nummus in Croesi divitiis
  obscuratur, pars est tamen divitiarum.</p>

  <p>This pre should be as wide as the first paragraph, even
  when the viewport is narrower than 650px:</p>

<pre>small</pre>

  <p>I want this pre to be wider than 650px no matter what the
  viewport size is:</p>

<pre>this pre should be wider than the text above
    <span>wxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxz</span>
</pre>
</body>

Here are the key points:

Here's a screenshot of what I'd like to happen, note the second pre that is wider than its parent:

enter image description here

I got as far as trying pre { display: inline-block; } but this doesn't work in Safari. (The white space is significant to reproducing this Safari problem!)

I feel like a lot of people are having trouble understanding that I'm asking for a good way to make pre blocks wider than their parent when the pre content is wider than the parent.

Alternatively, I'll take any other sane way to have pre blocks behave like min-width: 650px; max-width: fit-content;, while the rest of the content on the page is max-width: 650px;.

Upvotes: 4

Views: 646

Answers (1)

Dale
Dale

Reputation: 669

I ended up more-or-less solving this. The basic approach is not to constrain the width of body, but to constrain all children of body instead, then relax pres. (Feel free to choose a container further down from the body, e.g. your div#main or something.)

Note that I used display: table on pre because inline-block seemed to prevent margin collapsing, which was important to me elsewhere in my real use of this, if not important for this simplified example.

I'm feeling pretty confident that Safari/WebKit has a bug (that Chrome has not!), so using JavaScript was unavoidable.

I think this behaves somewhat less nice on browsers that don't (yet) support max-width: fit-content, but it does something acceptable there.

SO snippet below, but the CSS looks slightly prettier, IMHO, in Sass, which is how I originally wrote this solution: https://codepen.io/anon/pen/rrZVrg

function patchCodeSamplesForWebKit() {
    for (const codeElem of document.querySelectorAll("pre")) {
        if (codeElem.offsetWidth <= codeElem.parentNode.offsetWidth) {
            // No problem with this one.
            continue;
        }
        const treeWalker = document.createTreeWalker(codeElem,
                                                     NodeFilter.SHOW_TEXT);
        let textNode;
        while ((textNode = treeWalker.nextNode())) {
            // console.log("Pre fix considering:", textNode);
            const i = textNode.textContent.indexOf("\n");
            if (i >= 0) {
                // console.log("Splitting a text node for pre fix.");
                textNode.splitText(i + 1);
            }
        }
    }
    // console.log("Completed pre fixes for safari.");
}

if (/\bAppleWebKit\b/.test(navigator.userAgent)
    && !/\bChrom(e|ium)\b/.test(navigator.userAgent))
{
    if (document.readyState !== "loading") {
        patchCodeSamplesForWebKit();
    } else {
        document.addEventListener("DOMContentLoaded",
                                  patchCodeSamplesForWebKit);
    }
    console.log("Configured pre fix for WebKit/Safari.");
}
* {
  box-sizing: border-box;
}

body {
  margin: auto;
}
@media (min-width: 650px) {
  body {
    min-width: 650px;
    max-width: 650px;
    max-width: fit-content;
  }
}

body > * {
  max-width: 650px;
}

body > pre {
  display: table;
  max-width: none;
  min-width: 100%;
}
@media (min-width: 650px) {
  body > pre {
    min-width: 650px;
  }
}

body {
  border: 1px solid red;
}

pre {
  border: 1px solid green;
  background-color: #eee;
}
<body>
  <p>This is a sufficiently long paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quae hic rei publicae vulnera inponebat, eadem ille sanabat. Ut id aliis narrare gestiant? Cur iustitia laudatur? Nummus in Croesi divitiis obscuratur, pars
    est tamen divitiarum.</p>

  <p>This pre should be as wide as the first paragraph, even when the viewport is narrower than 650px:</p>

  <pre>small</pre>

  <p>I want this pre to be wider than 650px no matter what the viewport size is:</p>

  <pre>this pre should be wider than the text above
        <span>wxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxz</span>
    </pre>
</body>

Upvotes: 1

Related Questions