nasucat

Nasu Stack v1.0.0 を公開しました

初心者がWeb開発で困らないようにするためのshadcn 対応 UIコンポーネント集の第一弾をリリース

投稿: 2026年8月23日約 9 分で読めます

Nasu Stack v1.0.0 を公開しました

2026年8月20日に v1.0.0 をリリースしました。

Nasu Stack は「初心者は困らせない、ベテランは縛らない」を目標として、初心者が困りがち・忘れがちなレイアウト(レスポンシブ対応含む)、トンマナの統一、UI設計について最初から実装済みのコンポーネントを提供します。また、アクセシビリティも重視しており、利用者の使いやすさをある程度保証します。

  • リポジトリは こちら
  • コンポーネントカタログ(実物が触れます)は こちら
  • デモサイト(参考実装)は こちら

ライセンスは MIT です。

なぜ作ったか

Web開発に慣れていない人やノーコード開発をしたい人には Wix や Elementor のようなビジュアルビルダーがあります。すぐ形になりますが、Builder.io や Plasmic のような系統も含めて、乗り換えようとするとコンテンツを書き直す羽目になりがちです。Web開発に慣れている人には Astro や Next.js のような素のフレームワークがあります。何でもできますが、余白の値も、ローディング表示も、エラー処理も、二重送信の防止も、全部自分で決めなければいけません。

その間に、コードは触れるけれど全部は組み立てられない層が取り残されています。Nasu Stack が狙っているのはそこです。逆に、コードが読めない人はターゲットにしていません。そこは v0 や Lovable のような自然言語ファーストのツールがあるからです。

もう一つ決めたのは、見た目だけのコンポーネントは作らないということです。shadcn のブロック市場にはすでに2500個以上の「見た目」が並んでいます。そこに一枚足しても意味がありません。Nasu Stack が持っているのは、状態を持つコンポーネントレイアウトコンポーネントの二種類だけです。

何が入っているか

v1.0.0 時点で、npx shadcn add で個別に取れる配布物が40個あります。

レイアウト

PageBlock / ContentBlock / Section / Stack / Inline / Columns / Tiles / Spread / Box / Scrollable / Divider

余白は9段階のスケールに固定されています。neutral テーマだとこうなります。

名前px
none0
2xs4
xs8
sm12
md16
lg24
xl40
2xl64
3xl96

「ここ 14px にすべきか 16px にすべきか」で悩む時間が消えます。テーマを切り替えると、この対応表ごと切り替わります。

状態を持つコンポーネント

  • ActionButton — ローディング・エラー・成功・二重送信防止を全部持っている
  • AsyncForm + Field — フィールド単位のエラー表示付きフォーム
  • DataList / DataTable — スケルトン・空・失敗の表示込み。DataTable はタブレット幅を下回るとカード表示に変わる
  • AsyncSelect — 検索付きドロップダウン。リクエストのキャンセルまでやる
  • FileDrop — ドラッグ&ドロップ。進捗表示のために XHR を使っている
  • Toast / ConfirmDialog — 通知と確認ダイアログ
  • Dialog(モーダル / シート)/ Tabs / Disclosure / Accordion
  • SiteHeader / NavLink / SkipLink
  • HoneypotField — 単純なボット投稿を減らすためのもの

フック・その他

useAction / useResource / useInteractionGuard / useOptimisticList / ActionProvider / ThemeProvider / ThemeSwitcher、それと SEO 用の buildMeta(JSON-LD 込み)・buildSitemapbuildRss

テーマは neutral / warm / editorial / vivid の4つで、色だけでなく角丸・影・タイポグラフィ・余白スケールがまとめて変わります。CSS変数の上書きで自分のテーマも作れます。

設計思想

4層と、たった一つの契約

Theme     : tokens.css / themes.css(色・余白・書体)

Layout    : Stack / Columns / Tiles ...

Component : ActionButton / AsyncForm / DataTable ...

Contract  : Action<TInput, TOutput>

一番下の契約はこれだけです。

type Action<TInput, TOutput> = (
  input: TInput,
  ctx: { signal: AbortSignal }
) => Promise<TOutput>;

fetch でも Supabase でも Server Actions でも、Promise を返す関数にさえなっていれば刺さります。バックエンドに依存しないというのはこの一行の意味です。失敗はすべて ActionError に正規化されるので、エラー処理の書き方が場所ごとにブレません。

コンポーネントは外側の余白を持たない

これは強く決めています。余白はレイアウトコンポーネントだけが持ちます。ボタン自身が margin-bottom を持っていると、それを打ち消すための -mt-2 が生まれ、そこからCSSが壊れ始めます。

制約はデフォルトであって、壁ではない

9段階が補完に出てきますが、space="13px"clamp() も通ります。初期値として正しい選択肢を上に出しているだけで、外に出る道は塞いでいません。

足りなくなったら、乗り換えではなく「降りる」

これがエスケープハッチの形です。

<ActionButton action={...} />          状態管理を書かない
  ↓ 足りない
<Button onClick={...} /> + useAction() 状態だけ借りる
  ↓ 足りない
<Button /> + useInteractionGuard()     二重発火の防止だけ借りる
  ↓ 足りない
useState / useRef                      素のReact

レイアウトも同じで、<Stack space="lg"><Box padding>className と降りられます。「Nasu Stack をやめる」という決断をしなくても、一段ずつ抽象度を下げられるようにしてあります。

壊れても、静かに縮退する

ActionProvider が無ければ通知機能がオフになるだけ。ConfirmProvider が無ければ window.confirm にフォールバックします。全部入れないと動かない、という作りにはしていません。部分導入ができます。

JSを使わずに済むなら使わない

モバイルメニューは useState ではなく <details> で作っています。JSが落ちても開くし、Ctrl+F でのページ内検索も効きます。

値を二重に持たない

テンプレートは原本から生成していて、コミットしていません。registryDependencies も機械的に突き合わせています。同じ値が2箇所にあると、いつか片方だけ腐ります。

責任境界

UIライブラリは「安全そうに見えるもの」を大量に提供してしまいます。ファイルの種類を弾くドロップゾーン、必須チェック付きのフォーム、ボット避けのフィールド。これらはどれもセキュリティ機構ではありません。 勘違いしたまま本番に出ると事故ります。なので、境界を docs/boundaries.ja.md に明文化しました。

大まかにはこう分かれます。

Nasu Stack が持つアプリ・ドメインが持つサーバ・インフラが持つ
配布物の挙動・型・依存関係どれをどう組み合わせるか認証・認可
レスポンシブの初期値画面全体の見出し階層正典としてのバリデーション
部品内部のフォーカス・キーボード・ARIA成功の意味と表示文言CSRF・レート制限・ボット対策
ローディング / 成功 / 失敗の状態ドメインのルールと期待するデータ形ファイル検査(サイズ・種類・マジックバイト)
二重送信の防止・中断シグナルリトライして良い操作かの判断冪等性と単一実行の保証
ブラウザ側の形式チェック製品ごとのアップロード上限ログとシークレット管理

特に誤解されやすいものを個別に書いておきます。

  • FileDropacceptmaxSize はガードではありません。 見ているのは拡張子とブラウザが推測した MIME タイプで、どちらもユーザーが書き換えられます。誤操作を減らすためのフィードバックです。実体の検査はサーバの仕事です。
  • HoneypotField は認証・CSRFトークン・レート制限の代わりになりません。 単純なボットを減らすだけです。
  • EndpointSpec.defaultsrole / tenantId / userId を置いてはいけません。 ここはクライアントが渡す値で、入力パラメータに上書きされます。身元と権限はサーバが決めることです。
  • AbortSignal はロールバックの保証ではありません。 古い画面更新を止めるだけで、サーバ側の処理・送信済みメール・決済は戻りません。中断は「お願い」であって、「何も起きなかった証明」ではありません。
  • jsonRequest が検証しているのは通信の形式であって、ドメインの正しさではありません。 空の 204/205 と正しい JSON は通し、壊れたボディは fail-closed で落とします。中身が信用できるかどうかは、アプリ層で判断してください。
  • リトライは冪等な操作でのみ安全です。 決済・注文作成・メール送信は、サーバ側の Idempotency-Key と組にしないと単一実行を保証できません。

そして、Nasu Stack が提供しないもの

  • 認証・認可の仕組み
  • DB・ORM・トランザクション管理・サーバフレームワーク
  • サーバ側の正典バリデーションとドメインスキーマ
  • CSRF対策・レート制限・ボット対策・アップロード検査
  • 中断時にサーバ側の副作用が巻き戻る保証
  • ソースを書き換えた後もアクセシビリティと安全性が保たれる保証

プリミティブや連携例は置いてありますが、それで本番の責任がライブラリ側に移るわけではありません。

品質チェック

数値をログに出して人間に読ませる方式はやめて、満たせなかったらビルドを落とすようにしています。pnpm verify が29項目、pnpm verify:create が112項目走ります。

  • タップ領域が24px以上あるか
  • ARIA の role が正しいか
  • フォーカスがちゃんと戻るか
  • 画像がレイアウト領域を先に確保しているか(読み込みをブロックして確認)
  • モーダル中にスクロールがロックされるか(プログラム経由ではなく wheel イベントで確認)

検出器が本当に動いているかは、わざと壊して確かめています。NavLink から min-h-11 を外してみたところ、インライン要素の判定に例外が入っていて検出をすり抜けるバグが見つかりました。テストを信じるためには、テストが失敗するところを一度見る必要があります。

モバイル対応チェッカーもあり、横方向のはみ出し・24px未満のタップ領域・16px未満の入力欄(iOSで勝手にズームするやつ)・縮まない要素・長すぎる一行の文字数を検出します。npm run check で CI に組み込めます。

使い方

新規プロジェクト

npx https://github.com/Nasu726/Nasu-Stack/releases/download/v1.0.0/create-nasu-stack-1.0.0.tgz my-site

Astro(React アイランド付きの静的サイト)か Vite(React アプリ)を選べます。ブログテンプレート(ランディング・RSS・サイトマップ込み)も用意しています。

既存プロジェクトに足す

npx [email protected] add Nasu726/Nasu-Stack/action-button

npm パッケージとしてはインストールしません。shadcn 方式で、コードがそのままリポジトリにコピーされます。気に入らなければその場で書き換えてください。

注意:npx create-nasu-stack は実行しないでください

この npm 名は取得していません。npx create-nasu-stack を叩くと、無関係な第三者のコードが実行されます。 上の tarball URL を使ってください。

心配な場合は、チェックサムを検証してから実行できます。

curl -fsSL -O https://github.com/Nasu726/Nasu-Stack/releases/download/v1.0.0/create-nasu-stack-1.0.0.tgz
curl -fsSL -O https://github.com/Nasu726/Nasu-Stack/releases/download/v1.0.0/create-nasu-stack-1.0.0.tgz.sha256
sha256sum -c create-nasu-stack-1.0.0.tgz.sha256
npx ./create-nasu-stack-1.0.0.tgz my-site

チェックサムが一致することは「同一である」ことの確認であって、「作者が信用できる」ことの確認ではありません。それと、検証は実行のにしてください。

配布を npm ではなく GitHub Release にしているのは、これが個人で保守している趣味のプロジェクトだからです。維持するのは最新版だけで、古いバージョンへのバックポートはしません。脆弱性を見つけた場合は、公開 Issue ではなく GitHub Security Advisories から非公開で報告してください。ただし、応答期限は約束できません。即応が必要な用途には向いていないプロジェクトです。

これから

やる予定なのは、レジストリの静的ホスティング、ダッシュボードのテンプレート、フォームのリアルタイムバリデーション、認証・決済のアダプタあたりです。

やらないと決めているのはビジュアル編集機能です。 「コードを触れる人」を対象にすると決めた以上、そこに手を出すと軸がぶれます。

まだ検証できていないこともあります。公開している404のステータスコード周りと、僕以外の環境での実運用のUXです。もし使ってみて壊れたところがあれば、Issue で教えてもらえると助かります。