# Page Design Standards

> **Plugin**: ASU Canvas Course Builder
> **Version**: 0.5.0
> **Purpose**: Unified design standard for all Canvas page templates and dynamically generated content — component library, craft rules, and UX quality layer in one place.

> **Merged from**: `standards/design-taste.md` (craft rules) + UX Quality Layer adapted from ui-ux-pro-max skill. `design-taste.md` is deleted; this is the single source.

All content-generating skills reference this document when building Canvas page HTML. These are **composable components** — skills choose the right ones for each page type, not a rigid layout.

**Important**: Canvas strips `<style>` blocks, `<script>` tags, and most CSS classes. All styling MUST use inline `style=""` attributes.

**Canvas sanitizer constraints**: Canvas also strips `font-weight` from inline styles on page-body writes — every element, every value (verified live 2026-07-10 on canvas.asu.edu; pinned in `tests/integration/test_live_html_roundtrip.py::test_font_weight_stripped_from_inline_style`). Use `<strong>`/`<b>` for bold in Canvas page bodies; on default-bold elements (h1–h6, th) simply omit the declaration. Iframe-delivered interactives are exempt (served from Canvas Files, not sanitized).

---

## Heading Capitalization — APA Title Case

Headings and titles use **APA title case**. This is enforceable, not eyeballed: the deterministic linter `scripts/apa_title_case.py` checks headings and can be run on any staged HTML file or title string.

**Capitalize:**
- The **first word** and the **last word** of the title.
- The first word **after a colon or em-dash**.
- All **major words** — nouns, pronouns, verbs (including linking verbs like *is*, *are*, *be*), adjectives, adverbs.
- **Any word of 4 or more letters**, regardless of part of speech (e.g., *With*, *From*, *Into*, *Than*).

**Lowercase** (only when the word is **3 letters or fewer** AND is not the first word, the last word, or the first word after a colon-or-dash):
- Articles: *a, an, the*
- Coordinating conjunctions: *and, but, or, nor, so, yet, for*
- Short prepositions: *as, at, by, in, of, off, on, per, to, up, via, vs*

**Hyphenated compounds:** capitalize **both** parts — *Mid-Project*, *At-Risk*, *Copy-Paste*, *Peer-Rating*.

**Scope:** applies to headings (`<h2>`–`<h4>`), page / mockup / assignment / quiz / rubric titles, and UI labels, buttons, and column headers in generated interactives. It does **not** apply to body prose, and **never** to faculty-verbatim content (instruction text, faculty quiz questions, their existing pages) — that text is preserved exactly as written.

Acronyms keep their casing (R, ASU, MLO, CLO, WCAG, API).

**Correct examples:**
- "Use the Weekly Homework Standards"
- "Mid-Project Check-In (Week 5)"
- "Avg Peer Rating Received"

**Wrong → right:**
- "Group project score" → "Group Project Score" (*project*, *score* are major words)
- "What exists now" → "What Exists Now" (*exists*, *now* must be capitalized)

---

## No em or en dashes

Generated Canvas content uses no em dashes (—, `&mdash;`) or en dashes (–, `&ndash;`). Em/en dashes are an AI-tell pattern that trips preflight's `style.em_dash` warning. All generated content must be born dash-free; do not introduce them in templates or generator output.

Replace by function:
- **Headings**: colon for a label separator ("Term: description"). If the heading already contains a colon, use a comma instead to avoid double-colon.
- **Body text**: comma for a parenthetical or clause break ("The result, surprisingly, held.").
- **Numeric ranges**: "to" ("10 to 15 minutes") or a plain hyphen only in tightly-paired numerics ("10-15 min").
- **Compound modifiers**: hyphen ("low-stakes quiz", "risk-adjusted return").

**Carve-out — minus sign U+2212 (−)**: The minus sign (`−`, distinct from both the hyphen `-` and the en dash `–`) is allowed in numeric and technical contexts (`−2R`, `−3%`, temperature). Never replace it.

`preflight_checks.check_no_em_dash` warns on any em/en dash in staged page bodies. `scripts/dedash.py` and `scripts/content_remediation.fix_dashes` fix them automatically (headings to colon/comma, body to comma). Run `preflight_checks.py --check-page --fix` to auto-apply and see residual issues.

---

## Color Palette

| Token | Hex | Usage |
|---|---|---|
| Maroon (primary) | `#8C1D40` | Headings, borders, buttons, accents |
| Maroon (dark) | `#5C0F2D` | Gradient endpoints, hover states |
| Maroon (deepest) | `#2D0618` | Gradient terminus for dramatic headers |
| Gold (accent) | `#FFC627` | Highlights, alert borders, module labels, icons |
| Dark surface | `#1B1B2F` | Audio/media section backgrounds |
| Warm cream | `#FFF8E1` | Left-border alert backgrounds |
| Light warm | `#f8f4f0` | Card backgrounds, info boxes |
| Text primary | `#333333` | Body text |
| Text secondary | `#595959` | Subtitles, metadata (meets 4.5:1 on white) |
| Border light | `#e0e0e0` | Card borders, dividers |
| Video blue | `#1565C0` | Video resource cards |
| Reading purple | `#7B1FA2` | Reading resource cards |
| Reference pink | `#AD1457` | Reference resource cards |
| Success green | `#2E7D32` | Completion indicators |

---

## Components

### 1. Module Header Banner

Full-width gradient banner for Overview pages and major section headers.

```html
<div style="background: linear-gradient(135deg, #8C1D40 0%, #4A0A24 70%, #2D0618 100%); color: white; padding: 35px 30px; border-radius: 10px; margin-bottom: 25px;">
  <p style="color: #FFC627; font-size: 14px; text-transform: uppercase; letter-spacing: 1.5px; margin: 0 0 8px 0;"><strong>Module {{MODULE_NUMBER}}</strong></p>
  <h2 style="color: white; margin: 0 0 8px 0; font-size: 28px;">{{MODULE_TITLE}}</h2>
  <p style="color: #f0f0f0; margin: 0; font-size: 16px;">{{MODULE_SUBTITLE}}</p>
</div>
```

**When to use**: Overview page header. Can also be used for course-level banners (Getting Started, Final Assessment).

---

### 2. Audio Overview Section (Dark Theme)

Dark-themed container for audio primer embeds. Uses Canvas inline media embed — never links externally.

```html
<div style="background: linear-gradient(135deg, #1B1B2F 0%, #0d0d1a 100%); border-radius: 10px; padding: 30px; margin: 25px 0;">
  <div style="display: flex; align-items: flex-start; gap: 15px; margin-bottom: 15px;">
    <span style="font-size: 28px;">🎧</span>
    <div>
      <h3 style="color: white; margin: 0 0 6px 0; font-size: 20px;">Audio Overview</h3>
      <p style="color: #b0b0b0; margin: 0; font-size: 14px;">~5 minutes | Listen before the lecture to prime your thinking</p>
    </div>
  </div>
  <div style="margin: 15px 0;">
    <a id="media_comment_{{AUDIO_MEDIA_ID}}" class="instructure_inline_media_comment audio_comment">Audio Primer: Module {{MODULE_NUMBER}}</a>
  </div>
  <!-- Transcript toggle injected here by add_transcripts.py -->
</div>
```

**When to use**: Prepare to Learn pages. Audio MUST be uploaded to Canvas Files first; the `{{AUDIO_MEDIA_ID}}` is the Canvas media object ID from upload.

**Fallback** (when audio is not yet available):
```html
<div style="background: linear-gradient(135deg, #1B1B2F 0%, #0d0d1a 100%); border-radius: 10px; padding: 30px; margin: 25px 0;">
  <div style="display: flex; align-items: flex-start; gap: 15px;">
    <span style="font-size: 28px;">🎧</span>
    <div>
      <h3 style="color: white; margin: 0 0 6px 0; font-size: 20px;">Audio Overview</h3>
      <p style="color: #b0b0b0; margin: 0; font-size: 14px;">Audio primer coming soon</p>
    </div>
  </div>
</div>
```

---

### 3. Left-Border Alert (Top-of-Page Context Setter)

Warm-toned callout with gold left border. Used near the top of a page for framing, big-picture context, or key takeaways.

```html
<div style="background-color: #FFF8E1; border-left: 4px solid #FFC627; padding: 20px; border-radius: 0 8px 8px 0; margin: 20px 0;">
  <p style="color: #333; line-height: 1.6; margin: 0;">
    <strong>The big idea:</strong> {{CONTEXT_TEXT}}
  </p>
</div>
```

**Variants**:
- **Key question**: `<strong>Key question:</strong> {{QUESTION}}`
- **Why this matters**: `<strong>Why this matters:</strong> {{RELEVANCE_TEXT}}`
- **Before you begin**: `<strong>Before you begin:</strong> {{PREREQ_TEXT}}`

**When to use**: Near the top of any content page — after the header, before the main content. One per page maximum.

---

### 4. Full-Card Alert (Bottom-of-Page Wrap-Up)

Subtle bordered card for wrap-up messages, completion confirmations, or time estimates. Placed near the bottom of a page.

```html
<div style="background-color: #f9f9f9; border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px; margin: 25px 0;">
  <p style="color: #333; line-height: 1.6; margin: 0;">
    {{WRAP_UP_TEXT}}
  </p>
</div>
```

**Variants**:
- **With emoji prefix**: Add emoji before text (e.g., `⏱ Estimated time: 30 minutes`)
- **With heading**: Add `<h4 style="color: #8C1D40; margin: 0 0 8px 0;">{{HEADING}}</h4>` before paragraph
- **Success/completion** (green accent): Change border to `border: 1px solid #c8e6c9; background-color: #f1f8e9;`

**When to use**: Bottom of a page, before any navigation button. Completion checklists, time estimates, "what's next" notes.

---

### 5. Numbered Concept Items

Numbered cards with maroon circle indicators. Used for key concepts on Lesson pages.

```html
<div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px; margin-bottom: 15px; display: flex; gap: 15px; align-items: flex-start;">
  <div style="background-color: #8C1D40; color: white; width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0;"><strong>1</strong></div>
  <div style="flex: 1;">
    <h4 style="color: #8C1D40; margin: 0 0 8px 0;">{{CONCEPT_TITLE}}</h4>
    <p style="color: #333; line-height: 1.6; margin: 0;">{{CONCEPT_DESCRIPTION}}</p>
    <!-- Optional key terms callout inside the card -->
    <div style="background-color: #f8f4f0; border-radius: 6px; padding: 12px; margin-top: 12px;">
      <p style="color: #595959; font-size: 13px; margin: 0;"><strong>Key terms:</strong> {{TERMS_LIST}}</p>
    </div>
  </div>
</div>
```

**When to use**: Lesson pages for key concepts (typically 3-5 items). The inner callout (key terms, mechanism, example) is optional per item.

---

### 6. Concept Grid (2-Column Cards)

Two-column card layout for cross-references, connections, or category overviews.

```html
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0;">
  <div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px;">
    <h4 style="color: #8C1D40; margin: 0 0 8px 0;">{{CARD_1_TITLE}}</h4>
    <p style="color: #595959; line-height: 1.5; margin: 0; font-size: 14px;">{{CARD_1_TEXT}}</p>
  </div>
  <div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px;">
    <h4 style="color: #8C1D40; margin: 0 0 8px 0;">{{CARD_2_TITLE}}</h4>
    <p style="color: #595959; line-height: 1.5; margin: 0; font-size: 14px;">{{CARD_2_TEXT}}</p>
  </div>
  <div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px;">
    <h4 style="color: #8C1D40; margin: 0 0 8px 0;">{{CARD_3_TITLE}}</h4>
    <p style="color: #595959; line-height: 1.5; margin: 0; font-size: 14px;">{{CARD_3_TEXT}}</p>
  </div>
  <div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px;">
    <h4 style="color: #8C1D40; margin: 0 0 8px 0;">{{CARD_4_TITLE}}</h4>
    <p style="color: #595959; line-height: 1.5; margin: 0; font-size: 14px;">{{CARD_4_TEXT}}</p>
  </div>
</div>
```

**When to use**: Overview pages for "Where This Shows Up" sections, Conclusion pages for cross-module connections. Works with 2 or 4 cards (use `grid-template-columns: 1fr` for single-column fallback on mobile — Canvas handles this via viewport).

---

### 7. Resource Cards (Color-Coded by Type)

Individual resource cards with type-specific color coding. Replace plain tables on Lesson and Resources pages.

#### Video Resource (Blue)
```html
<div style="border: 1px solid #BBDEFB; border-radius: 8px; padding: 20px; margin-bottom: 15px; background-color: #E3F2FD;">
  <div style="display: flex; align-items: flex-start; gap: 12px;">
    <span style="font-size: 20px;">🎬</span>
    <div style="flex: 1;">
      <p style="color: #1565C0; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; margin: 0 0 4px 0;"><strong>Video</strong></p>
      <h4 style="margin: 0 0 6px 0;"><a href="{{RESOURCE_URL}}" target="_blank" rel="noopener noreferrer" style="color: #333; text-decoration: none;">{{RESOURCE_TITLE}} <span style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0;">(opens in new tab)</span></a></h4>
      <p style="color: #595959; font-size: 13px; margin: 0 0 4px 0;">{{AUTHOR_OR_SOURCE}} &middot; {{DURATION}}</p>
      <p style="color: #333; line-height: 1.5; margin: 0; font-size: 14px;">{{RESOURCE_DESCRIPTION}}</p>
    </div>
  </div>
</div>
```

#### Reading Resource (Purple)
```html
<div style="border: 1px solid #CE93D8; border-radius: 8px; padding: 20px; margin-bottom: 15px; background-color: #F3E5F5;">
  <div style="display: flex; align-items: flex-start; gap: 12px;">
    <span style="font-size: 20px;">📖</span>
    <div style="flex: 1;">
      <p style="color: #7B1FA2; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; margin: 0 0 4px 0;"><strong>Reading</strong></p>
      <h4 style="margin: 0 0 6px 0;"><a href="{{RESOURCE_URL}}" target="_blank" rel="noopener noreferrer" style="color: #333; text-decoration: none;">{{RESOURCE_TITLE}} <span style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0;">(opens in new tab)</span></a></h4>
      <p style="color: #595959; font-size: 13px; margin: 0 0 4px 0;">{{AUTHOR_OR_SOURCE}}</p>
      <p style="color: #333; line-height: 1.5; margin: 0; font-size: 14px;">{{RESOURCE_DESCRIPTION}}</p>
    </div>
  </div>
</div>
```

#### Reference Resource (Pink)
```html
<div style="border: 1px solid #F48FB1; border-radius: 8px; padding: 20px; margin-bottom: 15px; background-color: #FCE4EC;">
  <div style="display: flex; align-items: flex-start; gap: 12px;">
    <span style="font-size: 20px;">📋</span>
    <div style="flex: 1;">
      <p style="color: #AD1457; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; margin: 0 0 4px 0;"><strong>Reference</strong></p>
      <h4 style="margin: 0 0 6px 0;"><a href="{{RESOURCE_URL}}" target="_blank" rel="noopener noreferrer" style="color: #333; text-decoration: none;">{{RESOURCE_TITLE}} <span style="position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0;">(opens in new tab)</span></a></h4>
      <p style="color: #595959; font-size: 13px; margin: 0 0 4px 0;">{{SOURCE}}</p>
      <p style="color: #333; line-height: 1.5; margin: 0; font-size: 14px;">{{RESOURCE_DESCRIPTION}}</p>
    </div>
  </div>
</div>
```

**When to use**: Lesson pages (replacing the plain resource table), Resources pages, any page listing external materials.

---

### 8. CTA Button (Primary Action)

Maroon call-to-action button for primary actions (start quiz, submit assignment, open external tool).

```html
<div style="text-align: center; margin: 30px 0;">
  <a href="{{ACTION_URL}}"
     style="display: inline-block; background-color: #8C1D40; color: white; padding: 15px 40px; border-radius: 6px; text-decoration: none; font-size: 18px;">
    <strong>{{BUTTON_TEXT}} →</strong>
  </a>
</div>
```

**When to use**: One per page maximum for the primary action. Quiz launch, assignment submission, external tool links.

---

### 9. Navigation Button

"Continue to next page" navigation. Consistent across all page types.

```html
<div style="text-align: center; margin: 30px 0; padding-top: 20px; border-top: 1px solid #e0e0e0;">
  <a href="{{NEXT_PAGE_URL}}"
     style="display: inline-block; background-color: #8C1D40; color: white; padding: 12px 30px; border-radius: 6px; text-decoration: none; font-size: 16px;">
    <strong>Continue to {{NEXT_PAGE_TITLE}} →</strong>
  </a>
</div>
```

**When to use**: Bottom of every page except the last page in a module (Conclusion). Optional — Canvas sidebar navigation also works, but this gives a clear next step.

---

### 10. Left-Border Info Box (Maroon)

General-purpose info box with maroon left border. More neutral than the gold alert — for details, not framing.

```html
<div style="background-color: #f8f4f0; border-left: 4px solid #8C1D40; padding: 20px; border-radius: 0 8px 8px 0; margin-bottom: 20px;">
  <h4 style="color: #8C1D40; margin: 0 0 10px 0;">{{HEADING}}</h4>
  <p style="color: #333; line-height: 1.6; margin: 0;">{{CONTENT}}</p>
</div>
```

**When to use**: Quiz details, assignment scenarios, learning objectives. The existing maroon-bordered boxes in current templates.

---

### 11. Video Placeholder

Styled placeholder for lesson pages when lecture video is not yet available.

```html
<div style="border: 2px dashed #ccc; border-radius: 10px; padding: 40px 30px; margin: 25px 0; text-align: center; background-color: #fafafa;">
  <p style="font-size: 32px; margin: 0 0 10px 0;">📹</p>
  <h3 style="color: #8C1D40; margin: 0 0 8px 0;">Lecture Video Coming Soon</h3>
  <p style="color: #595959; margin: 0; font-size: 14px;">Module {{MODULE_NUMBER}}: {{MODULE_TITLE}} &middot; ~{{ESTIMATED_DURATION}} minutes</p>
  <p style="color: #595959; margin: 8px 0 0 0; font-size: 13px;">Covers: {{OBJECTIVES_SUMMARY}}</p>
</div>
```

**When to use**: Lesson pages when the instructor hasn't recorded the lecture yet. Used by the `instructor-guide` skill's Lecture Support Mode placeholder.

---

### 12. Interactive Activity Container

Styled wrapper for embedded HTML interactive activities (iframes). Includes instructional context above the activity.

```html
<div style="margin: 25px 0; padding: 20px; background-color: #f8f4f0; border: 1px solid #ddd; border-radius: 8px;">
  <h3 style="color: #8C1D40; margin: 0 0 5px 0;">{{ACTIVITY_TITLE}}</h3>
  <p style="color: #595959; font-size: 14px; margin: 0 0 8px 0;">{{ACTIVITY_TYPE}} | ~{{DURATION}} min | Formative (not graded)</p>
  <p style="color: #333; font-size: 15px; margin: 0 0 12px 0;">{{INSTRUCTION_TEXT}}</p>
  <iframe src="/courses/{{COURSE_ID}}/files/{{FILE_ID}}/preview"
          width="100%" height="{{HEIGHT}}"
          style="border: 1px solid #ccc; border-radius: 6px;"
          sandbox="allow-same-origin allow-scripts allow-forms"
          scrolling="no"
          title="{{ACTIVITY_TITLE}}"
          loading="lazy"></iframe>
</div>
```

**When to use**: Prepare to Learn pages (dialog cards), Guided Practice pages (all interactive types).

**`scrolling="no"`** prevents double-scroll UX — activities must fit within their allocated iframe height. Content that exceeds available space should use pagination or stepping, never scrolling.

**`{{INSTRUCTION_TEXT}}`** is a 1-2 sentence instruction explaining what the student should do. Every embedded activity requires this context. Default instructions are provided per activity type in `deploy_interactives.py`.

**`{{HEIGHT}}`** varies by activity type: Dialog Cards 550px, Fill-in-Blanks 600px, Sequencing/Quiz 650px, Branching 700px. See `IFRAME_HEIGHTS` in `deploy_interactives.py`.

---

### 13. Instructional Image (Captioned)

Full-featured image component with proper `<figure>` semantics, numbered caption, and attribution. This is the standard way to embed images in course pages.

```html
<figure style="margin: 25px 0; text-align: center;">
  <img src="/courses/{{COURSE_ID}}/files/{{FILE_ID}}/preview"
       alt="{{ALT_TEXT}}"
       style="max-width: 100%; height: auto; border-radius: 8px; display: block; margin: 0 auto;">
  <figcaption style="font-size: 13px; color: #595959; margin-top: 8px; line-height: 1.4;">
    <strong>Figure {{FIGURE_NUMBER}}:</strong> {{CAPTION_TEXT}}
    <span style="display: block; font-size: 11px; color: #888; margin-top: 4px;">{{ATTRIBUTION_LINE}}</span>
  </figcaption>
</figure>
```

**When to use**: Lesson pages (concept illustrations), Overview pages (module context images). Every instructional image MUST have a figure number, descriptive caption, and attribution line. Alt text describes what the image shows; the caption explains why it matters.

**Variants**:
- **AI-generated**: Add disclosure in attribution span: `"AI-generated illustration using [model name]"`
- **CC0/Public domain** (no attribution required): Omit the attribution span
- **Complex diagram** (needs long description): Add `aria-describedby="{{DESC_ID}}"` to the `<img>` and place a Left-Border Info Box (Component 10) below with `id="{{DESC_ID}}"` containing the full description

---

### 14. Image + Concept Card

Pairs an image with a concept explanation side-by-side. Follows Mayer's Spatial Contiguity Principle (text near related image).

```html
<div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px; margin-bottom: 15px;">
  <div style="display: flex; gap: 20px; align-items: flex-start;">
    <img src="/courses/{{COURSE_ID}}/files/{{FILE_ID}}/preview"
         alt="{{ALT_TEXT}}"
         style="width: 250px; max-width: 35%; height: auto; border-radius: 6px; flex-shrink: 0;">
    <div style="flex: 1;">
      <h4 style="color: #8C1D40; margin: 0 0 8px 0;">{{CONCEPT_TITLE}}</h4>
      <p style="color: #333; line-height: 1.6; margin: 0;">{{CONCEPT_DESCRIPTION}}</p>
    </div>
  </div>
  <p style="font-size: 11px; color: #888; margin: 8px 0 0 0;">{{ATTRIBUTION_LINE}}</p>
</div>
```

**When to use**: Lesson pages when a concept explanation benefits from a paired visual. Limit to 2-3 per page to avoid cognitive overload. Image width is capped at 35% so text remains readable on smaller screens.

---

### 15. Image Comparison (Side-by-Side)

Two-column grid for comparing visuals — before/after, normal/pathological, two approaches, etc.

```html
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 25px 0;">
  <figure style="margin: 0; text-align: center;">
    <img src="/courses/{{COURSE_ID}}/files/{{FILE_ID_1}}/preview"
         alt="{{ALT_TEXT_1}}"
         style="width: 100%; height: auto; border-radius: 8px;">
    <figcaption style="font-size: 13px; color: #595959; margin-top: 8px;">
      <strong>{{LABEL_1}}</strong>: {{CAPTION_1}}
    </figcaption>
  </figure>
  <figure style="margin: 0; text-align: center;">
    <img src="/courses/{{COURSE_ID}}/files/{{FILE_ID_2}}/preview"
         alt="{{ALT_TEXT_2}}"
         style="width: 100%; height: auto; border-radius: 8px;">
    <figcaption style="font-size: 13px; color: #595959; margin-top: 8px;">
      <strong>{{LABEL_2}}</strong>: {{CAPTION_2}}
    </figcaption>
  </figure>
</div>
```

**When to use**: Lesson pages for comparison tasks (two conditions, two time periods, two approaches). Both images must have independent alt text. Add a heading above explaining what to compare. Add attribution below the grid if needed.

---

### 16. Expandable Transcript Block

A `<details>` disclosure whose body is a fixed-height, scrollable container, so a long video or audio transcript does not stretch the page. Generated by `scripts/add_transcripts.py`, which inserts it after a media embed or fills an existing "[Transcript here]" placeholder.

```html
<details style="margin: 15px 0; border: 1px solid #ddd; border-radius: 6px; padding: 0;">
<summary style="padding: 12px 15px; background-color: #f5f5f5; cursor: pointer; color: #8C1D40; border-radius: 6px;"><strong>Lecture Video Transcript (click to expand)</strong></summary>
<div style="padding: 15px; font-size: 14px; line-height: 1.6; color: #333; max-height: 400px; overflow-y: auto;">
<p>{{PARAGRAPH_1}}</p>
<p>{{PARAGRAPH_2}}</p>
</div>
</details>
```

**When to use**: below any video or audio embed on a Lesson/Prepare to Learn page, for WCAG 1.2.1/1.2.2. Collapsed by default; the `max-height: 400px; overflow-y: auto` on the inner `<div>` keeps a long transcript scrollable in place instead of pushing the rest of the page down.

---

## Component Composition Guidelines

### Overview Page
1. Module Header Banner
2. Left-Border Alert ("Why this matters" or welcome text)
3. Left-Border Info Box (Learning Objectives)
4. Module Roadmap (table — keep existing table pattern)
5. Concept Grid (professional/clinical connections)
6. Full-Card Alert (estimated time + "complete in order" note)

### Prepare to Learn Page
1. Page heading + subtitle
2. Left-Border Alert (intro framing)
3. Audio Overview Section (Dark Theme)
4. Interactive Activity Container (dialog cards)
5. "What to Think About" questions
6. Full-Card Alert (transition note)

### Lesson Page
1. Page heading + subtitle
2. Left-Border Alert (key question or big idea)
3. Video embed (or Video Placeholder)
4. Numbered Concept Items (3-5)
5. Resource Cards (color-coded by type)
6. Full-Card Alert (review reminder)

### Knowledge Check Page
1. Page heading + subtitle
2. Left-Border Info Box (quiz details: questions, points, attempts)
3. Left-Border Alert (tips for success — gold border)
4. Topics covered list
5. CTA Button (start quiz)

### Guided Practice Page
1. Page heading + subtitle
2. Left-Border Alert (formative note)
3. Instructions
4. Interactive Activity Containers (1-2 activities)
5. "After You Practice" reflection
6. CTA Button (submit)

### Create an Artifact Page
1. Page heading + subtitle
2. Left-Border Info Box (scenario/context)
3. Task list (ordered)
4. Submission requirements
5. Rubric summary table
6. Left-Border Alert (tips for success — gold border)
7. CTA Button (submit)

### Conclusion Page
1. Page heading + subtitle
2. Left-Border Info Box (key takeaways)
3. Discussion link + CTA Button
4. Self-assessment table
5. Concept Grid (connections forward)
6. Full-Card Alert (completion checklist — green accent)

### Resources Page
1. Module Header Banner (or simple heading)
2. Resource Cards grouped by type (Video, Reading, Reference)
3. Full-Card Alert (course-specific help info)

## Tables in staged content

Canvas strips `<style>` blocks, and the staging renderer forces a default light-gray background on bare `<th>`/`<td>` cells. That default fill paints **over** any background set on the parent `<tr>`, so a "header row" styled as `<tr style="background:#8C1D40;color:#fff">` renders white text on light gray — failing contrast.

Set table-header colors as **inline styles on each `<th>` cell** (both `background` and `color`), not on the `<tr>` and not via a class or `<style>` block:

```html
<!-- Correct: portable + override-proof -->
<tr>
  <th style="background:#8C1D40;color:#fff;">Criterion</th>
  <th style="background:#8C1D40;color:#fff;">Points</th>
</tr>

<!-- Wrong: row background gets painted over by the default th fill -->
<tr style="background:#8C1D40;color:#fff;">
  <th>Criterion</th>
  <th>Points</th>
</tr>
```

Inline cell styles are the only option that survives both the staging preview and live Canvas. (The staging renderer also neutralizes row backgrounds defensively via CSS, but Canvas itself does not — so the inline-cell convention is what makes the table portable.)

---

## Design Craft Standards

> Formerly `standards/design-taste.md`. Defines how to build well, not just what to build. Apply when generating new full pages, redesigning pages, or creating custom visuals. Not required for minimal-diff edits to existing pages.

### Core Philosophy

**Every element earns its space.** Don't add a border, shadow, color, or visual element unless it communicates something — hierarchy, grouping, emphasis, or meaning. Restraint is the primary signal of quality.

**Favor typography and spacing over decoration.** A well-set heading with good whitespace reads as more "designed" than a heading with gradients, shadows, and borders. Let content breathe.

**Be specific, not generic.** AI-generated content has recognizable tells. The goal is output that looks like a skilled instructional designer built it by hand.

---

### Typography

#### Hierarchy Through Weight and Size, Not Just Scale

Don't rely on making headings enormous. Use the full toolkit:

| Signal | Technique |
|---|---|
| Primary heading | `font-size: 28px; letter-spacing: -0.5px; line-height: 1.15` (headings are bold by default; Canvas strips inline `font-weight`) |
| Secondary heading | `font-size: 20px; color: #8C1D40` (bold by default) |
| Label / eyebrow | `font-size: 12px; text-transform: uppercase; letter-spacing: 1.5px; color: #595959` + wrap the text in `<strong>` for weight |
| Body | `font-size: 16px; line-height: 1.65; color: #333; max-width: 65ch` (for readability) |
| Caption / metadata | `font-size: 13px; color: #595959; line-height: 1.4` |

#### Rules

- **Tighten display text.** Headlines above 24px should use `letter-spacing: -0.5px` or `-0.3px`. This is the single biggest difference between amateur and professional typography.
- **Loosen body text.** Body paragraphs at 16px use `line-height: 1.6` to `1.7`. Dense text is hard to scan.
- **Cap line length.** Long lines of text are exhausting. Use `max-width: 65ch` on body text containers when layout allows.
- **Use eyebrow labels.** Small uppercase labels above headings (like "MODULE 3" or "KEY CONCEPT") create visual hierarchy without making headings larger.

#### ASU Digital Font Stack (canonical declaration)

The official ASU digital font stack — use this verbatim in all Canvas content:

```css
font-family: Arial, Helvetica, 'Nimbus Sans L', 'Liberation Sans', FreeSans, sans-serif;
```

- Always lead with `Arial`. Never lead with a system-UI font (`-apple-system`, `BlinkMacSystemFont`, `Segoe UI`, `Roboto`).
- Monospace stacks for `<code>` / `<pre>` blocks are exempt: `'Courier New', Courier, monospace` is acceptable.
- `font-family: inherit` is exempt (inherits from Canvas host).
- Violations are enforced by `scripts/check_asu_brand.py` (BRAND.FONT).

#### Italics Ban

**ASU brand forbids italics entirely on digital surfaces.** This is a hard rule, not a preference.

- Never use `<i>`, `<em>`, or `font-style: italic`.
- For emphasis, use `<strong>` (bold) or quotation marks.
- Screen readers do not convey italic semantics reliably; bold is clearer for all users.
- Violations are enforced by `scripts/check_asu_brand.py` (BRAND.ITALIC).

---

### Color Discipline

#### ASU Full Palette (canonical reference)

**Primary** (use freely):

| Name | Hex | Use |
|---|---|---|
| Maroon | `#8C1D40` | Headings, borders, buttons, primary emphasis |
| Gold | `#FFC627` | Alerts, highlights, labels (sparingly) |
| Black | `#000000` | Text (use `#333333` for body — see below) |
| White | `#FFFFFF` | Backgrounds |

**Secondary** (use for semantic color-coding — status, type, category):

| Name | Hex | Semantic use |
|---|---|---|
| Green | `#78BE20` | Success, completion, correct |
| Blue | `#00A3E0` | Info, links, video |
| Orange | `#FF7F32` | Warning, caution |
| Gray | `#747474` | Muted text, metadata |
| Copper | `#AF674B` | Resource cards (warm tint) |
| Turquoise | `#4AB7C4` | Resource cards (cool tint) |
| Pink | `#E74973` | Resource cards (accent) |

**Grays** (for UI chrome, borders, backgrounds):

`#191919` `#484848` `#747474` `#BFBFBF` `#D0D0D0` `#E8E8E8` `#FAFAFA`

**Documented maroon shades** (approved darkening variants):

`#5C0F2D` `#2D0618` `#4A0A24` `#5C0A26` `#C99700`

Off-palette colors emit a `BRAND.OFF_PALETTE` warning from `scripts/check_asu_brand.py` (advisory, non-blocking per D1 decision).

#### Banned Contrast Pairs

The following color combinations are banned by the ASU brand guide regardless of WCAG contrast ratio:

| Foreground | Background | Why |
|---|---|---|
| Gold `#FFC627` | White `#FFFFFF` | Low perceived contrast; illegible on screens |
| White `#FFFFFF` | Gold `#FFC627` | Same pair, reversed |
| Gold `#FFC627` | Near-white `#FAFAFA` | Same problem |
| Maroon `#8C1D40` | Black `#000000` | Colors merge; unreadable |
| Black `#000000` | Maroon `#8C1D40` | Same pair, reversed |

Violations are enforced by `scripts/check_asu_brand.py` (BRAND.CONTRAST_PAIR) as blocking failures.

#### One Accent Per Context

Within the palette, apply restraint:

- **Primary accent**: Maroon (`#8C1D40`) — headings, borders, buttons, emphasis
- **Secondary accent**: Gold (`#FFC627`) — alerts, highlights, labels (sparingly)
- **Never combine** multiple bright accent colors in the same visual zone. A card with a maroon heading, gold border, AND blue icon is visual noise.

#### Banned Patterns

- **Pure black** (`#000000`) for text. Use `#333333` (body) or `#1a1a1a` (headings). Pure black on white has too much contrast and looks harsh.
- **Oversaturated backgrounds.** If a background is colored, keep it light (pastel tint). Dark backgrounds only for intentional media sections (dark audio container, gradient banners).
- **Color for decoration.** Every color should signal something — type (video=blue, reading=purple), hierarchy (maroon=primary), or state (green=success). Don't add color just to "make it pop."

---

### Layout

#### Break the Center Bias

Not every section needs to be centered. Use asymmetric layouts for variety:

- **Left-aligned content with right whitespace** — heading and body text left-aligned, leaving the right side open
- **Split layouts** — `display: flex; gap: 30px` with a 60/40 or 65/35 split (image + text, concept + illustration)
- **Offset grids** — a 2-column grid where one column holds a single tall element and the other holds 2-3 stacked items

#### Avoid the 3-Equal-Columns Trap

Three identical cards in a row is the most recognizable AI layout pattern. Instead:

- Use **2 columns** with varied card sizes
- Use a **list with left-border accents** instead of cards when items are sequential
- Use **numbered items** (Component 5) for ordered concepts
- If you need 3+ items, consider a **stacked list** with generous spacing rather than a grid

#### Spacing Rhythm

Consistent spacing is more important than any visual embellishment:

| Element | Spacing |
|---|---|
| Between major sections | `margin: 35px 0` or `margin: 40px 0` |
| Between related items | `margin-bottom: 15px` |
| Inside containers | `padding: 20px` to `padding: 30px` |
| Between heading and content | `margin: 0 0 8px 0` (tight coupling) |
| Between paragraphs | `margin: 0 0 16px 0` |

**Never use inconsistent spacing** between similar elements. If three cards each have different margin-bottom values, it looks broken.

---

### Component Craft

#### Cards and Containers

- **Shadows**: If you use shadows, tint them warm: `box-shadow: 0 2px 12px rgba(140, 29, 64, 0.06)` (maroon-tinted). Never pure black shadows (`rgba(0,0,0,0.2)`).
- **Border radius**: Stay consistent — `border-radius: 8px` for cards, `border-radius: 6px` for inner elements, `border-radius: 10px` for large containers (banners). Don't mix `4px`, `8px`, `12px`, and `20px` on the same page.
- **Borders**: Prefer `border: 1px solid #e0e0e0` (light) or `border-left: 4px solid #8C1D40` (accent). Avoid thick borders on all four sides — it looks boxy.
- **Don't overuse cards.** When items are logically sequential, a list with `border-top: 1px solid #e8e8e8` dividers is cleaner than wrapping every item in a bordered card.

#### Empty Space Is a Design Element

- Don't fill every pixel. A page with 5 well-spaced components reads better than 10 crammed components.
- Between major sections, use `padding-top: 20px; border-top: 1px solid #e0e0e0` as a section divider — it's lighter than stacking cards.

---

### Content Quality (Anti-AI Tells)

> For the full banned-phrase and banned-structure list (throat-clearing openers, emphasis crutches, business jargon, filler phrases, binary contrasts, and the instructional carve-out), see `standards/anti-slop.md`.

#### Text

- **No AI copywriting clichés**: "Elevate," "Unleash," "Seamless," "Next-Gen," "Revolutionize," "Dive into," "Journey through," "Embark on," "Explore the world of"
- **No filler instructions**: "Scroll down to learn more," "Click below to explore," "Ready? Let's begin!" These add no value.
- **Be direct**: "This module covers X, Y, and Z" beats "In this exciting module, you'll embark on a journey through X, Y, and Z."
- **Use the instructor's voice**: Match the formality level of the course. A graduate seminar reads differently than an intro survey course.

#### Data and Examples

- **No placeholder names**: "John Doe," "Jane Smith," "Acme Corp." If examples need names, use realistic, diverse names. If a name isn't needed, don't invent one.
- **No round numbers**: `47.3%` is more believable than `50%`. `$1,847` is more believable than `$2,000`. Use realistic precision in examples.

#### Images and Alt Text

- **Alt text describes, captions explain.** Alt text: "Bar chart showing declining coral reef coverage from 1990 to 2020." Caption: "Figure 3: Coral reef coverage has decreased by 50% in three decades, driven primarily by ocean acidification."
- **Don't say "image of"** in alt text. Screen readers already announce it as an image.

---

### Canvas-Specific Constraints

These technical constraints shape everything above:

1. **All styling is inline** — Canvas strips `<style>` blocks. Every rule goes in `style=""` attributes.
2. **No JavaScript** — Canvas strips `<script>` tags. No interactivity in page HTML (use iframed interactives instead).
3. **No custom fonts** — Canvas uses the system font stack. Don't set `font-family` unless needed for monospace code blocks.
4. **No external images by URL in components** — Images must be uploaded to Canvas Files and referenced as `/courses/{courseId}/files/{fileId}/preview`.
5. **No CSS Grid in older Canvas views** — Some Canvas mobile views don't support grid well. For critical layouts, `display: flex` with `flex-wrap: wrap` is safer. Use grid for desktop-primary content (comparison grids, concept grids).
6. **Semantic HTML matters** — Use `<figure>`, `<figcaption>`, `<blockquote>`, `<details>`, `<summary>` where appropriate. Canvas preserves these elements and they improve accessibility.
7. **No inline `font-weight`** — Canvas strips `font-weight` from inline styles (verified live 2026-07-10; pinned in the live integration suite). Use `<strong>`/`<b>` for bold; omit the declaration on default-bold elements (h1–h6, th). Iframe-delivered interactives are exempt.

---

### Quality Checklist (Before Outputting Generated HTML)

Run through this mentally before sending any generated content:

- [ ] **Typography**: Headings use tight letter-spacing? Body text has good line-height? Hierarchy is clear without oversizing?
- [ ] **Heading case**: Headings use Title Case, not sentence case (see §Heading Capitalization above). Acronyms (R, ASU, MLO) keep their casing.
- [ ] **No em/en dashes**: no —, –, `&mdash;`, or `&ndash;`; use colon / comma / "to" / hyphen instead (see §No em or en dashes above).
- [ ] **Color**: One accent color dominant? No pure black text? Colored backgrounds are light/pastel?
- [ ] **Layout**: Not every section centered? At least one asymmetric element? No 3-equal-column grids?
- [ ] **Spacing**: Consistent margins between similar elements? Generous padding inside containers? Breathing room between sections?
- [ ] **Borders/Shadows**: Consistent border-radius? Shadows are subtle and tinted? No thick borders on all four sides?
- [ ] **Content**: No AI clichés? No filler phrases? Direct, clear language?
- [ ] **Accessibility**: Alt text on images? Sufficient color contrast (4.5:1)? Semantic HTML elements used?
- [ ] **Specificity**: Examples use realistic data? Names aren't generic placeholders?

---

## UX Quality Layer

> Adapted from the ui-ux-pro-max skill (v2.5.0, MIT, nextlevelbuilder); full skill vendored at `docs/vendor/ui-ux-pro-max-skill.zip`. ASU-relevant rules only — the skill's 67 styles, 161 palettes, 57 font pairings, and framework stacks are omitted (ASU brand is fixed).

Apply when generating design mockups or new full Canvas pages. Not required for small assignment/quiz description edits.

### Accessibility

- **Contrast**: Normal text (below 18px regular or 14px bold) requires 4.5:1 contrast ratio against its background. Large text (18px+ regular or 14px+ bold) requires 3:1.
- **Focus rings**: Interactive elements (links, buttons, form controls) must have a visible focus indicator. Never suppress `outline` without an equivalent replacement.
- **Alt text**: Every `<img>` needs descriptive alt text. Decorative images use `alt=""`. For complex diagrams, use `aria-describedby` pointing to a nearby long description. Defer to the image alt-text auditor (`scripts/audit_image_alt.py`) for vision-assisted evaluation.
- **Heading hierarchy**: Use `<h1>` through `<h4>` in sequential order; never skip levels (e.g., `<h2>` directly to `<h4>`). Canvas body content typically starts at `<h2>`.
- **Color alone**: Never convey meaning by color alone. Always pair color with text, icon, or pattern (e.g., an error state needs "Error:" text, not just a red border).
- **Link text**: Descriptive link text only. "Click here" and "Learn more" are failures — name the destination or action ("Download the Module 3 rubric").

### Typography

- **Font stack**: Follow the ASU digital font stack (see §ASU Digital Font Stack above). Body text: 14–16px, line-height 1.5–1.75.
- **Type scale**: Use a consistent scale — 12 / 14 / 16 / 18 / 20 / 24 / 28 / 32px. Don't invent intermediate sizes.
- **Weight hierarchy**: Headings are bold by default; body stays at the default weight. For bold labels and UI elements, wrap the text in `<strong>`/`<b>` — Canvas strips inline `font-weight` (see §Canvas-Specific Constraints).
- **Line length**: Target 65–75 characters per line for body text (`max-width: 65ch` to `75ch`).

### Spacing

- **8pt rhythm**: All margins and padding use multiples of 8px (8, 16, 24, 32, 40, 48). Exceptions need a design reason; don't use 10px, 15px, 22px, 35px arbitrarily.

### Layout

- **Hierarchy first**: Establish visual hierarchy through size, spacing, and contrast — not color alone.
- **Container max-width**: Constrain content containers to a consistent max-width (typically `max-width: 900px` or the Canvas column width). Don't let text expand to full viewport width.
- **Core content first**: Place the most important content at the top of the page. Don't bury objectives or key context below decorative elements.

### Interaction

- **Touch targets**: Clickable elements (links styled as buttons, CTAs, nav items) must be at least 44x44px in their hit area. Inline text links in prose are exempt.
- **One primary CTA per page**: One dominant call-to-action per page. Secondary actions are visually subordinate (smaller, lower contrast, or outlined rather than filled).
- **State distinction**: Hover, active, and disabled states must be visually distinct. Don't style a disabled button identically to an active one.

### Animation (if any)

- **Duration**: 150–300ms for UI transitions. Below 150ms feels instant (fine for micro-interactions); above 300ms feels slow.
- **Properties**: Animate `transform` and `opacity` only. Avoid animating `width`, `height`, `top`, `left` — these trigger layout recalculation.
- **Reduced motion**: Any animation must respect `prefers-reduced-motion: reduce`. Wrap animations in `@media (prefers-reduced-motion: no-preference)`.

### Media

- **Aspect ratio reservation**: Reserve space for embedded video with a fixed aspect ratio (`aspect-ratio: 16/9` or a padding-top percentage) to prevent layout shift on load.
- **Lazy loading**: Images below the fold use `loading="lazy"`. Above-the-fold hero images do NOT use lazy loading.
- **No emoji as icons**: Use inline SVG for functional icons, not emoji. Emoji render inconsistently across platforms and are announced verbosely by screen readers. (Decorative emoji in prose are acceptable — this rule targets icon use.)

### Forms and Feedback (if any)

- **Visible labels**: Every input field has a visible `<label>`. Never rely on placeholder text alone as the label — placeholder disappears on focus and is not reliably announced.
- **Error placement**: Error messages appear adjacent to the field that caused them, not only at the top of the form.

---

## Conflict Resolution and Scope

### ASU Brand Is the Fixed Baseline

ASU brand is the non-negotiable foundation:
- **Structural color**: Maroon `#8C1D40` for headings, borders, structural elements.
- **Interactive color**: Gold `#FFC627` for interactive elements only (buttons, CTAs).
- **Near-black headings**: `#191919` for the darkest heading variant.
- **Reference look**: The `Mockup: Homework HTML Design` page is the canonical visual reference for the system's aesthetic.

### UX Quality Layer Operates Within the Brand

The UX Quality Layer (above) elevates execution within ASU brand constraints. It never overrides ASU color, logo, or font choices.

**When a UX Quality Layer rule and an established ASU-specific convention conflict** (for example, if the UX layer suggests 16px body text and the established ASU convention is 14px for a specific context): **ASU wins.** Flag the conflict explicitly in a comment or response — do not silently change the brand convention to satisfy the general rule.

### Scope

Apply this full document (components + craft + UX Quality Layer) when:
- Generating new full Canvas pages from scratch
- Redesigning or polishing existing pages at user request
- Building course mockups

Do NOT apply the craft/UX sections to:
- Small assignment, quiz, or discussion description edits (minimal-diff only)
- Single-field metadata changes
- Content updates that don't touch layout or visual structure

### Provenance

UX Quality Layer adapted from the ui-ux-pro-max skill (v2.5.0, MIT, nextlevelbuilder); full skill vendored at `docs/vendor/ui-ux-pro-max-skill.zip`.
