@karaoke-cms/theme-default 0.9.3 → 0.9.6
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/README.md +56 -36
- package/package.json +3 -3
- package/src/index.ts +24 -19
- package/src/styles.css +29 -4
package/README.md
CHANGED
|
@@ -1,65 +1,85 @@
|
|
|
1
1
|
# @karaoke-cms/theme-default
|
|
2
2
|
|
|
3
|
-
Two-column knowledge base theme for karaoke-cms
|
|
3
|
+
Two-column knowledge base theme for karaoke-cms — blog, docs, and tags with a clean system-UI design and automatic dark mode.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @karaoke-cms/theme-default @karaoke-cms/module-blog @karaoke-cms/module-docs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
8
12
|
|
|
9
13
|
```ts
|
|
10
14
|
// karaoke.config.ts
|
|
15
|
+
import { defineConfig } from '@karaoke-cms/astro';
|
|
11
16
|
import { loadEnv } from '@karaoke-cms/astro/env';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
import { blog } from '@karaoke-cms/module-blog';
|
|
18
|
+
import { docs } from '@karaoke-cms/module-docs';
|
|
19
|
+
import { themeDefault } from '@karaoke-cms/theme-default';
|
|
20
|
+
|
|
21
|
+
const env = loadEnv(new URL('.', import.meta.url));
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
vault: env.KARAOKE_VAULT,
|
|
25
|
+
title: 'My Site',
|
|
26
|
+
theme: themeDefault({
|
|
27
|
+
implements: [
|
|
28
|
+
blog({ mount: '/blog' }),
|
|
29
|
+
docs({ mount: '/docs' }),
|
|
30
|
+
],
|
|
31
|
+
}),
|
|
32
|
+
});
|
|
18
33
|
```
|
|
19
34
|
|
|
20
|
-
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
Pass modules to `implements` to tell the theme which content sections to wire up. Modules listed here get their routes injected from their own npm packages.
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
| Option | Type | Default | Description |
|
|
40
|
+
|--------|------|---------|-------------|
|
|
41
|
+
| `implements` | `ModuleInstance[]` | `[]` | Content modules this theme integrates |
|
|
23
42
|
|
|
24
|
-
|
|
43
|
+
## Routes
|
|
25
44
|
|
|
26
|
-
| Route |
|
|
27
|
-
|
|
45
|
+
| Route | Description |
|
|
46
|
+
|-------|-------------|
|
|
28
47
|
| `/` | Home — recent blog posts + recent docs |
|
|
29
|
-
| `/blog` | Blog
|
|
30
|
-
| `/
|
|
31
|
-
| `/
|
|
32
|
-
| `/
|
|
33
|
-
| `/tags` | Tags index — all tags with post counts |
|
|
34
|
-
| `/tags/[tag]` | Tag page — posts filtered by tag |
|
|
48
|
+
| `/blog`, `/blog/[slug]`, `/blog/page/[page]` | Blog section (from `module-blog`) |
|
|
49
|
+
| `/docs`, `/docs/list`, `/docs/[slug]` | Docs section (from `module-docs`) |
|
|
50
|
+
| `/tags` | All tags with post counts |
|
|
51
|
+
| `/tags/[tag]` | Posts filtered by tag |
|
|
35
52
|
| `/404` | Not found page |
|
|
36
53
|
|
|
37
|
-
|
|
54
|
+
Routes for modules listed in `implements` are injected by the module package. Other routes are injected by the theme as fallbacks.
|
|
38
55
|
|
|
39
|
-
|
|
56
|
+
## Design system
|
|
57
|
+
|
|
58
|
+
All tokens are CSS variables on `:root`:
|
|
40
59
|
|
|
41
60
|
- **Typography**: `--font-body`, `--font-mono`, `--font-size-base`, `--font-size-sm/lg/xl`
|
|
42
|
-
- **Color**: `--color-bg`, `--color-text`, `--color-muted`, `--color-border`, `--color-link
|
|
61
|
+
- **Color**: `--color-bg`, `--color-text`, `--color-muted`, `--color-border`, `--color-link`
|
|
43
62
|
- **Spacing**: `--spacing-xs/sm/md/lg/xl`
|
|
44
|
-
- **Sizing**: `--width-content` (680px), `--width-site` (800px)
|
|
63
|
+
- **Sizing**: `--width-content` (680px), `--width-site` (800px)
|
|
45
64
|
|
|
46
65
|
Dark mode is automatic via `@media (prefers-color-scheme: dark)` — no JavaScript toggle.
|
|
47
66
|
|
|
48
|
-
##
|
|
67
|
+
## Layout regions
|
|
49
68
|
|
|
50
|
-
|
|
69
|
+
Configure sidebar content in `karaoke.config.ts`:
|
|
51
70
|
|
|
52
|
-
```
|
|
53
|
-
|
|
71
|
+
```ts
|
|
72
|
+
layout: {
|
|
73
|
+
regions: {
|
|
74
|
+
right: { components: ['recent-posts'] },
|
|
75
|
+
},
|
|
76
|
+
}
|
|
54
77
|
```
|
|
55
78
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
Your vault needs `blog/` and/or `docs/` directories with Markdown files. Only files with `publish: true` in frontmatter appear on the site.
|
|
79
|
+
Available: `'header'`, `'main-menu'`, `'search'`, `'recent-posts'`, `'footer'`.
|
|
59
80
|
|
|
60
|
-
##
|
|
81
|
+
## What's new in 0.9.5
|
|
61
82
|
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
- The layout is driven by `Base.astro`'s region system — sidebar content (recent posts, search) is controlled by the `layout.regions` config in `karaoke.config.ts`, not hardcoded in the theme.
|
|
83
|
+
- **`themeDefault()` function API** replaces the old string `theme: '@karaoke-cms/theme-default'`
|
|
84
|
+
- **`implements` option** — pass `blog()` and/or `docs()` module instances to wire up content sections; routes come from the module packages
|
|
85
|
+
- No user-facing visual changes in this release
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karaoke-cms/theme-default",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.6",
|
|
5
5
|
"description": "Default theme for karaoke-cms — two-column knowledge base with blog and docs",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
"karaoke-cms"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@karaoke-cms/module-blog": "
|
|
20
|
+
"@karaoke-cms/module-blog": "^0.9.6"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"astro": ">=6.0.0",
|
|
24
|
-
"@karaoke-cms/astro": "^0.9.
|
|
24
|
+
"@karaoke-cms/astro": "^0.9.6"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@karaoke-cms/astro": "workspace:*",
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AstroIntegration } from 'astro';
|
|
2
|
+
import type { ModuleInstance } from '@karaoke-cms/astro';
|
|
2
3
|
import { fileURLToPath } from 'url';
|
|
3
4
|
import { existsSync } from 'fs';
|
|
4
5
|
|
|
@@ -7,27 +8,30 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
|
7
8
|
// Constructs the Astro integration for theme-default.
|
|
8
9
|
// Exported separately so it can be called by both the new factory (themeDefault)
|
|
9
10
|
// and the legacy default export without triggering a @karaoke-cms/astro import.
|
|
10
|
-
|
|
11
|
+
//
|
|
12
|
+
// When `implementedModuleIds` is provided, routes for those modules are skipped —
|
|
13
|
+
// the core integration injects them from the module package instead.
|
|
14
|
+
function buildIntegration(implementedModuleIds: string[] = []): AstroIntegration {
|
|
11
15
|
return {
|
|
12
16
|
name: '@karaoke-cms/theme-default',
|
|
13
17
|
hooks: {
|
|
14
18
|
'astro:config:setup': ({ injectRoute, updateConfig, config: astroConfig }) => {
|
|
15
|
-
// Blog
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
// Blog post route: fall back to module-blog post page when blog is not in implements[].
|
|
20
|
+
if (!implementedModuleIds.includes('blog')) {
|
|
21
|
+
injectRoute({ pattern: '/blog/[slug]', entrypoint: '@karaoke-cms/module-blog/pages/post' });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Docs routes: fall back to built-in pages when docs is not in implements[].
|
|
25
|
+
if (!implementedModuleIds.includes('docs')) {
|
|
26
|
+
injectRoute({ pattern: '/docs', entrypoint: `${__dirname}pages/docs/index.astro` });
|
|
27
|
+
injectRoute({ pattern: '/docs/[slug]', entrypoint: `${__dirname}pages/docs/[slug].astro` });
|
|
28
|
+
}
|
|
22
29
|
|
|
23
|
-
// Docs, tags, and homepage remain owned by theme-default.
|
|
24
30
|
// Skip injecting '/' if the user already has src/pages/index.astro.
|
|
25
31
|
const userIndex = fileURLToPath(new URL('src/pages/index.astro', astroConfig.root));
|
|
26
32
|
if (!existsSync(userIndex)) {
|
|
27
33
|
injectRoute({ pattern: '/', entrypoint: `${__dirname}pages/index.astro` });
|
|
28
34
|
}
|
|
29
|
-
injectRoute({ pattern: '/docs', entrypoint: `${__dirname}pages/docs/index.astro` });
|
|
30
|
-
injectRoute({ pattern: '/docs/[slug]', entrypoint: `${__dirname}pages/docs/[slug].astro` });
|
|
31
35
|
injectRoute({ pattern: '/tags', entrypoint: `${__dirname}pages/tags/index.astro` });
|
|
32
36
|
injectRoute({ pattern: '/tags/[tag]', entrypoint: `${__dirname}pages/tags/[tag].astro` });
|
|
33
37
|
injectRoute({ pattern: '/404', entrypoint: `${__dirname}pages/404.astro` });
|
|
@@ -47,10 +51,6 @@ function buildIntegration(): AstroIntegration {
|
|
|
47
51
|
/**
|
|
48
52
|
* New API: returns a ThemeInstance for use with defineConfig().
|
|
49
53
|
*
|
|
50
|
-
* Note: ThemeInstance is constructed manually here (without importing defineTheme
|
|
51
|
-
* from @karaoke-cms/astro) so that this file remains loadable via Node's native
|
|
52
|
-
* ESM when used through the legacy string-based theme config path.
|
|
53
|
-
*
|
|
54
54
|
* @example
|
|
55
55
|
* // karaoke.config.ts
|
|
56
56
|
* import { themeDefault } from '@karaoke-cms/theme-default';
|
|
@@ -58,12 +58,15 @@ function buildIntegration(): AstroIntegration {
|
|
|
58
58
|
* theme: themeDefault({ implements: [blog({ mount: '/blog' })] }),
|
|
59
59
|
* });
|
|
60
60
|
*/
|
|
61
|
-
export function themeDefault(config: { implements?:
|
|
61
|
+
export function themeDefault(config: { implements?: ModuleInstance[] } = {}) {
|
|
62
|
+
const implementedModules = config.implements ?? [];
|
|
63
|
+
const implementedModuleIds = implementedModules.map(m => m.id);
|
|
62
64
|
return {
|
|
63
65
|
_type: 'theme-instance' as const,
|
|
64
66
|
id: 'theme-default',
|
|
65
|
-
implementedModuleIds
|
|
66
|
-
|
|
67
|
+
implementedModuleIds,
|
|
68
|
+
implementedModules,
|
|
69
|
+
toAstroIntegration: () => buildIntegration(implementedModuleIds),
|
|
67
70
|
};
|
|
68
71
|
}
|
|
69
72
|
|
|
@@ -72,7 +75,9 @@ export function themeDefault(config: { implements?: Array<{ id: string }> } = {}
|
|
|
72
75
|
* theme: '@karaoke-cms/theme-default'
|
|
73
76
|
* The core integration calls themeModule.default(config) when loading
|
|
74
77
|
* themes by package name string.
|
|
78
|
+
*
|
|
79
|
+
* Injects blog routes directly (legacy path — blog module not in modules[]).
|
|
75
80
|
*/
|
|
76
81
|
export default function themeDefaultLegacy(_config: unknown): AstroIntegration {
|
|
77
|
-
return buildIntegration();
|
|
82
|
+
return buildIntegration([]);
|
|
78
83
|
}
|
package/src/styles.css
CHANGED
|
@@ -87,6 +87,10 @@ header {
|
|
|
87
87
|
margin-bottom: var(--spacing-xl);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
header.landing-header {
|
|
91
|
+
margin-bottom: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
90
94
|
.header-inner {
|
|
91
95
|
max-width: var(--width-landing);
|
|
92
96
|
margin: 0 auto;
|
|
@@ -107,6 +111,10 @@ header {
|
|
|
107
111
|
letter-spacing: -0.01em;
|
|
108
112
|
}
|
|
109
113
|
|
|
114
|
+
.site-name:visited {
|
|
115
|
+
color: var(--color-on-dark);
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
.site-name:hover {
|
|
111
119
|
color: #fff;
|
|
112
120
|
}
|
|
@@ -134,6 +142,10 @@ header nav ul a {
|
|
|
134
142
|
transition: color 0.15s;
|
|
135
143
|
}
|
|
136
144
|
|
|
145
|
+
header nav ul a:visited {
|
|
146
|
+
color: #94a3b8;
|
|
147
|
+
}
|
|
148
|
+
|
|
137
149
|
header nav ul a:hover,
|
|
138
150
|
header nav ul a[aria-current="page"] {
|
|
139
151
|
color: var(--color-on-dark);
|
|
@@ -271,6 +283,10 @@ footer a {
|
|
|
271
283
|
text-decoration: none;
|
|
272
284
|
}
|
|
273
285
|
|
|
286
|
+
footer a:visited {
|
|
287
|
+
color: var(--color-on-dark-2);
|
|
288
|
+
}
|
|
289
|
+
|
|
274
290
|
footer a:hover {
|
|
275
291
|
color: var(--color-on-dark);
|
|
276
292
|
}
|
|
@@ -284,6 +300,10 @@ footer a:hover {
|
|
|
284
300
|
padding-bottom: var(--spacing-xs);
|
|
285
301
|
}
|
|
286
302
|
|
|
303
|
+
.footer-col nav.karaoke-menu > ul > li > a:visited {
|
|
304
|
+
color: var(--color-on-dark);
|
|
305
|
+
}
|
|
306
|
+
|
|
287
307
|
.footer-col nav.karaoke-menu > ul > li > ul a {
|
|
288
308
|
font-size: var(--font-size-sm);
|
|
289
309
|
color: #94a3b8;
|
|
@@ -291,6 +311,10 @@ footer a:hover {
|
|
|
291
311
|
padding: 2px 0;
|
|
292
312
|
}
|
|
293
313
|
|
|
314
|
+
.footer-col nav.karaoke-menu > ul > li > ul a:visited {
|
|
315
|
+
color: #94a3b8;
|
|
316
|
+
}
|
|
317
|
+
|
|
294
318
|
.footer-col nav.karaoke-menu > ul > li > ul a:hover {
|
|
295
319
|
color: var(--color-on-dark);
|
|
296
320
|
}
|
|
@@ -616,12 +640,12 @@ nav.karaoke-menu[data-orientation="vertical"] li ul {
|
|
|
616
640
|
|
|
617
641
|
/* ── Header CTA button ──────────────────────────────────────────────── */
|
|
618
642
|
|
|
619
|
-
.btn-cta {
|
|
643
|
+
a.btn-cta {
|
|
620
644
|
font-size: var(--font-size-sm);
|
|
621
645
|
font-weight: 600;
|
|
622
646
|
color: #fff;
|
|
623
647
|
background: var(--color-accent);
|
|
624
|
-
padding:
|
|
648
|
+
padding: 6px 20px;
|
|
625
649
|
border-radius: 999px;
|
|
626
650
|
text-decoration: none;
|
|
627
651
|
transition: opacity 0.15s;
|
|
@@ -629,8 +653,9 @@ nav.karaoke-menu[data-orientation="vertical"] li ul {
|
|
|
629
653
|
white-space: nowrap;
|
|
630
654
|
}
|
|
631
655
|
|
|
632
|
-
.btn-cta:
|
|
633
|
-
.btn-cta:visited
|
|
656
|
+
a.btn-cta:link,
|
|
657
|
+
a.btn-cta:visited,
|
|
658
|
+
a.btn-cta:hover {
|
|
634
659
|
opacity: 0.85;
|
|
635
660
|
color: #fff;
|
|
636
661
|
text-decoration: none;
|