Two Different Jobs for One Element
The <code> element is an inline element with a default monospace font. That embedded-in-sentence role is the only job its defaults address, yet it is almost always called upon to do a second job: render a full block of code. Marking up a code block properly means using a <pre> wrapper, because <pre> is what honors whitespace. But while <code> is the semantically correct child element for that listing, it is still just an inline element by default. And visually, what looks right inline rarely looks right inside a block.
That split means the element carries a dual identity in CSS: one when it appears in running text, and another when it appears inside <pre>. It is an unusual situation, because the markup structure you use to get a code block is not identical to how that block looks or behaves.
Scoping Inline Styles vs. Block Styles
A common approach is styling the tag itself and scoping those styles within text elements, like a post or an article container:
/* For all <code> */
code {
font-family: MyFancyCustomFont, monospace;
font-size: inherit;
}
/* Code in text */
p > code,
li > code,
dd > code,
td > code {
background: #ffeff0;
word-wrap: break-word;
box-decoration-break: clone;
padding: .1rem .3rem .2rem;
border-radius: .2rem;
}
That scoping matters because the same <code> element can end up inside an <h3> heading, for example. In that case, the heading-styles of the context should take over; the inline code styling behind a heading should only affect the font itself, not the background and padding used for code inside paragraphs.
<h3>The <code>.cool</code> Class</h3>

The :not(pre) Catch
A common alternative is trying to keep block-level code unstyled by targeting the inline cases only, using a selector like :not(pre). The idea is to match the <code> elements that sit outside <pre> blocks:
/* this was working */
.post :not(pre) code {
}
/* and this was not */
:not(pre) code {
}
The trap is that :not(pre) matches ancestors too. It matches the <body> element, and everything else that is not a <pre>, so the style applies in unintended places. The correct place to scope is from a container like the post itself, which catches the <code> elements that are within paragraphs, headings, images and other inline contexts.
Protecting Block Styles
For sites that use lots of code blocks, it is safer to take the opposite route: aggressively style the <code> elements nested inside a <pre>, rather than trying to exclude them from general selectors. That way, the block-specific styles are guaranteed and anything else can be reset:
pre code {
display: block;
background: none;
white-space: pre;
-webkit-overflow-scrolling: touch;
overflow-x: scroll;
max-width: 100%;
min-width: 100px;
padding: 0;
}
That kind of snippet is not especially clever. It just acknowledges that a <code> inside a <pre> has to fight against many other styles to end up looking how you expect. Overriding possible undesirable styling—whether from the element itself, its ancestor elements, or reset rules—is the practical way to ensure code blocks render correctly.
The core challenge is that <code> is among the rare elements where the styling needs depend heavily on where the element sits, so both markup structure and content context play a role in getting that styling right.



