@nexpress/theme-default 0.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/dist/index.js ADDED
@@ -0,0 +1,1388 @@
1
+ // src/index.ts
2
+ import { defineTheme } from "@nexpress/theme";
3
+
4
+ // src/footer.tsx
5
+ import { DefaultFooter } from "./components/footer-columns.js";
6
+
7
+ // src/header.tsx
8
+ import { getI18nConfig } from "@nexpress/core";
9
+ import { getCachedNavigation, resolveAvailableLocales } from "@nexpress/next";
10
+ import { headers } from "next/headers";
11
+ import Link from "next/link";
12
+ import { DarkModeToggle } from "./components/dark-mode-toggle.js";
13
+ import { LanguagePicker } from "./components/language-picker.js";
14
+ import { MemberStatusWidget } from "./components/member-status-widget.js";
15
+ import { MobileNav } from "./components/mobile-nav.js";
16
+ import { jsx, jsxs } from "react/jsx-runtime";
17
+ async function DefaultHeader() {
18
+ const headerNav = await getCachedNavigation("header");
19
+ const i18n = getI18nConfig();
20
+ const showLanguagePicker = (i18n?.locales.length ?? 0) > 1;
21
+ let availableLocales = null;
22
+ if (showLanguagePicker) {
23
+ const headerList = await headers();
24
+ const pathname = headerList.get("x-np-pathname");
25
+ if (pathname) {
26
+ try {
27
+ availableLocales = await resolveAvailableLocales(pathname);
28
+ } catch {
29
+ availableLocales = null;
30
+ }
31
+ }
32
+ }
33
+ return /* @__PURE__ */ jsx("header", { className: "np-site-header", children: /* @__PURE__ */ jsxs("div", { className: "np-site-header-inner", children: [
34
+ /* @__PURE__ */ jsx(Link, { href: "/", className: "np-site-logo", children: "NexPress" }),
35
+ /* @__PURE__ */ jsx("nav", { className: "np-site-nav-desktop", "aria-label": "Primary", children: /* @__PURE__ */ jsx("ul", { className: "np-site-nav", children: headerNav.map((item, index) => /* @__PURE__ */ jsxs("li", { className: "np-site-nav-item", children: [
36
+ item.url ? /* @__PURE__ */ jsx(Link, { href: item.url, children: item.label }) : /* @__PURE__ */ jsx("span", { children: item.label }),
37
+ item.children && item.children.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "np-site-subnav", children: item.children.map((child, childIndex) => /* @__PURE__ */ jsx("li", { children: child.url ? /* @__PURE__ */ jsx(Link, { href: child.url, children: child.label }) : /* @__PURE__ */ jsx("span", { children: child.label }) }, `nav-${index.toString()}-${childIndex.toString()}`)) }) : null
38
+ ] }, `nav-${index.toString()}`)) }) }),
39
+ /* @__PURE__ */ jsxs("div", { className: "np-site-header-tools", children: [
40
+ /* @__PURE__ */ jsxs(
41
+ "form",
42
+ {
43
+ action: "/search",
44
+ method: "GET",
45
+ role: "search",
46
+ className: "np-site-search",
47
+ children: [
48
+ /* @__PURE__ */ jsx("label", { className: "sr-only", htmlFor: "np-site-search-input", children: "Search" }),
49
+ /* @__PURE__ */ jsx(
50
+ "input",
51
+ {
52
+ id: "np-site-search-input",
53
+ type: "search",
54
+ name: "q",
55
+ placeholder: "Search\u2026",
56
+ autoComplete: "off",
57
+ className: "np-site-search-input"
58
+ }
59
+ )
60
+ ]
61
+ }
62
+ ),
63
+ showLanguagePicker && i18n ? /* @__PURE__ */ jsx(
64
+ LanguagePicker,
65
+ {
66
+ locales: i18n.locales,
67
+ availableLocales: availableLocales ?? void 0
68
+ }
69
+ ) : null,
70
+ /* @__PURE__ */ jsx(DarkModeToggle, {}),
71
+ /* @__PURE__ */ jsx(MemberStatusWidget, {}),
72
+ /* @__PURE__ */ jsx(MobileNav, { items: headerNav })
73
+ ] })
74
+ ] }) });
75
+ }
76
+
77
+ // src/shell.tsx
78
+ import { NpColorSchemeScript } from "@nexpress/theme";
79
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
80
+ function DefaultShell({ children }) {
81
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
82
+ /* @__PURE__ */ jsx2(NpColorSchemeScript, {}),
83
+ children
84
+ ] });
85
+ }
86
+
87
+ // src/styles.ts
88
+ var defaultThemeCss = `
89
+ /* ----------------------------------------------------------------
90
+ * Typography ramp
91
+ * --------------------------------------------------------------- */
92
+ .np-page,
93
+ .np-post,
94
+ .np-post-list {
95
+ --np-content-max: 1100px;
96
+ --np-content-max-wide: 1300px;
97
+ }
98
+ .np-page h1,
99
+ .np-post-title,
100
+ .np-post-list-header h1 {
101
+ font-size: clamp(2rem, 4vw, 2.75rem);
102
+ line-height: 1.15;
103
+ letter-spacing: -0.02em;
104
+ font-weight: 800;
105
+ margin: 0 0 1rem;
106
+ }
107
+ .np-page h2,
108
+ .np-post-body h2 {
109
+ font-size: clamp(1.4rem, 2.4vw, 1.75rem);
110
+ letter-spacing: -0.015em;
111
+ margin: 2.5rem 0 1rem;
112
+ }
113
+ .np-page h3,
114
+ .np-post-body h3 {
115
+ font-size: 1.25rem;
116
+ margin: 2rem 0 0.75rem;
117
+ }
118
+ .np-page p,
119
+ .np-post-body p {
120
+ font-size: 1.0625rem;
121
+ line-height: 1.7;
122
+ margin: 0 0 1rem;
123
+ }
124
+ .np-post-body a {
125
+ color: var(--np-color-primary, #4f46e5);
126
+ text-decoration: underline;
127
+ text-underline-offset: 0.2em;
128
+ }
129
+ .np-post-body code {
130
+ background: var(--np-color-muted, #f1f5f9);
131
+ padding: 0.1em 0.35em;
132
+ border-radius: 4px;
133
+ font-size: 0.95em;
134
+ }
135
+ .np-post-body pre {
136
+ background: var(--np-color-muted, #f1f5f9);
137
+ padding: 1rem;
138
+ border-radius: var(--np-radius-md, 0.5rem);
139
+ overflow-x: auto;
140
+ font-size: 0.875rem;
141
+ line-height: 1.6;
142
+ }
143
+ .np-post-body blockquote {
144
+ border-inline-start: 3px solid var(--np-color-primary, #4f46e5);
145
+ margin: 1.5rem 0;
146
+ padding: 0.25rem 0 0.25rem 1rem;
147
+ color: var(--np-color-muted-foreground, #64748b);
148
+ font-style: italic;
149
+ }
150
+ .np-post-body img {
151
+ max-width: 100%;
152
+ height: auto;
153
+ border-radius: var(--np-radius-md, 0.5rem);
154
+ }
155
+
156
+ /* ----------------------------------------------------------------
157
+ * Header \u2014 sticky desktop bar + mobile drawer
158
+ * --------------------------------------------------------------- */
159
+ .np-site-header {
160
+ position: sticky;
161
+ top: 0;
162
+ z-index: 30;
163
+ background: color-mix(in oklch, var(--np-color-background, #fff) 92%, transparent);
164
+ backdrop-filter: blur(12px);
165
+ -webkit-backdrop-filter: blur(12px);
166
+ border-bottom: 1px solid var(--np-color-border, #e5e7eb);
167
+ }
168
+ .np-site-header-inner {
169
+ display: flex;
170
+ align-items: center;
171
+ gap: 1.5rem;
172
+ max-width: 1200px;
173
+ margin: 0 auto;
174
+ padding: 0.85rem 1.5rem;
175
+ }
176
+ .np-site-logo {
177
+ font-weight: 800;
178
+ font-size: 1.15rem;
179
+ text-decoration: none;
180
+ color: inherit;
181
+ letter-spacing: -0.02em;
182
+ white-space: nowrap;
183
+ }
184
+ .np-site-nav-desktop {
185
+ flex: 1;
186
+ }
187
+ .np-site-nav {
188
+ display: flex;
189
+ align-items: center;
190
+ gap: 1.4rem;
191
+ list-style: none;
192
+ padding: 0;
193
+ margin: 0;
194
+ }
195
+ .np-site-nav a {
196
+ color: var(--np-color-muted-foreground, #64748b);
197
+ text-decoration: none;
198
+ font-size: 0.9375rem;
199
+ font-weight: 500;
200
+ transition: color 0.15s ease;
201
+ }
202
+ .np-site-nav a:hover {
203
+ color: var(--np-color-foreground, #0f172a);
204
+ }
205
+ /* Sub-menu \u2014 desktop hover dropdown. Hidden until parent <li> is
206
+ * hovered or focus enters the subtree. Shallow drop, neutral
207
+ * surface so it inherits theme tokens automatically. */
208
+ .np-site-nav-item {
209
+ position: relative;
210
+ }
211
+ .np-site-subnav {
212
+ position: absolute;
213
+ top: 100%;
214
+ left: 0;
215
+ display: none;
216
+ min-width: 11rem;
217
+ padding: 0.5rem 0;
218
+ margin: 0;
219
+ list-style: none;
220
+ background: var(--np-color-card, #fff);
221
+ border: 1px solid var(--np-color-border, #e5e7eb);
222
+ border-radius: var(--np-radius-md, 0.5rem);
223
+ box-shadow: 0 4px 16px -8px rgba(0, 0, 0, 0.08);
224
+ z-index: 10;
225
+ }
226
+ .np-site-nav-item:hover > .np-site-subnav,
227
+ .np-site-nav-item:focus-within > .np-site-subnav {
228
+ display: block;
229
+ }
230
+ .np-site-subnav li {
231
+ padding: 0;
232
+ }
233
+ .np-site-subnav a {
234
+ display: block;
235
+ padding: 0.4rem 1rem;
236
+ font-size: 0.875rem;
237
+ }
238
+ .np-site-header-tools {
239
+ display: flex;
240
+ align-items: center;
241
+ gap: 0.6rem;
242
+ margin-inline-start: auto;
243
+ }
244
+ .np-site-search {
245
+ display: contents;
246
+ }
247
+ .np-site-search-input {
248
+ padding: 0.4rem 0.75rem;
249
+ font: inherit;
250
+ font-size: 0.875rem;
251
+ color: inherit;
252
+ background: var(--np-color-muted, #f8fafc);
253
+ border: 1px solid var(--np-color-border, #e5e7eb);
254
+ border-radius: var(--np-radius-md, 0.5rem);
255
+ width: 12rem;
256
+ transition: border-color 0.15s ease, box-shadow 0.15s ease, width 0.15s ease;
257
+ }
258
+ .np-site-search-input:focus {
259
+ outline: none;
260
+ border-color: var(--np-color-ring, #4f46e5);
261
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--np-color-ring, #4f46e5) 20%, transparent);
262
+ width: 14rem;
263
+ }
264
+
265
+ /* Mobile drawer machinery */
266
+ .np-mobile-nav-toggle {
267
+ display: none;
268
+ align-items: center;
269
+ justify-content: center;
270
+ width: 2.25rem;
271
+ height: 2.25rem;
272
+ padding: 0;
273
+ border: 1px solid var(--np-color-border, #e5e7eb);
274
+ border-radius: var(--np-radius-md, 0.5rem);
275
+ background: transparent;
276
+ color: inherit;
277
+ cursor: pointer;
278
+ }
279
+ .np-mobile-nav-toggle:hover {
280
+ background: var(--np-color-muted, #f8fafc);
281
+ }
282
+ .np-mobile-nav-overlay {
283
+ position: fixed;
284
+ inset: 0;
285
+ background: rgba(0, 0, 0, 0.4);
286
+ z-index: 40;
287
+ }
288
+ .np-mobile-nav-drawer {
289
+ position: fixed;
290
+ top: 0;
291
+ inset-inline-end: 0;
292
+ width: min(20rem, 85vw);
293
+ height: 100dvh;
294
+ background: var(--np-color-background, #fff);
295
+ border-inline-start: 1px solid var(--np-color-border, #e5e7eb);
296
+ z-index: 50;
297
+ transform: translateX(100%);
298
+ transition: transform 0.2s ease;
299
+ display: flex;
300
+ flex-direction: column;
301
+ }
302
+ .np-mobile-nav-drawer[data-open="true"] {
303
+ transform: translateX(0);
304
+ }
305
+ .np-mobile-nav-drawer-header {
306
+ display: flex;
307
+ align-items: center;
308
+ justify-content: space-between;
309
+ padding: 1rem 1.25rem;
310
+ border-bottom: 1px solid var(--np-color-border, #e5e7eb);
311
+ }
312
+ .np-mobile-nav-drawer-label {
313
+ font-weight: 700;
314
+ letter-spacing: 0.02em;
315
+ text-transform: uppercase;
316
+ font-size: 0.75rem;
317
+ color: var(--np-color-muted-foreground, #64748b);
318
+ }
319
+ .np-mobile-nav-close {
320
+ background: transparent;
321
+ border: none;
322
+ color: inherit;
323
+ cursor: pointer;
324
+ padding: 0.25rem;
325
+ border-radius: var(--np-radius-md, 0.5rem);
326
+ }
327
+ .np-mobile-nav-close:hover {
328
+ background: var(--np-color-muted, #f8fafc);
329
+ }
330
+ .np-mobile-nav-list {
331
+ list-style: none;
332
+ margin: 0;
333
+ padding: 0.75rem 0;
334
+ overflow-y: auto;
335
+ flex: 1;
336
+ }
337
+ .np-mobile-subnav,
338
+ .np-site-footer-subnav {
339
+ list-style: none;
340
+ margin: 0;
341
+ padding-left: 1.25rem;
342
+ }
343
+ .np-mobile-subnav a {
344
+ font-size: 0.9375rem;
345
+ }
346
+ .np-site-footer-subnav a {
347
+ font-size: 0.85rem;
348
+ opacity: 0.85;
349
+ }
350
+ .np-mobile-nav-list a {
351
+ display: block;
352
+ padding: 0.85rem 1.25rem;
353
+ text-decoration: none;
354
+ color: inherit;
355
+ font-size: 1rem;
356
+ border-bottom: 1px solid color-mix(in oklch, var(--np-color-border, #e5e7eb) 50%, transparent);
357
+ }
358
+ .np-mobile-nav-list a:hover {
359
+ background: var(--np-color-muted, #f8fafc);
360
+ }
361
+
362
+ @media (max-width: 768px) {
363
+ .np-site-nav-desktop,
364
+ .np-site-search,
365
+ .np-site-search-input {
366
+ display: none;
367
+ }
368
+ .np-mobile-nav-toggle {
369
+ display: inline-flex;
370
+ }
371
+ }
372
+ @media (min-width: 769px) {
373
+ .np-mobile-nav-drawer,
374
+ .np-mobile-nav-overlay {
375
+ display: none;
376
+ }
377
+ }
378
+
379
+ /* ----------------------------------------------------------------
380
+ * Footer \u2014 4-column grid, social strip, newsletter signup
381
+ * --------------------------------------------------------------- */
382
+ .np-site-footer {
383
+ margin-top: 6rem;
384
+ background: var(--np-color-muted, #f8fafc);
385
+ border-top: 1px solid var(--np-color-border, #e5e7eb);
386
+ }
387
+ .np-site-footer-inner {
388
+ max-width: 1200px;
389
+ margin: 0 auto;
390
+ padding: 3rem 1.5rem 1.5rem;
391
+ }
392
+ .np-site-footer-grid {
393
+ display: grid;
394
+ grid-template-columns: 1.4fr 1fr 1fr 1.2fr;
395
+ gap: 2.5rem;
396
+ align-items: start;
397
+ }
398
+ @media (max-width: 768px) {
399
+ .np-site-footer-grid {
400
+ grid-template-columns: 1fr 1fr;
401
+ gap: 2rem;
402
+ }
403
+ .np-site-footer-brand,
404
+ .np-site-footer-subscribe {
405
+ grid-column: span 2;
406
+ }
407
+ }
408
+ @media (max-width: 480px) {
409
+ .np-site-footer-grid {
410
+ grid-template-columns: 1fr;
411
+ }
412
+ .np-site-footer-brand,
413
+ .np-site-footer-subscribe {
414
+ grid-column: span 1;
415
+ }
416
+ }
417
+ .np-site-footer-col { min-width: 0; }
418
+ .np-site-footer-logo {
419
+ font-weight: 800;
420
+ font-size: 1.15rem;
421
+ text-decoration: none;
422
+ color: inherit;
423
+ letter-spacing: -0.02em;
424
+ }
425
+ .np-site-footer-tagline {
426
+ margin: 0.5rem 0 1rem;
427
+ color: var(--np-color-muted-foreground, #64748b);
428
+ font-size: 0.9rem;
429
+ line-height: 1.5;
430
+ }
431
+ .np-site-footer-heading {
432
+ font-size: 0.75rem;
433
+ text-transform: uppercase;
434
+ letter-spacing: 0.12em;
435
+ color: var(--np-color-muted-foreground, #64748b);
436
+ margin: 0 0 0.85rem;
437
+ font-weight: 700;
438
+ }
439
+ .np-site-footer-links {
440
+ list-style: none;
441
+ margin: 0;
442
+ padding: 0;
443
+ display: flex;
444
+ flex-direction: column;
445
+ gap: 0.55rem;
446
+ font-size: 0.9rem;
447
+ }
448
+ .np-site-footer-links a {
449
+ color: var(--np-color-muted-foreground, #64748b);
450
+ text-decoration: none;
451
+ transition: color 0.15s ease;
452
+ }
453
+ .np-site-footer-links a:hover {
454
+ color: var(--np-color-foreground, #0f172a);
455
+ }
456
+ .np-site-footer-social {
457
+ list-style: none;
458
+ margin: 1rem 0 0;
459
+ padding: 0;
460
+ display: flex;
461
+ gap: 0.5rem;
462
+ }
463
+ .np-site-footer-social a {
464
+ display: inline-flex;
465
+ align-items: center;
466
+ justify-content: center;
467
+ width: 2.25rem;
468
+ height: 2.25rem;
469
+ border: 1px solid var(--np-color-border, #e5e7eb);
470
+ border-radius: var(--np-radius-md, 0.5rem);
471
+ color: var(--np-color-muted-foreground, #64748b);
472
+ text-decoration: none;
473
+ transition: all 0.15s ease;
474
+ }
475
+ .np-site-footer-social a:hover {
476
+ color: var(--np-color-foreground, #0f172a);
477
+ border-color: var(--np-color-foreground, #0f172a);
478
+ transform: translateY(-1px);
479
+ }
480
+ .np-site-footer-subscribe-blurb {
481
+ margin: 0 0 0.75rem;
482
+ font-size: 0.85rem;
483
+ color: var(--np-color-muted-foreground, #64748b);
484
+ }
485
+ .np-site-footer-subscribe-form {
486
+ display: flex;
487
+ flex-direction: column;
488
+ gap: 0.5rem;
489
+ }
490
+ .np-site-footer-subscribe-form input[type="email"] {
491
+ padding: 0.55rem 0.75rem;
492
+ font: inherit;
493
+ font-size: 0.9rem;
494
+ border: 1px solid var(--np-color-border, #e5e7eb);
495
+ border-radius: var(--np-radius-md, 0.5rem);
496
+ background: var(--np-color-background, #fff);
497
+ color: inherit;
498
+ }
499
+ .np-site-footer-subscribe-form input[type="email"]:focus {
500
+ outline: none;
501
+ border-color: var(--np-color-ring, #4f46e5);
502
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--np-color-ring, #4f46e5) 20%, transparent);
503
+ }
504
+ .np-site-footer-subscribe-form button {
505
+ padding: 0.55rem 0.75rem;
506
+ font: inherit;
507
+ font-size: 0.9rem;
508
+ font-weight: 600;
509
+ background: var(--np-color-foreground, #0f172a);
510
+ color: var(--np-color-background, #fff);
511
+ border: none;
512
+ border-radius: var(--np-radius-md, 0.5rem);
513
+ cursor: pointer;
514
+ transition: opacity 0.15s ease;
515
+ }
516
+ .np-site-footer-subscribe-form button:hover { opacity: 0.9; }
517
+ .np-site-footer-subscribe-form button:disabled { opacity: 0.6; cursor: progress; }
518
+ .np-site-footer-subscribe-success {
519
+ margin: 0;
520
+ font-size: 0.9rem;
521
+ color: var(--np-color-foreground, #0f172a);
522
+ background: color-mix(in oklch, var(--np-color-primary, #4f46e5) 12%, transparent);
523
+ padding: 0.6rem 0.75rem;
524
+ border-radius: var(--np-radius-md, 0.5rem);
525
+ }
526
+ .np-site-footer-subscribe-error {
527
+ margin: 0;
528
+ font-size: 0.8rem;
529
+ color: var(--np-color-destructive, #b91c1c);
530
+ }
531
+ .np-site-footer-bottom {
532
+ margin-top: 2.5rem;
533
+ padding-top: 1.25rem;
534
+ border-top: 1px solid color-mix(in oklch, var(--np-color-border, #e5e7eb) 70%, transparent);
535
+ display: flex;
536
+ align-items: center;
537
+ justify-content: space-between;
538
+ gap: 1rem;
539
+ flex-wrap: wrap;
540
+ }
541
+ .np-site-footer-copy {
542
+ margin: 0;
543
+ font-size: 0.8rem;
544
+ color: var(--np-color-muted-foreground, #64748b);
545
+ }
546
+ .np-site-footer-meta {
547
+ display: flex;
548
+ list-style: none;
549
+ margin: 0;
550
+ padding: 0;
551
+ gap: 1rem;
552
+ font-size: 0.8rem;
553
+ }
554
+ .np-site-footer-meta a {
555
+ color: var(--np-color-muted-foreground, #64748b);
556
+ text-decoration: none;
557
+ }
558
+ .np-site-footer-meta a:hover { color: var(--np-color-foreground, #0f172a); }
559
+
560
+ /* ----------------------------------------------------------------
561
+ * Page templates: default, wide, landing, sidebar
562
+ * --------------------------------------------------------------- */
563
+ .np-page-default {
564
+ max-width: var(--np-content-max);
565
+ margin: 0 auto;
566
+ padding: 3rem 1.5rem 4rem;
567
+ }
568
+ .np-page-wide {
569
+ max-width: none;
570
+ margin: 0;
571
+ padding: 0;
572
+ }
573
+ .np-page-landing {
574
+ max-width: none;
575
+ margin: 0;
576
+ padding: 0;
577
+ }
578
+ .np-page-landing-blocks > * + * { margin-top: 0; }
579
+ .np-page-landing-hero {
580
+ max-width: var(--np-content-max-wide);
581
+ margin: 0 auto;
582
+ padding: 6rem 1.5rem 4rem;
583
+ text-align: center;
584
+ }
585
+ .np-page-landing-intro {
586
+ font-size: clamp(1.1rem, 1.6vw, 1.25rem);
587
+ color: var(--np-color-muted-foreground, #64748b);
588
+ max-width: 38rem;
589
+ margin: 1rem auto 0;
590
+ line-height: 1.6;
591
+ }
592
+ .np-page-sidebar {
593
+ max-width: var(--np-content-max-wide);
594
+ margin: 0 auto;
595
+ padding: 3rem 1.5rem 4rem;
596
+ display: grid;
597
+ grid-template-columns: minmax(0, 1fr) 18rem;
598
+ gap: 3rem;
599
+ }
600
+ @media (max-width: 900px) {
601
+ .np-page-sidebar { grid-template-columns: 1fr; }
602
+ .np-page-sidebar-aside { order: -1; }
603
+ }
604
+ .np-page-sidebar-aside {
605
+ position: sticky;
606
+ top: 5rem;
607
+ align-self: start;
608
+ font-size: 0.9rem;
609
+ }
610
+ .np-page-sidebar-placeholder {
611
+ border: 1px dashed var(--np-color-border, #e5e7eb);
612
+ border-radius: var(--np-radius-md, 0.5rem);
613
+ padding: 1rem;
614
+ }
615
+ .np-page-sidebar-placeholder-label {
616
+ margin: 0 0 0.5rem;
617
+ font-weight: 600;
618
+ font-size: 0.75rem;
619
+ text-transform: uppercase;
620
+ letter-spacing: 0.1em;
621
+ color: var(--np-color-muted-foreground, #64748b);
622
+ }
623
+ .np-page-sidebar-placeholder-hint {
624
+ margin: 0;
625
+ color: var(--np-color-muted-foreground, #64748b);
626
+ font-size: 0.85rem;
627
+ line-height: 1.5;
628
+ }
629
+ .np-page-sidebar-placeholder code {
630
+ background: var(--np-color-muted, #f1f5f9);
631
+ padding: 0.1em 0.3em;
632
+ border-radius: 3px;
633
+ font-size: 0.9em;
634
+ }
635
+
636
+ /* ----------------------------------------------------------------
637
+ * Post detail
638
+ * --------------------------------------------------------------- */
639
+ .np-post-default {
640
+ max-width: var(--np-content-max);
641
+ margin: 0 auto;
642
+ padding: 2.5rem 1.5rem 4rem;
643
+ }
644
+ .np-post-cover {
645
+ margin: 0 0 2rem;
646
+ aspect-ratio: 16 / 9;
647
+ border-radius: var(--np-radius-lg, 0.75rem);
648
+ overflow: hidden;
649
+ }
650
+ .np-post-cover img {
651
+ width: 100%;
652
+ height: 100%;
653
+ object-fit: cover;
654
+ display: block;
655
+ }
656
+ .np-post-header { margin-bottom: 2rem; }
657
+ .np-post-tags {
658
+ list-style: none;
659
+ margin: 0 0 0.75rem;
660
+ padding: 0;
661
+ display: flex;
662
+ flex-wrap: wrap;
663
+ gap: 0.4rem;
664
+ }
665
+ .np-post-tags a,
666
+ .np-post-tags span {
667
+ display: inline-block;
668
+ font-size: 0.75rem;
669
+ font-weight: 600;
670
+ text-transform: uppercase;
671
+ letter-spacing: 0.08em;
672
+ padding: 0.25rem 0.6rem;
673
+ border-radius: 999px;
674
+ background: var(--np-color-muted, #f1f5f9);
675
+ color: var(--np-color-muted-foreground, #64748b);
676
+ text-decoration: none;
677
+ }
678
+ .np-post-tags a:hover { color: var(--np-color-foreground, #0f172a); }
679
+ .np-post-excerpt {
680
+ font-size: 1.125rem;
681
+ color: var(--np-color-muted-foreground, #64748b);
682
+ margin: 0 0 1.25rem;
683
+ line-height: 1.6;
684
+ }
685
+ .np-post-meta {
686
+ display: flex;
687
+ flex-wrap: wrap;
688
+ gap: 1rem;
689
+ font-size: 0.875rem;
690
+ color: var(--np-color-muted-foreground, #64748b);
691
+ border-top: 1px solid var(--np-color-border, #e5e7eb);
692
+ padding-top: 1rem;
693
+ }
694
+ .np-post-meta-author {
695
+ font-weight: 600;
696
+ color: var(--np-color-foreground, #0f172a);
697
+ }
698
+ .np-post-body > * + * { margin-top: 1rem; }
699
+
700
+ /* ----------------------------------------------------------------
701
+ * Post list (blog index)
702
+ * --------------------------------------------------------------- */
703
+ .np-post-list {
704
+ max-width: var(--np-content-max-wide);
705
+ margin: 0 auto;
706
+ padding: 3rem 1.5rem 4rem;
707
+ }
708
+ .np-post-list-header {
709
+ margin-bottom: 2.5rem;
710
+ text-align: center;
711
+ }
712
+ .np-post-list-header h1 { margin: 0; }
713
+ .np-post-list-intro {
714
+ margin: 0.75rem auto 0;
715
+ max-width: 38rem;
716
+ color: var(--np-color-muted-foreground, #64748b);
717
+ font-size: 1.05rem;
718
+ line-height: 1.6;
719
+ }
720
+ .np-post-list-feature { margin-bottom: 2rem; }
721
+ .np-post-list-feature .np-post-card { display: block; }
722
+ .np-post-list-feature .np-post-card-link {
723
+ display: grid;
724
+ grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
725
+ gap: 0;
726
+ align-items: stretch;
727
+ background: var(--np-color-card, #fff);
728
+ border: 1px solid var(--np-color-border, #e5e7eb);
729
+ border-radius: var(--np-radius-lg, 0.75rem);
730
+ overflow: hidden;
731
+ text-decoration: none;
732
+ color: inherit;
733
+ transition: border-color 0.15s ease, transform 0.2s ease;
734
+ }
735
+ .np-post-list-feature .np-post-card-link:hover {
736
+ border-color: var(--np-color-foreground, #0f172a);
737
+ transform: translateY(-2px);
738
+ }
739
+ .np-post-list-feature .np-post-card-cover {
740
+ aspect-ratio: 16 / 10;
741
+ margin: 0;
742
+ overflow: hidden;
743
+ }
744
+ .np-post-list-feature .np-post-card-cover img {
745
+ width: 100%;
746
+ height: 100%;
747
+ object-fit: cover;
748
+ }
749
+ .np-post-list-feature .np-post-card-body {
750
+ padding: 1.75rem;
751
+ display: flex;
752
+ flex-direction: column;
753
+ gap: 0.5rem;
754
+ justify-content: center;
755
+ }
756
+ .np-post-list-feature .np-post-card-title {
757
+ font-size: clamp(1.4rem, 2.4vw, 1.85rem);
758
+ margin: 0;
759
+ letter-spacing: -0.015em;
760
+ }
761
+ @media (max-width: 768px) {
762
+ .np-post-list-feature .np-post-card-link { grid-template-columns: 1fr; }
763
+ .np-post-list-feature .np-post-card-cover { aspect-ratio: 16 / 9; }
764
+ }
765
+ .np-post-list-grid {
766
+ display: grid;
767
+ grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
768
+ gap: 1.5rem;
769
+ }
770
+ .np-post-card {
771
+ background: var(--np-color-card, #fff);
772
+ border: 1px solid var(--np-color-border, #e5e7eb);
773
+ border-radius: var(--np-radius-lg, 0.75rem);
774
+ overflow: hidden;
775
+ transition: border-color 0.15s ease, transform 0.2s ease;
776
+ }
777
+ .np-post-card:hover {
778
+ border-color: var(--np-color-foreground, #0f172a);
779
+ transform: translateY(-2px);
780
+ }
781
+ .np-post-card-link {
782
+ display: block;
783
+ text-decoration: none;
784
+ color: inherit;
785
+ }
786
+ .np-post-card-cover {
787
+ margin: 0;
788
+ aspect-ratio: 16 / 9;
789
+ overflow: hidden;
790
+ background: var(--np-color-muted, #f1f5f9);
791
+ }
792
+ .np-post-card-cover img {
793
+ width: 100%;
794
+ height: 100%;
795
+ object-fit: cover;
796
+ }
797
+ .np-post-card-body {
798
+ padding: 1.25rem;
799
+ display: flex;
800
+ flex-direction: column;
801
+ gap: 0.5rem;
802
+ }
803
+ .np-post-card-title {
804
+ margin: 0;
805
+ font-size: 1.15rem;
806
+ line-height: 1.3;
807
+ letter-spacing: -0.01em;
808
+ }
809
+ .np-post-card-excerpt {
810
+ margin: 0;
811
+ font-size: 0.9rem;
812
+ color: var(--np-color-muted-foreground, #64748b);
813
+ line-height: 1.55;
814
+ display: -webkit-box;
815
+ -webkit-line-clamp: 3;
816
+ -webkit-box-orient: vertical;
817
+ overflow: hidden;
818
+ }
819
+ .np-post-card-meta {
820
+ display: flex;
821
+ flex-wrap: wrap;
822
+ gap: 0.6rem;
823
+ font-size: 0.78rem;
824
+ color: var(--np-color-muted-foreground, #64748b);
825
+ margin-top: 0.25rem;
826
+ }
827
+ .np-post-list-empty header {
828
+ text-align: center;
829
+ padding: 4rem 1.5rem;
830
+ color: var(--np-color-muted-foreground, #64748b);
831
+ }
832
+ .np-post-list-empty h1 { color: var(--np-color-foreground, #0f172a); }
833
+
834
+ /* ----------------------------------------------------------------
835
+ * Pagination
836
+ * --------------------------------------------------------------- */
837
+ .np-pagination {
838
+ display: flex;
839
+ align-items: center;
840
+ justify-content: center;
841
+ gap: 0.75rem;
842
+ margin: 3rem auto 0;
843
+ }
844
+ .np-pagination-step,
845
+ .np-pagination-page,
846
+ .np-pagination-gap {
847
+ display: inline-flex;
848
+ align-items: center;
849
+ justify-content: center;
850
+ min-width: 2.25rem;
851
+ height: 2.25rem;
852
+ padding: 0 0.6rem;
853
+ font-size: 0.875rem;
854
+ border: 1px solid var(--np-color-border, #e5e7eb);
855
+ border-radius: var(--np-radius-md, 0.5rem);
856
+ text-decoration: none;
857
+ color: inherit;
858
+ background: transparent;
859
+ transition: all 0.15s ease;
860
+ }
861
+ .np-pagination-page:hover,
862
+ .np-pagination-step:hover { border-color: var(--np-color-foreground, #0f172a); }
863
+ .np-pagination-current {
864
+ background: var(--np-color-foreground, #0f172a);
865
+ color: var(--np-color-background, #fff);
866
+ border-color: var(--np-color-foreground, #0f172a);
867
+ }
868
+ .np-pagination-disabled {
869
+ color: var(--np-color-muted-foreground, #94a3b8);
870
+ border-color: var(--np-color-border, #e5e7eb);
871
+ pointer-events: none;
872
+ }
873
+ .np-pagination-pages {
874
+ list-style: none;
875
+ display: flex;
876
+ gap: 0.4rem;
877
+ margin: 0;
878
+ padding: 0;
879
+ }
880
+ .np-pagination-gap {
881
+ border-color: transparent;
882
+ cursor: default;
883
+ }
884
+
885
+ /* ----------------------------------------------------------------
886
+ * Color-mode toggle (Phase 11.5) + language picker (12.6)
887
+ * --------------------------------------------------------------- */
888
+ .np-color-scheme-toggle {
889
+ display: inline-flex;
890
+ align-items: center;
891
+ justify-content: center;
892
+ width: 2.25rem;
893
+ height: 2.25rem;
894
+ padding: 0;
895
+ border: 1px solid var(--np-color-border, #e5e7eb);
896
+ border-radius: var(--np-radius-md, 0.5rem);
897
+ background: transparent;
898
+ color: inherit;
899
+ cursor: pointer;
900
+ transition: background 0.15s ease, border-color 0.15s ease;
901
+ }
902
+ .np-color-scheme-toggle:hover {
903
+ background: var(--np-color-muted, #f8fafc);
904
+ border-color: var(--np-color-muted-foreground, #94a3b8);
905
+ }
906
+ .np-color-scheme-toggle:focus-visible {
907
+ outline: 2px solid var(--np-color-ring, #4f46e5);
908
+ outline-offset: 2px;
909
+ }
910
+ .np-color-scheme-toggle-placeholder {
911
+ width: 2.25rem;
912
+ height: 2.25rem;
913
+ border: 1px solid transparent;
914
+ }
915
+ .np-language-picker {
916
+ display: inline-flex;
917
+ align-items: center;
918
+ gap: 0.25rem;
919
+ font-size: 0.75rem;
920
+ text-transform: uppercase;
921
+ letter-spacing: 0.05em;
922
+ }
923
+ .np-language-picker-link {
924
+ display: inline-flex;
925
+ align-items: center;
926
+ justify-content: center;
927
+ min-width: 2rem;
928
+ height: 1.85rem;
929
+ padding: 0 0.55rem;
930
+ border-radius: var(--np-radius-md, 0.5rem);
931
+ text-decoration: none;
932
+ color: inherit;
933
+ opacity: 0.6;
934
+ transition: opacity 0.15s ease, background 0.15s ease;
935
+ }
936
+ .np-language-picker-link:hover {
937
+ opacity: 1;
938
+ background: var(--np-color-muted, #f8fafc);
939
+ }
940
+ .np-language-picker-link[data-active="true"] {
941
+ opacity: 1;
942
+ font-weight: 600;
943
+ background: var(--np-color-muted, #f8fafc);
944
+ }
945
+
946
+ /* ----------------------------------------------------------------
947
+ * Dark mode \u2014 re-skin the design tokens
948
+ * --------------------------------------------------------------- */
949
+ [data-theme="dark"] {
950
+ --np-color-background: oklch(0.145 0.004 285.823);
951
+ --np-color-foreground: oklch(0.985 0.001 106.423);
952
+ --np-color-muted: oklch(0.215 0.006 286.033);
953
+ --np-color-muted-foreground: oklch(0.711 0.008 285.879);
954
+ --np-color-border: oklch(0.269 0.006 286.033);
955
+ --np-color-card: oklch(0.18 0.005 285.5);
956
+ --np-color-card-foreground: oklch(0.985 0.001 106.423);
957
+ --np-color-accent: oklch(0.269 0.006 286.033);
958
+ --np-color-accent-foreground: oklch(0.985 0.001 106.423);
959
+ }
960
+ `.trim();
961
+
962
+ // src/templates/page-default.tsx
963
+ import { renderBlocks } from "@nexpress/blocks";
964
+ import { jsx as jsx3 } from "react/jsx-runtime";
965
+ function PageDefaultTemplate({ doc, blockCtx }) {
966
+ const blocks = doc.blocks;
967
+ const title = doc.title;
968
+ return /* @__PURE__ */ jsx3("div", { className: "np-page np-page-default", children: blocks ? renderBlocks(blocks, { ctx: blockCtx }) : /* @__PURE__ */ jsx3("h1", { children: title ?? "Untitled" }) });
969
+ }
970
+
971
+ // src/templates/page-landing.tsx
972
+ import { renderBlocks as renderBlocks2 } from "@nexpress/blocks";
973
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
974
+ function PageLandingTemplate({ doc, blockCtx }) {
975
+ const blocks = doc.blocks;
976
+ const title = doc.title ?? "Untitled";
977
+ const intro = doc.seoDescription;
978
+ return /* @__PURE__ */ jsx4("div", { className: "np-page np-page-landing", children: blocks && blocks.length > 0 ? /* @__PURE__ */ jsx4("div", { className: "np-page-landing-blocks", children: renderBlocks2(blocks, { ctx: blockCtx }) }) : /* @__PURE__ */ jsxs3("section", { className: "np-page-landing-hero", children: [
979
+ /* @__PURE__ */ jsx4("h1", { children: title }),
980
+ intro ? /* @__PURE__ */ jsx4("p", { className: "np-page-landing-intro", children: intro }) : null
981
+ ] }) });
982
+ }
983
+
984
+ // src/templates/page-sidebar.tsx
985
+ import { renderBlocks as renderBlocks3 } from "@nexpress/blocks";
986
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
987
+ function PageSidebarTemplate({ doc, blockCtx }) {
988
+ const blocks = doc.blocks;
989
+ const sidebar = doc.sidebar;
990
+ const title = doc.title ?? "Untitled";
991
+ return /* @__PURE__ */ jsxs4("div", { className: "np-page np-page-sidebar", children: [
992
+ /* @__PURE__ */ jsx5("article", { className: "np-page-sidebar-main", children: blocks ? renderBlocks3(blocks, { ctx: blockCtx }) : /* @__PURE__ */ jsx5("h1", { children: title }) }),
993
+ /* @__PURE__ */ jsx5("aside", { className: "np-page-sidebar-aside", "aria-label": "Page sidebar", children: sidebar && sidebar.length > 0 ? renderBlocks3(sidebar, { ctx: blockCtx }) : /* @__PURE__ */ jsxs4("div", { className: "np-page-sidebar-placeholder", children: [
994
+ /* @__PURE__ */ jsx5("p", { className: "np-page-sidebar-placeholder-label", children: "On this page" }),
995
+ /* @__PURE__ */ jsxs4("p", { className: "np-page-sidebar-placeholder-hint", children: [
996
+ "Add a ",
997
+ /* @__PURE__ */ jsx5("code", { children: "sidebar" }),
998
+ " field to your pages collection to fill this column with secondary blocks."
999
+ ] })
1000
+ ] }) })
1001
+ ] });
1002
+ }
1003
+
1004
+ // src/templates/page-wide.tsx
1005
+ import { renderBlocks as renderBlocks4 } from "@nexpress/blocks";
1006
+ import { jsx as jsx6 } from "react/jsx-runtime";
1007
+ function PageWideTemplate({ doc, blockCtx }) {
1008
+ const blocks = doc.blocks;
1009
+ const title = doc.title;
1010
+ return /* @__PURE__ */ jsx6("div", { className: "np-page np-page-wide", children: blocks ? renderBlocks4(blocks, { ctx: blockCtx }) : /* @__PURE__ */ jsx6("h1", { children: title ?? "Untitled" }) });
1011
+ }
1012
+
1013
+ // src/templates/post-default.tsx
1014
+ import { renderRichText } from "@nexpress/editor/server";
1015
+ import Link2 from "next/link";
1016
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1017
+ function coverUrl(value) {
1018
+ if (!value) return null;
1019
+ if (typeof value === "string") return value;
1020
+ return value.url ?? null;
1021
+ }
1022
+ function coverAlt(value, fallback) {
1023
+ if (value && typeof value === "object" && value.alt) return value.alt;
1024
+ return fallback;
1025
+ }
1026
+ function authorLabel(author) {
1027
+ if (!author) return null;
1028
+ if (typeof author === "string") return author;
1029
+ return author.name ?? null;
1030
+ }
1031
+ function tagItems(tags) {
1032
+ if (!tags || tags.length === 0) return [];
1033
+ return tags.map((tag) => {
1034
+ if (typeof tag === "string") return { label: tag };
1035
+ return { label: tag.label ?? tag.slug ?? "tag", slug: tag.slug };
1036
+ });
1037
+ }
1038
+ function formatDate(value) {
1039
+ if (!value) return null;
1040
+ try {
1041
+ const d = typeof value === "string" ? new Date(value) : value;
1042
+ if (Number.isNaN(d.getTime())) return null;
1043
+ return d.toLocaleDateString(void 0, {
1044
+ year: "numeric",
1045
+ month: "long",
1046
+ day: "numeric"
1047
+ });
1048
+ } catch {
1049
+ return null;
1050
+ }
1051
+ }
1052
+ function readingTimeLabel(value) {
1053
+ if (!value && value !== 0) return null;
1054
+ if (typeof value === "number") return `${value.toString()} min read`;
1055
+ return value;
1056
+ }
1057
+ function PostDefaultTemplate({ doc }) {
1058
+ const post = doc;
1059
+ const title = post.title ?? "Untitled";
1060
+ const cover = coverUrl(post.cover);
1061
+ const author = authorLabel(post.author);
1062
+ const date = formatDate(post.publishedAt);
1063
+ const reading = readingTimeLabel(post.readingTime);
1064
+ const tags = tagItems(post.tags);
1065
+ return /* @__PURE__ */ jsxs5("article", { className: "np-post np-post-default", children: [
1066
+ cover ? /* @__PURE__ */ jsx7("figure", { className: "np-post-cover", children: /* @__PURE__ */ jsx7("img", { src: cover, alt: coverAlt(post.cover, title) }) }) : null,
1067
+ /* @__PURE__ */ jsxs5("header", { className: "np-post-header", children: [
1068
+ tags.length > 0 ? /* @__PURE__ */ jsx7("ul", { className: "np-post-tags", children: tags.map((tag) => /* @__PURE__ */ jsx7("li", { children: tag.slug ? /* @__PURE__ */ jsx7(Link2, { href: `/tags/${tag.slug}`, children: tag.label }) : /* @__PURE__ */ jsx7("span", { children: tag.label }) }, tag.slug ?? tag.label)) }) : null,
1069
+ /* @__PURE__ */ jsx7("h1", { className: "np-post-title", children: title }),
1070
+ post.excerpt ? /* @__PURE__ */ jsx7("p", { className: "np-post-excerpt", children: post.excerpt }) : null,
1071
+ /* @__PURE__ */ jsxs5("div", { className: "np-post-meta", children: [
1072
+ author ? /* @__PURE__ */ jsxs5("span", { className: "np-post-meta-author", children: [
1073
+ "By ",
1074
+ author
1075
+ ] }) : null,
1076
+ date ? /* @__PURE__ */ jsx7(
1077
+ "time",
1078
+ {
1079
+ className: "np-post-meta-date",
1080
+ dateTime: String(post.publishedAt),
1081
+ children: date
1082
+ }
1083
+ ) : null,
1084
+ reading ? /* @__PURE__ */ jsx7("span", { className: "np-post-meta-reading", children: reading }) : null
1085
+ ] })
1086
+ ] }),
1087
+ /* @__PURE__ */ jsx7("div", { className: "np-post-body", children: post.content ? renderRichText(post.content) : null })
1088
+ ] });
1089
+ }
1090
+
1091
+ // src/components/post-card.tsx
1092
+ import Link3 from "next/link";
1093
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1094
+ function coverImageUrl(cover) {
1095
+ if (!cover) return null;
1096
+ if (typeof cover === "string") return cover;
1097
+ return cover.url ?? null;
1098
+ }
1099
+ function coverAlt2(cover, fallback) {
1100
+ if (cover && typeof cover === "object" && cover.alt) return cover.alt;
1101
+ return fallback;
1102
+ }
1103
+ function formatDate2(value) {
1104
+ if (!value) return null;
1105
+ try {
1106
+ const d = typeof value === "string" ? new Date(value) : value;
1107
+ if (Number.isNaN(d.getTime())) return null;
1108
+ return d.toLocaleDateString(void 0, {
1109
+ year: "numeric",
1110
+ month: "short",
1111
+ day: "numeric"
1112
+ });
1113
+ } catch {
1114
+ return null;
1115
+ }
1116
+ }
1117
+ function readingTimeLabel2(value) {
1118
+ if (!value && value !== 0) return null;
1119
+ if (typeof value === "number") return `${value.toString()} min read`;
1120
+ return value;
1121
+ }
1122
+ function authorName(author) {
1123
+ if (!author) return null;
1124
+ if (typeof author === "string") return author;
1125
+ return author.name ?? null;
1126
+ }
1127
+ function postHref(doc) {
1128
+ if (doc.slug) {
1129
+ return doc.slug.startsWith("/") ? doc.slug : `/blog/${doc.slug}`;
1130
+ }
1131
+ return "#";
1132
+ }
1133
+ function PostCard({ doc, variant = "grid" }) {
1134
+ const href = postHref(doc);
1135
+ const cover = coverImageUrl(doc.cover);
1136
+ const date = formatDate2(doc.publishedAt);
1137
+ const reading = readingTimeLabel2(doc.readingTime);
1138
+ const author = authorName(doc.author);
1139
+ const title = doc.title ?? "Untitled";
1140
+ return /* @__PURE__ */ jsx8(
1141
+ "article",
1142
+ {
1143
+ className: `np-post-card${variant === "feature" ? " np-post-card-feature" : ""}`,
1144
+ children: /* @__PURE__ */ jsxs6(Link3, { href, className: "np-post-card-link", children: [
1145
+ cover ? /* @__PURE__ */ jsx8("div", { className: "np-post-card-cover", children: /* @__PURE__ */ jsx8("img", { src: cover, alt: coverAlt2(doc.cover, title), loading: "lazy" }) }) : null,
1146
+ /* @__PURE__ */ jsxs6("div", { className: "np-post-card-body", children: [
1147
+ /* @__PURE__ */ jsx8("h3", { className: "np-post-card-title", children: title }),
1148
+ doc.excerpt ? /* @__PURE__ */ jsx8("p", { className: "np-post-card-excerpt", children: doc.excerpt }) : null,
1149
+ /* @__PURE__ */ jsxs6("div", { className: "np-post-card-meta", children: [
1150
+ author ? /* @__PURE__ */ jsx8("span", { children: author }) : null,
1151
+ date ? /* @__PURE__ */ jsx8("time", { dateTime: String(doc.publishedAt), children: date }) : null,
1152
+ reading ? /* @__PURE__ */ jsx8("span", { children: reading }) : null
1153
+ ] })
1154
+ ] })
1155
+ ] })
1156
+ }
1157
+ );
1158
+ }
1159
+
1160
+ // src/templates/post-list.tsx
1161
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1162
+ function PostListTemplate({ doc }) {
1163
+ const data = doc;
1164
+ const heading = data.heading ?? "Posts";
1165
+ const intro = data.intro;
1166
+ const all = data.docs ?? [];
1167
+ if (all.length === 0) {
1168
+ return /* @__PURE__ */ jsx9("section", { className: "np-post-list np-post-list-empty", children: /* @__PURE__ */ jsxs7("header", { children: [
1169
+ /* @__PURE__ */ jsx9("h1", { children: heading }),
1170
+ /* @__PURE__ */ jsx9("p", { children: "No posts yet \u2014 once you publish from the admin, they'll appear here." })
1171
+ ] }) });
1172
+ }
1173
+ const [feature, ...rest] = all;
1174
+ return /* @__PURE__ */ jsxs7("section", { className: "np-post-list", children: [
1175
+ /* @__PURE__ */ jsxs7("header", { className: "np-post-list-header", children: [
1176
+ /* @__PURE__ */ jsx9("h1", { children: heading }),
1177
+ intro ? /* @__PURE__ */ jsx9("p", { className: "np-post-list-intro", children: intro }) : null
1178
+ ] }),
1179
+ feature ? /* @__PURE__ */ jsx9("div", { className: "np-post-list-feature", children: /* @__PURE__ */ jsx9(PostCard, { doc: feature, variant: "feature" }) }) : null,
1180
+ rest.length > 0 ? /* @__PURE__ */ jsx9("div", { className: "np-post-list-grid", children: rest.map((post) => /* @__PURE__ */ jsx9(PostCard, { doc: post }, post.id ?? post.slug ?? post.title)) }) : null
1181
+ ] });
1182
+ }
1183
+
1184
+ // src/index.ts
1185
+ import { MemberStatusWidget as MemberStatusWidget2 } from "./components/member-status-widget.js";
1186
+ import { MobileNav as MobileNav2 } from "./components/mobile-nav.js";
1187
+
1188
+ // src/components/social-links.tsx
1189
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1190
+ function buildLinks() {
1191
+ const links = [];
1192
+ const env = process.env;
1193
+ if (env.NP_SOCIAL_GITHUB) {
1194
+ links.push({ href: env.NP_SOCIAL_GITHUB, label: "GitHub", Icon: GithubIcon });
1195
+ }
1196
+ if (env.NP_SOCIAL_TWITTER) {
1197
+ links.push({ href: env.NP_SOCIAL_TWITTER, label: "Twitter / X", Icon: TwitterIcon });
1198
+ }
1199
+ if (env.NP_SOCIAL_LINKEDIN) {
1200
+ links.push({ href: env.NP_SOCIAL_LINKEDIN, label: "LinkedIn", Icon: LinkedInIcon });
1201
+ }
1202
+ if (env.NP_SOCIAL_MASTODON) {
1203
+ links.push({ href: env.NP_SOCIAL_MASTODON, label: "Mastodon", Icon: MastodonIcon });
1204
+ }
1205
+ if (env.NP_SOCIAL_EMAIL) {
1206
+ const value = env.NP_SOCIAL_EMAIL.startsWith("mailto:") ? env.NP_SOCIAL_EMAIL : `mailto:${env.NP_SOCIAL_EMAIL}`;
1207
+ links.push({ href: value, label: "Email", Icon: EmailIcon });
1208
+ }
1209
+ links.push({
1210
+ href: env.NP_SOCIAL_RSS ?? "/feed.xml",
1211
+ label: "RSS",
1212
+ Icon: RssIcon
1213
+ });
1214
+ return links;
1215
+ }
1216
+ function SocialLinks() {
1217
+ const links = buildLinks();
1218
+ if (links.length === 0) return null;
1219
+ return /* @__PURE__ */ jsx10("ul", { className: "np-site-footer-social", children: links.map((link) => /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(
1220
+ "a",
1221
+ {
1222
+ href: link.href,
1223
+ "aria-label": link.label,
1224
+ rel: link.label === "Mastodon" ? "me noopener noreferrer" : "noopener noreferrer",
1225
+ target: link.href.startsWith("mailto:") || link.href.startsWith("/") ? void 0 : "_blank",
1226
+ children: /* @__PURE__ */ jsx10(link.Icon, {})
1227
+ }
1228
+ ) }, link.href)) });
1229
+ }
1230
+ var ICON_PROPS = {
1231
+ width: 18,
1232
+ height: 18,
1233
+ viewBox: "0 0 24 24",
1234
+ fill: "none",
1235
+ stroke: "currentColor",
1236
+ strokeWidth: 1.8,
1237
+ strokeLinecap: "round",
1238
+ strokeLinejoin: "round",
1239
+ "aria-hidden": true
1240
+ };
1241
+ function GithubIcon() {
1242
+ return /* @__PURE__ */ jsx10("svg", { xmlns: "http://www.w3.org/2000/svg", ...ICON_PROPS, children: /* @__PURE__ */ jsx10("path", { d: "M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22" }) });
1243
+ }
1244
+ function TwitterIcon() {
1245
+ return /* @__PURE__ */ jsx10("svg", { xmlns: "http://www.w3.org/2000/svg", ...ICON_PROPS, children: /* @__PURE__ */ jsx10("path", { d: "M22 4.01c-1 .49-1.98.689-3 .99-1.121-1.265-2.783-1.335-4.38-.737S11.977 6.323 12 8v1c-3.245.083-6.135-1.395-8-4 0 0-4.182 7.433 4 11-1.872 1.247-3.739 2.088-6 2 3.308 1.803 6.913 2.423 10.034 1.517 3.58-1.04 6.522-3.723 7.651-7.742a13.84 13.84 0 0 0 .497-3.753c-.002-.249 1.51-2.772 1.818-4.013z" }) });
1246
+ }
1247
+ function LinkedInIcon() {
1248
+ return /* @__PURE__ */ jsxs8("svg", { xmlns: "http://www.w3.org/2000/svg", ...ICON_PROPS, children: [
1249
+ /* @__PURE__ */ jsx10("path", { d: "M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-4 0v7h-4v-7a6 6 0 0 1 6-6z" }),
1250
+ /* @__PURE__ */ jsx10("rect", { x: "2", y: "9", width: "4", height: "12" }),
1251
+ /* @__PURE__ */ jsx10("circle", { cx: "4", cy: "4", r: "2" })
1252
+ ] });
1253
+ }
1254
+ function MastodonIcon() {
1255
+ return /* @__PURE__ */ jsxs8("svg", { xmlns: "http://www.w3.org/2000/svg", ...ICON_PROPS, children: [
1256
+ /* @__PURE__ */ jsx10("path", { d: "M21 12c0 4.97-4.03 7-9 7s-9-2.03-9-7V8a5 5 0 0 1 5-5h8a5 5 0 0 1 5 5v4z" }),
1257
+ /* @__PURE__ */ jsx10("path", { d: "M9 13V9.5a2.5 2.5 0 1 1 5 0V13" })
1258
+ ] });
1259
+ }
1260
+ function EmailIcon() {
1261
+ return /* @__PURE__ */ jsxs8("svg", { xmlns: "http://www.w3.org/2000/svg", ...ICON_PROPS, children: [
1262
+ /* @__PURE__ */ jsx10("path", { d: "M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" }),
1263
+ /* @__PURE__ */ jsx10("polyline", { points: "22,6 12,13 2,6" })
1264
+ ] });
1265
+ }
1266
+ function RssIcon() {
1267
+ return /* @__PURE__ */ jsxs8("svg", { xmlns: "http://www.w3.org/2000/svg", ...ICON_PROPS, children: [
1268
+ /* @__PURE__ */ jsx10("path", { d: "M4 11a9 9 0 0 1 9 9" }),
1269
+ /* @__PURE__ */ jsx10("path", { d: "M4 4a16 16 0 0 1 16 16" }),
1270
+ /* @__PURE__ */ jsx10("circle", { cx: "5", cy: "19", r: "1" })
1271
+ ] });
1272
+ }
1273
+
1274
+ // src/index.ts
1275
+ import { NewsletterForm } from "./components/newsletter-form.js";
1276
+
1277
+ // src/components/pagination.tsx
1278
+ import Link4 from "next/link";
1279
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1280
+ function pageWindow(current, total, siblings) {
1281
+ const windowSet = /* @__PURE__ */ new Set([1, total]);
1282
+ for (let i = -siblings; i <= siblings; i++) {
1283
+ const candidate = current + i;
1284
+ if (candidate >= 1 && candidate <= total) windowSet.add(candidate);
1285
+ }
1286
+ const sorted = Array.from(windowSet).sort((a, b) => a - b);
1287
+ const out = [];
1288
+ let prev = null;
1289
+ for (const n of sorted) {
1290
+ if (prev !== null && n - prev > 1) out.push("gap");
1291
+ out.push(n);
1292
+ prev = n;
1293
+ }
1294
+ return out;
1295
+ }
1296
+ function Pagination({ page, totalPages, hrefForPage, siblings = 1 }) {
1297
+ if (totalPages <= 1) return null;
1298
+ const items = pageWindow(page, totalPages, siblings);
1299
+ const prev = page > 1 ? hrefForPage(page - 1) : null;
1300
+ const next = page < totalPages ? hrefForPage(page + 1) : null;
1301
+ return /* @__PURE__ */ jsxs9("nav", { className: "np-pagination", "aria-label": "Pagination", children: [
1302
+ prev ? /* @__PURE__ */ jsx11(Link4, { href: prev, rel: "prev", className: "np-pagination-step", children: "\u2190 Prev" }) : /* @__PURE__ */ jsx11("span", { className: "np-pagination-step np-pagination-disabled", children: "\u2190 Prev" }),
1303
+ /* @__PURE__ */ jsx11("ol", { className: "np-pagination-pages", children: items.map((entry, index) => /* @__PURE__ */ jsx11("li", { children: entry === "gap" ? /* @__PURE__ */ jsx11("span", { className: "np-pagination-gap", "aria-hidden": "true", children: "\u2026" }) : /* @__PURE__ */ jsx11(
1304
+ Link4,
1305
+ {
1306
+ href: hrefForPage(entry),
1307
+ "aria-current": entry === page ? "page" : void 0,
1308
+ className: entry === page ? "np-pagination-page np-pagination-current" : "np-pagination-page",
1309
+ children: entry.toString()
1310
+ }
1311
+ ) }, `${typeof entry === "number" ? entry.toString() : "gap"}-${index.toString()}`)) }),
1312
+ next ? /* @__PURE__ */ jsx11(Link4, { href: next, rel: "next", className: "np-pagination-step", children: "Next \u2192" }) : /* @__PURE__ */ jsx11("span", { className: "np-pagination-step np-pagination-disabled", children: "Next \u2192" })
1313
+ ] });
1314
+ }
1315
+
1316
+ // src/index.ts
1317
+ var defaultTheme = defineTheme({
1318
+ manifest: {
1319
+ id: "default",
1320
+ name: "NexPress Default",
1321
+ version: "0.1.0",
1322
+ description: "Production-grade baseline theme. Sticky header with mobile drawer, four-column footer, blog list / detail templates, landing + sidebar page variants, dark-mode parity, social + newsletter slots in the footer.",
1323
+ author: { name: "NexPress" },
1324
+ nexpress: { minVersion: "0.1.0" }
1325
+ },
1326
+ impl: {
1327
+ shell: DefaultShell,
1328
+ slots: {
1329
+ header: DefaultHeader,
1330
+ footer: DefaultFooter
1331
+ },
1332
+ css: defaultThemeCss,
1333
+ templates: {
1334
+ pages: {
1335
+ default: {
1336
+ label: "Default",
1337
+ description: "Centered content container with the standard reading width.",
1338
+ component: PageDefaultTemplate
1339
+ },
1340
+ wide: {
1341
+ label: "Wide",
1342
+ description: "Edge-to-edge layout with no max-width. Best for galleries and immersive media.",
1343
+ component: PageWideTemplate
1344
+ },
1345
+ landing: {
1346
+ label: "Landing",
1347
+ description: "Marketing-style template \u2014 full-bleed hero from the first block, then sections render edge-to-edge so Hero / FeatureGrid / CTA blocks span the viewport.",
1348
+ component: PageLandingTemplate
1349
+ },
1350
+ sidebar: {
1351
+ label: "Sidebar",
1352
+ description: "Two-column layout with a sticky right sidebar. Suited to docs / knowledge bases. Sites can populate the aside with a `sidebar` field on their pages collection.",
1353
+ component: PageSidebarTemplate
1354
+ }
1355
+ },
1356
+ posts: {
1357
+ default: {
1358
+ label: "Article",
1359
+ description: "Centered article column with cover image, tags, byline, reading time, and Lexical body.",
1360
+ component: PostDefaultTemplate
1361
+ },
1362
+ list: {
1363
+ label: "List view",
1364
+ description: "Blog-index template: one feature card on top, then a 3-column grid (collapses on phones). Suitable for any collection that ships PostCard-shaped docs.",
1365
+ component: PostListTemplate
1366
+ }
1367
+ }
1368
+ }
1369
+ }
1370
+ });
1371
+ export {
1372
+ DefaultFooter,
1373
+ DefaultHeader,
1374
+ DefaultShell,
1375
+ MemberStatusWidget2 as MemberStatusWidget,
1376
+ MobileNav2 as MobileNav,
1377
+ NewsletterForm,
1378
+ PageLandingTemplate,
1379
+ PageSidebarTemplate,
1380
+ Pagination,
1381
+ PostCard,
1382
+ PostDefaultTemplate,
1383
+ PostListTemplate,
1384
+ SocialLinks,
1385
+ defaultTheme,
1386
+ defaultThemeCss
1387
+ };
1388
+ //# sourceMappingURL=index.js.map