@karaoke-cms/theme-default 0.9.0 → 0.9.3
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/package.json +8 -5
- package/src/components/AudienceGrid.astro +143 -0
- package/src/components/CtaSection.astro +91 -0
- package/src/components/FeatureGrid.astro +138 -0
- package/src/components/Hero.astro +141 -0
- package/src/components/HowItWorks.astro +117 -0
- package/src/components/InstallBox.astro +71 -0
- package/src/components/MetaphorSection.astro +69 -0
- package/src/components/ScaleSection.astro +146 -0
- package/src/index.ts +53 -6
- package/src/pages/404.astro +3 -3
- package/src/pages/docs/[slug].astro +3 -3
- package/src/pages/docs/index.astro +3 -3
- package/src/pages/index.astro +18 -60
- package/src/pages/tags/[tag].astro +3 -3
- package/src/pages/tags/index.astro +3 -3
- package/src/styles/blog.css +9 -0
- package/src/styles.css +153 -20
- package/src/pages/blog/[slug].astro +0 -66
- package/src/pages/blog/index.astro +0 -31
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.3",
|
|
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": {
|
|
@@ -16,15 +16,18 @@
|
|
|
16
16
|
"theme",
|
|
17
17
|
"karaoke-cms"
|
|
18
18
|
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@karaoke-cms/module-blog": "workspace:*"
|
|
21
|
+
},
|
|
19
22
|
"peerDependencies": {
|
|
20
23
|
"astro": ">=6.0.0",
|
|
21
|
-
"@karaoke-cms/astro": "^0.9.
|
|
24
|
+
"@karaoke-cms/astro": "^0.9.2"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
24
|
-
"astro": "
|
|
25
|
-
"
|
|
27
|
+
"@karaoke-cms/astro": "workspace:*",
|
|
28
|
+
"astro": "^6.0.8"
|
|
26
29
|
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"test": "echo \"Stub — no tests\""
|
|
29
32
|
}
|
|
30
|
-
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface AudienceCard {
|
|
3
|
+
icon: string;
|
|
4
|
+
title: string;
|
|
5
|
+
body: string;
|
|
6
|
+
items: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
label?: string;
|
|
11
|
+
heading?: string;
|
|
12
|
+
cards?: AudienceCard[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
label = "Who it's for",
|
|
17
|
+
heading = 'Built for everyone who publishes',
|
|
18
|
+
cards = [
|
|
19
|
+
{
|
|
20
|
+
icon: '⚡',
|
|
21
|
+
title: 'Developers',
|
|
22
|
+
body: 'One npm install. All-in on Astro — typed config, virtual modules, zero lock-in. Ship a site in an afternoon. Bring your own theme or build one from scratch.',
|
|
23
|
+
items: [
|
|
24
|
+
'TypeScript-first configuration',
|
|
25
|
+
'Composable theme packages',
|
|
26
|
+
'Turborepo monorepo, ready to extend',
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
icon: '✍️',
|
|
31
|
+
title: 'Content creators',
|
|
32
|
+
body: 'Write in Obsidian — your notes app, not a CMS dashboard. No logins, no editors, no friction. Your vault IS your CMS.',
|
|
33
|
+
items: [
|
|
34
|
+
'Publish with a single frontmatter flag',
|
|
35
|
+
'Wikilinks work natively on your live site',
|
|
36
|
+
'AI fills in descriptions and tags for you',
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
icon: '🏢',
|
|
41
|
+
title: 'Enterprise teams',
|
|
42
|
+
body: 'Git-native audit trail, private-by-default publishing, AI enrichment pipeline, and a composable module system built to scale.',
|
|
43
|
+
items: [
|
|
44
|
+
'Collections scoped per team or department',
|
|
45
|
+
'CI-enforced privacy gate',
|
|
46
|
+
'Extends to multi-author editorial workflows',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
} = Astro.props;
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
<section class="audiences">
|
|
54
|
+
<div class="container">
|
|
55
|
+
<span class="section-label">{label}</span>
|
|
56
|
+
<h2>{heading}</h2>
|
|
57
|
+
<div class="card-grid">
|
|
58
|
+
{cards.map(card => (
|
|
59
|
+
<div class="card">
|
|
60
|
+
<div class="card-icon" aria-hidden="true">{card.icon}</div>
|
|
61
|
+
<h3>{card.title}</h3>
|
|
62
|
+
<p>{card.body}</p>
|
|
63
|
+
<ul>
|
|
64
|
+
{card.items.map(item => <li>{item}</li>)}
|
|
65
|
+
</ul>
|
|
66
|
+
</div>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<style>
|
|
73
|
+
.audiences {
|
|
74
|
+
background: var(--color-bg-alt, #f8fafc);
|
|
75
|
+
padding: 88px 0;
|
|
76
|
+
}
|
|
77
|
+
.container {
|
|
78
|
+
max-width: var(--width-landing, 1100px);
|
|
79
|
+
margin: 0 auto;
|
|
80
|
+
padding: 0 24px;
|
|
81
|
+
}
|
|
82
|
+
h2 {
|
|
83
|
+
font-size: clamp(1.5rem, 3vw, 2.25rem);
|
|
84
|
+
font-weight: 700;
|
|
85
|
+
letter-spacing: -0.01em;
|
|
86
|
+
color: var(--color-text);
|
|
87
|
+
margin: 0 0 48px;
|
|
88
|
+
}
|
|
89
|
+
.card-grid {
|
|
90
|
+
display: grid;
|
|
91
|
+
grid-template-columns: repeat(3, 1fr);
|
|
92
|
+
gap: 24px;
|
|
93
|
+
}
|
|
94
|
+
.card {
|
|
95
|
+
background: var(--color-bg);
|
|
96
|
+
border: 1px solid var(--color-border);
|
|
97
|
+
border-radius: var(--radius-lg, 12px);
|
|
98
|
+
padding: 32px;
|
|
99
|
+
transition: box-shadow 0.2s, transform 0.2s;
|
|
100
|
+
}
|
|
101
|
+
.card:hover {
|
|
102
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
|
|
103
|
+
transform: translateY(-2px);
|
|
104
|
+
}
|
|
105
|
+
.card-icon {
|
|
106
|
+
font-size: 2rem;
|
|
107
|
+
line-height: 1;
|
|
108
|
+
margin-bottom: 16px;
|
|
109
|
+
}
|
|
110
|
+
.card h3 {
|
|
111
|
+
font-size: 1.125rem;
|
|
112
|
+
font-weight: 700;
|
|
113
|
+
color: var(--color-text);
|
|
114
|
+
margin: 0 0 10px;
|
|
115
|
+
}
|
|
116
|
+
.card p {
|
|
117
|
+
font-size: 0.9375rem;
|
|
118
|
+
color: var(--color-muted);
|
|
119
|
+
line-height: 1.65;
|
|
120
|
+
margin: 0 0 16px;
|
|
121
|
+
}
|
|
122
|
+
.card ul {
|
|
123
|
+
list-style: none;
|
|
124
|
+
padding: 0;
|
|
125
|
+
margin: 0;
|
|
126
|
+
}
|
|
127
|
+
.card ul li {
|
|
128
|
+
font-size: 0.875rem;
|
|
129
|
+
color: var(--color-muted);
|
|
130
|
+
padding: 4px 0 4px 18px;
|
|
131
|
+
position: relative;
|
|
132
|
+
}
|
|
133
|
+
.card ul li::before {
|
|
134
|
+
content: '·';
|
|
135
|
+
position: absolute;
|
|
136
|
+
left: 6px;
|
|
137
|
+
color: var(--color-accent, #6366f1);
|
|
138
|
+
font-weight: 700;
|
|
139
|
+
}
|
|
140
|
+
@media (max-width: 768px) {
|
|
141
|
+
.card-grid { grid-template-columns: 1fr; }
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
import InstallBox from './InstallBox.astro';
|
|
3
|
+
|
|
4
|
+
interface CtaLink {
|
|
5
|
+
href: string;
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
heading?: string;
|
|
11
|
+
sub?: string;
|
|
12
|
+
ghUrl?: string;
|
|
13
|
+
links?: CtaLink[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
heading = 'Ready to sing?',
|
|
18
|
+
sub = 'One command. Your site is running in minutes.',
|
|
19
|
+
ghUrl,
|
|
20
|
+
links = [
|
|
21
|
+
...(ghUrl ? [{ href: ghUrl, text: 'GitHub →' }] : []),
|
|
22
|
+
{ href: '/docs', text: 'Documentation →' },
|
|
23
|
+
{ href: '/blog', text: 'Blog →' },
|
|
24
|
+
],
|
|
25
|
+
} = Astro.props;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<section class="cta-section">
|
|
29
|
+
<div class="container">
|
|
30
|
+
<h2>{heading}</h2>
|
|
31
|
+
<p>{sub}</p>
|
|
32
|
+
<InstallBox size="lg" />
|
|
33
|
+
{links.length > 0 && (
|
|
34
|
+
<div class="cta-links">
|
|
35
|
+
{links.map(link => (
|
|
36
|
+
<a href={link.href} {...(link.href.startsWith('http') ? { target: '_blank', rel: 'noopener' } : {})}>
|
|
37
|
+
{link.text}
|
|
38
|
+
</a>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
</section>
|
|
44
|
+
|
|
45
|
+
<style>
|
|
46
|
+
.cta-section {
|
|
47
|
+
background: var(--color-bg);
|
|
48
|
+
border-top: 1px solid var(--color-border);
|
|
49
|
+
padding: 96px 0;
|
|
50
|
+
text-align: center;
|
|
51
|
+
}
|
|
52
|
+
.container {
|
|
53
|
+
max-width: var(--width-landing, 1100px);
|
|
54
|
+
margin: 0 auto;
|
|
55
|
+
padding: 0 24px;
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 0;
|
|
60
|
+
}
|
|
61
|
+
h2 {
|
|
62
|
+
font-size: clamp(1.6rem, 3vw, 2.25rem);
|
|
63
|
+
font-weight: 700;
|
|
64
|
+
letter-spacing: -0.01em;
|
|
65
|
+
color: var(--color-text);
|
|
66
|
+
margin: 0 0 12px;
|
|
67
|
+
}
|
|
68
|
+
p {
|
|
69
|
+
font-size: 1.125rem;
|
|
70
|
+
color: var(--color-muted);
|
|
71
|
+
margin: 0 0 32px;
|
|
72
|
+
}
|
|
73
|
+
.cta-links {
|
|
74
|
+
display: flex;
|
|
75
|
+
justify-content: center;
|
|
76
|
+
gap: 32px;
|
|
77
|
+
flex-wrap: wrap;
|
|
78
|
+
margin-top: 32px;
|
|
79
|
+
}
|
|
80
|
+
.cta-links a {
|
|
81
|
+
font-size: 0.9375rem;
|
|
82
|
+
font-weight: 600;
|
|
83
|
+
color: var(--color-muted);
|
|
84
|
+
text-decoration: none;
|
|
85
|
+
transition: color 0.15s;
|
|
86
|
+
}
|
|
87
|
+
.cta-links a:hover {
|
|
88
|
+
color: var(--color-accent, #6366f1);
|
|
89
|
+
text-decoration: none;
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Feature {
|
|
3
|
+
icon: string;
|
|
4
|
+
title: string;
|
|
5
|
+
body: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
label?: string;
|
|
10
|
+
heading?: string;
|
|
11
|
+
features?: Feature[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
label = "What's included",
|
|
16
|
+
heading = 'Everything wired up, out of the box',
|
|
17
|
+
features = [
|
|
18
|
+
{
|
|
19
|
+
icon: '🔒',
|
|
20
|
+
title: 'Private by default',
|
|
21
|
+
body: 'Nothing publishes until you add publish: true. The privacy gate runs at build time and in CI — nothing slips through.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
icon: '🤖',
|
|
25
|
+
title: 'AI-native',
|
|
26
|
+
body: 'Pre-commit hook enriches every file with OpenAI or Anthropic. Descriptions, reading times, tags, related links — automatic.',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
icon: '🔗',
|
|
30
|
+
title: 'Obsidian wikilinks',
|
|
31
|
+
body: '[[note]] and [[note|alias]] resolve to real links on your site. Write in Obsidian, publish what you see.',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
icon: '🎨',
|
|
35
|
+
title: 'Swap themes in one line',
|
|
36
|
+
body: 'Default, blog, and minimal themes ship out of the box. Switch with one config change. Build and publish your own as an npm package.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
icon: '📡',
|
|
40
|
+
title: 'RSS · Sitemap · Search',
|
|
41
|
+
body: 'RSS feed, XML sitemap, and Pagefind full-text search are built in. No plugins to hunt down. No configuration required.',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
icon: '🚀',
|
|
45
|
+
title: 'Cloudflare Pages deploy',
|
|
46
|
+
body: 'GitHub Actions workflow included. Push to main — site builds, privacy check runs, deploys. Done.',
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
} = Astro.props;
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
<section class="features">
|
|
53
|
+
<div class="container">
|
|
54
|
+
<span class="section-label">{label}</span>
|
|
55
|
+
<h2>{heading}</h2>
|
|
56
|
+
<div class="feature-grid">
|
|
57
|
+
{features.map(f => (
|
|
58
|
+
<div class="feature-item">
|
|
59
|
+
<span class="fi-icon" aria-hidden="true">{f.icon}</span>
|
|
60
|
+
<div>
|
|
61
|
+
<h4>{f.title}</h4>
|
|
62
|
+
<p>{f.body}</p>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</section>
|
|
69
|
+
|
|
70
|
+
<style>
|
|
71
|
+
.features {
|
|
72
|
+
background: var(--color-bg);
|
|
73
|
+
border-top: 1px solid var(--color-border);
|
|
74
|
+
padding: 88px 0;
|
|
75
|
+
}
|
|
76
|
+
.container {
|
|
77
|
+
max-width: var(--width-landing, 1100px);
|
|
78
|
+
margin: 0 auto;
|
|
79
|
+
padding: 0 24px;
|
|
80
|
+
}
|
|
81
|
+
h2 {
|
|
82
|
+
font-size: clamp(1.5rem, 3vw, 2.25rem);
|
|
83
|
+
font-weight: 700;
|
|
84
|
+
letter-spacing: -0.01em;
|
|
85
|
+
color: var(--color-text);
|
|
86
|
+
margin: 0 0 48px;
|
|
87
|
+
}
|
|
88
|
+
.feature-grid {
|
|
89
|
+
display: grid;
|
|
90
|
+
grid-template-columns: repeat(3, 1fr);
|
|
91
|
+
border: 1px solid var(--color-border);
|
|
92
|
+
border-radius: var(--radius-lg, 12px);
|
|
93
|
+
overflow: hidden;
|
|
94
|
+
}
|
|
95
|
+
.feature-item {
|
|
96
|
+
padding: 32px;
|
|
97
|
+
border-right: 1px solid var(--color-border);
|
|
98
|
+
border-bottom: 1px solid var(--color-border);
|
|
99
|
+
display: flex;
|
|
100
|
+
gap: 16px;
|
|
101
|
+
align-items: flex-start;
|
|
102
|
+
}
|
|
103
|
+
/* Remove right border from every 3rd item */
|
|
104
|
+
.feature-item:nth-child(3n) { border-right: none; }
|
|
105
|
+
/* Remove bottom border from last row */
|
|
106
|
+
.feature-item:nth-last-child(-n+3) { border-bottom: none; }
|
|
107
|
+
.fi-icon {
|
|
108
|
+
font-size: 1.5rem;
|
|
109
|
+
flex-shrink: 0;
|
|
110
|
+
line-height: 1.2;
|
|
111
|
+
}
|
|
112
|
+
.feature-item h4 {
|
|
113
|
+
font-size: 1rem;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
color: var(--color-text);
|
|
116
|
+
margin: 0 0 6px;
|
|
117
|
+
}
|
|
118
|
+
.feature-item p {
|
|
119
|
+
font-size: 0.875rem;
|
|
120
|
+
line-height: 1.6;
|
|
121
|
+
color: var(--color-muted);
|
|
122
|
+
margin: 0;
|
|
123
|
+
}
|
|
124
|
+
@media (max-width: 768px) {
|
|
125
|
+
.feature-grid {
|
|
126
|
+
grid-template-columns: 1fr;
|
|
127
|
+
border: none;
|
|
128
|
+
gap: 1px;
|
|
129
|
+
background: var(--color-border);
|
|
130
|
+
border-radius: 0;
|
|
131
|
+
}
|
|
132
|
+
.feature-item {
|
|
133
|
+
border-right: none !important;
|
|
134
|
+
border-bottom: none !important;
|
|
135
|
+
background: var(--color-bg);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
</style>
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
import InstallBox from './InstallBox.astro';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
badge?: string;
|
|
6
|
+
ghUrl?: string;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
badge = 'Open source · Git-native · AI-ready',
|
|
12
|
+
ghUrl,
|
|
13
|
+
tags = ['Astro', 'Obsidian', 'Git', 'AI', 'TypeScript', 'Cloudflare Pages'],
|
|
14
|
+
} = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<section class="hero">
|
|
18
|
+
<div class="hero-glow" aria-hidden="true"></div>
|
|
19
|
+
<div class="hero-inner">
|
|
20
|
+
{badge && <div class="badge">{badge}</div>}
|
|
21
|
+
|
|
22
|
+
<h1><slot name="headline">All you have to do<br />is sing.</slot></h1>
|
|
23
|
+
|
|
24
|
+
<p class="hero-sub">
|
|
25
|
+
<slot name="sub">
|
|
26
|
+
karaoke-cms is a CMS framework built on Obsidian and Git.
|
|
27
|
+
Write your content. Everything else — publishing, privacy,
|
|
28
|
+
AI enrichment, navigation — is handled for you.
|
|
29
|
+
</slot>
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
<div class="hero-actions">
|
|
33
|
+
<InstallBox />
|
|
34
|
+
{ghUrl && (
|
|
35
|
+
<a href={ghUrl} target="_blank" rel="noopener" class="btn-ghost">
|
|
36
|
+
View on GitHub
|
|
37
|
+
</a>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
{tags.length > 0 && (
|
|
42
|
+
<div class="hero-tags">
|
|
43
|
+
{tags.map(tag => <span>{tag}</span>)}
|
|
44
|
+
</div>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
</section>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.hero {
|
|
51
|
+
position: relative;
|
|
52
|
+
background: var(--color-section-dark, #0f172a);
|
|
53
|
+
overflow: hidden;
|
|
54
|
+
padding: 96px 0 80px;
|
|
55
|
+
text-align: center;
|
|
56
|
+
}
|
|
57
|
+
.hero-glow {
|
|
58
|
+
position: absolute;
|
|
59
|
+
inset: 0;
|
|
60
|
+
background:
|
|
61
|
+
radial-gradient(ellipse 60% 50% at 50% 0%, rgba(99, 102, 241, 0.25) 0%, transparent 70%),
|
|
62
|
+
radial-gradient(ellipse 40% 40% at 80% 60%, rgba(139, 92, 246, 0.15) 0%, transparent 60%);
|
|
63
|
+
pointer-events: none;
|
|
64
|
+
}
|
|
65
|
+
.hero-inner {
|
|
66
|
+
position: relative;
|
|
67
|
+
max-width: var(--width-landing, 1100px);
|
|
68
|
+
margin: 0 auto;
|
|
69
|
+
padding: 0 24px;
|
|
70
|
+
}
|
|
71
|
+
.badge {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: 6px;
|
|
75
|
+
font-size: 0.75rem;
|
|
76
|
+
font-weight: 600;
|
|
77
|
+
letter-spacing: 0.06em;
|
|
78
|
+
text-transform: uppercase;
|
|
79
|
+
color: #a5b4fc;
|
|
80
|
+
border: 1px solid rgba(165, 180, 252, 0.3);
|
|
81
|
+
background: rgba(99, 102, 241, 0.1);
|
|
82
|
+
padding: 5px 14px;
|
|
83
|
+
border-radius: 999px;
|
|
84
|
+
margin-bottom: 28px;
|
|
85
|
+
}
|
|
86
|
+
h1 {
|
|
87
|
+
color: #fff;
|
|
88
|
+
font-size: clamp(2.4rem, 5vw, 4rem);
|
|
89
|
+
font-weight: 800;
|
|
90
|
+
letter-spacing: -0.02em;
|
|
91
|
+
line-height: 1.15;
|
|
92
|
+
margin: 0 0 20px;
|
|
93
|
+
}
|
|
94
|
+
.hero-sub {
|
|
95
|
+
max-width: 580px;
|
|
96
|
+
margin: 0 auto 36px;
|
|
97
|
+
font-size: 1.125rem;
|
|
98
|
+
color: #94a3b8;
|
|
99
|
+
line-height: 1.65;
|
|
100
|
+
}
|
|
101
|
+
.hero-actions {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
gap: 16px;
|
|
106
|
+
flex-wrap: wrap;
|
|
107
|
+
margin-bottom: 32px;
|
|
108
|
+
}
|
|
109
|
+
.btn-ghost {
|
|
110
|
+
font-size: 0.9rem;
|
|
111
|
+
font-weight: 600;
|
|
112
|
+
color: #94a3b8;
|
|
113
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
114
|
+
padding: 10px 20px;
|
|
115
|
+
border-radius: var(--radius-lg, 12px);
|
|
116
|
+
text-decoration: none;
|
|
117
|
+
transition: color 0.15s, border-color 0.15s;
|
|
118
|
+
}
|
|
119
|
+
.btn-ghost:hover {
|
|
120
|
+
color: #fff;
|
|
121
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
122
|
+
text-decoration: none;
|
|
123
|
+
}
|
|
124
|
+
.hero-tags {
|
|
125
|
+
display: flex;
|
|
126
|
+
flex-wrap: wrap;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
gap: 8px;
|
|
129
|
+
}
|
|
130
|
+
.hero-tags span {
|
|
131
|
+
font-size: 0.75rem;
|
|
132
|
+
color: #475569;
|
|
133
|
+
background: rgba(255, 255, 255, 0.04);
|
|
134
|
+
border: 1px solid rgba(255, 255, 255, 0.07);
|
|
135
|
+
padding: 3px 10px;
|
|
136
|
+
border-radius: 999px;
|
|
137
|
+
}
|
|
138
|
+
@media (max-width: 640px) {
|
|
139
|
+
.hero { padding: 64px 0 56px; }
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Step {
|
|
3
|
+
num: string;
|
|
4
|
+
title: string;
|
|
5
|
+
body: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
label?: string;
|
|
10
|
+
heading?: string;
|
|
11
|
+
steps?: Step[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
label = 'How it works',
|
|
16
|
+
heading = 'Three steps from note to live site',
|
|
17
|
+
steps = [
|
|
18
|
+
{
|
|
19
|
+
num: '01',
|
|
20
|
+
title: 'Write in Obsidian',
|
|
21
|
+
body: 'Open your vault. Write notes. Add publish: true to any file you want public. Everything else stays private.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
num: '02',
|
|
25
|
+
title: 'Commit to git',
|
|
26
|
+
body: 'The pre-commit hook runs automatically. AI enriches your content. Stage, commit, push — the hook handles enrichment silently.',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
num: '03',
|
|
30
|
+
title: "It's live",
|
|
31
|
+
body: 'CI builds your Astro site, runs the privacy gate, and deploys to Cloudflare Pages. Your site is live. Private content stays private.',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
} = Astro.props;
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<section class="how">
|
|
38
|
+
<div class="container">
|
|
39
|
+
<span class="section-label">{label}</span>
|
|
40
|
+
<h2>{heading}</h2>
|
|
41
|
+
<div class="steps">
|
|
42
|
+
{steps.map((step, i) => (
|
|
43
|
+
<>
|
|
44
|
+
{i > 0 && <span class="step-arrow" aria-hidden="true">→</span>}
|
|
45
|
+
<div class="step">
|
|
46
|
+
<div class="step-num" aria-hidden="true">{step.num}</div>
|
|
47
|
+
<h4>{step.title}</h4>
|
|
48
|
+
<p>{step.body}</p>
|
|
49
|
+
</div>
|
|
50
|
+
</>
|
|
51
|
+
))}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</section>
|
|
55
|
+
|
|
56
|
+
<style>
|
|
57
|
+
.how {
|
|
58
|
+
background: var(--color-bg-alt, #f8fafc);
|
|
59
|
+
border-top: 1px solid var(--color-border);
|
|
60
|
+
padding: 88px 0;
|
|
61
|
+
}
|
|
62
|
+
.container {
|
|
63
|
+
max-width: var(--width-landing, 1100px);
|
|
64
|
+
margin: 0 auto;
|
|
65
|
+
padding: 0 24px;
|
|
66
|
+
}
|
|
67
|
+
h2 {
|
|
68
|
+
font-size: clamp(1.5rem, 3vw, 2.25rem);
|
|
69
|
+
font-weight: 700;
|
|
70
|
+
letter-spacing: -0.01em;
|
|
71
|
+
color: var(--color-text);
|
|
72
|
+
margin: 0 0 56px;
|
|
73
|
+
}
|
|
74
|
+
.steps {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: flex-start;
|
|
77
|
+
gap: 16px;
|
|
78
|
+
}
|
|
79
|
+
.step {
|
|
80
|
+
flex: 1;
|
|
81
|
+
background: var(--color-bg);
|
|
82
|
+
border: 1px solid var(--color-border);
|
|
83
|
+
border-radius: var(--radius-lg, 12px);
|
|
84
|
+
padding: 32px;
|
|
85
|
+
}
|
|
86
|
+
.step-num {
|
|
87
|
+
font-size: 2rem;
|
|
88
|
+
font-weight: 800;
|
|
89
|
+
color: var(--color-accent, #6366f1);
|
|
90
|
+
opacity: 0.4;
|
|
91
|
+
margin-bottom: 12px;
|
|
92
|
+
font-variant-numeric: tabular-nums;
|
|
93
|
+
letter-spacing: -0.03em;
|
|
94
|
+
}
|
|
95
|
+
.step h4 {
|
|
96
|
+
font-size: 1.05rem;
|
|
97
|
+
font-weight: 600;
|
|
98
|
+
color: var(--color-text);
|
|
99
|
+
margin: 0 0 8px;
|
|
100
|
+
}
|
|
101
|
+
.step p {
|
|
102
|
+
font-size: 0.9rem;
|
|
103
|
+
color: var(--color-muted);
|
|
104
|
+
line-height: 1.6;
|
|
105
|
+
margin: 0;
|
|
106
|
+
}
|
|
107
|
+
.step-arrow {
|
|
108
|
+
color: var(--color-border);
|
|
109
|
+
font-size: 1.5rem;
|
|
110
|
+
padding-top: 40px;
|
|
111
|
+
flex-shrink: 0;
|
|
112
|
+
}
|
|
113
|
+
@media (max-width: 640px) {
|
|
114
|
+
.steps { flex-direction: column; }
|
|
115
|
+
.step-arrow { display: none; }
|
|
116
|
+
}
|
|
117
|
+
</style>
|