@nu-appdev/northwestern-starlight-theme 1.3.0 → 1.3.2

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,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.2] - 2026-03-30
11
+
12
+ ### Fixed
13
+
14
+ - H1/H2 now use Noto Serif (`--nu-font-display`), H3–H6 use Poppins (`--nu-font-heading`). Sizes, weights, and colors match the [Department 4.0 stylesheet](https://common.northwestern.edu/dept/4.0/css/styles.css).
15
+ - Poppins 400 loaded the Light file instead of Regular. Poppins 500 (Medium) added from v4 CDN.
16
+ - Table headers now use `--nu-purple-100` with white text.
17
+ - Table striping moved to odd rows (`#f9f6ff`); dark mode uses `rgb(78 42 132 / 10%)`.
18
+ - `--nu-purple-surface` corrected to `#f9f6ff`, `--nu-purple-surface-subtle` to `#f3f0f7`.
19
+ - Blockquote background uses `#f9f6ff` instead of translucent purple.
20
+ - Horizontal rules use 3px `--nu-purple-10` instead of 1px gray.
21
+ - List markers use `--nu-purple-100` instead of `--nu-purple-40`.
22
+
23
+ ## [1.3.1] - 2026-03-27
24
+
25
+ ### Fixed
26
+
27
+ - Tooltips on mobile rendered outside the viewport near screen edges. Horizontal position now clamps with 8px padding; arrow shifts to track the trigger.
28
+ - Tooltips stayed visible while scrolling on touch devices. Dismisses when the trigger moves 50px from its open position or leaves the viewport.
29
+ - Tapping a tooltip after scroll-dismiss had no effect. Trigger now blurs on scroll-dismiss so the next tap re-focuses.
30
+ - Tooltips near the top of the page overlapped Starlight's fixed header and "On this page" dropdown. Flips to bottom when space above is insufficient (100px header safe zone).
31
+
10
32
  ## [1.3.0] - 2026-03-26
11
33
 
12
34
  ### Added
@@ -121,7 +143,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
121
143
  - OpenAPI plugin compatibility with method badge preservation
122
144
  - Reduced motion support for transitions
123
145
 
124
- [Unreleased]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.3.0...HEAD
146
+ [Unreleased]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.3.2...HEAD
147
+ [1.3.2]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.3.1...v1.3.2
148
+ [1.3.1]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.3.0...v1.3.1
125
149
  [1.3.0]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.2.0...v1.3.0
126
150
  [1.2.0]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.1.1...v1.2.0
127
151
  [1.1.1]: https://github.com/NIT-Administrative-Systems/northwestern-starlight-theme/compare/v1.1.0...v1.1.1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-appdev/northwestern-starlight-theme",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "A Northwestern-branded theme for Astro Starlight",
5
5
  "license": "MIT",
6
6
  "author": "Danny Foster <danny@northwestern.edu>",
@@ -130,6 +130,7 @@ const imageWidth = themeConfig.homepage.imageWidth;
130
130
  line-height: var(--sl-line-height-headings);
131
131
  font-weight: 600;
132
132
  color: var(--nu-purple-100);
133
+ box-shadow: none;
133
134
  }
134
135
 
135
136
  :root:not([data-theme="light"]) h1 {
@@ -66,15 +66,25 @@ if (resolvedColor) {
66
66
  if (!trigger || !content) return;
67
67
 
68
68
  let isVisible = false;
69
+ let openY = 0;
69
70
  const hasPopover = "showPopover" in content;
71
+ const preferredSide = wrapper.classList.contains("nu-tooltip--bottom") ? "bottom" : "top";
70
72
 
71
73
  function onScroll() {
72
- if (isVisible) position();
74
+ if (!isVisible) return;
75
+ const rect = trigger!.getBoundingClientRect();
76
+ if (rect.bottom < 0 || rect.top > window.innerHeight || Math.abs(rect.top - openY) > 50) {
77
+ trigger!.blur();
78
+ hide();
79
+ return;
80
+ }
81
+ position();
73
82
  }
74
83
 
75
84
  function show() {
76
85
  if (isVisible) return;
77
86
  isVisible = true;
87
+ openY = trigger!.getBoundingClientRect().top;
78
88
  if (hasPopover) {
79
89
  content!.showPopover();
80
90
  } else {
@@ -102,17 +112,52 @@ if (resolvedColor) {
102
112
 
103
113
  function position() {
104
114
  const rect = trigger!.getBoundingClientRect();
105
- const side = wrapper.classList.contains("nu-tooltip--bottom") ? "bottom" : "top";
115
+ const preferred = preferredSide;
106
116
  const gap = 8;
117
+ const pad = 8;
118
+ const vw = document.documentElement.clientWidth;
119
+
120
+ // Measure tooltip height to decide if we need to flip
121
+ content!.style.transform = "";
122
+ content!.style.left = "0";
123
+ content!.style.top = "0";
124
+ const tipHeight = content!.offsetHeight;
125
+ const tipWidth = content!.offsetWidth;
126
+
127
+ // Reserve space for fixed header + mobile TOC (~100px)
128
+ const headerZone = 100;
129
+
130
+ // Auto-flip: if preferred side would overflow, use the other
131
+ const spaceAbove = rect.top - headerZone - gap;
132
+ const spaceBelow = window.innerHeight - rect.bottom - gap;
133
+ let side = preferred;
134
+ if (preferred === "top" && spaceAbove < tipHeight + pad && spaceBelow > spaceAbove) {
135
+ side = "bottom";
136
+ } else if (preferred === "bottom" && spaceBelow < tipHeight + pad && spaceAbove > spaceBelow) {
137
+ side = "top";
138
+ }
107
139
 
108
- content!.style.left = `${rect.left + rect.width / 2}px`;
140
+ // Update wrapper class so CSS arrow borders match
141
+ wrapper.classList.toggle("nu-tooltip--top", side === "top");
142
+ wrapper.classList.toggle("nu-tooltip--bottom", side === "bottom");
109
143
 
110
- if (side === "top") {
111
- content!.style.top = `${rect.top - gap}px`;
112
- content!.style.transform = "translate(-50%, -100%)";
113
- } else {
114
- content!.style.top = `${rect.bottom + gap}px`;
115
- content!.style.transform = "translate(-50%, 0)";
144
+ content!.style.top = side === "top"
145
+ ? `${rect.top - gap}px`
146
+ : `${rect.bottom + gap}px`;
147
+
148
+ let left = rect.left + rect.width / 2 - tipWidth / 2;
149
+ left = Math.max(pad, Math.min(left, vw - tipWidth - pad));
150
+
151
+ content!.style.left = `${left}px`;
152
+ content!.style.transform = side === "top" ? "translateY(-100%)" : "";
153
+
154
+ // Shift arrow to stay centered on trigger and flip its direction
155
+ const arrow = content!.querySelector<HTMLElement>(".nu-tooltip__arrow");
156
+ if (arrow) {
157
+ const tipCenter = left + tipWidth / 2;
158
+ const triggerCenter = rect.left + rect.width / 2;
159
+ const offset = triggerCenter - tipCenter;
160
+ arrow.style.left = `calc(50% + ${offset}px)`;
116
161
  }
117
162
  }
118
163
 
@@ -7,8 +7,9 @@
7
7
  padding: 1rem 1.5rem;
8
8
  margin: 2rem 0;
9
9
  color: var(--nu-color-text);
10
- background: rgb(228 224 238 / 42%);
10
+ background: #f9f6ff;
11
11
  border-inline-start: 10px solid var(--nu-purple-100);
12
+ border-radius: 0 0.75rem 0.75rem 0 !important;
12
13
  }
13
14
 
14
15
  .sl-markdown-content blockquote:not(:where(.not-content *)) p:last-child {
@@ -557,6 +557,7 @@
557
557
  .nu-property-table:not(.nu-property-table--table) .nu-property__head {
558
558
  flex-direction: column;
559
559
  gap: 0.25rem;
560
+ align-items: flex-start;
560
561
  }
561
562
 
562
563
  .nu-property-table:not(.nu-property-table--table) .nu-property__prop-col {
@@ -16,18 +16,20 @@
16
16
  }
17
17
 
18
18
  .sl-markdown-content table:not(:where(.not-content *)) > thead > tr > th {
19
- padding: 0.5rem 1rem;
20
- font-family: var(--nu-font-heading);
21
- font-weight: 600;
22
- color: var(--nu-black-80);
19
+ padding: 0.75rem 0.5rem;
20
+ font-family: var(--nu-font-body);
21
+ font-size: 1rem;
22
+ font-weight: 700;
23
+ color: #fff;
23
24
  text-align: left;
24
- background-color: var(--nu-purple-surface);
25
+ background-color: var(--nu-purple-100);
25
26
  border: 1px solid var(--nu-border-color);
27
+ border-right: 1px solid rgb(255 255 255 / 30%);
28
+ border-left: 1px solid rgb(255 255 255 / 30%);
26
29
  }
27
30
 
28
31
  :root:not([data-theme="light"]) .sl-markdown-content table:not(:where(.not-content *)) > thead > tr > th {
29
- color: var(--nu-color-text);
30
- background-color: rgb(78 42 132 / 14%);
32
+ background-color: rgb(78 42 132 / 40%);
31
33
  }
32
34
 
33
35
  .sl-markdown-content table:not(:where(.not-content *)) > tbody > tr > td {
@@ -46,15 +48,19 @@
46
48
  text-align: center;
47
49
  }
48
50
 
49
- .sl-markdown-content table:not(:where(.not-content *)) > tbody > tr:nth-child(even) {
50
- background-color: #f3f4f6;
51
+ .sl-markdown-content table:not(:where(.not-content *)) > tbody > tr:nth-child(odd) {
52
+ background-color: #f9f6ff;
53
+ }
54
+
55
+ .sl-markdown-content table:not(:where(.not-content *)) > tbody > tr:hover {
56
+ background-color: #f0f0f0;
57
+ }
58
+
59
+ :root:not([data-theme="light"]) .sl-markdown-content table:not(:where(.not-content *)) > tbody > tr:nth-child(odd) {
60
+ background-color: rgb(78 42 132 / 10%);
51
61
  }
52
62
 
53
- :root:not([data-theme="light"])
54
- .sl-markdown-content
55
- table:not(:where(.not-content *))
56
- > tbody
57
- > tr:nth-child(even) {
58
- background-color: #1e1e1e;
63
+ :root:not([data-theme="light"]) .sl-markdown-content table:not(:where(.not-content *)) > tbody > tr:hover {
64
+ background-color: rgb(255 255 255 / 5%);
59
65
  }
60
66
  }
@@ -39,7 +39,7 @@
39
39
  width: 6px;
40
40
  height: 6px;
41
41
  content: "";
42
- background: var(--nu-purple-40);
42
+ background: var(--nu-purple-100);
43
43
  }
44
44
 
45
45
  :root:not([data-theme="light"]) .sl-markdown-content ul:not(:where(.not-content *)) > li::before {
@@ -56,10 +56,16 @@
56
56
  border-color: var(--nu-purple-30);
57
57
  }
58
58
 
59
+ .sl-markdown-content mark:not(:where(.not-content *)),
60
+ .sl-markdown-content .highlight:not(:where(.not-content *)) {
61
+ padding: 3px;
62
+ background: rgb(228 224 238 / 50%);
63
+ }
64
+
59
65
  .sl-markdown-content hr:not(:where(.not-content *)) {
60
- height: 1px;
66
+ height: 3px;
61
67
  margin: 2.5rem 0;
62
- background: var(--nu-border-color);
68
+ background: var(--nu-purple-10);
63
69
  border: 0;
64
70
  }
65
71
 
@@ -78,7 +78,15 @@
78
78
  font-family: Poppins;
79
79
  font-style: normal;
80
80
  font-weight: 400;
81
- src: url("https://common.northwestern.edu/v8/css/fonts/Poppins-Light.woff") format("woff");
81
+ src: url("https://common.northwestern.edu/v8/css/fonts/Poppins-Regular.woff") format("woff");
82
+ font-display: swap;
83
+ }
84
+
85
+ @font-face {
86
+ font-family: Poppins;
87
+ font-style: normal;
88
+ font-weight: 500;
89
+ src: url("https://common.northwestern.edu/dept/4.0/css/fonts/poppins/poppins-v21-latin-500.woff2") format("woff2");
82
90
  font-display: swap;
83
91
  }
84
92
 
@@ -150,18 +158,81 @@
150
158
 
151
159
  h1,
152
160
  h2 {
153
- font-family: var(--nu-font-heading);
154
- }
155
-
156
- h1 {
157
- color: var(--sl-color-text-accent);
161
+ font-family: var(--nu-font-display);
158
162
  }
159
163
 
160
164
  h3,
161
165
  h4,
162
166
  h5,
163
167
  h6 {
164
- font-family: var(--nu-font-body);
168
+ font-family: var(--nu-font-heading);
169
+ }
170
+
171
+ .main-pane h1 {
172
+ display: inline;
173
+ font-size: clamp(2rem, 1rem + 3vw, 2.8125rem);
174
+ font-weight: 500;
175
+ line-height: 1.4;
176
+ color: #000;
177
+ box-shadow: inset 0 -0.55em 0 0 var(--nu-purple-10);
178
+ box-decoration-break: clone;
179
+ }
180
+
181
+ :root:not([data-theme="light"]) .main-pane h1 {
182
+ color: #fff;
183
+ box-shadow: inset 0 -0.55em 0 0 var(--nu-purple-140);
184
+ }
185
+
186
+ .sl-markdown-content h2 {
187
+ font-size: 1.875rem;
188
+ font-weight: 700;
189
+ line-height: 1.4;
190
+ color: var(--nu-purple-120);
191
+ }
192
+
193
+ .sl-markdown-content h3 {
194
+ font-size: 1.625rem;
195
+ font-weight: 500;
196
+ line-height: 1.4;
197
+ color: var(--nu-black-80);
198
+ }
199
+
200
+ .sl-markdown-content h4 {
201
+ font-size: 1.4375rem;
202
+ font-weight: 500;
203
+ line-height: 1.4;
204
+ color: var(--nu-purple-120);
205
+ }
206
+
207
+ .sl-markdown-content h5 {
208
+ font-size: 1.25rem;
209
+ font-weight: 500;
210
+ line-height: 1.4;
211
+ color: var(--nu-black-80);
212
+ }
213
+
214
+ .sl-markdown-content h6 {
215
+ font-size: 1.125rem;
216
+ font-weight: 500;
217
+ line-height: 1.4;
218
+ color: var(--nu-black-80);
219
+ }
220
+
221
+ :root:not([data-theme="light"]) .sl-markdown-content h2 {
222
+ color: var(--nu-purple-30);
223
+ }
224
+
225
+ :root:not([data-theme="light"]) .sl-markdown-content h3 {
226
+ color: var(--nu-color-text);
227
+ }
228
+
229
+ :root:not([data-theme="light"]) .sl-markdown-content h4 {
230
+ color: var(--nu-purple-30);
231
+ }
232
+
233
+ :root:not([data-theme="light"]) .sl-markdown-content h5,
234
+ :root:not([data-theme="light"]) .sl-markdown-content h6 {
235
+ color: var(--nu-color-text);
165
236
  }
166
237
 
167
238
  .site-title {
@@ -55,12 +55,12 @@
55
55
  --nu-transition-default: 200ms ease;
56
56
  --nu-transition-slow: 300ms ease-in-out;
57
57
 
58
- --nu-purple-surface: #f3f0f7;
59
- --nu-purple-surface-subtle: #f7f5f9;
58
+ --nu-purple-surface: #f9f6ff;
59
+ --nu-purple-surface-subtle: #f3f0f7;
60
60
  --nu-purple-tint: rgb(78 42 132 / 15%);
61
61
  --nu-purple-tint-strong: rgb(78 42 132 / 20%);
62
62
  --nu-surface-muted: #f9fafb;
63
- --nu-hover-bg: #f3f4f6;
63
+ --nu-hover-bg: #f0f0f0;
64
64
 
65
65
  --nu-focus-ring: var(--nu-purple-100);
66
66
  --nu-focus-border: #a795c2;
@@ -90,10 +90,10 @@
90
90
  --nu-focus-ring: var(--nu-purple-100);
91
91
  --nu-focus-shadow-color: rgb(78 42 132 / 25%);
92
92
 
93
- --nu-purple-surface: #f3f0f7;
94
- --nu-purple-surface-subtle: #f7f5f9;
93
+ --nu-purple-surface: #f9f6ff;
94
+ --nu-purple-surface-subtle: #f3f0f7;
95
95
  --nu-surface-muted: #f9fafb;
96
- --nu-hover-bg: #f3f4f6;
96
+ --nu-hover-bg: #f0f0f0;
97
97
  }
98
98
 
99
99
  :root:not([data-theme="light"]),