cmx-sdk 0.2.9 → 0.2.10
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/add-studio-YUDYE2OH.js +0 -0
- package/dist/chunk-7TDMLYBI.js +0 -0
- package/dist/chunk-EDXXR5BE.js +0 -0
- package/dist/chunk-FPQYL5GE.js +0 -0
- package/dist/chunk-IIQLQIDP.js +0 -0
- package/dist/chunk-NZQ6SBFS.js +0 -0
- package/dist/{chunk-EZMBZWH7.js → chunk-Y3S3K6M3.js} +73 -28
- package/dist/cli.js +2 -2
- package/dist/init-FLRQXJX4.js +0 -0
- package/dist/{interactive-menu-FYVOQSTL.js → interactive-menu-5PRQIESI.js} +1 -1
- package/dist/studio-HAS6DYLO.js +0 -0
- package/dist/{update-sdk-KJZ6VB4M.js → update-sdk-ZXMWQF3I.js} +2 -1
- package/dist/update-studio-TWCYSYIS.js +0 -0
- package/package.json +14 -16
- package/templates/AGENTS.md +0 -173
- package/templates/CLAUDE.md +0 -28
- package/templates/claude/commands/check.md +0 -64
- package/templates/claude/commands/next-action.md +0 -66
- package/templates/claude/skills/cmx-cache/SKILL.md +0 -50
- package/templates/claude/skills/cmx-cache/references/cache-patterns.md +0 -153
- package/templates/claude/skills/cmx-component/SKILL.md +0 -108
- package/templates/claude/skills/cmx-component/references/component-schema.md +0 -123
- package/templates/claude/skills/cmx-content/SKILL.md +0 -158
- package/templates/claude/skills/cmx-content/references/migration-patterns.md +0 -120
- package/templates/claude/skills/cmx-content/references/seed-patterns.md +0 -146
- package/templates/claude/skills/cmx-dev/SKILL.md +0 -266
- package/templates/claude/skills/cmx-dev/references/api-patterns.md +0 -220
- package/templates/claude/skills/cmx-dev/references/cli-reference.md +0 -54
- package/templates/claude/skills/cmx-form/SKILL.md +0 -103
- package/templates/claude/skills/cmx-form/references/form-template.md +0 -152
- package/templates/claude/skills/cmx-migrate/SKILL.md +0 -501
- package/templates/claude/skills/cmx-migrate/references/analysis-guide.md +0 -127
- package/templates/claude/skills/cmx-migrate/references/html-to-mdx.md +0 -99
- package/templates/claude/skills/cmx-migrate/references/intermediate-format.md +0 -196
- package/templates/claude/skills/cmx-migrate/references/tool-setup.md +0 -150
- package/templates/claude/skills/cmx-schema/SKILL.md +0 -159
- package/templates/claude/skills/cmx-schema/references/field-types.md +0 -164
- package/templates/claude/skills/cmx-schema/references/migration-scenarios.md +0 -44
- package/templates/claude/skills/cmx-seo/SKILL.md +0 -54
- package/templates/claude/skills/cmx-seo/references/seo-patterns.md +0 -216
- package/templates/claude/skills/cmx-style/SKILL.md +0 -48
- package/templates/claude/skills/cmx-style/references/style-patterns.md +0 -114
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# フィールドタイプリファレンス
|
|
2
|
-
|
|
3
|
-
## 一覧
|
|
4
|
-
|
|
5
|
-
| type | 説明 | options |
|
|
6
|
-
|------|------|---------|
|
|
7
|
-
| `text` | 単行テキスト | `maxLength`, `placeholder` |
|
|
8
|
-
| `textarea` | 複数行テキスト | `maxLength`, `placeholder` |
|
|
9
|
-
| `richtext` | リッチテキストエディタ | - |
|
|
10
|
-
| `number` | 数値 | `min`, `max`, `step` |
|
|
11
|
-
| `date` | 日付 | - |
|
|
12
|
-
| `datetime` | 日時 | - |
|
|
13
|
-
| `boolean` | 真偽値 | - |
|
|
14
|
-
| `select` | ドロップダウン選択 | `choices[]`, `multiple` |
|
|
15
|
-
| `multiselect` | 複数選択 | `choices[]` |
|
|
16
|
-
| `image` | 画像 | - |
|
|
17
|
-
| `file` | ファイル | - |
|
|
18
|
-
| `json` | JSONデータ | - |
|
|
19
|
-
| `url` | URL | - |
|
|
20
|
-
| `email` | メールアドレス | - |
|
|
21
|
-
| `relation` | 他データタイプへの参照 | `targetType` |
|
|
22
|
-
|
|
23
|
-
## options 詳細
|
|
24
|
-
|
|
25
|
-
```json
|
|
26
|
-
{
|
|
27
|
-
"maxLength": 100,
|
|
28
|
-
"placeholder": "入力してください",
|
|
29
|
-
"min": 0,
|
|
30
|
-
"max": 999,
|
|
31
|
-
"step": 1,
|
|
32
|
-
"choices": [
|
|
33
|
-
{ "value": "option1", "label": "選択肢1" },
|
|
34
|
-
{ "value": "option2", "label": "選択肢2" }
|
|
35
|
-
],
|
|
36
|
-
"multiple": false,
|
|
37
|
-
"targetType": "staff"
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### text / textarea
|
|
42
|
-
|
|
43
|
-
- `maxLength` (number) — 最大文字数
|
|
44
|
-
- `placeholder` (string) — プレースホルダーテキスト
|
|
45
|
-
|
|
46
|
-
### number
|
|
47
|
-
|
|
48
|
-
- `min` (number) — 最小値
|
|
49
|
-
- `max` (number) — 最大値
|
|
50
|
-
- `step` (number) — ステップ値
|
|
51
|
-
|
|
52
|
-
### select
|
|
53
|
-
|
|
54
|
-
- `choices` (array) — `{ "value": "key", "label": "表示名" }` の配列
|
|
55
|
-
- `multiple` (boolean) — `true` で複数選択可
|
|
56
|
-
|
|
57
|
-
### relation
|
|
58
|
-
|
|
59
|
-
- `targetType` (string) — 参照先データタイプの slug
|
|
60
|
-
|
|
61
|
-
## defaultValue(デフォルト値)
|
|
62
|
-
|
|
63
|
-
フィールド定義に `defaultValue` を設定すると、エントリ作成時に値が未入力の場合に自動的に適用されます。
|
|
64
|
-
|
|
65
|
-
```json
|
|
66
|
-
{
|
|
67
|
-
"key": "published",
|
|
68
|
-
"label": "公開",
|
|
69
|
-
"type": "boolean",
|
|
70
|
-
"required": true,
|
|
71
|
-
"defaultValue": false
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**動作:**
|
|
76
|
-
- エントリ作成時、フィールドに値が指定されていない場合に `defaultValue` が適用される
|
|
77
|
-
- 明示的に `null` が指定された場合は `null` が保存される(デフォルト値は適用されない)
|
|
78
|
-
- 値の型は `type` と一致する必要がある(例: `boolean` フィールドに `"false"` は不可)
|
|
79
|
-
|
|
80
|
-
**使用例:**
|
|
81
|
-
- `published: false` — グローバルデータタイプで、デフォルトで非公開
|
|
82
|
-
- `status: "draft"` — ステータスフィールドで、デフォルトで下書き
|
|
83
|
-
- `order: 0` — ソート順フィールドで、デフォルトで0
|
|
84
|
-
|
|
85
|
-
## データタイプ JSON 例
|
|
86
|
-
|
|
87
|
-
### スタッフ
|
|
88
|
-
|
|
89
|
-
```json
|
|
90
|
-
{
|
|
91
|
-
"slug": "staff",
|
|
92
|
-
"name": "スタッフ",
|
|
93
|
-
"description": "チームメンバー情報",
|
|
94
|
-
"fields": [
|
|
95
|
-
{
|
|
96
|
-
"key": "name",
|
|
97
|
-
"label": "名前",
|
|
98
|
-
"type": "text",
|
|
99
|
-
"required": true,
|
|
100
|
-
"description": "フルネーム",
|
|
101
|
-
"options": { "maxLength": 100 }
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
"key": "position",
|
|
105
|
-
"label": "役職",
|
|
106
|
-
"type": "select",
|
|
107
|
-
"options": {
|
|
108
|
-
"choices": [
|
|
109
|
-
{ "value": "director", "label": "取締役" },
|
|
110
|
-
{ "value": "manager", "label": "マネージャー" },
|
|
111
|
-
{ "value": "engineer", "label": "エンジニア" }
|
|
112
|
-
]
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
{ "key": "bio", "label": "自己紹介", "type": "textarea", "options": { "maxLength": 500 } },
|
|
116
|
-
{ "key": "photo", "label": "写真", "type": "image" },
|
|
117
|
-
{ "key": "email", "label": "メールアドレス", "type": "email" }
|
|
118
|
-
]
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### FAQ
|
|
123
|
-
|
|
124
|
-
```json
|
|
125
|
-
{
|
|
126
|
-
"slug": "faq",
|
|
127
|
-
"name": "よくある質問",
|
|
128
|
-
"fields": [
|
|
129
|
-
{ "key": "question", "label": "質問", "type": "text", "required": true },
|
|
130
|
-
{ "key": "answer", "label": "回答", "type": "richtext", "required": true },
|
|
131
|
-
{
|
|
132
|
-
"key": "category",
|
|
133
|
-
"label": "カテゴリ",
|
|
134
|
-
"type": "select",
|
|
135
|
-
"options": {
|
|
136
|
-
"choices": [
|
|
137
|
-
{ "value": "general", "label": "一般" },
|
|
138
|
-
{ "value": "pricing", "label": "料金" },
|
|
139
|
-
{ "value": "technical", "label": "技術" }
|
|
140
|
-
]
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
{ "key": "is_featured", "label": "注目", "type": "boolean" }
|
|
144
|
-
]
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### 実績・事例
|
|
149
|
-
|
|
150
|
-
```json
|
|
151
|
-
{
|
|
152
|
-
"slug": "case-studies",
|
|
153
|
-
"name": "実績・事例",
|
|
154
|
-
"description": "導入事例や制作実績",
|
|
155
|
-
"fields": [
|
|
156
|
-
{ "key": "title", "label": "タイトル", "type": "text", "required": true },
|
|
157
|
-
{ "key": "client", "label": "クライアント名", "type": "text" },
|
|
158
|
-
{ "key": "description", "label": "概要", "type": "textarea" },
|
|
159
|
-
{ "key": "thumbnail", "label": "サムネイル", "type": "image" },
|
|
160
|
-
{ "key": "url", "label": "サイトURL", "type": "url" },
|
|
161
|
-
{ "key": "published_date", "label": "公開日", "type": "date" }
|
|
162
|
-
]
|
|
163
|
-
}
|
|
164
|
-
```
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# 移行シナリオ別ガイド
|
|
2
|
-
|
|
3
|
-
サイト種別ごとに、よく使われるデータタイプとコレクションの構成例。
|
|
4
|
-
|
|
5
|
-
## コーポレートサイト
|
|
6
|
-
|
|
7
|
-
データタイプ:
|
|
8
|
-
- `staff` — メンバー紹介(name, position, bio, photo)
|
|
9
|
-
- `service` — サービス紹介(title, description, icon, features)
|
|
10
|
-
- `faq` — よくある質問(question, answer, category)
|
|
11
|
-
- `testimonial` — お客様の声(name, company, content, rating)
|
|
12
|
-
|
|
13
|
-
コレクション:
|
|
14
|
-
- `news` type=news: お知らせ
|
|
15
|
-
- `page` type=page: 会社概要、アクセス等
|
|
16
|
-
|
|
17
|
-
## LP(ランディングページ)
|
|
18
|
-
|
|
19
|
-
データタイプ:
|
|
20
|
-
- `feature` — 機能紹介(title, description, icon)
|
|
21
|
-
- `pricing` — 料金プラン(name, price, features, is_popular)
|
|
22
|
-
- `testimonial` — 利用者の声(name, company, content)
|
|
23
|
-
- `cta` — CTAセクション(title, description, button_text, button_url)
|
|
24
|
-
|
|
25
|
-
## ブログ・メディア
|
|
26
|
-
|
|
27
|
-
コレクション:
|
|
28
|
-
- `post` type=post: 記事(blog, column, interview等)
|
|
29
|
-
- フロントマター: title, description, category, tags, published_at
|
|
30
|
-
- **category と tags はフロントマターに直接記述(データタイプ不要)**
|
|
31
|
-
|
|
32
|
-
データタイプ:
|
|
33
|
-
- `author` — 著者情報(name, bio, avatar, social_links)
|
|
34
|
-
|
|
35
|
-
## ECサイト
|
|
36
|
-
|
|
37
|
-
データタイプ:
|
|
38
|
-
- `product` — 商品(name, price, description, image, category)
|
|
39
|
-
- `category` — カテゴリ(name, slug, parent)
|
|
40
|
-
- `review` — レビュー(rating, content, author)
|
|
41
|
-
|
|
42
|
-
コレクション:
|
|
43
|
-
- `page` type=page: 特商法表記、プライバシーポリシー等
|
|
44
|
-
- `news` type=news: セール情報、新商品情報
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: cmx-seo
|
|
3
|
-
description: |
|
|
4
|
-
CMX Starter Kit の SEO・メタデータ実装スキル。generateMetadata、OGP、sitemap.ts、robots.ts、JSON-LD 構造化データのパターン。
|
|
5
|
-
トリガー: 「SEOを設定」「メタデータを追加」「OGPを設定」「サイトマップを作成」
|
|
6
|
-
「robots.txtを設定」「構造化データを追加」「JSON-LDを追加」「検索対策」
|
|
7
|
-
「Twitterカードを設定」「og:imageを設定」「canonical URLを設定」など。
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# CMX SEO・メタデータ
|
|
11
|
-
|
|
12
|
-
## 事前確認
|
|
13
|
-
|
|
14
|
-
`cmx/site-config.md` のサイト名・説明を確認し、メタデータに反映する。
|
|
15
|
-
|
|
16
|
-
## 環境変数
|
|
17
|
-
|
|
18
|
-
`NEXT_PUBLIC_SITE_URL` — サイトの本番 URL。sitemap・canonical・OGP の URL 構築に必須。`.env.local` で設定済みか確認。
|
|
19
|
-
|
|
20
|
-
## 既存ユーティリティ
|
|
21
|
-
|
|
22
|
-
`src/lib/utils/metadata.ts` に `generateCollectionMetadata` / `generateContentMetadata` ヘルパーが存在。拡張する場合はこのファイルを修正。
|
|
23
|
-
|
|
24
|
-
## パターン別ガイド
|
|
25
|
-
|
|
26
|
-
各パターンの詳細コードは [references/seo-patterns.md](references/seo-patterns.md) を参照。
|
|
27
|
-
|
|
28
|
-
### generateMetadata
|
|
29
|
-
|
|
30
|
-
- **静的ページ**: `export const metadata: Metadata` で直接定義
|
|
31
|
-
- **動的ページ**: `export async function generateMetadata()` で CMX API からデータ取得
|
|
32
|
-
- **プレビューページ**: `robots: { index: false, follow: false }` を必ず設定
|
|
33
|
-
|
|
34
|
-
### OGP / Twitter Card
|
|
35
|
-
|
|
36
|
-
`metadata.ts` の既存ヘルパーを拡張し、`openGraph` と `twitter` フィールドを追加する。デフォルト OG 画像は `/public/og-default.jpg` に配置。
|
|
37
|
-
|
|
38
|
-
### sitemap.ts
|
|
39
|
-
|
|
40
|
-
`src/app/sitemap.ts` を作成。CMX API から全コレクションの記事を取得し動的生成。
|
|
41
|
-
|
|
42
|
-
### robots.ts
|
|
43
|
-
|
|
44
|
-
`src/app/robots.ts` を作成。`/preview/` と `/api/` を disallow。
|
|
45
|
-
|
|
46
|
-
### JSON-LD 構造化データ
|
|
47
|
-
|
|
48
|
-
記事詳細ページに `<script type="application/ld+json">` を埋め込む。スキーマタイプは `BlogPosting`(ブログ)、`NewsArticle`(ニュース)、`WebPage`(その他)。
|
|
49
|
-
|
|
50
|
-
## 変更後
|
|
51
|
-
|
|
52
|
-
1. 各ページの `<head>` 出力を確認(ブラウザの DevTools)
|
|
53
|
-
2. OGP デバッガー(Facebook Sharing Debugger 等)で確認
|
|
54
|
-
3. Google Rich Results Test で JSON-LD を検証
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
# SEO 実装パターン
|
|
2
|
-
|
|
3
|
-
## generateMetadata — 静的ページ
|
|
4
|
-
|
|
5
|
-
```tsx
|
|
6
|
-
// src/app/about/page.tsx
|
|
7
|
-
import type { Metadata } from "next"
|
|
8
|
-
|
|
9
|
-
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com"
|
|
10
|
-
|
|
11
|
-
export const metadata: Metadata = {
|
|
12
|
-
title: "会社概要 | サイト名",
|
|
13
|
-
description: "会社概要ページの説明文",
|
|
14
|
-
alternates: { canonical: `${SITE_URL}/about` },
|
|
15
|
-
openGraph: {
|
|
16
|
-
title: "会社概要 | サイト名",
|
|
17
|
-
description: "会社概要ページの説明文",
|
|
18
|
-
url: `${SITE_URL}/about`,
|
|
19
|
-
images: ["/og-default.jpg"],
|
|
20
|
-
},
|
|
21
|
-
}
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## generateMetadata — 動的ページ(記事詳細)
|
|
25
|
-
|
|
26
|
-
`src/lib/utils/metadata.ts` を拡張:
|
|
27
|
-
|
|
28
|
-
```tsx
|
|
29
|
-
import type { Metadata } from "next"
|
|
30
|
-
import { fetchPublishedContent } from "@/lib/api/public-client"
|
|
31
|
-
|
|
32
|
-
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com"
|
|
33
|
-
|
|
34
|
-
export async function generateContentMetadata(
|
|
35
|
-
collectionSlug: string,
|
|
36
|
-
contentSlug: string
|
|
37
|
-
): Promise<Metadata> {
|
|
38
|
-
const data = await fetchPublishedContent(collectionSlug, contentSlug)
|
|
39
|
-
if (!data) {
|
|
40
|
-
throw new Error(`Failed to fetch content metadata: ${collectionSlug}/${contentSlug}`)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const url = `${SITE_URL}/${collectionSlug}/${contentSlug}`
|
|
44
|
-
const title = `${data.content.title} | ${data.collection.name}`
|
|
45
|
-
const description = data.content.description || undefined
|
|
46
|
-
const image = "/og-default.jpg"
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
title,
|
|
50
|
-
description,
|
|
51
|
-
alternates: { canonical: url },
|
|
52
|
-
openGraph: {
|
|
53
|
-
title: data.content.title,
|
|
54
|
-
description,
|
|
55
|
-
url,
|
|
56
|
-
type: "article",
|
|
57
|
-
images: [image],
|
|
58
|
-
},
|
|
59
|
-
twitter: {
|
|
60
|
-
card: "summary_large_image",
|
|
61
|
-
title: data.content.title,
|
|
62
|
-
description,
|
|
63
|
-
images: [image],
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## sitemap.ts
|
|
70
|
-
|
|
71
|
-
```tsx
|
|
72
|
-
// src/app/sitemap.ts
|
|
73
|
-
import type { MetadataRoute } from "next"
|
|
74
|
-
import { fetchPublishedContents } from "@/lib/api/public-client"
|
|
75
|
-
import { COLLECTION_SLUGS } from "@/lib/constants/collections"
|
|
76
|
-
|
|
77
|
-
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com"
|
|
78
|
-
|
|
79
|
-
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
80
|
-
const entries: MetadataRoute.Sitemap = [
|
|
81
|
-
{
|
|
82
|
-
url: SITE_URL,
|
|
83
|
-
lastModified: new Date(),
|
|
84
|
-
changeFrequency: "daily",
|
|
85
|
-
priority: 1,
|
|
86
|
-
},
|
|
87
|
-
]
|
|
88
|
-
|
|
89
|
-
// 静的ページ
|
|
90
|
-
const staticPages = ["/about", "/contact"]
|
|
91
|
-
for (const page of staticPages) {
|
|
92
|
-
entries.push({
|
|
93
|
-
url: `${SITE_URL}${page}`,
|
|
94
|
-
changeFrequency: "monthly",
|
|
95
|
-
priority: 0.5,
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// 各コレクションの記事を取得
|
|
100
|
-
for (const slug of Object.values(COLLECTION_SLUGS)) {
|
|
101
|
-
// コレクション一覧ページ
|
|
102
|
-
entries.push({
|
|
103
|
-
url: `${SITE_URL}/${slug}`,
|
|
104
|
-
changeFrequency: "daily",
|
|
105
|
-
priority: 0.8,
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
// 各記事
|
|
109
|
-
try {
|
|
110
|
-
const data = await fetchPublishedContents(slug)
|
|
111
|
-
if (data?.contents) {
|
|
112
|
-
for (const item of data.contents) {
|
|
113
|
-
entries.push({
|
|
114
|
-
url: `${SITE_URL}/${slug}/${item.slug}`,
|
|
115
|
-
lastModified: item.publishedAt ? new Date(item.publishedAt) : new Date(),
|
|
116
|
-
changeFrequency: "weekly",
|
|
117
|
-
priority: 0.7,
|
|
118
|
-
})
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
} catch {
|
|
122
|
-
// コレクション取得失敗時はスキップ
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return entries
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## robots.ts
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
// src/app/robots.ts
|
|
134
|
-
import type { MetadataRoute } from "next"
|
|
135
|
-
|
|
136
|
-
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com"
|
|
137
|
-
|
|
138
|
-
export default function robots(): MetadataRoute.Robots {
|
|
139
|
-
return {
|
|
140
|
-
rules: {
|
|
141
|
-
userAgent: "*",
|
|
142
|
-
allow: "/",
|
|
143
|
-
disallow: ["/preview/", "/api/"],
|
|
144
|
-
},
|
|
145
|
-
sitemap: `${SITE_URL}/sitemap.xml`,
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
## JSON-LD 構造化データ
|
|
151
|
-
|
|
152
|
-
### ブログ記事
|
|
153
|
-
|
|
154
|
-
```tsx
|
|
155
|
-
// src/app/blog/[slug]/page.tsx 内
|
|
156
|
-
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com"
|
|
157
|
-
|
|
158
|
-
function BlogPostJsonLd({ content, collectionSlug, slug }: {
|
|
159
|
-
content: { title: string; description?: string; publishedAt?: string; updatedAt?: string; featuredImage?: string }
|
|
160
|
-
collectionSlug: string
|
|
161
|
-
slug: string
|
|
162
|
-
}) {
|
|
163
|
-
const jsonLd = {
|
|
164
|
-
"@context": "https://schema.org",
|
|
165
|
-
"@type": "BlogPosting",
|
|
166
|
-
headline: content.title,
|
|
167
|
-
description: content.description,
|
|
168
|
-
datePublished: content.publishedAt,
|
|
169
|
-
dateModified: content.updatedAt ?? content.publishedAt,
|
|
170
|
-
image: content.featuredImage,
|
|
171
|
-
url: `${SITE_URL}/${collectionSlug}/${slug}`,
|
|
172
|
-
publisher: {
|
|
173
|
-
"@type": "Organization",
|
|
174
|
-
name: "サイト名", // site-config.md のサイト名を使用
|
|
175
|
-
},
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return (
|
|
179
|
-
<script
|
|
180
|
-
type="application/ld+json"
|
|
181
|
-
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
182
|
-
/>
|
|
183
|
-
)
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### 組織情報(ルートレイアウト)
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
// src/app/layout.tsx に追加
|
|
191
|
-
function OrganizationJsonLd() {
|
|
192
|
-
const jsonLd = {
|
|
193
|
-
"@context": "https://schema.org",
|
|
194
|
-
"@type": "Organization",
|
|
195
|
-
name: "サイト名",
|
|
196
|
-
url: process.env.NEXT_PUBLIC_SITE_URL,
|
|
197
|
-
logo: `${process.env.NEXT_PUBLIC_SITE_URL}/logo.png`,
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return (
|
|
201
|
-
<script
|
|
202
|
-
type="application/ld+json"
|
|
203
|
-
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
204
|
-
/>
|
|
205
|
-
)
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
## コレクションタイプ別の JSON-LD スキーマタイプ
|
|
210
|
-
|
|
211
|
-
| コレクションタイプ | schema.org @type |
|
|
212
|
-
|------------------|------------------|
|
|
213
|
-
| post(ブログ) | BlogPosting |
|
|
214
|
-
| news(ニュース) | NewsArticle |
|
|
215
|
-
| page(ページ) | WebPage |
|
|
216
|
-
| doc(ドキュメント) | TechArticle |
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: cmx-style
|
|
3
|
-
description: |
|
|
4
|
-
CMX Starter Kit のスタイル・デザイン変更スキル。CSS 変数、フォント、レイアウト、カラーの変更パターンと site-config.md との連動。
|
|
5
|
-
トリガー: 「スタイルを変更」「デザインを変えたい」「色を変更」「フォントを変更」
|
|
6
|
-
「レイアウトを変更」「ヘッダーを修正」「フッターを修正」「ダークモード」
|
|
7
|
-
「余白を調整」「見た目を変えたい」など。
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# CMX スタイル変更
|
|
11
|
-
|
|
12
|
-
## 事前確認
|
|
13
|
-
|
|
14
|
-
変更前に必ず `cmx/site-config.md` のデザイン方針セクションを読み、変更が方針と矛盾しないか確認する。矛盾する場合はコンフィグの更新も提案すること。
|
|
15
|
-
|
|
16
|
-
## 変更対象と対応ファイル
|
|
17
|
-
|
|
18
|
-
| 変更内容 | 対象ファイル |
|
|
19
|
-
|---------|------------|
|
|
20
|
-
| カラー(テーマ色、背景色等) | `src/app/globals.css` |
|
|
21
|
-
| フォント | `src/app/layout.tsx` |
|
|
22
|
-
| Header | `src/components/layout/Header.tsx` |
|
|
23
|
-
| Footer | `src/components/layout/Footer.tsx` |
|
|
24
|
-
| MDX 記事のスタイル | `src/app/globals.css`(`.prose` カスタマイズ) |
|
|
25
|
-
| コンポーネントスタイル | 該当 TSX ファイル |
|
|
26
|
-
| 全体的なスペーシング | `src/app/globals.css` |
|
|
27
|
-
|
|
28
|
-
## パターン別ガイド
|
|
29
|
-
|
|
30
|
-
詳細は [references/style-patterns.md](references/style-patterns.md) を参照。
|
|
31
|
-
|
|
32
|
-
### カラー変更
|
|
33
|
-
|
|
34
|
-
`src/app/globals.css` の CSS 変数を修正。shadcn/ui のテーマ変数体系に従う。
|
|
35
|
-
|
|
36
|
-
### フォント変更
|
|
37
|
-
|
|
38
|
-
`src/app/layout.tsx` の `next/font/google` インポートを変更。CSS 変数でフォントファミリーを指定。
|
|
39
|
-
|
|
40
|
-
### レイアウト変更
|
|
41
|
-
|
|
42
|
-
Header/Footer は `src/components/layout/` 配下。ナビゲーション項目、ロゴ、カラムレイアウトを修正。
|
|
43
|
-
|
|
44
|
-
## 変更後
|
|
45
|
-
|
|
46
|
-
1. 開発サーバーで全ページの表示を確認
|
|
47
|
-
2. レスポンシブ(モバイル/タブレット/デスクトップ)を確認
|
|
48
|
-
3. `cmx/site-config.md` のデザイン方針セクションを更新して同期
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# スタイル変更パターン
|
|
2
|
-
|
|
3
|
-
## カラー変更
|
|
4
|
-
|
|
5
|
-
`src/app/globals.css` の `:root` と `.dark` で CSS 変数を定義。shadcn/ui 体系:
|
|
6
|
-
|
|
7
|
-
```css
|
|
8
|
-
:root {
|
|
9
|
-
--background: 0 0% 100%; /* 背景色 */
|
|
10
|
-
--foreground: 222.2 84% 4.9%; /* テキスト色 */
|
|
11
|
-
--card: 0 0% 100%; /* カード背景 */
|
|
12
|
-
--card-foreground: 222.2 84% 4.9%; /* カードテキスト */
|
|
13
|
-
--primary: 222.2 47.4% 11.2%; /* プライマリ */
|
|
14
|
-
--primary-foreground: 210 40% 98%; /* プライマリ上のテキスト */
|
|
15
|
-
--secondary: 210 40% 96.1%; /* セカンダリ */
|
|
16
|
-
--muted: 210 40% 96.1%; /* 控えめな背景 */
|
|
17
|
-
--muted-foreground: 215.4 16.3% 46.9%; /* 控えめなテキスト */
|
|
18
|
-
--accent: 210 40% 96.1%; /* アクセント */
|
|
19
|
-
--destructive: 0 84.2% 60.2%; /* 削除・エラー */
|
|
20
|
-
--border: 214.3 31.8% 91.4%; /* ボーダー */
|
|
21
|
-
--ring: 222.2 84% 4.9%; /* フォーカスリング */
|
|
22
|
-
--radius: 0.5rem; /* 角丸 */
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
値は HSL 形式(`hue saturation% lightness%`)。Tailwind で `bg-primary` のように使用される。
|
|
27
|
-
|
|
28
|
-
## フォント変更
|
|
29
|
-
|
|
30
|
-
`src/app/layout.tsx` で Google Fonts を読み込み:
|
|
31
|
-
|
|
32
|
-
```tsx
|
|
33
|
-
import { Inter, Noto_Sans_JP } from "next/font/google"
|
|
34
|
-
|
|
35
|
-
const inter = Inter({
|
|
36
|
-
subsets: ["latin"],
|
|
37
|
-
variable: "--font-sans",
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
const notoSansJP = Noto_Sans_JP({
|
|
41
|
-
subsets: ["latin"],
|
|
42
|
-
weight: ["400", "500", "700"],
|
|
43
|
-
variable: "--font-noto-sans-jp",
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
// <body> に className で適用
|
|
47
|
-
<body className={`${inter.variable} ${notoSansJP.variable} font-sans`}>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
`globals.css` でフォントファミリーを定義:
|
|
51
|
-
|
|
52
|
-
```css
|
|
53
|
-
:root {
|
|
54
|
-
--font-sans: var(--font-inter), var(--font-noto-sans-jp), sans-serif;
|
|
55
|
-
--font-mono: var(--font-geist-mono), monospace;
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Header カスタマイズ
|
|
60
|
-
|
|
61
|
-
`src/components/layout/Header.tsx`:
|
|
62
|
-
|
|
63
|
-
```tsx
|
|
64
|
-
const navItems = [
|
|
65
|
-
{ href: "/blog", label: "ブログ" },
|
|
66
|
-
{ href: "/about", label: "企業情報" },
|
|
67
|
-
{ href: "/contact", label: "お問い合わせ" },
|
|
68
|
-
]
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
- ナビ項目の追加・削除・順序変更
|
|
72
|
-
- ロゴの変更(テキスト or 画像)
|
|
73
|
-
- スティッキーヘッダーの有無
|
|
74
|
-
- モバイルメニューの挙動
|
|
75
|
-
|
|
76
|
-
## Footer カスタマイズ
|
|
77
|
-
|
|
78
|
-
`src/components/layout/Footer.tsx`:
|
|
79
|
-
|
|
80
|
-
- カラム数とカラム内容の変更
|
|
81
|
-
- SNS リンクの追加
|
|
82
|
-
- コピーライト表記の変更
|
|
83
|
-
|
|
84
|
-
## MDX 記事スタイル
|
|
85
|
-
|
|
86
|
-
`@tailwindcss/typography` の `.prose` クラスをカスタマイズ:
|
|
87
|
-
|
|
88
|
-
```css
|
|
89
|
-
.prose {
|
|
90
|
-
--tw-prose-body: hsl(var(--foreground));
|
|
91
|
-
--tw-prose-headings: hsl(var(--foreground));
|
|
92
|
-
--tw-prose-links: hsl(var(--primary));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.prose h2 {
|
|
96
|
-
@apply border-b pb-2;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.prose img {
|
|
100
|
-
@apply rounded-lg;
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## レスポンシブ設計
|
|
105
|
-
|
|
106
|
-
Tailwind のブレークポイント:
|
|
107
|
-
|
|
108
|
-
| プレフィックス | 最小幅 | 用途 |
|
|
109
|
-
|-------------|-------|------|
|
|
110
|
-
| (なし) | 0px | モバイル |
|
|
111
|
-
| `sm:` | 640px | 小型タブレット |
|
|
112
|
-
| `md:` | 768px | タブレット |
|
|
113
|
-
| `lg:` | 1024px | デスクトップ |
|
|
114
|
-
| `xl:` | 1280px | 大画面 |
|