From 372d0aeadf1f460bcd4ecc452e43da33a898c65d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 14:11:31 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E7=84=A1=E6=96=99100=E5=80=8B=E3=81=AE?= =?UTF-8?q?=E5=8F=AF=E8=A6=96=E5=8C=96=E3=81=A8=E8=B2=B7=E3=81=84=E5=88=87?= =?UTF-8?q?=E3=82=8A=E8=A1=A8=E7=8F=BE=E3=81=AE=E7=B5=B1=E4=B8=80=EF=BC=88?= =?UTF-8?q?=E4=BD=9C=E6=A5=AD=E4=B8=AD=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=83=9D=E3=82=A4=E3=83=B3=E3=83=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - スタジオに「無料100個」トグルとカードの「無料」バッジを追加。 検索・カテゴリ・お気に入りとAND併用可 - 無料IDの生成スクリプトと照合テスト(880件のmanifestと100/100一致・重複ゼロを ビルド時とテストの両方で恒久検査) - 統計バーの「¥0 あなたの月額」→「100 無料で使える数」(ja/en)。 LPの「3つのプラン」表記も2プランに修正 - 件数だけを freeCount.ts に分離し、LP初期チャンクへのID混入を回避(実測で確認) ※ デザイン磨き込み(P2)と配布物整備(P3)が並行作業中のため、その途中経過を含む。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01YXb6DP2sELzRyZkqpe15p4 --- .gitignore | 3 + LICENSE | 133 +++++++++++++ docs/dist-README.md | 213 +++++++++++++++++++++ mcp/README.md | 86 +++++++-- package.json | 9 +- scripts/build-free-ids.mjs | 93 +++++++++ scripts/build-release.mjs | 331 ++++++++++++++++++++++++++++++++ scripts/verify-release.mjs | 337 +++++++++++++++++++++++++++++++++ src/Studio.test.tsx | 92 +++++++++ src/Studio.tsx | 55 +++++- src/components/GalleryView.tsx | 11 ++ src/lib/free.ts | 20 ++ src/pages/Landing.tsx | 16 +- src/registry/freeCount.ts | 7 + src/registry/freeIds.test.ts | 40 ++++ src/registry/freeIds.ts | 106 +++++++++++ 16 files changed, 1512 insertions(+), 40 deletions(-) create mode 100644 LICENSE create mode 100644 docs/dist-README.md create mode 100644 scripts/build-free-ids.mjs create mode 100644 scripts/build-release.mjs create mode 100644 scripts/verify-release.mjs create mode 100644 src/Studio.test.tsx create mode 100644 src/lib/free.ts create mode 100644 src/registry/freeCount.ts create mode 100644 src/registry/freeIds.test.ts create mode 100644 src/registry/freeIds.ts diff --git a/.gitignore b/.gitignore index 81f7aa1..fe278b1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ public/r/ # 静的HTMLは生成物(npm run html で作る)。6.7MB あるので配信物には含めない。 public/html/ + +# BOOTH 配布用の ZIP と展開ディレクトリ(node scripts/build-release.mjs で作る) +release/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b1be03f --- /dev/null +++ b/LICENSE @@ -0,0 +1,133 @@ +LP Section Studio ライセンス + +本製品には 2 つの版があります。適用される条件は版ごとに異なります。 +自分がどちらを持っているかは、同梱の components.json の "edition" +(free / full)、または入手経路(無料 MCP か、BOOTH での購入か)で判断できます。 + + +================================================================ +【無料版】MCP サーバ経由で提供される 100 個 +================================================================ + +・個人利用・非商用の範囲で利用できます。 +・利用者が作った成果物の所有権は、利用者にあります。 +・コンポーネント自体をそのまま再配布・再販することはできません。 +・商用利用(受託制作、自社プロダクト、収益を伴うウェブサイトを含む) + には、買い切り版のライセンスが必要です。 + + +================================================================ +【買い切り版】880 個(BOOTH で購入したパッケージ) +================================================================ + +購入者は、本製品に含まれるコンポーネント(ソースコード・静的HTML・ +レジストリJSON を含む)について、以下の範囲で利用できます。 + +【できること】 +・商用・非商用を問わず、無制限の数のプロジェクトで利用すること +・自身または自社のために制作するウェブサイト・アプリケーションに + 組み込むこと +・受託制作において、クライアントに納品する成果物に組み込むこと +・コンポーネントを改変して利用すること + +【できないこと】 +・本製品そのもの、またはその一部を、コンポーネント集・テンプレート集 + として再配布・再販すること +・本製品を、これと競合する製品(UIコンポーネント集、UIキット等)の + 素材として利用すること +・購入していない第三者に、ソースコード一式を共有すること + +【購入者の範囲】 +1ライセンスにつき、個人1名または法人内の1チーム(5名まで)が +利用できます。 + + +================================================================ +【保証】(無料版・買い切り版の両方に適用) +================================================================ + +本製品は現状有姿で提供されます。特定目的への適合性を含め、明示・ +黙示を問わず、いかなる保証も行いません。本製品の利用により生じた +いかなる損害についても、作者は責任を負いません。 + + +================================================================ +【第三者ソフトウェア】 +================================================================ + +本製品のコンポーネントは react / lucide-react / clsx / tailwind-merge、 +および Tailwind CSS を前提としています。これらは本製品には含まれず、 +それぞれの配布元のライセンス(いずれも MIT)が適用されます。 + + +--- English --- + +LP Section Studio License + +This product comes in two editions. The applicable terms differ by edition. +You can tell which one you have from the "edition" field in the bundled +components.json (free / full), or from how you obtained it (the free MCP +server, or a purchase on BOOTH). + + +================================================================ +[Free edition] The 100 components served through the MCP server +================================================================ + +- May be used for personal, non-commercial purposes only. +- You own the work you create with it. +- You may not redistribute or resell the components themselves. +- Commercial use (including client work, in-house products, and any + revenue-generating website) requires a paid-edition license. + + +================================================================ +[Paid edition] The 880 components (the package purchased on BOOTH) +================================================================ + +The purchaser may use the components included in this product (including +source code, static HTML, and registry JSON) within the following scope. + +[You may] +- Use them in an unlimited number of projects, commercial or not +- Incorporate them into websites and applications you build for yourself + or for your own company +- Incorporate them into deliverables handed to clients in contract work +- Modify the components and use the modified versions + +[You may not] +- Redistribute or resell this product, or any part of it, as a component + collection or template collection +- Use this product as material for a competing product (a UI component + collection, UI kit, or similar) +- Share the full source with third parties who have not purchased it + +[Scope of the license] +One license covers one individual, or one team within a company +(up to 5 people). + + +================================================================ +[Warranty] (applies to both the free and paid editions) +================================================================ + +This product is provided "as is". No warranty of any kind, express or +implied, including fitness for a particular purpose, is given. The author +is not liable for any damages arising from the use of this product. + + +================================================================ +[Third-party software] +================================================================ + +The components assume react / lucide-react / clsx / tailwind-merge and +Tailwind CSS. These are not included in this product and remain subject to +their own licenses (all MIT). + + +注記 / Note: +本ライセンスは日本語版を正文とします。英訳は参考であり、日本語版との +間に相違がある場合は日本語版が優先します。 +The Japanese text is the governing version. The English translation is +provided for reference; in case of any discrepancy, the Japanese text +prevails. diff --git a/docs/dist-README.md b/docs/dist-README.md new file mode 100644 index 0000000..c3b0c5d --- /dev/null +++ b/docs/dist-README.md @@ -0,0 +1,213 @@ +# LP Section Studio + +検証済み UI コンポーネント **880個**。React / TypeScript / Tailwind CSS。 +うち **397個** は React 無しの静的 HTML としてそのまま使えます。 + +このファイルは購入者向けの導入手順です。ライセンスは同梱の `LICENSE` を参照してください。 + +--- + +## 入っているもの + +| ディレクトリ | 中身 | +| --- | --- | +| `components/` | React / TypeScript ソース **880件**(`demos/`)+ 動かすのに要る依存一式 | +| `html/` | 静的 HTML **880件** + `index.json`(どれが React 不要かの判定つき) | +| `registry/` | shadcn 互換の配信 JSON **885件** | +| `mcp/` | MCP サーバ(全部入り版・880件収録) | +| `studio/` | ビルド済みスタジオ(検索・プレビュー・コピー画面) | + +--- + +## 1. まず動かしてみる(1分・何もインストールしない) + +`html/` の中の HTML を1つ、ブラウザにドラッグして開いてください。 + +``` +html/offers-value-stack-offer.html +``` + +React も Node も要りません。Tailwind を CDN から1行読み込み、その下に +このコンポーネント集のデザイントークン(`bg-card` などの色定義)を +埋め込んであるので、ファイル単体で意図した見た目になります。 + +**ネット接続が要ります。** Tailwind を CDN から読むためです。オフラインで +使う場合や本番に載せる場合は、` + +`; +} + +/** の末尾に設定を差し込む。既に入っていれば何もしない。 */ +function injectHead(html, injection) { + if (html.includes("tailwind.config")) return html; + return html.replace(" ", `${injection} `); +} + +// ────────────────────────────────────────────────────────────────────────── +// コピーのヘルパー +// ────────────────────────────────────────────────────────────────────────── + +/** 顧客に渡してはいけないもの。ここに一致する名前は絶対にコピーしない。 */ +const NEVER_COPY = new Set([ + "node_modules", + ".git", + ".env", + ".env.local", + ".DS_Store", + ".claude", + "package-lock.json", +]); + +let copied = 0; + +function copyTree(from, to, { filter } = {}) { + mkdirSync(to, { recursive: true }); + for (const name of readdirSync(from)) { + if (NEVER_COPY.has(name) || name.endsWith(".tsbuildinfo") || name.endsWith(".log")) { + continue; + } + const src = join(from, name); + const dst = join(to, name); + if (statSync(src).isDirectory()) { + if (filter && !filter(src, true)) continue; + copyTree(src, dst, { filter }); + } else { + if (filter && !filter(src, false)) continue; + mkdirSync(dirname(dst), { recursive: true }); + copyFileSync(src, dst); + copied++; + } + } +} + +function copyOne(from, to) { + mkdirSync(dirname(to), { recursive: true }); + copyFileSync(resolve(ROOT, from), to); + copied++; +} + +const count = (dir, ext) => + existsSync(dir) ? readdirSync(dir).filter((f) => f.endsWith(ext)).length : 0; + +// ────────────────────────────────────────────────────────────────────────── +// 組み立て +// ────────────────────────────────────────────────────────────────────────── + +console.log(`${NAME} を組み立てます…\n`); +rmSync(STAGE, { recursive: true, force: true }); +mkdirSync(STAGE, { recursive: true }); + +const injection = await buildHeadInjection(); + +// --- README / LICENSE --- +copyOne("docs/dist-README.md", join(STAGE, "README.md")); +copyOne("LICENSE", join(STAGE, "LICENSE")); + +// --- components/ : 880件 + それが import する依存一式 --- +// demos は「@/components/ui/*」「@/lib/utils」「@/registry」を参照するので、 +// それらを同じ木に入れておかないと買った側でコンパイルが通らない。 +copyTree(resolve(ROOT, "src/registry/demos"), join(STAGE, "components/demos"), { + filter: (p, isDir) => isDir || p.endsWith(".tsx"), +}); +copyTree(resolve(ROOT, "src/components/ui"), join(STAGE, "components/ui")); +copyOne("src/lib/utils.ts", join(STAGE, "components/lib/utils.ts")); +copyOne("src/lib/i18n.ts", join(STAGE, "components/lib/i18n.ts")); +copyOne("src/registry/types.ts", join(STAGE, "components/registry/types.ts")); +copyOne("src/registry/i18n.ts", join(STAGE, "components/registry/i18n.ts")); +copyTree( + resolve(ROOT, "src/registry/i18n.parts"), + join(STAGE, "components/registry/i18n.parts") +); +// demos は `import type { DemoMeta } from "@/registry"` と書く。スタジオ本体の +// index.ts は import.meta.glob(Vite 専用)を使うので、型だけを再輸出する。 +writeFileSync( + join(STAGE, "components/registry/index.ts"), + `// demos が参照するのは型だけなので、型だけを再輸出する。\n` + + `export type { DemoMeta, RegistryEntry, ManifestEntry } from "./types";\n` +); +copied++; +// bg-card / text-muted-foreground などは Tailwind の標準クラスではない。 +// 買った側のプロジェクトでも同じ色を出せるよう、トークンの定義そのものを渡す。 +copyOne("tailwind.config.js", join(STAGE, "components/theme/tailwind.config.js")); +copyOne("src/index.css", join(STAGE, "components/theme/tokens.css")); + +const demoCount = readdirSync(join(STAGE, "components/demos"), { recursive: true }) + .filter((f) => String(f).endsWith(".tsx")) + .length; + +// --- html/ : 880件 + index.json(Tailwind 設定を注入したもの) --- +const htmlSrc = resolve(ROOT, "public/html"); +const htmlDst = join(STAGE, "html"); +mkdirSync(htmlDst, { recursive: true }); +let htmlCount = 0; +for (const f of readdirSync(htmlSrc)) { + if (f.endsWith(".html")) { + writeFileSync(join(htmlDst, f), injectHead(readFileSync(join(htmlSrc, f), "utf-8"), injection)); + htmlCount++; + } else if (f === "index.json") { + copyFileSync(join(htmlSrc, f), join(htmlDst, f)); + } +} +copied += htmlCount + 1; + +// --- registry/ : shadcn 互換 JSON --- +copyTree(resolve(ROOT, "public/r"), join(STAGE, "registry")); + +// --- mcp/ : 全部入り版 --- +// build-mcp-bundle.mjs の出力先は mcp/data/components.json 固定なので、 +// リポジトリの無料版データを退避 → --all で作る → 取り出す → 元に戻す。 +const freeData = resolve(ROOT, "mcp/data/components.json"); +const backup = existsSync(freeData) ? readFileSync(freeData) : null; +let fullBundle; +try { + execFileSync("node", ["scripts/build-mcp-bundle.mjs", "--all"], { + cwd: ROOT, + stdio: ["ignore", "pipe", "inherit"], + }); + fullBundle = JSON.parse(readFileSync(freeData, "utf-8")); +} finally { + if (backup) writeFileSync(freeData, backup); +} +if (fullBundle.edition !== "full") { + throw new Error(`MCP バンドルが full になっていません: ${fullBundle.edition}`); +} +// MCP が返す HTML も配布物と同じものにする +for (const item of fullBundle.items) { + if (item.html) item.html = injectHead(item.html, injection); +} +copyTree(resolve(ROOT, "mcp/src"), join(STAGE, "mcp/src")); +copyOne("mcp/package.json", join(STAGE, "mcp/package.json")); +copyOne("mcp/README.md", join(STAGE, "mcp/README.md")); +copyOne("LICENSE", join(STAGE, "mcp/LICENSE")); +mkdirSync(join(STAGE, "mcp/data"), { recursive: true }); +writeFileSync( + join(STAGE, "mcp/data/components.json"), + JSON.stringify(fullBundle, null, 0) + "\n" +); +copied++; + +// --- studio/ : ビルド済み --- +// Vite は public/ をそのまま dist/ にコピーするので、dist/r と dist/html は +// registry/ と html/ の丸ごと重複になる(合わせて 12MB)。しかも dist/html は +// Tailwind 設定を注入する前の版なので、残すと配布物の中で食い違う。 +// スタジオ本体はどちらも実行時に参照していないので外す。 +const DIST = resolve(ROOT, "dist"); +const DIST_DUPES = new Set(["r", "html"]); +copyTree(DIST, join(STAGE, "studio"), { + filter: (p) => !DIST_DUPES.has(relative(DIST, p)), +}); + +// ────────────────────────────────────────────────────────────────────────── +// ZIP 化 +// ────────────────────────────────────────────────────────────────────────── + +const zipPath = join(OUT_DIR, `${NAME}.zip`); +rmSync(zipPath, { force: true }); +execFileSync("zip", ["-r", "-q", "-X", `${NAME}.zip`, NAME], { cwd: OUT_DIR }); +if (!KEEP_STAGING) rmSync(STAGE, { recursive: true, force: true }); + +const mb = (n) => `${(n / 1024 / 1024).toFixed(1)} MB`; +console.log( + `\n${relative(ROOT, zipPath)} を作りました(${mb(statSync(zipPath).size)})\n` + + ` components/ ${demoCount} 件(.tsx)\n` + + ` html/ ${htmlCount} 件 + index.json\n` + + ` registry/ ${count(resolve(ROOT, "public/r"), ".json")} 件(.json)\n` + + ` mcp/ ${fullBundle.included} 件収録(edition: ${fullBundle.edition})\n` + + ` 合計 ${copied} ファイル\n\n` + + `検証: node scripts/verify-release.mjs` +); diff --git a/scripts/verify-release.mjs b/scripts/verify-release.mjs new file mode 100644 index 0000000..a3a5847 --- /dev/null +++ b/scripts/verify-release.mjs @@ -0,0 +1,337 @@ +/** + * 配布用 ZIP を実際に展開して中身を検める。 + * + * 出品前に「入っているつもり」で出さないための検査。数えるだけでなく、 + * 静的 HTML を実ブラウザで開いて**本当に描画されるか**まで見る。 + * + * node scripts/verify-release.mjs + * node scripts/verify-release.mjs --zip release/lp-section-studio-v1.0.0.zip + * node scripts/verify-release.mjs --samples 5 + * + * 見るもの: + * 1. 想定したディレクトリと件数が揃っているか + * 2. 顧客に渡してはいけないもの(node_modules/.git/.env 等)が入っていないか + * 3. README と LICENSE が入っていて、両方の版のライセンスが読み取れるか + * 4. 静的 HTML を無作為に数件、実ブラウザで開いて描画とスタイルを実測 + * 5. スタジオが HTTP 越しに開けるか + * + * 4 について: この HTML は cdn.tailwindcss.com を読む作りだが、検査環境から + * 外部 CDN には出られない。そこで CDN の代わりに、HTML が宣言している + * tailwind.config と同じ設定でローカルに組んだ Tailwind を差し込む。 + * トークンの値(--card など)は HTML 自身が持つ +``` + +値は `tailwind.config.js` と `src/index.css` から生成しています。手で書き写すと、いつか必ずズレるからです。 + +直した後、同じ3件をもう一度ブラウザで測りました。 -**目視は、この種の欠陥に対して無力です。** +``` +bg-card の背景色 rgba(0, 0, 0, 0) → rgb(255, 255, 255) +text-muted-foreground の文字色 rgb(0, 0, 0) → rgb(113, 113, 122) +bg-muted の背景色 rgba(0, 0, 0, 0) → rgb(244, 244, 245) +``` + +880個すべてで注入を確認。HTMLは1件あたり3.7KB増えました。 + +ついでに、**この欠陥を再発させない検査**を足しました。配布ZIPを作るスクリプトが、トークン設定の入っていないHTMLを1件でも見つけたら、そこで止まって「`npm run html` を実行しろ」と言って終了します。中身のコピーを持たせず、「入っているかどうか」だけを見る検査です。 -1.27:1 のコントラストも、一瞬だけ出る横スクロールバーも、捨てられている `aria-label` も、見て気づけるものではありません。私は2年間これらを見続けていて、1件も気づきませんでした。 +--- + +## 分かったこと -同時に、**検査を作るのは思ったより軽い作業でした。**4つ合わせて数百行です。難しいのは検査を書くことではなく、「検査していない」という状態に気づくことのほうでした。CIが緑だと、検査した気になります。 +**検査は、検査した経路しか守らない。** これに尽きます。 -もうひとつ。**検査は、売り文句の精度も上げました。** +4つの検査はどれも真面目に書いたもので、実際に67件を掘り出しました。でもそれは「Reactとして描画したとき」の話でした。同じコンポーネントが別の形(静的HTML)で出ていく経路があることを、検査の設計に入れ忘れていた。緑のCIは、その穴の存在を教えてくれません。むしろ「全部見た」という誤った安心をくれます。 -このコンポーネント集は「React を使わない現場でも使える」ことを特徴にしようとしていました。静的HTMLとして書き出せるからです。ところが全880件を実際に書き出して数えたら、**そのまま使えるのは397個**でした。残り483個は状態や操作を持つので、静的版は見た目だけになります。 +もうひとつ。**配る前に、配る形のまま自分で開いてみる。** 今回それをやったのは半分偶然でした。ZIPの中身を確認していて、なんとなくブラウザにドラッグしただけです。やらなければ、色の出ないコンポーネント430個をそのまま出荷していました。 -「880個すべてがReact不要」と書きたくなるところでした。測っていなければ、そう書いていたと思います。 +そして、この記事の書き出しに戻ります。109という数字も、検算せずに書いていました。**測ったつもり・数えたつもりが、いちばん危ない。** --- ## 事実確認 -この記事の数字は、以下のコマンドで再現できます。 +この記事の数字は、以下で再現できます。 ```bash -npm test # 描画・コントラスト・a11y・キーボード -npm run check:contrast # コントラスト(全組み合わせを表示) -npm run check:overflow # 横スクロール(実ブラウザ・幅375px) +node scripts/check-contrast.mjs # コントラスト → 0件 +npm test # axe / キーボード / 描画 +node scripts/check-overflow.mjs # 横スクロール → 0件 +node scripts/build-release.mjs # 配布物(トークン注入の検査を含む) +node scripts/verify-release.mjs # 展開して実ブラウザで描画確認 ``` ---- - -*このコンポーネント集は、無料のMCPサーバとして100個を配っています。全880個は買い切りです。(※リンクは公開時に差し替え)* +導入時点の違反数(12 / 29 / 0 / 26)は、当時の記録がコード内のコメントに残っています(`src/registry/a11y.test.tsx` と `scripts/check-overflow.mjs` の冒頭)。 diff --git a/mcp/data/components.json b/mcp/data/components.json index 2f21e62..10894d1 100644 --- a/mcp/data/components.json +++ b/mcp/data/components.json @@ -16,7 +16,7 @@ ], "standalone": true, "source": "import { ArrowRight, MessageSquare, Sparkles, Star } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アシスタント紹介カード\",\n category: \"AI / チャット\",\n description: \"AIアシスタントのプロフィールと得意分野を示すカード。\",\n align: \"center\",\n isNew: true,\n tags: [\"ai\", \"chat\", \"animation\"],\n};\n\nconst SKILLS = [\n { ja: \"コード生成\", en: \"Code generation\" },\n { ja: \"要約\", en: \"Summarization\" },\n { ja: \"翻訳\", en: \"Translation\" },\n { ja: \"データ分析\", en: \"Data analysis\" },\n];\n\nexport default function AssistantCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n \n\n
\n
\n
\n \n
\n \n
\n
\n

{en ? \"Assistant Pro\" : \"アシスタント Pro\"}

\n
\n \n 4.9\n {en ? \"· 12k chats\" : \"· 12k 会話\"}\n
\n
\n
\n\n

\n {en\n ? \"A versatile assistant that handles a wide range of tasks with high accuracy — from long-form reading to code generation.\"\n : \"長文の読解からコード生成まで、幅広いタスクを高精度でこなす汎用アシスタントです。\"}\n

\n\n
\n {SKILLS.map((s) => (\n \n {en ? s.en : s.ja}\n \n ))}\n
\n\n \n \n {en ? \"Start a chat\" : \"会話を始める\"}\n \n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n

\n アシスタント Pro\n

\n
\n \n \n \n \n \n 4.9\n \n \n · 12k 会話\n \n
\n
\n
\n

\n 長文の読解からコード生成まで、幅広いタスクを高精度でこなす汎用アシスタントです。\n

\n
\n \n コード生成\n \n \n 要約\n \n \n 翻訳\n \n \n データ分析\n \n
\n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n

\n アシスタント Pro\n

\n
\n \n \n \n \n \n 4.9\n \n \n · 12k 会話\n \n
\n
\n
\n

\n 長文の読解からコード生成まで、幅広いタスクを高精度でこなす汎用アシスタントです。\n

\n
\n \n コード生成\n \n \n 要約\n \n \n 翻訳\n \n \n データ分析\n \n
\n \n
\n
\n \n" }, { "id": "ai2-chat-bubble-variants", @@ -30,7 +30,7 @@ ], "standalone": true, "source": "import { AlertTriangle, Bot, Check, Info } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"チャットバブル種別\",\n category: \"AI / チャット\",\n description: \"ユーザー・アシスタント・通知・警告の各バブル見本。\",\n align: \"center\",\n isNew: true,\n tags: [\"ai\", \"chat\", \"animation\"],\n};\n\nexport default function ChatBubbleVariants() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* user */}\n
\n
\n YK\n
\n
\n {en ? \"How do I use this component?\" : \"このコンポーネントの使い方を教えて\"}\n
\n
\n\n {/* assistant */}\n
\n
\n \n
\n
\n {en\n ? \"These variants use different colors for each state. Let me walk you through the details next.\"\n : \"こちらのバリアントは状態ごとに配色を変えています。続けて詳細を説明しますね。\"}\n
\n
\n\n {/* system / notice */}\n
\n
\n \n {en ? \"Switched to a new model\" : \"新しいモデルに切り替わりました\"}\n
\n
\n\n {/* success inline */}\n \n\n {/* warning */}\n \n
\n );\n}\n\nfunction Notice({\n icon: Icon,\n tone,\n title,\n body,\n}: {\n icon: typeof Info;\n tone: \"emerald\" | \"amber\";\n title: string;\n body: string;\n}) {\n const tones = {\n emerald: \"border-emerald-500/30 bg-emerald-500/5 text-emerald-500\",\n amber: \"border-amber-500/30 bg-amber-500/5 text-amber-500\",\n };\n return (\n
\n \n
\n

{title}

\n

{body}

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n YK\n
\n
\n このコンポーネントの使い方を教えて\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n こちらのバリアントは状態ごとに配色を変えています。続けて詳細を説明しますね。\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n 新しいモデルに切り替わりました\n
\n
\n
\n \n \n \n \n
\n

\n 保存しました\n

\n

\n 会話の内容を履歴に保存しました。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n トークン上限に近づいています\n

\n

\n 長い会話は要約されることがあります。\n

\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n YK\n
\n
\n このコンポーネントの使い方を教えて\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n こちらのバリアントは状態ごとに配色を変えています。続けて詳細を説明しますね。\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n 新しいモデルに切り替わりました\n
\n
\n
\n \n \n \n \n
\n

\n 保存しました\n

\n

\n 会話の内容を履歴に保存しました。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n トークン上限に近づいています\n

\n

\n 長い会話は要約されることがあります。\n

\n
\n
\n
\n \n" }, { "id": "ai2-thinking-indicator", @@ -44,7 +44,7 @@ ], "standalone": true, "source": "import { Bot } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"思考中インジケーター\",\n category: \"AI / チャット\",\n description: \"3つの点が脈打つ「考え中…」のタイピング表示。\",\n align: \"center\",\n isNew: true,\n tags: [\"ai\", \"chat\", \"animation\"],\n};\n\nexport default function ThinkingIndicator() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n
\n \n
\n
\n {[0, 0.16, 0.32].map((d) => (\n \n ))}\n
\n
\n\n
\n
\n
\n \n \n \n \n {en ? \"Thinking about a reply…\" : \"回答を考えています…\"}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n 回答を考えています…\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n 回答を考えています…\n
\n
\n
\n \n" }, { "id": "analytics-analytics-overview", @@ -58,7 +58,7 @@ ], "standalone": true, "source": "import { ArrowDownRight, ArrowUpRight, DollarSign, Eye, Users } from \"lucide-react\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アナリティクス概要\",\n category: \"ダッシュボード\",\n description: \"3つのKPIタイルとグラデーション付きエリアチャートを備えた概要パネル。\",\n align: \"full\",\n isNew: true,\n tags: [\"analytics\", \"charts\", \"kpi\"],\n};\n\nconst KPIS = [\n {\n label: { ja: \"総収益\", en: \"Total revenue\" },\n value: \"¥4,820,400\",\n delta: 12.4,\n icon: DollarSign,\n up: true,\n },\n {\n label: { ja: \"アクティブユーザー\", en: \"Active users\" },\n value: \"38,294\",\n delta: 8.1,\n icon: Users,\n up: true,\n },\n {\n label: { ja: \"ページビュー\", en: \"Page views\" },\n value: \"1.24M\",\n delta: -2.6,\n icon: Eye,\n up: false,\n },\n];\n\n// 28日分のサンプルデータ(0-100)\nconst SERIES = [\n 32, 38, 30, 44, 41, 52, 48, 60, 55, 67, 62, 71, 69, 78, 74, 82, 79, 88, 84,\n 92, 86, 95, 90, 98, 93, 100, 96, 99,\n];\n\nfunction buildPaths(data: number[], width: number, height: number, pad: number) {\n const max = Math.max(...data);\n const min = Math.min(...data);\n const span = max - min || 1;\n const innerW = width - pad * 2;\n const innerH = height - pad * 2;\n const points = data.map((v, i) => {\n const x = pad + (i / (data.length - 1)) * innerW;\n const y = pad + innerH - ((v - min) / span) * innerH;\n return [x, y] as const;\n });\n const line = points\n .map(([x, y], i) => `${i === 0 ? \"M\" : \"L\"}${x.toFixed(2)},${y.toFixed(2)}`)\n .join(\" \");\n const area = `${line} L${(width - pad).toFixed(2)},${(height - pad).toFixed(\n 2\n )} L${pad.toFixed(2)},${(height - pad).toFixed(2)} Z`;\n return { line, area, points };\n}\n\nexport default function AnalyticsOverview() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const W = 760;\n const H = 240;\n const PAD = 12;\n const { line, area, points } = buildPaths(SERIES, W, H, PAD);\n const last = points[points.length - 1];\n\n return (\n \n \n
\n
\n

\n {en ? \"Performance overview\" : \"パフォーマンス概要\"}\n

\n

\n {en ? \"Last 28 days\" : \"過去28日間の推移\"}\n

\n
\n
\n \n \n {en ? \"Revenue\" : \"収益\"}\n \n \n \n {en ? \"Previous period\" : \"前期間\"}\n \n
\n
\n\n
\n {KPIS.map((kpi) => (\n \n
\n \n {en ? kpi.label.en : kpi.label.ja}\n \n \n
\n
\n {kpi.value}\n
\n \n {kpi.up ? (\n \n ) : (\n \n )}\n {kpi.up ? \"+\" : \"\"}\n {kpi.delta}%\n \n {en ? \"vs last month\" : \"先月比\"}\n \n
\n
\n ))}\n \n\n
\n \n \n \n \n \n \n \n {[0.25, 0.5, 0.75].map((g) => (\n \n ))}\n \n \n \n \n
\n \n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n

\n パフォーマンス概要\n

\n

\n 過去28日間の推移\n

\n
\n
\n \n \n \n 収益\n \n \n \n \n 前期間\n \n
\n
\n
\n
\n
\n \n 総収益\n \n \n \n \n \n \n \n
\n
\n ¥4,820,400\n
\n
\n \n \n \n \n \n \n +12.4%\n \n 先月比\n \n
\n
\n
\n
\n \n アクティブユーザー\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 38,294\n
\n
\n \n \n \n \n \n \n +8.1%\n \n 先月比\n \n
\n
\n
\n
\n \n ページビュー\n \n \n \n \n \n \n \n
\n
\n 1.24M\n
\n
\n \n \n \n \n \n \n -2.6%\n \n 先月比\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n

\n パフォーマンス概要\n

\n

\n 過去28日間の推移\n

\n
\n
\n \n \n \n 収益\n \n \n \n \n 前期間\n \n
\n
\n
\n
\n
\n \n 総収益\n \n \n \n \n \n \n \n
\n
\n ¥4,820,400\n
\n
\n \n \n \n \n \n \n +12.4%\n \n 先月比\n \n
\n
\n
\n
\n \n アクティブユーザー\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 38,294\n
\n
\n \n \n \n \n \n \n +8.1%\n \n 先月比\n \n
\n
\n
\n
\n \n ページビュー\n \n \n \n \n \n \n \n
\n
\n 1.24M\n
\n
\n \n \n \n \n \n \n -2.6%\n \n 先月比\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n" }, { "id": "analytics-contribution-heatmap", @@ -72,7 +72,7 @@ ], "standalone": true, "source": "import { Card, CardContent } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"コントリビューションヒートマップ\",\n category: \"ダッシュボード\",\n description: \"週×曜日のGitHub風コントリビューショングリッド。強度バケットと凡例付き。\",\n align: \"full\",\n isNew: true,\n tags: [\"analytics\", \"heatmap\", \"activity\"],\n};\n\nconst WEEKS = 26;\nconst DAYS = 7;\nconst DAY_LABELS_JA = [\"月\", \"\", \"水\", \"\", \"金\", \"\", \"日\"];\nconst DAY_LABELS_EN = [\"Mon\", \"\", \"Wed\", \"\", \"Fri\", \"\", \"Sun\"];\n\n// 決定的な擬似乱数で強度バケット(0-4)を生成\nfunction bucket(week: number, day: number): number {\n const seed = (week * 7 + day) * 9301 + 49297;\n const r = (seed % 233280) / 233280;\n const weekend = day >= 5 ? 0.5 : 1;\n const v = r * weekend;\n if (v < 0.45) return 0;\n if (v < 0.62) return 1;\n if (v < 0.78) return 2;\n if (v < 0.9) return 3;\n return 4;\n}\n\nconst LEVELS = [\n \"bg-muted\",\n \"bg-emerald-500/25\",\n \"bg-emerald-500/50\",\n \"bg-emerald-500/75\",\n \"bg-emerald-500\",\n];\n\nconst MONTH_LABELS_JA = [\"1月\", \"\", \"2月\", \"\", \"3月\", \"\", \"4月\", \"\", \"5月\", \"\", \"6月\", \"\", \"7月\"];\nconst MONTH_LABELS_EN = [\"Jan\", \"\", \"Feb\", \"\", \"Mar\", \"\", \"Apr\", \"\", \"May\", \"\", \"Jun\", \"\", \"Jul\"];\n\nexport default function ContributionHeatmap() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const dayLabels = en ? DAY_LABELS_EN : DAY_LABELS_JA;\n const monthLabels = en ? MONTH_LABELS_EN : MONTH_LABELS_JA;\n const grid = Array.from({ length: WEEKS }, (_, w) =>\n Array.from({ length: DAYS }, (_, d) => bucket(w, d))\n );\n const total = grid.flat().reduce((a, b) => a + b * 3, 0);\n\n return (\n \n \n
\n
\n

\n {en ? \"Activity\" : \"アクティビティ\"}\n

\n

\n {en ? (\n <>\n {total.toLocaleString(\"en-US\")} contributions in the last 6\n months\n \n ) : (\n <>過去6ヶ月で {total.toLocaleString(\"ja-JP\")} 件のコントリビューション\n )}\n

\n
\n
\n\n
\n
\n
\n {dayLabels.map((l, i) => (\n \n {l}\n \n ))}\n
\n\n
\n
\n {grid.map((_, w) => (\n \n {w < monthLabels.length ? monthLabels[w] : \"\"}\n \n ))}\n
\n
\n {grid.map((week, w) => (\n
\n {week.map((lvl, d) => (\n \n ))}\n
\n ))}\n
\n
\n
\n
\n\n
\n {en ? \"Less\" : \"少\"}\n {LEVELS.map((c, i) => (\n \n ))}\n {en ? \"More\" : \"多\"}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n

\n アクティビティ\n

\n

\n 過去6ヶ月で 486 件のコントリビューション\n

\n
\n
\n
\n
\n
\n \n 月\n \n \n \n \n 水\n \n \n \n \n 金\n \n \n \n \n 日\n \n
\n
\n
\n \n 1月\n \n \n \n \n 2月\n \n \n \n \n 3月\n \n \n \n \n 4月\n \n \n \n \n 5月\n \n \n \n \n 6月\n \n \n \n \n 7月\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n 少\n \n \n \n \n \n \n \n \n \n \n \n \n 多\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n

\n アクティビティ\n

\n

\n 過去6ヶ月で 486 件のコントリビューション\n

\n
\n
\n
\n
\n
\n \n 月\n \n \n \n \n 水\n \n \n \n \n 金\n \n \n \n \n 日\n \n
\n
\n
\n \n 1月\n \n \n \n \n 2月\n \n \n \n \n 3月\n \n \n \n \n 4月\n \n \n \n \n 5月\n \n \n \n \n 6月\n \n \n \n \n 7月\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n 少\n \n \n \n \n \n \n \n \n \n \n \n \n 多\n \n
\n
\n
\n \n" }, { "id": "analytics-funnel-chart", @@ -86,7 +86,7 @@ ], "standalone": true, "source": "import { Card, CardContent } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"コンバージョンファネル\",\n category: \"ダッシュボード\",\n description: \"各ステージの割合と離脱率を表示する降順のコンバージョンファネル。\",\n align: \"full\",\n isNew: true,\n tags: [\"analytics\", \"funnel\", \"conversion\"],\n};\n\nconst STAGES = [\n { label: \"訪問\", labelEn: \"Visits\", value: 48200 },\n { label: \"サインアップ\", labelEn: \"Sign-ups\", value: 21400 },\n { label: \"トライアル開始\", labelEn: \"Trial started\", value: 12850 },\n { label: \"アクティブ化\", labelEn: \"Activated\", value: 7320 },\n { label: \"有料転換\", labelEn: \"Converted\", value: 3180 },\n];\n\nconst ACCENTS = [\n \"bg-sky-500\",\n \"bg-indigo-500\",\n \"bg-violet-500\",\n \"bg-fuchsia-500\",\n \"bg-rose-500\",\n];\n\nexport default function FunnelChart() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const top = STAGES[0].value;\n\n return (\n \n \n
\n
\n

\n {en ? \"Conversion funnel\" : \"コンバージョンファネル\"}\n

\n

\n {en ? \"Overall conversion \" : \"全体転換率\"}{\" \"}\n \n {((STAGES[STAGES.length - 1].value / top) * 100).toFixed(1)}%\n \n

\n
\n
\n\n
\n {STAGES.map((stage, i) => {\n const pct = (stage.value / top) * 100;\n const prev = i === 0 ? stage.value : STAGES[i - 1].value;\n const dropoff = i === 0 ? 0 : ((prev - stage.value) / prev) * 100;\n return (\n
\n
\n \n {en ? stage.labelEn : stage.label}\n \n \n \n {stage.value.toLocaleString(en ? \"en-US\" : \"ja-JP\")}\n \n \n {pct.toFixed(1)}%\n \n \n
\n
\n \n {i > 0 && (\n \n −{dropoff.toFixed(1)}% {en ? \"drop-off\" : \"離脱\"}\n \n )}\n
\n
\n );\n })}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n

\n コンバージョンファネル\n

\n

\n 全体転換率\n \n 6.6%\n \n

\n
\n
\n
\n
\n
\n \n 訪問\n \n \n \n 48,200\n \n \n 100.0%\n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n サインアップ\n \n \n \n 21,400\n \n \n 44.4%\n \n \n
\n
\n
\n
\n \n −55.6% 離脱\n \n
\n
\n
\n
\n \n トライアル開始\n \n \n \n 12,850\n \n \n 26.7%\n \n \n
\n
\n
\n
\n \n −40.0% 離脱\n \n
\n
\n
\n
\n \n アクティブ化\n \n \n \n 7,320\n \n \n 15.2%\n \n \n
\n
\n
\n
\n \n −43.0% 離脱\n \n
\n
\n
\n
\n \n 有料転換\n \n \n \n 3,180\n \n \n 6.6%\n \n \n
\n
\n
\n
\n \n −56.6% 離脱\n \n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n

\n コンバージョンファネル\n

\n

\n 全体転換率\n \n 6.6%\n \n

\n
\n
\n
\n
\n
\n \n 訪問\n \n \n \n 48,200\n \n \n 100.0%\n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n サインアップ\n \n \n \n 21,400\n \n \n 44.4%\n \n \n
\n
\n
\n
\n \n −55.6% 離脱\n \n
\n
\n
\n
\n \n トライアル開始\n \n \n \n 12,850\n \n \n 26.7%\n \n \n
\n
\n
\n
\n \n −40.0% 離脱\n \n
\n
\n
\n
\n \n アクティブ化\n \n \n \n 7,320\n \n \n 15.2%\n \n \n
\n
\n
\n
\n \n −43.0% 離脱\n \n
\n
\n
\n
\n \n 有料転換\n \n \n \n 3,180\n \n \n 6.6%\n \n \n
\n
\n
\n
\n \n −56.6% 離脱\n \n
\n
\n
\n
\n
\n \n" }, { "id": "analytics-leaderboard", @@ -100,7 +100,7 @@ ], "standalone": true, "source": "import { Crown, Minus, TrendingDown, TrendingUp } from \"lucide-react\";\nimport { Card, CardContent } from \"@/components/ui/card\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"リーダーボード\",\n category: \"ダッシュボード\",\n description: \"順位・アバター・スコア・変動を表示し、上位3名を強調するランキング。\",\n align: \"center\",\n isNew: true,\n tags: [\"analytics\", \"leaderboard\", \"ranking\"],\n};\n\ntype Entry = {\n name: string;\n score: number;\n move: number; // 順位の変動 (+上昇 / -下降 / 0)\n};\n\nconst ENTRIES: Entry[] = [\n { name: \"佐藤 美咲\", score: 9824, move: 0 },\n { name: \"Liam Carter\", score: 9410, move: 2 },\n { name: \"田中 翔太\", score: 9187, move: -1 },\n { name: \"Aisha Khan\", score: 8640, move: 1 },\n { name: \"鈴木 健\", score: 8021, move: -2 },\n { name: \"Noah Kim\", score: 7755, move: 0 },\n];\n\nfunction initials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length === 1) return parts[0].slice(0, 2);\n return parts[0][0] + parts[1][0];\n}\n\nconst TOP_RING = [\n \"ring-amber-400/70 text-amber-500\",\n \"ring-slate-400/60 text-slate-400\",\n \"ring-orange-500/60 text-orange-500\",\n];\n\nexport default function Leaderboard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n \n \n
\n

\n {en ? \"Leaderboard\" : \"リーダーボード\"}\n

\n \n {en ? \"This week\" : \"今週\"}\n \n
\n\n
    \n {ENTRIES.map((e, i) => {\n const rank = i + 1;\n const isTop = rank <= 3;\n return (\n \n \n {rank}\n \n\n
    \n \n {initials(e.name)}\n
    \n {rank === 1 && (\n \n )}\n \n\n \n {e.name}\n \n\n \n {e.score.toLocaleString(en ? \"en-US\" : \"ja-JP\")}\n \n\n 0 && \"text-emerald-500\",\n e.move < 0 && \"text-rose-500\",\n e.move === 0 && \"text-muted-foreground\"\n )}\n >\n {e.move > 0 && }\n {e.move < 0 && }\n {e.move === 0 && }\n {e.move !== 0 && Math.abs(e.move)}\n \n \n );\n })}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n

\n リーダーボード\n

\n \n 今週\n \n
\n
    \n
  1. \n \n 1\n \n
    \n
    \n 佐美\n
    \n \n \n \n \n \n \n
    \n \n 佐藤 美咲\n \n \n 9,824\n \n \n \n \n \n \n \n
  2. \n
  3. \n \n 2\n \n
    \n
    \n LC\n
    \n
    \n \n Liam Carter\n \n \n 9,410\n \n \n \n \n \n \n \n \n 2\n \n
  4. \n
  5. \n \n 3\n \n
    \n
    \n 田翔\n
    \n
    \n \n 田中 翔太\n \n \n 9,187\n \n \n \n \n \n \n \n \n 1\n \n
  6. \n
  7. \n \n 4\n \n
    \n
    \n AK\n
    \n
    \n \n Aisha Khan\n \n \n 8,640\n \n \n \n \n \n \n \n \n 1\n \n
  8. \n
  9. \n \n 5\n \n
    \n
    \n 鈴健\n
    \n
    \n \n 鈴木 健\n \n \n 8,021\n \n \n \n \n \n \n \n \n 2\n \n
  10. \n
  11. \n \n 6\n \n
    \n
    \n NK\n
    \n
    \n \n Noah Kim\n \n \n 7,755\n \n \n \n \n \n \n \n
  12. \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n

\n リーダーボード\n

\n \n 今週\n \n
\n
    \n
  1. \n \n 1\n \n
    \n
    \n 佐美\n
    \n \n \n \n \n \n \n
    \n \n 佐藤 美咲\n \n \n 9,824\n \n \n \n \n \n \n \n
  2. \n
  3. \n \n 2\n \n
    \n
    \n LC\n
    \n
    \n \n Liam Carter\n \n \n 9,410\n \n \n \n \n \n \n \n \n 2\n \n
  4. \n
  5. \n \n 3\n \n
    \n
    \n 田翔\n
    \n
    \n \n 田中 翔太\n \n \n 9,187\n \n \n \n \n \n \n \n \n 1\n \n
  6. \n
  7. \n \n 4\n \n
    \n
    \n AK\n
    \n
    \n \n Aisha Khan\n \n \n 8,640\n \n \n \n \n \n \n \n \n 1\n \n
  8. \n
  9. \n \n 5\n \n
    \n
    \n 鈴健\n
    \n
    \n \n 鈴木 健\n \n \n 8,021\n \n \n \n \n \n \n \n \n 2\n \n
  10. \n
  11. \n \n 6\n \n
    \n
    \n NK\n
    \n
    \n \n Noah Kim\n \n \n 7,755\n \n \n \n \n \n \n \n
  12. \n
\n
\n
\n \n" }, { "id": "app-avatar-group", @@ -110,7 +110,7 @@ "tags": [], "standalone": true, "source": "import { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アバターグループ\",\n category: \"アプリUI\",\n description: \"重なり合うアバタースタックと「+N」オーバーフロー、オンライン状態ドット、サイズ違い。\",\n align: \"center\",\n};\n\ntype Person = {\n ja: string;\n en: string;\n color: string;\n online?: boolean;\n};\n\nconst PEOPLE: Person[] = [\n { ja: \"佐藤 太郎\", en: \"Taro Sato\", color: \"from-rose-500 to-orange-500\", online: true },\n { ja: \"Maya Lin\", en: \"Maya Lin\", color: \"from-violet-500 to-indigo-500\", online: true },\n { ja: \"Kenji Ito\", en: \"Kenji Ito\", color: \"from-emerald-500 to-teal-500\" },\n { ja: \"Ava Cole\", en: \"Ava Cole\", color: \"from-sky-500 to-blue-500\", online: true },\n { ja: \"Liam Ono\", en: \"Liam Ono\", color: \"from-amber-500 to-yellow-500\" },\n];\n\nfunction initials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length === 1) return parts[0].slice(0, 2);\n return (parts[0][0] ?? \"\") + (parts[parts.length - 1][0] ?? \"\");\n}\n\nconst SIZES = {\n sm: { wrap: \"h-7 w-7 text-[10px]\", dot: \"h-2 w-2\", ring: \"ring-2\" },\n md: { wrap: \"h-9 w-9 text-xs\", dot: \"h-2.5 w-2.5\", ring: \"ring-2\" },\n lg: { wrap: \"h-12 w-12 text-sm\", dot: \"h-3 w-3\", ring: \"ring-[3px]\" },\n} as const;\n\ntype SizeKey = keyof typeof SIZES;\n\nfunction Avatar({\n person,\n size = \"md\",\n showStatus = true,\n}: {\n person: Person;\n size?: SizeKey;\n showStatus?: boolean;\n}) {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const s = SIZES[size];\n const name = en ? person.en : person.ja;\n return (\n \n \n {initials(name)}\n \n {showStatus && person.online && (\n \n )}\n \n );\n}\n\nexport default function AvatarGroup() {\n const visible = PEOPLE.slice(0, 4);\n const overflow = PEOPLE.length - visible.length + 2;\n\n return (\n
\n {/* 重なりスタック + オーバーフロー */}\n
\n {visible.map((p) => (\n \n ))}\n \n +{overflow}\n \n
\n\n {/* サイズバリアント */}\n
\n \n \n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n \n \n 佐太\n \n \n \n \n \n \n ML\n \n \n \n \n \n \n KI\n \n \n \n \n AC\n \n \n \n \n \n +3\n \n
\n
\n \n \n ML\n \n \n \n \n \n \n 佐太\n \n \n \n \n \n \n AC\n \n \n \n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n 佐太\n \n \n \n \n \n \n ML\n \n \n \n \n \n \n KI\n \n \n \n \n AC\n \n \n \n \n \n +3\n \n
\n
\n \n \n ML\n \n \n \n \n \n \n 佐太\n \n \n \n \n \n \n AC\n \n \n \n \n
\n
\n \n" }, { "id": "app-empty-state", @@ -120,7 +120,7 @@ "tags": [], "standalone": true, "source": "import { FolderOpen, Plus, Upload } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"空の状態\",\n category: \"アプリUI\",\n description: \"ソフトな円のアイコン・見出し・説明・主要/副次アクションを備えた空状態。\",\n align: \"center\",\n};\n\nexport default function EmptyState() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n
\n \n
\n
\n\n

\n {en ? \"No projects yet\" : \"プロジェクトがありません\"}\n

\n

\n {en\n ? \"Create your first project to start working with your team. You can also import from a template.\"\n : \"最初のプロジェクトを作成して、チームでの作業を始めましょう。テンプレートからのインポートも可能です。\"}\n

\n\n
\n \n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n
\n
\n

\n プロジェクトがありません\n

\n

\n 最初のプロジェクトを作成して、チームでの作業を始めましょう。テンプレートからのインポートも可能です。\n

\n
\n \n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n
\n
\n

\n プロジェクトがありません\n

\n

\n 最初のプロジェクトを作成して、チームでの作業を始めましょう。テンプレートからのインポートも可能です。\n

\n
\n \n \n
\n
\n \n" }, { "id": "aww-hero-asymmetric-hero", @@ -134,7 +134,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { ArrowDownRight } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"非対称グリッドヒーロー\",\n category: \"Awwwards\",\n description:\n \"崩したグリッドと余白の偏りで緊張感を作る、非対称レイアウトのエディトリアルヒーロー。\",\n align: \"full\",\n isNew: true,\n tags: [\"awwwards\", \"hero\", \"animation\"],\n};\n\nexport default function AsymmetricHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n
\n \n Editorial 26\n \n \n {en ? (\n <>\n Space is\n the\n language of silence.\n \n ) : (\n <>\n 余白は\n 沈黙\n の言語。\n \n )}\n \n
\n\n \n

\n {en\n ? \"Deliberately breaking an orderly grid. Creating steps in the flow of the eye gives the screen breath and rhythm.\"\n : \"整然としたグリッドをあえて崩す。視線の流れに段差を作ることで、画面に呼吸とリズムが生まれる。\"}\n

\n \n
\n\n \n \n
\n ’26\n
\n
\n \n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n \n Editorial 26\n \n

\n 余白は\n \n 沈黙\n \n \n の言語。\n \n

\n
\n
\n

\n 整然としたグリッドをあえて崩す。視線の流れに段差を作ることで、画面に呼吸とリズムが生まれる。\n

\n \n
\n
\n
\n
\n
\n ’26\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n Editorial 26\n \n

\n 余白は\n \n 沈黙\n \n \n の言語。\n \n

\n
\n
\n

\n 整然としたグリッドをあえて崩す。視線の流れに段差を作ることで、画面に呼吸とリズムが生まれる。\n

\n \n
\n
\n
\n
\n
\n ’26\n
\n
\n
\n
\n \n" }, { "id": "aww-hero-award-badge-showcase", @@ -148,7 +148,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Award, Star } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"受賞バッジショーケース\",\n category: \"Awwwards\",\n description:\n \"回転する受賞バッジと実績指標を主役にした、アワード受賞風のフルブリードショーケース。\",\n align: \"full\",\n isNew: true,\n tags: [\"awwwards\", \"hero\", \"animation\"],\n};\n\nconst BADGES = [\n { label: \"Site of the Day\", y: \"FEB 2026\", ac: \"#fbbf24\" },\n { label: \"Developer Award\", y: \"JAN 2026\", ac: \"#34d399\" },\n { label: \"Honorable Mention\", y: \"DEC 2025\", ac: \"#f472b6\" },\n];\n\nfunction SpinBadge({ color }: { color: string }) {\n const text = \"★ AWARD WINNING · CRAFTED WITH CARE · \";\n return (\n
\n \n \n \n \n \n {text + text}\n \n \n \n \n \n
\n );\n}\n\nexport default function AwardBadgeShowcase() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n\n
\n
\n \n Recognition\n \n \n {en ? (\n <>\n An obsession\n
\n with detail.\n \n ) : (\n <>\n 称賛される、\n
\n 細部への執着。\n \n )}\n \n
\n\n
\n {BADGES.map((b, i) => (\n \n \n
\n
{b.label}
\n
\n {b.y}\n
\n
\n
\n ))}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n \n \n \n \n \n Recognition\n \n

\n 称賛される、\n
\n 細部への執着。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n ★ AWARD WINNING · CRAFTED WITH CARE · ★ AWARD WINNING · CRAFTED WITH CARE ·\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n Site of the Day\n
\n
\n FEB 2026\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n ★ AWARD WINNING · CRAFTED WITH CARE · ★ AWARD WINNING · CRAFTED WITH CARE ·\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n Developer Award\n
\n
\n JAN 2026\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n ★ AWARD WINNING · CRAFTED WITH CARE · ★ AWARD WINNING · CRAFTED WITH CARE ·\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n Honorable Mention\n
\n
\n DEC 2025\n
\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n \n \n \n \n \n Recognition\n \n

\n 称賛される、\n
\n 細部への執着。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n ★ AWARD WINNING · CRAFTED WITH CARE · ★ AWARD WINNING · CRAFTED WITH CARE ·\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n Site of the Day\n
\n
\n FEB 2026\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n ★ AWARD WINNING · CRAFTED WITH CARE · ★ AWARD WINNING · CRAFTED WITH CARE ·\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n Developer Award\n
\n
\n JAN 2026\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n ★ AWARD WINNING · CRAFTED WITH CARE · ★ AWARD WINNING · CRAFTED WITH CARE ·\n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n Honorable Mention\n
\n
\n DEC 2025\n
\n
\n
\n
\n
\n
\n \n" }, { "id": "aww-hero-bento-editorial", @@ -162,7 +162,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { ArrowUpRight, Sparkles, Zap, Globe } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"ベントーエディトリアル\",\n category: \"Awwwards\",\n description:\n \"大小のセルを組み合わせたベントーグリッドで、ヒーローとコンテンツを一画面に圧縮した構成。\",\n align: \"full\",\n isNew: true,\n tags: [\"awwwards\", \"hero\", \"animation\"],\n};\n\nexport default function BentoEditorial() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n {/* hero cell */}\n \n
\n \n Bento Studio\n \n \n {en ? (\n <>\n Say it all\n
\n on one screen.\n \n ) : (\n <>\n 一画面で\n
\n 語り尽くす。\n \n )}\n \n
\n\n
\n \n
\n
260+
\n
Components
\n
\n
\n\n
\n \n
\n
0ms
\n
External calls
\n
\n
\n\n
\n
\n
\n Worldwide ready\n
\n

\n {en\n ? \"Responsive, with dark and light support. It fits in anywhere you place it.\"\n : \"レスポンシブとダーク/ライト両対応。どこに置いても馴染む。\"}\n

\n
\n \n \n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n \n Bento Studio\n \n

\n 一画面で\n
\n 語り尽くす。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 260+\n
\n
\n Components\n
\n
\n
\n
\n \n \n \n \n
\n
\n 0ms\n
\n
\n External calls\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n Worldwide ready\n
\n

\n レスポンシブとダーク/ライト両対応。どこに置いても馴染む。\n

\n
\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n \n Bento Studio\n \n

\n 一画面で\n
\n 語り尽くす。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 260+\n
\n
\n Components\n
\n
\n
\n
\n \n \n \n \n
\n
\n 0ms\n
\n
\n External calls\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n Worldwide ready\n
\n

\n レスポンシブとダーク/ライト両対応。どこに置いても馴染む。\n

\n
\n \n
\n
\n
\n \n" }, { "id": "aww-hero-clip-path-reveal-hero", @@ -176,7 +176,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"クリップパス展開ヒーロー\",\n category: \"Awwwards\",\n description:\n \"clip-pathの多角形マスクが展開しながら現れる、幾何学的でドラマチックなヒーロー。\",\n align: \"full\",\n isNew: true,\n tags: [\"awwwards\", \"hero\", \"animation\"],\n};\n\nexport default function ClipPathRevealHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n
\n
\n
\n
\n\n
\n

\n clip-path / Reveal\n

\n \n Cut\n
\n The Frame\n \n

\n {en\n ? \"A polygonal mask cuts diagonally open to reveal the background — a transition where the shape itself becomes the motion.\"\n : \"多角形のマスクが斜めに切り開きながら背景を露わにする。形そのものが動きになるトランジション。\"}\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n

\n clip-path / Reveal\n

\n

\n Cut\n
\n The Frame\n

\n

\n 多角形のマスクが斜めに切り開きながら背景を露わにする。形そのものが動きになるトランジション。\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n

\n clip-path / Reveal\n

\n

\n Cut\n
\n The Frame\n

\n

\n 多角形のマスクが斜めに切り開きながら背景を露わにする。形そのものが動きになるトランジション。\n

\n
\n
\n \n" }, { "id": "bg-anim-animated-gradient", @@ -190,7 +190,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アニメグラデ背景\",\n category: \"背景アニメ\",\n description: \"複数色のグラデーションが滑らかに移ろい続けるヒーロー背景。\",\n align: \"full\",\n isNew: true,\n tags: [\"background\", \"animation\", \"gradient\"],\n};\n\nexport default function AnimatedGradient() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n
\n \n Animated Gradient\n \n

\n {en\n ? \"A smooth gradient that breathes with color\"\n : \"色が呼吸する、滑らかなグラデーション\"}\n

\n

\n {en\n ? \"Keyframed background-position shifts the hue seamlessly, with no visible seams.\"\n : \"background-position をキーフレームで動かし、継ぎ目なく色相が移ろいます。\"}\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n \n Animated Gradient\n \n

\n 色が呼吸する、滑らかなグラデーション\n

\n

\n background-position をキーフレームで動かし、継ぎ目なく色相が移ろいます。\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n \n Animated Gradient\n \n

\n 色が呼吸する、滑らかなグラデーション\n

\n

\n background-position をキーフレームで動かし、継ぎ目なく色相が移ろいます。\n

\n
\n
\n \n" }, { "id": "bg-anim-aurora", @@ -204,7 +204,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"オーロラ背景\",\n category: \"背景アニメ\",\n description: \"北極光のようなグラデーションの光が背後でゆっくり漂うヒーロー背景。\",\n align: \"full\",\n isNew: true,\n tags: [\"background\", \"animation\", \"aurora\"],\n};\n\nexport default function Aurora() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n \n \n \n
\n
\n
\n \n Aurora Background\n \n

\n {en\n ? \"A night-sky backdrop with shimmering light\"\n : \"光がゆらめく、夜空のような背景\"}\n

\n

\n {en\n ? \"Gradients drifting like the aurora, moving quietly behind your content.\"\n : \"オーロラのように漂うグラデーション。コンテンツの背後で静かに動き続けます。\"}\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Aurora Background\n \n

\n 光がゆらめく、夜空のような背景\n

\n

\n オーロラのように漂うグラデーション。コンテンツの背後で静かに動き続けます。\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Aurora Background\n \n

\n 光がゆらめく、夜空のような背景\n

\n

\n オーロラのように漂うグラデーション。コンテンツの背後で静かに動き続けます。\n

\n
\n
\n \n" }, { "id": "bg-anim-conic-glow", @@ -219,7 +219,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"コニックグロー背景\",\n category: \"背景アニメ\",\n description: \"回転するコニックグラデーションの光輪が中央カードを照らす背景。\",\n align: \"full\",\n isNew: true,\n tags: [\"background\", \"animation\", \"conic\", \"glow\"],\n};\n\nexport default function ConicGlow() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n \n
\n
\n
\n
\n \n Conic Glow\n \n

\n {en\n ? \"A card wrapped in a spinning halo of light\"\n : \"回る光輪に包まれるカード\"}\n

\n

\n {en\n ? \"A conic gradient rotates, softly illuminating the card at the center.\"\n : \"コニックグラデーションが回転し、中央のカードを柔らかく照らします。\"}\n

\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n \n Conic Glow\n \n

\n 回る光輪に包まれるカード\n

\n

\n コニックグラデーションが回転し、中央のカードを柔らかく照らします。\n

\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n \n Conic Glow\n \n

\n 回る光輪に包まれるカード\n

\n

\n コニックグラデーションが回転し、中央のカードを柔らかく照らします。\n

\n
\n
\n
\n \n" }, { "id": "bg-anim-grid-beams", @@ -234,7 +234,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グリッドビーム背景\",\n category: \"背景アニメ\",\n description: \"グリッド上を光のビームが横切るテック系ヒーロー背景。\",\n align: \"full\",\n isNew: true,\n tags: [\"background\", \"animation\", \"grid\", \"beams\"],\n};\n\nexport default function GridBeams() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const beams = [\n { top: \"20%\", delay: \"0s\", duration: \"6s\", color: \"rgba(56,189,248,0.9)\" },\n { top: \"45%\", delay: \"2s\", duration: \"8s\", color: \"rgba(168,85,247,0.9)\" },\n { top: \"70%\", delay: \"4s\", duration: \"7s\", color: \"rgba(52,211,153,0.9)\" },\n { top: \"88%\", delay: \"1.5s\", duration: \"9s\", color: \"rgba(244,114,182,0.9)\" },\n ];\n return (\n
\n \n \n
\n {beams.map((b) => (\n \n ))}\n
\n
\n \n Grid Beams\n \n

\n {en\n ? \"Beams of light racing across the grid\"\n : \"グリッドを駆け抜ける光のビーム\"}\n

\n

\n {en\n ? \"Glowing beams sweep across the grid at a steady rhythm.\"\n : \"発光するビームがグリッド上を一定リズムで横切ります。\"}\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Grid Beams\n \n

\n グリッドを駆け抜ける光のビーム\n

\n

\n 発光するビームがグリッド上を一定リズムで横切ります。\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Grid Beams\n \n

\n グリッドを駆け抜ける光のビーム\n

\n

\n 発光するビームがグリッド上を一定リズムで横切ります。\n

\n
\n
\n \n" }, { "id": "botanical-botanical-feature", @@ -248,7 +248,7 @@ ], "standalone": true, "source": "import { Droplets, Recycle, ShieldCheck, Sprout } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ボタニカル・フィーチャー\",\n category: \"ボタニカル\",\n description: \"植物のラインアートを背景にした特徴・ベネフィット紹介セクション。\",\n align: \"full\",\n isNew: true,\n tags: [\"botanical\", \"organic\", \"wellness\"],\n};\n\nfunction BranchLine({ className }: { className?: string }) {\n return (\n \n \n {[60, 140, 220, 300].map((x, i) => (\n \n \n \n \n ))}\n \n );\n}\n\nconst features = [\n {\n icon: Sprout,\n titleJa: \"畑から処方へ\",\n titleEn: \"From field to formula\",\n descJa: \"契約農家で育てた植物を、収穫から72時間以内に抽出。鮮度の高い有効成分を届けます。\",\n descEn: \"Plants grown on partner farms are extracted within 72 hours of harvest, delivering fresh, active ingredients.\",\n },\n {\n icon: Droplets,\n titleJa: \"やさしい保湿\",\n titleEn: \"Gentle hydration\",\n descJa: \"肌のうるおいバリアを守る植物オイルブレンド。敏感肌の方にも穏やかに寄り添います。\",\n descEn: \"A plant-oil blend that protects the skin's moisture barrier — gentle even for sensitive skin.\",\n },\n {\n icon: ShieldCheck,\n titleJa: \"確かな安全性\",\n titleEn: \"Proven safety\",\n descJa: \"全成分を開示し、第三者機関でパッチテスト済み。安心して毎日使える処方です。\",\n descEn: \"Every ingredient is disclosed and third-party patch-tested — a formula you can trust to use daily.\",\n },\n {\n icon: Recycle,\n titleJa: \"地球への配慮\",\n titleEn: \"Kind to the planet\",\n descJa: \"容器は100%リサイクル素材。詰め替えプログラムで廃棄物を最小限に抑えます。\",\n descEn: \"Packaging is 100% recycled material, and our refill program keeps waste to a minimum.\",\n },\n];\n\nexport default function BotanicalFeature() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n

\n {en ? \"Botanical wisdom for everyday care\" : \"植物の知恵を、毎日のケアに\"}\n

\n

\n {en\n ? \"Our commitment, born from the harmony of nature and science.\"\n : \"自然と科学の調和から生まれる、わたしたちのこだわり。\"}\n

\n
\n\n
\n {features.map(({ icon: Icon, titleJa, titleEn, descJa, descEn }) => (\n
\n \n \n \n
\n

\n {en ? titleEn : titleJa}\n

\n

\n {en ? descEn : descJa}\n

\n
\n
\n ))}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n 植物の知恵を、毎日のケアに\n

\n

\n 自然と科学の調和から生まれる、わたしたちのこだわり。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 畑から処方へ\n

\n

\n 契約農家で育てた植物を、収穫から72時間以内に抽出。鮮度の高い有効成分を届けます。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n やさしい保湿\n

\n

\n 肌のうるおいバリアを守る植物オイルブレンド。敏感肌の方にも穏やかに寄り添います。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n 確かな安全性\n

\n

\n 全成分を開示し、第三者機関でパッチテスト済み。安心して毎日使える処方です。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 地球への配慮\n

\n

\n 容器は100%リサイクル素材。詰め替えプログラムで廃棄物を最小限に抑えます。\n

\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n 植物の知恵を、毎日のケアに\n

\n

\n 自然と科学の調和から生まれる、わたしたちのこだわり。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 畑から処方へ\n

\n

\n 契約農家で育てた植物を、収穫から72時間以内に抽出。鮮度の高い有効成分を届けます。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n やさしい保湿\n

\n

\n 肌のうるおいバリアを守る植物オイルブレンド。敏感肌の方にも穏やかに寄り添います。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n 確かな安全性\n

\n

\n 全成分を開示し、第三者機関でパッチテスト済み。安心して毎日使える処方です。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 地球への配慮\n

\n

\n 容器は100%リサイクル素材。詰め替えプログラムで廃棄物を最小限に抑えます。\n

\n
\n
\n
\n
\n
\n \n" }, { "id": "botanical-botanical-footer", @@ -262,7 +262,7 @@ ], "standalone": true, "source": "import { Instagram, Leaf, Send, Twitter, Youtube } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ボタニカル・フッター\",\n category: \"ボタニカル\",\n description: \"枝のディバイダーを添えたナチュラルなフッター。\",\n align: \"full\",\n isNew: true,\n tags: [\"botanical\", \"organic\", \"wellness\"],\n};\n\nfunction BranchDivider() {\n return (\n \n \n \n \n \n \n \n \n \n );\n}\n\nconst columns = [\n {\n id: \"shop\",\n titleJa: \"ショップ\",\n titleEn: \"Shop\",\n links: [\n { ja: \"スキンケア\", en: \"Skincare\" },\n { ja: \"ヘアケア\", en: \"Haircare\" },\n { ja: \"アロマ\", en: \"Aromatherapy\" },\n { ja: \"ギフト\", en: \"Gifts\" },\n ],\n },\n {\n id: \"brand\",\n titleJa: \"ブランド\",\n titleEn: \"Brand\",\n links: [\n { ja: \"私たちの物語\", en: \"Our story\" },\n { ja: \"成分へのこだわり\", en: \"Our ingredients\" },\n { ja: \"サステナビリティ\", en: \"Sustainability\" },\n ],\n },\n {\n id: \"support\",\n titleJa: \"サポート\",\n titleEn: \"Support\",\n links: [\n { ja: \"お問い合わせ\", en: \"Contact\" },\n { ja: \"配送と返品\", en: \"Shipping & returns\" },\n { ja: \"よくある質問\", en: \"FAQ\" },\n ],\n },\n];\n\nexport default function BotanicalFooter() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n \n\n
\n
\n
\n \n \n Verdé\n \n
\n

\n {en\n ? \"Balance skin and spirit with the power of plants — wellness in tune with nature, for your every day.\"\n : \"植物の力で、肌と心を整える。自然と共にあるウェルネスを、あなたの毎日へ。\"}\n

\n
\n {[Instagram, Twitter, Youtube].map((Icon, i) => (\n \n \n \n ))}\n
\n
\n\n {columns.map((col) => (\n
\n

\n {en ? col.titleEn : col.titleJa}\n

\n
    \n {col.links.map((l) => (\n
  • \n \n {en ? l.en : l.ja}\n \n
  • \n ))}\n
\n
\n ))}\n
\n\n
\n

© 2026 Verdé Botanicals. All rights reserved.

\n \n {\" \"}\n {en ? \"Subscribe to the journal\" : \"ジャーナルを購読する\"}\n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n \n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n \n \n" }, { "id": "botanical-botanical-hero", @@ -276,7 +276,7 @@ ], "standalone": true, "source": "import { ArrowRight, Leaf, Sparkles } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ボタニカル・ヒーロー\",\n category: \"ボタニカル\",\n description: \"葉のSVG装飾と柔らかなCTAを備えたオーガニックなヒーロー。\",\n align: \"full\",\n isNew: true,\n tags: [\"botanical\", \"organic\", \"wellness\"],\n};\n\nfunction LeafSpray({ className }: { className?: string }) {\n return (\n \n \n {[40, 80, 120, 160, 200].map((y, i) => {\n const dir = i % 2 === 0 ? 1 : -1;\n return (\n \n \n \n \n );\n })}\n \n );\n}\n\nexport default function BotanicalHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n \n\n
\n \n \n NATURE-DERIVED CARE\n \n

\n {en ? (\n <>\n Botanical gentleness,\n
\n for skin and spirit.\n \n ) : (\n <>\n 肌と心に、\n
\n 植物のやさしさを。\n \n )}\n

\n

\n {en\n ? \"A 100% nature-derived formula born from the field. With soft scents and the power of plants, turn daily skincare into a quiet wellness ritual.\"\n : \"畑から生まれた100%自然由来の処方。穏やかな香りと植物の力で、毎日のスキンケアを静かなウェルネスの時間へ。\"}\n

\n
\n \n \n \n {en ? \"Our philosophy\" : \"私たちの哲学\"}\n \n
\n

\n {en\n ? \"Vegan certified · Cruelty-free · 100% recycled packaging\"\n : \"ヴィーガン認証 · 動物実験フリー · 100% リサイクル容器\"}\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n
\n \n \n \n \n \n \n \n NATURE-DERIVED CARE\n \n

\n 肌と心に、\n
\n 植物のやさしさを。\n

\n

\n 畑から生まれた100%自然由来の処方。穏やかな香りと植物の力で、毎日のスキンケアを静かなウェルネスの時間へ。\n

\n
\n \n \n
\n

\n ヴィーガン認証 · 動物実験フリー · 100% リサイクル容器\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n
\n \n \n \n \n \n \n \n NATURE-DERIVED CARE\n \n

\n 肌と心に、\n
\n 植物のやさしさを。\n

\n

\n 畑から生まれた100%自然由来の処方。穏やかな香りと植物の力で、毎日のスキンケアを静かなウェルネスの時間へ。\n

\n
\n \n \n
\n

\n ヴィーガン認証 · 動物実験フリー · 100% リサイクル容器\n

\n
\n
\n \n" }, { "id": "botanical-botanical-pricing", @@ -290,7 +290,7 @@ ], "standalone": true, "source": "import { Check, Leaf } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ボタニカル・プライシング\",\n category: \"ボタニカル\",\n description: \"ウェルネス会員プランの料金プラン3種。自然な配色で。\",\n align: \"full\",\n isNew: true,\n tags: [\"botanical\", \"organic\", \"wellness\"],\n};\n\ntype Tier = {\n id: string;\n nameJa: string;\n nameEn: string;\n price: string;\n periodJa: string;\n periodEn: string;\n descJa: string;\n descEn: string;\n featuresJa: string[];\n featuresEn: string[];\n featured?: boolean;\n};\n\nconst tiers: Tier[] = [\n {\n id: \"seed\",\n nameJa: \"シード\",\n nameEn: \"Seed\",\n price: \"¥0\",\n periodJa: \"/ 月\",\n periodEn: \"/ mo\",\n descJa: \"はじめての方に。基本のケアを無料で。\",\n descEn: \"For newcomers — essential care, free.\",\n featuresJa: [\"月1回のお手入れガイド\", \"コミュニティ参加\", \"季節のレシピ配信\"],\n featuresEn: [\"Monthly care guide\", \"Community access\", \"Seasonal recipes\"],\n },\n {\n id: \"bloom\",\n nameJa: \"ブルーム\",\n nameEn: \"Bloom\",\n price: \"¥1,980\",\n periodJa: \"/ 月\",\n periodEn: \"/ mo\",\n descJa: \"毎日のウェルネスを習慣に。\",\n descEn: \"Make daily wellness a habit.\",\n featuresJa: [\n \"シードの全特典\",\n \"月替わりボタニカルボックス\",\n \"オンライン瞑想クラス\",\n \"会員限定15%オフ\",\n ],\n featuresEn: [\n \"Everything in Seed\",\n \"Monthly botanical box\",\n \"Online meditation classes\",\n \"Member-only 15% off\",\n ],\n featured: true,\n },\n {\n id: \"forest\",\n nameJa: \"フォレスト\",\n nameEn: \"Forest\",\n price: \"¥4,800\",\n periodJa: \"/ 月\",\n periodEn: \"/ mo\",\n descJa: \"本格的なセルフケアを求める方へ。\",\n descEn: \"For those seeking serious self-care.\",\n featuresJa: [\n \"ブルームの全特典\",\n \"個別カウンセリング\",\n \"スパ施術 月1回無料\",\n \"新商品の先行アクセス\",\n ],\n featuresEn: [\n \"Everything in Bloom\",\n \"One-on-one consultations\",\n \"One free spa treatment a month\",\n \"Early access to new products\",\n ],\n },\n];\n\nexport default function BotanicalPricing() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n \n MEMBERSHIP\n \n

\n {en ? \"A plan that grows at your pace\" : \"あなたのペースで育つプラン\"}\n

\n

\n {en\n ? \"Change or cancel anytime. Wellness that lasts, naturally — like a plant.\"\n : \"いつでも変更・解約可能。植物のように、無理なく続くウェルネスを。\"}\n

\n
\n\n
\n {tiers.map((t) => (\n \n {t.featured && (\n \n {en ? \"Popular\" : \"人気\"}\n \n )}\n \n {en ? t.nameEn : t.nameJa}\n \n \n {en ? t.descEn : t.descJa}\n

\n
\n {t.price}\n \n {en ? t.periodEn : t.periodJa}\n \n
\n
    \n {(en ? t.featuresEn : t.featuresJa).map((f) => (\n
  • \n \n \n {f}\n \n
  • \n ))}\n
\n \n {en ? \"Choose plan\" : \"プランを選ぶ\"}\n \n
\n ))}\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n MEMBERSHIP\n \n

\n あなたのペースで育つプラン\n

\n

\n いつでも変更・解約可能。植物のように、無理なく続くウェルネスを。\n

\n
\n
\n
\n

\n シード\n

\n

\n はじめての方に。基本のケアを無料で。\n

\n
\n \n ¥0\n \n \n / 月\n \n
\n
    \n
  • \n \n \n \n \n \n 月1回のお手入れガイド\n \n
  • \n
  • \n \n \n \n \n \n コミュニティ参加\n \n
  • \n
  • \n \n \n \n \n \n 季節のレシピ配信\n \n
  • \n
\n \n
\n
\n \n 人気\n \n

\n ブルーム\n

\n

\n 毎日のウェルネスを習慣に。\n

\n
\n \n ¥1,980\n \n \n / 月\n \n
\n
    \n
  • \n \n \n \n \n \n シードの全特典\n \n
  • \n
  • \n \n \n \n \n \n 月替わりボタニカルボックス\n \n
  • \n
  • \n \n \n \n \n \n オンライン瞑想クラス\n \n
  • \n
  • \n \n \n \n \n \n 会員限定15%オフ\n \n
  • \n
\n \n
\n
\n

\n フォレスト\n

\n

\n 本格的なセルフケアを求める方へ。\n

\n
\n \n ¥4,800\n \n \n / 月\n \n
\n
    \n
  • \n \n \n \n \n \n ブルームの全特典\n \n
  • \n
  • \n \n \n \n \n \n 個別カウンセリング\n \n
  • \n
  • \n \n \n \n \n \n スパ施術 月1回無料\n \n
  • \n
  • \n \n \n \n \n \n 新商品の先行アクセス\n \n
  • \n
\n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n MEMBERSHIP\n \n

\n あなたのペースで育つプラン\n

\n

\n いつでも変更・解約可能。植物のように、無理なく続くウェルネスを。\n

\n
\n
\n
\n

\n シード\n

\n

\n はじめての方に。基本のケアを無料で。\n

\n
\n \n ¥0\n \n \n / 月\n \n
\n
    \n
  • \n \n \n \n \n \n 月1回のお手入れガイド\n \n
  • \n
  • \n \n \n \n \n \n コミュニティ参加\n \n
  • \n
  • \n \n \n \n \n \n 季節のレシピ配信\n \n
  • \n
\n \n
\n
\n \n 人気\n \n

\n ブルーム\n

\n

\n 毎日のウェルネスを習慣に。\n

\n
\n \n ¥1,980\n \n \n / 月\n \n
\n
    \n
  • \n \n \n \n \n \n シードの全特典\n \n
  • \n
  • \n \n \n \n \n \n 月替わりボタニカルボックス\n \n
  • \n
  • \n \n \n \n \n \n オンライン瞑想クラス\n \n
  • \n
  • \n \n \n \n \n \n 会員限定15%オフ\n \n
  • \n
\n \n
\n
\n

\n フォレスト\n

\n

\n 本格的なセルフケアを求める方へ。\n

\n
\n \n ¥4,800\n \n \n / 月\n \n
\n
    \n
  • \n \n \n \n \n \n ブルームの全特典\n \n
  • \n
  • \n \n \n \n \n \n 個別カウンセリング\n \n
  • \n
  • \n \n \n \n \n \n スパ施術 月1回無料\n \n
  • \n
  • \n \n \n \n \n \n 新商品の先行アクセス\n \n
  • \n
\n \n
\n
\n
\n
\n \n" }, { "id": "brutalist-brutal-button-set", @@ -304,7 +304,7 @@ ], "standalone": true, "source": "import { ArrowRight, Download, Heart, Sparkles, Trash2 } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ブルータル・ボタン集\",\n category: \"ブルータリスト\",\n description: \"色違いのバリアントとホバーで沈むブルータルなボタンとバッジ。\",\n align: \"center\",\n isNew: true,\n tags: [\"brutalist\", \"bold\", \"buttons\"],\n};\n\nconst baseBtn =\n \"inline-flex items-center gap-2 border-4 border-black px-5 py-2.5 font-black uppercase shadow-[5px_5px_0_0_#000] transition-all hover:translate-x-1 hover:translate-y-1 hover:shadow-[1px_1px_0_0_#000] active:translate-x-1.5 active:translate-y-1.5 active:shadow-none\";\n\nexport default function BrutalButtonSet() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n
\n {en ? \"Buttons\" : \"ボタン\"}\n
\n
\n \n \n \n \n
\n
\n\n
\n
\n {en ? \"Sizes\" : \"サイズ\"}\n
\n
\n \n \n \n
\n
\n\n
\n
\n {en ? \"Badges\" : \"バッジ\"}\n
\n
\n \n NEW\n \n \n SALE -50%\n \n \n {\" \"}\n {en ? \"Popular\" : \"人気\"}\n \n \n {en ? \"Limited\" : \"限定\"}\n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n ボタン\n
\n
\n \n \n \n \n
\n
\n
\n
\n サイズ\n
\n
\n \n \n \n
\n
\n
\n
\n バッジ\n
\n
\n \n NEW\n \n \n SALE -50%\n \n \n \n \n \n \n 人気\n \n \n 限定\n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n ボタン\n
\n
\n \n \n \n \n
\n
\n
\n
\n サイズ\n
\n
\n \n \n \n
\n
\n
\n
\n バッジ\n
\n
\n \n NEW\n \n \n SALE -50%\n \n \n \n \n \n \n 人気\n \n \n 限定\n \n
\n
\n
\n
\n \n" }, { "id": "brutalist-brutal-card", @@ -318,7 +318,7 @@ ], "standalone": true, "source": "import { ArrowUpRight, Heart, MessageCircle } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ブルータル・カード\",\n category: \"ブルータリスト\",\n description: \"ハードシャドウとホバーで沈むコンテンツカード。\",\n align: \"center\",\n isNew: true,\n tags: [\"brutalist\", \"bold\", \"card\"],\n};\n\nexport default function BrutalCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n B\n
\n\n
\n
\n {en ? \"Design\" : \"設計\"}\n
\n

\n {en ? \"Raw, undressed design\" : \"生のままのデザイン\"}\n

\n

\n {en\n ? \"Strip away the decoration and speak with structure and contrast alone. That's brutalism.\"\n : \"余計な装飾を削ぎ落とし、構造とコントラストだけで語る。それがブルータリズム。\"}\n

\n\n
\n
\n \n 248\n \n \n 32\n \n
\n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n \n B\n \n
\n
\n
\n 設計\n
\n

\n 生のままのデザイン\n

\n

\n 余計な装飾を削ぎ落とし、構造とコントラストだけで語る。それがブルータリズム。\n

\n
\n
\n \n \n \n \n \n 248\n \n \n \n \n \n \n 32\n \n
\n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n B\n \n
\n
\n
\n 設計\n
\n

\n 生のままのデザイン\n

\n

\n 余計な装飾を削ぎ落とし、構造とコントラストだけで語る。それがブルータリズム。\n

\n
\n
\n \n \n \n \n \n 248\n \n \n \n \n \n \n 32\n \n
\n \n
\n
\n
\n
\n \n" }, { "id": "brutalist-brutal-cta", @@ -332,7 +332,7 @@ ], "standalone": true, "source": "import { ArrowRight, Zap } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ブルータル・CTA\",\n category: \"ブルータリスト\",\n description: \"黒地に飽和色を効かせた高インパクトなCTAバンド。\",\n align: \"full\",\n isNew: true,\n tags: [\"brutalist\", \"bold\", \"cta\"],\n};\n\nexport default function BrutalCTA() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n
\n
\n \n {en ? \"Free now\" : \"いま無料\"}\n
\n

\n {en ? (\n <>\n Stop stalling.\n
\n Ship it.\n \n ) : (\n <>\n 迷うな。\n
\n 出荷しろ。\n \n )}\n

\n

\n {en\n ? \"No credit card. Your first component in production in 30 seconds.\"\n : \"クレカ不要。30秒で最初のコンポーネントを本番へ。\"}\n

\n
\n\n
\n \n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n いま無料\n
\n

\n 迷うな。\n
\n \n 出荷しろ。\n \n

\n

\n クレカ不要。30秒で最初のコンポーネントを本番へ。\n

\n
\n
\n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n いま無料\n
\n

\n 迷うな。\n
\n \n 出荷しろ。\n \n

\n

\n クレカ不要。30秒で最初のコンポーネントを本番へ。\n

\n
\n
\n \n \n
\n
\n
\n
\n \n" }, { "id": "brutalist-brutal-feature-grid", @@ -346,7 +346,7 @@ ], "standalone": true, "source": "import { Bolt, Lock, Palette, Rocket, Ruler, Shield } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ブルータル・特徴グリッド\",\n category: \"ブルータリスト\",\n description: \"飽和カラーブロックと極太ボーダーの機能グリッド。\",\n align: \"full\",\n isNew: true,\n tags: [\"brutalist\", \"bold\", \"features\"],\n};\n\ntype Feature = {\n icon: LucideIcon;\n title: string;\n titleEn: string;\n body: string;\n bodyEn: string;\n bg: string;\n};\n\nconst features: Feature[] = [\n { icon: Bolt, title: \"爆速\", titleEn: \"Blazing\", body: \"サブミリ秒で描画。待たせない。\", bodyEn: \"Sub-millisecond rendering. No waiting.\", bg: \"bg-yellow-300\" },\n { icon: Shield, title: \"頑丈\", titleEn: \"Tough\", body: \"壊れにくい設計。安心して使える。\", bodyEn: \"Built not to break. Use with confidence.\", bg: \"bg-lime-300\" },\n { icon: Palette, title: \"大胆\", titleEn: \"Bold\", body: \"妥協なき色とコントラスト。\", bodyEn: \"Uncompromising color and contrast.\", bg: \"bg-fuchsia-400\" },\n { icon: Lock, title: \"安全\", titleEn: \"Secure\", body: \"デフォルトで型安全・アクセシブル。\", bodyEn: \"Type-safe and accessible by default.\", bg: \"bg-cyan-300\" },\n { icon: Ruler, title: \"正確\", titleEn: \"Precise\", body: \"ピクセル単位で意図通り。\", bodyEn: \"Pixel-perfect, exactly as intended.\", bg: \"bg-orange-400\" },\n { icon: Rocket, title: \"即出荷\", titleEn: \"Ship now\", body: \"貼って終わり。設定地獄なし。\", bodyEn: \"Paste and done. No config hell.\", bg: \"bg-white\" },\n];\n\nexport default function BrutalFeatureGrid() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n

\n {en ? \"Features\" : \"機能\"}_{en ? \"list\" : \"一覧\"}\n

\n\n
\n {features.map((f) => {\n const Icon = f.icon;\n return (\n \n \n \n \n

{en ? f.titleEn : f.title}

\n

{en ? f.bodyEn : f.body}

\n
\n );\n })}\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n

\n 機能\n \n _\n \n 一覧\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 爆速\n

\n

\n サブミリ秒で描画。待たせない。\n

\n
\n
\n \n \n \n \n \n \n

\n 頑丈\n

\n

\n 壊れにくい設計。安心して使える。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n 大胆\n

\n

\n 妥協なき色とコントラスト。\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 安全\n

\n

\n デフォルトで型安全・アクセシブル。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n 正確\n

\n

\n ピクセル単位で意図通り。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n

\n 即出荷\n

\n

\n 貼って終わり。設定地獄なし。\n

\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n 機能\n \n _\n \n 一覧\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 爆速\n

\n

\n サブミリ秒で描画。待たせない。\n

\n
\n
\n \n \n \n \n \n \n

\n 頑丈\n

\n

\n 壊れにくい設計。安心して使える。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n 大胆\n

\n

\n 妥協なき色とコントラスト。\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 安全\n

\n

\n デフォルトで型安全・アクセシブル。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n 正確\n

\n

\n ピクセル単位で意図通り。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n

\n 即出荷\n

\n

\n 貼って終わり。設定地獄なし。\n

\n
\n
\n
\n
\n \n" }, { "id": "btn-fun-brutalist", @@ -359,7 +359,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Zap } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"ブルータリスト\",\n category: \"ボタン\",\n description: \"極太の黒枠とベタ塗りの硬いドロップシャドウ。押すと影に沈むブルータリスト・ボタン。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"fun\"],\n};\n\nexport default function Brutalist() {\n return (\n
\n \n \n CLICK ME\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n \n" }, { "id": "btn-fun-gooey", @@ -372,7 +372,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Blend } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"グーイ\",\n category: \"ボタン\",\n description: \"SVGフィルターで粘性のあるグー(goo)表現。ホバーで雫が溶け合う。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"fun\"],\n};\n\nexport default function Gooey() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n {en ? \"Gooey\" : \"とろり\"}\n \n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n
\n \n" }, { "id": "btn-fun-gradient-pill", @@ -385,7 +385,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Flame } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"グラデピル\",\n category: \"ボタン\",\n description: \"鮮やかなマルチグラデが流れるピル型。ホバーで色がゆっくり巡る。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"fun\"],\n};\n\nexport default function GradientPill() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n \n \n \n {en ? \"Trending\" : \"トレンド\"}\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n
\n \n" }, { "id": "btn-fun-hand-drawn", @@ -398,7 +398,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Pencil } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"手描き枠\",\n category: \"ボタン\",\n description: \"ラフな手描き風の枠線。ホバーで線がゆらいで生き生きするボタン。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"fun\"],\n};\n\nexport default function HandDrawn() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n \n \n \n {en ? \"Sketch\" : \"スケッチ\"}\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n
\n \n" }, { "id": "button-anim-arrow-slide-button", @@ -413,7 +413,7 @@ ], "standalone": true, "source": "import { ArrowRight } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アロースライドボタン\",\n category: \"ボタン演出\",\n description: \"ホバーで矢印がスライドし、ラベルがずれて動くCTA。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"animation\", \"arrow\", \"cta\"],\n};\n\nexport default function ArrowSlideButton() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n {/* 矢印が右へ滑る */}\n \n\n {/* 矢印が外から入ってくる */}\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n
\n \n" }, { "id": "button-anim-border-beam-button", @@ -428,7 +428,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ボーダービームボタン\",\n category: \"ボタン演出\",\n description: \"ボタンの縁を光のビームが一周し続けるボタン。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"animation\", \"border\", \"beam\"],\n};\n\nexport default function BorderBeamButton() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n \n\n \n\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n" }, { "id": "button-anim-glow-pulse-button", @@ -442,7 +442,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グロウパルスボタン\",\n category: \"ボタン演出\",\n description: \"呼吸するように光のグロウが脈打つボタン。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"animation\", \"glow\"],\n};\n\nexport default function GlowPulseButton() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n \n\n \n {en ? \"Power on\" : \"パワーオン\"}\n \n\n \n {en ? \"Connect\" : \"接続する\"}\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n" }, { "id": "button-anim-gooey-button", @@ -456,7 +456,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グーイボタン\",\n category: \"ボタン演出\",\n description: \"SVGフィルターでブロブが溶け合うグーイなホバー演出ボタン。\",\n align: \"center\",\n isNew: true,\n tags: [\"button\", \"animation\", \"gooey\"],\n};\n\nexport default function GooeyButton() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n \n\n {/* SVG goo filter */}\n \n \n \n \n \n \n \n \n \n\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n" }, { "id": "card-anim-border-beam-card", @@ -472,7 +472,7 @@ ], "standalone": true, "source": "import { Zap } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ボーダービームカード\",\n category: \"カード演出\",\n description: \"一筋の光のビームがカードの縁を offset-path で周回する。\",\n align: \"center\",\n isNew: true,\n tags: [\"card\", \"hover\", \"animation\", \"border\", \"beam\"],\n};\n\nexport default function BorderBeamCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n {/* traveling beam */}\n \n
\n \n \n \n

{en ? \"Border Beam\" : \"ボーダー・ビーム\"}

\n

\n {en\n ? \"A single point of light traces the border continuously — a perpetual animation that loops smoothly along an offset-path.\"\n : \"一点の光が枠線をなぞり続けます。offset-path で滑らかに周回する常時アニメーション。\"}\n

\n
\n live\n {en ? \"Connect →\" : \"接続 →\"}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n
\n \n \n \n \n \n \n

\n ボーダー・ビーム\n

\n

\n 一点の光が枠線をなぞり続けます。offset-path で滑らかに周回する常時アニメーション。\n

\n
\n \n live\n \n \n 接続 →\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n \n \n \n \n \n

\n ボーダー・ビーム\n

\n

\n 一点の光が枠線をなぞり続けます。offset-path で滑らかに周回する常時アニメーション。\n

\n
\n \n live\n \n \n 接続 →\n \n
\n
\n
\n \n" }, { "id": "card-anim-expand-image-card", @@ -488,7 +488,7 @@ ], "standalone": true, "source": "import { MapPin, ArrowUpRight } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"拡大イメージカード\",\n category: \"カード演出\",\n description: \"グラデーション製の「画像」がホバーでズームし、文字が下から現れる。\",\n align: \"center\",\n isNew: true,\n tags: [\"card\", \"hover\", \"animation\", \"image\", \"zoom\"],\n};\n\nexport default function ExpandImageCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* gradient \"image\" */}\n
\n
\n\n
\n \n {en ? \"Kyoto\" : \"京都\"}\n \n {/* rising overlay text */}\n
\n

\n {en ? \"Sunset over the old capital\" : \"古都の夕景\"}\n

\n

\n {en\n ? \"On hover, the background slowly zooms and the description rises into view.\"\n : \"ホバーで背景がゆっくりズームし、説明文がせり上がって表示されます。\"}\n

\n \n {en ? \"View itinerary\" : \"旅程を見る\"} \n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n 京都\n \n
\n

\n 古都の夕景\n

\n

\n ホバーで背景がゆっくりズームし、説明文がせり上がって表示されます。\n

\n \n 旅程を見る\n \n \n \n \n \n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n 京都\n \n
\n

\n 古都の夕景\n

\n

\n ホバーで背景がゆっくりズームし、説明文がせり上がって表示されます。\n

\n \n 旅程を見る\n \n \n \n \n \n \n \n
\n
\n
\n \n" }, { "id": "card-anim-flip-card", @@ -504,7 +504,7 @@ ], "standalone": true, "source": "import { CreditCard, Wifi } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"フリップカード\",\n category: \"カード演出\",\n description: \"ホバーで表裏が3D回転して切り替わるカード(backface-hidden)。\",\n align: \"center\",\n isNew: true,\n tags: [\"card\", \"hover\", \"animation\", \"flip\", \"3d\"],\n};\n\nexport default function FlipCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n {/* Front */}\n
\n
\n \n \n
\n
\n

•••• •••• •••• 4921

\n

Aurora Bank

\n
\n
\n {/* Back */}\n
\n
\n
\n
\n CVV\n •••\n
\n

\n {en\n ? \"Hover to reveal the back. A 3D rotation with hidden backface makes the flip feel natural.\"\n : \"ホバーで裏面が表示されます。3D回転と裏面非表示で自然な反転を実現。\"}\n

\n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n •••• •••• •••• 4921\n

\n

\n Aurora Bank\n

\n
\n
\n
\n
\n
\n
\n
\n \n CVV\n \n \n •••\n \n
\n

\n ホバーで裏面が表示されます。3D回転と裏面非表示で自然な反転を実現。\n

\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n •••• •••• •••• 4921\n

\n

\n Aurora Bank\n

\n
\n
\n
\n
\n
\n
\n
\n \n CVV\n \n \n •••\n \n
\n

\n ホバーで裏面が表示されます。3D回転と裏面非表示で自然な反転を実現。\n

\n
\n
\n
\n
\n \n" }, { "id": "card-anim-gradient-border-card", @@ -520,7 +520,7 @@ ], "standalone": true, "source": "import { Rocket } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"回転グラデーション枠カード\",\n category: \"カード演出\",\n description: \"円錐グラデーションのビームが縁を回り続ける発光ボーダー。\",\n align: \"center\",\n isNew: true,\n tags: [\"card\", \"hover\", \"animation\", \"border\", \"gradient\"],\n};\n\nexport default function GradientBorderCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n \n
\n \n \n \n

\n {en ? \"Rotating gradient border\" : \"回転グラデーション枠\"}\n

\n

\n {en\n ? \"A beam orbits the edge endlessly. On hover it glows brighter for a premium feel.\"\n : \"縁をビームが絶えず周回します。ホバーで光量が増し、プレミアムな印象を強めます。\"}\n

\n
\n animated\n \n {en ? \"Upgrade →\" : \"アップグレード →\"}\n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n

\n 回転グラデーション枠\n

\n

\n 縁をビームが絶えず周回します。ホバーで光量が増し、プレミアムな印象を強めます。\n

\n
\n \n animated\n \n \n アップグレード →\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n

\n 回転グラデーション枠\n

\n

\n 縁をビームが絶えず周回します。ホバーで光量が増し、プレミアムな印象を強めます。\n

\n
\n \n animated\n \n \n アップグレード →\n \n
\n
\n
\n \n" }, { "id": "conversion-guarantee-badge", @@ -534,7 +534,7 @@ ], "standalone": true, "source": "import { Check, ShieldCheck } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"返金保証シール\",\n category: \"コンバージョン\",\n description:\n \"購入リスクを取り除く、力強い全額返金保証のシール/カード。\",\n align: \"center\",\n level: \"advanced\",\n isNew: true,\n tags: [\"guarantee\", \"risk-reversal\", \"trust\"],\n principle:\n \"「効果がなければ全額返金」と売り手がリスクを肩代わりするリスク・リバーサルが、買い手の損する不安を消し、最後の購入障壁を取り除く。\",\n};\n\nconst POINTS = [\n { ja: \"理由を問わず全額返金\", en: \"Full refund, no questions asked\" },\n { ja: \"解約はワンクリック\", en: \"Cancel in one click\" },\n { ja: \"手数料・違約金なし\", en: \"No fees, no penalties\" },\n];\n\nexport default function GuaranteeBadge() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* Seal */}\n
\n \n \n \n \n money back · 30 days · money back · 30 days ·\n \n \n \n
\n \n
\n
\n\n

\n {en ? \"100% satisfaction guarantee\" : \"100% 満足保証\"}\n

\n

\n {en ? \"30-day money-back guarantee\" : \"30日間 全額返金保証\"}\n

\n

\n {en\n ? \"Try it risk-free. If you're not satisfied, we'll refund you in full within 30 days.\"\n : \"まずはノーリスクでお試しください。ご満足いただけなければ、30日以内に全額お返しします。\"}\n

\n\n
    \n {POINTS.map((p) => (\n
  • \n \n \n \n {en ? p.en : p.ja}\n
  • \n ))}\n
\n\n \n

\n {en ? \"No credit card required.\" : \"クレジットカードは不要です。\"}\n

\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n money back · 30 days · money back · 30 days ·\n \n \n \n
\n \n \n \n \n \n \n
\n
\n

\n 100% 満足保証\n

\n

\n 30日間 全額返金保証\n

\n

\n まずはノーリスクでお試しください。ご満足いただけなければ、30日以内に全額お返しします。\n

\n
    \n
  • \n \n \n \n \n \n \n 理由を問わず全額返金\n
  • \n
  • \n \n \n \n \n \n \n 解約はワンクリック\n
  • \n
  • \n \n \n \n \n \n \n 手数料・違約金なし\n
  • \n
\n \n

\n クレジットカードは不要です。\n

\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n money back · 30 days · money back · 30 days ·\n \n \n \n
\n \n \n \n \n \n \n
\n
\n

\n 100% 満足保証\n

\n

\n 30日間 全額返金保証\n

\n

\n まずはノーリスクでお試しください。ご満足いただけなければ、30日以内に全額お返しします。\n

\n
    \n
  • \n \n \n \n \n \n \n 理由を問わず全額返金\n
  • \n
  • \n \n \n \n \n \n \n 解約はワンクリック\n
  • \n
  • \n \n \n \n \n \n \n 手数料・違約金なし\n
  • \n
\n \n

\n クレジットカードは不要です。\n

\n
\n \n" }, { "id": "conversion-testimonial-wall", @@ -548,7 +548,7 @@ ], "standalone": true, "source": "import { BadgeCheck, Star } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"推薦ウォール\",\n category: \"コンバージョン\",\n description:\n \"アバター・評価・認証マーク付きの短い推薦を敷き詰めたグリッドウォール。\",\n align: \"full\",\n level: \"advanced\",\n isNew: true,\n tags: [\"social-proof\", \"testimonials\", \"reviews\"],\n principle:\n \"肯定的な声を「量」で見せる社会的証明が、一件あたりの説得力を超えて『これだけの人が満足している』という規模の安心を生み、懐疑を打ち消す。\",\n};\n\ntype Review = {\n name: string;\n roleJa: string;\n roleEn: string;\n textJa: string;\n textEn: string;\n hue: string;\n rating: number;\n};\n\nconst REVIEWS: Review[] = [\n { name: \"中村 由美\", roleJa: \"プロダクトデザイナー\", roleEn: \"Product Designer\", textJa: \"導入初週でリリース速度が体感2倍。チームの空気まで変わった。\", textEn: \"Our release speed felt 2x in the first week. It even changed the team's mood.\", hue: \"from-rose-500 to-pink-500\", rating: 5 },\n { name: \"James K.\", roleJa: \"エンジニアリード\", roleEn: \"Eng. Lead\", textJa: \"オンボーディングが驚くほどスムーズ。今年いちばんのツール選定だった。\", textEn: \"The onboarding was effortless. Best tool decision we made this year.\", hue: \"from-sky-500 to-indigo-500\", rating: 5 },\n { name: \"高橋 蓮\", roleJa: \"スタートアップ創業者\", roleEn: \"Startup Founder\", textJa: \"解約しようとしたサービスを全部これに置き換えた。コスパが異常。\", textEn: \"I replaced every tool I was about to cancel with this. The value is unreal.\", hue: \"from-emerald-500 to-teal-500\", rating: 5 },\n { name: \"Sofia M.\", roleJa: \"グロースPM\", roleEn: \"Growth PM\", textJa: \"導入後、料金ページのコンバージョンが18%向上した。\", textEn: \"Conversion on our pricing page jumped 18% after we shipped it.\", hue: \"from-amber-500 to-orange-500\", rating: 5 },\n { name: \"小林 彩\", roleJa: \"マーケター\", roleEn: \"Marketer\", textJa: \"サポートの返信が早すぎて逆に申し訳ない。安心して任せられる。\", textEn: \"Support replies so fast I almost feel guilty. I can rely on them completely.\", hue: \"from-violet-500 to-fuchsia-500\", rating: 4 },\n { name: \"Liam T.\", roleJa: \"インディーハッカー\", roleEn: \"Indie Hacker\", textJa: \"3つのツールを1つに統合できた。週末が自分のものに戻った。\", textEn: \"Replaced three tools with one. My weekends are mine again.\", hue: \"from-cyan-500 to-blue-500\", rating: 5 },\n];\n\nfunction initials(name: string) {\n const p = name.trim().split(/\\s+/);\n return (p[0]?.[0] ?? \"\") + (p[1]?.[0] ?? \"\");\n}\n\nexport default function TestimonialWall() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n
\n {Array.from({ length: 5 }).map((_, i) => (\n \n ))}\n
\n

\n {en ? \"2,800+ five-star reviews\" : \"2,800件以上の高評価レビュー\"}\n

\n

\n {en\n ? \"Hear from the teams who use it every day.\"\n : \"実際に使っているチームの声をご覧ください。\"}\n

\n
\n\n
*]:mb-4\">\n {REVIEWS.map((r) => (\n \n
\n {Array.from({ length: 5 }).map((_, i) => (\n \n ))}\n
\n
\n {en ? r.textEn : r.textJa}\n
\n
\n \n {initials(r.name)}\n \n
\n

\n {r.name}\n \n

\n

\n {en ? r.roleEn : r.roleJa}\n

\n
\n
\n \n ))}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 2,800件以上の高評価レビュー\n

\n

\n 実際に使っているチームの声をご覧ください。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 導入初週でリリース速度が体感2倍。チームの空気まで変わった。\n
\n
\n \n 中由\n \n
\n

\n \n 中村 由美\n \n \n \n \n \n \n \n

\n

\n プロダクトデザイナー\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n オンボーディングが驚くほどスムーズ。今年いちばんのツール選定だった。\n
\n
\n \n JK\n \n
\n

\n \n James K.\n \n \n \n \n \n \n \n

\n

\n エンジニアリード\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 解約しようとしたサービスを全部これに置き換えた。コスパが異常。\n
\n
\n \n 高蓮\n \n
\n

\n \n 高橋 蓮\n \n \n \n \n \n \n \n

\n

\n スタートアップ創業者\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 導入後、料金ページのコンバージョンが18%向上した。\n
\n
\n \n SM\n \n
\n

\n \n Sofia M.\n \n \n \n \n \n \n \n

\n

\n グロースPM\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n サポートの返信が早すぎて逆に申し訳ない。安心して任せられる。\n
\n
\n \n 小彩\n \n
\n

\n \n 小林 彩\n \n \n \n \n \n \n \n

\n

\n マーケター\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 3つのツールを1つに統合できた。週末が自分のものに戻った。\n
\n
\n \n LT\n \n
\n

\n \n Liam T.\n \n \n \n \n \n \n \n

\n

\n インディーハッカー\n

\n
\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 2,800件以上の高評価レビュー\n

\n

\n 実際に使っているチームの声をご覧ください。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 導入初週でリリース速度が体感2倍。チームの空気まで変わった。\n
\n
\n \n 中由\n \n
\n

\n \n 中村 由美\n \n \n \n \n \n \n \n

\n

\n プロダクトデザイナー\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n オンボーディングが驚くほどスムーズ。今年いちばんのツール選定だった。\n
\n
\n \n JK\n \n
\n

\n \n James K.\n \n \n \n \n \n \n \n

\n

\n エンジニアリード\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 解約しようとしたサービスを全部これに置き換えた。コスパが異常。\n
\n
\n \n 高蓮\n \n
\n

\n \n 高橋 蓮\n \n \n \n \n \n \n \n

\n

\n スタートアップ創業者\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 導入後、料金ページのコンバージョンが18%向上した。\n
\n
\n \n SM\n \n
\n

\n \n Sofia M.\n \n \n \n \n \n \n \n

\n

\n グロースPM\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n サポートの返信が早すぎて逆に申し訳ない。安心して任せられる。\n
\n
\n \n 小彩\n \n
\n

\n \n 小林 彩\n \n \n \n \n \n \n \n

\n

\n マーケター\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n 3つのツールを1つに統合できた。週末が自分のものに戻った。\n
\n
\n \n LT\n \n
\n

\n \n Liam T.\n \n \n \n \n \n \n \n

\n

\n インディーハッカー\n

\n
\n
\n
\n
\n
\n
\n \n" }, { "id": "conversion-trust-badges", @@ -562,7 +562,7 @@ ], "standalone": true, "source": "import { CreditCard, Headphones, RefreshCw, ShieldCheck } from \"lucide-react\";\nimport type { LucideIcon } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"信頼バッジ列\",\n category: \"コンバージョン\",\n description:\n \"SSL・返金保証・30日無料・24時間対応などの安心バッジを並べた帯。\",\n align: \"full\",\n level: \"advanced\",\n isNew: true,\n tags: [\"trust\", \"authority\", \"badges\"],\n principle:\n \"第三者保証や安全性の象徴を並べる権威の原理が、決済直前の知覚リスクを下げて安心感を与え、カート離脱を抑える。\",\n};\n\ntype Badge = {\n icon: LucideIcon;\n titleJa: string;\n titleEn: string;\n subJa: string;\n subEn: string;\n accent: string;\n};\n\nconst BADGES: Badge[] = [\n {\n icon: ShieldCheck,\n titleJa: \"SSL 暗号化通信\",\n titleEn: \"SSL Encrypted\",\n subJa: \"256-bit セキュア決済\",\n subEn: \"256-bit secure checkout\",\n accent: \"text-emerald-500 bg-emerald-500/10\",\n },\n {\n icon: RefreshCw,\n titleJa: \"30日返金保証\",\n titleEn: \"30-Day Refund\",\n subJa: \"理由を問わず全額返金\",\n subEn: \"Full refund, no questions\",\n accent: \"text-sky-500 bg-sky-500/10\",\n },\n {\n icon: CreditCard,\n titleJa: \"カード不要トライアル\",\n titleEn: \"No-Card Trial\",\n subJa: \"30日間すべて無料\",\n subEn: \"Everything free for 30 days\",\n accent: \"text-violet-500 bg-violet-500/10\",\n },\n {\n icon: Headphones,\n titleJa: \"24時間サポート\",\n titleEn: \"24-Hour Support\",\n subJa: \"日本語で365日対応\",\n subEn: \"Available 365 days a year\",\n accent: \"text-amber-500 bg-amber-500/10\",\n },\n];\n\nexport default function TrustBadges() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n

\n {en ? \"Shop with confidence\" : \"安心してご利用いただけます\"}\n

\n
\n {BADGES.map((b) => (\n \n \n \n \n
\n

\n {en ? b.titleEn : b.titleJa}\n

\n

\n {en ? b.subEn : b.subJa}\n

\n
\n
\n ))}\n
\n

\n {en ? (\n <>\n Trusted by{\" \"}\n 12,400+{\" \"}\n companies. Average rating{\" \"}\n 4.9 / 5.0\n \n ) : (\n <>\n すでに{\" \"}\n 12,400社{\" \"}\n 以上が導入。平均満足度{\" \"}\n 4.9 / 5.0\n \n )}\n

\n
\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n

\n 安心してご利用いただけます\n

\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n SSL 暗号化通信\n

\n

\n 256-bit セキュア決済\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 30日返金保証\n

\n

\n 理由を問わず全額返金\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n カード不要トライアル\n

\n

\n 30日間すべて無料\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 24時間サポート\n

\n

\n 日本語で365日対応\n

\n
\n
\n
\n

\n すでに\n \n 12,400社\n \n 以上が導入。平均満足度\n \n 4.9 / 5.0\n \n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n 安心してご利用いただけます\n

\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n SSL 暗号化通信\n

\n

\n 256-bit セキュア決済\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 30日返金保証\n

\n

\n 理由を問わず全額返金\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n カード不要トライアル\n

\n

\n 30日間すべて無料\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 24時間サポート\n

\n

\n 日本語で365日対応\n

\n
\n
\n
\n

\n すでに\n \n 12,400社\n \n 以上が導入。平均満足度\n \n 4.9 / 5.0\n \n

\n
\n
\n \n" }, { "id": "darktech-dev-footer", @@ -576,7 +576,7 @@ ], "standalone": true, "source": "import { Hexagon, Github, Twitter, MessageCircle } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"デベロッパー・フッター\",\n category: \"ダークテック\",\n description: \"ドキュメントリンクをグループ化したダーク開発者フッター(ステータスピル付き)。\",\n align: \"full\",\n isNew: true,\n tags: [\"dark\", \"developer\", \"tech\"],\n};\n\ntype Link = { ja: string; en: string };\nconst GROUPS: { id: string; titleJa: string; titleEn: string; links: Link[] }[] = [\n {\n id: \"product\",\n titleJa: \"製品\",\n titleEn: \"Product\",\n links: [\n { ja: \"概要\", en: \"Overview\" },\n { ja: \"ランタイム\", en: \"Runtime\" },\n { ja: \"CLI\", en: \"CLI\" },\n { ja: \"価格\", en: \"Pricing\" },\n ],\n },\n {\n id: \"developers\",\n titleJa: \"開発者\",\n titleEn: \"Developers\",\n links: [\n { ja: \"ドキュメント\", en: \"Docs\" },\n { ja: \"API リファレンス\", en: \"API reference\" },\n { ja: \"SDK\", en: \"SDK\" },\n { ja: \"変更履歴\", en: \"Changelog\" },\n ],\n },\n {\n id: \"resources\",\n titleJa: \"リソース\",\n titleEn: \"Resources\",\n links: [\n { ja: \"ブログ\", en: \"Blog\" },\n { ja: \"ガイド\", en: \"Guides\" },\n { ja: \"サンプル\", en: \"Examples\" },\n { ja: \"コミュニティ\", en: \"Community\" },\n ],\n },\n {\n id: \"company\",\n titleJa: \"会社\",\n titleEn: \"Company\",\n links: [\n { ja: \"採用情報\", en: \"Careers\" },\n { ja: \"お問い合わせ\", en: \"Contact\" },\n { ja: \"プライバシー\", en: \"Privacy\" },\n { ja: \"利用規約\", en: \"Terms\" },\n ],\n },\n];\n\nexport default function DevFooter() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n
\n \n \n \n \n forge\n \n

\n {en\n ? \"An edge-native platform built to help developers ship faster.\"\n : \"開発者がより速く出荷するためのエッジネイティブなプラットフォーム。\"}\n

\n
\n {[\n { Icon: Github, label: \"GitHub\" },\n { Icon: Twitter, label: \"X (Twitter)\" },\n { Icon: MessageCircle, label: \"Discord\" },\n ].map(({ Icon, label }) => (\n \n \n \n ))}\n
\n
\n\n {GROUPS.map((g) => (\n
\n

\n {en ? g.titleEn : g.titleJa}\n

\n
    \n {g.links.map((l) => (\n
  • \n \n {en ? l.en : l.ja}\n \n
  • \n ))}\n
\n
\n ))}\n
\n\n
\n

\n © 2026 Forge Inc. All rights reserved.\n

\n \n \n \n \n \n {en ? \"All systems operational\" : \"全システム稼働中\"}\n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n \n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n \n \n" }, { "id": "darktech-dev-pricing", @@ -590,7 +590,7 @@ ], "standalone": true, "source": "import { Check, Zap } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"デベロッパー価格表\",\n category: \"ダークテック\",\n description: \"Hobby / Pro / Enterprise のダーク開発者ツール向け料金プラン。\",\n align: \"full\",\n isNew: true,\n tags: [\"dark\", \"developer\", \"tech\"],\n};\n\ntype Plan = {\n name: string;\n price: string;\n unitJa: string;\n unitEn: string;\n descJa: string;\n descEn: string;\n features: { ja: string; en: string }[];\n ctaJa: string;\n ctaEn: string;\n featured?: boolean;\n};\n\nconst PLANS: Plan[] = [\n {\n name: \"Hobby\",\n price: \"$0\",\n unitJa: \"/月\",\n unitEn: \"/mo\",\n descJa: \"個人プロジェクトと試作向け。\",\n descEn: \"For personal projects and prototypes.\",\n features: [\n { ja: \"100K リクエスト/月\", en: \"100K requests/mo\" },\n { ja: \"1 プロジェクト\", en: \"1 project\" },\n { ja: \"コミュニティサポート\", en: \"Community support\" },\n { ja: \"共有ランタイム\", en: \"Shared runtime\" },\n ],\n ctaJa: \"無料で始める\",\n ctaEn: \"Start for free\",\n },\n {\n name: \"Pro\",\n price: \"$20\",\n unitJa: \"/月\",\n unitEn: \"/mo\",\n descJa: \"本番環境のチームに最適。\",\n descEn: \"Best for teams in production.\",\n features: [\n { ja: \"5M リクエスト/月\", en: \"5M requests/mo\" },\n { ja: \"無制限プロジェクト\", en: \"Unlimited projects\" },\n { ja: \"優先サポート\", en: \"Priority support\" },\n { ja: \"エッジランタイム\", en: \"Edge runtime\" },\n { ja: \"カスタムドメイン\", en: \"Custom domains\" },\n ],\n ctaJa: \"Pro にアップグレード\",\n ctaEn: \"Upgrade to Pro\",\n featured: true,\n },\n {\n name: \"Enterprise\",\n price: \"Custom\",\n unitJa: \"\",\n unitEn: \"\",\n descJa: \"大規模・コンプライアンス要件に。\",\n descEn: \"For scale and compliance needs.\",\n features: [\n { ja: \"無制限リクエスト\", en: \"Unlimited requests\" },\n { ja: \"SLA 99.99%\", en: \"99.99% SLA\" },\n { ja: \"専任エンジニア\", en: \"Dedicated engineer\" },\n { ja: \"SSO / SAML\", en: \"SSO / SAML\" },\n { ja: \"監査ログ\", en: \"Audit logs\" },\n ],\n ctaJa: \"問い合わせ\",\n ctaEn: \"Contact us\",\n },\n];\n\nexport default function DevPricing() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n \n Pricing\n \n

\n {en ? \"Pricing that scales with you\" : \"スケールに合わせた料金\"}\n

\n

\n {en ? \"Pay for what you use. No hidden costs.\" : \"使った分だけ。隠れたコストはありません。\"}\n

\n
\n\n
\n {PLANS.map((p) => (\n \n {p.featured && (\n <>\n
\n \n \n {en ? \"Popular\" : \"人気\"}\n \n \n )}\n

\n {p.name}\n

\n
\n {p.price}\n {en ? p.unitEn : p.unitJa}\n
\n

{en ? p.descEn : p.descJa}

\n \n {en ? p.ctaEn : p.ctaJa}\n \n
    \n {p.features.map((f) => (\n
  • \n \n {en ? f.en : f.ja}\n
  • \n ))}\n
\n
\n ))}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n Pricing\n \n

\n スケールに合わせた料金\n

\n

\n 使った分だけ。隠れたコストはありません。\n

\n
\n
\n
\n

\n Hobby\n

\n
\n \n $0\n \n \n /月\n \n
\n

\n 個人プロジェクトと試作向け。\n

\n \n
    \n
  • \n \n \n \n \n 100K リクエスト/月\n
  • \n
  • \n \n \n \n \n 1 プロジェクト\n
  • \n
  • \n \n \n \n \n コミュニティサポート\n
  • \n
  • \n \n \n \n \n 共有ランタイム\n
  • \n
\n
\n
\n
\n
\n \n \n \n \n \n 人気\n \n

\n Pro\n

\n
\n \n $20\n \n \n /月\n \n
\n

\n 本番環境のチームに最適。\n

\n \n
    \n
  • \n \n \n \n \n 5M リクエスト/月\n
  • \n
  • \n \n \n \n \n 無制限プロジェクト\n
  • \n
  • \n \n \n \n \n 優先サポート\n
  • \n
  • \n \n \n \n \n エッジランタイム\n
  • \n
  • \n \n \n \n \n カスタムドメイン\n
  • \n
\n
\n
\n

\n Enterprise\n

\n
\n \n Custom\n \n \n \n
\n

\n 大規模・コンプライアンス要件に。\n

\n \n
    \n
  • \n \n \n \n \n 無制限リクエスト\n
  • \n
  • \n \n \n \n \n SLA 99.99%\n
  • \n
  • \n \n \n \n \n 専任エンジニア\n
  • \n
  • \n \n \n \n \n SSO / SAML\n
  • \n
  • \n \n \n \n \n 監査ログ\n
  • \n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n Pricing\n \n

\n スケールに合わせた料金\n

\n

\n 使った分だけ。隠れたコストはありません。\n

\n
\n
\n
\n

\n Hobby\n

\n
\n \n $0\n \n \n /月\n \n
\n

\n 個人プロジェクトと試作向け。\n

\n \n
    \n
  • \n \n \n \n \n 100K リクエスト/月\n
  • \n
  • \n \n \n \n \n 1 プロジェクト\n
  • \n
  • \n \n \n \n \n コミュニティサポート\n
  • \n
  • \n \n \n \n \n 共有ランタイム\n
  • \n
\n
\n
\n
\n
\n \n \n \n \n \n 人気\n \n

\n Pro\n

\n
\n \n $20\n \n \n /月\n \n
\n

\n 本番環境のチームに最適。\n

\n \n
    \n
  • \n \n \n \n \n 5M リクエスト/月\n
  • \n
  • \n \n \n \n \n 無制限プロジェクト\n
  • \n
  • \n \n \n \n \n 優先サポート\n
  • \n
  • \n \n \n \n \n エッジランタイム\n
  • \n
  • \n \n \n \n \n カスタムドメイン\n
  • \n
\n
\n
\n

\n Enterprise\n

\n
\n \n Custom\n \n \n \n
\n

\n 大規模・コンプライアンス要件に。\n

\n \n
    \n
  • \n \n \n \n \n 無制限リクエスト\n
  • \n
  • \n \n \n \n \n SLA 99.99%\n
  • \n
  • \n \n \n \n \n 専任エンジニア\n
  • \n
  • \n \n \n \n \n SSO / SAML\n
  • \n
  • \n \n \n \n \n 監査ログ\n
  • \n
\n
\n
\n
\n
\n \n" }, { "id": "darktech-dev-stats", @@ -604,7 +604,7 @@ ], "standalone": true, "source": "import { Activity, Gauge, ServerCog, TrendingUp, TrendingDown } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"デベロッパー統計\",\n category: \"ダークテック\",\n description: \"スパークラインSVG付きのメトリクスタイル(リクエスト・レイテンシ等)。\",\n align: \"full\",\n isNew: true,\n tags: [\"dark\", \"developer\", \"tech\"],\n};\n\nfunction Sparkline({ points, color }: { points: number[]; color: string }) {\n const w = 120;\n const h = 36;\n const max = Math.max(...points);\n const min = Math.min(...points);\n const range = max - min || 1;\n const step = w / (points.length - 1);\n const coords = points.map((p, i) => [i * step, h - ((p - min) / range) * h]);\n const line = coords.map(([x, y], i) => `${i === 0 ? \"M\" : \"L\"}${x},${y}`).join(\" \");\n const area = `${line} L${w},${h} L0,${h} Z`;\n const id = `g-${color.replace(/[^a-z]/gi, \"\")}-${points[0]}`;\n\n return (\n \n \n \n \n \n \n \n \n \n \n );\n}\n\ntype Tile = {\n label: string;\n labelEn: string;\n value: string;\n delta: string;\n up: boolean;\n good: boolean;\n icon: typeof Activity;\n color: string;\n data: number[];\n};\n\nconst TILES: Tile[] = [\n {\n label: \"リクエスト\",\n labelEn: \"Requests\",\n value: \"2.84M\",\n delta: \"+12.4%\",\n up: true,\n good: true,\n icon: Activity,\n color: \"#34d399\",\n data: [12, 18, 14, 22, 19, 28, 24, 31, 27, 36],\n },\n {\n label: \"p95 レイテンシ\",\n labelEn: \"p95 latency\",\n value: \"42ms\",\n delta: \"-8.1%\",\n up: false,\n good: true,\n icon: Gauge,\n color: \"#22d3ee\",\n data: [60, 55, 58, 50, 48, 52, 45, 43, 44, 42],\n },\n {\n label: \"エラー率\",\n labelEn: \"Error rate\",\n value: \"0.03%\",\n delta: \"+0.4%\",\n up: true,\n good: false,\n icon: ServerCog,\n color: \"#a78bfa\",\n data: [4, 3, 5, 3, 4, 2, 3, 4, 3, 5],\n },\n {\n label: \"稼働率\",\n labelEn: \"Uptime\",\n value: \"99.98%\",\n delta: \"+0.02%\",\n up: true,\n good: true,\n icon: TrendingUp,\n color: \"#fbbf24\",\n data: [98, 99, 98, 99, 99, 100, 99, 100, 99, 100],\n },\n];\n\nexport default function DevStats() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n \n Live metrics\n \n {en ? \"Last 24 hours\" : \"過去24時間\"}\n
\n
\n {TILES.map((t) => {\n const Icon = t.icon;\n const Trend = t.up ? TrendingUp : TrendingDown;\n return (\n \n
\n {en ? t.labelEn : t.label}\n \n
\n
\n {t.value}\n \n \n {t.delta}\n \n
\n
\n \n
\n
\n );\n })}\n
\n
\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n \n Live metrics\n \n \n 過去24時間\n \n
\n
\n
\n
\n \n リクエスト\n \n \n \n \n \n
\n
\n \n 2.84M\n \n \n \n \n \n \n \n \n +12.4%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n p95 レイテンシ\n \n \n \n \n \n \n \n
\n
\n \n 42ms\n \n \n \n \n \n \n \n \n -8.1%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n エラー率\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 0.03%\n \n \n \n \n \n \n \n \n +0.4%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n 稼働率\n \n \n \n \n \n \n \n
\n
\n \n 99.98%\n \n \n \n \n \n \n \n \n +0.02%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n Live metrics\n \n \n 過去24時間\n \n
\n
\n
\n
\n \n リクエスト\n \n \n \n \n \n
\n
\n \n 2.84M\n \n \n \n \n \n \n \n \n +12.4%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n p95 レイテンシ\n \n \n \n \n \n \n \n
\n
\n \n 42ms\n \n \n \n \n \n \n \n \n -8.1%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n エラー率\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 0.03%\n \n \n \n \n \n \n \n \n +0.4%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n 稼働率\n \n \n \n \n \n \n \n
\n
\n \n 99.98%\n \n \n \n \n \n \n \n \n +0.02%\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n" }, { "id": "darktech-status-dashboard", @@ -618,7 +618,7 @@ ], "standalone": true, "source": "import { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ステータス・ダッシュボード\",\n category: \"ダークテック\",\n description: \"稼働状況とアップタイムバーを表示するサービスステータスボード。\",\n align: \"full\",\n isNew: true,\n tags: [\"dark\", \"developer\", \"tech\"],\n};\n\ntype Status = \"operational\" | \"degraded\" | \"outage\";\n\ntype Service = { name: string; status: Status; uptime: number; bars: Status[] };\n\nconst COLOR: Record = {\n operational: \"bg-emerald-400\",\n degraded: \"bg-amber-400\",\n outage: \"bg-rose-500\",\n};\nconst LABEL_JA: Record = {\n operational: \"稼働中\",\n degraded: \"低下\",\n outage: \"停止\",\n};\nconst LABEL_EN: Record = {\n operational: \"Operational\",\n degraded: \"Degraded\",\n outage: \"Outage\",\n};\nconst DOT: Record = {\n operational: \"text-emerald-400\",\n degraded: \"text-amber-400\",\n outage: \"text-rose-500\",\n};\n\nfunction makeBars(_seed: number, badAt: number[] = []): Status[] {\n return Array.from({ length: 40 }, (_, i) =>\n badAt.includes(i) ? (i % 7 === 0 ? \"outage\" : \"degraded\") : \"operational\"\n ).reverse();\n}\n\nconst SERVICES: Service[] = [\n { name: \"API Gateway\", status: \"operational\", uptime: 99.98, bars: makeBars(1) },\n { name: \"Edge Runtime\", status: \"operational\", uptime: 99.99, bars: makeBars(2) },\n { name: \"Database\", status: \"degraded\", uptime: 99.71, bars: makeBars(3, [3, 4, 5]) },\n { name: \"Auth\", status: \"operational\", uptime: 99.95, bars: makeBars(4) },\n { name: \"Storage (S3)\", status: \"outage\", uptime: 98.42, bars: makeBars(5, [0, 1, 2, 9]) },\n];\n\nexport default function StatusDashboard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const LABEL = en ? LABEL_EN : LABEL_JA;\n const allUp = SERVICES.every((s) => s.status === \"operational\");\n\n return (\n
\n
\n \n
\n \n \n \n \n \n {allUp\n ? en\n ? \"All systems operational\"\n : \"全システム正常稼働中\"\n : en\n ? \"Some services affected\"\n : \"一部のサービスに影響あり\"}\n \n
\n \n {en ? \"Updated: just now\" : \"最終更新: たった今\"}\n \n
\n\n
\n {SERVICES.map((s) => (\n
\n
\n
\n \n {s.name}\n
\n
\n \n {s.uptime.toFixed(2)}%\n \n \n {LABEL[s.status]}\n \n
\n
\n
\n {s.bars.map((b, i) => (\n \n ))}\n
\n
\n {en ? \"90 days ago\" : \"90日前\"}\n {en ? \"Today\" : \"今日\"}\n
\n
\n ))}\n
\n
\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n 一部のサービスに影響あり\n \n
\n \n 最終更新: たった今\n \n
\n
\n
\n
\n
\n \n ●\n \n \n API Gateway\n \n
\n
\n \n 99.98%\n \n \n 稼働中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Edge Runtime\n \n
\n
\n \n 99.99%\n \n \n 稼働中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Database\n \n
\n
\n \n 99.71%\n \n \n 低下\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Auth\n \n
\n
\n \n 99.95%\n \n \n 稼働中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Storage (S3)\n \n
\n
\n \n 98.42%\n \n \n 停止\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n 一部のサービスに影響あり\n \n
\n \n 最終更新: たった今\n \n
\n
\n
\n
\n
\n \n ●\n \n \n API Gateway\n \n
\n
\n \n 99.98%\n \n \n 稼働中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Edge Runtime\n \n
\n
\n \n 99.99%\n \n \n 稼働中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Database\n \n
\n
\n \n 99.71%\n \n \n 低下\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Auth\n \n
\n
\n \n 99.95%\n \n \n 稼働中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n ●\n \n \n Storage (S3)\n \n
\n
\n \n 98.42%\n \n \n 停止\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n 90日前\n \n \n 今日\n \n
\n
\n
\n
\n
\n \n" }, { "id": "feature2-animated-bento", @@ -632,7 +632,7 @@ ], "standalone": true, "source": "import { Sparkles, Zap, Shield, BarChart3, Globe, Layers } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アニメーション・ベント\",\n category: \"マーケティング\",\n description:\n \"サイズの異なるタイルが組み合わさったベントグリッド。ホバーで光彩が走り、登場時にふわっと立ち上がる。\",\n align: \"full\",\n isNew: true,\n tags: [\"feature\", \"bento\", \"animation\"],\n};\n\nconst TILES = [\n {\n icon: Sparkles,\n title: \"自動でひらめく\",\n titleEn: \"Insights on autopilot\",\n body: \"面倒な設定は不要。最初の5分で価値が伝わる体験を。\",\n bodyEn: \"No fiddly setup — feel the value within the first five minutes.\",\n span: \"sm:col-span-2 sm:row-span-2\",\n tone: \"from-violet-500/20 to-fuchsia-500/10\",\n },\n {\n icon: Zap,\n title: \"圧倒的な速さ\",\n titleEn: \"Blazing fast\",\n body: \"ミリ秒単位の応答。\",\n bodyEn: \"Millisecond responses.\",\n span: \"\",\n tone: \"from-amber-500/20 to-orange-500/10\",\n },\n {\n icon: Shield,\n title: \"堅牢な安全性\",\n titleEn: \"Rock-solid security\",\n body: \"暗号化を標準装備。\",\n bodyEn: \"Encryption built in.\",\n span: \"\",\n tone: \"from-emerald-500/20 to-teal-500/10\",\n },\n {\n icon: BarChart3,\n title: \"明快な分析\",\n titleEn: \"Clear analytics\",\n body: \"数字で意思決定を支える、リアルタイムのダッシュボード。\",\n bodyEn: \"Real-time dashboards that back decisions with numbers.\",\n span: \"sm:col-span-2\",\n tone: \"from-sky-500/20 to-cyan-500/10\",\n },\n {\n icon: Globe,\n title: \"世界中で稼働\",\n titleEn: \"Runs worldwide\",\n body: \"エッジ配信。\",\n bodyEn: \"Edge delivery.\",\n span: \"\",\n tone: \"from-indigo-500/20 to-blue-500/10\",\n },\n {\n icon: Layers,\n title: \"拡張は自在\",\n titleEn: \"Endlessly extensible\",\n body: \"プラグインで機能を足し算。\",\n bodyEn: \"Add features with plugins.\",\n span: \"\",\n tone: \"from-rose-500/20 to-pink-500/10\",\n },\n];\n\nexport default function AnimatedBento() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n

\n {en ? \"Every feature, at a glance.\" : \"必要な機能を、ひと目で。\"}\n

\n

\n {en\n ? \"Tiles with different roles combine to distill the product's appeal into a single screen.\"\n : \"役割の違うタイルを組み合わせ、製品の魅力を一画面に凝縮しました。\"}\n

\n
\n\n
\n {TILES.map((t, i) => {\n const Icon = t.icon;\n return (\n \n \n \n
\n \n \n \n

{en ? t.titleEn : t.title}

\n

{en ? t.bodyEn : t.body}

\n
\n
\n );\n })}\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n

\n 必要な機能を、ひと目で。\n

\n

\n 役割の違うタイルを組み合わせ、製品の魅力を一画面に凝縮しました。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n 自動でひらめく\n

\n

\n 面倒な設定は不要。最初の5分で価値が伝わる体験を。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n

\n 圧倒的な速さ\n

\n

\n ミリ秒単位の応答。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n

\n 堅牢な安全性\n

\n

\n 暗号化を標準装備。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n

\n 明快な分析\n

\n

\n 数字で意思決定を支える、リアルタイムのダッシュボード。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n 世界中で稼働\n

\n

\n エッジ配信。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n 拡張は自在\n

\n

\n プラグインで機能を足し算。\n

\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n

\n 必要な機能を、ひと目で。\n

\n

\n 役割の違うタイルを組み合わせ、製品の魅力を一画面に凝縮しました。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n 自動でひらめく\n

\n

\n 面倒な設定は不要。最初の5分で価値が伝わる体験を。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n

\n 圧倒的な速さ\n

\n

\n ミリ秒単位の応答。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n

\n 堅牢な安全性\n

\n

\n 暗号化を標準装備。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n

\n 明快な分析\n

\n

\n 数字で意思決定を支える、リアルタイムのダッシュボード。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n 世界中で稼働\n

\n

\n エッジ配信。\n

\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n 拡張は自在\n

\n

\n プラグインで機能を足し算。\n

\n
\n
\n
\n
\n
\n \n" }, { "id": "feature2-bento-with-mini-charts", @@ -646,7 +646,7 @@ ], "standalone": true, "source": "import type * as React from \"react\";\nimport { TrendingUp, Users, Activity, DollarSign } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ミニチャート付きベント\",\n category: \"マーケティング\",\n description:\n \"インラインSVGの折れ線・棒・ドーナツ・スパークラインを並べた、データ志向のベントグリッド。\",\n align: \"full\",\n isNew: true,\n tags: [\"feature\", \"bento\", \"animation\"],\n};\n\nfunction Line() {\n const pts = [8, 20, 14, 28, 22, 36, 30, 46];\n const w = 200,\n h = 64;\n const max = Math.max(...pts);\n const d = pts\n .map((p, i) => {\n const x = (i / (pts.length - 1)) * w;\n const y = h - (p / max) * h;\n return `${i === 0 ? \"M\" : \"L\"}${x.toFixed(1)},${y.toFixed(1)}`;\n })\n .join(\" \");\n return (\n \n \n \n \n \n \n \n \n \n \n );\n}\n\nfunction Bars() {\n const bars = [40, 65, 50, 80, 60, 95];\n return (\n
\n {bars.map((b, i) => (\n \n ))}\n
\n );\n}\n\nfunction Donut() {\n const r = 26,\n c = 2 * Math.PI * r;\n const pct = 0.72;\n return (\n \n \n \n \n 72%\n \n \n );\n}\n\nfunction Spark() {\n const pts = [10, 14, 9, 18, 13, 22, 19, 28];\n const w = 120,\n h = 40,\n max = Math.max(...pts);\n const d = pts\n .map((p, i) => `${i === 0 ? \"M\" : \"L\"}${(i / (pts.length - 1)) * w},${h - (p / max) * h}`)\n .join(\" \");\n return (\n \n \n \n );\n}\n\nconst STATS = [\n { icon: TrendingUp, label: \"月間成長\", labelEn: \"Monthly growth\", value: \"+38%\" },\n { icon: Users, label: \"アクティブ\", labelEn: \"Active users\", value: \"12,480\" },\n { icon: Activity, label: \"稼働率\", labelEn: \"Uptime\", value: \"99.98%\" },\n { icon: DollarSign, label: \"売上\", labelEn: \"Revenue\", value: \"¥4.2M\" },\n];\n\nexport default function BentoWithMiniCharts() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n

\n {en ? \"Your data starts to speak.\" : \"データが、語りはじめる。\"}\n

\n

\n {en\n ? \"The metrics that matter, in clear visuals on a single screen.\"\n : \"重要な指標を、見やすいビジュアルで一画面に。\"}\n

\n
\n\n
\n
\n
\n \n {en ? \"Traffic trend\" : \"トラフィック推移\"}\n \n +24%\n
\n

84.2k

\n
\n \n
\n
\n\n
\n \n {en ? \"Weekly activity\" : \"週間アクティビティ\"}\n \n
\n \n
\n
\n\n
\n \n \n {en ? \"Goal completion\" : \"目標達成率\"}\n \n
\n\n
\n \n {en ? \"Conversion rate\" : \"変換率\"}\n \n

6.4%

\n
\n \n
\n
\n\n {STATS.map((s) => {\n const Icon = s.icon;\n return (\n \n \n \n \n
\n

{en ? s.labelEn : s.label}

\n

{s.value}

\n
\n
\n );\n })}\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n

\n データが、語りはじめる。\n

\n

\n 重要な指標を、見やすいビジュアルで一画面に。\n

\n
\n
\n
\n
\n \n トラフィック推移\n \n \n +24%\n \n
\n

\n 84.2k\n

\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n 週間アクティビティ\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n 72%\n \n \n \n 目標達成率\n \n
\n
\n \n 変換率\n \n

\n 6.4%\n

\n
\n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n 月間成長\n

\n

\n +38%\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n アクティブ\n

\n

\n 12,480\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 稼働率\n

\n

\n 99.98%\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n 売上\n

\n

\n ¥4.2M\n

\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n

\n データが、語りはじめる。\n

\n

\n 重要な指標を、見やすいビジュアルで一画面に。\n

\n
\n
\n
\n
\n \n トラフィック推移\n \n \n +24%\n \n
\n

\n 84.2k\n

\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n 週間アクティビティ\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n 72%\n \n \n \n 目標達成率\n \n
\n
\n \n 変換率\n \n

\n 6.4%\n

\n
\n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n 月間成長\n

\n

\n +38%\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n アクティブ\n

\n

\n 12,480\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 稼働率\n

\n

\n 99.98%\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n 売上\n

\n

\n ¥4.2M\n

\n
\n
\n
\n
\n
\n \n" }, { "id": "feature2-comparison-us-vs-them", @@ -660,7 +660,7 @@ ], "standalone": true, "source": "import { Check, X, Sparkles } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"他社比較テーブル\",\n category: \"マーケティング\",\n description:\n \"自社と他社を並べた比較表。自社列がハイライトされ、チェックが順に灯る演出付き。\",\n align: \"full\",\n isNew: true,\n tags: [\"feature\", \"bento\", \"animation\"],\n};\n\nconst ROWS = [\n {\n id: \"setup\",\n labelJa: \"セットアップ時間\",\n labelEn: \"Setup time\",\n us: { ja: \"5分\", en: \"5 min\" },\n them: { ja: \"数日\", en: \"Days\" },\n },\n {\n id: \"members\",\n labelJa: \"無制限メンバー\",\n labelEn: \"Unlimited members\",\n us: true,\n them: false,\n },\n {\n id: \"sync\",\n labelJa: \"リアルタイム同期\",\n labelEn: \"Real-time sync\",\n us: true,\n them: true,\n },\n {\n id: \"automation\",\n labelJa: \"高度な自動化\",\n labelEn: \"Advanced automation\",\n us: true,\n them: false,\n },\n {\n id: \"support\",\n labelJa: \"24時間サポート\",\n labelEn: \"24/7 support\",\n us: true,\n them: false,\n },\n {\n id: \"cap\",\n labelJa: \"従量課金の上限\",\n labelEn: \"Usage cap\",\n us: { ja: \"なし\", en: \"None\" },\n them: { ja: \"あり\", en: \"Yes\" },\n },\n];\n\nfunction Cell({ value }: { value: boolean | { ja: string; en: string } }) {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n if (typeof value === \"object\")\n return (\n {en ? value.en : value.ja}\n );\n return value ? (\n \n \n \n ) : (\n \n \n \n );\n}\n\nexport default function ComparisonUsVsThem() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n

\n {en ? \"Why teams choose us.\" : \"なぜ、選ばれるのか。\"}\n

\n

\n {en\n ? \"The difference, made clear with facts and figures.\"\n : \"数字と事実で、違いをはっきりと。\"}\n

\n
\n\n
\n
\n \n {en ? \"Feature\" : \"機能\"}\n \n \n \n {en ? \"Us\" : \"自社\"}\n \n \n {en ? \"Others\" : \"他社\"}\n \n
\n\n {ROWS.map((r, i) => (\n \n \n {en ? r.labelEn : r.labelJa}\n \n \n \n
\n
\n \n
\n
\n ))}\n \n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n

\n なぜ、選ばれるのか。\n

\n

\n 数字と事実で、違いをはっきりと。\n

\n
\n
\n
\n \n 機能\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 自社\n \n \n 他社\n \n
\n
\n \n セットアップ時間\n \n
\n \n 5分\n \n
\n
\n \n 数日\n \n
\n
\n
\n \n 無制限メンバー\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n \n リアルタイム同期\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n
\n \n 高度な自動化\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n \n 24時間サポート\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n \n 従量課金の上限\n \n
\n \n なし\n \n
\n
\n \n あり\n \n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n

\n なぜ、選ばれるのか。\n

\n

\n 数字と事実で、違いをはっきりと。\n

\n
\n
\n
\n \n 機能\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 自社\n \n \n 他社\n \n
\n
\n \n セットアップ時間\n \n
\n \n 5分\n \n
\n
\n \n 数日\n \n
\n
\n
\n \n 無制限メンバー\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n \n リアルタイム同期\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n
\n \n 高度な自動化\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n \n 24時間サポート\n \n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n \n 従量課金の上限\n \n
\n \n なし\n \n
\n
\n \n あり\n \n
\n
\n
\n
\n
\n \n" }, { "id": "feature2-feature-spotlight", @@ -674,7 +674,7 @@ ], "standalone": true, "source": "import { Cpu, Gauge, Layers3, ShieldCheck } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"主役機能スポットライト\",\n category: \"マーケティング\",\n description:\n \"ひとつの主力機能を大きく据え、周囲に補助機能を配置。中央に呼吸する光のスポットライト。\",\n align: \"full\",\n isNew: true,\n tags: [\"feature\", \"bento\", \"animation\"],\n};\n\nconst SUB = [\n {\n icon: Gauge,\n titleJa: \"高速処理\",\n titleEn: \"High speed\",\n bodyJa: \"ミリ秒単位の応答性能。\",\n bodyEn: \"Millisecond response times.\",\n },\n {\n icon: Layers3,\n titleJa: \"拡張性\",\n titleEn: \"Scalability\",\n bodyJa: \"規模に合わせて自在に。\",\n bodyEn: \"Scales freely with your needs.\",\n },\n {\n icon: ShieldCheck,\n titleJa: \"安全性\",\n titleEn: \"Security\",\n bodyJa: \"標準で暗号化を実装。\",\n bodyEn: \"Encryption built in by default.\",\n },\n];\n\nexport default function FeatureSpotlight() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n
\n \n\n
\n \n \n \n

\n {en\n ? \"One engine powers everything.\"\n : \"ひとつのエンジンが、すべてを動かす。\"}\n

\n

\n {en\n ? \"A purpose-built core supports every feature behind the scenes — fast, and always on.\"\n : \"独自の処理基盤が、あらゆる機能を裏側で支えます。だから速くて、止まらない。\"}\n

\n
\n\n
\n {SUB.map((s) => {\n const Icon = s.icon;\n return (\n \n \n \n \n

\n {en ? s.titleEn : s.titleJa}\n

\n

\n {en ? s.bodyEn : s.bodyJa}\n

\n
\n );\n })}\n
\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n ひとつのエンジンが、すべてを動かす。\n

\n

\n 独自の処理基盤が、あらゆる機能を裏側で支えます。だから速くて、止まらない。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n

\n 高速処理\n

\n

\n ミリ秒単位の応答性能。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n 拡張性\n

\n

\n 規模に合わせて自在に。\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 安全性\n

\n

\n 標準で暗号化を実装。\n

\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n

\n ひとつのエンジンが、すべてを動かす。\n

\n

\n 独自の処理基盤が、あらゆる機能を裏側で支えます。だから速くて、止まらない。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n

\n 高速処理\n

\n

\n ミリ秒単位の応答性能。\n

\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n 拡張性\n

\n

\n 規模に合わせて自在に。\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 安全性\n

\n

\n 標準で暗号化を実装。\n

\n
\n
\n
\n
\n
\n \n" }, { "id": "glass-glass-card", @@ -688,7 +688,7 @@ ], "standalone": true, "source": "import { ArrowUpRight, Layers } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グラス・カード\",\n category: \"グラスモーフィズム\",\n description: \"サンセット調の背景に映える、上品な単体フロステッドガラスカード。\",\n align: \"center\",\n isNew: true,\n tags: [\"glass\", \"frosted\", \"card\"],\n};\n\nexport default function GlassCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n \n\n
\n
\n \n
\n\n

\n {en ? \"Layered design\" : \"レイヤード・デザイン\"}\n

\n

\n {en\n ? \"Stacking translucent glass layers lets the background colors shine through while keeping the information hierarchy clear.\"\n : \"半透明のガラス層を重ねることで、背景の色彩を活かしながら情報の階層を明確に表現します。\"}\n

\n\n \n {en ? \"Learn more\" : \"もっと知る\"}\n \n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n レイヤード・デザイン\n

\n

\n 半透明のガラス層を重ねることで、背景の色彩を活かしながら情報の階層を明確に表現します。\n

\n \n もっと知る\n \n \n \n \n \n \n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n レイヤード・デザイン\n

\n

\n 半透明のガラス層を重ねることで、背景の色彩を活かしながら情報の階層を明確に表現します。\n

\n \n もっと知る\n \n \n \n \n \n \n \n
\n
\n \n" }, { "id": "glass-glass-cta", @@ -702,7 +702,7 @@ ], "standalone": true, "source": "import { ArrowRight, Rocket } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グラス・CTAバンド\",\n category: \"グラスモーフィズム\",\n description: \"鮮烈なグラデーションの上に、フロステッドガラスのCTAバンドを配置。\",\n align: \"full\",\n isNew: true,\n tags: [\"glass\", \"frosted\", \"cta\"],\n};\n\nexport default function GlassCTA() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n \n\n
\n
\n \n \n {en ? \"Get started now\" : \"今すぐ始めよう\"}\n \n\n

\n {en ? (\n <>\n Give your next project\n
\n the best experience.\n \n ) : (\n <>\n 次のプロジェクトを、\n
\n 最高の体験で。\n \n )}\n

\n\n

\n {en\n ? \"No credit card required. Set up in minutes and start building frosted UIs right away.\"\n : \"クレジットカード不要。数分でセットアップし、すぐにフロステッドなUIを構築できます。\"}\n

\n\n
\n \n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n 今すぐ始めよう\n \n

\n 次のプロジェクトを、\n
\n 最高の体験で。\n

\n

\n クレジットカード不要。数分でセットアップし、すぐにフロステッドなUIを構築できます。\n

\n
\n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n 今すぐ始めよう\n \n

\n 次のプロジェクトを、\n
\n 最高の体験で。\n

\n

\n クレジットカード不要。数分でセットアップし、すぐにフロステッドなUIを構築できます。\n

\n
\n \n \n
\n
\n
\n
\n \n" }, { "id": "glass-glass-feature", @@ -716,7 +716,7 @@ ], "standalone": true, "source": "import { Gauge, Lock, Palette, Workflow } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グラス・機能セクション\",\n category: \"グラスモーフィズム\",\n description: \"アクア〜パープルの背景にフロステッドガラスの機能カードを並べたセクション。\",\n align: \"full\",\n isNew: true,\n tags: [\"glass\", \"frosted\", \"feature\"],\n};\n\nconst features = [\n {\n icon: Gauge,\n titleJa: \"圧倒的な高速性\",\n titleEn: \"Blazing fast\",\n bodyJa: \"エッジ最適化により、世界中どこからでもミリ秒単位の応答を実現します。\",\n bodyEn: \"Edge optimization delivers millisecond responses from anywhere in the world.\",\n },\n {\n icon: Lock,\n titleJa: \"堅牢なセキュリティ\",\n titleEn: \"Rock-solid security\",\n bodyJa: \"エンドツーエンド暗号化と細やかな権限制御で、データを守ります。\",\n bodyEn: \"End-to-end encryption and granular permissions keep your data safe.\",\n },\n {\n icon: Palette,\n titleJa: \"美しいデザイン\",\n titleEn: \"Beautiful design\",\n bodyJa: \"細部までこだわったコンポーネントで、洗練された体験を構築できます。\",\n bodyEn: \"Meticulously crafted components let you build a refined experience.\",\n },\n {\n icon: Workflow,\n titleJa: \"柔軟なワークフロー\",\n titleEn: \"Flexible workflow\",\n bodyJa: \"あらゆるツールと連携し、チームの生産性を最大化します。\",\n bodyEn: \"Integrates with any tool to maximize your team's productivity.\",\n },\n];\n\nexport default function GlassFeature() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n \n\n
\n
\n

\n {en ? \"One platform, everything you need\" : \"すべてが揃った、ひとつの基盤\"}\n

\n

\n {en\n ? \"From development to deploy to operations — every feature, in a frosted experience.\"\n : \"開発からデプロイ、運用まで。必要な機能をフロステッドな体験で。\"}\n

\n
\n\n
\n {features.map((f) => (\n \n
\n \n
\n
\n

{en ? f.titleEn : f.titleJa}

\n

\n {en ? f.bodyEn : f.bodyJa}\n

\n
\n
\n ))}\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n

\n すべてが揃った、ひとつの基盤\n

\n

\n 開発からデプロイ、運用まで。必要な機能をフロステッドな体験で。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n

\n 圧倒的な高速性\n

\n

\n エッジ最適化により、世界中どこからでもミリ秒単位の応答を実現します。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n

\n 堅牢なセキュリティ\n

\n

\n エンドツーエンド暗号化と細やかな権限制御で、データを守ります。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n 美しいデザイン\n

\n

\n 細部までこだわったコンポーネントで、洗練された体験を構築できます。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n
\n

\n 柔軟なワークフロー\n

\n

\n あらゆるツールと連携し、チームの生産性を最大化します。\n

\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n

\n すべてが揃った、ひとつの基盤\n

\n

\n 開発からデプロイ、運用まで。必要な機能をフロステッドな体験で。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n

\n 圧倒的な高速性\n

\n

\n エッジ最適化により、世界中どこからでもミリ秒単位の応答を実現します。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n

\n 堅牢なセキュリティ\n

\n

\n エンドツーエンド暗号化と細やかな権限制御で、データを守ります。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n 美しいデザイン\n

\n

\n 細部までこだわったコンポーネントで、洗練された体験を構築できます。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n
\n

\n 柔軟なワークフロー\n

\n

\n あらゆるツールと連携し、チームの生産性を最大化します。\n

\n
\n
\n
\n
\n
\n \n" }, { "id": "glass-glass-hero", @@ -731,7 +731,7 @@ ], "standalone": true, "source": "import { ArrowRight, Sparkles } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グラス・ヒーロー\",\n category: \"グラスモーフィズム\",\n description: \"鮮やかなグラデーションとぼかしブロブの上にフロステッドガラスのカードを重ねたヒーロー。\",\n align: \"full\",\n isNew: true,\n tags: [\"glass\", \"frosted\", \"blur\", \"hero\"],\n};\n\nexport default function GlassHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* color blobs */}\n \n \n \n\n
\n
\n \n \n {en ? \"A new design experience\" : \"新しいデザイン体験\"}\n \n\n

\n {en ? (\n <>\n A translucent,\n
\n next-generation interface.\n \n ) : (\n <>\n 透明感のある、\n
\n 次世代のインターフェース。\n \n )}\n

\n\n

\n {en\n ? \"Frosted glass textures combine depth and refinement. Build UIs that elevate your content, instantly.\"\n : \"フロステッドガラスの質感で、奥行きと洗練を両立。コンテンツを引き立てるUIを、すぐに。\"}\n

\n\n
\n \n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n 新しいデザイン体験\n \n

\n 透明感のある、\n
\n 次世代のインターフェース。\n

\n

\n フロステッドガラスの質感で、奥行きと洗練を両立。コンテンツを引き立てるUIを、すぐに。\n

\n
\n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n 新しいデザイン体験\n \n

\n 透明感のある、\n
\n 次世代のインターフェース。\n

\n

\n フロステッドガラスの質感で、奥行きと洗練を両立。コンテンツを引き立てるUIを、すぐに。\n

\n
\n \n \n
\n
\n
\n
\n \n" }, { "id": "hero-anim-animated-mesh-hero", @@ -746,7 +746,7 @@ ], "standalone": true, "source": "import { ArrowRight, Wand2 } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"メッシュ・ヒーロー\",\n category: \"ヒーロー・LP\",\n description: \"ぼかしたグラデーションのブロブがゆっくり変形・移動するメッシュ背景。\",\n align: \"full\",\n isNew: true,\n tags: [\"hero\", \"animation\", \"mesh\", \"gradient\"],\n};\n\nexport default function AnimatedMeshHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n \n \n \n \n
\n
\n\n
\n \n \n {en ? \"AI-native design\" : \"AIネイティブ・デザイン\"}\n \n

\n {en ? (\n <>\n Where colors blend,\n
\n a canvas for creation\n \n ) : (\n <>\n 色が溶け合う、\n
\n 創造のためのキャンバス\n \n )}\n

\n

\n {en\n ? \"A smoothly morphing gradient mesh — giving your ideas room to breathe.\"\n : \"滑らかに変形するグラデーションメッシュ。あなたのアイデアに、空気のような余白を。\"}\n

\n
\n \n {en ? \"Start creating\" : \"作りはじめる\"}\n \n \n \n {en ? \"Gallery\" : \"ギャラリー\"}\n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n AIネイティブ・デザイン\n \n

\n 色が溶け合う、\n
\n 創造のためのキャンバス\n

\n

\n 滑らかに変形するグラデーションメッシュ。あなたのアイデアに、空気のような余白を。\n

\n
\n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n AIネイティブ・デザイン\n \n

\n 色が溶け合う、\n
\n 創造のためのキャンバス\n

\n

\n 滑らかに変形するグラデーションメッシュ。あなたのアイデアに、空気のような余白を。\n

\n
\n \n \n
\n
\n
\n \n" }, { "id": "hero-anim-aurora-hero", @@ -761,7 +761,7 @@ ], "standalone": true, "source": "import { ArrowRight, Sparkles } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"オーロラ・ヒーロー\",\n category: \"ヒーロー・LP\",\n description: \"中央のヒーローの背後でオーロラ状のグラデーションがゆっくり漂う。\",\n align: \"full\",\n isNew: true,\n tags: [\"hero\", \"animation\", \"aurora\", \"gradient\"],\n};\n\nexport default function AuroraHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n \n \n \n
\n\n \n\n
\n \n \n {en ? \"v2.0 released\" : \"v2.0 リリース\"}\n \n \n {en ? (\n <>\n Where light shimmers,\n
\n a next-gen product experience\n \n ) : (\n <>\n 光がゆらめく、\n
\n 次世代のプロダクト体験\n \n )}\n \n \n {en\n ? \"Behind drifting aurora-like gradients, bring your team's work into one place.\"\n : \"オーロラのように漂うグラデーションの背後で、チームの仕事を一つの場所に。\"}\n

\n \n \n {en ? \"Start for free\" : \"無料で始める\"}\n \n \n \n {en ? \"Watch demo\" : \"デモを見る\"}\n \n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n v2.0 リリース\n \n

\n 光がゆらめく、\n
\n 次世代のプロダクト体験\n

\n

\n オーロラのように漂うグラデーションの背後で、チームの仕事を一つの場所に。\n

\n
\n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n v2.0 リリース\n \n

\n 光がゆらめく、\n
\n 次世代のプロダクト体験\n

\n

\n オーロラのように漂うグラデーションの背後で、チームの仕事を一つの場所に。\n

\n
\n \n \n
\n
\n
\n \n" }, { "id": "hero-anim-bento-hero", @@ -775,7 +775,7 @@ ], "standalone": true, "source": "import {\n ArrowRight,\n Activity,\n Shield,\n Sparkles,\n Globe2,\n Zap,\n} from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ベント・ヒーロー\",\n category: \"ヒーロー・LP\",\n description: \"左にコピー、右に発光・ホバーするアニメーション付きベントグリッド。\",\n align: \"full\",\n isNew: true,\n tags: [\"hero\", \"animation\", \"bento\"],\n};\n\nconst tiles = [\n {\n icon: Activity,\n ja: \"リアルタイム分析\",\n en: \"Real-time analytics\",\n span: \"col-span-2\",\n accent: \"rgba(56,189,248,0.5)\",\n delay: \"0s\",\n },\n {\n icon: Shield,\n ja: \"セキュア\",\n en: \"Secure\",\n span: \"\",\n accent: \"rgba(52,211,153,0.5)\",\n delay: \"0.6s\",\n },\n {\n icon: Zap,\n ja: \"高速\",\n en: \"Fast\",\n span: \"\",\n accent: \"rgba(251,191,36,0.5)\",\n delay: \"1.2s\",\n },\n {\n icon: Globe2,\n ja: \"グローバル配信\",\n en: \"Global delivery\",\n span: \"col-span-2\",\n accent: \"rgba(168,85,247,0.5)\",\n delay: \"1.8s\",\n },\n];\n\nexport default function BentoHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n \n\n
\n
\n \n \n {en ? \"All-in-one platform\" : \"オールインワン・プラットフォーム\"}\n \n

\n {en ? (\n <>\n Every feature you need,\n
\n on a single screen\n \n ) : (\n <>\n 必要な機能が、\n
\n ひとつの画面に\n \n )}\n

\n

\n {en\n ? \"Analytics, security, and delivery unified — bringing scattered tools together as one.\"\n : \"分析・セキュリティ・配信を統合。バラバラなツールを一つにまとめます。\"}\n

\n
\n \n {en ? \"Start for free\" : \"無料で始める\"}\n \n \n \n {en ? \"All features\" : \"機能一覧\"}\n \n
\n
\n\n
\n {tiles.map((t) => {\n const Icon = t.icon;\n return (\n \n \n \n

\n {en ? t.en : t.ja}\n

\n
\n );\n })}\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n オールインワン・プラットフォーム\n \n

\n 必要な機能が、\n
\n ひとつの画面に\n

\n

\n 分析・セキュリティ・配信を統合。バラバラなツールを一つにまとめます。\n

\n
\n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n

\n リアルタイム分析\n

\n
\n
\n
\n
\n \n \n \n \n

\n セキュア\n

\n
\n
\n
\n
\n \n \n \n \n

\n 高速\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n グローバル配信\n

\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n オールインワン・プラットフォーム\n \n

\n 必要な機能が、\n
\n ひとつの画面に\n

\n

\n 分析・セキュリティ・配信を統合。バラバラなツールを一つにまとめます。\n

\n
\n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n

\n リアルタイム分析\n

\n
\n
\n
\n
\n \n \n \n \n

\n セキュア\n

\n
\n
\n
\n
\n \n \n \n \n

\n 高速\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n

\n グローバル配信\n

\n
\n
\n
\n
\n \n" }, { "id": "hero-anim-gradient-shift-hero", @@ -789,7 +789,7 @@ ], "standalone": true, "source": "import { ArrowRight, Flame } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"グラデーションシフト・ヒーロー\",\n category: \"ヒーロー・LP\",\n description: \"背景全体のグラデーションが滑らかに循環し、中央コピーを際立たせる。\",\n align: \"full\",\n isNew: true,\n tags: [\"hero\", \"animation\", \"gradient\"],\n};\n\nexport default function GradientShiftHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n \n
\n \n\n
\n \n \n {en ? \"The most talked-about product right now\" : \"いま、最も話題のプロダクト\"}\n \n

\n {en ? (\n <>\n Colors in motion,\n
\n a vivid brand experience\n \n ) : (\n <>\n 色が巡る、\n
\n 鮮やかなブランド体験\n \n )}\n

\n

\n {en\n ? \"Against a cycling gradient, bring your message crisply to the front.\"\n : \"循環するグラデーションを背に、伝えたいメッセージをくっきりと前面に。\"}\n

\n
\n \n {en ? \"Get started\" : \"はじめる\"}\n \n \n \n {en ? \"Learn more\" : \"もっと見る\"}\n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n いま、最も話題のプロダクト\n \n

\n 色が巡る、\n
\n 鮮やかなブランド体験\n

\n

\n 循環するグラデーションを背に、伝えたいメッセージをくっきりと前面に。\n

\n
\n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n いま、最も話題のプロダクト\n \n

\n 色が巡る、\n
\n 鮮やかなブランド体験\n

\n

\n 循環するグラデーションを背に、伝えたいメッセージをくっきりと前面に。\n

\n
\n \n \n
\n
\n
\n \n" }, { "id": "interaction-marquee", @@ -803,7 +803,7 @@ ], "standalone": true, "source": "import { Sparkles } from \"lucide-react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"マーキー\",\n category: \"インタラクション\",\n description: \"ホバーで一時停止する無限スクロール。ロゴや告知に。\",\n align: \"full\",\n isNew: true,\n tags: [\"animation\", \"micro-interaction\", \"marquee\"],\n principle:\n \"途切れない動きは視線を引きつけつつ圧迫感が少ない。ホバーで止まることで読みたい情報をユーザーが制御できる。\",\n};\n\nconst ITEMS = [\n \"Acme Corp\",\n \"Globex\",\n \"Initech\",\n \"Umbrella\",\n \"Soylent\",\n \"Hooli\",\n \"Stark Industries\",\n \"Wayne Enterprises\",\n];\n\nfunction Track({ ariaHidden }: { ariaHidden?: boolean }) {\n return (\n \n {ITEMS.map((label, i) => (\n \n \n {label}\n \n ))}\n \n );\n}\n\nexport default function Marquee() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n \n {en ? \"Trusted partners\" : \"信頼されるパートナー\"}\n \n
\n
\n
\n \n \n
\n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n 信頼されるパートナー\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Acme Corp\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Globex\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Initech\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Umbrella\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Soylent\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Hooli\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Stark Industries\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Wayne Enterprises\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Acme Corp\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Globex\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Initech\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Umbrella\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Soylent\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Hooli\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Stark Industries\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Wayne Enterprises\n
\n
\n
\n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n 信頼されるパートナー\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Acme Corp\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Globex\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Initech\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Umbrella\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Soylent\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Hooli\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Stark Industries\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Wayne Enterprises\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Acme Corp\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Globex\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Initech\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Umbrella\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Soylent\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Hooli\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Stark Industries\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n Wayne Enterprises\n
\n
\n
\n \n
\n
\n \n" }, { "id": "loaders-morphing-loader", @@ -818,7 +818,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"モーフィングローダー\",\n category: \"ローダー・マイクロ\",\n description: \"四角↔円↔三角形へ変形する形状モーフローダー(clip-path/transform)。\",\n align: \"center\",\n isNew: true,\n tags: [\"loader\", \"animation\", \"micro\", \"morph\"],\n};\n\nconst styles = `\n@keyframes ldr-morph {\n 0%, 100% {\n border-radius: 12%;\n clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);\n transform: rotate(0deg);\n }\n 33% {\n border-radius: 50%;\n clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);\n transform: rotate(120deg);\n }\n 66% {\n border-radius: 8%;\n clip-path: polygon(50% 0, 100% 100%, 0 100%, 0 100%);\n transform: rotate(240deg);\n }\n}\n@keyframes ldr-morph-shadow {\n 0%, 100% { transform: scaleX(1); opacity: 0.35; }\n 50% { transform: scaleX(0.6); opacity: 0.15; }\n}\n@keyframes ldr-morph-hue {\n 0%, 100% { background: hsl(var(--primary)); }\n 50% { background: hsl(var(--primary) / 0.6); }\n}\n`;\n\nexport default function MorphingLoader() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n\n
\n \n \n
\n\n

{en ? \"Morphing…\" : \"変形中…\"}

\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n

\n 変形中…\n

\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n

\n 変形中…\n

\n
\n \n" }, { "id": "loaders-pulse-dots", @@ -833,7 +833,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"パルスドット\",\n category: \"ローダー・マイクロ\",\n description: \"タイピング/読み込みドットとパルスするプレゼンス表示。\",\n align: \"center\",\n isNew: true,\n tags: [\"loader\", \"animation\", \"micro\", \"dots\"],\n};\n\nconst styles = `\n@keyframes ldr-typing { 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } 30% { transform: translateY(-5px); opacity: 1; } }\n@keyframes ldr-presence { 0% { transform: scale(0.8); opacity: 0.8; } 70%, 100% { transform: scale(2.2); opacity: 0; } }\n@keyframes ldr-wave { 0%, 100% { transform: scaleY(0.3); } 50% { transform: scaleY(1); } }\n`;\n\nexport default function PulseDots() {\n const en = typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n return (\n
\n \n\n {/* Typing bubble */}\n
\n
\n AI\n
\n
\n {[0, 1, 2].map((i) => (\n \n ))}\n
\n
\n\n {/* Presence indicators */}\n
\n {[\n { label: \"オンライン\", labelEn: \"Online\", color: \"bg-emerald-500\" },\n { label: \"取込中\", labelEn: \"Away\", color: \"bg-amber-500\" },\n { label: \"通話中\", labelEn: \"On a call\", color: \"bg-sky-500\" },\n ].map((s) => (\n
\n \n \n \n \n {en ? s.labelEn : s.label}\n
\n ))}\n
\n\n {/* Audio wave */}\n
\n {[0, 1, 2, 3, 4, 5, 6].map((i) => (\n \n ))}\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n AI\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n オンライン\n \n
\n
\n \n \n \n \n \n \n \n 取込中\n \n
\n
\n \n \n \n \n \n \n \n 通話中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n AI\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n オンライン\n \n
\n
\n \n \n \n \n \n \n \n 取込中\n \n
\n
\n \n \n \n \n \n \n \n 通話中\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n" }, { "id": "loaders-ripple-loader", @@ -848,7 +848,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"リップルローダー\",\n category: \"ローダー・マイクロ\",\n description: \"広がる波紋・パルスリングのローダー。\",\n align: \"center\",\n isNew: true,\n tags: [\"loader\", \"animation\", \"micro\", \"ripple\"],\n};\n\nconst styles = `\n@keyframes ldr-ripple {\n 0% { transform: scale(0); opacity: 0.7; }\n 100% { transform: scale(1); opacity: 0; }\n}\n@keyframes ldr-ripple-core { 0%, 100% { transform: scale(1); } 50% { transform: scale(0.8); } }\n@keyframes ldr-radar { 0% { transform: scale(0.2); opacity: 0.8; } 100% { transform: scale(1); opacity: 0; } }\n`;\n\nexport default function RippleLoader() {\n return (\n
\n \n\n {/* Expanding rings */}\n
\n
\n {[0, 1, 2].map((i) => (\n \n ))}\n \n
\n \n Ripple\n \n
\n\n {/* Radar fill */}\n
\n
\n {[0, 1, 2, 3].map((i) => (\n \n ))}\n \n
\n \n Radar\n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n \n \n \n \n \n \n
\n \n Ripple\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n \n Radar\n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n \n \n \n \n \n \n
\n \n Ripple\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n \n Radar\n \n
\n
\n \n" }, { "id": "loaders-spinners", @@ -863,7 +863,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"スピナー集\",\n category: \"ローダー・マイクロ\",\n description: \"リング・ドット・バー・オービットなど6種類以上のCSSスピナー。\",\n align: \"center\",\n isNew: true,\n tags: [\"loader\", \"animation\", \"micro\", \"spinner\"],\n};\n\nconst styles = `\n@keyframes ldr-spin { to { transform: rotate(360deg); } }\n@keyframes ldr-dot-bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } }\n@keyframes ldr-bar-stretch { 0%, 40%, 100% { transform: scaleY(0.4); } 20% { transform: scaleY(1); } }\n@keyframes ldr-orbit { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } }\n@keyframes ldr-pulse-ring { 0% { transform: scale(0.5); opacity: 1; } 100% { transform: scale(1.4); opacity: 0; } }\n@keyframes ldr-flip { 0% { transform: perspective(120px) rotateX(0) rotateY(0); } 50% { transform: perspective(120px) rotateX(-180deg) rotateY(0); } 100% { transform: perspective(120px) rotateX(-180deg) rotateY(-180deg); } }\n@keyframes ldr-dual-rev { to { transform: rotate(-360deg); } }\n`;\n\nfunction Cell({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n
\n
{children}
\n \n {label}\n \n
\n );\n}\n\nexport default function Spinners() {\n return (\n
\n \n
\n \n \n \n\n \n \n \n \n \n \n\n \n \n {[0, 1, 2].map((i) => (\n \n ))}\n \n \n\n \n \n {[0, 1, 2, 3, 4].map((i) => (\n \n ))}\n \n \n\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n\n \n \n \n\n \n \n {Array.from({ length: 9 }).map((_, i) => (\n \n ))}\n \n \n\n \n \n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n
\n \n Ring\n \n
\n
\n
\n \n \n \n \n \n \n
\n \n Dual Ring\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n Dots\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Bars\n \n
\n
\n
\n \n \n \n \n \n \n
\n \n Orbit\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n Pulse\n \n
\n
\n
\n \n \n
\n \n Flip\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Grid\n \n
\n
\n
\n \n \n
\n \n Gradient\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n
\n \n Ring\n \n
\n
\n
\n \n \n \n \n \n \n
\n \n Dual Ring\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n Dots\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Bars\n \n
\n
\n
\n \n \n \n \n \n \n
\n \n Orbit\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n Pulse\n \n
\n
\n
\n \n \n
\n \n Flip\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Grid\n \n
\n
\n
\n \n \n
\n \n Gradient\n \n
\n
\n
\n \n" }, { "id": "luxury-award-badge-set", @@ -878,7 +878,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アワード・バッジセット\",\n category: \"ラグジュアリー\",\n description: \"月桂樹の SVG をあしらった、金の受賞・認証バッジ群。\",\n align: \"center\",\n isNew: true,\n tags: [\"luxury\", \"premium\", \"gold\", \"award\"],\n principle: \"金の月桂樹と刻印風レイアウトが、第三者からの権威づけと信頼を瞬時に伝える。\",\n};\n\nconst badges = [\n { top: \"Best of\", year: \"2026\", label: \"Luxury Award\" },\n { top: \"Master\", year: \"100\", label: \"Years Craft\" },\n { top: \"Certified\", year: \"AAA\", label: \"Excellence\" },\n];\n\nexport default function AwardBadgeSet() {\n return (\n
\n
\n {badges.map((b) => (\n \n
\n \n
\n \n {b.top}\n \n \n {b.year}\n \n \n {b.label}\n \n
\n
\n \n ))}\n
\n
\n );\n}\n\nfunction Laurel({ className }: { className?: string }) {\n // 左右対称の月桂冠(片側を定義しミラーで複製)\n const leaves = [0, 1, 2, 3, 4, 5, 6];\n return (\n \n \n {/* 左枝 */}\n \n {leaves.map((i) => {\n const t = i / (leaves.length - 1);\n const x = 40 - t * 18;\n const y = 22 + t * 70;\n return (\n \n );\n })}\n {/* 右枝 */}\n \n {leaves.map((i) => {\n const t = i / (leaves.length - 1);\n const x = 80 + t * 18;\n const y = 22 + t * 70;\n return (\n \n );\n })}\n {/* 下の結び目 */}\n \n \n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Best of\n \n \n 2026\n \n \n Luxury Award\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Master\n \n \n 100\n \n \n Years Craft\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Certified\n \n \n AAA\n \n \n Excellence\n \n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Best of\n \n \n 2026\n \n \n Luxury Award\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Master\n \n \n 100\n \n \n Years Craft\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n Certified\n \n \n AAA\n \n \n Excellence\n \n
\n
\n
\n
\n
\n \n" }, { "id": "luxury-brand-story", @@ -893,7 +893,7 @@ ], "standalone": true, "source": "import { ArrowUpRight } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ブランドストーリー\",\n category: \"ラグジュアリー\",\n description: \"エディトリアルな二分割のブランドストーリーセクション。\",\n align: \"full\",\n isNew: true,\n tags: [\"luxury\", \"premium\", \"gold\", \"story\"],\n principle: \"雑誌的な左右分割と縦書き風の見出しが、ブランドを語る編集物としての格を与える。\",\n};\n\nexport default function BrandStory() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n {/* ビジュアル(CSS のみ) */}\n
\n
\n
\n
\n \n 1924\n \n
\n \n Maison Founded\n \n
\n\n {/* テキスト */}\n
\n

\n Our Heritage\n

\n

\n {en ? (\n <>\n A story born of\n
\n a single philosophy\n \n ) : (\n <>\n ひとつの哲学から\n
\n 始まった物語\n \n )}\n

\n\n
\n\n
\n

\n {en\n ? \"In a small Parisian atelier, our founder held to a single conviction: that beauty never yields to time. More than a century on, that spirit endures, passed down unchanged.\"\n : \"パリの小さなアトリエで、創業者は一つの信念を掲げました。「美は、時に屈しない」と。その精神は一世紀を超えて、今も変わることなく受け継がれています。\"}\n

\n

\n {en\n ? \"The warmth of the human hand, which no machine can ever replicate. We have always chosen the eternal over the efficient.\"\n : \"機械では決して生み出せない、人の手の温度。私たちは効率ではなく、永遠を選び続けてきました。\"}\n

\n
\n\n \n {en ? \"Read the Maison's history\" : \"メゾンの歴史を読む\"}\n \n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n 1924\n \n
\n \n Maison Founded\n \n
\n
\n

\n Our Heritage\n

\n

\n ひとつの哲学から\n
\n 始まった物語\n

\n
\n
\n
\n

\n パリの小さなアトリエで、創業者は一つの信念を掲げました。「美は、時に屈しない」と。その精神は一世紀を超えて、今も変わることなく受け継がれています。\n

\n

\n 機械では決して生み出せない、人の手の温度。私たちは効率ではなく、永遠を選び続けてきました。\n

\n
\n \n メゾンの歴史を読む\n \n \n \n \n \n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n 1924\n \n
\n \n Maison Founded\n \n
\n
\n

\n Our Heritage\n

\n

\n ひとつの哲学から\n
\n 始まった物語\n

\n
\n
\n
\n

\n パリの小さなアトリエで、創業者は一つの信念を掲げました。「美は、時に屈しない」と。その精神は一世紀を超えて、今も変わることなく受け継がれています。\n

\n

\n 機械では決して生み出せない、人の手の温度。私たちは効率ではなく、永遠を選び続けてきました。\n

\n
\n \n メゾンの歴史を読む\n \n \n \n \n \n \n \n
\n
\n
\n \n" }, { "id": "luxury-gold-testimonial", @@ -908,7 +908,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ゴールド・テスティモニアル\",\n category: \"ラグジュアリー\",\n description: \"金の引用符を添えた、洗練された顧客の声。\",\n align: \"center\",\n isNew: true,\n tags: [\"luxury\", \"premium\", \"gold\", \"testimonial\"],\n principle: \"大きな金の引用符と十分な余白が、言葉そのものに重みと権威を与える。\",\n};\n\nexport default function GoldTestimonial() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n\n \n “\n \n\n
\n

\n {en\n ? \"The moment it touches you, you feel the weight of the real. This is not adornment — it is a chapter of my own story.\"\n : \"身に纏った瞬間に分かる、本物の重み。これは装飾ではなく、私自身の物語の一部です。\"}\n

\n
\n\n
\n\n
\n

\n Isabella Moreau\n

\n

\n Art Collector · Paris\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n \n “\n \n
\n

\n 身に纏った瞬間に分かる、本物の重み。これは装飾ではなく、私自身の物語の一部です。\n

\n
\n
\n
\n
\n

\n Isabella Moreau\n

\n

\n Art Collector · Paris\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n “\n \n
\n

\n 身に纏った瞬間に分かる、本物の重み。これは装飾ではなく、私自身の物語の一部です。\n

\n
\n
\n
\n
\n

\n Isabella Moreau\n

\n

\n Art Collector · Paris\n

\n
\n
\n \n" }, { "id": "luxury-luxury-feature", @@ -923,7 +923,7 @@ ], "standalone": true, "source": "import { Gem, Hammer, Leaf, ShieldCheck } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ラグジュアリー・フィーチャー\",\n category: \"ラグジュアリー\",\n description: \"細罫の仕切りで区切る、上質で端正な特徴セクション。\",\n align: \"full\",\n isNew: true,\n tags: [\"luxury\", \"premium\", \"gold\", \"feature\"],\n principle: \"ヘアラインの仕切りと均等な余白が、整然とした秩序と作り手の誠実さを語る。\",\n};\n\nconst features = [\n {\n icon: Hammer,\n title: \"熟練の手仕事\",\n titleEn: \"Master Craftsmanship\",\n body: \"一点ごとに数百時間を費やす、マイスターによる完全なハンドメイド。\",\n bodyEn: \"Entirely handmade by master artisans, with hundreds of hours devoted to every single piece.\",\n },\n {\n icon: Gem,\n title: \"厳選された素材\",\n titleEn: \"Curated Materials\",\n body: \"世界の鉱区から選び抜いた、最高品質の原石と貴金属のみを使用。\",\n bodyEn: \"Only the finest stones and precious metals, hand-selected from mines around the world.\",\n },\n {\n icon: ShieldCheck,\n title: \"生涯保証\",\n titleEn: \"Lifetime Guarantee\",\n body: \"所有される限り続く、無償のメンテナンスと修復のお約束。\",\n bodyEn: \"Complimentary maintenance and restoration, promised for as long as the piece is yours.\",\n },\n {\n icon: Leaf,\n title: \"持続可能性\",\n titleEn: \"Sustainability\",\n body: \"倫理的に調達された素材と、責任ある製造へのたゆまぬ取り組み。\",\n bodyEn: \"An unwavering commitment to ethically sourced materials and responsible craftsmanship.\",\n },\n];\n\nexport default function LuxuryFeature() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n

\n The Maison\n

\n

\n {en ? \"Excellence, Promised\" : \"約束された卓越\"}\n

\n
\n
\n\n
\n {features.map((f) => (\n \n \n

\n {en ? f.titleEn : f.title}\n

\n
\n

\n {en ? f.bodyEn : f.body}\n

\n
\n ))}\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n

\n The Maison\n

\n

\n 約束された卓越\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n

\n 熟練の手仕事\n

\n
\n
\n

\n 一点ごとに数百時間を費やす、マイスターによる完全なハンドメイド。\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 厳選された素材\n

\n
\n
\n

\n 世界の鉱区から選び抜いた、最高品質の原石と貴金属のみを使用。\n

\n
\n
\n \n \n \n \n \n \n

\n 生涯保証\n

\n
\n
\n

\n 所有される限り続く、無償のメンテナンスと修復のお約束。\n

\n
\n
\n \n \n \n \n \n \n

\n 持続可能性\n

\n
\n
\n

\n 倫理的に調達された素材と、責任ある製造へのたゆまぬ取り組み。\n

\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n The Maison\n

\n

\n 約束された卓越\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n

\n 熟練の手仕事\n

\n
\n
\n

\n 一点ごとに数百時間を費やす、マイスターによる完全なハンドメイド。\n

\n
\n
\n \n \n \n \n \n \n \n \n

\n 厳選された素材\n

\n
\n
\n

\n 世界の鉱区から選び抜いた、最高品質の原石と貴金属のみを使用。\n

\n
\n
\n \n \n \n \n \n \n

\n 生涯保証\n

\n
\n
\n

\n 所有される限り続く、無償のメンテナンスと修復のお約束。\n

\n
\n
\n \n \n \n \n \n \n

\n 持続可能性\n

\n
\n
\n

\n 倫理的に調達された素材と、責任ある製造へのたゆまぬ取り組み。\n

\n
\n
\n
\n \n" }, { "id": "marquee-avatar-marquee", @@ -938,7 +938,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アバターマーキー\",\n category: \"マーキー\",\n description: \"イニシャルアバターが流れる「N 人に信頼されています」帯。\",\n align: \"full\",\n isNew: true,\n tags: [\"marquee\", \"animation\", \"infinite\", \"avatar\"],\n};\n\nconst USERS = [\n { initials: \"MS\", color: \"bg-rose-500\" },\n { initials: \"LC\", color: \"bg-sky-500\" },\n { initials: \"TK\", color: \"bg-violet-500\" },\n { initials: \"AN\", color: \"bg-amber-500\" },\n { initials: \"YA\", color: \"bg-emerald-500\" },\n { initials: \"JD\", color: \"bg-indigo-500\" },\n { initials: \"RB\", color: \"bg-fuchsia-500\" },\n { initials: \"KS\", color: \"bg-teal-500\" },\n { initials: \"EL\", color: \"bg-orange-500\" },\n { initials: \"PW\", color: \"bg-cyan-500\" },\n];\n\nexport default function AvatarMarquee() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n

\n 28,000+{\" \"}\n {en ? \"users trust us\" : \"人のユーザーに信頼されています\"}\n

\n \n
\n {[...USERS, ...USERS].map((u, i) => (\n \n {u.initials}\n \n ))}\n
\n
\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n

\n \n 28,000+\n \n 人のユーザーに信頼されています\n

\n
\n
\n \n MS\n \n \n LC\n \n \n TK\n \n \n AN\n \n \n YA\n \n \n JD\n \n \n RB\n \n \n KS\n \n \n EL\n \n \n PW\n \n \n MS\n \n \n LC\n \n \n TK\n \n \n AN\n \n \n YA\n \n \n JD\n \n \n RB\n \n \n KS\n \n \n EL\n \n \n PW\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n

\n \n 28,000+\n \n 人のユーザーに信頼されています\n

\n
\n
\n \n MS\n \n \n LC\n \n \n TK\n \n \n AN\n \n \n YA\n \n \n JD\n \n \n RB\n \n \n KS\n \n \n EL\n \n \n PW\n \n \n MS\n \n \n LC\n \n \n TK\n \n \n AN\n \n \n YA\n \n \n JD\n \n \n RB\n \n \n KS\n \n \n EL\n \n \n PW\n \n
\n
\n
\n \n" }, { "id": "marquee-brand-ticker", @@ -953,7 +953,7 @@ ], "standalone": true, "source": "import { Sparkles } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ブランドティッカー\",\n category: \"マーキー\",\n description: \"ニュース速報風の細いスクロールテキスト帯。\",\n align: \"full\",\n isNew: true,\n tags: [\"marquee\", \"animation\", \"infinite\", \"ticker\"],\n};\n\nconst HEADLINES = [\n { ja: \"新機能をリリースしました\", en: \"New feature just shipped\" },\n { ja: \"ユーザー数 100,000 突破\", en: \"Over 100,000 users\" },\n { ja: \"今週末限定セール開催中\", en: \"Weekend-only sale on now\" },\n { ja: \"新しいインテグレーションに対応\", en: \"New integrations supported\" },\n { ja: \"カンファレンスのチケット販売開始\", en: \"Conference tickets on sale\" },\n { ja: \"アップタイム 99.99% を達成\", en: \"Achieved 99.99% uptime\" },\n];\n\nexport default function BrandTicker() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const line = [...HEADLINES, ...HEADLINES];\n return (\n
\n \n
\n \n \n NEWS\n \n
\n
\n {line.map((h, i) => (\n \n {en ? h.en : h.ja}\n \n \n ))}\n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n NEWS\n \n
\n
\n \n \n 新機能をリリースしました\n \n \n •\n \n \n \n \n ユーザー数 100,000 突破\n \n \n •\n \n \n \n \n 今週末限定セール開催中\n \n \n •\n \n \n \n \n 新しいインテグレーションに対応\n \n \n •\n \n \n \n \n カンファレンスのチケット販売開始\n \n \n •\n \n \n \n \n アップタイム 99.99% を達成\n \n \n •\n \n \n \n \n 新機能をリリースしました\n \n \n •\n \n \n \n \n ユーザー数 100,000 突破\n \n \n •\n \n \n \n \n 今週末限定セール開催中\n \n \n •\n \n \n \n \n 新しいインテグレーションに対応\n \n \n •\n \n \n \n \n カンファレンスのチケット販売開始\n \n \n •\n \n \n \n \n アップタイム 99.99% を達成\n \n \n •\n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n NEWS\n \n
\n
\n \n \n 新機能をリリースしました\n \n \n •\n \n \n \n \n ユーザー数 100,000 突破\n \n \n •\n \n \n \n \n 今週末限定セール開催中\n \n \n •\n \n \n \n \n 新しいインテグレーションに対応\n \n \n •\n \n \n \n \n カンファレンスのチケット販売開始\n \n \n •\n \n \n \n \n アップタイム 99.99% を達成\n \n \n •\n \n \n \n \n 新機能をリリースしました\n \n \n •\n \n \n \n \n ユーザー数 100,000 突破\n \n \n •\n \n \n \n \n 今週末限定セール開催中\n \n \n •\n \n \n \n \n 新しいインテグレーションに対応\n \n \n •\n \n \n \n \n カンファレンスのチケット販売開始\n \n \n •\n \n \n \n \n アップタイム 99.99% を達成\n \n \n •\n \n \n
\n
\n
\n
\n \n" }, { "id": "marquee-dual-marquee", @@ -968,7 +968,7 @@ ], "standalone": true, "source": "import { Atom, Cloud, Cpu, Database, Globe, Layers, Shield, Workflow } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"デュアルマーキー\",\n category: \"マーキー\",\n description: \"上下で逆方向にスクロールする2列のロゴ帯。\",\n align: \"full\",\n isNew: true,\n tags: [\"marquee\", \"animation\", \"infinite\", \"dual\"],\n};\n\nconst ROW_A = [\n { name: \"Cloudly\", Icon: Cloud },\n { name: \"Atomix\", Icon: Atom },\n { name: \"Globex\", Icon: Globe },\n { name: \"Layerly\", Icon: Layers },\n];\nconst ROW_B = [\n { name: \"Coreon\", Icon: Cpu },\n { name: \"Datavault\", Icon: Database },\n { name: \"Shieldr\", Icon: Shield },\n { name: \"Flowly\", Icon: Workflow },\n];\n\ntype Item = { name: string; Icon: typeof Cloud };\n\nfunction Row({ items, reverse }: { items: Item[]; reverse?: boolean }) {\n return (\n \n \n {[...items, ...items].map(({ name, Icon }, i) => (\n \n \n {name}\n \n ))}\n \n \n );\n}\n\nexport default function DualMarquee() {\n return (\n
\n \n \n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n \n \n \n Cloudly\n \n
\n
\n \n \n \n \n \n \n \n \n \n Atomix\n \n
\n
\n \n \n \n \n \n \n \n \n \n Globex\n \n
\n
\n \n \n \n \n \n \n \n \n \n Layerly\n \n
\n
\n \n \n \n \n \n Cloudly\n \n
\n
\n \n \n \n \n \n \n \n \n \n Atomix\n \n
\n
\n \n \n \n \n \n \n \n \n \n Globex\n \n
\n
\n \n \n \n \n \n \n \n \n \n Layerly\n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Coreon\n \n
\n
\n \n \n \n \n \n \n \n \n \n Datavault\n \n
\n
\n \n \n \n \n \n Shieldr\n \n
\n
\n \n \n \n \n \n \n \n \n \n Flowly\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Coreon\n \n
\n
\n \n \n \n \n \n \n \n \n \n Datavault\n \n
\n
\n \n \n \n \n \n Shieldr\n \n
\n
\n \n \n \n \n \n \n \n \n \n Flowly\n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n \n \n \n Cloudly\n \n
\n
\n \n \n \n \n \n \n \n \n \n Atomix\n \n
\n
\n \n \n \n \n \n \n \n \n \n Globex\n \n
\n
\n \n \n \n \n \n \n \n \n \n Layerly\n \n
\n
\n \n \n \n \n \n Cloudly\n \n
\n
\n \n \n \n \n \n \n \n \n \n Atomix\n \n
\n
\n \n \n \n \n \n \n \n \n \n Globex\n \n
\n
\n \n \n \n \n \n \n \n \n \n Layerly\n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Coreon\n \n
\n
\n \n \n \n \n \n \n \n \n \n Datavault\n \n
\n
\n \n \n \n \n \n Shieldr\n \n
\n
\n \n \n \n \n \n \n \n \n \n Flowly\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Coreon\n \n
\n
\n \n \n \n \n \n \n \n \n \n Datavault\n \n
\n
\n \n \n \n \n \n Shieldr\n \n
\n
\n \n \n \n \n \n \n \n \n \n Flowly\n \n
\n
\n
\n
\n \n" }, { "id": "marquee-icon-marquee", @@ -983,7 +983,7 @@ ], "standalone": true, "source": "import {\n Boxes,\n Cloud,\n Code2,\n Database,\n Figma,\n GitBranch,\n Github,\n Globe,\n Mail,\n MessageCircle,\n Slack,\n Terminal,\n} from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アイコンマーキー\",\n category: \"マーキー\",\n description: \"連携サービスのアイコンが一列に流れる。\",\n align: \"full\",\n isNew: true,\n tags: [\"marquee\", \"animation\", \"infinite\", \"icons\"],\n};\n\nconst ICONS = [\n { Icon: Github, name: \"GitHub\" },\n { Icon: Slack, name: \"Slack\" },\n { Icon: Figma, name: \"Figma\" },\n { Icon: Database, name: \"Postgres\" },\n { Icon: Cloud, name: \"Cloud\" },\n { Icon: GitBranch, name: \"Git\" },\n { Icon: Terminal, name: \"CLI\" },\n { Icon: Code2, name: \"SDK\" },\n { Icon: Mail, name: \"Mail\" },\n { Icon: Globe, name: \"Web\" },\n { Icon: MessageCircle, name: \"Chat\" },\n { Icon: Boxes, name: \"Containers\" },\n];\n\nexport default function IconMarquee() {\n return (\n
\n \n \n
\n {[...ICONS, ...ICONS].map(({ Icon, name }, i) => (\n \n \n
\n ))}\n
\n \n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n" }, { "id": "mat3d-gloss-crystal-gem-facet", @@ -998,7 +998,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"クリスタル ジェムファセット\",\n category: \"3Dアニメ\",\n description:\n \"CSSのperspectiveとpreserve-3dで組んだ多面カット宝石が回転。星形グリントと縁の虹色分散できらめく。\",\n align: \"center\",\n isNew: true,\n level: \"advanced\",\n tags: [\"3d\", \"gloss\", \"materials\", \"animation\"],\n principle:\n \"多面体のきらめきと虹の分散は希少な宝石の合図。眩しさのピーク=価値のピークと脳が結びつける。\",\n};\n\n/**\n * 真の立体ジェム(ブリリアントカット)。\n * 縦軸まわりに N 個のファセットを放射状に並べる(rotateY(i*360/N) translateZ(R))ので、\n * どの回転角でもシルエットが宝石のまま=細い板に潰れない。\n * - テーブル(頂上の平らな多角形)\n * - クラウン(上部リング:外へ開く台形ファセット)\n * - ガードル(中央のくびれ)\n * - パビリオン(下部リング:底のキューレットへ収束する三角ファセット)\n */\n\nconst N = 10; // ファセット数\nconst R = 56; // 軸からファセットまでの半径(px)\nconst CROWN_H = 40; // クラウン(上半身)の高さ\nconst PAV_H = 78; // パビリオン(下半身)の高さ\nconst FACET_W = (2 * Math.PI * R) / N + 6; // リングを隙間なく埋める弦幅 + のりしろ\n\n// ファセットごとに明るさ・色相を少し変えて、回転で「光を拾う面」が変わって見えるようにする\nconst FACETS = Array.from({ length: N }, (_, i) => {\n const angle = (i * 360) / N;\n // 0..1 の波で明暗を作る(隣り合う面が交互に明るい)\n const wave = (Math.sin((i / N) * Math.PI * 2) + 1) / 2;\n const crownBright = 0.55 + wave * 0.45;\n const pavBright = 0.4 + (1 - wave) * 0.4;\n return { i, angle, crownBright, pavBright };\n});\n\nfunction crownGradient(b: number): string {\n // 上が明るく下(ガードル側)がやや濃い、氷青〜クリア〜淡い菫\n const top = `rgba(${Math.round(196 * b + 40)}, ${Math.round(\n 222 * b + 30\n )}, 255, ${0.42 + b * 0.3})`;\n const mid = `rgba(${Math.round(150 * b + 30)}, ${Math.round(\n 180 * b + 24\n )}, ${Math.round(236 * b + 19)}, ${0.34 + b * 0.26})`;\n const bot = `rgba(${Math.round(120 * b + 24)}, ${Math.round(\n 138 * b + 20\n )}, ${Math.round(214 * b + 30)}, ${0.3 + b * 0.22})`;\n return `linear-gradient(180deg, ${top} 0%, ${mid} 52%, ${bot} 100%)`;\n}\n\nfunction pavilionGradient(b: number): string {\n // ガードル側が明るく、キューレット(先端)へ向け深い藍に沈む\n const top = `rgba(${Math.round(130 * b + 30)}, ${Math.round(\n 160 * b + 26\n )}, ${Math.round(236 * b + 19)}, ${0.4 + b * 0.28})`;\n const tip = `rgba(${Math.round(60 * b + 14)}, ${Math.round(\n 84 * b + 16\n )}, ${Math.round(168 * b + 28)}, ${0.5 + b * 0.28})`;\n return `linear-gradient(180deg, ${top} 0%, ${tip} 100%)`;\n}\n\n// テーブル(多角形)の clip-path:N 角形\nconst TABLE_CLIP =\n \"polygon(\" +\n Array.from({ length: N }, (_, i) => {\n const a = (i / N) * Math.PI * 2 - Math.PI / 2;\n const x = 50 + Math.cos(a) * 50;\n const y = 50 + Math.sin(a) * 50;\n return `${x.toFixed(1)}% ${y.toFixed(1)}%`;\n }).join(\", \") +\n \")\";\n\n// 漂う虹グリント(虹色分散)。面の上できらめく小さな閃光。\nconst GLINTS = [\n { x: 30, y: 30, s: 16, dur: 4.2, delay: 0, hue: 0 },\n { x: 66, y: 42, s: 12, dur: 5.1, delay: 1.4, hue: 140 },\n { x: 46, y: 60, s: 14, dur: 4.6, delay: 2.6, hue: 260 },\n { x: 58, y: 24, s: 10, dur: 5.6, delay: 3.4, hue: 60 },\n];\n\nexport default function CrystalGemFacet() {\n const en =\n typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n\n return (\n \n \n\n {/* シーン:perspective を与えて立体回転させる */}\n \n {/* 背後の虹ブルーム(細い帯にならない円形) */}\n \n\n {/* 回転する宝石本体 */}\n \n {/* 内側のコア:奥のファセットが透けたとき、空洞ではなく結晶の芯が見えるように */}\n \n\n {/* テーブル(頂上の平らな多角形) */}\n \n {/* テーブル上の漂う虹(ドリフトする線形虹で、潰れない面光沢) */}\n \n \n\n {/* クラウン:上部リングのファセット(外へ開く台形) */}\n {FACETS.map(({ i, angle, crownBright }) => (\n \n ))}\n\n {/* クラウンの間を埋める三角ファセット(半オフセットの放射で多面感を強める) */}\n {FACETS.map(({ i, angle, crownBright }) => (\n \n ))}\n\n {/* パビリオン:下部リングのファセット(底のキューレットへ収束する三角) */}\n {FACETS.map(({ i, angle, pavBright }) => (\n \n ))}\n\n {/* ガードル:中央のくびれを示す光沢リング */}\n {FACETS.map(({ i, angle, crownBright }) => (\n \n ))}\n\n {/* 漂う虹グリント(面の上できらめく小さな閃光・虹色分散) */}\n {GLINTS.map((g, i) => (\n \n \n \n \n ))}\n \n \n\n

\n {en ? \"Brilliant cut · 1.2 ct\" : \"ブリリアントカット · 1.2 カラット\"}\n

\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n ブリリアントカット · 1.2 カラット\n

\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n ブリリアントカット · 1.2 カラット\n

\n
\n \n" }, { "id": "mat3d-gloss-liquid-chrome-headline", @@ -1013,7 +1013,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"リキッドクローム 見出し\",\n category: \"3Dアニメ\",\n description:\n \"立体的に押し出した巨大見出し。流れるクローム/液体金属グラデと横切るミラー帯で動く。\",\n align: \"full\",\n isNew: true,\n level: \"advanced\",\n tags: [\"3d\", \"gloss\", \"materials\", \"animation\"],\n principle:\n \"重く磨かれた金属の艶は“高性能・プレミアム”の連想を起こし、キネティックな反射が視線を掴む。\",\n};\n\nexport default function LiquidChromeHeadline() {\n const en =\n typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n\n const word = en ? \"CHROME\" : \"クローム\";\n const sub = en\n ? \"Liquid metal, kinetic shine\"\n : \"液体金属の、動くきらめき\";\n\n return (\n \n \n\n
\n {/* extruded depth copies (behind) */}\n
\n {EXTRUDE.map((z, i) => (\n \n {word}\n \n ))}\n
\n\n {/* chrome flowing face */}\n \n {word}\n \n\n {/* moving mirror band over the text */}\n \n \n
\n \n\n

\n {sub}\n

\n \n );\n}\n\nconst EXTRUDE = [2, 4, 6, 8, 10, 12, 14];\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n
\n

\n クローム\n

\n
\n
\n
\n
\n
\n

\n 液体金属の、動くきらめき\n

\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n \n クローム\n \n
\n

\n クローム\n

\n
\n
\n
\n
\n
\n

\n 液体金属の、動くきらめき\n

\n
\n \n" }, { "id": "mat3d-gloss-urushi-lacquer-hero", @@ -1028,7 +1028,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Sparkles } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"漆 ラッカーヒーロー\",\n category: \"3Dアニメ\",\n description:\n \"深い漆黒の漆面に金箔のアクセント。白いスペキュラ帯がゆっくり横切る和の高級ヒーロー。\",\n align: \"full\",\n isNew: true,\n level: \"advanced\",\n tags: [\"3d\", \"gloss\", \"materials\", \"animation\"],\n principle:\n \"鏡面のツヤと金の艶めきは“手間をかけた本物=高級”を一瞬で伝え、価格を正当化する。\",\n};\n\nexport default function UrushiLacquerHero() {\n const en =\n typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n\n return (\n \n \n\n {/* lacquer depth sheen */}\n \n\n {/* gold-leaf flecks */}\n
\n {GOLD_FLECKS.map((f, i) => (\n \n ))}\n
\n\n {/* moving white specular band */}\n \n\n
\n \n \n {en ? \"Maki-e Edition\" : \"蒔絵 限定\"}\n \n\n \n {en ? (\n <>\n The depth of{\" \"}\n \n black lacquer\n \n \n ) : (\n <>\n 漆黒に宿る\n \n 黄金の艶\n \n \n )}\n \n\n

\n {en\n ? \"Hand-burnished over many coats, the surface holds light like still water — a luster only time and craft can give.\"\n : \"幾度も塗り重ね、丹念に磨き上げた面は、静かな水のように光を湛える。時と技だけが生む、本物の艶。\"}\n

\n\n
\n \n {en ? \"Discover the craft\" : \"技を知る\"}\n \n \n {en ? \"Kyoto · Since 1789\" : \"京都 · 寛政元年創業\"}\n \n
\n
\n \n );\n}\n\nconst GOLD_FLECKS: {\n x: number;\n y: number;\n s: number;\n r: number;\n d: number;\n delay: number;\n}[] = [\n { x: 10, y: 22, s: 7, r: 18, d: 3.2, delay: 0 },\n { x: 26, y: 70, s: 5, r: -32, d: 4.1, delay: 0.6 },\n { x: 78, y: 30, s: 9, r: 44, d: 3.6, delay: 1.1 },\n { x: 88, y: 64, s: 6, r: -12, d: 4.4, delay: 0.3 },\n { x: 62, y: 14, s: 4, r: 70, d: 2.9, delay: 1.4 },\n { x: 44, y: 84, s: 8, r: -50, d: 3.9, delay: 0.9 },\n { x: 70, y: 88, s: 5, r: 24, d: 4.2, delay: 1.7 },\n { x: 16, y: 50, s: 6, r: -8, d: 3.4, delay: 2.0 },\n];\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n 蒔絵 限定\n \n

\n 漆黒に宿る\n \n 黄金の艶\n \n

\n

\n 幾度も塗り重ね、丹念に磨き上げた面は、静かな水のように光を湛える。時と技だけが生む、本物の艶。\n

\n
\n \n 技を知る\n \n \n 京都 · 寛政元年創業\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n 蒔絵 限定\n \n

\n 漆黒に宿る\n \n 黄金の艶\n \n

\n

\n 幾度も塗り重ね、丹念に磨き上げた面は、静かな水のように光を湛える。時と技だけが生む、本物の艶。\n

\n
\n \n 技を知る\n \n \n 京都 · 寛政元年創業\n \n
\n
\n
\n \n" }, { "id": "mat3d-gloss-wet-glass-product", @@ -1043,7 +1043,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Droplets } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"ウェットグラス プロダクト\",\n category: \"3Dアニメ\",\n description:\n \"濡れたガラス層の下に置いた製品カード。ぼかしたハイライトが反射のように滑り、水滴がきらめく。\",\n align: \"center\",\n isNew: true,\n level: \"advanced\",\n tags: [\"3d\", \"gloss\", \"materials\", \"animation\"],\n principle:\n \"水濡れのような艶は鮮度・清潔・上質の連想を呼び、触れたくなる衝動(タッチ欲求)を高める。\",\n};\n\nexport default function WetGlassProduct() {\n const en =\n typeof document !== \"undefined\" && document.documentElement.lang === \"en\";\n\n return (\n
\n \n\n \n {/* product surface */}\n
\n \n \n {en ? \"AQ\" : \"潤\"}\n \n
\n\n

\n {en ? \"Aqua Serum\" : \"アクア セラム\"}\n

\n

\n {en ? \"Deep hydration · 30ml\" : \"ディープ保湿 · 30ml\"}\n

\n\n {/* clipped reflection of the product */}\n \n \n
\n \n\n {/* droplet sparkles */}\n {DROPS.map((d, i) => (\n \n ))}\n\n {/* wet-glass moving highlight */}\n \n\n {/* top glaze */}\n \n \n\n

\n \n {en ? \"Wet-glass finish\" : \"ウェットグラス仕上げ\"}\n

\n \n );\n}\n\nconst DROPS: { x: number; y: number; s: number; dur: number; delay: number }[] =\n [\n { x: 18, y: 30, s: 8, dur: 2.6, delay: 0 },\n { x: 74, y: 24, s: 6, dur: 3.1, delay: 0.5 },\n { x: 82, y: 58, s: 10, dur: 2.9, delay: 1.0 },\n { x: 30, y: 66, s: 5, dur: 3.4, delay: 0.7 },\n { x: 60, y: 78, s: 7, dur: 2.7, delay: 1.3 },\n ];\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n 潤\n \n
\n

\n アクア セラム\n

\n

\n ディープ保湿 · 30ml\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n

\n \n \n \n \n \n \n ウェットグラス仕上げ\n

\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n 潤\n \n
\n

\n アクア セラム\n

\n

\n ディープ保湿 · 30ml\n

\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n

\n \n \n \n \n \n \n ウェットグラス仕上げ\n

\n
\n \n" }, { "id": "memphis-memphis-badge-set", @@ -1057,7 +1057,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Sparkles, Flame, Star, Tag } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"メンフィス・バッジ集\",\n category: \"メンフィス\",\n description: \"幾何学バッジ・タグとパターンスウォッチのセット。\",\n align: \"center\",\n isNew: true,\n tags: [\"memphis\", \"geometric\", \"80s\"],\n};\n\n/** パターンスウォッチ(背景パターンの見本タイル) */\nfunction Swatch({ children, label }: { children: React.ReactNode; label: string }) {\n return (\n
\n
\n {children}\n
\n {label}\n
\n );\n}\n\nexport default function MemphisBadgeSet() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* バッジ・タグ */}\n

{en ? \"Badges & Tags\" : \"バッジ & タグ\"}

\n
\n \n \n {en ? \"Hot\" : \"話題\"}\n \n \n \n {en ? \"Pick\" : \"おすすめ\"}\n \n \n \n {en ? \"New\" : \"新着\"}\n \n \n \n ¥980\n \n \n {en ? \"Limited\" : \"限定\"}\n \n
\n\n {/* ステータスドット */}\n
\n {[\n { c: \"#1fb6c1\", t: \"稼働中\", en: \"Active\" },\n { c: \"#ffd23f\", t: \"保留\", en: \"Pending\" },\n { c: \"#ff5c8a\", t: \"停止\", en: \"Down\" },\n ].map((s) => (\n \n \n {en ? s.en : s.t}\n \n ))}\n
\n\n {/* パターンスウォッチセット */}\n

{en ? \"Patterns\" : \"パターン\"}

\n
\n \n \n {[0, 1, 2, 3].map((r) =>\n [0, 1, 2, 3].map((c) => (\n \n ))\n )}\n \n \n \n \n {[14, 34, 54].map((y) => (\n \n ))}\n \n \n \n
\n {[\"#7b5cff\", \"#fff\", \"#ffd23f\", \"#fff\", \"#ff8c42\", \"#fff\"].map((c, i) => (\n
\n ))}\n
\n \n \n \n {[8, 32, 56].map((x) =>\n [20, 44].map((y) => (\n \n ))\n )}\n \n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n

\n バッジ & タグ\n

\n
\n \n \n \n \n \n 話題\n \n \n \n \n \n \n おすすめ\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 新着\n \n \n \n \n \n \n \n \n ¥980\n \n \n 限定\n \n
\n
\n \n \n \n 稼働中\n \n \n \n \n 保留\n \n \n \n \n 停止\n \n
\n

\n パターン\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n ドット\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n ジグザグ\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n ストライプ\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n トライ\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n

\n バッジ & タグ\n

\n
\n \n \n \n \n \n 話題\n \n \n \n \n \n \n おすすめ\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 新着\n \n \n \n \n \n \n \n \n ¥980\n \n \n 限定\n \n
\n
\n \n \n \n 稼働中\n \n \n \n \n 保留\n \n \n \n \n 停止\n \n
\n

\n パターン\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n ドット\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n ジグザグ\n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n ストライプ\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n トライ\n \n
\n
\n
\n \n" }, { "id": "memphis-memphis-button-set", @@ -1071,7 +1071,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { Heart, Star, Zap, Plus } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"メンフィス・ボタン集\",\n category: \"メンフィス\",\n description: \"アウトラインのプレイフルなボタンとシェイプバッジ。\",\n align: \"center\",\n isNew: true,\n tags: [\"memphis\", \"geometric\", \"80s\"],\n};\n\nexport default function MemphisButtonSet() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* 角の装飾と影が箱の外に出るので、そのぶんの余白を外側に確保する */}\n
\n
\n\n {/* ソリッドボタン */}\n
\n \n \n \n
\n\n {/* アウトラインボタン */}\n
\n \n \n \n \n \n
\n\n {/* シェイプバッジ */}\n
\n
\n \n \n {en ? \"Popular\" : \"人気\"}\n \n \n {en ? \"New\" : \"新着\"}\n \n \n {en ? \"Sale\" : \"セール\"}\n \n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n
\n \n \n \n \n \n 人気\n \n \n 新着\n \n \n セール\n \n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n
\n \n \n \n \n \n 人気\n \n \n 新着\n \n \n セール\n \n \n \n
\n
\n
\n
\n \n" }, { "id": "memphis-memphis-card", @@ -1085,7 +1085,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { ArrowUpRight } from \"lucide-react\";\n\nexport const meta: DemoMeta = {\n name: \"メンフィス・カード\",\n category: \"メンフィス\",\n description: \"シェイプアクセント付きのコンテンツカード。\",\n align: \"center\",\n isNew: true,\n tags: [\"memphis\", \"geometric\", \"80s\"],\n};\n\nexport default function MemphisCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* 装飾が箱の外に出る設計なので、はみ出すぶんの余白を外側に確保する */}\n
\n {/* 外側に飛び出す装飾シェイプ */}\n
\n
\n \n\n
\n {/* 上部のパターンバンド */}\n
\n \n \n \n
\n
\n
\n\n
\n \n {en ? \"New\" : \"新着\"}\n \n

\n {en ? \"Geometric Studio\" : \"ジオメトリック・スタジオ\"}\n

\n

\n {en\n ? \"Bold shapes and primary colors for postmodern visuals that grab every eye.\"\n : \"大胆なシェイプとプライマリーカラーで、見る人の目を引きつけるポストモダンなビジュアルを。\"}\n

\n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n
\n \n 新着\n \n

\n ジオメトリック・スタジオ\n

\n

\n 大胆なシェイプとプライマリーカラーで、見る人の目を引きつけるポストモダンなビジュアルを。\n

\n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n
\n \n 新着\n \n

\n ジオメトリック・スタジオ\n

\n

\n 大胆なシェイプとプライマリーカラーで、見る人の目を引きつけるポストモダンなビジュアルを。\n

\n \n
\n
\n
\n
\n \n" }, { "id": "memphis-memphis-cta", @@ -1099,7 +1099,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\nimport { ArrowRight } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport const meta: DemoMeta = {\n name: \"メンフィス・CTA\",\n category: \"メンフィス\",\n description: \"紙吹雪シェイプが弾けるエネルギッシュなCTAバンド。\",\n align: \"full\",\n isNew: true,\n tags: [\"memphis\", \"geometric\", \"80s\"],\n};\n\n/** 紙吹雪シェイプ一片 */\nfunction Confetti({\n className,\n color,\n shape = \"square\",\n}: {\n className?: string;\n color: string;\n shape?: \"square\" | \"circle\" | \"triangle\";\n}) {\n if (shape === \"circle\") {\n return ;\n }\n if (shape === \"triangle\") {\n return (\n \n );\n }\n return ;\n}\n\nexport default function MemphisCTA() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n {/* 紙吹雪 */}\n \n \n \n \n \n \n \n \n \n\n
\n

\n {en ? (\n <>\n Make your design{\" \"}\n pop today.\n \n ) : (\n <>\n 今日から、デザインを\n 弾けさせよう。\n \n )}\n

\n

\n {en\n ? \"Start free, upgrade anytime. No credit card needed.\"\n : \"無料で始めて、いつでもアップグレード。クレジットカード不要。\"}\n

\n
\n \n \n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 今日から、デザインを\n \n 弾けさせよう。\n \n

\n

\n 無料で始めて、いつでもアップグレード。クレジットカード不要。\n

\n
\n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 今日から、デザインを\n \n 弾けさせよう。\n \n

\n

\n 無料で始めて、いつでもアップグレード。クレジットカード不要。\n

\n
\n \n \n
\n
\n
\n
\n \n" }, { "id": "minimal-editorial-list", @@ -1113,7 +1113,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"エディトリアル・インデックス\",\n category: \"ミニマル\",\n description: \"罫線で区切った番号付き目次。書籍のような構成。\",\n align: \"full\",\n isNew: true,\n tags: [\"minimal\", \"swiss\", \"editorial\"],\n principle: \"通し番号と罫線が走査を助け、一覧の階層を明確にする。\",\n};\n\nconst items = [\n { no: \"01\", ja: \"グリッドの規律\", en: \"Grid discipline\", meta: \"Foundations\", page: \"012\" },\n { no: \"02\", ja: \"タイポグラフィの階層\", en: \"Type hierarchy\", meta: \"Type\", page: \"028\" },\n { no: \"03\", ja: \"余白とリズム\", en: \"Whitespace & rhythm\", meta: \"Space\", page: \"046\" },\n { no: \"04\", ja: \"モノクロームの色設計\", en: \"Monochrome color\", meta: \"Color\", page: \"061\" },\n { no: \"05\", ja: \"ヘアラインと境界\", en: \"Hairlines & borders\", meta: \"Detail\", page: \"079\" },\n { no: \"06\", ja: \"反転とコントラスト\", en: \"Inversion & contrast\", meta: \"Inversion\", page: \"094\" },\n];\n\nexport default function EditorialList() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n

{en ? \"Contents\" : \"目次\"}

\n \n Contents — 06\n \n
\n\n
    \n {items.map((it) => (\n
  1. \n \n \n {it.no}\n \n \n {en ? it.en : it.ja}\n \n \n \n {it.page}\n \n \n
  2. \n ))}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n \n" }, { "id": "minimal-feature-grid-swiss", @@ -1127,7 +1127,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"スイス・フィーチャー\",\n category: \"ミニマル\",\n description: \"通し番号を持つ、厳格なモジュラー機能グリッド。\",\n align: \"full\",\n isNew: true,\n tags: [\"minimal\", \"swiss\", \"grid\"],\n principle: \"均等なモジュールと番号で、機能群を体系的に読み解かせる。\",\n};\n\nconst features = [\n {\n no: \"01\",\n ja: \"グリッド設計\",\n en: \"Grid system\",\n bodyJa: \"8pt基準のモジュラーグリッドで、すべての要素を整然と配置する。\",\n bodyEn: \"An 8pt modular grid keeps every element in precise order.\",\n },\n {\n no: \"02\",\n ja: \"タイプスケール\",\n en: \"Type scale\",\n bodyJa: \"比率に基づく明快な見出し階層が、読みやすさを担保する。\",\n bodyEn: \"A ratio-based heading hierarchy ensures clear readability.\",\n },\n {\n no: \"03\",\n ja: \"ヘアライン\",\n en: \"Hairlines\",\n bodyJa: \"1pxの罫線のみで領域を区切り、影や塗りに頼らない。\",\n bodyEn: \"Only 1px rules divide regions — no shadows, no fills.\",\n },\n {\n no: \"04\",\n ja: \"余白の設計\",\n en: \"Whitespace\",\n bodyJa: \"意図的な空白がリズムを生み、密度を制御する。\",\n bodyEn: \"Deliberate space creates rhythm and controls density.\",\n },\n {\n no: \"05\",\n ja: \"モノクローム\",\n en: \"Monochrome\",\n bodyJa: \"無彩色を基調に、ただ一点のアクセントだけを許容する。\",\n bodyEn: \"An achromatic base allows just a single accent.\",\n },\n {\n no: \"06\",\n ja: \"反転テーマ\",\n en: \"Inverted theme\",\n bodyJa: \"明暗を入れ替えても破綻しない、対称的な配色設計。\",\n bodyEn: \"A symmetric palette that holds up when light and dark swap.\",\n },\n];\n\nexport default function FeatureGridSwiss() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n

\n {en ? \"Design principles\" : \"設計の原理\"}\n

\n

\n Six principles of the International Typographic Style.\n

\n
\n\n
\n {features.map((f) => (\n \n \n {f.no}\n \n

\n {en ? f.en : f.ja}\n

\n

\n {en ? f.bodyEn : f.bodyJa}\n

\n \n ))}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n

\n 設計の原理\n

\n

\n Six principles of the International Typographic Style.\n

\n
\n
\n
\n \n 01\n \n

\n グリッド設計\n

\n

\n 8pt基準のモジュラーグリッドで、すべての要素を整然と配置する。\n

\n
\n
\n \n 02\n \n

\n タイプスケール\n

\n

\n 比率に基づく明快な見出し階層が、読みやすさを担保する。\n

\n
\n
\n \n 03\n \n

\n ヘアライン\n

\n

\n 1pxの罫線のみで領域を区切り、影や塗りに頼らない。\n

\n
\n
\n \n 04\n \n

\n 余白の設計\n

\n

\n 意図的な空白がリズムを生み、密度を制御する。\n

\n
\n
\n \n 05\n \n

\n モノクローム\n

\n

\n 無彩色を基調に、ただ一点のアクセントだけを許容する。\n

\n
\n
\n \n 06\n \n

\n 反転テーマ\n

\n

\n 明暗を入れ替えても破綻しない、対称的な配色設計。\n

\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n

\n 設計の原理\n

\n

\n Six principles of the International Typographic Style.\n

\n
\n
\n
\n \n 01\n \n

\n グリッド設計\n

\n

\n 8pt基準のモジュラーグリッドで、すべての要素を整然と配置する。\n

\n
\n
\n \n 02\n \n

\n タイプスケール\n

\n

\n 比率に基づく明快な見出し階層が、読みやすさを担保する。\n

\n
\n
\n \n 03\n \n

\n ヘアライン\n

\n

\n 1pxの罫線のみで領域を区切り、影や塗りに頼らない。\n

\n
\n
\n \n 04\n \n

\n 余白の設計\n

\n

\n 意図的な空白がリズムを生み、密度を制御する。\n

\n
\n
\n \n 05\n \n

\n モノクローム\n

\n

\n 無彩色を基調に、ただ一点のアクセントだけを許容する。\n

\n
\n
\n \n 06\n \n

\n 反転テーマ\n

\n

\n 明暗を入れ替えても破綻しない、対称的な配色設計。\n

\n
\n
\n
\n
\n \n" }, { "id": "minimal-invert-hero", @@ -1141,7 +1141,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"反転ヒーロー\",\n category: \"ミニマル\",\n description: \"暗転したダーク基調のヒーロー。明暗反転の対称設計。\",\n align: \"full\",\n isNew: true,\n tags: [\"minimal\", \"swiss\", \"dark\"],\n principle: \"明暗の反転が強いコントラストを生み、見出しに重みを与える。\",\n};\n\nexport default function InvertHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n
\n \n Inverted / Dark\n \n \n 09 — 12\n \n
\n\n
\n

\n {en ? (\n <>\n In the dark,\n
\n form becomes clear.\n \n ) : (\n <>\n 闇の中でこそ、\n
\n 形は明瞭になる。\n \n )}\n

\n
\n\n
\n

\n {en\n ? \"Truly minimal design holds together even when light and dark are reversed — same grid, same discipline.\"\n : \"明暗を反転しても崩れない設計こそ、真にミニマルである。同じグリッド、同じ規律。\"}\n

\n
\n \n {en ? \"Get started\" : \"はじめる\"}\n \n \n {en ? \"Docs →\" : \"ドキュメント →\"}\n \n
\n
\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n Inverted / Dark\n \n \n 09 — 12\n \n
\n
\n

\n 闇の中でこそ、\n
\n 形は明瞭になる。\n

\n
\n
\n

\n 明暗を反転しても崩れない設計こそ、真にミニマルである。同じグリッド、同じ規律。\n

\n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n Inverted / Dark\n \n \n 09 — 12\n \n
\n
\n

\n 闇の中でこそ、\n
\n 形は明瞭になる。\n

\n
\n
\n

\n 明暗を反転しても崩れない設計こそ、真にミニマルである。同じグリッド、同じ規律。\n

\n \n
\n
\n
\n
\n \n" }, { "id": "minimal-label-badge-set", @@ -1155,7 +1155,7 @@ ], "standalone": true, "source": "import { ArrowUpRight, Check } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ラベル・バッジ集\",\n category: \"ミニマル\",\n description: \"ミニマルなラベル・タグ・バッジの一式。\",\n align: \"center\",\n isNew: true,\n tags: [\"minimal\", \"swiss\", \"badge\"],\n principle: \"形状を最小化し、種別差を罫線と無彩色の濃淡だけで表現する。\",\n};\n\nexport default function LabelBadgeSet() {\n return (\n
\n
\n
\n Labels\n
\n
\n {[\"Design\", \"Type\", \"Grid\", \"Color\", \"Motion\"].map((l) => (\n \n {l}\n \n ))}\n
\n
\n\n
\n
\n Status\n
\n
\n \n \n Stable\n \n \n Beta\n \n \n \n New\n \n
\n
\n\n
\n
\n Counters\n
\n
\n {[\n { k: \"Issues\", v: \"012\" },\n { k: \"PRs\", v: \"07\" },\n { k: \"Stars\", v: \"248\" },\n ].map((c) => (\n \n {c.k}\n \n {c.v}\n \n \n ))}\n
\n
\n\n
\n
\n Tags\n
\n
\n {[\"minimal\", \"swiss\", \"monochrome\", \"grid\"].map((t) => (\n \n #\n {t}\n \n \n ))}\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n Labels\n
\n
\n \n Design\n \n \n Type\n \n \n Grid\n \n \n Color\n \n \n Motion\n \n
\n
\n
\n
\n Status\n
\n
\n \n \n \n \n \n Stable\n \n \n Beta\n \n \n \n \n New\n \n
\n
\n
\n
\n Counters\n
\n
\n \n Issues\n \n 012\n \n \n \n PRs\n \n 07\n \n \n \n Stars\n \n 248\n \n \n
\n
\n
\n
\n Tags\n
\n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n Labels\n
\n
\n \n Design\n \n \n Type\n \n \n Grid\n \n \n Color\n \n \n Motion\n \n
\n
\n
\n
\n Status\n
\n
\n \n \n \n \n \n Stable\n \n \n Beta\n \n \n \n \n New\n \n
\n
\n
\n
\n Counters\n
\n
\n \n Issues\n \n 012\n \n \n \n PRs\n \n 07\n \n \n \n Stars\n \n 248\n \n \n
\n
\n
\n
\n Tags\n
\n \n
\n
\n \n" }, { "id": "neumorphism-soft-pricing", @@ -1169,7 +1169,7 @@ ], "standalone": true, "source": "import { Check, Sparkles } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ソフトUI価格表\",\n category: \"ニューモーフィズム\",\n description: \"中央のプランをインセットで強調した、3枚の柔らかな価格カード。\",\n align: \"full\",\n isNew: true,\n tags: [\"neumorphism\", \"soft-ui\", \"pricing\"],\n};\n\nconst RAISED = \"shadow-[6px_6px_12px_#a3b1c6,-6px_-6px_12px_#ffffff]\";\nconst INSET = \"shadow-[inset_6px_6px_12px_#a3b1c6,inset_-6px_-6px_12px_#ffffff]\";\n\nconst plans = [\n {\n name: \"スターター\",\n nameEn: \"Starter\",\n price: \"¥0\",\n period: \"/月\",\n periodEn: \"/mo\",\n desc: \"個人で始める方に\",\n descEn: \"For individuals getting started\",\n features: [\n { ja: \"3プロジェクト\", en: \"3 projects\" },\n { ja: \"1GB ストレージ\", en: \"1GB storage\" },\n { ja: \"コミュニティ支援\", en: \"Community support\" },\n ],\n featured: false,\n },\n {\n name: \"プロ\",\n nameEn: \"Pro\",\n price: \"¥1,800\",\n period: \"/月\",\n periodEn: \"/mo\",\n desc: \"成長するチームに\",\n descEn: \"For growing teams\",\n features: [\n { ja: \"無制限プロジェクト\", en: \"Unlimited projects\" },\n { ja: \"100GB ストレージ\", en: \"100GB storage\" },\n { ja: \"優先サポート\", en: \"Priority support\" },\n { ja: \"高度な分析\", en: \"Advanced analytics\" },\n ],\n featured: true,\n },\n {\n name: \"エンタープライズ\",\n nameEn: \"Enterprise\",\n price: \"¥6,400\",\n period: \"/月\",\n periodEn: \"/mo\",\n desc: \"大規模組織に\",\n descEn: \"For large organizations\",\n features: [\n { ja: \"SSO / SAML\", en: \"SSO / SAML\" },\n { ja: \"専任担当\", en: \"Dedicated manager\" },\n { ja: \"SLA 99.9%\", en: \"99.9% SLA\" },\n { ja: \"監査ログ\", en: \"Audit logs\" },\n ],\n featured: false,\n },\n];\n\nexport default function SoftPricing() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n

\n {en ? \"Pricing plans\" : \"料金プラン\"}\n

\n

\n {en\n ? \"Upgrade or downgrade anytime.\"\n : \"いつでもアップグレード・ダウングレード可能。\"}\n

\n
\n\n
\n {plans.map((p) => (\n \n {p.featured && (\n \n \n {en ? \"Popular\" : \"人気\"}\n \n )}\n

{en ? p.nameEn : p.name}

\n

{en ? p.descEn : p.desc}

\n
\n {p.price}\n {en ? p.periodEn : p.period}\n
\n\n
    \n {p.features.map((f) => (\n
  • \n \n \n \n {en ? f.en : f.ja}\n
  • \n ))}\n
\n\n \n {p.featured ? (en ? \"Choose Pro\" : \"プロを選ぶ\") : en ? \"Select\" : \"選択する\"}\n \n
\n ))}\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n

\n 料金プラン\n

\n

\n いつでもアップグレード・ダウングレード可能。\n

\n
\n
\n
\n

\n スターター\n

\n

\n 個人で始める方に\n

\n
\n \n ¥0\n \n \n /月\n \n
\n
    \n
  • \n \n \n \n \n \n \n 3プロジェクト\n
  • \n
  • \n \n \n \n \n \n \n 1GB ストレージ\n
  • \n
  • \n \n \n \n \n \n \n コミュニティ支援\n
  • \n
\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n 人気\n \n

\n プロ\n

\n

\n 成長するチームに\n

\n
\n \n ¥1,800\n \n \n /月\n \n
\n
    \n
  • \n \n \n \n \n \n \n 無制限プロジェクト\n
  • \n
  • \n \n \n \n \n \n \n 100GB ストレージ\n
  • \n
  • \n \n \n \n \n \n \n 優先サポート\n
  • \n
  • \n \n \n \n \n \n \n 高度な分析\n
  • \n
\n \n
\n
\n

\n エンタープライズ\n

\n

\n 大規模組織に\n

\n
\n \n ¥6,400\n \n \n /月\n \n
\n
    \n
  • \n \n \n \n \n \n \n SSO / SAML\n
  • \n
  • \n \n \n \n \n \n \n 専任担当\n
  • \n
  • \n \n \n \n \n \n \n SLA 99.9%\n
  • \n
  • \n \n \n \n \n \n \n 監査ログ\n
  • \n
\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n

\n 料金プラン\n

\n

\n いつでもアップグレード・ダウングレード可能。\n

\n
\n
\n
\n

\n スターター\n

\n

\n 個人で始める方に\n

\n
\n \n ¥0\n \n \n /月\n \n
\n
    \n
  • \n \n \n \n \n \n \n 3プロジェクト\n
  • \n
  • \n \n \n \n \n \n \n 1GB ストレージ\n
  • \n
  • \n \n \n \n \n \n \n コミュニティ支援\n
  • \n
\n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n 人気\n \n

\n プロ\n

\n

\n 成長するチームに\n

\n
\n \n ¥1,800\n \n \n /月\n \n
\n
    \n
  • \n \n \n \n \n \n \n 無制限プロジェクト\n
  • \n
  • \n \n \n \n \n \n \n 100GB ストレージ\n
  • \n
  • \n \n \n \n \n \n \n 優先サポート\n
  • \n
  • \n \n \n \n \n \n \n 高度な分析\n
  • \n
\n \n
\n
\n

\n エンタープライズ\n

\n

\n 大規模組織に\n

\n
\n \n ¥6,400\n \n \n /月\n \n
\n
    \n
  • \n \n \n \n \n \n \n SSO / SAML\n
  • \n
  • \n \n \n \n \n \n \n 専任担当\n
  • \n
  • \n \n \n \n \n \n \n SLA 99.9%\n
  • \n
  • \n \n \n \n \n \n \n 監査ログ\n
  • \n
\n \n
\n
\n
\n \n" }, { "id": "neumorphism-soft-stats", @@ -1184,7 +1184,7 @@ ], "standalone": true, "source": "import { Activity, Droplet, Flame, Footprints } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ソフト統計リング\",\n category: \"ニューモーフィズム\",\n description: \"押し込み式の進捗リング(SVG)を備えた、柔らかな統計タイル。\",\n align: \"full\",\n isNew: true,\n tags: [\"neumorphism\", \"soft-ui\", \"stats\", \"rings\"],\n};\n\nconst RAISED = \"shadow-[6px_6px_12px_#a3b1c6,-6px_-6px_12px_#ffffff]\";\n\nconst stats = [\n { icon: Footprints, label: \"歩数\", labelEn: \"Steps\", value: \"8,420\", pct: 72, accent: false },\n { icon: Flame, label: \"消費カロリー\", labelEn: \"Calories\", value: \"612\", unit: \"kcal\", pct: 58, accent: true },\n { icon: Activity, label: \"心拍\", labelEn: \"Heart rate\", value: \"74\", unit: \"bpm\", pct: 49, accent: false },\n { icon: Droplet, label: \"水分\", labelEn: \"Water\", value: \"1.6\", unit: \"L\", pct: 64, accent: false },\n];\n\nfunction Ring({ pct, accent }: { pct: number; accent: boolean }) {\n const r = 30;\n const c = 2 * Math.PI * r;\n const offset = c - (pct / 100) * c;\n return (\n
\n \n \n \n \n {pct}%\n
\n );\n}\n\nexport default function SoftStats() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n

\n {en ? \"Today's activity\" : \"今日のアクティビティ\"}\n

\n

\n {en ? \"Almost at your goal.\" : \"目標まであと一息。\"}\n

\n
\n \n {en ? \"61% done\" : \"61% 達成\"}\n \n
\n\n
\n {stats.map((s) => (\n
\n \n \n \n {en ? s.labelEn : s.label}\n \n

\n {s.value}\n {s.unit && {s.unit}}\n

\n
\n ))}\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n

\n 今日のアクティビティ\n

\n

\n 目標まであと一息。\n

\n
\n \n 61% 達成\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n 72%\n \n
\n \n \n \n \n \n \n \n \n \n \n \n 歩数\n \n

\n 8,420\n

\n
\n
\n
\n \n \n \n \n \n \n \n 58%\n \n
\n \n \n \n \n \n 消費カロリー\n \n

\n 612\n \n kcal\n \n

\n
\n
\n
\n \n \n \n \n \n \n \n 49%\n \n
\n \n \n \n \n \n 心拍\n \n

\n 74\n \n bpm\n \n

\n
\n
\n
\n \n \n \n \n \n \n \n 64%\n \n
\n \n \n \n \n \n 水分\n \n

\n 1.6\n \n L\n \n

\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n

\n 今日のアクティビティ\n

\n

\n 目標まであと一息。\n

\n
\n \n 61% 達成\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n 72%\n \n
\n \n \n \n \n \n \n \n \n \n \n \n 歩数\n \n

\n 8,420\n

\n
\n
\n
\n \n \n \n \n \n \n \n 58%\n \n
\n \n \n \n \n \n 消費カロリー\n \n

\n 612\n \n kcal\n \n

\n
\n
\n
\n \n \n \n \n \n \n \n 49%\n \n
\n \n \n \n \n \n 心拍\n \n

\n 74\n \n bpm\n \n

\n
\n
\n
\n \n \n \n \n \n \n \n 64%\n \n
\n \n \n \n \n \n 水分\n \n

\n 1.6\n \n L\n \n

\n
\n
\n
\n \n" }, { "id": "nordic-nordic-card", @@ -1198,7 +1198,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"北欧コンテンツカード\",\n category: \"北欧\",\n description: \"ぬくもりのあるコンテンツカード。\",\n align: \"center\",\n isNew: true,\n tags: [\"nordic\", \"scandinavian\", \"hygge\"],\n principle: \"やわらかな角丸と淡い影が安心感を与え、読み進めを促す。\",\n};\n\nexport default function NordicCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* line-art landscape header */}\n
\n \n \n \n \n \n \n Journal\n \n
\n\n
\n

\n {en ? \"June 2026 · 4 min read\" : \"2026年6月 · 読了 4分\"}\n

\n

\n {en\n ? \"Light and space: making winter feel warm\"\n : \"冬を心地よく過ごす、灯りと余白の整え方\"}\n

\n

\n {en\n ? \"In the dark season, cherish the rhythm of daily life. With candlelight and roomy, unhurried spaces, here are small ways to welcome hygge into the home.\"\n : \"暗い季節こそ、暮らしのリズムを大切に。キャンドルの灯りとゆったりとした間取りで、家のなかにhyggeを招き入れる小さな工夫を。\"}\n

\n\n
\n
\n \n A\n \n
\n

{en ? \"Astrid\" : \"アストリッド\"}

\n

\n {en ? \"Editorial\" : \"編集部\"}\n

\n
\n
\n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n Journal\n \n
\n
\n

\n 2026年6月 · 読了 4分\n

\n

\n 冬を心地よく過ごす、灯りと余白の整え方\n

\n

\n 暗い季節こそ、暮らしのリズムを大切に。キャンドルの灯りとゆったりとした間取りで、家のなかにhyggeを招き入れる小さな工夫を。\n

\n
\n
\n \n A\n \n
\n

\n アストリッド\n

\n

\n 編集部\n

\n
\n
\n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n Journal\n \n
\n
\n

\n 2026年6月 · 読了 4分\n

\n

\n 冬を心地よく過ごす、灯りと余白の整え方\n

\n

\n 暗い季節こそ、暮らしのリズムを大切に。キャンドルの灯りとゆったりとした間取りで、家のなかにhyggeを招き入れる小さな工夫を。\n

\n
\n
\n \n A\n \n
\n

\n アストリッド\n

\n

\n 編集部\n

\n
\n
\n \n
\n
\n
\n \n" }, { "id": "nordic-nordic-feature", @@ -1212,7 +1212,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"北欧フィーチャー\",\n category: \"北欧\",\n description: \"ラインアイコンを添えた穏やかな機能セクション。\",\n align: \"full\",\n isNew: true,\n tags: [\"nordic\", \"scandinavian\", \"hygge\"],\n principle: \"細線アイコンと均等な間隔が、価値の並列を静かに整理して伝える。\",\n};\n\ntype IconKey = \"leaf\" | \"mountain\" | \"sun\" | \"weave\";\n\nconst features: { icon: IconKey; ja: string; en: string; bodyJa: string; bodyEn: string; tint: string }[] = [\n {\n icon: \"leaf\",\n ja: \"自然由来の素材\",\n en: \"Natural materials\",\n bodyJa: \"持続可能な森から届く木材とリネン。手に触れるたび温もりを感じます。\",\n bodyEn: \"Wood and linen from sustainable forests — warmth you feel with every touch.\",\n tint: \"#8a9a7b\",\n },\n {\n icon: \"sun\",\n ja: \"やわらかな光設計\",\n en: \"Soft lighting\",\n bodyJa: \"北欧の長い夜に寄り添う、目にやさしい灯りの色味を追求しました。\",\n bodyEn: \"A gentle light tone crafted to ease the long Nordic nights.\",\n tint: \"#c08457\",\n },\n {\n icon: \"mountain\",\n ja: \"長く使える堅牢さ\",\n en: \"Built to last\",\n bodyJa: \"飽きのこない形と確かな作り。世代を越えて受け継げる道具です。\",\n bodyEn: \"Timeless forms and solid craft — tools to pass down through generations.\",\n tint: \"#7d92a3\",\n },\n {\n icon: \"weave\",\n ja: \"職人の手仕事\",\n en: \"Artisan handcraft\",\n bodyJa: \"小さな工房で一つひとつ丁寧に。folkの模様に物語を編み込みます。\",\n bodyEn: \"Made one by one in a small workshop, weaving stories into folk patterns.\",\n tint: \"#c08457\",\n },\n];\n\nexport default function NordicFeature() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n \n Our Philosophy\n \n

\n {en\n ? \"True comfort, found within simplicity.\"\n : \"シンプルさのなかに、確かな心地よさを。\"}\n

\n
\n\n
\n {features.map((f) => (\n
\n \n \n
\n

{en ? f.en : f.ja}

\n

\n {en ? f.bodyEn : f.bodyJa}\n

\n
\n ))}\n
\n
\n \n );\n}\n\nfunction LineIcon({ name }: { name: IconKey }) {\n const common = {\n className: \"h-7 w-7\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 1.8,\n strokeLinecap: \"round\" as const,\n strokeLinejoin: \"round\" as const,\n viewBox: \"0 0 24 24\",\n };\n switch (name) {\n case \"leaf\":\n return (\n \n \n \n \n );\n case \"mountain\":\n return (\n \n \n \n \n );\n case \"sun\":\n return (\n \n \n \n \n );\n case \"weave\":\n return (\n \n \n \n );\n }\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n \n Our Philosophy\n \n

\n シンプルさのなかに、確かな心地よさを。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 自然由来の素材\n

\n

\n 持続可能な森から届く木材とリネン。手に触れるたび温もりを感じます。\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n やわらかな光設計\n

\n

\n 北欧の長い夜に寄り添う、目にやさしい灯りの色味を追求しました。\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 長く使える堅牢さ\n

\n

\n 飽きのこない形と確かな作り。世代を越えて受け継げる道具です。\n

\n
\n
\n
\n \n \n \n \n
\n

\n 職人の手仕事\n

\n

\n 小さな工房で一つひとつ丁寧に。folkの模様に物語を編み込みます。\n

\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n Our Philosophy\n \n

\n シンプルさのなかに、確かな心地よさを。\n

\n
\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 自然由来の素材\n

\n

\n 持続可能な森から届く木材とリネン。手に触れるたび温もりを感じます。\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n やわらかな光設計\n

\n

\n 北欧の長い夜に寄り添う、目にやさしい灯りの色味を追求しました。\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n 長く使える堅牢さ\n

\n

\n 飽きのこない形と確かな作り。世代を越えて受け継げる道具です。\n

\n
\n
\n
\n \n \n \n \n
\n

\n 職人の手仕事\n

\n

\n 小さな工房で一つひとつ丁寧に。folkの模様に物語を編み込みます。\n

\n
\n
\n
\n
\n \n" }, { "id": "nordic-nordic-footer", @@ -1226,7 +1226,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"北欧フッター\",\n category: \"北欧\",\n description: \"ぬくもりのあるミニマルなフッター。\",\n align: \"full\",\n isNew: true,\n tags: [\"nordic\", \"scandinavian\", \"hygge\"],\n principle: \"整理された列と淡い区切りで、情報量があっても圧迫感を与えない。\",\n};\n\nconst groups: {\n id: string;\n titleJa: string;\n titleEn: string;\n links: { ja: string; en: string }[];\n}[] = [\n {\n id: \"collection\",\n titleJa: \"コレクション\",\n titleEn: \"Collection\",\n links: [\n { ja: \"椅子と座\", en: \"Chairs & seating\" },\n { ja: \"灯り\", en: \"Lighting\" },\n { ja: \"テキスタイル\", en: \"Textiles\" },\n { ja: \"器\", en: \"Tableware\" },\n ],\n },\n {\n id: \"brand\",\n titleJa: \"ブランド\",\n titleEn: \"Brand\",\n links: [\n { ja: \"わたしたちの物語\", en: \"Our story\" },\n { ja: \"工房を訪ねる\", en: \"Visit the workshop\" },\n { ja: \"持続可能性\", en: \"Sustainability\" },\n { ja: \"採用\", en: \"Careers\" },\n ],\n },\n {\n id: \"support\",\n titleJa: \"サポート\",\n titleEn: \"Support\",\n links: [\n { ja: \"配送と返品\", en: \"Shipping & returns\" },\n { ja: \"お手入れ方法\", en: \"Care guide\" },\n { ja: \"よくある質問\", en: \"FAQ\" },\n { ja: \"お問い合わせ\", en: \"Contact\" },\n ],\n },\n];\n\nexport default function NordicFooter() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n
\n
\n \n \n \n \n Bjørk\n
\n

\n {en\n ? \"Quiet comfort at the center of daily life — tools born of Nordic wisdom.\"\n : \"静かな心地よさを、暮らしのまんなかに。北欧の知恵から生まれた道具たち。\"}\n

\n
\n {[\"instagram\", \"pin\", \"mail\"].map((s) => (\n \n \n \n ))}\n
\n
\n\n {groups.map((g) => (\n
\n

\n {en ? g.titleEn : g.titleJa}\n

\n
    \n {g.links.map((l) => (\n
  • \n \n {en ? l.en : l.ja}\n \n
  • \n ))}\n
\n
\n ))}\n
\n\n
\n

\n {en\n ? \"© 2026 Bjørk. Gentle, in every season.\"\n : \"© 2026 Bjørk. すべての季節に、やさしく。\"}\n

\n \n
\n
\n
\n );\n}\n\nfunction Social({ name }: { name: string }) {\n const p = {\n className: \"h-4 w-4\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 1.8,\n strokeLinecap: \"round\" as const,\n strokeLinejoin: \"round\" as const,\n viewBox: \"0 0 24 24\",\n };\n if (name === \"mail\")\n return (\n \n \n \n \n );\n if (name === \"pin\")\n return (\n \n \n \n \n );\n return (\n \n \n \n \n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n \n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n \n \n" }, { "id": "nordic-nordic-gallery", @@ -1240,7 +1240,7 @@ ], "standalone": true, "source": "import type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"北欧ギャラリー\",\n category: \"北欧\",\n description: \"キャプション付きのやわらかなインテリアギャラリー。\",\n align: \"full\",\n isNew: true,\n tags: [\"nordic\", \"scandinavian\", \"hygge\"],\n principle: \"落ち着いた色面のグリッドとキャプションで、世界観を一貫して見せる。\",\n};\n\ntype Scene = \"window\" | \"shelf\" | \"plant\" | \"lamp\" | \"rug\" | \"chair\";\n\nconst items: { scene: Scene; ja: string; en: string; place: string; bg: string; ink: string; span: string }[] = [\n { scene: \"window\", ja: \"朝の光\", en: \"Morning light\", place: \"Living Room\", bg: \"#eae5d8\", ink: \"#c08457\", span: \"sm:col-span-2 sm:row-span-2\" },\n { scene: \"plant\", ja: \"緑のある角\", en: \"A green corner\", place: \"Corner\", bg: \"#e3e7dd\", ink: \"#8a9a7b\", span: \"\" },\n { scene: \"lamp\", ja: \"やわらかな灯り\", en: \"Soft glow\", place: \"Reading Nook\", bg: \"#e7e3da\", ink: \"#c08457\", span: \"\" },\n { scene: \"shelf\", ja: \"余白の棚\", en: \"Open shelving\", place: \"Hallway\", bg: \"#e1e6e9\", ink: \"#7d92a3\", span: \"\" },\n { scene: \"rug\", ja: \"編みのラグ\", en: \"Woven rug\", place: \"Bedroom\", bg: \"#eae5d8\", ink: \"#8a9a7b\", span: \"\" },\n];\n\nexport default function NordicGallery() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n
\n \n Interiors\n \n

\n {en ? \"Scenes of everyday life\" : \"暮らしの一場面\"}\n

\n
\n \n
\n\n
\n {items.map((it) => (\n \n \n
\n

\n {it.place}\n

\n

\n {en ? it.en : it.ja}\n

\n
\n \n ))}\n
\n
\n
\n );\n}\n\nfunction SceneArt({ scene, ink }: { scene: Scene; ink: string }) {\n const p = {\n className: \"absolute inset-0 h-full w-full p-8\",\n fill: \"none\",\n stroke: ink,\n strokeWidth: 2,\n strokeLinecap: \"round\" as const,\n strokeLinejoin: \"round\" as const,\n viewBox: \"0 0 120 120\",\n preserveAspectRatio: \"xMidYMid meet\",\n };\n switch (scene) {\n case \"window\":\n return (\n \n \n \n \n \n );\n case \"plant\":\n return (\n \n \n \n \n );\n case \"lamp\":\n return (\n \n \n \n \n );\n case \"shelf\":\n return (\n \n \n \n \n \n \n );\n case \"rug\":\n return (\n \n \n \n \n );\n case \"chair\":\n return (\n \n \n \n );\n }\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n Interiors\n \n

\n 暮らしの一場面\n

\n
\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n Living Room\n

\n

\n 朝の光\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n Corner\n

\n

\n 緑のある角\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n Reading Nook\n

\n

\n やわらかな灯り\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n Hallway\n

\n

\n 余白の棚\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n Bedroom\n

\n

\n 編みのラグ\n

\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n Interiors\n \n

\n 暮らしの一場面\n

\n
\n \n
\n
\n
\n \n \n \n \n \n \n \n \n
\n

\n Living Room\n

\n

\n 朝の光\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n Corner\n

\n

\n 緑のある角\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n Reading Nook\n

\n

\n やわらかな灯り\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n Hallway\n

\n

\n 余白の棚\n

\n
\n
\n
\n \n \n \n \n \n \n
\n

\n Bedroom\n

\n

\n 編みのラグ\n

\n
\n
\n
\n
\n
\n \n" }, { "id": "offers-comparison-matrix", @@ -1254,7 +1254,7 @@ ], "standalone": true, "source": "import { Fragment } from \"react\";\nimport { Check, Minus, Sparkles } from \"lucide-react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"プラン比較マトリクス\",\n category: \"価格・オファー\",\n description:\n \"3プランの機能をチェック/ダッシュで比較。固定ヘッダーとおすすめ列のハイライト付き。\",\n align: \"full\",\n level: \"advanced\",\n isNew: true,\n tags: [\"pricing\", \"comparison\", \"matrix\"],\n principle:\n \"選択のパラドックス回避。機能差を一目で可視化し、おすすめ列を強調することで認知負荷を下げ、迷いによる離脱を防ぐ。\",\n};\n\ntype Cell = boolean | string;\n\nconst PLANS = [\"Starter\", \"Pro\", \"Enterprise\"] as const;\nconst RECOMMENDED = 1; // Pro\n\nconst PRICES = [\"¥980\", \"¥4,800\", \"お問い合わせ\"];\nconst PRICES_EN = [\"$9\", \"$48\", \"Contact us\"];\n\nconst GROUPS: {\n group: string;\n groupEn: string;\n rows: { label: string; labelEn: string; cells: Cell[]; cellsEn: Cell[] }[];\n}[] = [\n {\n group: \"基本機能\",\n groupEn: \"Core features\",\n rows: [\n { label: \"プロジェクト数\", labelEn: \"Projects\", cells: [\"3\", \"無制限\", \"無制限\"], cellsEn: [\"3\", \"Unlimited\", \"Unlimited\"] },\n { label: \"メンバー\", labelEn: \"Members\", cells: [\"1\", \"10\", \"無制限\"], cellsEn: [\"1\", \"10\", \"Unlimited\"] },\n { label: \"ストレージ\", labelEn: \"Storage\", cells: [\"5GB\", \"200GB\", \"1TB+\"], cellsEn: [\"5GB\", \"200GB\", \"1TB+\"] },\n ],\n },\n {\n group: \"コラボレーション\",\n groupEn: \"Collaboration\",\n rows: [\n { label: \"共同編集\", labelEn: \"Real-time editing\", cells: [false, true, true], cellsEn: [false, true, true] },\n { label: \"コメント / レビュー\", labelEn: \"Comments / reviews\", cells: [false, true, true], cellsEn: [false, true, true] },\n { label: \"バージョン履歴\", labelEn: \"Version history\", cells: [\"7日\", \"90日\", \"無期限\"], cellsEn: [\"7 days\", \"90 days\", \"Unlimited\"] },\n ],\n },\n {\n group: \"セキュリティ・運用\",\n groupEn: \"Security & operations\",\n rows: [\n { label: \"SSO / SAML\", labelEn: \"SSO / SAML\", cells: [false, false, true], cellsEn: [false, false, true] },\n { label: \"監査ログ\", labelEn: \"Audit logs\", cells: [false, false, true], cellsEn: [false, false, true] },\n { label: \"SLA 保証\", labelEn: \"SLA guarantee\", cells: [false, false, \"99.99%\"], cellsEn: [false, false, \"99.99%\"] },\n { label: \"専任サポート\", labelEn: \"Dedicated support\", cells: [false, true, true], cellsEn: [false, true, true] },\n ],\n },\n];\n\nfunction CellView({ value, highlight }: { value: Cell; highlight: boolean }) {\n if (value === true)\n return (\n \n );\n if (value === false)\n return ;\n return (\n \n {value}\n \n );\n}\n\nexport default function ComparisonMatrix() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n \n \n \n \n {PLANS.map((plan, i) => (\n \n
\n {i === RECOMMENDED && (\n \n {en ? \"Recommended\" : \"おすすめ\"}\n \n )}\n {plan}\n \n {en ? PRICES_EN[i] : PRICES[i]}\n \n
\n \n ))}\n
\n \n \n {GROUPS.map((g) => (\n \n \n \n {en ? g.groupEn : g.group}\n \n \n {g.rows.map((row) => (\n \n \n {(en ? row.cellsEn : row.cells).map((cell, i) => (\n \n \n \n ))}\n \n ))}\n \n ))}\n \n \n \n \n ))}\n \n \n
\n {en ? \"Feature\" : \"機能\"}\n
{en ? row.labelEn : row.label}
\n {PLANS.map((plan, i) => (\n \n \n {i === PLANS.length - 1 ? (en ? \"Contact us\" : \"相談する\") : (en ? \"Choose\" : \"選ぶ\")}\n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n 機能\n \n
\n \n Starter\n \n \n ¥980\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n おすすめ\n
\n \n Pro\n \n \n ¥4,800\n \n
\n
\n
\n \n Enterprise\n \n \n お問い合わせ\n \n
\n
\n 基本機能\n
\n プロジェクト数\n \n \n 3\n \n \n \n 無制限\n \n \n \n 無制限\n \n
\n メンバー\n \n \n 1\n \n \n \n 10\n \n \n \n 無制限\n \n
\n ストレージ\n \n \n 5GB\n \n \n \n 200GB\n \n \n \n 1TB+\n \n
\n コラボレーション\n
\n 共同編集\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n コメント / レビュー\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n バージョン履歴\n \n \n 7日\n \n \n \n 90日\n \n \n \n 無期限\n \n
\n セキュリティ・運用\n
\n SSO / SAML\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n 監査ログ\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n SLA 保証\n \n \n \n \n \n \n \n \n \n \n \n \n 99.99%\n \n
\n 専任サポート\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n 機能\n \n
\n \n Starter\n \n \n ¥980\n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n おすすめ\n
\n \n Pro\n \n \n ¥4,800\n \n
\n
\n
\n \n Enterprise\n \n \n お問い合わせ\n \n
\n
\n 基本機能\n
\n プロジェクト数\n \n \n 3\n \n \n \n 無制限\n \n \n \n 無制限\n \n
\n メンバー\n \n \n 1\n \n \n \n 10\n \n \n \n 無制限\n \n
\n ストレージ\n \n \n 5GB\n \n \n \n 200GB\n \n \n \n 1TB+\n \n
\n コラボレーション\n
\n 共同編集\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n コメント / レビュー\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n バージョン履歴\n \n \n 7日\n \n \n \n 90日\n \n \n \n 無期限\n \n
\n セキュリティ・運用\n
\n SSO / SAML\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n 監査ログ\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n SLA 保証\n \n \n \n \n \n \n \n \n \n \n \n \n 99.99%\n \n
\n 専任サポート\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n
\n
\n
\n \n" }, { "id": "offers-enterprise-contact", @@ -1268,7 +1268,7 @@ ], "standalone": true, "source": "import { Building2, ShieldCheck, Headset, Plug, ArrowRight } from \"lucide-react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n} from \"@/components/ui/card\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"エンタープライズ問い合わせ\",\n category: \"価格・オファー\",\n description:\n \"価格を表示せず、機能と専任サポートを訴求して商談につなげるエンタープライズ向けカード。\",\n align: \"center\",\n level: \"advanced\",\n isNew: true,\n tags: [\"enterprise\", \"contact\", \"sales\"],\n principle:\n \"価格の非提示(アンカー回避)。価格を出さず価値を会話で伝えることで価格基準点の固定を避け、要件に応じた高単価提案を可能にする。\",\n};\n\nconst FEATURES = [\n { icon: ShieldCheck, label: \"SSO / SAML・監査ログ・SLA 99.99%\", labelEn: \"SSO / SAML, audit logs, 99.99% SLA\" },\n { icon: Headset, label: \"専任カスタマーサクセス担当 (CSM)\", labelEn: \"Dedicated customer success manager (CSM)\" },\n { icon: Plug, label: \"カスタム連携・オンプレ / 専用クラウド\", labelEn: \"Custom integrations, on-prem / private cloud\" },\n { icon: Building2, label: \"全社導入・ボリューム割引・請求書払い\", labelEn: \"Company-wide rollout, volume discounts, invoicing\" },\n];\n\nexport default function EnterpriseContact() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n \n \n
\n
\n \n
\n Enterprise\n
\n

\n {en ? \"A dedicated plan for large organizations\" : \"大規模組織のための専用プラン\"}\n

\n

\n {en\n ? \"We tailor it to your requirements. Let's start with a conversation.\"\n : \"要件に合わせて設計します。まずはお話を聞かせてください。\"}\n

\n
\n \n {en ? \"Custom quote\" : \"カスタム見積もり\"}\n \n
\n

\n {en\n ? \"We propose the best price based on your scale and requirements.\"\n : \"利用規模・必要要件に応じて最適な価格をご提案します。\"}\n

\n
\n\n \n
    \n {FEATURES.map(({ icon: Icon, label, labelEn }) => (\n
  • \n \n \n \n {en ? labelEn : label}\n
  • \n ))}\n
\n
\n\n \n \n \n

\n {en ? \"We usually reply within one business day.\" : \"通常1営業日以内にご返信します。\"}\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n Enterprise\n
\n
\n

\n 大規模組織のための専用プラン\n

\n

\n 要件に合わせて設計します。まずはお話を聞かせてください。\n

\n
\n \n カスタム見積もり\n \n
\n

\n 利用規模・必要要件に応じて最適な価格をご提案します。\n

\n
\n
\n
    \n
  • \n \n \n \n \n \n \n \n \n \n SSO / SAML・監査ログ・SLA 99.99%\n \n
  • \n
  • \n \n \n \n \n \n \n \n \n \n 専任カスタマーサクセス担当 (CSM)\n \n
  • \n
  • \n \n \n \n \n \n \n \n \n \n \n \n \n \n カスタム連携・オンプレ / 専用クラウド\n \n
  • \n
  • \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 全社導入・ボリューム割引・請求書払い\n \n
  • \n
\n
\n
\n \n \n

\n 通常1営業日以内にご返信します。\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n Enterprise\n
\n
\n

\n 大規模組織のための専用プラン\n

\n

\n 要件に合わせて設計します。まずはお話を聞かせてください。\n

\n
\n \n カスタム見積もり\n \n
\n

\n 利用規模・必要要件に応じて最適な価格をご提案します。\n

\n
\n
\n
    \n
  • \n \n \n \n \n \n \n \n \n \n SSO / SAML・監査ログ・SLA 99.99%\n \n
  • \n
  • \n \n \n \n \n \n \n \n \n \n 専任カスタマーサクセス担当 (CSM)\n \n
  • \n
  • \n \n \n \n \n \n \n \n \n \n \n \n \n \n カスタム連携・オンプレ / 専用クラウド\n \n
  • \n
  • \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 全社導入・ボリューム割引・請求書払い\n \n
  • \n
\n
\n
\n \n \n

\n 通常1営業日以内にご返信します。\n

\n
\n
\n \n" }, { "id": "offers-money-back-card", @@ -1282,7 +1282,7 @@ ], "standalone": true, "source": "import { Check, ShieldCheck, RotateCcw } from \"lucide-react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n} from \"@/components/ui/card\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"返金保証つき料金カード\",\n category: \"価格・オファー\",\n description:\n \"30日返金保証を前面に出してリスクを取り除く料金カード。保証シール付き。\",\n align: \"center\",\n level: \"advanced\",\n isNew: true,\n tags: [\"pricing\", \"guarantee\", \"risk-reversal\"],\n principle:\n \"リスクリバーサル(損失回避の中和)。返金保証で購入の損失リスクを売り手側へ移し、決断の心理的ハードルを下げる。\",\n};\n\nconst FEATURES = [\n { ja: \"全機能フルアクセス\", en: \"Full access to all features\" },\n { ja: \"メンバー無制限\", en: \"Unlimited members\" },\n { ja: \"優先サポート\", en: \"Priority support\" },\n { ja: \"毎週の新テンプレート\", en: \"New templates every week\" },\n];\n\nexport default function MoneyBackCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* 保証シールが箱の外に出るので、そのぶんの余白を外側に確保する */}\n \n {/* 保証シール */}\n
\n
\n 30\n \n DAY\n
\n GUARANTEE\n
\n
\n
\n\n \n \n {en ? \"Pro plan\" : \"Proプラン\"}\n \n
\n ¥4,800\n {en ? \"/ mo\" : \"/ 月\"}\n
\n

\n {en\n ? \"Try it first — if it's not for you, we'll refund every yen.\"\n : \"まずは試して、合わなければ全額返金します。\"}\n

\n
\n\n \n
    \n {FEATURES.map((f) => (\n
  • \n \n {en ? f.en : f.ja}\n
  • \n ))}\n
\n\n
\n \n
\n

\n {en ? \"30-day full money-back guarantee\" : \"30日間 全額返金保証\"}\n

\n

\n {en\n ? \"Full refund within 30 days, no questions asked. There's no risk to you.\"\n : \"理由を問わず、30日以内なら全額返金。あなたにリスクはありません。\"}\n

\n
\n
\n
\n\n \n \n

\n \n {en ? \"Cancel anytime in one click\" : \"解約はいつでもワンクリック\"}\n

\n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n 30\n \n \n DAY\n
\n GUARANTEE\n
\n
\n
\n
\n
\n Proプラン\n
\n
\n \n ¥4,800\n \n \n / 月\n \n
\n

\n まずは試して、合わなければ全額返金します。\n

\n
\n
\n
    \n
  • \n \n \n \n \n \n 全機能フルアクセス\n \n
  • \n
  • \n \n \n \n \n \n メンバー無制限\n \n
  • \n
  • \n \n \n \n \n \n 優先サポート\n \n
  • \n
  • \n \n \n \n \n \n 毎週の新テンプレート\n \n
  • \n
\n
\n \n \n \n \n \n \n
\n

\n 30日間 全額返金保証\n

\n

\n 理由を問わず、30日以内なら全額返金。あなたにリスクはありません。\n

\n
\n
\n
\n
\n \n

\n \n \n \n \n \n \n 解約はいつでもワンクリック\n

\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n 30\n \n \n DAY\n
\n GUARANTEE\n
\n
\n
\n
\n
\n Proプラン\n
\n
\n \n ¥4,800\n \n \n / 月\n \n
\n

\n まずは試して、合わなければ全額返金します。\n

\n
\n
\n
    \n
  • \n \n \n \n \n \n 全機能フルアクセス\n \n
  • \n
  • \n \n \n \n \n \n メンバー無制限\n \n
  • \n
  • \n \n \n \n \n \n 優先サポート\n \n
  • \n
  • \n \n \n \n \n \n 毎週の新テンプレート\n \n
  • \n
\n
\n \n \n \n \n \n \n
\n

\n 30日間 全額返金保証\n

\n

\n 理由を問わず、30日以内なら全額返金。あなたにリスクはありません。\n

\n
\n
\n
\n
\n \n

\n \n \n \n \n \n \n 解約はいつでもワンクリック\n

\n
\n
\n
\n \n" }, { "id": "offers-value-stack-offer", @@ -1296,7 +1296,7 @@ ], "standalone": true, "source": "import { Check, Gift } from \"lucide-react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Card,\n CardContent,\n CardFooter,\n CardHeader,\n} from \"@/components/ui/card\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"バリュースタック・オファー\",\n category: \"価格・オファー\",\n description:\n \"同梱特典を1つずつ金額化して総額を提示し、それを大きく下回る価格で見せるオファー。\",\n align: \"center\",\n level: \"advanced\",\n isNew: true,\n tags: [\"pricing\", \"value-stack\", \"bundle\"],\n principle:\n \"価値の積み上げ(バリュースタッキング)。個々の価値を可視化して総額を膨らませ、最終価格との差を「圧倒的なお得感」として知覚させる。\",\n};\n\nconst ITEMS = [\n { label: \"コアプラットフォーム(年間ライセンス)\", labelEn: \"Core platform (annual license)\", value: 48000 },\n { label: \"プレミアムテンプレート集 120点\", labelEn: \"120 premium templates\", value: 24000 },\n { label: \"1対1オンボーディング(90分)\", labelEn: \"1-on-1 onboarding (90 min)\", value: 30000 },\n { label: \"限定コミュニティへの永久アクセス\", labelEn: \"Lifetime access to the private community\", value: 18000 },\n { label: \"四半期ごとの戦略レビュー\", labelEn: \"Quarterly strategy reviews\", value: 36000 },\n];\n\nfunction yen(n: number) {\n return \"¥\" + n.toLocaleString(\"ja-JP\");\n}\n\nexport default function ValueStackOffer() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n const total = ITEMS.reduce((s, i) => s + i.value, 0);\n const price = 49800;\n const saved = total - price;\n\n return (\n \n \n
\n \n {en ? \"Limited-time bundle\" : \"期間限定バンドル\"}\n \n {en ? \"Ends today\" : \"本日締切\"}\n
\n

\n {en ? \"The complete growth package\" : \"グロース完全パッケージ\"}\n

\n

\n {en\n ? \"Everything you need to get results, bundled into one.\"\n : \"成果に必要なものを、ひとつにまとめました。\"}\n

\n
\n\n \n
    \n {ITEMS.map((item) => (\n \n \n \n {en ? item.labelEn : item.label}\n \n \n {yen(item.value)}\n \n \n ))}\n
\n\n
\n {en ? \"Total value\" : \"総額\"}\n \n {yen(total)}\n \n
\n\n
\n

\n {en ? \"Your price\" : \"あなたの価格\"}\n

\n

\n {yen(price)}\n

\n

\n {en\n ? `Save ${yen(saved)} (${Math.round((saved / total) * 100)}% off)`\n : `${yen(saved)} お得(${Math.round((saved / total) * 100)}% OFF)`}\n

\n
\n
\n\n \n \n

\n {en\n ? \"Includes a 30-day money-back guarantee · cancel anytime\"\n : \"30日間の返金保証付き・いつでもキャンセル可\"}\n

\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n 期間限定バンドル\n
\n \n 本日締切\n \n
\n

\n グロース完全パッケージ\n

\n

\n 成果に必要なものを、ひとつにまとめました。\n

\n
\n
\n
    \n
  • \n \n \n \n \n \n \n コアプラットフォーム(年間ライセンス)\n \n \n \n ¥48,000\n \n
  • \n
  • \n \n \n \n \n \n \n プレミアムテンプレート集 120点\n \n \n \n ¥24,000\n \n
  • \n
  • \n \n \n \n \n \n \n 1対1オンボーディング(90分)\n \n \n \n ¥30,000\n \n
  • \n
  • \n \n \n \n \n \n \n 限定コミュニティへの永久アクセス\n \n \n \n ¥18,000\n \n
  • \n
  • \n \n \n \n \n \n \n 四半期ごとの戦略レビュー\n \n \n \n ¥36,000\n \n
  • \n
\n
\n \n 総額\n \n \n ¥156,000\n \n
\n
\n

\n あなたの価格\n

\n

\n ¥49,800\n

\n

\n ¥106,200 お得(68% OFF)\n

\n
\n
\n
\n \n

\n 30日間の返金保証付き・いつでもキャンセル可\n

\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n 期間限定バンドル\n
\n \n 本日締切\n \n
\n

\n グロース完全パッケージ\n

\n

\n 成果に必要なものを、ひとつにまとめました。\n

\n
\n
\n
    \n
  • \n \n \n \n \n \n \n コアプラットフォーム(年間ライセンス)\n \n \n \n ¥48,000\n \n
  • \n
  • \n \n \n \n \n \n \n プレミアムテンプレート集 120点\n \n \n \n ¥24,000\n \n
  • \n
  • \n \n \n \n \n \n \n 1対1オンボーディング(90分)\n \n \n \n ¥30,000\n \n
  • \n
  • \n \n \n \n \n \n \n 限定コミュニティへの永久アクセス\n \n \n \n ¥18,000\n \n
  • \n
  • \n \n \n \n \n \n \n 四半期ごとの戦略レビュー\n \n \n \n ¥36,000\n \n
  • \n
\n
\n \n 総額\n \n \n ¥156,000\n \n
\n
\n

\n あなたの価格\n

\n

\n ¥49,800\n

\n

\n ¥106,200 お得(68% OFF)\n

\n
\n
\n
\n \n

\n 30日間の返金保証付き・いつでもキャンセル可\n

\n
\n
\n \n" }, { "id": "playful-emoji-feature-grid", @@ -1310,7 +1310,7 @@ ], "standalone": true, "source": "import { Wand2, Palette, Rocket, Users, Shield, Sparkles } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ポップ機能グリッド\",\n category: \"プレイフル\",\n description: \"カラフルなバブルにlucideアイコンを入れた機能グリッド。\",\n align: \"full\",\n isNew: true,\n tags: [\"playful\", \"rounded\", \"features\"],\n};\n\nconst features = [\n { icon: Wand2, color: \"#ff8fab\", title: \"かんたん作成\", titleEn: \"Easy to build\", desc: \"ドラッグするだけ。コード知識はいりません。\", descEn: \"Just drag and drop. No code required.\" },\n { icon: Palette, color: \"#4cc9f0\", title: \"カラフルテーマ\", titleEn: \"Colorful themes\", desc: \"気分でガラッと着せ替えできます。\", descEn: \"Restyle the whole thing on a whim.\" },\n { icon: Rocket, color: \"#06d6a0\", title: \"サクッと公開\", titleEn: \"Ship in a snap\", desc: \"ワンクリックで世界に届けよう。\", descEn: \"Go live with a single click.\" },\n { icon: Users, color: \"#ffd166\", title: \"みんなで編集\", titleEn: \"Edit together\", desc: \"チームでリアルタイムにワイワイ。\", descEn: \"Real-time fun with your whole team.\" },\n { icon: Shield, color: \"#b388ff\", title: \"安心セキュリティ\", titleEn: \"Safe & secure\", desc: \"大事なデータをしっかり守ります。\", descEn: \"Your important data stays protected.\" },\n { icon: Sparkles, color: \"#ff8fab\", title: \"AIのおてつだい\", titleEn: \"AI helper\", desc: \"アイデア出しもおまかせください。\", descEn: \"Stuck for ideas? Leave it to us.\" },\n];\n\nexport default function EmojiFeatureGrid() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n \n {en ? \"What you can do\" : \"できること\"}\n \n

\n {en ? \"Everything fun, all in one\" : \"わくわくが、ぜんぶ揃う\"}\n

\n
\n\n
\n {features.map((f) => {\n const Icon = f.icon;\n return (\n \n \n \n
\n

{en ? f.titleEn : f.title}

\n

{en ? f.descEn : f.desc}

\n
\n );\n })}\n
\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n \n できること\n \n

\n わくわくが、ぜんぶ揃う\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n かんたん作成\n

\n

\n ドラッグするだけ。コード知識はいりません。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n カラフルテーマ\n

\n

\n 気分でガラッと着せ替えできます。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n サクッと公開\n

\n

\n ワンクリックで世界に届けよう。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n みんなで編集\n

\n

\n チームでリアルタイムにワイワイ。\n

\n
\n
\n
\n \n \n \n \n
\n

\n 安心セキュリティ\n

\n

\n 大事なデータをしっかり守ります。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n AIのおてつだい\n

\n

\n アイデア出しもおまかせください。\n

\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n できること\n \n

\n わくわくが、ぜんぶ揃う\n

\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n かんたん作成\n

\n

\n ドラッグするだけ。コード知識はいりません。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n カラフルテーマ\n

\n

\n 気分でガラッと着せ替えできます。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n サクッと公開\n

\n

\n ワンクリックで世界に届けよう。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n みんなで編集\n

\n

\n チームでリアルタイムにワイワイ。\n

\n
\n
\n
\n \n \n \n \n
\n

\n 安心セキュリティ\n

\n

\n 大事なデータをしっかり守ります。\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n AIのおてつだい\n

\n

\n アイデア出しもおまかせください。\n

\n
\n
\n
\n \n" }, { "id": "playful-mascot-card", @@ -1324,7 +1324,7 @@ ], "standalone": true, "source": "import { Sparkles } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"マスコットカード\",\n category: \"プレイフル\",\n description: \"SVGで描いたかわいいマスコット入りカード。\",\n align: \"center\",\n isNew: true,\n tags: [\"playful\", \"rounded\", \"mascot\"],\n};\n\nfunction Mascot() {\n return (\n \n {/* body */}\n \n \n {/* ears */}\n \n \n \n \n {/* cheeks */}\n \n \n {/* eyes */}\n \n \n \n \n {/* smile */}\n \n {/* sparkle */}\n \n \n );\n}\n\nexport default function MascotCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n \n
\n
\n
\n

{en ? \"Hi, I'm Piyo!\" : \"こんにちは、ぴよすけ!\"}

\n

\n {en\n ? \"Your trusty sidekick for getting things done. Give me a shout anytime you're stuck.\"\n : \"あなたの作業をサポートする相棒です。困ったらいつでも声をかけてね。\"}\n

\n \n {en ? \"Say hi\" : \"なかよくなる\"}\n \n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n

\n こんにちは、ぴよすけ!\n

\n

\n あなたの作業をサポートする相棒です。困ったらいつでも声をかけてね。\n

\n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n

\n こんにちは、ぴよすけ!\n

\n

\n あなたの作業をサポートする相棒です。困ったらいつでも声をかけてね。\n

\n \n
\n
\n \n" }, { "id": "playful-playful-button-set", @@ -1338,7 +1338,7 @@ ], "standalone": true, "source": "import { Heart, Plus, Check, ArrowRight, Star } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ポップ・ボタン集\",\n category: \"プレイフル\",\n description: \"丸くて弾むボタンとチップのセット。\",\n align: \"center\",\n isNew: true,\n tags: [\"playful\", \"rounded\", \"button\"],\n};\n\nfunction BounceButton({\n children,\n color,\n shadow,\n}: {\n children: React.ReactNode;\n color: string;\n shadow: string;\n}) {\n return (\n \n {children}\n \n );\n}\n\nconst chips = [\n { t: \"デザイン\", en: \"Design\", c: \"#ff8fab\" },\n { t: \"開発\", en: \"Dev\", c: \"#4cc9f0\" },\n { t: \"マーケ\", en: \"Marketing\", c: \"#06d6a0\" },\n { t: \"リサーチ\", en: \"Research\", c: \"#ffd166\" },\n { t: \"AI\", en: \"AI\", c: \"#b388ff\" },\n];\n\nexport default function PlayfulButtonSet() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n

\n {en ? \"Buttons\" : \"ボタン\"}\n

\n
\n \n {en ? \"Like\" : \"いいね\"}\n \n \n {en ? \"Continue\" : \"つづける\"} \n \n \n {en ? \"Done\" : \"完了\"}\n \n
\n\n
\n \n \n {en ? \"Featured\" : \"おすすめ\"}\n \n
\n\n

\n {en ? \"Chips\" : \"チップ\"}\n

\n
\n {chips.map((chip, i) => (\n \n {en ? chip.en : chip.t}\n \n ))}\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n

\n ボタン\n

\n
\n \n \n \n
\n
\n \n \n
\n

\n チップ\n

\n
\n \n デザイン\n \n \n 開発\n \n \n マーケ\n \n \n リサーチ\n \n \n AI\n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n

\n ボタン\n

\n
\n \n \n \n
\n
\n \n \n
\n

\n チップ\n

\n
\n \n デザイン\n \n \n 開発\n \n \n マーケ\n \n \n リサーチ\n \n \n AI\n \n
\n
\n \n" }, { "id": "playful-playful-cta", @@ -1352,7 +1352,7 @@ ], "standalone": true, "source": "import { ArrowRight } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ポップCTAバンド\",\n category: \"プレイフル\",\n description: \"紙吹雪SVG付きの陽気なCTAバンド。\",\n align: \"full\",\n isNew: true,\n tags: [\"playful\", \"rounded\", \"cta\"],\n};\n\nconst confetti = [\n { x: 8, y: 20, r: 8, c: \"#ffd166\", rot: 20 },\n { x: 20, y: 70, r: 6, c: \"#06d6a0\", rot: -15 },\n { x: 34, y: 30, r: 5, c: \"#fff\", rot: 40 },\n { x: 50, y: 80, r: 7, c: \"#b388ff\", rot: 0 },\n { x: 66, y: 25, r: 6, c: \"#fff\", rot: -30 },\n { x: 80, y: 65, r: 8, c: \"#ffd166\", rot: 15 },\n { x: 92, y: 35, r: 5, c: \"#06d6a0\", rot: -45 },\n { x: 14, y: 50, r: 5, c: \"#fff\", rot: 30 },\n { x: 74, y: 18, r: 6, c: \"#b388ff\", rot: -20 },\n];\n\nexport default function PlayfulCTA() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n \n {/* confetti */}\n \n {confetti.map((c, i) => (\n \n ))}\n \n\n
\n

\n {en ? \"Let's get started!\" : \"さあ、はじめよう!\"}\n

\n

\n {en\n ? \"No credit card needed. Your studio is up and running in 30 seconds.\"\n : \"クレジットカードは不要。30秒であなたのスタジオが立ち上がります。\"}\n

\n
\n \n \n
\n
\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n さあ、はじめよう!\n

\n

\n クレジットカードは不要。30秒であなたのスタジオが立ち上がります。\n

\n
\n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n さあ、はじめよう!\n

\n

\n クレジットカードは不要。30秒であなたのスタジオが立ち上がります。\n

\n
\n \n \n
\n
\n
\n \n" }, { "id": "retro-arcade-card", @@ -1367,7 +1367,7 @@ ], "standalone": true, "source": "import { Gamepad2, Star, Trophy } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"アーケード・カード\",\n category: \"レトロ・Y2K\",\n description: \"ゲーム筐体風のスコア表示とネオングローを持つアーケードカード。\",\n align: \"center\",\n isNew: true,\n tags: [\"retro\", \"y2k\", \"neon\", \"arcade\"],\n};\n\nexport default function ArcadeCard() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n {/* scanline overlay */}\n \n\n
\n \n \n Stage 4\n \n
\n {[0, 1, 2].map((i) => (\n \n ))}\n
\n
\n\n \n \n
\n\n \n {en ? \"High Score!\" : \"ハイスコア達成\"}\n \n\n
\n

Score

\n \n 999,950\n

\n
\n\n \n
\n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n Stage 4\n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n ハイスコア達成\n

\n
\n

\n Score\n

\n

\n 999,950\n

\n
\n \n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n Stage 4\n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n ハイスコア達成\n

\n
\n

\n Score\n

\n

\n 999,950\n

\n
\n \n
\n
\n \n" }, { "id": "retro-neon-cta", @@ -1382,7 +1382,7 @@ ], "standalone": true, "source": "import { ArrowRight, Rocket } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ネオンCTA\",\n category: \"レトロ・Y2K\",\n description: \"グリッド背景と発光する縁取りで誘導する、ネオンのCTAバンド。\",\n align: \"full\",\n isNew: true,\n tags: [\"retro\", \"y2k\", \"neon\", \"cta\"],\n};\n\nexport default function NeonCTA() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n {/* grid */}\n \n {/* corner glow */}\n \n\n
\n \n \n launch ready\n \n\n \n {en ? \"Level up your game\" : \"次のレベルへ、いこう\"}\n \n

\n {en\n ? \"Neon-charged components to take your product to the next stage.\"\n : \"ネオンに輝くコンポーネントで、あなたのプロダクトを一段上の体験に。\"}\n

\n\n
\n \n \n
\n
\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n launch ready\n \n

\n 次のレベルへ、いこう\n

\n

\n ネオンに輝くコンポーネントで、あなたのプロダクトを一段上の体験に。\n

\n
\n \n \n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n launch ready\n \n

\n 次のレベルへ、いこう\n

\n

\n ネオンに輝くコンポーネントで、あなたのプロダクトを一段上の体験に。\n

\n
\n \n \n
\n
\n
\n
\n \n" }, { "id": "retro-neon-hero", @@ -1397,7 +1397,7 @@ ], "standalone": true, "source": "import { ArrowRight, Play, Sparkles } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ネオン・ヒーロー\",\n category: \"レトロ・Y2K\",\n description:\n \"夕焼けパースペクティブグリッドと発光する見出しを組み合わせたシンセウェイヴ・ヒーロー。\",\n align: \"full\",\n isNew: true,\n tags: [\"retro\", \"y2k\", \"neon\", \"synthwave\"],\n};\n\nexport default function NeonHero() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n {/* sun */}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {[200, 224, 252, 284].map((y) => (\n \n ))}\n \n \n \n\n {/* glow halo */}\n \n\n {/* perspective grid floor */}\n \n \n \n\n
\n \n \n insert coin\n \n\n \n {en ? (\n <>\n The future\n
\n glows in neon\n \n ) : (\n <>\n 未来は、\n
\n ネオンに輝く\n \n )}\n \n\n

\n {en\n ? \"An '80s dream burned into pixels — a new digital experience where every edge lights up.\"\n : \"80年代の夢をピクセルに焼き付けた、新しいデジタル体験。境界線は、すべて発光する。\"}\n

\n\n
\n \n \n
\n
\n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n insert coin\n \n

\n 未来は、\n
\n ネオンに輝く\n

\n

\n 80年代の夢をピクセルに焼き付けた、新しいデジタル体験。境界線は、すべて発光する。\n

\n
\n \n \n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n insert coin\n \n

\n 未来は、\n
\n ネオンに輝く\n

\n

\n 80年代の夢をピクセルに焼き付けた、新しいデジタル体験。境界線は、すべて発光する。\n

\n
\n \n \n
\n
\n
\n \n" }, { "id": "retro-neon-stats", @@ -1412,7 +1412,7 @@ ], "standalone": true, "source": "import { Cpu, Users, Zap, Trophy } from \"lucide-react\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"ネオン・スタッツ\",\n category: \"レトロ・Y2K\",\n description: \"発光する数値とアイコンを並べた、レトロフューチャーな実績スタッツ。\",\n align: \"full\",\n isNew: true,\n tags: [\"retro\", \"y2k\", \"neon\", \"stats\"],\n};\n\nconst stats = [\n { icon: Users, value: \"120K+\", label: \"プレイヤー\", labelEn: \"Players\", color: \"#ff2e97\" },\n { icon: Zap, value: \"9.9M\", label: \"総スコア\", labelEn: \"Total score\", color: \"#05d9e8\" },\n { icon: Trophy, value: \"4,200\", label: \"達成バッジ\", labelEn: \"Badges earned\", color: \"#bef264\" },\n { icon: Cpu, value: \"99.9%\", label: \"稼働率\", labelEn: \"Uptime\", color: \"#c084fc\" },\n];\n\nexport default function NeonStats() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n \n
\n {stats.map(({ icon: Icon, value, label, labelEn, color }) => (\n \n \n \n
\n \n {value}\n

\n

\n {en ? labelEn : label}\n

\n \n ))}\n \n
\n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n 120K+\n

\n

\n プレイヤー\n

\n
\n
\n
\n \n \n \n \n
\n

\n 9.9M\n

\n

\n 総スコア\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 4,200\n

\n

\n 達成バッジ\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 99.9%\n

\n

\n 稼働率\n

\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n

\n 120K+\n

\n

\n プレイヤー\n

\n
\n
\n
\n \n \n \n \n
\n

\n 9.9M\n

\n

\n 総スコア\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 4,200\n

\n

\n 達成バッジ\n

\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n

\n 99.9%\n

\n

\n 稼働率\n

\n
\n
\n
\n \n" }, { "id": "scroll-anim-sticky-stack-cards", @@ -1426,7 +1426,7 @@ ], "standalone": true, "source": "import { cn } from \"@/lib/utils\";\nimport type { DemoMeta } from \"@/registry\";\n\nexport const meta: DemoMeta = {\n name: \"スタッキングカード\",\n category: \"スクロール演出\",\n description: \"スクロールでカードがピン留めされ重なって積み上がる。\",\n align: \"full\",\n isNew: true,\n tags: [\"scroll\", \"animation\", \"sticky\"],\n};\n\nconst CARDS = [\n { t: \"Plan\", d: \"目標とスコープを定義する。\", dEn: \"Define goals and scope.\", c: \"bg-gradient-to-br from-indigo-500 to-blue-600\" },\n { t: \"Design\", d: \"体験とUIを設計する。\", dEn: \"Design the experience and UI.\", c: \"bg-gradient-to-br from-emerald-500 to-teal-600\" },\n { t: \"Build\", d: \"コンポーネントを実装する。\", dEn: \"Implement the components.\", c: \"bg-gradient-to-br from-amber-500 to-orange-600\" },\n { t: \"Ship\", d: \"本番へリリースする。\", dEn: \"Release to production.\", c: \"bg-gradient-to-br from-pink-500 to-rose-600\" },\n];\n\nexport default function StickyStackCards() {\n const en =\n typeof document !== \"undefined\" &&\n document.documentElement.lang === \"en\";\n return (\n
\n
\n
\n {en ? \"↓ Scroll to stack\" : \"↓ スクロールで重なる\"}\n
\n
\n {CARDS.map((card, i) => (\n \n \n \n 0{i + 1}\n \n

{card.t}

\n

{en ? card.dEn : card.d}

\n
\n
\n ))}\n
\n \n \n );\n}\n", - "html": "\n\n \n \n \n \n \n \n \n
\n
\n
\n ↓ スクロールで重なる\n
\n
\n
\n
\n \n 01\n \n

\n Plan\n

\n

\n 目標とスコープを定義する。\n

\n
\n
\n
\n
\n \n 02\n \n

\n Design\n

\n

\n 体験とUIを設計する。\n

\n
\n
\n
\n
\n \n 03\n \n

\n Build\n

\n

\n コンポーネントを実装する。\n

\n
\n
\n
\n
\n \n 04\n \n

\n Ship\n

\n

\n 本番へリリースする。\n

\n
\n
\n
\n
\n
\n \n" + "html": "\n\n \n \n \n \n \n \n \n \n \n \n
\n
\n
\n ↓ スクロールで重なる\n
\n
\n
\n
\n \n 01\n \n

\n Plan\n

\n

\n 目標とスコープを定義する。\n

\n
\n
\n
\n
\n \n 02\n \n

\n Design\n

\n

\n 体験とUIを設計する。\n

\n
\n
\n
\n
\n \n 03\n \n

\n Build\n

\n

\n コンポーネントを実装する。\n

\n
\n
\n
\n
\n \n 04\n \n

\n Ship\n

\n

\n 本番へリリースする。\n

\n
\n
\n
\n
\n
\n \n" } ] } diff --git a/package.json b/package.json index 1aa9cec..2887792 100644 --- a/package.json +++ b/package.json @@ -5,21 +5,22 @@ "type": "module", "description": "自分専用の無料コンポーネント・スタジオ(21st.dev の代替)", "scripts": { - "predev": "node scripts/build-manifest.mjs && node scripts/build-free-ids.mjs", + "predev": "node scripts/build-manifest.mjs && node scripts/build-free-ids.mjs && node scripts/build-vanilla-head.mjs", "dev": "vite", - "build": "npm run registry && npm run manifest && npm run freeids && tsc -b && npm run build:client && npm run build:server && npm run prerender", + "build": "npm run registry && npm run manifest && npm run freeids && npm run vanillahead && tsc -b && npm run build:client && npm run build:server && npm run prerender", "build:client": "vite build", "build:server": "vite build --ssr src/entry-server.tsx --outDir dist-ssr --logLevel warn", "prerender": "node scripts/prerender.mjs", "preview": "vite preview", - "typecheck": "node scripts/build-manifest.mjs && node scripts/build-free-ids.mjs && tsc -b --noEmit", + "typecheck": "node scripts/build-manifest.mjs && node scripts/build-free-ids.mjs && node scripts/build-vanilla-head.mjs && tsc -b --noEmit", "registry": "node scripts/build-registry.mjs", "manifest": "node scripts/build-manifest.mjs", "freeids": "node scripts/build-free-ids.mjs", - "test": "node scripts/build-manifest.mjs && node scripts/build-free-ids.mjs && node scripts/check-contrast.mjs --quiet && vitest run", + "vanillahead": "node scripts/build-vanilla-head.mjs", + "test": "node scripts/build-manifest.mjs && node scripts/build-free-ids.mjs && node scripts/build-vanilla-head.mjs && node scripts/check-contrast.mjs --quiet && vitest run", "check:contrast": "node scripts/check-contrast.mjs", "check:overflow": "node scripts/build-manifest.mjs && node scripts/check-overflow.mjs", - "html": "node scripts/build-manifest.mjs && node scripts/build-static-html.mjs", + "html": "node scripts/build-manifest.mjs && node scripts/build-vanilla-head.mjs && node scripts/build-static-html.mjs", "mcp:bundle": "node scripts/build-mcp-bundle.mjs" }, "dependencies": { diff --git a/scripts/build-release.mjs b/scripts/build-release.mjs index 30b3736..7d63726 100644 --- a/scripts/build-release.mjs +++ b/scripts/build-release.mjs @@ -38,7 +38,6 @@ import { } from "node:fs"; import { join, resolve, dirname, relative } from "node:path"; import { execFileSync } from "node:child_process"; -import { pathToFileURL } from "node:url"; const ROOT = resolve(import.meta.dirname, ".."); const arg = (name, fallback) => { @@ -70,90 +69,31 @@ if (missing.length > 0) { } // ────────────────────────────────────────────────────────────────────────── -// 静的HTML への Tailwind 設定の注入 +// デザイントークンが HTML に入っているかの確認 // -// 生成された静的HTML は cdn.tailwindcss.com を1行読むだけになっているが、 // bg-card / text-muted-foreground のような**このプロジェクト固有のトークン**は -// 素の Tailwind には存在しない。実測したところ 880 件中 430 件がこれらを使って -// おり、そのままではその分の色が一切当たらない(bg-card → transparent)。 +// 素の Tailwind には存在しない(880 件中 430 件が使っている)。そのため +// cdn.tailwindcss.com を1行読むだけの HTML では色が一切当たらない。 // -// そこで配布時に、tailwind.config.js の extend と src/index.css の :root/.dark を -// HTML の に埋め込む。Play CDN が公式に用意している設定方法で、 -// 「Tailwind を1行読むだけ」という売り文句を保ったまま色が正しく出る。 +// この注入は書き出しの**源流**(src/lib/vanilla.ts の wrapDocument。設定は +// scripts/build-vanilla-head.mjs が生成する src/lib/vanillaHead.generated.ts)で +// 行うので、ここで配布時に足すことはしない。同じ内容を2箇所に持つと、 +// 片方だけ直したときに配布物とスタジオの書き出しが食い違うため。 // -// 値は tailwind.config.js と src/index.css から毎回読み直すので、 -// トークンを直したら再生成するだけで配布物にも反映される。 +// 代わりに「入っているか」だけを検める。入っていなければ材料が古い。 // ────────────────────────────────────────────────────────────────────────── -/** CSS から `selector { ... }` の中身を括弧の対応を数えて取り出す */ -function extractCssBlock(css, selector) { - const at = css.indexOf(selector); - if (at === -1) return null; - const open = css.indexOf("{", at); - if (open === -1) return null; - let depth = 0; - for (let i = open; i < css.length; i++) { - if (css[i] === "{") depth++; - else if (css[i] === "}") { - depth--; - if (depth === 0) return css.slice(open + 1, i); - } +/** トークン設定が入っていない HTML を洗い出し、あれば止める */ +function assertTokensInjected(label, htmls, how) { + const broken = htmls.filter(([, html]) => html && !html.includes("tailwind.config")); + if (broken.length > 0) { + console.error( + `${label}: デザイントークンの設定が入っていない HTML が ${broken.length} 件あります。\n` + + ` 例: ${broken.slice(0, 3).map(([name]) => name).join(", ")}\n` + + ` bg-card / text-muted-foreground などの色が出ない状態です。${how} を実行して作り直してください。` + ); + process.exit(1); } - return null; -} - -/** CSS コメントを落として1行1宣言に整える */ -function tidyDeclarations(body, indent) { - return body - .replace(/\/\*[\s\S]*?\*\//g, "") - .split(";") - .map((s) => s.trim()) - .filter(Boolean) - .map((s) => `${indent}${s};`) - .join("\n"); -} - -async function buildHeadInjection() { - const cfg = (await import(pathToFileURL(resolve(ROOT, "tailwind.config.js")).href)) - .default; - const css = readFileSync(resolve(ROOT, "src/index.css"), "utf-8"); - const root = extractCssBlock(css, ":root"); - const dark = extractCssBlock(css, ".dark"); - if (!root || !dark) { - throw new Error("src/index.css から :root / .dark を取り出せませんでした"); - } - const config = JSON.stringify( - { darkMode: cfg.darkMode, theme: { extend: cfg.theme.extend } }, - null, - 2 - ) - .split("\n") - .map((l, i) => (i === 0 ? l : ` ${l}`)) - .join("\n"); - - return ` - - -`; -} - -/** の末尾に設定を差し込む。既に入っていれば何もしない。 */ -function injectHead(html, injection) { - if (html.includes("tailwind.config")) return html; - return html.replace(" ", `${injection} `); } // ────────────────────────────────────────────────────────────────────────── @@ -210,8 +150,6 @@ console.log(`${NAME} を組み立てます…\n`); rmSync(STAGE, { recursive: true, force: true }); mkdirSync(STAGE, { recursive: true }); -const injection = await buildHeadInjection(); - // --- README / LICENSE --- copyOne("docs/dist-README.md", join(STAGE, "README.md")); copyOne("LICENSE", join(STAGE, "LICENSE")); @@ -248,19 +186,22 @@ const demoCount = readdirSync(join(STAGE, "components/demos"), { recursive: true .filter((f) => String(f).endsWith(".tsx")) .length; -// --- html/ : 880件 + index.json(Tailwind 設定を注入したもの) --- +// --- html/ : 880件 + index.json --- const htmlSrc = resolve(ROOT, "public/html"); const htmlDst = join(STAGE, "html"); mkdirSync(htmlDst, { recursive: true }); -let htmlCount = 0; +const htmlFiles = readdirSync(htmlSrc).filter((f) => f.endsWith(".html")); +assertTokensInjected( + "html/", + htmlFiles.map((f) => [f, readFileSync(join(htmlSrc, f), "utf-8")]), + "npm run html" +); for (const f of readdirSync(htmlSrc)) { - if (f.endsWith(".html")) { - writeFileSync(join(htmlDst, f), injectHead(readFileSync(join(htmlSrc, f), "utf-8"), injection)); - htmlCount++; - } else if (f === "index.json") { + if (f.endsWith(".html") || f === "index.json") { copyFileSync(join(htmlSrc, f), join(htmlDst, f)); } } +const htmlCount = htmlFiles.length; copied += htmlCount + 1; // --- registry/ : shadcn 互換 JSON --- @@ -284,10 +225,12 @@ try { if (fullBundle.edition !== "full") { throw new Error(`MCP バンドルが full になっていません: ${fullBundle.edition}`); } -// MCP が返す HTML も配布物と同じものにする -for (const item of fullBundle.items) { - if (item.html) item.html = injectHead(item.html, injection); -} +// MCP が返す HTML も public/html/ から来るので、同じ検査を通す +assertTokensInjected( + "mcp/data/components.json", + fullBundle.items.map((i) => [i.id, i.html]), + "npm run html" +); copyTree(resolve(ROOT, "mcp/src"), join(STAGE, "mcp/src")); copyOne("mcp/package.json", join(STAGE, "mcp/package.json")); copyOne("mcp/README.md", join(STAGE, "mcp/README.md")); @@ -301,8 +244,7 @@ copied++; // --- studio/ : ビルド済み --- // Vite は public/ をそのまま dist/ にコピーするので、dist/r と dist/html は -// registry/ と html/ の丸ごと重複になる(合わせて 12MB)。しかも dist/html は -// Tailwind 設定を注入する前の版なので、残すと配布物の中で食い違う。 +// registry/ と html/ の丸ごと重複になる(合わせて 12MB)。 // スタジオ本体はどちらも実行時に参照していないので外す。 const DIST = resolve(ROOT, "dist"); const DIST_DUPES = new Set(["r", "html"]); diff --git a/scripts/build-vanilla-head.mjs b/scripts/build-vanilla-head.mjs new file mode 100644 index 0000000..3d9b403 --- /dev/null +++ b/scripts/build-vanilla-head.mjs @@ -0,0 +1,129 @@ +/** + * build-vanilla-head.mjs + * + * バニラHTML書き出し(src/lib/vanilla.ts)が に差し込む + * **デザイントークン定義**を `src/lib/vanillaHead.generated.ts` に生成する。 + * + * なぜ生成物にするのか: + * bg-card / text-muted-foreground / bg-background などはこのプロジェクト固有の + * トークンで、素の Tailwind には存在しない(tailwind.config.js の theme.extend と + * src/index.css の :root / .dark が定義の出所)。書き出したHTMLが + * cdn.tailwindcss.com を1行読むだけだと、これらのクラスは一切当たらず + * bg-card → transparent、text-muted-foreground → 真っ黒になる(880件中430件が該当)。 + * Play CDN 公式の方法どおり `tailwind.config = {...}` と CSS 変数を に + * 埋めれば直るが、`wrapDocument` はスタジオの書き出し機能としてブラウザ上でも + * 走るため、実行時にファイルを読むことができない。よってビルド前に定数化する。 + * + * (src/index.css を `?raw` で直接 import する手もあるが、vitest は既定で + * CSS の import を空文字に差し替えるためテスト環境で成立しない。 + * manifest.ts / stats.generated.ts / freeIds.ts と同じ生成物方式に揃える。) + * + * 使い方: node scripts/build-vanilla-head.mjs + * 元データ: tailwind.config.js(darkMode / theme.extend), src/index.css(:root / .dark) + * ドリフト検査: src/lib/vanillaHead.test.ts が元データから組み直して突き合わせる。 + * + * 注: 他の scripts と違い shebang を付けていない。この検査から import するが、 + * Vite の SSR 変換は `#!` を解析できず "Expected ident" で落ちるため。 + */ +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); + +/** CSS から `selector { ... }` の中身を、括弧の対応を数えて取り出す */ +function extractCssBlock(css, selector) { + const at = css.indexOf(selector); + if (at === -1) return null; + const open = css.indexOf("{", at); + if (open === -1) return null; + let depth = 0; + for (let i = open; i < css.length; i++) { + if (css[i] === "{") depth++; + else if (css[i] === "}") { + depth--; + if (depth === 0) return css.slice(open + 1, i); + } + } + return null; +} + +/** CSS コメントを落として1行1宣言に整える */ +function tidyDeclarations(body, indent) { + return body + .replace(/\/\*[\s\S]*?\*\//g, "") + .split(";") + .map((s) => s.trim()) + .filter(Boolean) + .map((s) => `${indent}${s};`) + .join("\n"); +} + +/** + * に差し込む文字列を組み立てる。 + * scripts と src/lib/vanillaHead.test.ts の両方から使えるよう export している。 + */ +export async function buildHeadInjection(rootDir = root) { + const cfg = (await import(pathToFileURL(join(rootDir, "tailwind.config.js")).href)) + .default; + const css = readFileSync(join(rootDir, "src", "index.css"), "utf-8"); + const cssRoot = extractCssBlock(css, ":root"); + const dark = extractCssBlock(css, ".dark"); + if (!cssRoot || !dark) { + throw new Error("src/index.css から :root / .dark を取り出せませんでした"); + } + const config = JSON.stringify( + { darkMode: cfg.darkMode, theme: { extend: cfg.theme.extend } }, + null, + 2 + ) + .split("\n") + .map((l, i) => (i === 0 ? l : ` ${l}`)) + .join("\n"); + + return ` + + +`; +} + +/** テンプレートリテラルに埋め込める形へ逃がす */ +function escapeTemplate(s) { + return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${"); +} + +// このモジュールを直接実行したときだけ生成する(test からは import して使う) +if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) { + const injection = await buildHeadInjection(); + const out = join(root, "src", "lib", "vanillaHead.generated.ts"); + writeFileSync( + out, + `// このファイルは scripts/build-vanilla-head.mjs により自動生成されます。直接編集しないでください。 +// 元データ: tailwind.config.js(darkMode / theme.extend)と src/index.css(:root / .dark) + +/** + * バニラHTML書き出しの に差し込むデザイントークン定義。 + * 素の Tailwind に無い bg-card / text-muted-foreground などを CDN 版でも + * 解決させるための設定(Play CDN 公式の方法)。 + * Tailwind CDN の - +${HEAD_TOKEN_INJECTION} ${indented} @@ -277,7 +287,7 @@ export function generateDynamicVanillaHtml(demoSource: string): string { - +${HEAD_TOKEN_INJECTION} + +`; diff --git a/src/lib/vanillaHead.test.ts b/src/lib/vanillaHead.test.ts new file mode 100644 index 0000000..f737474 --- /dev/null +++ b/src/lib/vanillaHead.test.ts @@ -0,0 +1,156 @@ +import { existsSync, readFileSync, readdirSync } from "node:fs"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; +import { renderToStaticMarkup } from "react-dom/server"; +import { createElement } from "react"; +import { buildHeadInjection } from "../../scripts/build-vanilla-head.mjs"; +import { HEAD_TOKEN_INJECTION } from "./vanillaHead.generated"; +import { generateVanillaHtml, generateDynamicVanillaHtml } from "./vanilla"; + +/** + * 書き出しHTMLのデザイントークン注入を固定する検査。 + * + * bg-card / text-muted-foreground などはこのプロジェクト固有のトークンで、 + * 素の Tailwind には無い。CDN を1行読むだけの HTML では bg-card が transparent、 + * text-muted-foreground が真っ黒になるため、書き出しの源流で必ず注入する。 + * ここが崩れると「コピペしてそのまま使える」が成立しない。 + * + * `vanillaHead.generated.ts` は tailwind.config.js / src/index.css からの派生物なので、 + * freeIds.ts と同様に元データから組み直して突き合わせ、黙って腐らないようにする。 + */ + +const ROOT = resolve(process.cwd()); + +describe("vanillaHead.generated.ts が元データとずれていない", () => { + it("tailwind.config.js と src/index.css から組み直した内容と一致する", async () => { + expect(HEAD_TOKEN_INJECTION).toBe(await buildHeadInjection(ROOT)); + }); + + it("src/index.css の CSS 変数を全て持っている", () => { + const css = readFileSync(resolve(ROOT, "src/index.css"), "utf-8"); + const vars = [...css.matchAll(/^\s*(--[\w-]+):/gm)].map((m) => m[1]); + expect(vars.length).toBeGreaterThan(15); + for (const v of new Set(vars)) { + expect(HEAD_TOKEN_INJECTION, `${v} が注入に含まれていない`).toContain(`${v}:`); + } + }); + + it("素の Tailwind に無い色トークンを tailwind.config で定義している", () => { + for (const token of ["card", "muted-foreground", "background", "foreground", "border"]) { + expect(HEAD_TOKEN_INJECTION).toContain(token); + } + expect(HEAD_TOKEN_INJECTION).toContain("tailwind.config = {"); + expect(HEAD_TOKEN_INJECTION).toContain('"darkMode": "class"'); + }); +}); + +/** 固有トークンだけで組んだ、書き出し対象のダミー部品 */ +function TokenCard() { + return createElement( + "div", + { className: "bg-card text-card-foreground border-border rounded-lg border p-6" }, + createElement("p", { className: "text-muted-foreground" }, "説明文") + ); +} + +describe("バニラHTML書き出しにトークン定義が入る", () => { + it("generateVanillaHtml の出力に tailwind.config と :root の CSS 変数が含まれる", async () => { + const html = await generateVanillaHtml(TokenCard); + expect(html).toContain("https://cdn.tailwindcss.com"); + expect(html).toContain("tailwind.config = {"); + expect(html).toContain(":root {"); + expect(html).toContain("--card:"); + expect(html).toContain("--muted-foreground:"); + expect(html).toContain(".dark {"); + }); + + it("固有トークンを使う部品を書き出しても、そのトークンが定義済みになっている", async () => { + const html = await generateVanillaHtml(TokenCard); + const body = html.slice(html.indexOf(" 側に揃っている + const head = html.slice(0, html.indexOf("")); + expect(head).toContain('"card"'); + expect(head).toContain('"muted"'); + expect(head).toContain("--card-foreground:"); + }); + + it("tailwind.config への代入は CDN の