SSOT Template Markup Language — フレームワーク非依存のUI宣言。

フロントエンドコードは2つのものを混ぜている:あなたの決定フレームワーク配線。どのAPIを呼ぶか、どのフィールドを見せるか、どの順序で、どの条件で——これらの決定がReact hooksやVue composablesの中に埋もれる。フレームワークを変えたりリファクタリングすると、決定が再解釈されたり失われたりする。

STMLはこれを分離する。決定はdata-*属性付きの標準HTMLに残り、フレームワーク配線は生成または委譲される。

GitHubリポジトリ

構造

┌─────────────────────────────────┐
│  STML (specs/)                  │  あなたの決定。フレームワーク非依存。
│  何を取得し、表示し、送信するか  │  どんなリライトも生き残る。
├─────────────────────────────────┤
│  コード生成 / LLM               │  フレームワーク配線を生成。
│  React, Vue, Svelte, 何でも     │  交換可能。
├─────────────────────────────────┤
│  ランタイム (artifacts/)         │  React TSX, Vue SFC など。
│  フック、状態、レンダリング      │  生成物。編集しない。
└─────────────────────────────────┘

保存されるもの

STML HTMLのすべてがユーザーの決定である:

  • どのAPIエンドポイントを呼ぶか (data-fetch, data-action)
  • どのフィールドを表示・収集するか (data-bind, data-field)
  • どの順序で要素が現れるか (DOM構造)
  • どう見えるか (Tailwindクラス、HTMLタグ)
  • どのテキストをユーザーが見るか (見出し、プレースホルダー、ボタンラベル)
  • どの条件が可視性を制御するか (data-state)
  • どのコンポーネントが特殊UXを処理するか (data-component)
  • リストの動作 — ページネーション、ソート、フィルタリング

Reactでもない。Vueでもない。ページが何をするか、標準HTML5で宣言したものだ。

<main class="max-w-4xl mx-auto p-6">
  <h1 class="text-2xl font-bold mb-6">予約一覧</h1>

  <section data-fetch="ListMyReservations"
           data-paginate data-sort="StartAt:desc" data-filter="Status">
    <ul data-each="reservations" class="space-y-3">
      <li class="flex justify-between p-4 border rounded">
        <span data-bind="RoomID" class="font-semibold"></span>
        <span data-bind="Status" class="text-sm text-gray-500"></span>
      </li>
    </ul>
    <p data-state="reservations.empty" class="text-gray-400">予約はありません</p>
  </section>

  <div data-action="CreateReservation">
    <input data-field="RoomID" type="number" placeholder="部屋番号" />
    <button type="submit">予約</button>
  </div>
</main>

同じファイルから異なるターゲットを生成できる:

ターゲット方法
React TSXstml gen(内蔵の決定的コード生成)
Vue SFCSTML + OpenAPIをLLMに:「Vueで実装して」
SvelteSTML + OpenAPIをLLMに:「Svelteで実装して」
FlutterSTML + OpenAPIをLLMに:「Flutterで実装して」

決定は保存される。フレームワーク配線だけが変わる。

検証

内蔵バリデーターがコード生成前にSTMLをOpenAPI対比で交差検証する:

  • operationIdが存在するか?HTTPメソッドは正しいか?
  • リクエストフィールド、レスポンスフィールド、パラメーターがスキーマと一致するか?
  • ソート/フィルター/インクルードカラムが許可リスト内にあるか?
  • 参照されたコンポーネントが存在するか?

フロントエンド-APIの不一致をランタイムではなくCI時に捕捉する。

stml validate specs/my-project    # OpenAPI対比12のシンボリック検査

data-* 属性

属性宣言内容
data-fetchGETエンドポイントからデータをロード
data-actionPOST/PUT/DELETEエンドポイントにデータを送信
data-fieldリクエストボディフィールドを収集
data-bindレスポンスフィールドを表示
data-param-*パス/クエリパラメーターが必要な操作
data-each配列フィールドを反復
data-state条件付き表示
data-componentカスタムコンポーネントに委譲
data-paginateページネーション付きリスト
data-sortソート可能リスト(デフォルトカラムと方向)
data-filterフィルター可能リスト(どのカラム)
data-include関連リソースを含むクエリ

ライセンス

MIT — GitHub