# Scientific & Educational Visual Standards

> Quality guardrails for SVG/HTML visuals generated by the `visual-builder` and `interactive-content` skills.
> These are general principles — not rigid templates. They ensure quality and accessibility while preserving creative freedom.

## Scope

This document covers two visual delivery paths:

| Path | Technology | Canvas Delivery | Use When |
|------|-----------|----------------|----------|
| **Static visual** | SVG source → PNG conversion | Uploaded as image, embedded via `<img>` (no iframe) | Labeled diagrams, process flows, charts, infographics, illustrations, timelines |
| **Interactive visual** | Self-contained HTML/CSS/JS | Uploaded to Canvas Files, embedded via iframe | Explorable diagrams, animated simulations, slider-controlled parameters, hover/click interactions |

**Decision guide**: If the visual needs user interaction (click, drag, slide, hover to reveal), use an interactive iframe. If it's a visual explanation that students look at and read, use SVG→PNG.

**Why not inline SVG?** Canvas strips raw SVGs from page HTML as an XSS prevention measure. SVG remains the authoring format (precise, scalable, editable), but delivery is PNG — converted via `scripts/convert_svg.py` at 2x scale for retina quality.

**Before building**: Check whether a real photograph or stock image would work better. Reference `image-generation-guidelines.md` and the search-first principle. SVG/HTML visuals are best when precision, labeling, editability, or custom representation matter more than photographic realism.

## SVG Construction Principles

### Responsive Scaling
- Always use `viewBox` on the root `<svg>` element — never hardcode `width`/`height` in pixels
- Set `width="100%"` and let `viewBox` control the aspect ratio
- Example: `<svg viewBox="0 0 800 400" width="100%" role="img">`

### Text
- All text as `<text>` elements — never rasterized or baked into paths
- Font: `font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"`
- Minimum font size: `12px` (for readability at all viewport widths)

### Line Weights
| Purpose | Weight | Example |
|---------|--------|---------|
| Primary outlines, borders | `2px` | Cell membranes, major structural boundaries |
| Secondary lines, subdivisions | `1px` | Internal structures, connecting lines |
| Grid lines, tick marks, subtle guides | `0.5px` | Background grids, axis ticks |
| Leader lines (label callouts) | `1px` | Lines connecting labels to diagram elements |

### Arrows & Markers
- Define reusable arrow markers in `<defs>` block
- Reference via `marker-end="url(#arrowhead)"`
- Keep arrow markers proportional to line weight

### Grouping
- Group related elements with `<g>` and give each group an `aria-label`
- Use meaningful IDs: `id="mitochondria"` not `id="shape-42"`

## Color Usage

### Content Colors: Full Freedom
Visuals use **whatever colors best represent the subject matter**. There is no restriction to the ASU palette for diagram content:
- Biology: greens, blues, reds for cellular structures
- Chemistry: standard element colors, orbital representations
- Physics: wavelength spectra, heat maps, force vectors
- Data visualization: sequential or categorical color scales appropriate to the data

### Contrast Requirements
- All text-on-background combinations must meet **WCAG 4.5:1** contrast ratio
- No color-only encoding — always pair color with pattern, shape, or label
- Test: if the visual were grayscale, could a student still understand the key distinctions?

### ASU Palette — UI Chrome Only
If the visual has interactive controls (buttons, sliders, toggle labels, navigation elements), those UI elements use:
- Primary: Maroon `#8C1D40`
- Accent: Gold `#FFC627`
- Text on controls: White `#FFFFFF` on maroon backgrounds

The diagram content inside the visual is unrestricted.

### Suggested Categorical Palette
When you need distinct categories without domain-specific colors, this palette provides good contrast and accessibility:
- `#1565C0` (blue), `#2E7D32` (green), `#7B1FA2` (purple), `#AD1457` (pink), `#E65100` (orange)
- Maximum 5-6 categories before the palette becomes hard to distinguish
- Always supplement with labels or patterns

## Label Placement

- **Contiguity**: Place labels adjacent to what they describe (Mayer's Contiguity Principle)
- **Leader lines**: When labels can't sit next to their element, use a 1px `#595959` leader line with no arrowhead
- **No overlap**: Labels must never overlap each other or diagram elements
- **Alignment**: Left-align label groups when possible for scannability
- **Hierarchy**: Use font weight and size to distinguish primary labels from annotations

## Accessibility

### Required on Every SVG
```xml
<svg viewBox="0 0 800 400" width="100%" role="img" aria-labelledby="title desc">
  <title id="title">Short alt text — what this visual shows</title>
  <desc id="desc">Detailed screen-reader description: what the visual depicts,
    key relationships, data points, and any information conveyed visually</desc>
  <!-- visual content -->
</svg>
```

### Grouping for Screen Readers
- Use `<g aria-label="Mitochondria section">` for logical groups
- For complex diagrams, use `aria-describedby` linking to a visible text description below the figure

### Color Independence
- Never encode information with color alone
- Pair color with: shape differences, pattern fills, text labels, or position
- Example: don't just use red/green for pass/fail — add checkmark/X icons

### Interactive Visual Accessibility
- All controls keyboard navigable (Tab, Enter, Arrow keys)
- Use `aria-live="polite"` regions for dynamic value updates
- Provide text alternatives for animated content
- Respect `prefers-reduced-motion` — disable animations when set

## Scientific Accuracy

- **Label units**: Always include units on axes, measurements, and quantitative labels
- **Standard notation**: Use proper scientific notation, chemical formulas, mathematical symbols
- **Data sources**: Cite sources in the `<figcaption>` when representing real data
- **Proportionality**: If relationships are proportional, make them visually proportional
- **Scale bars**: Include when spatial relationships matter (microscopy, maps, anatomical cross-sections)
- **Simplification disclosure**: If a diagram simplifies reality, note this in the caption (e.g., "Simplified representation — not to scale")

## Canvas Embedding

### Static Visuals (SVG → PNG → Image Embed)

Pipeline:
1. Generate SVG following all construction principles above (presentation attributes, `viewBox`, accessible `<title>`/`<desc>`)
2. Convert to PNG at 2x scale: `python scripts/convert_svg.py source.svg --output visual.png --scale 2`
3. Upload PNG to Canvas Files via `upload_image()` (stored in `images/m{N}/` or `images/shared/`)
4. Embed as `<img>` in a `<figure>` with `<figcaption>`:

```html
<figure style="margin: 25px 0; text-align: center;">
  <img src="/courses/{COURSE_ID}/files/{FILE_ID}/preview"
       alt="{descriptive alt text — what the visual shows}"
       style="max-width: 100%; height: auto; border-radius: 8px;" />
  <figcaption style="font-size: 13px; color: #595959; margin-top: 8px; line-height: 1.4;">
    <strong>Figure 1:</strong> Caption explaining the visual
    <span style="display: block; font-size: 11px; color: #888; margin-top: 4px;">Source: Attribution if applicable</span>
  </figcaption>
</figure>
```

**Why PNG instead of inline SVG?** Canvas strips `<style>` blocks and raw SVGs from page HTML as an XSS prevention measure. SVG remains the authoring format for precision and editability, but delivery is PNG.

**Preserve the SVG source** alongside the PNG as an intermediate artifact. This allows iteration on the vector source and re-conversion when edits are needed.

**SVG styling note**: Use **presentation attributes** (`fill`, `stroke`, `font-size`, `font-family`, `text-anchor`, `dominant-baseline`) directly on elements. This ensures correct rendering in both browsers (for preview) and cairosvg (for conversion).

### Interactive Visuals (iframe)
Same deployment as interactive content activities:
1. Generate self-contained HTML file with embedded CSS and JavaScript
2. Upload to Canvas Files via `deploy_interactives.py` or manual upload
3. Embed via iframe: `<iframe src="/courses/{id}/files/{id}/preview" sandbox="allow-same-origin allow-scripts allow-forms">`

### File Size Guidelines
| Type | Soft Limit | Notes |
|------|-----------|-------|
| SVG source | ~50KB | Keep lean for fast conversion; simplify if larger |
| PNG (from SVG) | ~500KB | Typical: 200-500KB at 2x scale. Reduce SVG complexity or scale if larger |
| Interactive HTML | ~500KB | Optimize assets, reduce inline data |

## Interactive Visual Technical Guardrails

- **Vanilla JavaScript only** — no React, D3, Angular, or other frameworks
- **All assets inline** — no CDN dependencies, no external scripts
- **Keyboard navigable** — every interactive control reachable via Tab/Enter/Arrow
- **ARIA live regions** — announce dynamic value changes to screen readers
- **Responsive** — adapt to iframe width (Canvas renders at various viewport sizes)
- **Performance** — keep animations under 60fps, avoid layout thrashing
- **`prefers-reduced-motion`** — wrap animations in a media query check
