@nu-appdev/northwestern-starlight-theme 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,7 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [1.0.0] - 2025-06-01
10
+ ## [1.1.0] - 2026-03-25
11
+
12
+ ### Added
13
+
14
+ - Automatic favicon — the theme ships Northwestern's official favicon and applies it without touching `public/`. Consumers can still override it with their own `favicon` in Starlight config.
15
+ - `homepage.imageWidth` option to control hero image size. Defaults to `500px`, useful for wide lockup images that need more space.
16
+
17
+ ### Fixed
18
+
19
+ - `showTitle: false` now works in the centered hero layout (previously only worked in split layout).
20
+ - Search result titles and headings no longer appear white-on-white in light mode. The `.header a` selector was too broad and bled into Pagefind's search modal.
21
+ - Search input focus ring now uses the Northwestern purple treatment instead of the browser's default blue outline.
22
+
23
+ ### Changed
24
+
25
+ - Release workflow uses npm trusted publishing (OIDC) instead of long-lived tokens.
26
+
27
+ ## [1.0.0] - 2026-03-25
11
28
 
12
29
  - Initial public release
13
30
  - Northwestern Purple color palette mapped to Starlight's accent system
@@ -17,5 +34,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
34
  - OpenAPI plugin compatibility with method badge preservation
18
35
  - Reduced motion support for transitions
19
36
 
20
- [Unreleased]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.0.0...HEAD
37
+ [Unreleased]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.1.0...HEAD
38
+ [1.1.0]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.0.0...v1.1.0
21
39
  [1.0.0]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/releases/tag/v1.0.0
package/index.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import { copyFileSync, mkdirSync, readFileSync } from "node:fs";
2
+ import type { IncomingMessage, ServerResponse } from "node:http";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
1
5
  import type { StarlightPlugin } from "@astrojs/starlight/types";
2
6
 
3
7
  export interface NorthwesternHomepageConfig {
@@ -17,6 +21,15 @@ export interface NorthwesternHomepageConfig {
17
21
  * @default true
18
22
  */
19
23
  showTitle?: boolean;
24
+
25
+ /**
26
+ * Maximum width of the hero image in the centered layout, in pixels.
27
+ *
28
+ * Use a larger value for wide lockup images (e.g., `"750px"`, `"1000px"`).
29
+ *
30
+ * @default "500px"
31
+ */
32
+ imageWidth?: string;
20
33
  }
21
34
 
22
35
  export interface NorthwesternThemeConfig {
@@ -53,12 +66,27 @@ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
53
66
 
54
67
  export default function northwesternTheme(config: NorthwesternThemeConfig = {}): StarlightPlugin {
55
68
  const { homepage = {}, mermaid = true } = config;
56
- const themeConfig = { homepage: { layout: homepage.layout ?? "centered", showTitle: homepage.showTitle ?? true } };
69
+ const themeConfig = {
70
+ homepage: {
71
+ layout: homepage.layout ?? "centered",
72
+ showTitle: homepage.showTitle ?? true,
73
+ imageWidth: homepage.imageWidth ?? "500px",
74
+ },
75
+ };
57
76
 
58
77
  return {
59
78
  name: "northwestern-starlight-theme",
60
79
  hooks: {
61
80
  async "config:setup"({ config: starlightConfig, updateConfig, addIntegration, logger }) {
81
+ // Apply default favicon if consumer hasn't set one
82
+ const consumerSetFavicon =
83
+ starlightConfig.favicon &&
84
+ (starlightConfig.favicon as unknown as { href: string }).href !== "/favicon.svg";
85
+
86
+ // Read favicon bytes once for the Vite plugin to serve
87
+ const faviconPath = join(dirname(fileURLToPath(import.meta.url)), "src", "favicon.png");
88
+ const faviconBytes = consumerSetFavicon ? null : readFileSync(faviconPath);
89
+
62
90
  addIntegration({
63
91
  name: "northwestern-theme-config",
64
92
  hooks: {
@@ -77,14 +105,54 @@ export default function northwesternTheme(config: NorthwesternThemeConfig = {}):
77
105
  }
78
106
  },
79
107
  },
108
+ // Serve the bundled favicon during dev — no file touches public/
109
+ ...(faviconBytes
110
+ ? [
111
+ {
112
+ name: "northwestern-theme-favicon",
113
+ configureServer(server: {
114
+ middlewares: {
115
+ use(
116
+ fn: (
117
+ req: IncomingMessage,
118
+ res: ServerResponse,
119
+ next: () => void,
120
+ ) => void,
121
+ ): void;
122
+ };
123
+ }) {
124
+ server.middlewares.use((req, res, next) => {
125
+ if (req.url === "/favicon.png") {
126
+ res.setHeader("Content-Type", "image/png");
127
+ res.setHeader(
128
+ "Cache-Control",
129
+ "public, max-age=31536000, immutable",
130
+ );
131
+ res.end(faviconBytes);
132
+ } else {
133
+ next();
134
+ }
135
+ });
136
+ },
137
+ },
138
+ ]
139
+ : []),
80
140
  ],
81
141
  },
82
142
  });
83
143
  },
144
+ // Copy favicon into build output so nothing touches public/
145
+ "astro:build:done": ({ dir }) => {
146
+ if (faviconBytes) {
147
+ const dest = join(fileURLToPath(dir), "favicon.png");
148
+ mkdirSync(dirname(dest), { recursive: true });
149
+ copyFileSync(faviconPath, dest);
150
+ }
151
+ },
84
152
  },
85
153
  });
86
154
  // Detect mermaid availability
87
- let mermaidEnabled = mermaid !== false;
155
+ let mermaidEnabled = mermaid;
88
156
 
89
157
  if (mermaidEnabled) {
90
158
  const hasMermaid = await canResolve("astro-mermaid");
@@ -102,6 +170,7 @@ export default function northwesternTheme(config: NorthwesternThemeConfig = {}):
102
170
  }
103
171
 
104
172
  updateConfig({
173
+ ...(consumerSetFavicon ? {} : { favicon: "/favicon.png" }),
105
174
  components: {
106
175
  ...(starlightConfig.components ?? {}),
107
176
  Hero:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-appdev/northwestern-starlight-theme",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "A Northwestern-branded theme for Astro Starlight",
5
5
  "license": "MIT",
6
6
  "author": "Danny Foster <danny@northwestern.edu>",
@@ -7,11 +7,12 @@ const PAGE_TITLE_ID = "_top";
7
7
  const { data } = Astro.locals.starlightRoute.entry;
8
8
  const { title = data.title, tagline, image, actions = [] } = data.hero || {};
9
9
 
10
+ const imageWidthPx = parseInt(themeConfig.homepage.imageWidth, 10) || 500;
11
+
10
12
  const imageAttrs = {
11
13
  loading: "eager" as const,
12
14
  decoding: "async" as const,
13
- width: 400,
14
- height: 400,
15
+ width: imageWidthPx,
15
16
  alt: image?.alt || "",
16
17
  };
17
18
 
@@ -32,9 +33,10 @@ if (image) {
32
33
  const hasImage = Boolean(darkImage || lightImage || rawHtml);
33
34
  const isSplit = hasImage && themeConfig.homepage.layout === "split";
34
35
  const showTitle = themeConfig.homepage.showTitle;
36
+ const imageWidth = themeConfig.homepage.imageWidth;
35
37
  ---
36
38
 
37
- <div class:list={["hero", { "hero--split": isSplit }]}>
39
+ <div class:list={["hero", { "hero--split": isSplit }]} style={`--hero-image-width: ${imageWidth};`}>
38
40
  {
39
41
  isSplit ? (
40
42
  <>
@@ -81,7 +83,7 @@ const showTitle = themeConfig.homepage.showTitle;
81
83
  </div>
82
84
  )}
83
85
  <div class="sl-flex copy">
84
- <h1 id={PAGE_TITLE_ID} data-page-title set:html={title} />
86
+ {showTitle && <h1 id={PAGE_TITLE_ID} data-page-title set:html={title} />}
85
87
  {tagline && <div class="tagline" set:html={tagline} />}
86
88
  </div>
87
89
  {actions.length > 0 && (
@@ -163,7 +165,7 @@ const showTitle = themeConfig.homepage.showTitle;
163
165
  .hero-image-centered .hero-image,
164
166
  .hero-image-centered .hero-html {
165
167
  object-fit: contain;
166
- width: min(90%, 500px);
168
+ width: min(90%, var(--hero-image-width, 500px));
167
169
  height: auto;
168
170
  }
169
171
 
Binary file
@@ -7,7 +7,8 @@
7
7
 
8
8
  @layer northwestern {
9
9
  .site-title,
10
- .header a,
10
+ .header > a,
11
+ .header > nav a,
11
12
  .social-icons a,
12
13
  starlight-theme-select,
13
14
  starlight-theme-select label,
@@ -58,6 +59,13 @@
58
59
  line-height: 1 !important;
59
60
  }
60
61
 
62
+ /* stylelint-disable-next-line selector-id-pattern -- Starlight's own ID */
63
+ #starlight__search input:focus {
64
+ --pagefind-ui-border: var(--nu-focus-border) !important;
65
+ outline: none !important;
66
+ box-shadow: 0 0 0 0.25rem var(--nu-focus-shadow-color) !important;
67
+ }
68
+
61
69
  site-search button[data-open-modal] > svg {
62
70
  flex-shrink: 0;
63
71
  align-self: center;
package/src/virtual.d.ts CHANGED
@@ -3,6 +3,7 @@ declare module "virtual:northwestern-theme/config" {
3
3
  homepage: {
4
4
  layout: "centered" | "split";
5
5
  showTitle: boolean;
6
+ imageWidth: string;
6
7
  };
7
8
  };
8
9
  export default config;