Why people don't read — and what to do about it
The average web reader scans 20–28% of the words on a page (Nielsen Norman Group). They don't read top-to-bottom — they skim headings, bold words, bullets, and images looking for what matters.
So: give them something to skim. Every technique below is a way to pull meaning out of a paragraph and make it land faster.
Chunking — break the wall
The single highest-impact move is breaking long paragraphs into 1–3 sentence chunks. It turns a scary block into a scannable list of ideas.
Show 4–6 strong case studies. Not everything you've ever made.
Each case study needs four things: the problem, your process, the result, and your role.
Be specific about what YOU did if you worked with a team. Hiring managers skim fast.
<div class="chunked"> <p><strong>Show 4–6 strong case studies.</strong> Not everything you've ever made.</p> <p><strong>Each case study needs four things:</strong> the problem, your process, the result, and your role.</p> <p><strong>Be specific about what YOU did</strong> if you worked with a team. Hiring managers skim fast.</p> </div>
.chunked {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-size: 13px;
line-height: 1.55;
color: #111;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.chunked p { margin-bottom: 10px; }
.chunked p:last-child { margin-bottom: 0; }
.chunked strong { color: #111; }
Headings — a table of contents on every page
Headings are where skimmers land. Use them every 100–200 words so a reader can answer "does this section matter to me?" in under 2 seconds.
Before the interview
Research the company. Practice the STAR method.
During
Prepare 3 questions. Dress one notch up.
After
Thank-you email in 24 hours, mention something specific.
<div class="signposted"> <h4>Before the interview</h4> <p>Research the company. Practice the STAR method.</p> <h4>During</h4> <p>Prepare 3 questions. Dress one notch up.</p> <h4>After</h4> <p>Thank-you email in 24 hours, mention something specific.</p> </div>
.signposted {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: #fff;
color: #111;
padding: 22px;
border-radius: 10px;
}
.signposted h4 {
font-size: 13px;
color: #f59e0b;
text-transform: uppercase;
letter-spacing: .08em;
margin: 0 0 4px;
font-weight: 700;
}
.signposted p {
font-size: 13px;
margin: 0 0 10px;
color: #111;
}
.signposted p:last-child { margin-bottom: 0; }
Bulleted lists — the fastest way to say many things
Any time your paragraph contains the words "and", "also", "as well as" more than twice, it's begging to be a list.
- Clear subject line
- One-sentence opener
- Why this role + company
- One line of proof
- Link to portfolio
- Direct ask for next step
<ul class="scannable-list"> <li>Clear subject line</li> <li>One-sentence opener</li> <li>Why this role + company</li> <li>One line of proof</li> <li>Link to portfolio</li> <li>Direct ask for next step</li> </ul>
.scannable-list {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 13px;
line-height: 1.65;
color: #111;
background: #fff;
padding: 22px 22px 22px 44px;
border-radius: 10px;
margin: 0;
}
.scannable-list li { margin-bottom: 4px; }
Icon + text — visual anchors
Pair each item with a relevant icon. The icon becomes the "thumbnail" your eye finds on re-read; the label describes it.
- Clone the repo
- Install dependencies
- Run the dev server
- Open in browser
<!-- Requires phosphor-icons:
<link rel="stylesheet" href="https://unpkg.com/@phosphor-icons/web@2.1.1/src/fill/style.css"> -->
<div class="icon-list">
<div class="item">
<div class="ico"><i class="ph-fill ph-git-branch"></i></div>
<div><strong>Clone the repo</strong><span>git clone <url></span></div>
</div>
<div class="item">
<div class="ico"><i class="ph-fill ph-package"></i></div>
<div><strong>Install dependencies</strong><span>npm install</span></div>
</div>
<div class="item">
<div class="ico"><i class="ph-fill ph-play"></i></div>
<div><strong>Run the dev server</strong><span>npm run dev</span></div>
</div>
<div class="item">
<div class="ico"><i class="ph-fill ph-browser"></i></div>
<div><strong>Open in browser</strong><span>localhost:3000</span></div>
</div>
</div>
.icon-list {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
display: flex;
flex-direction: column;
gap: 10px;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.icon-list .item {
display: flex;
gap: 12px;
align-items: flex-start;
}
.icon-list .ico {
width: 32px;
height: 32px;
border-radius: 8px;
background: rgba(245, 158, 11, .12);
color: #f59e0b;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 1rem;
}
.icon-list .item strong {
display: block;
font-size: 14px;
color: #111;
margin-bottom: 2px;
}
.icon-list .item span {
font-size: 13px;
color: #555;
}
Big numbers — when data is the point
If a sentence contains a strong stat ("80% of users", "3× faster", "$2M saved"), pull the number out and make it 3× the size.
<div class="stats-demo"> <div class="stat"><strong data-target="83">83</strong><span>apply with salary shown</span></div> <div class="stat"><strong data-target="12">12</strong><span>apply without it</span></div> <div class="stat"><strong data-target="7">7</strong><span>higher conversion</span></div> </div>
.stats-demo {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
text-align: center;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.stats-demo .stat strong {
display: block;
font-size: 2.2rem;
font-weight: 900;
background: linear-gradient(135deg, #f59e0b, #ec4899);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
line-height: 1;
}
.stats-demo .stat span {
font-size: 11px;
color: #666;
text-transform: uppercase;
letter-spacing: .08em;
display: block;
margin-top: 4px;
}
// Count-up animation when stats scroll into view
const stats = document.querySelectorAll('.stats-demo .stat strong');
const suffixes = ['%', '%', '×']; // 83%, 12%, 7x
const animateCount = (el, target, suffix) => {
const duration = 1200;
const start = performance.now();
const tick = (now) => {
const p = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased) + suffix;
if (p < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
};
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
stats.forEach((el, i) => {
const target = Number(el.dataset.target);
animateCount(el, target, suffixes[i] || '');
});
io.disconnect();
});
}, { threshold: 0.4 });
if (stats.length) io.observe(stats[0].closest('.stats-demo'));
Callout boxes — the one thing to remember
If a paragraph contains the "big takeaway", promote it into a tinted box with a colored left border. Even speed-readers see those.
There are many approaches to pricing: cost-plus, value-based, competitor-based, tiered, penetration.
<div class="callout-wrap">
<p>There are many approaches to pricing: cost-plus, value-based, competitor-based, tiered, penetration.</p>
<div class="callout-demo">
<strong>⚠ Most common mistake:</strong> charging too little out of fear. Underpricing kills more startups than overpricing.
</div>
</div>
.callout-wrap {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 13px;
color: #111;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.callout-wrap p { margin: 0 0 10px; }
.callout-demo {
padding: 14px 18px;
border-radius: 10px;
background: #fef3c7;
border-left: 4px solid #f59e0b;
margin: 10px 0 0;
font-size: 14px;
color: #78350f;
}
.callout-demo strong { color: #92400e; }
Pull quotes — lift the best sentence
Long articles benefit from a pull quote every few scrolls. It gives skimmers a reason to slow down and read the surrounding section.
The best advice I got was from a magazine editor.
"Treat every paragraph like it's being read standing up on a subway." — Magazine editor, circa 2015
If they can't follow it being jostled, you've lost them.
<div class="quote-wrap">
<p>The best advice I got was from a magazine editor.</p>
<blockquote class="pull-quote">
"Treat every paragraph like it's being read standing up on a subway."
<cite>— Magazine editor, circa 2015</cite>
</blockquote>
<p>If they can't follow it being jostled, you've lost them.</p>
</div>
.quote-wrap {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: #fff;
color: #111;
padding: 22px;
border-radius: 10px;
}
.quote-wrap p {
font-size: 13px;
margin: 0 0 8px;
}
.quote-wrap p:last-child { margin: 6px 0 0; }
.pull-quote {
font-family: Georgia, serif;
font-size: 1.6rem;
font-weight: 600;
line-height: 1.3;
color: #111;
padding: 18px 0 14px 24px;
border-left: 3px solid #f59e0b;
font-style: italic;
margin: 0;
}
.pull-quote cite {
display: block;
margin-top: 10px;
font-size: 12px;
font-style: normal;
text-transform: uppercase;
letter-spacing: .1em;
color: #666;
}
Tables — when you're comparing things
Whenever you find yourself writing "X has Y but Z has W" more than twice, it's a table.
| Tool | Free | Best for |
|---|---|---|
| Google Drive | 15GB | Collaboration |
| Dropbox | 2GB | Cheap paid |
| iCloud | 5GB | Apple users |
| Notion | ∞ | Docs & DBs |
<table class="tbl">
<thead>
<tr><th>Tool</th><th>Free</th><th>Best for</th></tr>
</thead>
<tbody>
<tr><td>Google Drive</td><td>15GB</td><td>Collaboration</td></tr>
<tr><td>Dropbox</td><td>2GB</td><td>Cheap paid</td></tr>
<tr><td>iCloud</td><td>5GB</td><td>Apple users</td></tr>
<tr><td>Notion</td><td>∞</td><td>Docs & DBs</td></tr>
</tbody>
</table>
.tbl {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
width: 100%;
border-collapse: collapse;
font-size: 13px;
background: #fff;
border-radius: 10px;
overflow: hidden;
}
.tbl th, .tbl td {
padding: 8px 10px;
text-align: left;
border-bottom: 1px solid #eee;
color: #333;
}
.tbl th {
background: #f9fafb;
font-size: 11px;
text-transform: uppercase;
letter-spacing: .08em;
color: #666;
font-weight: 700;
}
Timelines — when order matters
Anything sequential (onboarding, project phases, career milestones) reads better as a vertical timeline than as prose.
Meet team, set up accounts
Ship a small bug fix
Shadow a teammate
Own your first feature
<div class="tl"> <div class="tl-item"><strong>Day 1</strong><p>Meet team, set up accounts</p></div> <div class="tl-item"><strong>Week 1</strong><p>Ship a small bug fix</p></div> <div class="tl-item"><strong>Week 2</strong><p>Shadow a teammate</p></div> <div class="tl-item"><strong>Day 14</strong><p>Own your first feature</p></div> </div>
.tl {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
position: relative;
padding: 22px 22px 22px 46px;
background: #fff;
border-radius: 10px;
}
.tl::before {
content: "";
position: absolute;
left: 29px;
top: 22px;
bottom: 22px;
width: 2px;
background: #fcd34d;
}
.tl-item {
position: relative;
padding: 10px 0;
}
.tl-item::before {
content: "";
position: absolute;
left: -21px;
top: 16px;
width: 14px;
height: 14px;
border-radius: 50%;
background: #f59e0b;
border: 3px solid #fff;
box-shadow: 0 0 0 2px #fcd34d;
}
.tl-item strong {
display: block;
font-size: 11px;
text-transform: uppercase;
letter-spacing: .08em;
color: #92400e;
margin-bottom: 2px;
}
.tl-item p {
margin: 0;
font-size: 14px;
color: #111;
}
.tl-item.reveal {
opacity: 0;
transform: translateY(8px);
transition: opacity .5s, transform .5s;
}
.tl-item.reveal.visible {
opacity: 1;
transform: none;
}
// Reveal each timeline item as it scrolls into view
const items = document.querySelectorAll('.tl .tl-item');
items.forEach((el) => el.classList.add('reveal'));
const io = new IntersectionObserver((entries) => {
entries.forEach((entry, i) => {
if (!entry.isIntersecting) return;
setTimeout(() => entry.target.classList.add('visible'), i * 120);
io.unobserve(entry.target);
});
}, { threshold: 0.3 });
items.forEach((el) => io.observe(el));
Flow diagrams — show the process
Use simple boxes and arrows for any "A → B → C" explanation. Even a single line of flow beats three sentences of description.
<div class="flow"> <span class="flow-box">Sign up</span> <span class="flow-arrow">→</span> <span class="flow-box">Validate email</span> <span class="flow-arrow">→</span> <span class="flow-box">Send link</span> <span class="flow-arrow">→</span> <span class="flow-box">Click = activate</span> <span class="flow-arrow">→</span> <span class="flow-box">Dashboard</span> </div>
.flow {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.flow-box {
background: linear-gradient(135deg, #fef3c7, #fee4a9);
border: 1px solid #f59e0b;
color: #78350f;
padding: 8px 14px;
border-radius: 999px;
font-weight: 600;
font-size: 12px;
cursor: pointer;
transition: transform .15s, box-shadow .15s;
}
.flow-box:hover {
transform: translateY(-1px);
box-shadow: 0 4px 10px rgba(245,158,11,.25);
}
.flow-arrow {
color: #f59e0b;
font-weight: 700;
}
Info cards — one idea per box
When you have 3–6 parallel items (features, team members, values), put each one in its own card. Grids beat prose every time.
<!-- Requires phosphor-icons:
<link rel="stylesheet" href="https://unpkg.com/@phosphor-icons/web@2.1.1/src/fill/style.css"> -->
<div class="info-cards">
<div class="info-card">
<div class="ico"><i class="ph-fill ph-lightbulb"></i></div>
<strong>Curiosity</strong><span>Engine of good products</span>
</div>
<div class="info-card">
<div class="ico"><i class="ph-fill ph-gem"></i></div>
<strong>Craft</strong><span>Quality takes time</span>
</div>
<div class="info-card">
<div class="ico"><i class="ph-fill ph-hand-heart"></i></div>
<strong>Ownership</strong><span>"I'll fix it"</span>
</div>
<div class="info-card">
<div class="ico"><i class="ph-fill ph-chat-circle"></i></div>
<strong>Honesty</strong><span>Even when hard</span>
</div>
</div>
.info-cards {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 10px;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.info-card {
padding: 14px;
border-radius: 10px;
background: #fff;
border: 1px solid #eee;
}
.info-card .ico {
width: 32px;
height: 32px;
border-radius: 8px;
background: linear-gradient(135deg, #f59e0b, #ec4899);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
margin-bottom: 8px;
}
.info-card strong {
display: block;
font-size: 13px;
color: #111;
margin-bottom: 2px;
}
.info-card span {
font-size: 12px;
color: #666;
}
Progressive disclosure — hide detail until asked
Long FAQs and "optional explanations" belong in an accordion (<details>). The reader sees only the question; clicking reveals the answer.
What's your return policy?
30 days. Must be in original condition.
Do you ship internationally?
Yes — 80+ countries, 5–14 days.
Can I cancel my subscription?
Any time from account settings. No fees.
<div class="faq">
<details>
<summary>What's your return policy?</summary>
<p>30 days. Must be in original condition.</p>
</details>
<details>
<summary>Do you ship internationally?</summary>
<p>Yes — 80+ countries, 5–14 days.</p>
</details>
<details>
<summary>Can I cancel my subscription?</summary>
<p>Any time from account settings. No fees.</p>
</details>
</div>
.faq {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.faq details {
background: #fff;
border: 1px solid #eee;
border-radius: 10px;
padding: 10px 14px;
margin-bottom: 6px;
cursor: pointer;
overflow: hidden;
}
.faq summary {
font-weight: 600;
font-size: 14px;
color: #111;
list-style: none;
display: flex;
justify-content: space-between;
align-items: center;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
content: "+";
color: #f59e0b;
font-size: 1.1rem;
transition: transform .2s;
}
.faq details[open] summary::after {
content: "−";
}
.faq details p {
font-size: 13px;
color: #555;
margin: 10px 0 0;
}
// Optional: animate max-height so the panel slides instead of snapping.
// Pure CSS <details> works without this — add it only if you want richer motion.
document.querySelectorAll('.faq details').forEach((d) => {
const panel = d.querySelector('p');
if (!panel) return;
panel.style.transition = 'max-height .25s ease, opacity .25s ease';
panel.style.overflow = 'hidden';
const collapse = () => { panel.style.maxHeight = '0px'; panel.style.opacity = '0'; };
const expand = () => {
panel.style.maxHeight = panel.scrollHeight + 'px';
panel.style.opacity = '1';
};
if (!d.open) collapse();
d.addEventListener('toggle', () => (d.open ? expand() : collapse()));
});
<details><summary> and the accordion works natively — no JS required.
Highlight key terms — let the scan find you
People don't read sentences, they read keywords. Put your 1–2 most important phrases per paragraph in bold or highlighted. The scanner's eye lands on them and reads the surrounding words.
<div class="key-terms"> The most important decision is <span class="highlight">not which stock to pick</span> but <span class="highlight">how much you save</span>. Someone saving <strong>20%</strong> for <strong>30 years</strong> finishes wealthier than someone picking the right stock while saving only <strong>5%</strong>. </div>
.key-terms {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 13px;
line-height: 1.6;
color: #111;
background: #fff;
padding: 22px;
border-radius: 10px;
}
.key-terms .highlight {
background: linear-gradient(180deg, transparent 60%, #fcd34d 60%);
padding: 0 2px;
font-weight: 600;
}
.key-terms strong { font-weight: 700; color: #111; }
Drop caps & whitespace — the magazine trick
Long-form writing (essays, articles) benefits from classic print techniques: a drop cap on the first letter, generous line-height (1.6–1.8), and max-width 65ch. Signals "this is worth reading."
The first time I saw a well-designed magazine article I understood why print kept its monopoly on long reading for so long.
<div class="editorial"> <p class="dropcap">The first time I saw a well-designed magazine article I understood why print kept its monopoly on long reading for so long.</p> </div>
.editorial {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
line-height: 1.7;
color: #111;
background: #fff;
padding: 22px;
border-radius: 10px;
max-width: 40ch;
}
.editorial .dropcap { margin: 0; }
.editorial .dropcap::first-letter {
float: left;
font-size: 3.5rem;
line-height: .9;
padding-right: 6px;
font-weight: 900;
color: #f59e0b;
font-family: Georgia, serif;
}
Before you publish — the 10-point scan
Before hitting publish, reread your content with a skimmer's eye. If you can't answer the main question by only reading the headings and bold words, revise.
- No paragraph longer than 3 sentences
- Subheadings every 100–200 words
- Any 4+ item list is actually a
<ul>, not a sentence - Each section has a "key takeaway" in bold or a callout
- Numbers and stats are visually bigger than surrounding text
- Any comparison of 3+ things is a table, not prose
- Anything sequential is a timeline or numbered list
- Long FAQ entries are inside
<details>accordions - Key terms bolded / highlighted so skimmers find them
- Text max-width 65ch, line-height 1.6+ for readability
Go deeper
- How Users Read on the Web — Nielsen Norman Group's original research on scanning behavior
- F-Shaped Pattern — the classic eye-tracking study on how people scan content
- Butterick's Practical Typography — a free online book on typography that's a masterclass in readable writing
- Plain Language Guidelines — the official U.S. government guide to clear writing. Short and practical.
- Refactoring UI — book with dozens of before/after makeovers of text-heavy UI
Words first, then layers.
Write the full paragraph first. Then look for the list hiding inside it, the stat worth enlarging, the comparison begging for a table, the process crying for a timeline. Revision isn't failure — it's the visual design step.