UPSIDER Tech Blog

Claude Code SkillsでUXライティングを始めよう

こんにちは! UPSIDER 支払い.com事業部の岡橋です。

今回は、支払い.com事業部で導入したUXライティングにまつわるSkillsをご紹介します。

エラーメッセージ、悩みませんか?

突然ですが、みなさんのプロダクト内にある文言は誰が考えていますか?

デザインレベルで全てを決め、PdMや関係者で合意できれば理想だと思います。しかし現実には、特にエラーメッセージなどは実装者が頭を悩ませながら「よしなに」決めているケースが多いのではと思います。
(私たちのチームでも、文言の多くはエンジニアが考え実装しています。)

よしなに文言を決めていると統一感が損なわれがちになり、実装者によって文言にばらつきが生まれやすくなります。

  • 同じものを指しているのに微妙に言い回しが異なる(例: 請求書 / 資料 / 証憑)
  • トーンがズレる(例: ~と入力してください。 / ~とご入力ください。)
  • ユーザーにとってわかりにくいメッセージになっている(例: 入力値が不正です。/ エラーが発生しました)

UXライティングを取り入れよう

そこで、UXライティングの考え方を部分的に取り入れてみました。
いくつかの原則が提唱されていますが、以下のような原則に倣ってライティングを行う考え方だと捉えています。

Brevity(簡潔さ)短い文章で必要な情報を伝えること。
Clarity(明瞭さ) ユーザーがすぐ理解できるように簡単な言葉を使うこと。
Usefulness(有用性)ユーザーの行動を正しく助ける文言であること。
Consistency(一貫性)同じアクションや状態には同じ文言・用語を使うこと。
Empathy(共感・トーンの配慮)ユーザーの状況や感情に寄り添う言葉づかい。

参考: https://m2.material.io/design/communication/writing.html#principles
参考: https://www.uxdesigninstitute.com/blog/what-is-ux-writing/

今あるプロダクトコードを活用しよう

とは言っても、いきなり全てを取り入れてルール化することは難しいと感じます。 また一貫性についても、今使われている用語を人力で洗い出してまとめ、正しいものを判断していくのは難しいです。

そこで私たちのチームでは、今あるプロダクトのコードを元にAIを活用して使われている用語やトーンを洗い出し、Claude CodeのSkillsとして共有する形でほぼ人の手をかけずにUXライティング観点のレビュー環境を整えました。

具体的には、以下のような構成でスキルと関連する単語や表現をまとめています。

skills/ux-writing-review/
├── SKILL.md                          # スキル本体(レビュー観点の定義)
└── reference/
    ├── domain-terminology.md          # ドメイン用語辞書
    └── standard-expressions.md        # 標準表現パターン集

SKILL.md(一部ぼかしています)

---
name: ux-writing-review
description: Skill for reviewing UX writing quality in customer-facing UI text
allowed-tools: Read, Glob, Grep
---

# UX Writing Review Skill

## Overview

Review UI text in `XXXX` for quality and consistency.

## Activation Conditions

- Creating or modifying UI text (button labels, error messages, modal copy, form labels, notifications, etc.) under `XXXX`
- When a UX writing review is requested
## Review Criteria

1. **Terminology consistency** — Follows [domain-terminology.md](reference/domain-terminology.md)
2. **Expression pattern consistency** — Follows [standard-expressions.md](reference/standard-expressions.md)
3. **Clarity** — User knows what to do. One message per sentence
4. **Conciseness** — No unnecessary words. Labels should be 2-6 characters
5. **User-centric** — User perspective, not system perspective. Errors include recovery steps
6. **Action-oriented** — Buttons use specific action verbs, not generic "OK/Yes". Destructive actions clearly state what happens

## Report Format

```
#### [Severity: High/Medium/Low] Issue title
- **Location**: quote of the text
- **Issue**: what's wrong
- **Suggestion**: proposed fix
- **Basis**: terminology / expression pattern / UX principle
```

## Reference Materials

- [Domain Terminology](reference/domain-terminology.md) — Domain-specific terms, spelling rules, status labels
- [Standard Expressions](reference/standard-expressions.md) — Buttons, modals, notifications, validation, politeness levels, anti-patterns

domain-terminology.md (抜粋)

# Domain Terminology

Canonical terms used in the shiharai.com customer UI. Always use the canonical form to prevent inconsistency.

## Payment / Transfer

| Canonical | NG |
|-----------|----|
| 振込 | 振り込み、送金 |
| 振込金額 | 送金額、振込額 |
| 振込日 | 支払日、送金日 |
| 振込依頼人 | 振込人、送金者、振込名義 |
| 振込先情報 | 送金先情報 |
| 支払い | お支払い (use 支払い in UI labels) |
| 支払いカード / クレジットカード | クレカ、カード情報 |

## Bank Account

| Canonical | NG |
|-----------|----|
| 金融機関 | 銀行 |
| 支店 | 支店名 |
| 口座種別(普通 / 当座) | 口座タイプ、普通預金 / 当座預金 |
| 受取人名(口座名義) | 受取人名 alone |

standard-expressions.md(抜粋)

# Standard Expressions

Standard UI copy patterns for the shiharai.com customer UI. New text must follow these patterns.

---
## Politeness Levels

| Context | Level | Example |
|---------|-------|---------|
| Notices / warnings | High (ございます, いただく) | 「」 |
| Instructions / notifications | Standard (してください, しました) | 「入力内容をご確認ください」 |
| Validation errors | Standard (してください, ください) | 「{field}を入力してください」 |
| Labels / buttons | Minimal (noun phrases) | 「振込金額」「確定」 |

Validation errors should use consistent politeness within the same form. Either「〜してください」or「ご〜ください」is acceptable, but do not mix both styles in a single form.

## Form Elements

- **Labels**: 2-6 chars, omit particles (「振込の金額」→「振込金額」)
- **Help text**: Explain terms with「〜とは、〜です。」, instructions with「〜してください」
- **Placeholder**: Show examples (「株式会社UPSIDER」) or describe action (「受取人名または口座番号で検索」). Never use instruction text

具体例として、以下のようなFormをレビューさせてみます。
domain-terminology.md に沿っていない文言が使われていたり、エラーメッセージが不明瞭な箇所があります。

import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'

const FORM_SCHEMA = z.object({
  amount: z
    .number({ invalid_type_error: '入力値が不正です' })
    .max(100_000_000, {
      message: '上限値を超えています',
    }),
  transferDate: z.string().min(1, { message: '送金日を入力してください' }),
})

const mutate = async (_data: { amount: number; transferDate: string }) => {
  await new Promise((r) => setTimeout(r, 1000))
  return
}

export const SampleForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(FORM_SCHEMA),
  })

  const onValid = (args: Parameters<typeof mutate>[0]) => {
    mutate(args).catch(() => {
      onInvalid()
    })
  }

  const onInvalid = () => {
    alert('エラーが発生しました')
  }

  return (
    <form onSubmit={handleSubmit(onValid, onInvalid)}>
      <div>
        <label htmlFor="amount">金額</label>
        <input
          id="amount"
          type="text"
          inputMode="numeric"
          {...register('amount')}
        />
        {errors.amount && <p>{errors.amount.message}</p>}
      </div>

      <div>
        <label htmlFor="transferDate">送金日</label>
        <input id="transferDate" type="date" {...register('transferDate')} />
        {errors.transferDate && <p>{errors.transferDate.message}</p>}
      </div>

      <button type="submit">送信</button>
    </form>
  )
}

以下のような形で、ラベルやエラーメッセージが適切ではないことを検知できました 🙌

レビュー結果
---                                                                                              
[Severity: High] NG用語「送金日」の使用                         
                                                                                                 
- Location: L12 '送金日を入力してください' / L53 送金日                                          
- Issue: 「送金日」はNG用語。ドメイン用語辞書で「振込日」が正規表現と定義されている              
- Suggestion: 送金日 → 振込日                                                                    
- Basis: domain-terminology — 振込日 | NG: 支払日、送金日                                        
                                                                                                 
---                                                                                              
[Severity: High] システム視点のエラーメッセージ「入力値が不正です」

- Location: L8 '入力値が不正です'
- Issue: 「入力値が不正」はシステム視点の表現で、ユーザーが何をすればよいか分からない。標準のバリ
デーションパターンに沿っていない
- Suggestion: '入力値が不正です' → '振込金額を数値で入力してください'
- Basis: standard-expressions — Validation Messages:
「{field}が正しく入力されていることを確認してください。」 / Anti-patterns:
システム視点のコピーはNG

---
[Severity: High] 誤字・不適切なエラーメッセージ「上限値を超えています」

- Location: L10 '上限値を超えています'
- Issue: 上限値が具体的にいくらかユーザーに伝わらない
- Suggestion: '上限値を超えています' → '振込金額は¥100,000,000以下で入力してください'
- Basis: standard-expressions — Numbers & Symbols: Currency: ¥1,000,000 / UX原則:
Clarity(明瞭さ)、具体的な上限値を提示

---
[Severity: High] リカバリパスのないエラーメッセージ「エラーが発生しました」

- Location: L36 alert('エラーが発生しました')
- Issue: 「エラーが発生しました」単独はAnti-patternとして明記されている。ユーザーに次のアクション
が示されていない
- Suggestion: 'エラーが発生しました' → 'エラーが発生しました。時間をおいて再度お試しください。'
- Basis: standard-expressions — Anti-patterns: 「エラーが発生しました」(alone) →
「エラーが発生しました。時間をおいて再度お試しください。」 No recovery path

---
[Severity: Medium] ラベル「金額」が文脈不足

- Location: L42 金額
- Issue: 「金額」単体ではどの金額か曖昧。ドメイン用語辞書では「振込金額」が正規表現
- Suggestion: 金額 → 振込金額
- Basis: domain-terminology — 振込金額 | NG: 送金額、振込額 / standard-expressions — Labels: 2-6
chars

---
[Severity: Medium] ボタンラベル「送信」が汎用的

- Location: L58 送信
- Issue:
標準のボタンラベルでは実行系アクションには「確定」を使う。「送信」はボタンラベル規約に含まれない
- Suggestion: 送信 → 確定
- Basis: standard-expressions — Button Labels: Submit / Execute → 確定 | NG: OK、決定、実行

---

今後やりたいこと

現状作ったSkillsはあくまでも今あるものをベースとして統一を図るものです。
ここからは、よりわかりやすい表現や適切なトーンを設計しSkillsに落とし込んでいく必要があります。

また、このSkillsは開発者に閉じる必要もないと思っています。
例えばお問い合わせへの回答で活用したり、よくある質問の内容をレビューさせるなどプロダクト全体で活用していけば、よりよい体験につながると考えています 🔥

もう少し運用してみながら、Skillsをブラッシュアップしていきます💪

We Are Hiring !!

UPSIDERでは現在積極採用をしています。 ぜひお気軽にご応募ください。

herp.careers

herp.careers

UPSIDER Engineering Deckはこちら📣

speakerdeck.com