I have a CSS grid element and one of its cells contains some preformatted code (<pre><code>...</code></pre>
). This code can have some long lines and smaller screen widths may not be able to fit the whole line. However, I don’t want to line break because this could mess up certain code like:
console.log('test'); // Log the me
ssage
<pre>
and <code>
do this automatically, however they do not apply a scrollbar. Instead the content overflows the parent. To address this, I tried adding pre { overflow-x: auto; }
, which can show a scrollbar. However this still stretches any parent grid cell to fit the underlying long <code>
block, even if it is contained in a fixed width <pre>
tag which makes no sense to me.
See this minimal example where I reproduced this issue.
#grid {
display: grid;
/* Should split the page in half. */
grid-template: 'left right' 100% / 1fr 1fr;
}
#left {
grid-area: left;
}
#right {
grid-area: right;
}
pre {
/* For long content, horizontal scrollbar should appear. */
overflow-x: auto;
/* If we forcibly shorten `pre` width, the scrollbar appears, but grid is still stretched by the `code` element. */
/* width: 50%; */
}
#tall {
border: 1px solid red;
height: 1000px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Home</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="grid">
<div id="left">
<div>Some content on the left.</div>
<div>This should scroll with the single vertical scrollbar. </div>
</div>
<div id="right">
<pre>
<code>Some very very very very very very very very very very very very very very very very very very very long code content.</code>
</pre>
<div id="tall">Some very tall content</div>
</div>
</div>
</body>
</html>
I tried a minimal example of a grid with two columns of equal sizing. One of those columns contains a long <code>
tag. This is fine for screens large enough to fit the content, but for smaller screen sizes the <code>
tag stretches its cell beyond its half of the screen and the grid grows beyond its container as well and forces a horizontal scrollbar on the grid. Even if I force overflow-x: auto;
on the <pre>
, the <code>
tag still stretches the grid as long as it needs. The other column is shrunk to compensate and almost the entire width is dominated by this one <code>
tag. This is very undesirable but I can’t find a way to fix it. What I want in this example is for the grid to always be split 50/50 between the two columns. If the screen is too small to fit the long <code>
text, then a horizontal scrollbar should appear on the <pre>
or <code>
tag and the grid should always match the viewport width and never have a scrollbar.
This image hopefully demonstrates what happens when the viewport width is too small. The two column layout is no longer split down the middle and a scrollbar appears at the bottom of the grid (the two red marks). Instead I’m trying to keep the split in the middle of the grid and have the scrollbar only under the code content in the right column.
Inspect element shows me that the <pre>
tag is sized correctly. If I apply width: 50%;
to it, I can see that it is smaller than its containing grid cell. However the <code>
tag always extends to its full line length. I think that’s kind of expected, however I would think the overflow-x: auto;
on the <pre>
tag should avoid that length pushing on the grid cell. Am I missing something here? I’ve tried fiddling with width
, max-width
, overflow-x
, and white-space
in as many ways as I can figure out.
I’ve also managed to reproduce this with flexbox, so I don’t think it’s specific to grid. Is this some quirk with <code>
, <pre>
, or white-space: pre;
?
2
Answers
While updating the example to address Brett's answer, I managed to accidentally solve this problem, though I still don't understand how.
The trick is to add
#right { overflow-y: auto; }
.This doesn't really make sense to me though because
#right
isn't scrollable (and shouldn't be). No matter how small the viewport, or how big its content,#right
never gets a scrollbar. Instead the<html>
tag gets the scrollbar. I was really trying to put the scrollbar on#grid
, but in this particular case the grid is the whole page so it really doesn't matter for me even if it wasn't exactly what I was trying to do. This also updates thepre
tag to respect itsoverflow-x: auto;
and get its own horizontal scrollbar.Interestingly,
#grid { overflow-y: auto; }
actually has no effect.overflow
must be set on#right
, even though it triggers a scrollbar on<html>
. I've never seenoverflow
on a child element either:I feel like
#right
is the exact wrong place to putoverflow-y: auto;
, but it appears to be necessary. If anyone can help me understand why this is the case, I would be eternally grateful.Aside: I considered that adding
overflow
to#right
might be necessary to create a new block formatting context (BFC) to get thepre
tag to layout independent of#grid
. However, I don't think this is the case because MDN calls outdisplay: flow-root;
as a non-side effectful way of creating a new BFC. However, attempting to use#right { display: flow-root; }
does not have the intended effect and thepre
tag still don't get its own scrollbar. So I have to assume that means#right { overflow-y: auto; }
is doing something else to fix the problem, I just have no idea what that something is.You need the grid cell to scroll any overflow, not the code. Instead of styling the
<pre>
tag withoverflow: auto
, style the#right
cell with it instead.