返品・交換リクエストフォーム

Shopify用の返品・交換リクエストフォームデモページです。注文番号・注文日・商品名の入力、返品(返金)または交換の選択、理由の分類選択(商品破損・欠陥・誤配送・サイズ違い・イメージ違い等)に対応。商品状態の写真はドラッグ&ドロップで複数添付でき、サムネイルでプレビュー表示されます。ポリシー同意のチェックボックスも設置済み。カスタマーサポート業務を効率化し、返品・交換対応の品質向上に貢献します。HTML・CSS・JavaScriptをコピーしてすぐにご利用いただけます。
プレビュー

※プレビューのため送信ボタンは押せません

コード

HTML

<h1>返品・交換リクエスト</h1>
<p class="form-description">商品の返品・交換をご希望の場合は、以下のフォームよりお申し込みください。</p>

<div class="note">
  ※ 商品到着後7日以内にお申し込みください。<br>
  ※ お客様都合の返品は未使用・未開封の商品に限ります。
</div>

<form action="#" method="POST" enctype="multipart/form-data">
  <div class="form-row">
    <div class="form-group">
      <label for="name" class="required">お名前</label>
      <input type="text" id="name" name="name" required autocomplete="name">
    </div>
    <div class="form-group">
      <label for="email" class="required">メールアドレス</label>
      <input type="email" id="email" name="email" required autocomplete="email">
    </div>
  </div>

  <div class="form-row">
    <div class="form-group">
      <label for="order_number" class="required">注文番号</label>
      <input type="text" id="order_number" name="order_number" placeholder="例: #12345" required>
    </div>
    <div class="form-group">
      <label for="order_date">ご注文日</label>
      <input type="date" id="order_date" name="order_date">
    </div>
  </div>

  <div class="form-group">
    <label for="product_name" class="required">商品名</label>
    <input type="text" id="product_name" name="product_name" placeholder="返品・交換をご希望の商品名" required>
  </div>

  <div class="form-group">
    <label class="required">ご希望の対応</label>
    <div class="radio-group">
      <label class="radio-item">
        <input type="radio" name="request_type" value="return" required>
        <span>返品(返金)</span>
      </label>
      <label class="radio-item">
        <input type="radio" name="request_type" value="exchange">
        <span>交換</span>
      </label>
    </div>
  </div>

  <div class="form-group">
    <label for="reason" class="required">理由</label>
    <select id="reason" name="reason" required>
      <option value="">選択してください</option>
      <optgroup label="商品の不具合">
        <option value="damaged">商品が破損していた</option>
        <option value="defective">商品に欠陥がある</option>
        <option value="wrong_item">注文と異なる商品が届いた</option>
      </optgroup>
      <optgroup label="お客様都合">
        <option value="size">サイズが合わない</option>
        <option value="color">色・イメージが違った</option>
        <option value="duplicate">重複して注文した</option>
        <option value="other_customer">その他</option>
      </optgroup>
    </select>
  </div>

  <div class="form-group">
    <label for="detail" class="required">詳細</label>
    <textarea id="detail" name="detail" placeholder="具体的な状況をご記入ください(破損箇所、交換希望のサイズ・色など)" required></textarea>
  </div>

  <div class="form-group">
    <label>商品の状態がわかる写真</label>
    <div class="drop-zone">
      <input type="file" name="images[]" accept="image/*" multiple class="file-input">
      <div class="drop-zone-text">画像をドラッグ&ドロップ、またはクリックして選択</div>
      <div class="drop-zone-hint">複数選択可・1ファイル最大10MB(jpg, png, webp)</div>
    </div>
    <ul class="previews"></ul>
  </div>

  <div class="form-group">
    <label class="checkbox-item">
      <input type="checkbox" name="agree" required>
      <span>返品・交換ポリシーに同意します</span>
    </label>
  </div>

  <button type="submit" class="btn-submit">送信する</button>
</form>

CSS

.form-description {
  color: #666;
  margin-bottom: 32px;
  font-size: 14px;
}

.form-group {
  margin-bottom: 24px;
}

label {
  display: block;
  font-weight: 500;
  margin-bottom: 8px;
  font-size: 14px;
}

.required::after {
  content: "*";
  color: #e53935;
  margin-left: 4px;
}

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="date"],
select,
textarea {
  width: 100%;
  padding: 12px 16px;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 16px;
  transition: border-color 0.2s, box-shadow 0.2s;
box-sizing: border-box;
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: #2563eb;
  box-shadow: 0 0 0 3px rgba(37,99,235,0.1);
}

textarea {
  min-height: 120px;
  resize: vertical;
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

@media (max-width: 540px) {
  .form-row {
    grid-template-columns: 1fr;
  }
}

.radio-group {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}

.radio-item {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.radio-item input[type="radio"] {
  width: 18px;
  height: 18px;
  accent-color: #2563eb;
}

.checkbox-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  cursor: pointer;
}

.checkbox-item input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: #2563eb;
}

/* ドロップゾーン(画像添付) */
.drop-zone {
  border: 2px dashed #ddd;
  border-radius: 8px;
  padding: 32px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
  position: relative;
}

.drop-zone:hover,
.drop-zone.is-dragover {
  border-color: #2563eb;
  background: #f8fafc;
}

.drop-zone .file-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.drop-zone-text {
  color: #666;
  font-size: 14px;
}

.drop-zone-hint {
  color: #999;
  font-size: 12px;
  margin-top: 8px;
}

/* プレビューリスト */
.previews {
  list-style: none;
  padding: 0;
  margin: 12px 0 0;
}

.previews .preview {
  padding: 8px 12px;
  background: #f5f5f5;
  border-radius: 4px;
  font-size: 13px;
  color: #333;
  margin-bottom: 6px;
}

.btn-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 14px 32px;
  background: #2563eb;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.2s, transform 0.1s;
}

.btn-submit:hover {
  background: #1d4ed8;
}

.btn-submit:active {
  transform: translateY(1px);
}

.note {
  background: #fef3c7;
  border-left: 4px solid #f59e0b;
  padding: 12px 16px;
  margin-bottom: 24px;
  font-size: 14px;
  color: #92400e;
}

JavaScript

const form        = document.querySelector("form");
const fileInput   = form.querySelector(".file-input");
const dropZone    = form.querySelector(".drop-zone");
const previewList = form.querySelector(".previews");

const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
let selectedFiles = [];

function addFiles(list){
  for(const file of list){
    if(!file.type.startsWith("image/")) { console.warn("画像以外は不可:", file.name); continue; }
    if(file.size > MAX_FILE_SIZE)       { console.warn("10MB超過:", file.name); continue; }
    selectedFiles.push(file);
  }
  const dt = new DataTransfer();
  selectedFiles.forEach(f => dt.items.add(f));
  fileInput.files = dt.files;

  previewList.innerHTML = "";
  selectedFiles.forEach(f => {
    const li = document.createElement("li");
    li.className = "preview";
    li.textContent = `${f.name} (${(f.size/1024/1024).toFixed(1)} MB)`;
    previewList.appendChild(li);
  });
}

// 誤ドロップ遷移防止
addEventListener("dragover", e => e.preventDefault());
addEventListener("drop",     e => e.preventDefault());

// D&D
["dragenter","dragover"].forEach(ev => {
  dropZone.addEventListener(ev, e => { e.preventDefault(); dropZone.classList.add("is-dragover"); });
});
["dragleave","drop"].forEach(ev => {
  dropZone.addEventListener(ev, e => { e.preventDefault(); dropZone.classList.remove("is-dragover"); });
});
dropZone.addEventListener("drop",  e => addFiles(e.dataTransfer.files));
fileInput.addEventListener("change", e => addFiles(e.target.files));