News — Byline · Dateline · Read-time Meta
A self-contained editorial byline and meta component set for the fictional Meridian Standard, art-directed in a warm newsprint palette with Playfair Display headlines and Inter meta. It collects four byline layouts — simple author and date, author with portrait and role, multi-author, and agency credit — each carrying a dateline, published and updated timestamps, an estimated read-time, and a share and save icon row. A reading-time calculator estimates duration from a word count or pasted copy. Vanilla JS computes read time live, toggles the save state, and fires share toasts.
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.30);
--rule-hair: rgba(22, 19, 15, 0.10);
--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-text-size-adjust: 100%;
}
body {
margin: 0;
font-family: var(--sans);
font-size: 16px;
line-height: 1.5;
color: var(--ink);
background: var(--cream);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-image:
repeating-linear-gradient(0deg, transparent, transparent 3px, rgba(22, 19, 15, 0.012) 3px, rgba(22, 19, 15, 0.012) 4px);
}
a {
color: inherit;
}
.skip-link {
position: absolute;
left: -999px;
top: 0;
background: var(--ink);
color: var(--paper);
padding: 10px 16px;
z-index: 50;
border-radius: 0 0 var(--r-sm) 0;
}
.skip-link:focus {
left: 0;
}
:focus-visible {
outline: 2px solid var(--red);
outline-offset: 2px;
}
/* ---------- Masthead ---------- */
.masthead {
max-width: 1080px;
margin: 0 auto;
padding: 18px 24px 0;
}
.masthead__top {
display: flex;
justify-content: space-between;
align-items: baseline;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-3);
padding-bottom: 8px;
border-bottom: 1px solid var(--rule);
flex-wrap: wrap;
gap: 4px;
}
.masthead__name {
text-align: center;
padding: 14px 0 10px;
}
.masthead__kicker,
.masthead__motto {
display: block;
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
}
.masthead__title {
font-family: var(--serif);
font-weight: 900;
font-size: clamp(34px, 8vw, 72px);
line-height: 0.96;
margin: 6px 0 8px;
letter-spacing: -0.01em;
color: var(--ink);
}
.masthead__motto {
font-style: italic;
font-family: var(--serif);
text-transform: none;
letter-spacing: 0.01em;
font-size: 13px;
color: var(--ink-3);
}
.masthead__nav {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 4px 22px;
padding: 10px 0;
border-top: 3px double var(--rule-2);
border-bottom: 1px solid var(--rule);
font-size: 12px;
letter-spacing: 0.1em;
text-transform: uppercase;
font-weight: 600;
}
.masthead__nav a {
text-decoration: none;
color: var(--ink-2);
padding: 2px 0;
border-bottom: 2px solid transparent;
transition: color 0.15s, border-color 0.15s;
}
.masthead__nav a:hover,
.masthead__nav a[aria-current="page"] {
color: var(--red);
border-bottom-color: var(--red);
}
/* ---------- Layout wrap ---------- */
.wrap {
max-width: 1080px;
margin: 0 auto;
padding: 28px 24px 40px;
}
.rule {
border: none;
border-top: 3px double var(--rule-2);
margin: 36px 0;
}
.kicker {
font-family: var(--sans);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--red);
margin: 0 0 8px;
}
.kicker--section {
color: var(--ink-3);
}
/* ---------- Lead story ---------- */
.lead__headline {
font-family: var(--serif);
font-weight: 800;
font-size: clamp(30px, 5.4vw, 52px);
line-height: 1.04;
letter-spacing: -0.012em;
margin: 0 0 14px;
max-width: 16ch;
}
.lead__deck {
font-family: var(--serif);
font-style: italic;
font-size: clamp(17px, 2.4vw, 22px);
line-height: 1.4;
color: var(--ink-3);
margin: 0 0 22px;
max-width: 60ch;
}
/* ---------- Byline ---------- */
.byline {
display: flex;
align-items: flex-start;
gap: 14px;
padding: 14px 0;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
flex-wrap: wrap;
}
.byline--rich .byline__body {
flex: 1 1 240px;
}
.byline__avatar {
flex: 0 0 auto;
width: 46px;
height: 46px;
border-radius: 50%;
border: 1px solid var(--rule-2);
background:
radial-gradient(120% 120% at 30% 22%, #5c5043 0%, #2b2620 55%, #16130f 100%);
}
.byline__avatar[data-avatar="red"] {
background:
radial-gradient(120% 120% at 30% 22%, #d4564a 0%, #b4291f 50%, #6e1812 100%);
}
.byline__avatar[data-avatar="ink"] {
background:
radial-gradient(120% 120% at 32% 24%, #6a5f50 0%, #3a342c 55%, #16130f 100%);
}
.byline__avatar--sm {
width: 38px;
height: 38px;
}
.byline__body {
min-width: 0;
}
.byline__line {
margin: 0 0 3px;
font-size: 14px;
color: var(--ink-2);
}
.byline__author {
font-weight: 700;
text-decoration: none;
color: var(--ink);
border-bottom: 1px solid transparent;
transition: border-color 0.15s, color 0.15s;
}
.byline__author:hover {
color: var(--red);
border-bottom-color: var(--red-50);
}
.byline__agency {
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
font-size: 12.5px;
color: var(--ink);
}
.byline__role {
display: inline-block;
margin-left: 6px;
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
}
.dateline {
margin: 0;
font-size: 12.5px;
color: var(--ink-3);
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
}
.dateline__place {
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-2);
}
.dateline__updated {
color: var(--red-d);
}
.meta-sep {
color: var(--rule-2);
}
.readtime {
font-variant-numeric: tabular-nums;
font-weight: 600;
color: var(--ink-2);
white-space: nowrap;
}
.badge {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 2px 6px;
border-radius: var(--r-sm);
}
.badge--investigation {
color: var(--red-d);
background: var(--red-50);
}
/* ---------- Share / save ---------- */
.share {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
}
.share__btn {
display: inline-flex;
align-items: center;
gap: 6px;
font-family: var(--sans);
font-size: 12px;
font-weight: 600;
color: var(--ink-2);
background: var(--white);
border: 1px solid var(--rule);
border-radius: var(--r-sm);
padding: 7px 9px;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.05s;
}
.share__btn svg {
width: 17px;
height: 17px;
display: block;
fill: none;
stroke: currentColor;
stroke-width: 1.7;
stroke-linecap: round;
stroke-linejoin: round;
}
.share__btn svg .ic-bookmark {
fill: none;
stroke: currentColor;
}
.share__btn:hover {
border-color: var(--rule-2);
color: var(--red);
background: var(--paper);
}
.share__btn:active {
transform: translateY(1px);
}
.share__label {
display: inline;
}
.share__btn[aria-pressed="true"] {
color: var(--red);
border-color: var(--red);
background: var(--red-50);
}
.share__btn[aria-pressed="true"] svg .ic-bookmark {
fill: var(--red);
}
/* ---------- Figure / press photo ---------- */
.figure {
margin: 22px 0;
}
.figure--lead {
margin: 24px 0 26px;
}
.photo {
width: 100%;
aspect-ratio: 16 / 9;
border-radius: var(--r-sm);
border: 1px solid var(--rule);
background-color: var(--ink-3);
}
.photo--riverfront {
background:
linear-gradient(180deg, rgba(180, 41, 31, 0.18), rgba(22, 19, 15, 0.0) 38%),
radial-gradient(140% 90% at 78% 12%, #e6c77a 0%, rgba(230, 199, 122, 0) 42%),
radial-gradient(120% 120% at 18% 88%, #2b2620 0%, rgba(43, 38, 32, 0) 55%),
linear-gradient(155deg, #6f6450 0%, #463f33 38%, #211d18 100%);
}
.figure figcaption {
display: block;
margin-top: 8px;
font-size: 12.5px;
line-height: 1.45;
color: var(--ink-3);
border-left: 2px solid var(--red);
padding-left: 10px;
}
.figure__cap {
font-style: italic;
font-family: var(--serif);
}
.figure__credit {
display: block;
margin-top: 2px;
font-size: 11px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- Lead body columns ---------- */
.lead__columns {
columns: 2;
column-gap: 34px;
margin-top: 6px;
}
.lead__columns p {
margin: 0 0 14px;
text-align: justify;
hyphens: auto;
font-size: 16px;
line-height: 1.62;
color: var(--ink-2);
break-inside: avoid-column;
}
.has-dropcap::first-letter {
font-family: var(--serif);
font-weight: 800;
float: left;
font-size: 3.6em;
line-height: 0.74;
padding: 6px 10px 0 0;
color: var(--red);
}
.pullquote {
column-span: all;
margin: 18px 0;
padding: 18px 0;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
text-align: center;
}
.pullquote p {
font-family: var(--serif);
font-weight: 600;
font-style: italic;
font-size: clamp(20px, 3.2vw, 30px);
line-height: 1.25;
color: var(--ink);
margin: 0 auto;
max-width: 22ch;
text-align: center;
}
.pullquote cite {
display: block;
margin-top: 12px;
font-family: var(--sans);
font-style: normal;
font-size: 12px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- Variants gallery ---------- */
.variants__title {
font-family: var(--serif);
font-weight: 700;
font-size: clamp(24px, 4vw, 34px);
line-height: 1.08;
margin: 0 0 22px;
}
.variants__grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1px;
background: var(--rule);
border: 1px solid var(--rule);
}
.card {
background: var(--paper);
padding: 22px;
}
.card__label {
display: inline-block;
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--red-d);
background: var(--red-50);
padding: 3px 8px;
border-radius: var(--r-sm);
margin-bottom: 12px;
}
.card__hl {
font-family: var(--serif);
font-weight: 700;
font-size: 20px;
line-height: 1.16;
margin: 0 0 12px;
}
.card .byline {
border-top: none;
border-bottom: 1px solid var(--rule-hair);
padding-top: 0;
}
.card__body {
margin: 14px 0 0;
font-size: 14px;
line-height: 1.6;
color: var(--ink-3);
text-align: justify;
hyphens: auto;
}
/* ---------- Reading-time calculator ---------- */
.calc {
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: 34px;
align-items: start;
}
.calc__intro {
grid-column: 1 / -1;
}
.calc__title {
font-family: var(--serif);
font-weight: 700;
font-size: clamp(24px, 4vw, 34px);
margin: 0 0 8px;
}
.calc__lede {
font-family: var(--serif);
font-style: italic;
font-size: 17px;
color: var(--ink-3);
margin: 0;
max-width: 56ch;
}
.calc__form {
display: grid;
gap: 20px;
margin: 0;
}
.field {
display: grid;
gap: 7px;
}
.field__label {
font-size: 12px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--ink-2);
}
.field__row {
display: flex;
align-items: center;
gap: 14px;
}
.field__range {
flex: 1;
accent-color: var(--red);
height: 4px;
}
.field__out {
font-variant-numeric: tabular-nums;
font-weight: 700;
font-size: 13px;
color: var(--ink);
white-space: nowrap;
min-width: 70px;
text-align: right;
}
.field__hint {
margin: 0;
font-size: 12px;
color: var(--muted);
line-height: 1.45;
}
.field__input,
.field__area {
font-family: var(--sans);
font-size: 15px;
color: var(--ink);
background: var(--white);
border: 1px solid var(--rule-2);
border-radius: var(--r-sm);
padding: 11px 12px;
width: 100%;
}
.field__area {
resize: vertical;
line-height: 1.55;
}
.field__input:focus,
.field__area:focus {
border-color: var(--red);
}
.calc__result {
background: var(--ink);
color: var(--paper);
border-radius: var(--r-md);
padding: 24px;
position: sticky;
top: 16px;
}
.calc__result .kicker {
color: #e0a59f;
}
.calc__big {
font-family: var(--serif);
font-weight: 800;
font-size: 64px;
line-height: 0.9;
margin: 4px 0 16px;
display: flex;
align-items: baseline;
gap: 10px;
font-variant-numeric: tabular-nums;
}
.calc__unit {
font-family: var(--sans);
font-size: 14px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: rgba(244, 239, 228, 0.65);
}
.calc__stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0;
margin: 0 0 16px;
border-top: 1px solid rgba(244, 239, 228, 0.18);
border-bottom: 1px solid rgba(244, 239, 228, 0.18);
}
.calc__stats > div {
padding: 12px 0;
text-align: center;
border-right: 1px solid rgba(244, 239, 228, 0.12);
}
.calc__stats > div:last-child {
border-right: none;
}
.calc__stats dt {
font-size: 10.5px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: rgba(244, 239, 228, 0.55);
margin-bottom: 4px;
}
.calc__stats dd {
margin: 0;
font-weight: 700;
font-size: 16px;
font-variant-numeric: tabular-nums;
}
.calc__example {
margin: 0;
font-size: 12.5px;
color: rgba(244, 239, 228, 0.7);
}
.readtime-preview {
display: block;
margin-top: 6px;
font-family: var(--serif);
font-style: italic;
font-size: 14px;
color: var(--paper);
}
/* ---------- Footer ---------- */
.foot {
max-width: 1080px;
margin: 0 auto;
padding: 0 24px 40px;
}
.foot__rule {
border-top: 3px double var(--rule-2);
margin: 0 0 14px;
}
.foot__line {
margin: 0;
font-size: 12px;
color: var(--muted);
text-align: center;
letter-spacing: 0.02em;
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 24px;
transform: translate(-50%, 16px);
background: var(--ink);
color: var(--paper);
font-size: 13px;
font-weight: 600;
padding: 11px 18px;
border-radius: var(--r-md);
box-shadow: 0 6px 22px rgba(22, 19, 15, 0.28);
opacity: 0;
pointer-events: none;
transition: opacity 0.2s, transform 0.2s;
z-index: 60;
max-width: calc(100vw - 32px);
}
.toast--show {
opacity: 1;
transform: translate(-50%, 0);
}
/* ---------- Responsive ---------- */
@media (max-width: 860px) {
.calc {
grid-template-columns: 1fr;
}
.calc__result {
position: static;
}
}
@media (max-width: 720px) {
.lead__columns {
columns: 1;
}
.variants__grid {
grid-template-columns: 1fr;
}
.share {
margin-left: 0;
width: 100%;
}
}
@media (max-width: 420px) {
.wrap {
padding: 22px 16px 32px;
}
.masthead {
padding: 14px 16px 0;
}
.foot {
padding: 0 16px 32px;
}
.share__label {
display: none;
}
.calc__big {
font-size: 52px;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition-duration: 0.01ms !important;
}
}(function () {
"use strict";
/* ---------- helpers ---------- */
var STANDARD_WPM = 225;
function countWords(text) {
if (!text) return 0;
var m = text.trim().match(/[^\s]+/g);
return m ? m.length : 0;
}
function readMinutes(words, wpm) {
wpm = wpm || STANDARD_WPM;
if (!words || words <= 0) return 0;
return Math.max(1, Math.round(words / wpm));
}
function fmtInt(n) {
return Number(n).toLocaleString("en-US");
}
function fmtSeconds(totalSec) {
var s = Math.round(totalSec);
if (s < 60) return s + "s";
var m = Math.floor(s / 60);
var rem = s % 60;
return m + "m " + rem + "s";
}
/* ---------- toast ---------- */
var toastEl = document.getElementById("toast");
var toastTimer = null;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("toast--show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("toast--show");
}, 2400);
}
/* ---------- auto read-time on every byline block ----------
Each [data-readtime] byline has a sibling/related copy block
carrying [data-wordsource]; we count its words and fill the
[data-readtime-out] span inside the byline. */
function findWordSource(bylineEl) {
var article = bylineEl.closest("article, section");
if (!article) article = document;
var sources = article.querySelectorAll("[data-wordsource]");
var words = 0;
sources.forEach(function (s) {
words += countWords(s.textContent);
});
return words;
}
document.querySelectorAll("[data-readtime]").forEach(function (byline) {
var out = byline.querySelector("[data-readtime-out]");
if (!out) return;
var words = findWordSource(byline);
var mins = readMinutes(words, STANDARD_WPM);
out.textContent = mins + " min read";
out.setAttribute("title", fmtInt(words) + " words at " + STANDARD_WPM + " wpm");
});
/* ---------- bookmark / save toggle ---------- */
document.querySelectorAll("[data-bookmark]").forEach(function (btn) {
var label = btn.querySelector("[data-bookmark-label]");
btn.addEventListener("click", function () {
var saved = btn.getAttribute("aria-pressed") === "true";
saved = !saved;
btn.setAttribute("aria-pressed", String(saved));
if (label) label.textContent = saved ? "Saved" : "Save";
btn.setAttribute("aria-label", saved ? "Remove from saved" : "Save article");
toast(saved ? "Saved to your reading list" : "Removed from reading list");
});
});
/* ---------- share buttons ---------- */
document.querySelectorAll("[data-share]").forEach(function (btn) {
btn.addEventListener("click", function () {
var kind = btn.getAttribute("data-share");
if (kind === "link") {
var url = location.href;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(
function () { toast("Link copied to clipboard"); },
function () { toast("Link: " + url); }
);
} else {
toast("Link: " + url);
}
} else if (kind === "X") {
toast("Opening share to X…");
} else if (kind === "mail") {
toast("Opening your email client…");
} else {
toast("Sharing…");
}
});
});
/* ---------- reading-time calculator ---------- */
var wpmInput = document.getElementById("wpm");
var wpmOut = document.getElementById("wpm-out");
var wordsInput = document.getElementById("words");
var copyInput = document.getElementById("copy");
var outMin = document.getElementById("calc-min");
var statWords = document.getElementById("stat-words");
var statWpm = document.getElementById("stat-wpm");
var statSecs = document.getElementById("stat-secs");
var preview = document.getElementById("calc-preview");
function currentWpm() {
var v = wpmInput ? parseInt(wpmInput.value, 10) : STANDARD_WPM;
return v > 0 ? v : STANDARD_WPM;
}
function currentWords() {
// If copy is pasted, it wins and drives the number field.
if (copyInput && copyInput.value.trim().length) {
var w = countWords(copyInput.value);
if (wordsInput) wordsInput.value = w;
return w;
}
var n = wordsInput ? parseInt(wordsInput.value, 10) : 0;
return isNaN(n) || n < 0 ? 0 : n;
}
function renderCalc() {
var wpm = currentWpm();
var words = currentWords();
var mins = readMinutes(words, wpm);
var secs = words > 0 ? (words / wpm) * 60 : 0;
if (wpmOut) wpmOut.textContent = wpm + " wpm";
if (outMin) outMin.textContent = mins;
if (statWords) statWords.textContent = fmtInt(words);
if (statWpm) statWpm.textContent = wpm + " wpm";
if (statSecs) statSecs.textContent = fmtSeconds(secs);
if (preview) {
preview.textContent = "By a Staff Writer · " + mins + " min read";
}
}
if (wpmInput) wpmInput.addEventListener("input", renderCalc);
if (wordsInput) wordsInput.addEventListener("input", renderCalc);
if (copyInput) copyInput.addEventListener("input", renderCalc);
renderCalc();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Meridian Standard — Byline · Dateline · Read-time Meta</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&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="masthead" role="banner">
<div class="masthead__top">
<span class="masthead__date">Monday, June 8, 2026 · Late City Edition</span>
<span class="masthead__price">No. 14,902 · $3.50</span>
</div>
<div class="masthead__name">
<span class="masthead__kicker">Independent since 1911</span>
<h1 class="masthead__title">The Meridian Standard</h1>
<span class="masthead__motto">All the truth that fits the column</span>
</div>
<nav class="masthead__nav" aria-label="Sections">
<a href="#main" aria-current="page">Front Page</a>
<a href="#">Politics</a>
<a href="#">Business</a>
<a href="#">Culture</a>
<a href="#">Science</a>
<a href="#">Opinion</a>
</nav>
</header>
<main id="main" class="wrap">
<!-- LEAD STORY -->
<article class="lead" aria-labelledby="lead-hl">
<p class="kicker">Breaking · City Hall</p>
<h2 id="lead-hl" class="lead__headline">Council Approves Riverfront Plan After a Decade of Delays and Dispute</h2>
<p class="lead__deck">A narrow vote ends one of the longest civic standoffs in the district’s history — and reshapes twelve blocks of waterfront for a generation.</p>
<!-- BYLINE: author with portrait + role -->
<div class="byline byline--rich" data-readtime>
<span class="byline__avatar" aria-hidden="true" data-avatar="ink"></span>
<div class="byline__body">
<p class="byline__line">
By <a class="byline__author" href="#">Marisol Trevino</a>
<span class="byline__role">City Hall Correspondent</span>
</p>
<p class="dateline">
<span class="dateline__place">Riverside Heights</span>
<span class="meta-sep" aria-hidden="true">·</span>
<time datetime="2026-06-08T06:15">Published 6:15 AM</time>
<span class="meta-sep" aria-hidden="true">·</span>
<time class="dateline__updated" datetime="2026-06-08T09:40">Updated 9:40 AM</time>
<span class="meta-sep" aria-hidden="true">·</span>
<span class="readtime" data-readtime-out>— min read</span>
</p>
</div>
<div class="share" role="group" aria-label="Share and save this article">
<button class="share__btn" type="button" data-bookmark aria-pressed="false" aria-label="Save article">
<svg viewBox="0 0 24 24" aria-hidden="true"><path class="ic-bookmark" d="M6 4h12v16l-6-4-6 4z"/></svg>
<span class="share__label" data-bookmark-label>Save</span>
</button>
<button class="share__btn" type="button" data-share="X" aria-label="Share on X">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 4l16 16M20 4L4 20"/></svg>
</button>
<button class="share__btn" type="button" data-share="link" aria-label="Copy link">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 15l6-6M8 12l-2 2a3 3 0 104 4l2-2M16 12l2-2a3 3 0 10-4-4l-2 2"/></svg>
</button>
<button class="share__btn" type="button" data-share="mail" aria-label="Share by email">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h18v12H3z"/><path d="M3 6l9 7 9-7"/></svg>
</button>
</div>
</div>
<figure class="figure figure--lead">
<div class="photo photo--riverfront" role="img" aria-label="Aerial view of the contested riverfront district at dawn"></div>
<figcaption>
<span class="figure__cap">Dawn over the contested waterfront, hours before Monday’s decisive vote.</span>
<span class="figure__credit">Pavel Ostrowski / The Meridian Standard</span>
</figcaption>
</figure>
<div class="lead__columns" data-wordsource>
<p class="has-dropcap">The motion passed at 1:42 in the morning, after fourteen hours of public testimony, two recesses, and a final round of bargaining that aides described as exhausting and, at moments, openly hostile. When the gavel fell the chamber was nearly empty, but the consequences of the seven-to-five vote will be felt across the riverfront for decades.</p>
<p>For ten years the parcel had sat behind chain-link, a stretch of cracked tarmac and rusting cranes that residents passed without expectation. Successive councils studied it, zoned it, un-zoned it, and shelved it. Developers came with renderings and left without permits. The plan approved Monday is the fourth major proposal since 2016 — and the first to survive.</p>
<p>Under the approved framework, the district will reserve a third of the ground floor for public use, cap building heights along the embankment, and dedicate a continuous walkway from the old ferry terminal to the rail bridge. Critics had warned that the compromise would satisfy no one; supporters argued that consensus was never the point.</p>
<blockquote class="pullquote">
<p>“We were not asked to build a perfect waterfront. We were asked to stop arguing about it.”</p>
<cite>— Council Member Dìana Okafor, after the vote</cite>
</blockquote>
<p>The financing remains contingent on a bond measure scheduled for the autumn ballot, and several neighborhood associations have signalled that they may challenge the environmental review in court. Even so, the developer of record expects demolition permits to be filed within the quarter, with the first phase of the esplanade open to the public by the spring of 2028.</p>
<p>For Marisol Trevino, who has covered the riverfront since the first failed proposal, the morning carried a familiar texture. “Every council swears this one is different,” one veteran planner told her in the corridor. “The difference this time is that they were too tired to walk it back.”</p>
</div>
</article>
<hr class="rule" />
<!-- SECONDARY: BYLINE VARIANTS GALLERY -->
<section class="variants" aria-labelledby="variants-hl">
<p class="kicker kicker--section">The Component · Byline Patterns</p>
<h3 id="variants-hl" class="variants__title">Four ways to credit a story</h3>
<div class="variants__grid">
<!-- 1. Simple author + date -->
<article class="card">
<span class="card__label">Simple</span>
<h4 class="card__hl">Markets Steady as Central Bank Holds the Line on Rates</h4>
<div class="byline" data-readtime>
<div class="byline__body">
<p class="byline__line">By <a class="byline__author" href="#">Renata Voss</a></p>
<p class="dateline">
<time datetime="2026-06-08T08:00">June 8, 2026</time>
<span class="meta-sep" aria-hidden="true">·</span>
<span class="readtime" data-readtime-out>— min read</span>
</p>
</div>
</div>
<p class="card__body" data-wordsource>The benchmark rate held at four and a quarter percent for a third consecutive meeting, a decision that traders had priced in but politicians had not. Bond yields ticked downward in the afternoon while equities closed flat, leaving analysts to parse a statement that promised patience without quite promising relief. The governor declined to rule out a cut before winter.</p>
</article>
<!-- 2. Author with portrait + role -->
<article class="card">
<span class="card__label">Portrait + role</span>
<h4 class="card__hl">Inside the Lab Quietly Rewriting How Cities Cool Themselves</h4>
<div class="byline byline--rich" data-readtime>
<span class="byline__avatar byline__avatar--sm" aria-hidden="true" data-avatar="red"></span>
<div class="byline__body">
<p class="byline__line">By <a class="byline__author" href="#">Theo Almeida</a> <span class="byline__role">Science, Features Desk</span></p>
<p class="dateline">
<span class="dateline__place">Lakeside</span>
<span class="meta-sep" aria-hidden="true">·</span>
<time datetime="2026-06-07T11:30">June 7, 2026</time>
<span class="meta-sep" aria-hidden="true">·</span>
<span class="readtime" data-readtime-out>— min read</span>
</p>
</div>
</div>
<p class="card__body" data-wordsource>On the fourth floor of a converted brewery, a team of nine is testing pavement that breathes. Their coatings draw heat from the street at noon and release it after dark, shaving as much as six degrees from the surface of a summer sidewalk. The science is unglamorous and the funding precarious, but the early field trials, run across two boroughs, have begun to attract the attention of bigger institutions.</p>
</article>
<!-- 3. Multi-author -->
<article class="card">
<span class="card__label">Multi-author</span>
<h4 class="card__hl">How a Routine Audit Unravelled the Port Authority’s Ledger</h4>
<div class="byline" data-readtime>
<div class="byline__body">
<p class="byline__line">
By <a class="byline__author" href="#">Marisol Trevino</a>,
<a class="byline__author" href="#">Idris Bello</a>
and <a class="byline__author" href="#">Hana Köll</a>
</p>
<p class="dateline">
<time datetime="2026-06-06T07:00">June 6, 2026</time>
<span class="meta-sep" aria-hidden="true">·</span>
<span class="readtime" data-readtime-out>— min read</span>
<span class="meta-sep" aria-hidden="true">·</span>
<span class="badge badge--investigation">Investigation</span>
</p>
</div>
</div>
<p class="card__body" data-wordsource>It began as a clerical formality, a quarterly reconciliation that nobody expected to take longer than an afternoon. By the time three reporters had spent eleven weeks with the documents, the figures told a different story: contracts awarded twice, invoices addressed to firms that had dissolved, and a reserve account that grew by the season without an obvious source. None of it was hidden, exactly. It had simply never been read together.</p>
</article>
<!-- 4. Agency credit -->
<article class="card">
<span class="card__label">Agency credit</span>
<h4 class="card__hl">Coastal Nations Agree to Shared Fishing Limits Ahead of Summit</h4>
<div class="byline" data-readtime>
<div class="byline__body">
<p class="byline__line">
<span class="byline__agency">Meridian Wire Service</span>
<span class="byline__role">with reporting from Port Calder</span>
</p>
<p class="dateline">
<time datetime="2026-06-08T05:20">June 8, 2026</time>
<span class="meta-sep" aria-hidden="true">·</span>
<span class="readtime" data-readtime-out>— min read</span>
</p>
</div>
</div>
<p class="card__body" data-wordsource>Negotiators from six coastal states agreed in the early hours to a binding cap on the season’s catch, a deal that had eluded them through three previous rounds. The accord, brokered overnight, sets a single quota to be divided by historical tonnage and pledges a joint patrol of the disputed shelf. Environmental groups welcomed the limit while cautioning that enforcement, not arithmetic, had always been the harder problem.</p>
</article>
</div>
</section>
<hr class="rule" />
<!-- READING-TIME CALCULATOR -->
<section class="calc" aria-labelledby="calc-hl">
<div class="calc__intro">
<p class="kicker kicker--section">Tool · Newsroom</p>
<h3 id="calc-hl" class="calc__title">Reading-time calculator</h3>
<p class="calc__lede">Estimate how long a piece will hold a reader. Paste a word count or the copy itself; the meta line updates as you type.</p>
</div>
<form class="calc__form" novalidate>
<div class="field">
<label class="field__label" for="wpm">Reading speed</label>
<div class="field__row">
<input class="field__range" type="range" id="wpm" name="wpm" min="120" max="320" step="10" value="225" />
<output class="field__out" for="wpm" id="wpm-out">225 wpm</output>
</div>
<p class="field__hint">The newsroom standard for online news is roughly 225 words per minute.</p>
</div>
<div class="field">
<label class="field__label" for="words">Word count</label>
<input class="field__input" type="number" id="words" name="words" inputmode="numeric" min="0" step="1" value="0" placeholder="e.g. 720" />
</div>
<div class="field">
<label class="field__label" for="copy">…or paste the article text</label>
<textarea class="field__area" id="copy" name="copy" rows="4" placeholder="Paste copy here and the word count fills in automatically."></textarea>
</div>
</form>
<aside class="calc__result" aria-live="polite">
<p class="kicker">Estimated</p>
<p class="calc__big"><span id="calc-min">0</span><span class="calc__unit">min read</span></p>
<dl class="calc__stats">
<div><dt>Words</dt><dd id="stat-words">0</dd></div>
<div><dt>Speed</dt><dd id="stat-wpm">225 wpm</dd></div>
<div><dt>Seconds</dt><dd id="stat-secs">0s</dd></div>
</dl>
<p class="calc__example">Meta preview: <span class="readtime-preview" id="calc-preview">By a Staff Writer · 0 min read</span></p>
</aside>
</section>
</main>
<footer class="foot" role="contentinfo">
<p class="foot__rule"></p>
<p class="foot__line">© 2026 The Meridian Standard — a fictional publication for demonstration purposes. Set in Playfair Display & Inter.</p>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Byline · Dateline · Read-time Meta
A newsroom-grade byline kit for The Meridian Standard, a fictional broadsheet set in a warm newsprint palette with hairline rules, a single red accent, and Playfair Display over Inter. A lead story anchors the page with a rich byline — author portrait, role, dateline, published and updated timestamps, an auto-computed read-time, and a save and share icon row — above a duotone captioned figure, a justified two-column body with a red drop cap, and an oversized pull quote.
Below the rule, a gallery shows four ways to credit a story: a simple author-and-date line, an author with portrait and role, a multi-author line, and an agency-wire credit. Each variant carries its own dateline and read-time, and every read-time on the page is calculated from the article copy at the standard newsroom pace, so the meta always matches the words it describes.
The reading-time calculator estimates how long a piece will hold a reader. Drag the words-per-minute slider, type a word count, or paste the copy itself — the script counts the words, derives minutes and seconds, and renders a live meta preview. The save button toggles a bookmarked state, the share controls copy a link or open a target, and a small toast confirms each action.
Illustrative UI only — masthead, headlines, bylines, and articles are fictional; not a real news publication.