News — Pull Quote & Drop Cap Set
A self-contained editorial typographic specimen in classic newsprint style, pairing a justified reading column with a live compositor's panel that swaps four drop-cap treatments — raised, dropped three lines deep, a decorative red initial, and a boxed cap — against five pull-quote variants, from a centred rule-bracketed aside to a left-border accent, an oversized hanging quotation mark, a full-width ink banner, and an inset card. A reference plate grid, captioned duotone press photo, and vanilla JavaScript drive live style switching, keyboard-arrow controls, a one-click copy-snippet, and a small toast helper.
MCP
Code
:root {
--cream: #f4efe4;
--paper: #faf7f0;
--white: #ffffff;
--newsprint: #efe9da;
--ink: #16130f;
--ink-2: #2b2620;
--ink-3: #4a443b;
--muted: #7a7164;
--red: #b4291f;
--red-d: #8f1f17;
--red-50: #f3dcd9;
--rule: rgba(22, 19, 15, 0.16);
--rule-2: rgba(22, 19, 15, 0.3);
--rule-hair: rgba(22, 19, 15, 0.1);
--ok: #2f7d4f;
--warn: #b67a18;
--danger: #b4291f;
--r-sm: 4px;
--r-md: 8px;
--r-lg: 12px;
--serif: "Playfair Display", "Times New Roman", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
background: var(--cream);
color: var(--ink);
font-family: var(--sans);
line-height: 1.5;
font-size: 17px;
}
a {
color: inherit;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
background: var(--ink);
color: var(--cream);
padding: 10px 16px;
z-index: 50;
font: 600 13px/1 var(--sans);
}
.skip-link:focus {
left: 12px;
top: 12px;
}
:focus-visible {
outline: 2px solid var(--red);
outline-offset: 2px;
}
/* ---------- Masthead ---------- */
.masthead {
background: var(--paper);
border-bottom: 3px double var(--rule-2);
}
.masthead__inner {
max-width: 1180px;
margin: 0 auto;
padding: 22px 24px 14px;
text-align: center;
}
.masthead__top {
margin: 0 0 10px;
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
font: 600 11px/1 var(--sans);
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
}
.masthead__rule {
width: 38px;
height: 1px;
background: var(--rule-2);
}
.masthead__title {
margin: 0;
font-family: var(--serif);
font-weight: 900;
font-size: clamp(34px, 7vw, 64px);
letter-spacing: -0.01em;
line-height: 0.98;
color: var(--ink);
}
.masthead__strap {
margin: 8px 0 0;
font-style: italic;
font-family: var(--serif);
font-size: 15px;
color: var(--ink-3);
}
.masthead__nav {
border-top: 1px solid var(--rule);
margin-top: 14px;
}
.masthead__nav ul {
max-width: 1180px;
margin: 0 auto;
padding: 0 24px;
list-style: none;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0;
}
.masthead__nav li {
border-right: 1px solid var(--rule-hair);
}
.masthead__nav li:first-child {
border-left: 1px solid var(--rule-hair);
}
.masthead__nav a {
display: block;
padding: 11px 18px;
text-decoration: none;
font: 600 11px/1 var(--sans);
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-2);
}
.masthead__nav a:hover {
background: var(--newsprint);
color: var(--red);
}
.masthead__nav .is-current a {
color: var(--red);
}
/* ---------- Wrap / article header ---------- */
.wrap {
max-width: 1080px;
margin: 0 auto;
padding: 34px 24px 64px;
}
.lede {
text-align: center;
max-width: 820px;
margin: 0 auto;
}
.kicker {
margin: 0 0 12px;
font: 700 12px/1 var(--sans);
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--red);
}
.headline {
margin: 0;
font-family: var(--serif);
font-weight: 800;
font-size: clamp(28px, 5.2vw, 48px);
line-height: 1.06;
letter-spacing: -0.01em;
}
.deck {
margin: 14px auto 0;
max-width: 640px;
font-family: var(--serif);
font-style: italic;
font-size: clamp(16px, 2.4vw, 20px);
color: var(--ink-3);
line-height: 1.4;
}
.byline {
margin: 18px 0 0;
font: 500 13px/1.4 var(--sans);
color: var(--ink-3);
}
.byline strong {
color: var(--ink);
}
.byline .dot {
color: var(--rule-2);
margin: 0 8px;
}
.byline .dateline {
letter-spacing: 0.1em;
text-transform: uppercase;
font-size: 11px;
font-weight: 600;
}
.byline .readtime {
color: var(--muted);
}
.rule {
border: 0;
border-top: 1px solid var(--rule);
margin: 26px 0;
}
.rule--bold {
border-top: 3px double var(--rule-2);
}
/* ---------- Lab: controls + sample ---------- */
.lab {
display: grid;
grid-template-columns: 280px 1fr;
gap: 40px;
align-items: start;
}
/* Controls panel */
.panel {
position: sticky;
top: 18px;
background: var(--paper);
border: 1px solid var(--rule);
border-radius: var(--r-md);
padding: 18px 16px;
}
.panel__label {
margin: 0 0 14px;
padding-bottom: 10px;
border-bottom: 2px solid var(--ink);
font: 700 12px/1 var(--sans);
letter-spacing: 0.16em;
text-transform: uppercase;
}
.ctl {
border: 0;
padding: 0;
margin: 0 0 18px;
}
.ctl legend {
padding: 0;
margin-bottom: 8px;
font: 600 11px/1 var(--sans);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
.seg {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.seg--col {
flex-direction: column;
}
.seg__btn {
appearance: none;
cursor: pointer;
border: 1px solid var(--rule);
background: var(--white);
color: var(--ink-2);
border-radius: var(--r-sm);
padding: 8px 11px;
font: 600 12px/1.2 var(--sans);
text-align: left;
transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.seg__btn:hover {
border-color: var(--rule-2);
background: var(--newsprint);
}
.seg__btn.is-on {
background: var(--ink);
color: var(--cream);
border-color: var(--ink);
}
.panel__actions {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 4px;
}
.btn {
appearance: none;
cursor: pointer;
border-radius: var(--r-sm);
padding: 10px 14px;
font: 700 12px/1 var(--sans);
letter-spacing: 0.04em;
border: 1px solid transparent;
transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.btn--red {
background: var(--red);
color: var(--white);
border-color: var(--red-d);
}
.btn--red:hover {
background: var(--red-d);
}
.btn--red:active {
transform: translateY(1px);
}
.btn--ghost {
background: transparent;
color: var(--ink-2);
border-color: var(--rule);
}
.btn--ghost:hover {
background: var(--newsprint);
border-color: var(--rule-2);
}
.panel__hint {
margin: 12px 0 0;
padding-top: 10px;
border-top: 1px solid var(--rule-hair);
font: 500 11.5px/1.5 var(--sans);
color: var(--muted);
}
.panel__hint b {
color: var(--ink-2);
}
/* ---------- Sample column ---------- */
.sample {
font-size: 17.5px;
line-height: 1.62;
color: var(--ink-2);
max-width: 640px;
}
.sample p {
margin: 0 0 1.05em;
text-align: justify;
hyphens: auto;
}
.sample p:last-child {
margin-bottom: 0;
}
/* Figure / press photo */
.figure {
margin: 0 0 22px;
}
.press {
aspect-ratio: 3 / 2;
border-radius: var(--r-sm);
border: 1px solid var(--rule);
}
.press--shop {
background:
radial-gradient(60% 80% at 22% 18%, rgba(244, 239, 228, 0.45), transparent 60%),
radial-gradient(50% 70% at 82% 88%, rgba(180, 41, 31, 0.16), transparent 65%),
repeating-linear-gradient(
94deg,
rgba(22, 19, 15, 0.05) 0 2px,
transparent 2px 5px
),
linear-gradient(160deg, #3a342b 0%, #211d18 42%, #4a443b 100%);
background-blend-mode: screen, normal, overlay, normal;
position: relative;
}
.press--shop::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background:
linear-gradient(105deg, rgba(250, 247, 240, 0.1) 0 16%, transparent 16% 100%),
radial-gradient(120% 60% at 50% 120%, rgba(22, 19, 15, 0.5), transparent 60%);
mix-blend-mode: multiply;
}
.figure figcaption {
margin-top: 8px;
font-family: var(--serif);
font-style: italic;
font-size: 13.5px;
color: var(--ink-3);
line-height: 1.4;
}
.figure .credit {
display: block;
margin-top: 2px;
font-family: var(--sans);
font-style: normal;
font-size: 10.5px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- DROP CAP variants (applied to .lead via [data-cap] on .sample) ---------- */
.lead::first-letter {
/* base = raised */
font-family: var(--serif);
}
/* Raised initial */
.sample[data-cap="raised"] .lead::first-letter {
font-weight: 800;
font-size: 3.1em;
line-height: 0.8;
padding-right: 4px;
color: var(--ink);
vertical-align: -0.06em;
}
/* Dropped initial (float, sinks several lines) */
.sample[data-cap="dropped"] .lead::first-letter {
float: left;
font-weight: 800;
font-size: 4.4em;
line-height: 0.74;
padding: 0.02em 0.08em 0 0;
margin-right: 2px;
color: var(--ink);
}
/* Decorative red */
.sample[data-cap="decorative"] .lead::first-letter {
float: left;
font-weight: 900;
font-style: italic;
font-size: 4.8em;
line-height: 0.7;
padding: 0.04em 0.1em 0 0;
color: var(--red);
text-shadow: 2px 2px 0 var(--red-50);
}
/* Boxed initial */
.sample[data-cap="boxed"] .lead::first-letter {
float: left;
font-weight: 800;
font-size: 2.7em;
line-height: 1;
margin: 0.06em 0.42em 0 0;
padding: 0.16em 0.22em;
color: var(--cream);
background: var(--ink);
border-radius: var(--r-sm);
}
/* None */
.sample[data-cap="none"] .lead::first-letter {
font-size: inherit;
font-weight: inherit;
font-family: inherit;
float: none;
color: inherit;
padding: 0;
margin: 0;
background: none;
text-shadow: none;
}
/* ---------- PULL QUOTE variants (applied via [data-quote] on .sample) ---------- */
.pq {
margin: 1.6em 0;
}
.pq__quote {
margin: 0;
font-family: var(--serif);
font-weight: 700;
color: var(--ink);
quotes: none;
}
.pq__cite {
font: 500 12.5px/1.4 var(--sans);
color: var(--muted);
}
.pq__cite cite {
font-style: italic;
}
/* Rule-bracketed (centered) */
.sample[data-quote="bracketed"] .pq {
text-align: center;
border-top: 2px solid var(--ink);
border-bottom: 2px solid var(--ink);
padding: 18px 8px;
max-width: 440px;
margin-left: auto;
margin-right: auto;
}
.sample[data-quote="bracketed"] .pq__quote {
font-size: 1.55em;
line-height: 1.22;
font-style: italic;
}
.sample[data-quote="bracketed"] .pq__cite {
margin-top: 10px;
}
/* Left-border accent */
.sample[data-quote="border"] .pq {
border-left: 4px solid var(--red);
padding: 4px 0 4px 18px;
}
.sample[data-quote="border"] .pq__quote {
font-size: 1.45em;
line-height: 1.26;
}
.sample[data-quote="border"] .pq__cite {
margin-top: 8px;
}
/* Oversized hanging quote */
.sample[data-quote="hanging"] .pq {
position: relative;
padding-left: 4px;
}
.sample[data-quote="hanging"] .pq__quote {
font-size: 1.5em;
line-height: 1.24;
font-style: italic;
text-indent: 0.9em;
}
.sample[data-quote="hanging"] .pq__quote::before {
content: "\201C";
position: absolute;
left: -0.06em;
top: 0.16em;
font-family: var(--serif);
font-weight: 900;
font-size: 3.6em;
line-height: 0;
color: var(--red);
}
.sample[data-quote="hanging"] .pq__cite {
margin-top: 14px;
padding-left: 4px;
}
/* Full-width banner */
.sample[data-quote="banner"] .pq {
background: var(--ink);
color: var(--cream);
border-radius: var(--r-sm);
padding: 26px 28px;
margin-left: -28px;
margin-right: -28px;
text-align: center;
}
.sample[data-quote="banner"] .pq__quote {
color: var(--cream);
font-size: 1.6em;
line-height: 1.2;
}
.sample[data-quote="banner"] .pq__cite {
margin-top: 12px;
color: rgba(244, 239, 228, 0.7);
}
/* Inset card */
.sample[data-quote="inset"] .pq {
background: var(--paper);
border: 1px solid var(--rule);
border-top: 4px solid var(--red);
border-radius: var(--r-sm);
padding: 20px 22px;
}
.sample[data-quote="inset"] .pq__quote {
font-size: 1.4em;
line-height: 1.28;
}
.sample[data-quote="inset"] .pq__cite {
margin-top: 12px;
}
/* The second pull quote keeps a calmer, fixed treatment so the column has two */
.pq--second {
border-top: 1px solid var(--rule) !important;
border-bottom: 1px solid var(--rule) !important;
background: none !important;
color: var(--ink) !important;
margin-left: 0 !important;
margin-right: 0 !important;
max-width: none !important;
border-left: 0 !important;
text-align: left !important;
padding: 16px 0 !important;
}
.pq--second .pq__quote {
color: var(--ink) !important;
font-size: 1.35em !important;
font-style: italic;
line-height: 1.3 !important;
text-indent: 0 !important;
}
.pq--second .pq__quote::before {
content: none !important;
}
.pq--second .pq__cite {
color: var(--muted) !important;
padding-left: 0 !important;
margin-top: 8px !important;
}
/* ---------- Reference gallery ---------- */
.gallery__title {
margin: 0;
font-family: var(--serif);
font-weight: 800;
font-size: 24px;
}
.gallery__sub {
margin: 4px 0 20px;
font-style: italic;
font-family: var(--serif);
color: var(--ink-3);
}
.plates {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
}
.plate {
margin: 0;
}
.plate__art {
border: 1px solid var(--rule);
background: var(--paper);
border-radius: var(--r-sm);
padding: 18px 16px;
min-height: 116px;
display: flex;
flex-direction: column;
justify-content: center;
}
.plate__art p {
margin: 0;
font-family: var(--serif);
font-size: 16px;
line-height: 1.24;
font-weight: 600;
}
.plate__art--bracketed {
text-align: center;
gap: 10px;
}
.plate__art--bracketed p {
font-style: italic;
}
.plate__rule {
display: block;
height: 2px;
background: var(--ink);
}
.plate__art--border {
border-left: 4px solid var(--red);
}
.plate__art--hanging {
position: relative;
}
.plate__art--hanging span {
position: absolute;
top: 0;
left: 10px;
font-family: var(--serif);
font-weight: 900;
font-size: 52px;
line-height: 1;
color: var(--red);
}
.plate__art--hanging p {
margin-top: 22px;
font-style: italic;
}
.plate__art--banner {
background: var(--ink);
border-color: var(--ink);
text-align: center;
}
.plate__art--banner p {
color: var(--cream);
}
.plate__art--inset {
border-top: 4px solid var(--red);
}
.plate__art--cap p {
font-weight: 500;
font-family: var(--sans);
font-size: 14px;
}
.plate__cap {
float: left;
font-family: var(--serif);
font-weight: 800;
font-size: 46px;
line-height: 0.78;
padding-right: 6px;
color: var(--ink);
}
.plate figcaption {
margin-top: 8px;
font: 600 10.5px/1 var(--sans);
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- Colophon ---------- */
.colophon {
margin-top: 30px;
padding-top: 16px;
border-top: 3px double var(--rule-2);
font: 400 13px/1.6 var(--sans);
color: var(--ink-3);
}
.colophon strong {
color: var(--ink);
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 24px;
transform: translate(-50%, 20px);
background: var(--ink);
color: var(--cream);
font: 600 13px/1 var(--sans);
padding: 12px 18px;
border-radius: var(--r-sm);
opacity: 0;
pointer-events: none;
transition: opacity 0.18s, transform 0.18s;
z-index: 60;
max-width: 86vw;
text-align: center;
}
.toast.is-on {
opacity: 1;
transform: translate(-50%, 0);
}
/* ---------- Responsive ---------- */
@media (max-width: 860px) {
.lab {
grid-template-columns: 1fr;
gap: 24px;
}
.panel {
position: static;
}
.seg {
flex-direction: row;
}
.seg--col {
flex-direction: row;
}
.plates {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 720px) {
.sample p {
text-align: left;
hyphens: manual;
}
}
@media (max-width: 480px) {
body {
font-size: 16px;
}
.wrap {
padding: 24px 16px 48px;
}
.plates {
grid-template-columns: 1fr;
}
.sample[data-quote="banner"] .pq {
margin-left: -16px;
margin-right: -16px;
}
.masthead__nav a {
padding: 10px 12px;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
}
}(function () {
"use strict";
var column = document.getElementById("column");
var hintCap = document.getElementById("hint-cap");
var hintQuote = document.getElementById("hint-quote");
var CAP_LABELS = {
raised: "Raised",
dropped: "Dropped",
decorative: "Decorative red",
boxed: "Boxed",
none: "None",
};
var QUOTE_LABELS = {
bracketed: "Rule-bracketed",
border: "Left-border accent",
hanging: "Hanging quote",
banner: "Full-width banner",
inset: "Inset card",
};
/* ---- Toast helper ---- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("is-on");
window.clearTimeout(toastTimer);
toastTimer = window.setTimeout(function () {
toastEl.classList.remove("is-on");
}, 2200);
}
/* ---- Segmented control logic ---- */
function setActive(group, value) {
var buttons = document.querySelectorAll(
'[data-group="' + group + '"] .seg__btn'
);
buttons.forEach(function (btn) {
var on = btn.getAttribute("data-" + group) === value;
btn.classList.toggle("is-on", on);
btn.setAttribute("aria-checked", on ? "true" : "false");
});
}
function applyCap(value) {
if (!CAP_LABELS[value]) return;
column.setAttribute("data-cap", value);
setActive("cap", value);
if (hintCap) hintCap.textContent = CAP_LABELS[value];
}
function applyQuote(value) {
if (!QUOTE_LABELS[value]) return;
column.setAttribute("data-quote", value);
setActive("quote", value);
if (hintQuote) hintQuote.textContent = QUOTE_LABELS[value];
}
document.querySelectorAll('[data-group="cap"] .seg__btn').forEach(function (btn) {
btn.addEventListener("click", function () {
var v = btn.getAttribute("data-cap");
applyCap(v);
toast("Drop cap → " + CAP_LABELS[v]);
});
});
document.querySelectorAll('[data-group="quote"] .seg__btn').forEach(function (btn) {
btn.addEventListener("click", function () {
var v = btn.getAttribute("data-quote");
applyQuote(v);
toast("Pull quote → " + QUOTE_LABELS[v]);
});
});
/* ---- Keyboard arrow nav within each radiogroup ---- */
document.querySelectorAll('[role="radiogroup"]').forEach(function (group) {
group.addEventListener("keydown", function (e) {
if (e.key !== "ArrowRight" && e.key !== "ArrowLeft" &&
e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
var btns = Array.prototype.slice.call(group.querySelectorAll(".seg__btn"));
var idx = btns.indexOf(document.activeElement);
if (idx === -1) return;
e.preventDefault();
var dir = (e.key === "ArrowRight" || e.key === "ArrowDown") ? 1 : -1;
var next = (idx + dir + btns.length) % btns.length;
btns[next].focus();
btns[next].click();
});
});
/* ---- CSS snippet builder ---- */
function buildSnippet() {
var cap = column.getAttribute("data-cap");
var quote = column.getAttribute("data-quote");
var capRules = {
raised:
".lead::first-letter {\n font-family: \"Playfair Display\", serif;\n font-weight: 800;\n font-size: 3.1em;\n line-height: 0.8;\n vertical-align: -0.06em;\n}",
dropped:
".lead::first-letter {\n float: left;\n font-family: \"Playfair Display\", serif;\n font-weight: 800;\n font-size: 4.4em;\n line-height: 0.74;\n padding: 0.02em 0.08em 0 0;\n}",
decorative:
".lead::first-letter {\n float: left;\n font-family: \"Playfair Display\", serif;\n font-weight: 900;\n font-style: italic;\n font-size: 4.8em;\n line-height: 0.7;\n color: #b4291f;\n text-shadow: 2px 2px 0 #f3dcd9;\n}",
boxed:
".lead::first-letter {\n float: left;\n font-family: \"Playfair Display\", serif;\n font-weight: 800;\n font-size: 2.7em;\n margin: 0.06em 0.42em 0 0;\n padding: 0.16em 0.22em;\n color: #f4efe4;\n background: #16130f;\n border-radius: 4px;\n}",
none: ".lead::first-letter {\n /* no special initial */\n}",
};
var quoteRules = {
bracketed:
".pq {\n text-align: center;\n border-top: 2px solid #16130f;\n border-bottom: 2px solid #16130f;\n padding: 18px 8px;\n max-width: 440px;\n margin: 1.6em auto;\n}\n.pq blockquote {\n font: italic 700 1.55em/1.22 \"Playfair Display\", serif;\n}",
border:
".pq {\n border-left: 4px solid #b4291f;\n padding: 4px 0 4px 18px;\n margin: 1.6em 0;\n}\n.pq blockquote {\n font: 700 1.45em/1.26 \"Playfair Display\", serif;\n}",
hanging:
".pq {\n position: relative;\n margin: 1.6em 0;\n}\n.pq blockquote {\n font: italic 700 1.5em/1.24 \"Playfair Display\", serif;\n text-indent: 0.9em;\n}\n.pq blockquote::before {\n content: \"\\201C\";\n position: absolute;\n left: -0.06em;\n top: 0.16em;\n font: 900 3.6em/0 \"Playfair Display\", serif;\n color: #b4291f;\n}",
banner:
".pq {\n background: #16130f;\n color: #f4efe4;\n border-radius: 4px;\n padding: 26px 28px;\n margin: 1.6em -28px;\n text-align: center;\n}\n.pq blockquote {\n font: 700 1.6em/1.2 \"Playfair Display\", serif;\n}",
inset:
".pq {\n background: #faf7f0;\n border: 1px solid rgba(22,19,15,0.16);\n border-top: 4px solid #b4291f;\n border-radius: 4px;\n padding: 20px 22px;\n margin: 1.6em 0;\n}\n.pq blockquote {\n font: 700 1.4em/1.28 \"Playfair Display\", serif;\n}",
};
return (
"/* Drop cap: " + CAP_LABELS[cap] + " */\n" +
capRules[cap] +
"\n\n/* Pull quote: " + QUOTE_LABELS[quote] + " */\n" +
quoteRules[quote] +
"\n"
);
}
function copyText(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
return new Promise(function (resolve, reject) {
try {
var ta = document.createElement("textarea");
ta.value = text;
ta.setAttribute("readonly", "");
ta.style.position = "absolute";
ta.style.left = "-9999px";
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
document.body.removeChild(ta);
resolve();
} catch (err) {
reject(err);
}
});
}
/* ---- Action buttons ---- */
document.querySelectorAll("[data-action]").forEach(function (btn) {
btn.addEventListener("click", function () {
var action = btn.getAttribute("data-action");
if (action === "reset") {
applyCap("raised");
applyQuote("bracketed");
toast("Forme reset to defaults");
} else if (action === "copy") {
copyText(buildSnippet()).then(
function () {
toast("CSS snippet copied to clipboard");
},
function () {
toast("Copy failed — select the text manually");
}
);
}
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Kerning Gazette — Pull Quote & Drop Cap Set</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,600;0,700;0,800;0,900;1,500;1,600;1,700&family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#sample">Skip to live sample</a>
<!-- Masthead -->
<header class="masthead">
<div class="masthead__inner">
<p class="masthead__top">
<span>Typographic Supplement</span>
<span class="masthead__rule" aria-hidden="true"></span>
<span>Vol. XIV · No. 226</span>
</p>
<h1 class="masthead__title">The Kerning Gazette</h1>
<p class="masthead__strap">
A field guide to drop caps & pull quotes — set in print, since 1911
</p>
</div>
<nav class="masthead__nav" aria-label="Sections">
<ul>
<li><a href="#">Display</a></li>
<li><a href="#">Body</a></li>
<li class="is-current"><a href="#" aria-current="page">Specimens</a></li>
<li><a href="#">Letterpress</a></li>
<li><a href="#">Errata</a></li>
</ul>
</nav>
</header>
<main class="wrap">
<!-- Article header -->
<article aria-labelledby="head">
<header class="lede">
<p class="kicker">Specimen Sheet · Composition</p>
<h2 class="headline" id="head">
The Initial & the Aside: A Working Set of Drop Caps and Pull Quotes
</h2>
<p class="deck">
Five quotation treatments and four ways to set the first letter — toggled live, so
the editor can audition a column before the forme is locked.
</p>
<p class="byline">
By <strong>Marguerite Allard</strong>, Type Editor
<span class="dot" aria-hidden="true">•</span>
<span class="dateline">FLEET STREET</span>
<span class="dot" aria-hidden="true">•</span>
<span class="readtime">7 min read</span>
</p>
</header>
<hr class="rule rule--bold" />
<!-- Controls + Live sample -->
<section class="lab" id="sample" aria-label="Live specimen">
<!-- Controls panel -->
<form class="panel" aria-label="Specimen controls">
<p class="panel__label">Compositor's Desk</p>
<fieldset class="ctl">
<legend>Drop cap style</legend>
<div class="seg" role="radiogroup" aria-label="Drop cap style" data-group="cap">
<button type="button" class="seg__btn is-on" data-cap="raised" role="radio" aria-checked="true">Raised</button>
<button type="button" class="seg__btn" data-cap="dropped" role="radio" aria-checked="false">Dropped</button>
<button type="button" class="seg__btn" data-cap="decorative" role="radio" aria-checked="false">Decorative red</button>
<button type="button" class="seg__btn" data-cap="boxed" role="radio" aria-checked="false">Boxed</button>
<button type="button" class="seg__btn" data-cap="none" role="radio" aria-checked="false">None</button>
</div>
</fieldset>
<fieldset class="ctl">
<legend>Pull-quote treatment</legend>
<div class="seg seg--col" role="radiogroup" aria-label="Pull quote treatment" data-group="quote">
<button type="button" class="seg__btn is-on" data-quote="bracketed" role="radio" aria-checked="true">Rule-bracketed (centered)</button>
<button type="button" class="seg__btn" data-quote="border" role="radio" aria-checked="false">Left-border accent</button>
<button type="button" class="seg__btn" data-quote="hanging" role="radio" aria-checked="false">Oversized hanging quote</button>
<button type="button" class="seg__btn" data-quote="banner" role="radio" aria-checked="false">Full-width banner</button>
<button type="button" class="seg__btn" data-quote="inset" role="radio" aria-checked="false">Inset card</button>
</div>
</fieldset>
<div class="panel__actions">
<button type="button" class="btn btn--ghost" data-action="reset">Reset forme</button>
<button type="button" class="btn btn--red" data-action="copy">Copy CSS snippet</button>
</div>
<p class="panel__hint" aria-live="polite" id="hint">
Drop cap: <b id="hint-cap">Raised</b> · Quote: <b id="hint-quote">Rule-bracketed</b>
</p>
</form>
<!-- Live column -->
<div class="sample" id="column" data-cap="raised" data-quote="bracketed">
<figure class="figure">
<div class="press press--shop" role="img" aria-label="A composing room with type cases and a hand press, in duotone ink"></div>
<figcaption>
The composing room at Albion Court, where formes are still locked by hand.
<span class="credit">Photograph · Kerning Gazette Archive</span>
</figcaption>
</figure>
<p class="lead" data-cap-target>
Every page begins with a decision the reader will never consciously notice. The first
letter of a column either steps forward to greet you or settles quietly into the text,
and that single gesture sets the register for everything beneath it. A raised initial
feels declarative, almost spoken aloud; a dropped one is patient, architectural, the
letter sinking three lines deep as though it were load-bearing.
</p>
<p>
Compositors have argued the point for two centuries. The dropped cap descends from the
illuminated manuscript, where a scribe would surrender a square of vellum to a single
ornamented glyph. The raised cap is the broadsheet's compromise — cheaper to set, kinder
to a tight column measure, and forgiving when the lead paragraph runs short. Neither is
correct in the abstract. The right answer lives in the relationship between the headline
weight, the column width, and the colour of the page.
</p>
<!-- Pull quote (variant swapped by JS via data-quote on .sample) -->
<figure class="pq" data-quote-target>
<blockquote class="pq__quote">
A drop cap is not decoration. It is the page clearing its throat before it speaks.
</blockquote>
<figcaption class="pq__cite">— Hen/dro Voss, <cite>The Honest Forme</cite>, 1962</figcaption>
</figure>
<p>
Pull quotes obey a different logic. Where the initial letter anchors the beginning, the
quote interrupts the middle, and its job is to give a wandering eye somewhere to land.
Set it too small and it reads as an afterthought; set it the same size as the body and it
vanishes. The treatments below — centred between hairline rules, hung off a red border,
opened by an oversized quotation mark, banded across the full measure, or boxed as an inset
card — are simply different answers to one question: how loudly should the page repeat
itself?
</p>
<p>
The trick, every old hand will tell you, is restraint. One initial per column, one quote
per spread, a single accent colour used as punctuation rather than wallpaper. The eye
forgives a quiet page and tires of a busy one. Audition the controls to the left; the
column will recompose around your choice without disturbing a word of the copy.
</p>
<figure class="pq pq--second" data-quote-target>
<blockquote class="pq__quote">
Set one initial, hang one aside, and let the white space carry the rest of the argument.
</blockquote>
<figcaption class="pq__cite">— Marguerite Allard, Type Editor</figcaption>
</figure>
<p>
What remains is craft you cannot fake: the hairline that almost isn't there, the small-caps
byline, the justified measure with its hyphens placed where the language breathes. These
specimens are deliberately fictional, but the typography is not — every rule, cap, and
quotation mark here is the same furniture a real composing desk would reach for.
</p>
</div>
</section>
<hr class="rule" />
<!-- Static gallery of all variants -->
<section class="gallery" aria-label="Specimen reference">
<h3 class="gallery__title">Reference plates</h3>
<p class="gallery__sub">Each treatment, shown small, for the corrector's eye.</p>
<div class="plates">
<figure class="plate">
<div class="plate__art plate__art--bracketed">
<span class="plate__rule" aria-hidden="true"></span>
<p>The page that repeats itself does so on purpose.</p>
<span class="plate__rule" aria-hidden="true"></span>
</div>
<figcaption>Rule-bracketed</figcaption>
</figure>
<figure class="plate">
<div class="plate__art plate__art--border"><p>A border earns its keep at four points.</p></div>
<figcaption>Left-border accent</figcaption>
</figure>
<figure class="plate">
<div class="plate__art plate__art--hanging"><span aria-hidden="true">“</span><p>The mark larger than the line.</p></div>
<figcaption>Hanging quote</figcaption>
</figure>
<figure class="plate">
<div class="plate__art plate__art--banner"><p>Banded across the full measure.</p></div>
<figcaption>Full-width banner</figcaption>
</figure>
<figure class="plate">
<div class="plate__art plate__art--inset"><p>Boxed, inset, and quietly aside.</p></div>
<figcaption>Inset card</figcaption>
</figure>
<figure class="plate">
<div class="plate__art plate__art--cap"><span class="plate__cap">F</span><p>or the first letter, raised.</p></div>
<figcaption>Raised initial</figcaption>
</figure>
</div>
</section>
<footer class="colophon">
<p>
<strong>Colophon.</strong> Display in Playfair Display; text and furniture in Inter.
Rules set as hairlines; the single red is reserved for kickers, links, and one decorative cap.
</p>
</footer>
</article>
</main>
<!-- Toast -->
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Pull Quote & Drop Cap Set
A working specimen sheet for the two most quietly opinionated decisions on any editorial page: how the first letter steps forward and how the page repeats itself. A double-rule masthead and uppercase section nav sit above a red kicker, a Playfair Display headline, and an italic deck, followed by a justified reading column whose lead paragraph carries the live drop cap and whose middle holds the live pull quote.
A sticky compositor’s panel on the left audition swaps the typography without touching the copy. The drop-cap control offers a raised initial, a dropped cap that sinks several lines via float and ::first-letter, a decorative red letter with an offset shadow, a boxed cap, and none; the pull-quote control switches between a centred rule-bracketed aside, a left-border accent, an oversized hanging quotation mark, a full-width ink banner, and an inset card. Each choice updates a hint line and a reference plate grid below.
Every interaction is vanilla JavaScript: the segmented controls behave as ARIA radiogroups with arrow-key navigation, a reset button restores the default forme, and a copy button builds the exact CSS for the current cap-and-quote combination and writes it to the clipboard with an execCommand fallback. A small reusable toast() helper confirms each action. The two-column lab collapses to one under 860px, the body un-justifies under 720px for comfortable narrow reading, and the plate grid stacks down to 360px.
Illustrative UI only — masthead, headlines, bylines, and articles are fictional; not a real news publication.