feat: style release changelog with color-coded sections
Gives the GitHub-release-body markdown real typography on the /flash page: - `## [version]` becomes a bordered mono version heading - `### Added / Changed / Fixed / Removed / Deprecated / Security` render as uppercase section headers, color-coded per Keep-a-Changelog semantics (green / yellow / blue / red) - Nested bullet lists switch marker style (disc → circle → square) - Inline `code`, fenced `pre`, hr, strong, em, blockquote all styled - Proper vertical rhythm (line-height 1.65, section spacing) To colorize the section headers, `VersionSelector`.renderBody now post-processes marked's output to tag known h3 texts with a `data-section` attribute that CSS targets. Rules live in `src/styles/global.css` under `@layer base`, not in the Svelte component's `<style>` block: the Astro + Svelte 5 build pipeline was silently dropping the component's scoped CSS in the production bundle (works in dev via HMR, but `dist/_astro/*.css` shipped with zero `.changelog` rules), leaving the netlify site completely unstyled. Moving to global CSS sidesteps that toolchain issue; the `.changelog` parent selector is specific enough that no scope leakage is possible.
This commit is contained in:
2 files changed
+162
-41
No files matched your search
@@ -12,7 +12,17 @@
|
||||
|
||||
let { releases, selected, onselect, loading, error }: Props = $props();
|
||||
|
||||
let changelogHtml = $derived(selected?.body ? marked.parse(selected.body) : "");
|
||||
// Tag Keep-a-Changelog section headings so CSS can color-code them
|
||||
// (Added → green, Changed → yellow, Fixed → blue, etc.).
|
||||
function renderBody(body: string): string {
|
||||
const html = marked.parse(body) as string;
|
||||
return html.replace(
|
||||
/<h3>(Added|Changed|Fixed|Removed|Deprecated|Security)<\/h3>/g,
|
||||
(_, text) => `<h3 data-section="${text.toLowerCase()}">${text}</h3>`,
|
||||
);
|
||||
}
|
||||
|
||||
let changelogHtml = $derived(selected?.body ? renderBody(selected.body) : "");
|
||||
|
||||
function handleChange(e: Event) {
|
||||
const tag = (e.target as HTMLSelectElement).value;
|
||||
@@ -61,49 +71,14 @@
|
||||
</div>
|
||||
|
||||
{#if selected && changelogHtml}
|
||||
<div class="mt-4 bg-bg-card border border-border rounded p-4">
|
||||
<h4 class="font-mono text-xs text-text-dim uppercase tracking-wider mb-3">Changelog</h4>
|
||||
<div class="prose-dark text-sm text-text-muted leading-relaxed">
|
||||
<div class="mt-4 bg-bg-card border border-border rounded p-5">
|
||||
<h4 class="font-mono text-xs text-text-dim uppercase tracking-wider mb-4">Changelog</h4>
|
||||
<!-- Styles live in src/styles/global.css — Svelte component <style>
|
||||
scoping wasn't being emitted into the prod CSS bundle. -->
|
||||
<div class="changelog text-sm text-text-muted">
|
||||
{@html changelogHtml}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.prose-dark :global(h1),
|
||||
.prose-dark :global(h2),
|
||||
.prose-dark :global(h3) {
|
||||
color: var(--color-text);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.prose-dark :global(ul) {
|
||||
list-style-type: disc;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
.prose-dark :global(li) {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.prose-dark :global(code) {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.8em;
|
||||
color: var(--color-accent);
|
||||
background: var(--color-bg-elevated);
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.prose-dark :global(a) {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
.prose-dark :global(a:hover) {
|
||||
color: var(--color-accent-bright);
|
||||
}
|
||||
.prose-dark :global(p) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -70,4 +70,150 @@
|
||||
padding: 1rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Changelog typography (markdown rendered from GitHub release bodies).
|
||||
Kept here rather than in VersionSelector.svelte because the Astro +
|
||||
Svelte 5 build pipeline wasn't emitting the component's <style>
|
||||
block into any CSS file, leaving the prod changelog unstyled. */
|
||||
.changelog {
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* Top-level heading (e.g. `## [0.4.0] - 2026-04-11`) */
|
||||
.changelog h1,
|
||||
.changelog h2 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
.changelog h1:first-child,
|
||||
.changelog h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Section heading (e.g. `### Added`) */
|
||||
.changelog h3 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--color-text);
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.changelog h3:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Color-code Keep-a-Changelog sections */
|
||||
.changelog h3[data-section="added"] { color: var(--color-accent); }
|
||||
.changelog h3[data-section="changed"] { color: var(--color-warn); }
|
||||
.changelog h3[data-section="fixed"] { color: var(--color-info); }
|
||||
.changelog h3[data-section="removed"],
|
||||
.changelog h3[data-section="deprecated"] { color: var(--color-error); }
|
||||
.changelog h3[data-section="security"] { color: var(--color-warn); }
|
||||
|
||||
.changelog h4,
|
||||
.changelog h5,
|
||||
.changelog h6 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.changelog p {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
.changelog ul,
|
||||
.changelog ol {
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 1.4rem;
|
||||
}
|
||||
.changelog ul { list-style-type: disc; }
|
||||
.changelog ol { list-style-type: decimal; }
|
||||
.changelog li { margin-bottom: 0.35rem; }
|
||||
.changelog li::marker { color: var(--color-text-dim); }
|
||||
.changelog li > p { margin: 0; }
|
||||
/* Nested lists: different marker, tighter spacing */
|
||||
.changelog ul ul {
|
||||
list-style-type: circle;
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.changelog ul ul ul { list-style-type: square; }
|
||||
|
||||
/* Inline code */
|
||||
.changelog code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.85em;
|
||||
color: var(--color-accent);
|
||||
background: var(--color-bg-elevated);
|
||||
padding: 0.1em 0.35em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* Fenced code blocks */
|
||||
.changelog pre {
|
||||
background: var(--color-bg-elevated);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
padding: 0.75rem 1rem;
|
||||
margin: 0.75rem 0;
|
||||
font-size: 0.8125rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.changelog pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: var(--color-text);
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.changelog a {
|
||||
color: var(--color-accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: color-mix(in srgb, var(--color-accent) 40%, transparent);
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
.changelog a:hover {
|
||||
color: var(--color-accent-bright);
|
||||
text-decoration-color: var(--color-accent-bright);
|
||||
}
|
||||
|
||||
/* Horizontal rule (version separator) */
|
||||
.changelog hr {
|
||||
border: 0;
|
||||
border-top: 1px solid var(--color-border);
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.changelog strong {
|
||||
color: var(--color-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
.changelog em {
|
||||
color: var(--color-text);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.changelog blockquote {
|
||||
border-left: 3px solid var(--color-border-bright);
|
||||
margin: 0.5rem 0;
|
||||
padding: 0.25rem 0 0.25rem 1rem;
|
||||
color: var(--color-text-dim);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user