create-ampless 0.2.0-alpha.9 → 1.0.0-alpha.39

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.
Files changed (84) hide show
  1. package/README.ja.md +77 -0
  2. package/README.md +4 -1
  3. package/dist/index.js +99 -47
  4. package/dist/templates/_shared/AGENTS.ja.md +104 -0
  5. package/dist/templates/_shared/AGENTS.md +104 -0
  6. package/dist/templates/_shared/README.ja.md +175 -0
  7. package/dist/templates/_shared/README.md +175 -0
  8. package/dist/templates/_shared/RUNBOOK.ja.md +120 -0
  9. package/dist/templates/_shared/RUNBOOK.md +24 -51
  10. package/dist/templates/_shared/amplify/auth/post-confirmation/resource.ts +6 -0
  11. package/dist/templates/_shared/amplify/backend.ts +2 -0
  12. package/dist/templates/_shared/amplify/data/get-published-post.js +9 -12
  13. package/dist/templates/_shared/amplify/data/list-posts-by-tag.js +6 -9
  14. package/dist/templates/_shared/amplify/data/list-published-posts.js +10 -11
  15. package/dist/templates/_shared/amplify/data/resource.custom.ts +1 -2
  16. package/dist/templates/_shared/amplify/data/resource.ts +22 -5
  17. package/dist/templates/_shared/amplify/events/processor-untrusted/resource.ts +4 -0
  18. package/dist/templates/_shared/amplify/functions/mcp-handler/handler.ts +1 -0
  19. package/dist/templates/_shared/amplify/functions/mcp-handler/resource.ts +11 -0
  20. package/dist/templates/_shared/amplify/functions/user-admin/resource.ts +6 -0
  21. package/dist/templates/_shared/app/(admin)/admin/mcp-tokens/page.tsx +5 -0
  22. package/dist/templates/_shared/app/globals.css +55 -39
  23. package/dist/templates/_shared/app/layout.tsx +24 -11
  24. package/dist/templates/_shared/app/site/[siteId]/r/[slug]/[[...path]]/route.ts +17 -0
  25. package/dist/templates/_shared/cms.config.ts +1 -23
  26. package/dist/templates/_shared/components/site-chrome/site-sidebar.tsx +2 -3
  27. package/dist/templates/_shared/lib/admin.ts +9 -7
  28. package/dist/templates/_shared/lib/posts-public.ts +1 -1
  29. package/dist/templates/_shared/package.json +11 -12
  30. package/dist/templates/_shared/proxy.ts +3 -3
  31. package/dist/templates/blog/README.ja.md +22 -0
  32. package/dist/templates/blog/README.md +17 -47
  33. package/dist/templates/blog/manifest.ts +1 -1
  34. package/dist/templates/blog/pages/feed.ts +3 -4
  35. package/dist/templates/blog/pages/home.tsx +5 -8
  36. package/dist/templates/blog/pages/post.tsx +7 -7
  37. package/dist/templates/blog/pages/sitemap.ts +3 -4
  38. package/dist/templates/blog/pages/tag.tsx +4 -4
  39. package/dist/templates/blog/tokens.css +26 -40
  40. package/dist/templates/corporate/README.ja.md +18 -0
  41. package/dist/templates/corporate/README.md +12 -14
  42. package/dist/templates/corporate/pages/feed.ts +2 -3
  43. package/dist/templates/corporate/pages/home.tsx +5 -8
  44. package/dist/templates/corporate/pages/post.tsx +7 -7
  45. package/dist/templates/corporate/pages/sitemap.ts +2 -3
  46. package/dist/templates/corporate/pages/tag.tsx +4 -4
  47. package/dist/templates/corporate/tokens.css +17 -39
  48. package/dist/templates/dads/README.ja.md +31 -0
  49. package/dist/templates/dads/README.md +13 -17
  50. package/dist/templates/dads/pages/feed.ts +2 -3
  51. package/dist/templates/dads/pages/home.tsx +5 -8
  52. package/dist/templates/dads/pages/post.tsx +7 -7
  53. package/dist/templates/dads/pages/sitemap.ts +2 -3
  54. package/dist/templates/dads/pages/tag.tsx +4 -4
  55. package/dist/templates/dads/tokens.css +22 -42
  56. package/dist/templates/docs/README.ja.md +24 -0
  57. package/dist/templates/docs/README.md +10 -13
  58. package/dist/templates/docs/pages/feed.ts +2 -3
  59. package/dist/templates/docs/pages/home.tsx +5 -6
  60. package/dist/templates/docs/pages/post.tsx +8 -8
  61. package/dist/templates/docs/pages/sitemap.ts +2 -3
  62. package/dist/templates/docs/pages/tag.tsx +5 -5
  63. package/dist/templates/docs/tokens.css +17 -39
  64. package/dist/templates/landing/README.ja.md +20 -0
  65. package/dist/templates/landing/README.md +14 -19
  66. package/dist/templates/landing/pages/feed.ts +3 -4
  67. package/dist/templates/landing/pages/home.tsx +5 -8
  68. package/dist/templates/landing/pages/post.tsx +7 -7
  69. package/dist/templates/landing/pages/sitemap.ts +2 -3
  70. package/dist/templates/landing/pages/tag.tsx +4 -4
  71. package/dist/templates/landing/tokens.css +17 -39
  72. package/dist/templates/minimal/README.ja.md +14 -0
  73. package/dist/templates/minimal/README.md +9 -47
  74. package/dist/templates/minimal/pages/feed.ts +3 -4
  75. package/dist/templates/minimal/pages/home.tsx +3 -4
  76. package/dist/templates/minimal/pages/post.tsx +6 -6
  77. package/dist/templates/minimal/pages/sitemap.ts +3 -4
  78. package/dist/templates/minimal/pages/tag.tsx +3 -3
  79. package/dist/templates/minimal/tokens.css +17 -39
  80. package/package.json +1 -1
  81. package/dist/templates/_shared/app/(admin)/admin/sites/page.tsx +0 -5
  82. package/dist/templates/_shared/app/site/[siteId]/raw/[slug]/route.ts +0 -5
  83. package/dist/templates/_shared/lib/admin-site-client.ts +0 -10
  84. package/dist/templates/_shared/lib/admin-site.ts +0 -12
@@ -13,26 +13,31 @@
13
13
  * enough; we don't hardcode hex values here. The fallbacks after
14
14
  * each `var(...)` are the DADS values from version 0.3.4 in case
15
15
  * the plugin isn't loaded for some reason.
16
+ *
17
+ * Dark mode: DADS doesn't ship an official dark palette as of v0.3.4,
18
+ * so the D side of `light-dark()` here is an inverted approximation.
19
+ * When DADS adds a dark variant, bind the D arg to the corresponding
20
+ * plugin variables (e.g. `var(--color-blue-300)` for primary).
16
21
  */
17
22
 
18
23
  [data-theme='dads'] {
19
- --background: #ffffff;
20
- --foreground: #1a1a1c;
21
- --card: #ffffff;
22
- --card-foreground: #1a1a1c;
23
- --primary: var(--color-blue-900, #0017c1);
24
- --primary-foreground: #ffffff;
25
- --secondary: var(--color-blue-50, #e8f1fe);
26
- --secondary-foreground: var(--color-blue-900, #0017c1);
27
- --muted: #f5f5f5;
28
- --muted-foreground: #595959;
29
- --accent: var(--color-blue-50, #e8f1fe);
30
- --accent-foreground: var(--color-blue-900, #0017c1);
31
- --destructive: var(--color-red-900, #ce0000);
32
- --destructive-foreground: #ffffff;
33
- --border: #d9d9d9;
34
- --input: #d9d9d9;
35
- --ring: var(--color-blue-900, #0017c1);
24
+ --background: light-dark(#ffffff, #1a1a1c);
25
+ --foreground: light-dark(#1a1a1c, #f0f0f0);
26
+ --card: light-dark(#ffffff, #232327);
27
+ --card-foreground: light-dark(#1a1a1c, #f0f0f0);
28
+ --primary: light-dark(var(--color-blue-900, #0017c1), var(--color-blue-300, #9db7f9));
29
+ --primary-foreground: light-dark(#ffffff, #1a1a1c);
30
+ --secondary: light-dark(var(--color-blue-50, #e8f1fe), #2c2f3a);
31
+ --secondary-foreground: light-dark(var(--color-blue-900, #0017c1), #f0f0f0);
32
+ --muted: light-dark(#f5f5f5, #28282c);
33
+ --muted-foreground: light-dark(#595959, #b3b3b3);
34
+ --accent: light-dark(var(--color-blue-50, #e8f1fe), #2c2f3a);
35
+ --accent-foreground: light-dark(var(--color-blue-900, #0017c1), #f0f0f0);
36
+ --destructive: light-dark(var(--color-red-900, #ce0000), var(--color-red-700, #fa0000));
37
+ --destructive-foreground: light-dark(#ffffff, #ffffff);
38
+ --border: light-dark(#d9d9d9, #3a3a3f);
39
+ --input: light-dark(#d9d9d9, #3a3a3f);
40
+ --ring: light-dark(var(--color-blue-900, #0017c1), var(--color-blue-300, #9db7f9));
36
41
  --radius: 0.25rem;
37
42
  /* DADS recommends Noto Sans JP for body text. The plugin sets
38
43
  * fontFamily.sans to it; we surface that through our usual
@@ -40,28 +45,3 @@
40
45
  * up consistently. */
41
46
  --ampless-body-font: 'Noto Sans JP', -apple-system, BlinkMacSystemFont, sans-serif;
42
47
  }
43
-
44
- @media (prefers-color-scheme: dark) {
45
- [data-theme='dads'] {
46
- /* DADS doesn't ship an official dark palette as of v0.3.4; this
47
- * is an inverted approximation. When DADS adds a dark variant,
48
- * bind these to the corresponding plugin variables. */
49
- --background: #1a1a1c;
50
- --foreground: #f0f0f0;
51
- --card: #232327;
52
- --card-foreground: #f0f0f0;
53
- --primary: var(--color-blue-300, #9db7f9);
54
- --primary-foreground: #1a1a1c;
55
- --secondary: #2c2f3a;
56
- --secondary-foreground: #f0f0f0;
57
- --muted: #28282c;
58
- --muted-foreground: #b3b3b3;
59
- --accent: #2c2f3a;
60
- --accent-foreground: #f0f0f0;
61
- --destructive: var(--color-red-700, #fa0000);
62
- --destructive-foreground: #ffffff;
63
- --border: #3a3a3f;
64
- --input: #3a3a3f;
65
- --ring: var(--color-blue-300, #9db7f9);
66
- }
67
- }
@@ -0,0 +1,24 @@
1
+ > English: [README.md](./README.md)
2
+ >
3
+ # Docs テーマ
4
+
5
+ サイドバー主導のドキュメントレイアウト。サイドバー項目は通常のリンクに加え、**タグ駆動セクション**を組み合わせられます — `tag:<name>` 形式の項目を入れると、そのタグが付いた公開済み投稿の一覧に自動展開されます。タグでコンテンツを整理するだけでナビゲーションに自動反映されます。
6
+
7
+ ## カスタマイズ
8
+
9
+ `/admin/sites/<siteId>/theme` で設定:
10
+
11
+ - **プライマリカラー**
12
+ - **角丸**
13
+ - **コードフォント** — システム等幅 / JetBrains Mono
14
+ - **サイドバーナビ** — 各行は `ラベル` + `URL`。URL には以下を指定可能:
15
+ - パス(`/getting-started`)
16
+ - 外部 URL(`https://...`)
17
+ - タグ参照(`tag:tutorials`)→ 見出し + タグ付き投稿一覧として描画
18
+ - **ロゴ画像 URL**
19
+ - **ヘッダーナビ** — トップレベルのリンク
20
+ - **フッターリンク** — ラベル + URL のペア
21
+
22
+ ## 執筆のヒント
23
+
24
+ 投稿に `guide` タグを付けて、サイドバーに URL `tag:guide` の行を追加するだけ。新しい記事を公開しても手動でリンクを編集する必要はなく、サイドバーに自動で並びます。
@@ -1,27 +1,24 @@
1
- # {{siteName}}
1
+ > 日本語版: [README.ja.md](./README.ja.md)
2
+ >
3
+ # Docs theme
2
4
 
3
- Docs theme: sidebar-led documentation layout. The sidebar combines plain links with **tag-driven sections** — entering a URL like `tag:guide` in the sidebar nav field auto-expands to a list of every published post tagged "guide". Lets writers organize content by tag and have it appear in nav automatically.
5
+ Sidebar-led documentation layout. The sidebar combines plain links with **tag-driven sections** — a sidebar entry of the form `tag:<name>` auto-expands into a list of every published post with that tag. Lets writers organize content by tag and have it appear in nav automatically.
4
6
 
5
7
  ## Customizing
6
8
 
7
9
  In `/admin/sites/<siteId>/theme`:
8
10
 
11
+ - **Primary color**
12
+ - **Corner radius**
13
+ - **Code font** — System monospace / JetBrains Mono
9
14
  - **Sidebar navigation** — each row is `Label` + `URL`. The URL can be:
10
15
  - a path (`/getting-started`)
11
16
  - an external URL (`https://...`)
12
17
  - a tag reference (`tag:tutorials`) → renders as a heading + list of tagged posts
13
- - Header navigation (top-level links)
14
- - Code font (system mono / JetBrains Mono / etc.)
15
- - Primary color, corner radius
18
+ - **Logo image URL**
19
+ - **Header navigation** top-level links
20
+ - **Footer links** label + URL pairs
16
21
 
17
22
  ## Authoring tip
18
23
 
19
24
  Tag a post `guide` (in the post editor) and add a sidebar row with URL `tag:guide`. The sidebar will list that post automatically — no manual link editing every time you publish.
20
-
21
- ## Getting started
22
-
23
- ```bash
24
- npm install
25
- npx ampx sandbox
26
- npm run dev
27
- ```
@@ -1,12 +1,11 @@
1
1
  import { publicAssetUrl } from '@/lib/storage'
2
2
 
3
3
  interface Ctx {
4
- siteId: string
5
4
  request: Request
6
5
  }
7
6
 
8
- export async function docsFeedHandler({ siteId }: Ctx): Promise<Response> {
9
- const url = publicAssetUrl(`public/plugins/rss/${siteId}/feed.xml`)
7
+ export async function docsFeedHandler(_ctx: Ctx): Promise<Response> {
8
+ const url = publicAssetUrl('public/plugins/rss/feed.xml')
10
9
  const upstream = await fetch(url, { cache: 'no-store' })
11
10
  if (!upstream.ok) {
12
11
  return new Response(
@@ -11,12 +11,11 @@ import { CollapsibleSidebar } from '@/components/site-chrome/collapsible-sidebar
11
11
  // Docs home: sidebar nav on the left (with optional tag-driven
12
12
  // sections), latest posts list on the right. Acts as the docs landing
13
13
  // page until the user arranges static pages.
14
- export default async function DocsHome({ params }: ThemeRouteContext) {
15
- const { siteId } = await params
14
+ export default async function DocsHome(_: ThemeRouteContext) {
16
15
  const [settings, theme, postsResult] = await Promise.all([
17
- loadSiteSettings(siteId),
18
- loadThemeConfig(siteId),
19
- listPublishedPosts({ siteId, limit: 12 }),
16
+ loadSiteSettings(),
17
+ loadThemeConfig(),
18
+ listPublishedPosts({ limit: 12 }),
20
19
  ])
21
20
  const posts = postsResult.items
22
21
 
@@ -31,7 +30,7 @@ export default async function DocsHome({ params }: ThemeRouteContext) {
31
30
 
32
31
  <div className="mx-auto grid max-w-6xl gap-6 px-6 py-10 lg:grid-cols-[15rem_1fr] lg:gap-10">
33
32
  <CollapsibleSidebar className="lg:sticky lg:top-6 lg:self-start">
34
- <SiteSidebar links={theme.values.sidebarNav} siteId={siteId} />
33
+ <SiteSidebar links={theme.values.sidebarNav} />
35
34
  </CollapsibleSidebar>
36
35
 
37
36
  <main className="min-w-0">
@@ -17,21 +17,21 @@ import { CollapsibleSidebar } from '@/components/site-chrome/collapsible-sidebar
17
17
  type PostCtx = ThemeRouteContext<{ slug: string }>
18
18
 
19
19
  export async function generatePostMetadata({ params }: PostCtx): Promise<Metadata> {
20
- const { siteId, slug } = await params
21
- const post = await getPublishedPost(slug, { siteId })
20
+ const { slug } = await params
21
+ const post = await getPublishedPost(slug)
22
22
  if (!post) return {}
23
- return postMetadata(post, siteId)
23
+ return postMetadata(post)
24
24
  }
25
25
 
26
26
  // Docs post page: sidebar always visible while reading. The sidebar
27
27
  // re-uses theme.sidebarNav, so navigation context stays consistent
28
28
  // across the home page and individual articles.
29
29
  export default async function DocsPost({ params }: PostCtx) {
30
- const { siteId, slug } = await params
30
+ const { slug } = await params
31
31
  const [post, settings, theme] = await Promise.all([
32
- getPublishedPost(slug, { siteId }),
33
- loadSiteSettings(siteId),
34
- loadThemeConfig(siteId),
32
+ getPublishedPost(slug),
33
+ loadSiteSettings(),
34
+ loadThemeConfig(),
35
35
  ])
36
36
  if (!post) notFound()
37
37
 
@@ -52,7 +52,7 @@ export default async function DocsPost({ params }: PostCtx) {
52
52
 
53
53
  <div className="mx-auto grid max-w-6xl gap-6 px-6 py-10 lg:grid-cols-[15rem_1fr] lg:gap-10">
54
54
  <CollapsibleSidebar className="lg:sticky lg:top-6 lg:self-start">
55
- <SiteSidebar links={theme.values.sidebarNav} siteId={siteId} />
55
+ <SiteSidebar links={theme.values.sidebarNav} />
56
56
  </CollapsibleSidebar>
57
57
 
58
58
  <main className="min-w-0">
@@ -1,12 +1,11 @@
1
1
  import { publicAssetUrl } from '@/lib/storage'
2
2
 
3
3
  interface Ctx {
4
- siteId: string
5
4
  request: Request
6
5
  }
7
6
 
8
- export async function docsSitemapHandler({ siteId }: Ctx): Promise<Response> {
9
- const url = publicAssetUrl(`public/plugins/seo/${siteId}/sitemap.xml`)
7
+ export async function docsSitemapHandler(_ctx: Ctx): Promise<Response> {
8
+ const url = publicAssetUrl('public/plugins/seo/sitemap.xml')
10
9
  const upstream = await fetch(url, { cache: 'no-store' })
11
10
  if (!upstream.ok) {
12
11
  return new Response(
@@ -11,12 +11,12 @@ import { CollapsibleSidebar } from '@/components/site-chrome/collapsible-sidebar
11
11
  import { t } from '@/lib/i18n'
12
12
 
13
13
  export default async function DocsTag({ params }: ThemeRouteContext<{ tag: string }>) {
14
- const { siteId, tag } = await params
14
+ const { tag } = await params
15
15
  const decodedTag = decodeURIComponent(tag)
16
16
  const [{ items: posts }, settings, theme] = await Promise.all([
17
- listPostsByTag(decodedTag, { siteId, limit: 50 }),
18
- loadSiteSettings(siteId),
19
- loadThemeConfig(siteId),
17
+ listPostsByTag(decodedTag, { limit: 50 }),
18
+ loadSiteSettings(),
19
+ loadThemeConfig(),
20
20
  ])
21
21
  if (posts.length === 0) notFound()
22
22
 
@@ -31,7 +31,7 @@ export default async function DocsTag({ params }: ThemeRouteContext<{ tag: strin
31
31
 
32
32
  <div className="mx-auto grid max-w-6xl gap-6 px-6 py-10 lg:grid-cols-[15rem_1fr] lg:gap-10">
33
33
  <CollapsibleSidebar className="lg:sticky lg:top-6 lg:self-start">
34
- <SiteSidebar links={theme.values.sidebarNav} siteId={siteId} />
34
+ <SiteSidebar links={theme.values.sidebarNav} />
35
35
  </CollapsibleSidebar>
36
36
 
37
37
  <main className="min-w-0">
@@ -2,50 +2,28 @@
2
2
  * tighter radius, monospace-leaning code styling. */
3
3
 
4
4
  [data-theme='docs'] {
5
- --background: oklch(1 0 0);
6
- --foreground: oklch(0.18 0.02 280);
7
- --card: oklch(0.99 0 0);
8
- --card-foreground: oklch(0.18 0.02 280);
9
- --primary: oklch(0.5 0.18 280);
10
- --primary-foreground: oklch(0.99 0 0);
11
- --secondary: oklch(0.96 0.01 280);
12
- --secondary-foreground: oklch(0.25 0.05 280);
13
- --muted: oklch(0.97 0.005 280);
14
- --muted-foreground: oklch(0.5 0.02 280);
15
- --accent: oklch(0.95 0.04 280);
16
- --accent-foreground: oklch(0.3 0.1 280);
17
- --destructive: oklch(0.55 0.22 25);
18
- --destructive-foreground: oklch(0.99 0 0);
19
- --border: oklch(0.9 0.005 280);
20
- --input: oklch(0.9 0.005 280);
21
- --ring: oklch(0.5 0.18 280);
5
+ --background: light-dark(oklch(1 0 0), oklch(0.16 0.02 280));
6
+ --foreground: light-dark(oklch(0.18 0.02 280), oklch(0.96 0.005 280));
7
+ --card: light-dark(oklch(0.99 0 0), oklch(0.2 0.02 280));
8
+ --card-foreground: light-dark(oklch(0.18 0.02 280), oklch(0.96 0.005 280));
9
+ --primary: light-dark(oklch(0.5 0.18 280), oklch(0.72 0.16 280));
10
+ --primary-foreground: light-dark(oklch(0.99 0 0), oklch(0.16 0.02 280));
11
+ --secondary: light-dark(oklch(0.96 0.01 280), oklch(0.28 0.03 280));
12
+ --secondary-foreground: light-dark(oklch(0.25 0.05 280), oklch(0.96 0.005 280));
13
+ --muted: light-dark(oklch(0.97 0.005 280), oklch(0.24 0.02 280));
14
+ --muted-foreground: light-dark(oklch(0.5 0.02 280), oklch(0.7 0.02 280));
15
+ --accent: light-dark(oklch(0.95 0.04 280), oklch(0.32 0.06 280));
16
+ --accent-foreground: light-dark(oklch(0.3 0.1 280), oklch(0.96 0.005 280));
17
+ --destructive: light-dark(oklch(0.55 0.22 25), oklch(0.55 0.22 25));
18
+ --destructive-foreground: light-dark(oklch(0.99 0 0), oklch(0.99 0 0));
19
+ --border: light-dark(oklch(0.9 0.005 280), oklch(0.32 0.02 280));
20
+ --input: light-dark(oklch(0.9 0.005 280), oklch(0.32 0.02 280));
21
+ --ring: light-dark(oklch(0.5 0.18 280), oklch(0.72 0.16 280));
22
22
  --radius: 0.25rem;
23
23
  --ampless-body-font: system-ui, -apple-system, sans-serif;
24
24
  --ampless-code-font: ui-monospace, SFMono-Regular, Menlo, monospace;
25
25
  }
26
26
 
27
- @media (prefers-color-scheme: dark) {
28
- [data-theme='docs'] {
29
- --background: oklch(0.16 0.02 280);
30
- --foreground: oklch(0.96 0.005 280);
31
- --card: oklch(0.2 0.02 280);
32
- --card-foreground: oklch(0.96 0.005 280);
33
- --primary: oklch(0.72 0.16 280);
34
- --primary-foreground: oklch(0.16 0.02 280);
35
- --secondary: oklch(0.28 0.03 280);
36
- --secondary-foreground: oklch(0.96 0.005 280);
37
- --muted: oklch(0.24 0.02 280);
38
- --muted-foreground: oklch(0.7 0.02 280);
39
- --accent: oklch(0.32 0.06 280);
40
- --accent-foreground: oklch(0.96 0.005 280);
41
- --destructive: oklch(0.55 0.22 25);
42
- --destructive-foreground: oklch(0.99 0 0);
43
- --border: oklch(0.32 0.02 280);
44
- --input: oklch(0.32 0.02 280);
45
- --ring: oklch(0.72 0.16 280);
46
- }
47
- }
48
-
49
27
  /* Code font scope: prose code blocks under the docs theme use the
50
28
  * configurable --ampless-code-font variable. The body font remains the
51
29
  * theme-configured --ampless-body-font (or system fallback). */
@@ -0,0 +1,20 @@
1
+ > English: [README.md](./README.md)
2
+ >
3
+ # Landing テーマ
4
+
5
+ 1 ページ完結型のヒーロー中心レイアウト。任意で「最新記事」グリッドも併設可能。ヘッダー / フッターナビ設定とウォームコーラル系アクセントパレットを備えます。
6
+
7
+ ## カスタマイズ
8
+
9
+ `/admin/sites/<siteId>/theme` で設定:
10
+
11
+ - **ヒーロー見出し** — 空欄ならサイト名を使用
12
+ - **ヒーローサブ見出し** — 空欄ならサイト説明を使用
13
+ - **CTA ボタンのテキスト**
14
+ - **CTA リンク先**
15
+ - **プライマリカラー**
16
+ - **角丸**
17
+ - **フィーチャー記事のスラッグ** — 公開済み投稿を 1 件ホームに固定
18
+ - **ロゴ画像 URL**
19
+ - **ヘッダーナビ** — ラベル + URL のペア
20
+ - **フッターリンク** — ラベル + URL のペア
@@ -1,25 +1,20 @@
1
- # {{siteName}}
1
+ > 日本語版: [README.ja.md](./README.ja.md)
2
+ >
3
+ # Landing theme
2
4
 
3
- A site powered by [ampless](https://github.com/heavymoons/ampless), using the **Landing** theme — hero-led single-page layout with optional "Latest" post grid, configurable header / footer nav, and a warm-coral accent palette.
5
+ Single-page hero focus with optional "Latest" post grid. Hero-led layout with configurable header / footer nav and a warm-coral accent palette.
4
6
 
5
7
  ## Customizing
6
8
 
7
9
  In `/admin/sites/<siteId>/theme`:
8
10
 
9
- - Hero headline / subheadline / CTA button text + URL
10
- - Header navigation (label + URL pairs)
11
- - Footer links
12
- - Primary color
13
- - Corner radius
14
-
15
- Empty hero fields fall back to the site name / description from `/admin/sites/<siteId>`.
16
-
17
- ## Getting started
18
-
19
- ```bash
20
- npm install
21
- npx ampx sandbox # provision the AWS backend
22
- npm run dev # start Next.js
23
- ```
24
-
25
- See the project README for full setup.
11
+ - **Hero headline** empty falls back to the site name
12
+ - **Hero subheadline** empty falls back to the site description
13
+ - **CTA button text**
14
+ - **CTA URL**
15
+ - **Primary color**
16
+ - **Corner radius**
17
+ - **Featured post slug** pin one published post to the home page
18
+ - **Logo image URL**
19
+ - **Header navigation** — label + URL pairs
20
+ - **Footer links** — label + URL pairs
@@ -1,14 +1,13 @@
1
1
  import { publicAssetUrl } from '@/lib/storage'
2
2
 
3
3
  interface Ctx {
4
- siteId: string
5
4
  request: Request
6
5
  }
7
6
 
8
7
  // /feed.xml proxy — plugin-rss writes the feed to
9
- // `public/plugins/rss/{siteId}/feed.xml`. Same flow as the blog theme.
10
- export async function landingFeedHandler({ siteId }: Ctx): Promise<Response> {
11
- const url = publicAssetUrl(`public/plugins/rss/${siteId}/feed.xml`)
8
+ // `public/plugins/rss/feed.xml`. Same flow as the blog theme.
9
+ export async function landingFeedHandler(_ctx: Ctx): Promise<Response> {
10
+ const url = publicAssetUrl('public/plugins/rss/feed.xml')
12
11
  const upstream = await fetch(url, { cache: 'no-store' })
13
12
  if (!upstream.ok) {
14
13
  return new Response(
@@ -10,21 +10,18 @@ import { SiteFooter } from '@/components/site-chrome/site-footer'
10
10
  // Hero-led landing layout: big headline + subhead + CTA, then an
11
11
  // optional "Latest" grid sourced from published posts. Falls back to
12
12
  // site name / description when the manifest hero fields are empty.
13
- export default async function LandingHome({ params }: ThemeRouteContext) {
14
- const { siteId } = await params
13
+ export default async function LandingHome(_: ThemeRouteContext) {
15
14
  const [settings, theme, postsResult] = await Promise.all([
16
- loadSiteSettings(siteId),
17
- loadThemeConfig(siteId),
18
- listPublishedPosts({ siteId, limit: 6 }),
15
+ loadSiteSettings(),
16
+ loadThemeConfig(),
17
+ listPublishedPosts({ limit: 6 }),
19
18
  ])
20
19
 
21
20
  // Featured embed below the hero — typical use is a short "About"
22
21
  // or "Welcome" article. Filtered out of the Latest grid below to
23
22
  // avoid showing the same post twice.
24
23
  const featuredSlug = theme.values.featuredSlug?.trim()
25
- const featured = featuredSlug
26
- ? await getPublishedPost(featuredSlug, { siteId })
27
- : null
24
+ const featured = featuredSlug ? await getPublishedPost(featuredSlug) : null
28
25
  const posts = featured
29
26
  ? postsResult.items.filter((p) => p.slug !== featured.slug)
30
27
  : postsResult.items
@@ -16,18 +16,18 @@ import { t } from '@/lib/i18n'
16
16
  type PostCtx = ThemeRouteContext<{ slug: string }>
17
17
 
18
18
  export async function generatePostMetadata({ params }: PostCtx): Promise<Metadata> {
19
- const { siteId, slug } = await params
20
- const post = await getPublishedPost(slug, { siteId })
19
+ const { slug } = await params
20
+ const post = await getPublishedPost(slug)
21
21
  if (!post) return {}
22
- return postMetadata(post, siteId)
22
+ return postMetadata(post)
23
23
  }
24
24
 
25
25
  export default async function LandingPost({ params }: PostCtx) {
26
- const { siteId, slug } = await params
26
+ const { slug } = await params
27
27
  const [post, settings, theme] = await Promise.all([
28
- getPublishedPost(slug, { siteId }),
29
- loadSiteSettings(siteId),
30
- loadThemeConfig(siteId),
28
+ getPublishedPost(slug),
29
+ loadSiteSettings(),
30
+ loadThemeConfig(),
31
31
  ])
32
32
  if (!post) notFound()
33
33
 
@@ -1,12 +1,11 @@
1
1
  import { publicAssetUrl } from '@/lib/storage'
2
2
 
3
3
  interface Ctx {
4
- siteId: string
5
4
  request: Request
6
5
  }
7
6
 
8
- export async function landingSitemapHandler({ siteId }: Ctx): Promise<Response> {
9
- const url = publicAssetUrl(`public/plugins/seo/${siteId}/sitemap.xml`)
7
+ export async function landingSitemapHandler(_ctx: Ctx): Promise<Response> {
8
+ const url = publicAssetUrl('public/plugins/seo/sitemap.xml')
10
9
  const upstream = await fetch(url, { cache: 'no-store' })
11
10
  if (!upstream.ok) {
12
11
  return new Response(
@@ -9,12 +9,12 @@ import { SiteFooter } from '@/components/site-chrome/site-footer'
9
9
  import { t } from '@/lib/i18n'
10
10
 
11
11
  export default async function LandingTag({ params }: ThemeRouteContext<{ tag: string }>) {
12
- const { siteId, tag } = await params
12
+ const { tag } = await params
13
13
  const decodedTag = decodeURIComponent(tag)
14
14
  const [{ items: posts }, settings, theme] = await Promise.all([
15
- listPostsByTag(decodedTag, { siteId, limit: 50 }),
16
- loadSiteSettings(siteId),
17
- loadThemeConfig(siteId),
15
+ listPostsByTag(decodedTag, { limit: 50 }),
16
+ loadSiteSettings(),
17
+ loadThemeConfig(),
18
18
  ])
19
19
 
20
20
  if (posts.length === 0) notFound()
@@ -3,45 +3,23 @@
3
3
  * templates/blog/tokens.css for the scoped-selector pattern. */
4
4
 
5
5
  [data-theme='landing'] {
6
- --background: oklch(0.99 0.01 60);
7
- --foreground: oklch(0.18 0.02 30);
8
- --card: oklch(1 0 0);
9
- --card-foreground: oklch(0.18 0.02 30);
10
- --primary: oklch(0.6 0.18 35);
11
- --primary-foreground: oklch(0.99 0.005 60);
12
- --secondary: oklch(0.96 0.015 50);
13
- --secondary-foreground: oklch(0.25 0.04 30);
14
- --muted: oklch(0.96 0.015 50);
15
- --muted-foreground: oklch(0.5 0.02 40);
16
- --accent: oklch(0.95 0.05 35);
17
- --accent-foreground: oklch(0.3 0.1 30);
18
- --destructive: oklch(0.55 0.22 25);
19
- --destructive-foreground: oklch(0.99 0 0);
20
- --border: oklch(0.92 0.01 60);
21
- --input: oklch(0.92 0.01 60);
22
- --ring: oklch(0.6 0.18 35);
6
+ --background: light-dark(oklch(0.99 0.01 60), oklch(0.16 0.02 30));
7
+ --foreground: light-dark(oklch(0.18 0.02 30), oklch(0.96 0.01 60));
8
+ --card: light-dark(oklch(1 0 0), oklch(0.22 0.02 30));
9
+ --card-foreground: light-dark(oklch(0.18 0.02 30), oklch(0.96 0.01 60));
10
+ --primary: light-dark(oklch(0.6 0.18 35), oklch(0.72 0.16 35));
11
+ --primary-foreground: light-dark(oklch(0.99 0.005 60), oklch(0.16 0.02 30));
12
+ --secondary: light-dark(oklch(0.96 0.015 50), oklch(0.28 0.03 30));
13
+ --secondary-foreground: light-dark(oklch(0.25 0.04 30), oklch(0.96 0.01 60));
14
+ --muted: light-dark(oklch(0.96 0.015 50), oklch(0.28 0.03 30));
15
+ --muted-foreground: light-dark(oklch(0.5 0.02 40), oklch(0.7 0.02 40));
16
+ --accent: light-dark(oklch(0.95 0.05 35), oklch(0.32 0.06 30));
17
+ --accent-foreground: light-dark(oklch(0.3 0.1 30), oklch(0.96 0.01 60));
18
+ --destructive: light-dark(oklch(0.55 0.22 25), oklch(0.55 0.22 25));
19
+ --destructive-foreground: light-dark(oklch(0.99 0 0), oklch(0.99 0 0));
20
+ --border: light-dark(oklch(0.92 0.01 60), oklch(0.32 0.03 30));
21
+ --input: light-dark(oklch(0.92 0.01 60), oklch(0.32 0.03 30));
22
+ --ring: light-dark(oklch(0.6 0.18 35), oklch(0.72 0.16 35));
23
23
  --radius: 0.75rem;
24
24
  --ampless-body-font: system-ui, -apple-system, sans-serif;
25
25
  }
26
-
27
- @media (prefers-color-scheme: dark) {
28
- [data-theme='landing'] {
29
- --background: oklch(0.16 0.02 30);
30
- --foreground: oklch(0.96 0.01 60);
31
- --card: oklch(0.22 0.02 30);
32
- --card-foreground: oklch(0.96 0.01 60);
33
- --primary: oklch(0.72 0.16 35);
34
- --primary-foreground: oklch(0.16 0.02 30);
35
- --secondary: oklch(0.28 0.03 30);
36
- --secondary-foreground: oklch(0.96 0.01 60);
37
- --muted: oklch(0.28 0.03 30);
38
- --muted-foreground: oklch(0.7 0.02 40);
39
- --accent: oklch(0.32 0.06 30);
40
- --accent-foreground: oklch(0.96 0.01 60);
41
- --destructive: oklch(0.55 0.22 25);
42
- --destructive-foreground: oklch(0.99 0 0);
43
- --border: oklch(0.32 0.03 30);
44
- --input: oklch(0.32 0.03 30);
45
- --ring: oklch(0.72 0.16 35);
46
- }
47
- }
@@ -0,0 +1,14 @@
1
+ > English: [README.md](./README.md)
2
+ >
3
+ # Minimal テーマ
4
+
5
+ shadcn/ui のカラートークンをベースにした、暖色系ニュートラル地にソフトブルーをアクセントとした構成。カスタマイズ項目を絞ったシンプルなブログレイアウトで、デザインを主張させたくない場合に向きます。
6
+
7
+ ## カスタマイズ
8
+
9
+ `/admin/sites/<siteId>/theme` で設定:
10
+
11
+ - **プライマリカラー**
12
+ - **角丸**
13
+
14
+ 以上です。フォントや複数のアクセントカラー、フッター / ナビゲーションの制御など、より細かい調整が必要な場合は `blog` テーマを利用してください。
@@ -1,52 +1,14 @@
1
- # {{siteName}}
1
+ > 日本語版: [README.ja.md](./README.ja.md)
2
+ >
3
+ # Minimal theme
2
4
 
3
- A blog site powered by [ampless](https://github.com/heavymoons/ampless), using the **Minimal** theme — a soft blue accent on a warm-neutral background, derived from shadcn/ui's color tokens.
5
+ Soft blue accent on a warm-neutral background, derived from shadcn/ui's color tokens. Stripped-down blog layout with very few customization knobs — pick this when you want the design to disappear.
4
6
 
5
- ## Getting Started
7
+ ## Customizing
6
8
 
7
- This project uses Amplify Gen 2 for the backend (Cognito, DynamoDB, S3) and Next.js for the frontend.
9
+ In `/admin/sites/<siteId>/theme`:
8
10
 
9
- ```bash
10
- # 1. Install dependencies
11
- npm install
11
+ - **Primary color**
12
+ - **Corner radius**
12
13
 
13
- # 2. Start a personal AWS sandbox (terminal 1)
14
- # Requires AWS credentials configured (`aws configure`).
15
- # First run takes ~5–10 min to provision resources.
16
- # Generates amplify_outputs.json when ready.
17
- npx ampx sandbox
18
-
19
- # 3. Start the Next.js dev server (terminal 2)
20
- npm run dev
21
- ```
22
-
23
- Then open [http://localhost:3000](http://localhost:3000).
24
-
25
- ## First admin user
26
-
27
- Open [http://localhost:3000/login](http://localhost:3000/login) and click **Create admin account**. The first user to register is automatically added to the `ampless-admin` Cognito group.
28
-
29
- After that, manage content from `/admin`:
30
-
31
- - `/admin` — dashboard
32
- - `/admin/posts` — list / create / edit posts (tiptap editor)
33
- - `/admin/media` — upload images to S3
34
-
35
- ## Production deploy
36
-
37
- ```bash
38
- git init && git add . && git commit -m "init"
39
- git remote add origin <your-repo>
40
- git push
41
- # Then connect the repo to AWS Amplify Hosting in the AWS console.
42
- ```
43
-
44
- ## Customize
45
-
46
- - `cms.config.ts` — site name, media delivery mode, plugins
47
- - `app/` — Next.js App Router pages (`(public)/` for the blog, `(admin)/` for the CMS)
48
- - `amplify/` — Amplify Gen 2 backend definitions (auth / data / storage)
49
-
50
- ## Plugins
51
-
52
- Enabled: {{plugins}}
14
+ That's it. For richer customization (fonts, multiple accent colors, footer / nav controls), use the `blog` theme.