# 詞彙學習 - UI設計規範

**文件版本**: 1.0
**建立日期**: 2025-09-27
**目標讀者**: 前端實作工程師、UI設計師
**用途**: HTML/CSS實作、視覺設計、組件規範
**配合文檔**: [智能複習系統-開發指南.md](/note/智能複習/智能複習系統-開發指南.md) - 系統架構和業務邏輯
**來源**: 從備份檔案 `page-v1-original.tsx` 提取的完整UI設計規格

---

## 📋 總體設計架構

### 核心測驗類型
該系統包含6種主要測驗類型：
1. **翻卡記憶** (flip-memory)
2. **詞彙選擇** (vocab-choice)
3. **例句填空** (sentence-fill)
4. **例句重組** (sentence-reorder)
5. **聽力測驗** (listening - vocab-listening, sentence-listening)
6. **口說測驗** (speaking - sentence-speaking)

### 共同設計元素

#### 通用容器結構
```jsx
<div className="relative">
  {/* 錯誤回報按鈕 */}
  <div className="flex justify-end mb-2">
    <button className="px-3 py-2 rounded-md transition-colors text-gray-600 hover:text-gray-900">
      🚩 回報錯誤
    </button>
  </div>

  {/* 主要內容區 */}
  <div className="bg-white rounded-xl shadow-lg p-8">
    {/* 測驗內容 */}
  </div>

  {/* 導航按鈕 */}
  <div className="flex gap-4 mt-6">
    <button className="flex-1 py-3 bg-gray-500 text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-600 transition-colors font-medium">
      上一張
    </button>
    <button className="flex-1 py-3 bg-primary text-white rounded-lg hover:bg-primary-dark transition-colors font-medium">
      下一張/完成
    </button>
  </div>
</div>
```

#### 通用標題區
```jsx
<div className="flex justify-between items-start mb-6">
  <h2 className="text-2xl font-bold text-gray-900">
    {測驗名稱}
  </h2>
  <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
    {currentCard.difficultyLevel}
  </span>
</div>
```

#### 配色方案
- **主色調**: `bg-primary`, `text-white`, `hover:bg-primary-dark`
- **次要色調**: `bg-gray-500`, `hover:bg-gray-600`
- **成功狀態**: `bg-green-50`, `border-green-200`, `text-green-700`
- **錯誤狀態**: `bg-red-50`, `border-red-200`, `text-red-700`
- **提示狀態**: `bg-yellow-50`, `border-yellow-200`, `text-yellow-800`
- **信息狀態**: `bg-blue-50`, `border-blue-200`, `text-blue-700`

---

## 🔄 1. 翻卡記憶 (flip-memory)

### 核心特色
- 3D翻卡動畫效果
- 正面顯示單字，背面顯示詳細資訊
- 信心等級評估系統

### HTML結構
```jsx
<div
  className="card-container"
  onClick={handleFlip}
  style={{ height: `${cardHeight}px` }}
>
  <div className={`card ${isFlipped ? 'flipped' : ''}`}>
    {/* 正面 */}
    <div className="card-front">
      <div className="bg-white rounded-xl shadow-lg cursor-pointer hover:shadow-xl transition-shadow p-8">
        {/* 標題區 */}
        <div className="flex justify-between items-start mb-6">
          <h2 className="text-2xl font-bold text-gray-900">翻卡記憶</h2>
          <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
            {difficultyLevel}
          </span>
        </div>

        {/* 指示文字 */}
        <p className="text-lg text-gray-700 mb-6 text-left">
          點擊卡片翻面，根據你對單字的熟悉程度進行自我評估：
        </p>

        {/* 單字顯示區 */}
        <div className="flex-1 flex items-center justify-center mt-6">
          <div className="bg-gray-50 rounded-lg p-8 w-full text-center">
            <h3 className="text-4xl font-bold text-gray-900 mb-6">{word}</h3>
            <div className="flex items-center justify-center gap-3">
              <span className="text-lg text-gray-500">{pronunciation}</span>
              <AudioPlayer text={word} />
            </div>
          </div>
        </div>
      </div>
    </div>

    {/* 背面 */}
    <div className="card-back">
      <div className="bg-white rounded-xl shadow-lg cursor-pointer hover:shadow-xl transition-shadow">
        <div className="space-y-4">
          {/* 定義區塊 */}
          <div className="bg-gray-50 rounded-lg p-4">
            <h3 className="font-semibold text-gray-900 mb-2 text-left">定義</h3>
            <p className="text-gray-700 text-left">{definition}</p>
          </div>

          {/* 例句區塊 */}
          <div className="bg-gray-50 rounded-lg p-4">
            <h3 className="font-semibold text-gray-900 mb-2 text-left">例句</h3>
            <div className="relative">
              <p className="text-gray-700 italic mb-2 text-left pr-12">{example}</p>
              <div className="absolute bottom-0 right-0">
                <AudioPlayer text={example} />
              </div>
            </div>
            <p className="text-gray-600 text-sm text-left">{exampleTranslation}</p>
          </div>

          {/* 同義詞區塊 */}
          <div className="bg-gray-50 rounded-lg p-4">
            <h3 className="font-semibold text-gray-900 mb-2 text-left">同義詞</h3>
            <div className="flex flex-wrap gap-2">
              {synonyms.map(synonym => (
                <span className="bg-white text-gray-700 px-3 py-1 rounded-full text-sm border border-gray-200">
                  {synonym}
                </span>
              ))}
            </div>
          </div>

          {/* 信心等級評估區 */}
          <div className="bg-blue-50 rounded-lg p-4">
            <h3 className="font-semibold text-blue-900 mb-3 text-left">你對這個單字的熟悉程度：</h3>
            <div className="grid grid-cols-5 gap-2">
              {[1, 2, 3, 4, 5].map(level => (
                <button
                  key={level}
                  onClick={() => handleConfidenceLevel(level)}
                  className="py-3 px-4 bg-white border border-blue-200 text-blue-700 rounded-lg hover:bg-blue-100 hover:border-blue-300 transition-all text-center"
                >
                  <div className="font-bold text-lg">{level}</div>
                  <div className="text-xs">
                    {level === 1 ? '完全不懂' :
                     level === 2 ? '模糊' :
                     level === 3 ? '一般' :
                     level === 4 ? '熟悉' : '非常熟悉'}
                  </div>
                </button>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
```

### CSS動畫樣式
```css
.card-container {
  perspective: 1000px;
  transition: height 0.3s ease;
  overflow: visible;
  position: relative;
}

.card {
  position: relative;
  width: 100%;
  height: 100%;
  text-align: center;
  transition: transform 0.6s ease;
  transform-style: preserve-3d;
}

.card.flipped {
  transform: rotateY(180deg);
}

.card-front, .card-back {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  display: flex;
  align-items: stretch;
  justify-content: center;
  top: 0;
  left: 0;
}

.card-back {
  transform: rotateY(180deg);
}
```

---

## 🎯 2. 詞彙選擇 (vocab-choice)

### 核心特色
- 四選一選擇題
- 顯示詞彙定義，選擇對應單字
- 即時反饋系統

### HTML結構
```jsx
<div className="bg-white rounded-xl shadow-lg p-8">
  {/* 標題區 */}
  <div className="flex justify-between items-start mb-6">
    <h2 className="text-2xl font-bold text-gray-900">詞彙選擇</h2>
    <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
      {difficultyLevel}
    </span>
  </div>

  {/* 指示文字 */}
  <p className="text-lg text-gray-700 mb-6 text-left">
    請選擇符合上述定義的英文詞彙：
  </p>

  {/* 定義顯示區 */}
  <div className="text-center mb-8">
    <div className="bg-gray-50 rounded-lg p-4 mb-6">
      <h3 className="font-semibold text-gray-900 mb-2 text-left">定義</h3>
      <p className="text-gray-700 text-left">{definition}</p>
    </div>
  </div>

  {/* 選項區域 */}
  <div className="space-y-3 mb-6">
    {quizOptions.map((option, idx) => (
      <button
        key={idx}
        onClick={() => !showResult && handleQuizAnswer(option)}
        disabled={showResult}
        className={`w-full p-4 text-left rounded-lg border-2 transition-all ${
          showResult
            ? option === correctAnswer
              ? 'border-green-500 bg-green-50 text-green-700'
              : option === selectedAnswer
              ? 'border-red-500 bg-red-50 text-red-700'
              : 'border-gray-200 bg-gray-50 text-gray-500'
            : 'border-gray-200 hover:border-blue-300 hover:bg-blue-50'
        }`}
      >
        {option}
      </button>
    ))}
  </div>

  {/* 結果反饋區 */}
  {showResult && (
    <div className={`p-6 rounded-lg w-full mb-6 ${
      selectedAnswer === correctAnswer
        ? 'bg-green-50 border border-green-200'
        : 'bg-red-50 border border-red-200'
    }`}>
      <p className={`font-semibold text-left text-xl mb-4 ${
        selectedAnswer === correctAnswer ? 'text-green-700' : 'text-red-700'
      }`}>
        {selectedAnswer === correctAnswer ? '正確！' : '錯誤！'}
      </p>

      {selectedAnswer !== correctAnswer && (
        <div className="mb-4">
          <p className="text-gray-700 text-left">
            正確答案是：<strong className="text-lg">{correctAnswer}</strong>
          </p>
        </div>
      )}

      <div className="space-y-3">
        <div className="text-left">
          <div className="flex items-center text-gray-600">
            <span className="mx-2">{pronunciation}</span>
            <AudioPlayer text={correctAnswer} />
          </div>
        </div>

        <div className="text-left">
          <p className="text-gray-600">
            <strong>例句：</strong>{example}
          </p>
          <p className="text-gray-500 text-sm">
            <strong>翻譯：</strong>{exampleTranslation}
          </p>
        </div>
      </div>
    </div>
  )}
</div>
```

---

## ✏️ 3. 例句填空 (sentence-fill)

### 核心特色
- 內嵌式輸入框設計
- 動態寬度調整
- 提示系統
- 支援圖片顯示

### HTML結構
```jsx
<div className="bg-white rounded-xl shadow-lg p-8">
  {/* 標題區 */}
  <div className="flex justify-between items-start mb-6">
    <h2 className="text-2xl font-bold text-gray-900">例句填空</h2>
    <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
      {difficultyLevel}
    </span>
  </div>

  {/* 圖片區（如果有） */}
  {exampleImage && (
    <div className="mb-6">
      <div className="bg-gray-50 rounded-lg p-4">
        <h3 className="font-semibold text-gray-900 mb-2 text-left">圖片提示</h3>
        <img
          src={exampleImage}
          alt="Example illustration"
          className="w-full max-w-md mx-auto rounded-lg cursor-pointer"
          onClick={() => setModalImage(exampleImage)}
        />
      </div>
    </div>
  )}

  {/* 指示文字 */}
  <p className="text-lg text-gray-700 mb-6 text-left">
    請點擊例句中的空白處輸入正確的單字：
  </p>

  {/* 填空句子區域 */}
  <div className="mb-6">
    <div className="bg-gray-50 rounded-lg p-6">
      <div className="text-lg text-gray-700 leading-relaxed">
        {/* 動態生成填空句子 */}
        {sentence.split(targetWord).map((part, index) => {
          const isTargetWord = index > 0;
          return (
            <React.Fragment key={index}>
              <span>{part}</span>
              {isTargetWord && (
                <span className="relative inline-block mx-1">
                  <input
                    type="text"
                    value={fillAnswer}
                    onChange={(e) => setFillAnswer(e.target.value)}
                    onKeyDown={(e) => {
                      if (e.key === 'Enter' && !showResult && fillAnswer.trim()) {
                        handleFillAnswer()
                      }
                    }}
                    placeholder=""
                    disabled={showResult}
                    className={`inline-block px-2 py-1 text-center bg-transparent focus:outline-none disabled:bg-gray-100 ${
                      fillAnswer
                        ? 'border-b-2 border-blue-500'
                        : 'border-b-2 border-dashed border-gray-400 focus:border-blue-400 focus:border-solid'
                    }`}
                    style={{
                      width: `${Math.max(100, Math.max(targetWord.length * 12, fillAnswer.length * 12 + 20))}px`
                    }}
                  />
                  {!fillAnswer && (
                    <span
                      className="absolute inset-0 flex items-center justify-center text-gray-400 pointer-events-none"
                      style={{ paddingBottom: '8px' }}
                    >
                      ____
                    </span>
                  )}
                </span>
              )}
            </React.Fragment>
          )
        })}
      </div>
    </div>
  </div>

  {/* 操作按鈕 */}
  <div className="flex gap-3 mb-4">
    {!showResult && fillAnswer.trim() && (
      <button
        onClick={handleFillAnswer}
        className="px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary-dark transition-colors"
      >
        確認答案
      </button>
    )}
    <button
      onClick={() => setShowHint(!showHint)}
      className="px-6 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 transition-colors"
    >
      {showHint ? '隱藏提示' : '顯示提示'}
    </button>
  </div>

  {/* 提示區域 */}
  {showHint && (
    <div className="mb-4 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
      <h4 className="font-semibold text-yellow-800 mb-2">詞彙定義：</h4>
      <p className="text-yellow-800">{definition}</p>
    </div>
  )}

  {/* 結果反饋區 */}
  {showResult && (
    <div className={`mt-6 p-6 rounded-lg w-full ${
      fillAnswer.toLowerCase().trim() === targetWord.toLowerCase()
        ? 'bg-green-50 border border-green-200'
        : 'bg-red-50 border border-red-200'
    }`}>
      <p className={`font-semibold text-left text-xl mb-4 ${
        fillAnswer.toLowerCase().trim() === targetWord.toLowerCase()
          ? 'text-green-700'
          : 'text-red-700'
      }`}>
        {fillAnswer.toLowerCase().trim() === targetWord.toLowerCase() ? '正確！' : '錯誤！'}
      </p>

      {fillAnswer.toLowerCase().trim() !== targetWord.toLowerCase() && (
        <div className="mb-4">
          <p className="text-gray-700 text-left">
            正確答案是：<strong className="text-lg">{targetWord}</strong>
          </p>
        </div>
      )}

      <div className="space-y-3">
        <div className="text-left">
          <p className="text-gray-600">
            <strong>發音：</strong>
            <span className="mx-2">{pronunciation}</span>
            <AudioPlayer text={targetWord} />
          </p>
        </div>

        <div className="text-left">
          <p className="text-gray-600">
            <strong>完整例句：</strong>"{completeExample}"
          </p>
          <p className="text-gray-500 text-sm">
            <strong>翻譯：</strong>"{exampleTranslation}"
          </p>
        </div>
      </div>
    </div>
  )}
</div>
```

---

## 🧩 4. 例句重組 (sentence-reorder)

### 核心特色
- 拖拽式單字重組
- 雙區域設計（可用單字區 + 重組區域）
- 即時狀態反饋

### HTML結構
```jsx
<div className="bg-white rounded-xl shadow-lg p-8">
  {/* 標題區 */}
  <div className="flex justify-between items-start mb-6">
    <h2 className="text-2xl font-bold text-gray-900">例句重組</h2>
    <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
      {difficultyLevel}
    </span>
  </div>

  {/* 圖片區（如果有） */}
  {exampleImage && (
    <div className="mb-6">
      <div className="bg-gray-50 rounded-lg p-4">
        <h3 className="font-semibold text-gray-900 mb-2 text-left">圖片提示</h3>
        <img
          src={exampleImage}
          alt="Example illustration"
          className="w-full max-w-md mx-auto rounded-lg cursor-pointer"
          onClick={() => setModalImage(exampleImage)}
        />
      </div>
    </div>
  )}

  {/* 重組區域 */}
  <div className="mb-6">
    <h3 className="text-lg font-semibold text-gray-900 mb-3 text-left">重組區域：</h3>
    <div className="relative min-h-[120px] bg-gray-50 rounded-lg p-4 border-2 border-dashed border-gray-300">
      {arrangedWords.length === 0 ? (
        <div className="absolute inset-0 flex items-center justify-center text-gray-400 text-lg">
          將下方單字拖拽到這裡組成完整句子
        </div>
      ) : (
        <div className="flex flex-wrap gap-2">
          {arrangedWords.map((word, index) => (
            <div
              key={`arranged-${index}`}
              className="inline-flex items-center bg-blue-100 text-blue-800 px-3 py-2 rounded-full text-lg font-medium cursor-pointer hover:bg-blue-200 transition-colors"
              onClick={() => handleRemoveFromArranged(word)}
            >
              {word}
              <span className="ml-2 text-blue-600 hover:text-blue-800">×</span>
            </div>
          ))}
        </div>
      )}
    </div>
  </div>

  {/* 指示文字 */}
  <p className="text-lg text-gray-700 mb-6 text-left">
    點擊下方單字，依序重組成正確的句子：
  </p>

  {/* 可用單字區域 */}
  <div className="mb-6">
    <h3 className="text-lg font-semibold text-gray-900 mb-3 text-left">可用單字：</h3>
    <div className="bg-white border border-gray-200 rounded-lg p-4 min-h-[80px]">
      {shuffledWords.length === 0 ? (
        <div className="text-center text-gray-400">
          所有單字都已使用
        </div>
      ) : (
        <div className="flex flex-wrap gap-2">
          {shuffledWords.map((word, index) => (
            <button
              key={`shuffled-${index}`}
              onClick={() => handleWordClick(word)}
              className="bg-gray-100 text-gray-800 px-3 py-2 rounded-full text-lg font-medium cursor-pointer hover:bg-gray-200 active:bg-gray-300 transition-colors select-none"
            >
              {word}
            </button>
          ))}
        </div>
      )}
    </div>
  </div>

  {/* 控制按鈕 */}
  <div className="flex gap-3 mb-6">
    {arrangedWords.length > 0 && (
      <button
        onClick={handleCheckReorderAnswer}
        className="px-6 py-2 bg-primary text-white rounded-lg hover:bg-primary-dark transition-colors"
      >
        檢查答案
      </button>
    )}
    <button
      onClick={handleResetReorder}
      className="px-6 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 transition-colors"
    >
      重新開始
    </button>
  </div>

  {/* 結果反饋區 */}
  {reorderResult !== null && (
    <div className={`p-6 rounded-lg w-full mb-6 ${
      reorderResult
        ? 'bg-green-50 border border-green-200'
        : 'bg-red-50 border border-red-200'
    }`}>
      <p className={`font-semibold text-left text-xl mb-4 ${
        reorderResult ? 'text-green-700' : 'text-red-700'
      }`}>
        {reorderResult ? '正確！' : '錯誤！'}
      </p>

      {!reorderResult && (
        <div className="mb-4">
          <p className="text-gray-700 text-left">
            正確答案是：<strong className="text-lg">{correctSentence}</strong>
          </p>
        </div>
      )}

      <div className="space-y-3">
        <div className="text-left">
          <div className="flex items-center text-gray-600">
            <AudioPlayer text={correctSentence} />
          </div>
        </div>

        <div className="text-left">
          <p className="text-gray-600">
            {translation}
          </p>
        </div>
      </div>
    </div>
  )}
</div>
```

---

## 🎧 5. 聽力測驗 (listening)

### 5.1 詞彙聽力 (vocab-listening)
```jsx
<div className="bg-white rounded-xl shadow-lg p-8">
  {/* 標題區 */}
  <div className="flex justify-between items-start mb-6">
    <h2 className="text-2xl font-bold text-gray-900">詞彙聽力</h2>
    <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
      {difficultyLevel}
    </span>
  </div>

  {/* 指示文字 */}
  <p className="text-lg text-gray-700 mb-6 text-left">
    請聽發音並選擇正確的英文單字：
  </p>

  {/* 音頻播放區 */}
  <div className="space-y-4 mb-8">
    <div className="bg-gray-50 rounded-lg p-4">
      <h3 className="font-semibold text-gray-900 mb-2 text-left">發音</h3>
      <div className="flex items-center gap-3">
        <span className="text-gray-700">{pronunciation}</span>
        <AudioPlayer text={word} />
      </div>
    </div>
  </div>

  {/* 選項區域 - 2x2網格布局 */}
  <div className="grid grid-cols-2 gap-3 mb-6">
    {options.map((word) => (
      <button
        key={word}
        onClick={() => !showResult && handleListeningAnswer(word)}
        disabled={showResult}
        className={`p-4 text-center rounded-lg border-2 transition-all ${
          showResult
            ? word === correctAnswer
              ? 'border-green-500 bg-green-50 text-green-700'
              : word === selectedAnswer
              ? 'border-red-500 bg-red-50 text-red-700'
              : 'border-gray-200 bg-gray-50 text-gray-500'
            : 'border-gray-200 hover:border-blue-300 hover:bg-blue-50'
        }`}
      >
        <div className="text-lg font-medium">{word}</div>
        <div className="text-sm text-gray-500 mt-1">{pronunciation}</div>
      </button>
    ))}
  </div>

  {/* 結果反饋區 */}
  {showResult && (
    <div className={`p-6 rounded-lg w-full mb-6 ${
      selectedAnswer === correctAnswer
        ? 'bg-green-50 border border-green-200'
        : 'bg-red-50 border border-red-200'
    }`}>
      <p className={`font-semibold text-left text-xl mb-4 ${
        selectedAnswer === correctAnswer ? 'text-green-700' : 'text-red-700'
      }`}>
        {selectedAnswer === correctAnswer ? '正確！' : '錯誤！'}
      </p>

      <div className="space-y-3">
        <div className="text-left">
          <p className="text-gray-700">
            <strong>正確單字：</strong>{correctAnswer}
          </p>
          <p className="text-gray-600">
            <strong>定義：</strong>{definition}
          </p>
        </div>
      </div>
    </div>
  )}
</div>
```

### 5.2 例句聽力 (sentence-listening)
```jsx
<div className="bg-white rounded-xl shadow-lg p-8">
  {/* 標題區 */}
  <div className="flex justify-between items-start mb-6">
    <h2 className="text-2xl font-bold text-gray-900">例句聽力</h2>
    <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
      {difficultyLevel}
    </span>
  </div>

  {/* 指示文字 */}
  <p className="text-lg text-gray-700 mb-6 text-left">
    請聽例句並選擇正確的選項：
  </p>

  {/* 音頻播放區 */}
  <div className="text-center mb-8">
    <div className="mb-6">
      <AudioPlayer text={example} />
      <p className="text-sm text-gray-500 mt-2">
        點擊播放聽例句
      </p>
    </div>
  </div>

  {/* 選項區域 - 垂直列表布局 */}
  <div className="grid grid-cols-1 gap-3 mb-6">
    {sentenceOptions.map((sentence, idx) => (
      <button
        key={idx}
        onClick={() => !showResult && handleSentenceListeningAnswer(sentence)}
        disabled={showResult}
        className={`w-full p-4 text-left rounded-lg border-2 transition-all ${
          showResult
            ? sentence === correctSentence
              ? 'border-green-500 bg-green-50 text-green-700'
              : sentence === selectedAnswer
              ? 'border-red-500 bg-red-50 text-red-700'
              : 'border-gray-200 bg-gray-50 text-gray-500'
            : 'border-gray-200 hover:border-blue-300 hover:bg-blue-50'
        }`}
      >
        <div className="text-sm text-gray-600 mb-1">選項 {String.fromCharCode(65 + idx)}:</div>
        <div className="text-base">{sentence}</div>
      </button>
    ))}
  </div>

  {/* 結果反饋區 */}
  {showResult && (
    <div className={`p-6 rounded-lg w-full mb-6 ${
      selectedAnswer === correctSentence
        ? 'bg-green-50 border border-green-200'
        : 'bg-red-50 border border-red-200'
    }`}>
      <p className={`font-semibold text-left text-xl mb-4 ${
        selectedAnswer === correctSentence ? 'text-green-700' : 'text-red-700'
      }`}>
        {selectedAnswer === correctSentence ? '正確！' : '錯誤！'}
      </p>

      <div className="space-y-3">
        <div className="text-left">
          <p className="text-gray-700">
            {correctSentence}
          </p>
          <p className="text-gray-600">
            {translation}
          </p>
        </div>
      </div>
    </div>
  )}
</div>
```

---

## 🎤 6. 口說測驗 (sentence-speaking)

### 核心特色
- 整合 VoiceRecorder 組件
- 語音識別評估
- 即時反饋系統

### HTML結構
```jsx
<div className="bg-white rounded-xl shadow-lg p-8">
  {/* 標題區 */}
  <div className="flex justify-between items-start mb-6">
    <h2 className="text-2xl font-bold text-gray-900">例句口說</h2>
    <span className="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full">
      {difficultyLevel}
    </span>
  </div>

  {/* VoiceRecorder 組件區域 */}
  <div className="w-full">
    <VoiceRecorder
      targetText={example}
      targetTranslation={exampleTranslation}
      exampleImage={exampleImage}
      instructionText="請看例句圖片並大聲說出完整的例句："
      onRecordingComplete={() => {
        handleSpeakingAnswer(example)
      }}
    />
  </div>

  {/* 結果反饋區 */}
  {showResult && (
    <div className="mt-6 p-6 rounded-lg bg-blue-50 border border-blue-200 w-full">
      <p className="text-blue-700 text-left text-xl font-semibold mb-2">
        錄音完成！
      </p>
      <p className="text-gray-600 text-left">
        系統正在評估你的發音...
      </p>
    </div>
  )}
</div>
```

---

## 📊 進度條系統設計

### 雙層進度條
```jsx
<div className="mb-8">
  <div className="flex justify-between items-center mb-3">
    <span className="text-sm font-medium text-gray-900">學習進度</span>
    <div className="flex items-center gap-6">
      {/* 詞卡進度 */}
      <div className="flex items-center gap-2 text-sm text-gray-600">
        <span>詞卡: {completedCards}/{totalCards}</span>
      </div>
      {/* 測驗進度 */}
      <button
        onClick={() => setShowTaskListModal(true)}
        className="text-sm text-gray-600 hover:text-blue-600 transition-colors cursor-pointer flex items-center gap-1"
        title="點擊查看詳細進度"
      >
        測驗: {completedTests}/{totalTests}
        <span className="text-xs ml-1">📋</span>
      </button>
    </div>
  </div>

  {/* 詞卡進度條 */}
  <div className="flex items-center gap-2 mb-2">
    <span className="text-xs text-gray-500 w-12">詞卡</span>
    <div className="flex-1 bg-gray-200 rounded-full h-2">
      <div
        className="bg-green-500 h-2 rounded-full transition-all"
        style={{ width: `${(completedCards / totalCards) * 100}%` }}
      />
    </div>
    <span className="text-xs text-gray-500 w-8">{completedCards}/{totalCards}</span>
  </div>

  {/* 測驗進度條 */}
  <div className="flex items-center gap-2">
    <span className="text-xs text-gray-500 w-12">測驗</span>
    <div
      className="flex-1 bg-gray-200 rounded-full h-2 cursor-pointer hover:bg-gray-300 transition-colors"
      onClick={() => setShowTaskListModal(true)}
      title="點擊查看詳細進度"
    >
      <div
        className="bg-blue-500 h-2 rounded-full transition-all hover:bg-blue-600"
        style={{ width: `${(completedTests / totalTests) * 100}%` }}
      />
    </div>
    <span className="text-xs text-gray-500 w-8">{completedTests}/{totalTests}</span>
  </div>
</div>
```

---

## 🎨 通用Modal設計

### 圖片放大Modal
```jsx
<div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50" onClick={() => setModalImage(null)}>
  <div className="relative max-w-4xl max-h-[90vh] mx-4">
    <img
      src={modalImage}
      alt="放大圖片"
      className="max-w-full max-h-full rounded-lg"
    />
    <button
      onClick={() => setModalImage(null)}
      className="absolute top-4 right-4 bg-black bg-opacity-50 text-white p-2 rounded-full hover:bg-opacity-75"
    >
      ✕
    </button>
  </div>
</div>
```

### 任務清單Modal
```jsx
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
  <div className="bg-white rounded-xl shadow-2xl max-w-4xl w-full mx-4 max-h-[80vh] overflow-hidden">
    {/* Header */}
    <div className="flex items-center justify-between p-6 border-b border-gray-200">
      <h2 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
        📚 學習任務清單
      </h2>
      <button className="text-gray-400 hover:text-gray-600 text-2xl">✕</button>
    </div>

    {/* Content */}
    <div className="p-6 overflow-y-auto max-h-[60vh]">
      {/* 進度統計 */}
      <div className="mb-6 bg-blue-50 rounded-lg p-4">
        <div className="flex items-center justify-between text-sm">
          <span className="text-blue-900 font-medium">
            測驗進度: {completedTests} / {totalTests} ({progressPercentage}%)
          </span>
          <div className="flex items-center gap-4 text-blue-800">
            <span>✅ 已完成: {completedCount}</span>
            <span>⏳ 進行中: {currentCount}</span>
            <span>⚪ 待完成: {pendingCount}</span>
          </div>
        </div>
        <div className="mt-3 w-full bg-blue-200 rounded-full h-2">
          <div
            className="bg-blue-600 h-2 rounded-full transition-all"
            style={{ width: `${progressPercentage}%` }}
          />
        </div>
      </div>

      {/* 測驗清單 */}
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
        {testItems.map((item) => (
          <div
            key={item.id}
            className={`flex items-center gap-3 p-3 rounded-lg transition-all ${
              item.isCompleted
                ? 'bg-green-50 border border-green-200'
                : item.isCurrent
                ? 'bg-blue-50 border border-blue-300 shadow-sm'
                : 'bg-gray-50 border border-gray-200'
            }`}
          >
            <span className="text-lg">
              {item.isCompleted ? '✅' : item.isCurrent ? '⏳' : '⚪'}
            </span>
            <div className="flex-1">
              <div className="font-medium text-sm">
                {item.order}. {item.word} - {item.testName}
              </div>
              <div className={`text-xs ${
                item.isCompleted ? 'text-green-600' :
                item.isCurrent ? 'text-blue-600' : 'text-gray-500'
              }`}>
                {item.isCompleted ? '已完成' :
                 item.isCurrent ? '進行中' : '待完成'}
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>

    {/* Footer */}
    <div className="px-6 py-4 border-t border-gray-200 bg-gray-50">
      <button className="w-full py-2 bg-primary text-white rounded-lg hover:bg-primary-dark transition-colors">
        關閉
      </button>
    </div>
  </div>
</div>
```

---

## 🎮 導航控制設計

### 智能導航按鈕
```jsx
<div className="flex gap-4 mt-6">
  {/* 上一張按鈕 */}
  <button
    onClick={handlePrevious}
    disabled={currentCardIndex === 0}
    className="flex-1 py-3 bg-gray-500 text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-600 transition-colors font-medium"
  >
    上一張
  </button>

  {/* 動態下一張按鈕 */}
  <button
    onClick={handleNext}
    className="flex-1 py-3 bg-primary text-white rounded-lg hover:bg-primary-dark transition-colors font-medium"
  >
    {currentCardIndex < totalCards - 1 ? '下一張' : '完成學習'}
  </button>
</div>
```

### 跳過功能按鈕
```jsx
{/* 跳過按鈕（答題前狀態） */}
{!showResult && (
  <div className="flex justify-center mb-4">
    <button
      onClick={handleSkip}
      className="px-6 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600 transition-colors"
    >
      ⏭️ 暫時跳過
    </button>
  </div>
)}

{/* 繼續按鈕（答題後狀態） */}
{showResult && (
  <div className="flex justify-center mt-6">
    <button
      onClick={handleNext}
      className="px-8 py-3 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors font-medium"
    >
      繼續下一個 →
    </button>
  </div>
)}
```

---

## 🎨 設計特色總結

### 1. **一致性設計**
- 所有測驗類型都採用相同的容器結構 (`bg-white rounded-xl shadow-lg p-8`)
- 統一的配色方案和視覺層次
- 標準化的按鈕樣式和hover效果

### 2. **響應式布局**
- 使用 Grid 和 Flexbox 布局
- 適配不同螢幕尺寸 (`md:grid-cols-2 lg:grid-cols-3`)
- 移動端友好的觸控設計

### 3. **互動動畫**
- 翻卡3D效果 (`transform: rotateY(180deg)`)
- Hover狀態轉換 (`transition-all`, `hover:bg-blue-50`)
- 按鈕點擊反饋 (`active:bg-gray-300`)

### 4. **無障礙設計**
- 鍵盤導航支援 (`onKeyDown` Enter 鍵提交)
- 清晰的狀態反饋 (`disabled` 狀態顯示)
- 適當的對比度和字體大小

### 5. **視覺層次**
- 合理的間距系統 (`mb-6`, `p-4`, `gap-3`)
- 字體大小層次 (`text-4xl`, `text-2xl`, `text-lg`, `text-sm`)
- 顏色對比設計 (`text-gray-900`, `text-gray-700`, `text-gray-500`)

### 6. **即時反饋**
- 答題後的即時視覺反饋（綠色成功，紅色錯誤）
- 動態進度條更新
- 狀態圖標顯示 (`✅`, `❌`, `⏳`, `⚪`)

---

## 📝 實作注意事項

### 必需依賴組件
1. **AudioPlayer** - 音頻播放功能
2. **VoiceRecorder** - 語音錄製功能
3. **MasteryIndicator** - 熟悉度顯示
4. **ReviewTypeIndicator** - 測驗類型指示器

### 關鍵狀態管理
- `isFlipped` - 翻卡狀態
- `showResult` - 顯示答題結果
- `selectedAnswer` - 用戶選擇的答案
- `fillAnswer` - 填空題答案
- `arrangedWords` / `shuffledWords` - 重組題狀態

### 動畫效果實作
- 使用 CSS `transition-all` 實現平滑過渡
- 翻卡動畫需要 `transform-style: preserve-3d`
- 進度條動畫使用 `transition-all` 配合動態 `width` 樣式

這份文件保留了原始設計的所有精華和用戶體驗細節，可作為重新實作的完整參考。