インストール

アプリに Stimeo UI を追加

Stimeo UI は Rails 向けに 挙動だけ を提供する Stimulus コントローラ群です。 ARIA 状態、キーボード操作、フォーカス管理を data-* 属性で駆動し、CSS は一切同梱しません。見た目は完全にあなたのものです。

ステータス: alpha(0.x)です。 stimeo--* の属性 API は 1.0 までに変わる可能性があります。

Rails + importmap(推奨)

bundle add stimeo-ui
bin/rails generate stimeo:install

このコマンド(stimeo:install ジェネレータ)は、次のセットアップを自動で行います:

  • ビルド済みのライブラリ本体を vendor/javascript/stimeo/ に配置(同梱)し、
  • config/importmap.rbstimeo-ui の読み込み設定(importmap のエントリ)を追加し、
  • すべての stimeo--* コントローラを Stimulus に登録します。

何度実行しても安全です。ライブラリ本体は最新版に置き換わり、importmap の設定や既存の記述はそのまま残ります。

あとは HTML に属性を書くだけでコンポーネントが動きます:

<div data-controller="stimeo--dropdown">
  <button data-stimeo--dropdown-target="trigger"
          data-action="click->stimeo--dropdown#toggle">Menu</button>
  <div data-stimeo--dropdown-target="menu" hidden>…</div>
</div>

npm(jsbundling など任意のバンドラ)

npm install stimeo-ui @hotwired/stimulus
import { Application } from "@hotwired/stimulus";
import { registerStimeo } from "stimeo-ui";

const application = Application.start();
registerStimeo(application); // すべての stimeo--* コントローラを登録

必要なコントローラだけを使いたい場合は、stimeo-ui/controllers/* から個別に import して、好きな識別子で登録することもできます:

import { Application } from "@hotwired/stimulus";
import { DialogController } from "stimeo-ui/controllers/dialog_controller";

const application = Application.start();
application.register("stimeo--dialog", DialogController);

オプトインの positioning(フローティング配置)

popover / tooltip / hover-card のような「何かに沿って浮かぶ UI」では、開いたり閉じたりといった動きはコントローラが担います。一方でどこに表示するか(位置)は、通常はあなたの CSS で決めます。画面の端に来たときに自動で向きを反転させて収める、といった「動的な位置合わせ」が欲しいときだけ、@floating-ui/dom を使う positioning モジュール(stimeo-ui/positioning)を任意で追加します。必要なければ追加は不要で、本体(コアの stimeo-ui)には positioning モジュールも @floating-ui/dom も含まれないため、追加の依存はゼロのままです。使う場合は config/importmap.rb に次の 2 行を足して、サブパスと依存ライブラリも読み込めるようにします:

pin "stimeo-ui/positioning", to: "stimeo/positioning/index.js"
pin "@floating-ui/dom", to: "floating-ui-dom.js"

Lint(リント)

Stimeo UI はヘッドレス(見た目を持たない)なので、role などの WAI-ARIA 属性は あなた自身が書きます。一部のコントローラは、その属性を手がかりに要素を探します(例: data-grid は [role="row"] で行を見つけます)。そのため、あなたのマークアップには <ul role="menu"> / <div role="radio"> / <td role="gridcell"> のような、APG に沿った正しいカスタムウィジェットの ARIA が含まれます。

ところが厳格な静的 a11y リンタ(Biome の recommended プリセット(2.5 以降)や eslint-plugin-jsx-a11y)は、こうした正しい書き方をエラー扱いします。<button> のようなネイティブ要素を前提にしたチェックだからです。そこで Stimeo UI のマークアップを書く場所だけ 該当ルールを緩めます(includes のパスは自分の構成に合わせて調整します)。

Biome なら biome.json に次の overrides を追加します:

{
  "overrides": [
    {
      "includes": ["app/components/**"],
      "linter": {
        "rules": {
          "a11y": {
            "noNoninteractiveElementToInteractiveRole": "off",
            "noRedundantRoles": "off",
            "useSemanticElements": "off",
            "useFocusableInteractive": "off",
            "noNoninteractiveTabindex": "off"
          }
        }
      }
    }
  ]
}

ESLint(eslint-plugin-jsx-a11y)を使う場合も、同じ考え方で、対象のパス(.eslintrc.json など)だけ同等のルールを off にします。

次のステップ