From 1068d6e060432cb6efc88b040e51b7a8732ef118 Mon Sep 17 00:00:00 2001 From: Steven Le <387282+stevenle@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:36:06 -0800 Subject: [PATCH 1/2] fix(root): prioritize configured stylesheet entries --- packages/root/src/core/config.ts | 19 +++++++++++++++++++ packages/root/src/render/render.tsx | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/packages/root/src/core/config.ts b/packages/root/src/core/config.ts index a8059c8c0..83ee712d5 100644 --- a/packages/root/src/core/config.ts +++ b/packages/root/src/core/config.ts @@ -33,6 +33,25 @@ export interface RootUserConfig { exclude?: RegExp[]; }; + /** + * Config for manually injecting stylesheet entries as + * `` tags. + */ + styles?: { + /** + * Project-root-relative stylesheet files to include on every rendered + * page, e.g. `styles/index.css`. + * + * Entries are normalized as URL paths, so both `styles/index.css` and + * `/styles/index.css` resolve to `/styles/index.css`. + * + * Manual entries are injected first, then auto-collected CSS + * dependencies are merged after. Duplicate URLs are de-duped so each + * stylesheet is only injected once. + */ + entries?: string[]; + }; + /** * Config options for localization and internationalization. */ diff --git a/packages/root/src/render/render.tsx b/packages/root/src/render/render.tsx index bbe6e4f46..a41a0c806 100644 --- a/packages/root/src/render/render.tsx +++ b/packages/root/src/render/render.tsx @@ -227,6 +227,12 @@ export class Renderer { }); } + // Merge user-configured stylesheet entries first. This allows global + // entries to appear before auto-collected stylesheets. + this.getConfiguredStyleEntries().forEach((styleEntry) => { + cssDeps.add(styleEntry); + }); + // Parse the HTML for custom elements that are found within the project // and automatically inject the script deps for them. await this.collectElementDeps(mainHtml, jsDeps, cssDeps); @@ -580,6 +586,16 @@ export class Renderer { return orderedSitemap; } + + private getConfiguredStyleEntries() { + const styleEntries = this.rootConfig.styles?.entries || []; + const basePath = this.rootConfig.base || '/'; + return styleEntries + .map((entry) => entry.trim()) + .filter((entry) => entry) + .map((entry) => normalizeStyleEntry(entry, basePath)); + } + private async renderHtml(html: string, options?: RenderHtmlOptions) { const htmlAttrs = options?.htmlAttrs || {}; const headAttrs = options?.headAttrs || {}; @@ -860,6 +876,11 @@ function sortLocales(a: string, b: string) { return a.localeCompare(b); } +function normalizeStyleEntry(entry: string, basePath: string) { + const normalizedEntry = normalizeUrlPath(entry.replace(/^\.\//, '')); + return normalizeUrlPath(`${basePath}/${normalizedEntry}`); +} + function guessContentType(ext: string): string { const normalized = ext.trim().toLowerCase().replace(/^\./, ''); return CONTENT_TYPES[normalized] || 'application/octet-stream'; From 7a811a5355a41817bf2b967ed45104136379d6d9 Mon Sep 17 00:00:00 2001 From: Steven Le Date: Tue, 24 Feb 2026 21:19:37 -0800 Subject: [PATCH 2/2] chore: add changeset --- .changeset/rare-lemons-whisper.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rare-lemons-whisper.md diff --git a/.changeset/rare-lemons-whisper.md b/.changeset/rare-lemons-whisper.md new file mode 100644 index 000000000..b280aeef5 --- /dev/null +++ b/.changeset/rare-lemons-whisper.md @@ -0,0 +1,5 @@ +--- +'@blinkk/root': patch +--- + +feat: add `styles.entries` config for injecting global stylesheets (#929)