From 24ca2d3317c04666fa2e8dbbde7c6f3d6b8d14e6 Mon Sep 17 00:00:00 2001 From: hugo Date: Sun, 1 Mar 2026 07:43:46 +0100 Subject: [PATCH] feat: moved big css block out of pages into external css file --- package.json | 5 + src/main/engine/PageRenderer.ts | 24 +++ src/main/engine/assets/bds.css | 145 +++++++++++++++++ .../engine/templates/partials/head.liquid | 2 +- .../engine/templates/partials/styles.liquid | 147 ------------------ tests/engine/BlogGenerationEngine.test.ts | 14 +- .../GenerationRouteRendererFactory.test.ts | 2 +- tests/engine/PreviewServer.test.ts | 46 +++--- 8 files changed, 201 insertions(+), 184 deletions(-) create mode 100644 src/main/engine/assets/bds.css delete mode 100644 src/main/engine/templates/partials/styles.liquid diff --git a/package.json b/package.json index d75ad24..2147ba2 100644 --- a/package.json +++ b/package.json @@ -159,6 +159,11 @@ { "from": "src/main/engine/templates", "to": "templates" + }, + { + "from": "src/main/engine/assets", + "to": "assets", + "filter": ["*.css"] } ], "protocols": [ diff --git a/src/main/engine/PageRenderer.ts b/src/main/engine/PageRenderer.ts index 7009e33..5be2d80 100644 --- a/src/main/engine/PageRenderer.ts +++ b/src/main/engine/PageRenderer.ts @@ -11,6 +11,26 @@ import { CALENDAR_RUNTIME_JS } from './assets/calendarRuntime'; import { TAG_CLOUD_RUNTIME_JS } from './assets/tagCloudRuntime'; import { resolveRenderLanguageFromProjectPreferences, translateRender } from '../shared/i18n'; +function readLocalAsset(filename: string): string { + const candidates = [ + path.join(__dirname, 'assets', filename), + path.join(process.cwd(), 'dist', 'main', 'engine', 'assets', filename), + path.join(process.cwd(), 'src', 'main', 'engine', 'assets', filename), + ]; + + if (typeof process.resourcesPath === 'string' && process.resourcesPath.length > 0) { + candidates.unshift(path.join(process.resourcesPath, 'assets', filename)); + } + + for (const candidate of candidates) { + if (fs.existsSync(candidate)) { + return fs.readFileSync(candidate, 'utf-8'); + } + } + + throw new Error(`Local asset not found: ${filename}`); +} + export interface PythonMacroScript { id: string; slug: string; @@ -271,6 +291,10 @@ export const PREVIEW_ASSETS: Record = { contentType: 'application/javascript; charset=utf-8', sourceText: CALENDAR_RUNTIME_JS, }, + 'bds.css': { + contentType: 'text/css; charset=utf-8', + sourceText: readLocalAsset('bds.css'), + }, }; export const PREVIEW_IMAGE_ASSETS = { diff --git a/src/main/engine/assets/bds.css b/src/main/engine/assets/bds.css new file mode 100644 index 0000000..e0f8682 --- /dev/null +++ b/src/main/engine/assets/bds.css @@ -0,0 +1,145 @@ +:root { color-scheme: light dark; } +@media only screen and (prefers-color-scheme: dark) { + :root:not([data-theme]) { --pico-background-color: #13171f; } +} +[data-theme='dark'] { --pico-background-color: #13171f; } +body { max-width: 960px; margin: 0 auto; padding: 2rem 1rem 4rem; background: var(--pico-background-color, var(--background-color)); color: var(--pico-color, var(--color)); } +main { display: grid; gap: 1rem; } +.blog-menu { position: relative; display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; border-top: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); border-bottom: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); padding: .4rem 0; margin: -.15rem 0 .2rem; } +.blog-menu > .blog-menu-list { width: 100%; } +.blog-menu-list { list-style: none; display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem .75rem; margin: 0; padding: 0; } +.blog-menu-item { position: relative; } +.blog-menu-link { display: inline-flex; align-items: center; color: var(--pico-muted-color, var(--muted-color)); text-decoration: none; font-size: .94rem; line-height: 1.4; padding: .2rem .1rem; } +.blog-menu-item-with-children > .blog-menu-link::after { content: '▾'; font-size: .7em; margin-left: .38rem; opacity: .72; } +.blog-menu-link:hover, +.blog-menu-link:focus-visible { color: var(--pico-color, var(--color)); text-decoration: underline; } +.blog-menu-submenu { position: absolute; top: calc(100% + .12rem); left: 0; min-width: 12rem; display: none; border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); background: var(--pico-card-background-color, var(--card-background-color)); padding: .3rem 0; z-index: 10; } +.blog-menu-submenu .blog-menu-list { flex-direction: column; flex-wrap: nowrap; gap: 0; margin: 0; } +.blog-menu-submenu .blog-menu-item { display: block; padding: 0; margin: 0; } +.blog-menu-submenu .blog-menu-link { display: block; padding: .22rem .75rem; font-size: .88rem; line-height: 1.3; } +.blog-menu-submenu .blog-menu-item a.blog-menu-link { margin: 0; } +.blog-menu-item-with-children:hover > .blog-menu-submenu, +.blog-menu-item-with-children:focus-within > .blog-menu-submenu { display: block; } +.blog-menu-calendar { position: relative; display: inline-flex; align-items: baseline; justify-content: center; margin-left: auto; align-self: baseline; flex-shrink: 0; } +.blog-menu-calendar-button { display: inline-flex; align-items: center; justify-content: center; width: auto; height: auto; margin: 0; padding: .2rem .1rem; border: 0; background: transparent; color: var(--pico-muted-color, var(--muted-color)); border-radius: 0; cursor: pointer; font: inherit; font-size: .94rem; line-height: 1.4; appearance: none; -webkit-appearance: none; vertical-align: baseline; } +.blog-menu-calendar-button svg { display: block; width: .9rem; height: .9rem; fill: none; stroke: currentColor; transform: translateY(2px); } +.blog-menu-calendar-button:hover, +.blog-menu-calendar-button:focus-visible { color: var(--pico-color, var(--color)); } +.blog-calendar-panel { position: absolute; top: calc(100% + .15rem); right: 0; width: min(17.5rem, 92vw); border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); background: var(--pico-card-background-color, var(--card-background-color)); padding: .32rem; z-index: 30; } +.blog-calendar-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: .1rem; } +.blog-calendar-header strong { font-size: .9rem; line-height: 1.2; } +.blog-calendar-close { border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); background: transparent; color: var(--pico-muted-color, var(--muted-color)); width: 1.35rem; height: 1.35rem; border-radius: .2rem; padding: 0; cursor: pointer; line-height: 1; } +.blog-calendar-close:hover, +.blog-calendar-close:focus-visible { color: var(--pico-color, var(--color)); border-color: var(--pico-color, var(--color)); } +.blog-calendar-content { display: grid; gap: .08rem; } +.blog-calendar-status { margin: .1rem 0 0; color: var(--pico-muted-color, var(--muted-color)); font-size: .74rem; } +[data-blog-calendar-root] { font-size: .86rem; } +[data-blog-calendar-root] [data-vc=header] { margin-bottom: .08rem; } +[data-blog-calendar-root] [data-vc=month], +[data-blog-calendar-root] [data-vc=year] { padding: .08rem .18rem; font-size: .9rem; line-height: 1.15; } +[data-blog-calendar-root] [data-vc=months], +[data-blog-calendar-root] [data-vc=years] { row-gap: .32rem; } +[data-blog-calendar-root] [data-vc=years] { grid-template-columns: repeat(4, minmax(0, 1fr)); } +[data-blog-calendar-root] [data-vc-months-month], +[data-blog-calendar-root] [data-vc-years-year] { height: 1.72rem; } +[data-blog-calendar-root] [data-vc-months-month], +[data-blog-calendar-root] [data-vc-years-year] { word-break: normal; white-space: nowrap; } +[data-blog-calendar-root] [data-vc-years-year] { min-width: 2.5rem; font-size: .7rem; line-height: 1; } +[data-blog-calendar-root] [data-vc-week=days] { margin-bottom: .08rem; } +[data-blog-calendar-root] [data-vc-week-day] { font-size: .68rem; line-height: .9rem; min-width: 1.45rem; } +[data-blog-calendar-root] [data-vc-date] { padding-top: 0; padding-bottom: 0; } +[data-blog-calendar-root] [data-vc-date-btn] { min-height: 1.45rem; min-width: 1.45rem; font-size: .68rem; line-height: .9rem; } +[data-blog-calendar-has-posts='true'] [data-vc-date-btn] { + border-color: hsl(var(--blog-calendar-heat-hue, 210) 85% 42% / .95); + background-color: hsl(var(--blog-calendar-heat-hue, 210) 88% 52% / var(--blog-calendar-heat-alpha, 0)); +} +[data-blog-calendar-root] [data-vc-months-month][data-blog-calendar-has-posts='true'], +[data-blog-calendar-root] [data-vc-years-year][data-blog-calendar-has-posts='true'] { + background-color: hsl(var(--blog-calendar-heat-hue, 210) 88% 52% / var(--blog-calendar-heat-alpha, 0)); + border-color: hsl(var(--blog-calendar-heat-hue, 210) 85% 42% / .95); +} +.post { border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); padding: 1rem; background: var(--pico-card-background-color, var(--card-background-color)); min-width: 0; } +.post pre { position: relative; overflow-x: auto; max-width: 100%; border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); border-radius: .3rem; margin: .9rem 0; padding: .85rem .9rem; background: var(--pico-code-background-color, rgba(33, 38, 45, .82)); box-sizing: border-box; } +.post pre code { display: block; font-size: .88rem; line-height: 1.5; white-space: pre; } +.code-copy-button { + position: absolute; + top: .4rem; + right: .4rem; + border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); + background: var(--pico-card-background-color, var(--card-background-color)); + color: var(--pico-muted-color, var(--muted-color)); + border-radius: .25rem; + width: 1.8rem; + height: 1.8rem; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + cursor: pointer; + opacity: .88; +} +.code-copy-button:hover, +.code-copy-button:focus-visible { opacity: 1; color: var(--pico-color, var(--color)); } +.code-copy-icon { font-size: .95rem; line-height: 1; } +.code-copy-success .code-copy-button { color: var(--pico-ins-color, rgb(53, 117, 56)); border-color: var(--pico-ins-color, rgb(53, 117, 56)); } +.code-copy-failed .code-copy-button { color: var(--pico-del-color, rgb(183, 72, 72)); border-color: var(--pico-del-color, rgb(183, 72, 72)); } +.post iframe { width: 100%; min-height: 20rem; } +.macro-youtube, .macro-vimeo { margin-bottom: 1rem; } +.macro-gallery, .macro-photo-archive, .macro-tag-cloud { border: 1px dashed var(--pico-muted-border-color, var(--muted-border-color)); padding: .75rem; margin: 1rem 0; } +.gallery-container { display: grid; gap: .5rem; } +.macro-gallery.gallery-cols-1 .gallery-container { grid-template-columns: 1fr; } +.macro-gallery.gallery-cols-2 .gallery-container { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.macro-gallery.gallery-cols-3 .gallery-container { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.macro-gallery.gallery-cols-4 .gallery-container { grid-template-columns: repeat(4, minmax(0, 1fr)); } +.macro-gallery.gallery-cols-5 .gallery-container { grid-template-columns: repeat(5, minmax(0, 1fr)); } +.macro-gallery.gallery-cols-6 .gallery-container { grid-template-columns: repeat(6, minmax(0, 1fr)); } +.gallery-item, .photo-archive-item { display: block; overflow: hidden; border-radius: .25rem; } +.gallery-item img, .photo-archive-item img { display: block; width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover; } +.lb-nav a, .lb-nav a:hover, .lb-nav a:focus-visible { border: 0; box-shadow: none; outline: none; text-decoration: none; } +.gallery-caption { margin-top: .5rem; text-align: center; color: var(--pico-muted-color, var(--muted-color)); font-size: .92rem; } +.gallery-empty, .photo-archive-empty { color: var(--pico-muted-color, var(--muted-color)); font-style: italic; } +.photo-archive-container { display: grid; gap: 1rem; } +.photo-archive-month { display: grid; grid-template-columns: 3.25rem 1fr; gap: .75rem; align-items: start; } +.photo-archive-month-label { display: flex; justify-content: center; align-items: center; } +.photo-archive-month-label span { writing-mode: vertical-rl; transform: rotate(180deg); letter-spacing: .08em; text-transform: uppercase; color: var(--pico-muted-color, var(--muted-color)); } +.photo-archive-gallery { display: grid; gap: .5rem; grid-template-columns: repeat(4, minmax(0, 1fr)); } +.photo-archive-single-month .photo-archive-gallery { grid-template-columns: repeat(5, minmax(0, 1fr)); } +.macro-tag-cloud { min-height: 14rem; } +.tag-cloud-canvas { display: block; width: 100%; height: auto; min-height: 12rem; } +.tag-cloud-empty { color: var(--pico-muted-color, var(--muted-color)); font-style: italic; } +.archive-day-group { display: grid; grid-template-columns: 5.25rem 1fr; gap: 1.25rem; align-items: stretch; } +.archive-day-marker { display: flex; justify-content: center; align-items: center; color: var(--pico-muted-color, var(--muted-color)); } +.archive-day-marker span { writing-mode: vertical-rl; transform: rotate(180deg); letter-spacing: .16em; font-size: 1.05rem; font-weight: 600; text-transform: uppercase; } +.archive-day-posts { display: grid; gap: 1rem; } +.archive-day-separator { position: relative; height: 2px; width: 100%; color: var(--pico-color, var(--color)); border-top: 1px solid currentColor; opacity: .18; margin: .45rem 0 .65rem; } +.archive-day-separator::before { content: ''; position: absolute; inset: 0; background: linear-gradient(to right, transparent 0%, transparent 18%, currentColor 58%, transparent 92%, transparent 100%); opacity: .85; } +.single-post { margin: 0; padding: 0; background: transparent; border: 0; box-shadow: none; } +.single-post-taxonomy { display: flex; flex-wrap: wrap; gap: .4rem .45rem; margin: -.1rem 0 .2rem; } +.single-post-taxonomy-bubble { + --bubble-accent: var(--pico-ins-color, rgb(53, 117, 56)); + --bubble-bg: var(--bubble-accent); + display: inline-flex; + align-items: center; + border: 1px solid var(--bubble-accent); + border-radius: 999px; + padding: .1rem .5rem; + font-size: .74rem; + line-height: 1.35; + color: #000; + background: var(--bubble-bg, var(--bubble-accent)); + text-decoration: none; +} +.single-post-taxonomy-bubble:hover, +.single-post-taxonomy-bubble:focus-visible { text-decoration: underline; } +.single-post-taxonomy-bubble-category { --bubble-accent: var(--pico-ins-color, rgb(53, 117, 56)); --bubble-bg: var(--pico-ins-color, rgb(53, 117, 56)); } +.single-post-taxonomy-bubble-tag { --bubble-accent: var(--pico-del-color, rgb(183, 72, 72)); --bubble-bg: var(--pico-del-color, rgb(183, 72, 72)); } +.single-post-backlinks { display: flex; flex-wrap: wrap; gap: .4rem .45rem; align-items: center; margin-top: 1.5rem; } +.single-post-backlinks-label { font-size: .74rem; line-height: 1.35; color: var(--pico-muted-color, var(--muted-color)); margin-right: .15rem; } +.single-post-backlink-bubble { --bubble-accent: var(--pico-primary, rgb(16, 107, 193)); --bubble-bg: var(--pico-primary, rgb(16, 107, 193)); color: var(--pico-primary-inverse, #fff); } +.preview-pagination { display: flex; justify-content: space-between; align-items: center; gap: .75rem; margin-top: .25rem; } +.preview-pagination-link { color: var(--pico-muted-color, var(--muted-color)); text-decoration: none; font-size: .92rem; opacity: .72; transition: opacity .15s ease-in-out; } +.preview-pagination-link:hover, +.preview-pagination-link:focus-visible { opacity: 1; text-decoration: underline; } +.preview-pagination .spacer { flex: 1; } +.not-found { display: grid; place-items: center; min-height: 48vh; } +.not-found article { max-width: 32rem; text-align: center; } diff --git a/src/main/engine/templates/partials/head.liquid b/src/main/engine/templates/partials/head.liquid index fe3d23e..aaabbc2 100644 --- a/src/main/engine/templates/partials/head.liquid +++ b/src/main/engine/templates/partials/head.liquid @@ -7,9 +7,9 @@ + - {% render 'partials/styles' %} diff --git a/src/main/engine/templates/partials/styles.liquid b/src/main/engine/templates/partials/styles.liquid deleted file mode 100644 index 9fca7c5..0000000 --- a/src/main/engine/templates/partials/styles.liquid +++ /dev/null @@ -1,147 +0,0 @@ - diff --git a/tests/engine/BlogGenerationEngine.test.ts b/tests/engine/BlogGenerationEngine.test.ts index 1102589..6483366 100644 --- a/tests/engine/BlogGenerationEngine.test.ts +++ b/tests/engine/BlogGenerationEngine.test.ts @@ -462,7 +462,7 @@ describe('BlogGenerationEngine', () => { expect(html).toContain('data-template="post-list"'); expect(html).toContain('/assets/pico.min.css'); expect(html).toContain('/assets/lightbox.min.css'); - expect(html).toContain('.lb-nav a, .lb-nav a:hover, .lb-nav a:focus-visible { border: 0; box-shadow: none; outline: none; text-decoration: none; }'); + expect(html).toContain('/assets/bds.css'); expect(html).toContain('/assets/tag-cloud.js'); expect(html).toContain('rel="alternate" type="application/rss+xml"'); expect(html).toContain('href="/rss.xml"'); @@ -484,8 +484,7 @@ describe('BlogGenerationEngine', () => { const indexPath = path.join(tempDir, 'html', 'index.html'); const html = await readFile(indexPath, 'utf-8'); expect(html).toContain('href="/assets/pico.green.min.css"'); - expect(html).toContain('@media only screen and (prefers-color-scheme: dark)'); - expect(html).toContain('--pico-background-color: #13171f;'); + expect(html).toContain('/assets/bds.css'); }); it('generates single post pages at /{year}/{month}/{day}/{slug}/index.html', async () => { @@ -533,14 +532,7 @@ describe('BlogGenerationEngine', () => { expect(html).toContain('href="/category/article/"'); expect(html).toContain('href="/tag/css-only/"'); expect(html).toContain('style="--bubble-accent: #22aa88;"'); - expect(html).toContain('background: var(--bubble-bg, var(--bubble-accent));'); - expect(html).toContain('color: #000;'); - expect(html).toContain('.single-post-taxonomy-bubble-category {'); - expect(html).toContain('--bubble-accent: var(--pico-ins-color'); - expect(html).toContain('--bubble-bg: var(--pico-ins-color'); - expect(html).toContain('.single-post-taxonomy-bubble-tag {'); - expect(html).toContain('--bubble-accent: var(--pico-del-color'); - expect(html).toContain('--bubble-bg: var(--pico-del-color'); + expect(html).toContain('/assets/bds.css'); const categoryIndex = html.indexOf('single-post-taxonomy-bubble-category'); const tagIndex = html.indexOf('single-post-taxonomy-bubble-tag'); diff --git a/tests/engine/GenerationRouteRendererFactory.test.ts b/tests/engine/GenerationRouteRendererFactory.test.ts index b45209d..39a03cd 100644 --- a/tests/engine/GenerationRouteRendererFactory.test.ts +++ b/tests/engine/GenerationRouteRendererFactory.test.ts @@ -181,6 +181,6 @@ describe('GenerationRouteRendererFactory', () => { expect(html).toContain('class="macro-youtube"'); expect(html).toContain('youtube.com/embed/dQw4w9WgXcQ?rel=0'); - expect(html).toContain('.macro-youtube, .macro-vimeo { margin-bottom: 1rem; }'); + expect(html).toContain('/assets/bds.css'); }); }); diff --git a/tests/engine/PreviewServer.test.ts b/tests/engine/PreviewServer.test.ts index ad35874..09ba173 100644 --- a/tests/engine/PreviewServer.test.ts +++ b/tests/engine/PreviewServer.test.ts @@ -318,6 +318,7 @@ describe('PreviewServer', () => { expect(rootHtml).toContain('href="/assets/lightbox.min.css"'); expect(rootHtml).toContain('href="/assets/highlight.min.css"'); expect(rootHtml).toContain('href="/assets/vanilla-calendar.min.css"'); + expect(rootHtml).toContain('href="/assets/bds.css"'); expect(rootHtml).toContain('src="/assets/lightbox.min.js"'); expect(rootHtml).toContain('src="/assets/highlight.min.js"'); expect(rootHtml).toContain('src="/assets/code-enhancements.js"'); @@ -360,6 +361,18 @@ describe('PreviewServer', () => { expect(tagCloudJsResponse.status).toBe(200); expect(tagCloudJsResponse.headers.get('content-type')).toContain('application/javascript'); + const bdsCssResponse = await fetch(`${server.getBaseUrl()}/assets/bds.css`); + expect(bdsCssResponse.status).toBe(200); + expect(bdsCssResponse.headers.get('content-type')).toContain('text/css'); + const bdsCss = await bdsCssResponse.text(); + expect(bdsCss).toContain('.blog-menu'); + expect(bdsCss).toContain('.post {'); + expect(bdsCss).toContain('.single-post-taxonomy-bubble'); + expect(bdsCss).toContain('.archive-day-separator'); + expect(bdsCss).toContain('.not-found'); + expect(bdsCss).toContain('--pico-background-color: #13171f;'); + expect(bdsCss).toContain('.lb-nav a, .lb-nav a:hover, .lb-nav a:focus-visible'); + const lightboxPrevImageResponse = await fetch(`${server.getBaseUrl()}/images/prev.png`); expect(lightboxPrevImageResponse.status).toBe(200); expect(lightboxPrevImageResponse.headers.get('content-type')).toContain('image/png'); @@ -663,7 +676,7 @@ describe('PreviewServer', () => { expect(html).toContain(''); expect(html).toContain('href="/assets/pico.green.min.css"'); - expect(html).toContain('--pico-background-color: #13171f;'); + expect(html).toContain('/assets/bds.css'); }); it('limits list routes to 50 posts', async () => { @@ -757,12 +770,7 @@ describe('PreviewServer', () => { const separatorCount = (html.match(/class="archive-day-separator"/g) || []).length; expect(separatorCount).toBe(1); - expect(html).toContain('.archive-day-separator { position: relative; height: 2px;'); - expect(html).toContain('color: var(--pico-color, var(--color));'); - expect(html).toContain('border-top: 1px solid currentColor;'); - expect(html).toContain('opacity: .18;'); - expect(html).toContain('.archive-day-separator::before'); - expect(html).toContain('linear-gradient(to right, transparent 0%, transparent 18%, currentColor 58%, transparent 92%, transparent 100%)'); + expect(html).toContain('/assets/bds.css'); }); it('supports day-and-slug post route', async () => { @@ -784,7 +792,7 @@ describe('PreviewServer', () => { const html = await response.text(); expect(html).toContain('Single Post'); expect(html).toContain('data-template="single-post"'); - expect(html).toContain('.single-post { margin: 0; padding: 0; background: transparent; border: 0; box-shadow: none; }'); + expect(html).toContain('/assets/bds.css'); }); it('resets lightbox nav anchor hover and focus styles to avoid frame artifacts over images', async () => { @@ -808,7 +816,7 @@ describe('PreviewServer', () => { await server.start(0); const html = await (await fetch(`${server.getBaseUrl()}/2025/2/14/lightbox-style-post/`)).text(); - expect(html).toContain('.lb-nav a, .lb-nav a:hover, .lb-nav a:focus-visible { border: 0; box-shadow: none; outline: none; text-decoration: none; }'); + expect(html).toContain('/assets/bds.css'); }); it('keeps code blocks constrained to post column width with horizontal overflow inside the block', async () => { @@ -824,9 +832,7 @@ describe('PreviewServer', () => { await server.start(0); const html = await (await fetch(`${server.getBaseUrl()}/`)).text(); - expect(html).toContain('.post { border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); padding: 1rem; background: var(--pico-card-background-color, var(--card-background-color)); min-width: 0; }'); - expect(html).toContain('.post pre { position: relative; overflow-x: auto; max-width: 100%;'); - expect(html).toContain('.post pre code { display: block; font-size: .88rem; line-height: 1.5; white-space: pre; }'); + expect(html).toContain('/assets/bds.css'); }); it('renders single post title as h1', async () => { @@ -903,14 +909,7 @@ describe('PreviewServer', () => { expect(html).toContain('href="/category/article/"'); expect(html).toContain('href="/tag/css-only/"'); expect(html).toContain('style="--bubble-accent: #22aa88;"'); - expect(html).toContain('background: var(--bubble-bg, var(--bubble-accent));'); - expect(html).toContain('color: #000;'); - expect(html).toContain('.single-post-taxonomy-bubble-category {'); - expect(html).toContain('--bubble-accent: var(--pico-ins-color'); - expect(html).toContain('--bubble-bg: var(--pico-ins-color'); - expect(html).toContain('.single-post-taxonomy-bubble-tag {'); - expect(html).toContain('--bubble-accent: var(--pico-del-color'); - expect(html).toContain('--bubble-bg: var(--pico-del-color'); + expect(html).toContain('/assets/bds.css'); const categoryIndex = html.indexOf('single-post-taxonomy-bubble-category'); const tagIndex = html.indexOf('single-post-taxonomy-bubble-tag'); @@ -993,9 +992,8 @@ describe('PreviewServer', () => { expect(html).toContain('href="/2025/03/10/source-post"'); expect(html).toContain('href="/2025/03/12/a-very-long-slug-post-that-exceeds-thirty-characters"'); - // Backlinks use pico accent color - expect(html).toContain('.single-post-backlink-bubble'); - expect(html).toContain('--pico-primary'); + // Backlinks use pico accent color (in external bds.css) + expect(html).toContain('/assets/bds.css'); // Backlinks section is after the article const articleEndIndex = html.indexOf(''); @@ -1836,7 +1834,7 @@ describe('PreviewServer', () => { expect(html).toContain('class="macro-youtube"'); expect(html).toContain('youtube.com/embed/dQw4w9WgXcQ?rel=0'); - expect(html).toContain('.macro-youtube, .macro-vimeo { margin-bottom: 1rem; }'); + expect(html).toContain('/assets/bds.css'); }); it('resolves gallery linked images via post-media links even when media.linkedPostIds is empty', async () => {