<div class="wrapper">
<h1>I am the H1</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati, dolorem
animi iure neque nesciunt quisquam adipisci harum! Mollitia numquam
architecto veritatis, tenetur molestiae maiores asperiores libero hic veniam
dolores adipisci?
</p>
<ul>
<li>I am a list item</li>
<li>I am a list item</li>
<li>
I am a list item
<ul>
<li>I am a nested list item</li>
<li>I am a nested list item</li>
</ul>
</li>
<li>I am a list item</li>
</ul>
<h2>H2. Big gap above / small gap below</h2>
<h3>H3. Small gap above / small gap below</h3>
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Magnam cum
corporis voluptatum quisquam commodi fugit vero, est amet, dolores delectus
fuga natus porro quo nihil illo ad modi minima saepe.
</p>
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Magnam cum
corporis voluptatum quisquam commodi fugit vero, est amet, dolores delectus
fuga natus porro quo nihil illo ad modi minima saepe.
</p>
<h4>H4. Big gap above / small gap below</h4>
<h5>H5. Small gap above / small gap below</h5>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Minus doloremque,
velit aliquid temporibus sapiente, nisi distinctio sequi quod ipsum
reprehenderit delectus minima exercitationem, amet ut dolorem voluptatum ea
ratione accusantium!
</p>
<h5>H5. Big gap above / small gap below</h5>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ratione quia at qui
corrupti blanditiis aspernatur beatae ex, accusamus saepe? Officiis non vel
omnis dolorem itaque praesentium excepturi, iure sunt est!
</p>
<h2>H2. Small gap above / small gap below</h2>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illo, facere.
Dolorem ab debitis neque quam. Maiores, culpa delectus dignissimos accusamus
quos nihil! Ullam cum pariatur consectetur reprehenderit tenetur libero
deserunt.
</p>
<p>No gap below because I am the last longform text element.</p>
</div>
:root {
--space-default: 1rem;
--space-l1-lg: 4rem;
--space-l2-lg: 3rem;
--space-l3-lg: 2.5rem;
--space-l4-lg: 2rem;
--space-l5-lg: 1.5rem;
--space-l6-lg: 1rem;
--space-l1-sm: calc(var(--space-l1-lg) / 4);
--space-l2-sm: calc(var(--space-l2-lg) / 4);
--space-l3-sm: calc(var(--space-l3-lg) / 4);
--space-l4-sm: calc(var(--space-l4-lg) / 4);
--space-l5-sm: calc(var(--space-l5-lg) / 4);
--space-l6-sm: calc(var(--space-l6-lg) / 4);
--space-list-items: 0.5rem; // just for list items
}
// Bare-bones reset
* {
margin: 0;
}
// By default, every prose element has a bottom margin.
// `li` is excluded because list item spacing is handled later on.
// Same with headings because they have their own, more specific spacing values.
// Feel free to add other elements here e.g. blockquote.
:is(p, ul, ol) {
margin-bottom: var(--space-default);
}
// Headings have a little more bottom spacing than other prose elements
h1 {
margin-bottom: var(--space-l1-sm);
}
h2 {
margin-bottom: var(--space-l2-sm);
}
h3 {
margin-bottom: var(--space-l3-sm);
}
h4 {
margin-bottom: var(--space-l4-sm);
}
h5 {
margin-bottom: var(--space-l5-sm);
}
h6 {
margin-bottom: var(--space-l6-sm);
}
// Non-heading elements should have a big gap after them if followed by a heading.
// Star selector means that a bunch of stuff should "just work",
// e.g. if you insert images or other elements between text nodes.
// But you could change the `*` to `:is(h1,h2,h3,h4,h5,h6)` if you don't want that.
*:has(+ h2) {
margin-bottom: var(--space-l2-lg);
}
*:has(+ h3) {
margin-bottom: var(--space-l3-lg);
}
*:has(+ h4) {
margin-bottom: var(--space-l4-lg);
}
*:has(+ h5) {
margin-bottom: var(--space-l5-lg);
}
*:has(+ h6) {
margin-bottom: var(--space-l6-lg);
}
// Headings followed immediately by a heading of a level down
// should have only a small gap below.
// Using :where() for consistent specificity.
:where(h1):has(+ h2) {
margin-bottom: var(--space-l1-sm);
}
:where(h2):has(+ h3) {
margin-bottom: var(--space-l2-sm);
}
:where(h3):has(+ h4) {
margin-bottom: var(--space-l3-sm);
}
:where(h4):has(+ h5) {
margin-bottom: var(--space-l4-sm);
}
:where(h5):has(+ h6) {
margin-bottom: var(--space-l5-sm);
}
// List item spacing.
:where(li):has(+ li) {
margin-bottom: var(--space-list-items);
}
// Nested list spacing.
// This is the only place we need to use margin-top :'(
:where(li) :is(ul, ol) {
margin-top: var(--space-list-items);
}
// Remove the bottom margin on the last prose element
// (i.e. if it has no next sibling).
// Could there be an argument to apply this to ALL elements (*) with no next sibling?
:is(p, ul, ol, h1, h2, h3, h4, h5, h6):not(:has(+ *)) {
margin-bottom: 0;
}
// END
// Unrelated stuff
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
line-height: calc(0.75rem + 1em);
padding: 40px;
}
.wrapper {
max-width: 75ch;
margin: 0 auto;
border-block: 2px solid red; // to test vertical space around text
}
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.