Webパーツ辞典

インライン編集Inline edit

ふだんは値が文字で並んでいて、鉛筆のボタンを押すとその場所が入力欄と「保存」「キャンセル」に変わる。設定画面やプロフィールの項目を1つずつ直すのに使う。

表示例

表示名
山田 花子

Enter で保存、Esc で取り消し

アイコン: Font Awesome 7 Free(fa-solid / fa-regular)

別名・英語表記

  • その場で編集
  • インプレース編集
  • クリックして編集
  • 編集可能テキスト
  • 鉛筆ボタンで編集
  • Inline edit

実装例

<div class="x-inline-edit">
  <dl class="x-inline-edit__list">
    <div class="x-inline-edit__row">
      <dt class="x-inline-edit__k">表示名</dt>
      <dd class="x-inline-edit__v">
        <span class="x-inline-edit__val">山田 花子</span>
        <!-- 押すと、この dd の中身を下の行と同じ入力欄に差し替え、入力欄へフォーカスを移す -->
        <button class="x-inline-edit__edit" type="button" aria-label="表示名を編集">
          <i class="fa-solid fa-pen" aria-hidden="true"></i>
        </button>
      </dd>
    </div>
    <!-- 見本なので編集中の状態。保存・キャンセルのあとは値の表示に戻し、フォーカスを「編集」ボタンへ戻す -->
    <div class="x-inline-edit__row x-inline-edit__row--on">
      <dt class="x-inline-edit__k"><label class="x-inline-edit__lbl" for="x-inline-edit-i">部署</label></dt>
      <dd class="x-inline-edit__v">
        <input class="x-inline-edit__i" id="x-inline-edit-i" type="text" name="dept" value="デザイン部"
               aria-describedby="x-inline-edit-h">
        <div class="x-inline-edit__acts">
          <button class="x-inline-edit__save" type="button">保存</button>
          <button class="x-inline-edit__cancel" type="button">キャンセル</button>
        </div>
        <p class="x-inline-edit__h" id="x-inline-edit-h">Enter で保存、Esc で取り消し</p>
      </dd>
    </div>
  </dl>
  <!-- 保存したら「部署を保存しました」と書き換える。失敗したら入力欄を残して理由を出す -->
  <p class="x-inline-edit__sr" role="status"></p>
</div>

AIへの指示文

「インライン編集(Inline edit)」を実装してください。

## これは何か
ふだんは値が文字で並んでいて、鉛筆のボタンを押すとその場所が入力欄と「保存」「キャンセル」に変わる。設定画面やプロフィールの項目を1つずつ直すのに使う。

## 使いどころ
- プロフィール・設定・一覧の1項目のように、たまに1か所だけ直す値を、画面を移らずに直してもらうとき
- 読む時間が長く、直す時間が短い画面で、全部を入力欄で並べると読みにくくなるとき

## 実装の要件
- 項目名と値の組(dl など)+ 値の横に button[aria-label="〇〇を編集"]。編集中は同じ場所を label + input + 保存 / キャンセルの button に差し替える
- Enter で保存、Esc で取り消し。どちらのあとも表示に戻し、フォーカスを編集ボタンへ戻す
- 保存中はボタンを押せなくして二重送信を防ぎ、終わったら role="status" の領域に結果を書く
- 日本語の変換を確定・取り消す Enter・Esc では保存も取り消しもしない。keydown で event.isComposing が true か event.keyCode === 229 のときは何もしない。Safari 26 以前は確定の Enter で isComposing が false になる(27 で修正)ので、keyCode の確認を外さない
- 編集できることを見た目で示す。鉛筆のボタンを常に出しておく。ホバーでしか出ないと、タッチやキーボードの利用者は気づけない
- 値そのものをクリックで入力欄に変えるだけにしない。値の横に「編集」ボタンを置けば、キーボードでも読み上げでも操作が分かる
- 欄の外をクリックしたときに保存するのか取り消すのかを決め、画面全体でそろえる。入力途中の内容が黙って消える作りは避ける
- 保存に失敗したら入力欄を開いたまま残し、理由を欄の近くに出す。値の表示に戻してしまうと、何が保存されたのか分からなくなる
- 何項目もまとめて入れる画面には使わない。最初から入力欄を並べたフォーム(テキストフィールド)のほうが速い

## アクセシビリティの要件
- 編集ボタンは button にして、aria-label で「表示名を編集」のように何を直すのかを示す。鉛筆の絵だけでは伝わらない
- 編集を始めたら入力欄へフォーカスを移し、保存かキャンセルのあとは編集ボタンへ戻す。消えた要素にフォーカスが残ると body へ落ちる
- 入力欄は label で項目名と結び、「Enter で保存、Esc で取り消し」の説明を aria-describedby で添える
- 保存できたことを role="status" の領域で「部署を保存しました」と伝える(達成基準 4.1.3 ステータスメッセージ)
- フォーカスの位置が見えるよう :focus-visible に枠を出す(達成基準 2.4.7 フォーカスの可視化)

## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。

```html
<div class="x-inline-edit">
  <dl class="x-inline-edit__list">
    <div class="x-inline-edit__row">
      <dt class="x-inline-edit__k">表示名</dt>
      <dd class="x-inline-edit__v">
        <span class="x-inline-edit__val">山田 花子</span>
        <!-- 押すと、この dd の中身を下の行と同じ入力欄に差し替え、入力欄へフォーカスを移す -->
        <button class="x-inline-edit__edit" type="button" aria-label="表示名を編集">
          <i class="fa-solid fa-pen" aria-hidden="true"></i>
        </button>
      </dd>
    </div>
    <!-- 見本なので編集中の状態。保存・キャンセルのあとは値の表示に戻し、フォーカスを「編集」ボタンへ戻す -->
    <div class="x-inline-edit__row x-inline-edit__row--on">
      <dt class="x-inline-edit__k"><label class="x-inline-edit__lbl" for="x-inline-edit-i">部署</label></dt>
      <dd class="x-inline-edit__v">
        <input class="x-inline-edit__i" id="x-inline-edit-i" type="text" name="dept" value="デザイン部"
               aria-describedby="x-inline-edit-h">
        <div class="x-inline-edit__acts">
          <button class="x-inline-edit__save" type="button">保存</button>
          <button class="x-inline-edit__cancel" type="button">キャンセル</button>
        </div>
        <p class="x-inline-edit__h" id="x-inline-edit-h">Enter で保存、Esc で取り消し</p>
      </dd>
    </div>
  </dl>
  <!-- 保存したら「部署を保存しました」と書き換える。失敗したら入力欄を残して理由を出す -->
  <p class="x-inline-edit__sr" role="status"></p>
</div>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-inline-edit,
.x-inline-edit *:not([class*="fa-"]),
.x-inline-edit *:not([class*="fa-"])::before,
.x-inline-edit *:not([class*="fa-"])::after {
  box-sizing: border-box;
  /* 貼った先の既定の書体に引きずられないようにする。書体が変わると
     文字の幅と高さが変わり、枠のサイズまでずれる */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}

.x-inline-edit {
  width: 320px;
  max-width: 100%;
  color: #1b2028;
  font-size: 14px;
}
.x-inline-edit__list {
  margin: 0;
  border: 1px solid #7f8a99;
  border-radius: 8px;
  background: #fff;
}
.x-inline-edit__row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 12px;
}
.x-inline-edit__row + .x-inline-edit__row {
  border-top: 1px solid #d5dae1;
}
.x-inline-edit__k {
  flex: none;
  width: 56px;
  padding-top: 7px;
  color: #5b6472;
  font-size: 12px;
  font-weight: 700;
}
.x-inline-edit__v {
  display: flex;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-width: 0;
  margin: 0;
}
.x-inline-edit__val {
  flex: 1 1 auto;
  min-width: 0;
  padding: 6px 0;
  overflow-wrap: anywhere;
}
/* 鉛筆は常に見せる。ホバーでしか出ないと、タッチとキーボードの利用者が編集できると気づかない */
.x-inline-edit__edit {
  width: 32px;
  height: 32px;
  flex: none;
  border: 1px solid #7f8a99;
  border-radius: 6px;
  background: #fff;
  color: #1b2028;
  font-size: 12px;
  cursor: pointer;
}
.x-inline-edit__edit:hover {
  background: #eef2ff;
}
.x-inline-edit__i {
  flex: 1 1 100%;
  min-width: 0;
  height: 36px;
  padding: 0 10px;
  border: 1px solid #1d4ed8;
  border-radius: 6px;
  background: #fff;
  color: #1b2028;
  font: inherit;
  font-size: 14px;
}
.x-inline-edit__acts {
  display: flex;
  gap: 6px;
}
.x-inline-edit__save,
.x-inline-edit__cancel {
  height: 30px;
  padding: 0 12px;
  border: 1px solid #1d4ed8;
  border-radius: 6px;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.x-inline-edit__save {
  background: #1d4ed8;
  color: #fff;
}
.x-inline-edit__cancel {
  border-color: #7f8a99;
  background: #fff;
  color: #1b2028;
}
.x-inline-edit__h {
  margin: 0;
  color: #5b6472;
  font-size: 11px;
}
.x-inline-edit__edit:focus-visible,
.x-inline-edit__i:focus-visible,
.x-inline-edit__save:focus-visible,
.x-inline-edit__cancel:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
}
.x-inline-edit__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}
```

## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。

使いどころ

  • プロフィール・設定・一覧の1項目のように、たまに1か所だけ直す値を、画面を移らずに直してもらうとき
  • 読む時間が長く、直す時間が短い画面で、全部を入力欄で並べると読みにくくなるとき

注意点

  • 編集できることを見た目で示す。鉛筆のボタンを常に出しておく。ホバーでしか出ないと、タッチやキーボードの利用者は気づけない
  • 値そのものをクリックで入力欄に変えるだけにしない。値の横に「編集」ボタンを置けば、キーボードでも読み上げでも操作が分かる
  • 欄の外をクリックしたときに保存するのか取り消すのかを決め、画面全体でそろえる。入力途中の内容が黙って消える作りは避ける
  • 保存に失敗したら入力欄を開いたまま残し、理由を欄の近くに出す。値の表示に戻してしまうと、何が保存されたのか分からなくなる
  • 何項目もまとめて入れる画面には使わない。最初から入力欄を並べたフォーム(テキストフィールド)のほうが速い

アクセシビリティの要点

  • 編集ボタンは button にして、aria-label で「表示名を編集」のように何を直すのかを示す。鉛筆の絵だけでは伝わらない
  • 編集を始めたら入力欄へフォーカスを移し、保存かキャンセルのあとは編集ボタンへ戻す。消えた要素にフォーカスが残ると body へ落ちる
  • 入力欄は label で項目名と結び、「Enter で保存、Esc で取り消し」の説明を aria-describedby で添える
  • 保存できたことを role="status" の領域で「部署を保存しました」と伝える(達成基準 4.1.3 ステータスメッセージ)
  • フォーカスの位置が見えるよう :focus-visible に枠を出す(達成基準 2.4.7 フォーカスの可視化)

関連するパーツ