Evalium Rich Content: MVP → Perfect Plan
A polished, implementation-ready roadmap for rich text, tables, and media across Admin authoring and Candidate delivery.
1) strategy Summary (What We’ll Do)
- Make Evalium’s Block JSON the canonical content model (with
schema_version). - Use TipTap as the editor runtime, behind strict mappers (TipTap ⇄ Evalium Block JSON).
- Render via safe Svelte components (no
\{@html}for user content) with a strict CSP. - Build a robust paste pipeline (Excel/Docs/Sheets → blocks) with table intelligence and guardrails.
- Add accessibility gates, search indexes, publish/submission snapshots, and a governed media pipeline.
2) Why These Choices (Fit to Product, Tech, and Market)
- Product: SMB admins live in spreadsheets and word processors; they must paste and go. Candidates need clean, fast, consistent delivery.
- Tech: Versioned schemas + JSONB + SSR-safe rendering meets performance and security requirements.
- Market: Differentiators that matter to SMBs: paste-to-import wizard, content QA panel, branded exports.
3) Key Alternatives & Trade-offs
| Decision | Option A (Recommended) | Option B | Trade-off |
|---|---|---|---|
| Canonical schema | Evalium Block JSON | TipTap/ProseMirror JSON | A = independence & stability; B = faster day-1 but tighter coupling. |
| Tables scope (MVP) | Constrained (no nested tables, limited spans) | Full ProseMirror tables now | A = predictable perf/a11y; B = complexity & risk. |
| Media delivery | DIY (S3/R2 + CDN) | Managed (e.g., Cloudflare Stream) | A = lower cost, more control; B = faster to operate. |
| Collaboration | Optimistic locking (Phase 2) | CRDT (Y.js) (Phase 3) | A = simpler; B = real-time but heavier. |
4) Phased Roadmap (Detailed How + Why)
Phase 1 — MVP Foundations (Ship the Essentials, Safely)
Scope
- Rich text (paragraphs, headings, lists, links, inline code).
- Basic tables (no nesting; limited
rowspan/colspan). - Images (upload → asset ID → block).
- Admin Live Preview identical to candidate rendering.
Implementation
- Schema:
schema_version: 1, blocks + inline spans/marks; allow:paragraph,heading,list,table,image. - Renderer: Single SSR-safe component tree → semantic HTML; never
\{@html}for user content. - Editor: TipTap loaded client-only (
onMount+ dynamicimport()), mappers TipTap ⇄ Evalium with round-trip tests. - Paste v1: Intercept paste; detect tables/TSV; normalize (strip styles, resolve spans), cap size (e.g., 30×15), convert to blocks; otherwise fallback to TipTap defaults.
- Media v1 (images): Pre-signed upload; persist asset ID (not URL); store MIME, width/height, hash.
- Search readiness:
content_text+content_tsvectorwith trigger to stay in sync. - A11y basics: Require
alton images; tablecaption+ header association; heading level continuity. - Security: Strict CSP (
script-src 'self'; scopedimg-src/media-src); server-side validation of block JSON.
Acceptance Criteria
- No user path renders
\{@html}; CSP is active and tested. - Paste from Excel/Docs/Sheets produces clean blocks; oversized pastes are capped with user feedback.
- Live Preview matches candidate view for supported features.
- Image uploads create asset records; blocks reference asset IDs.
- Library search is fast (indexed
tsvector).
Phase 1.5 — Immediate SMB Wins
1) Paste Spreadsheet → Users/Groups/Items Wizard
- Modal mapping: columns → Name/Email/Group/etc.; inline validation; downloadable error CSV.
2) Content QA Panel
- Pre-publish checks for alt/captions, heading gaps, broken links, media weight, table size.
3) Branding Carry-Through
- Apply tenant branding to preview, candidate player, and exports for cohesion.
Acceptance Criteria
- Wizard imports typical HR spreadsheets with clear error feedback.
- QA panel blocks publish on critical issues; warnings are actionable.
- Branded previews/exports match tenant settings.
Phase 2 — Professionalization & Scale
Tables: A11y + Mobile Completeness
- Responsive fallback (stacked rows; row-header badges), sticky headers, full keyboard traversal.
- Allow nested lists inside cells (no nested tables).
Media Pipeline (Video/Audio)
- Asset registry with quotas & usage dashboards; EXIF strip, MIME/antivirus scan.
- Transcode worker → HLS/DASH; captions/transcript required.
- Orphan cleanup; plan-based size/time caps.
Snapshots & Versioning
- On publish: mint immutable
(evaluation_id, version, blocks, asset_versions). - On submission: store pointer to content version; optional rendered HTML snapshot.
Exports
- Unified pipeline: blocks → Sanitized HTML / Markdown / Plaintext / PDF.
- CSV neutralizes
= + - @prefixes (formula injection).
Performance & Ops
- Editor bundles lazy-loaded; SSR read-only renders.
- Observability: paste metrics (source, size, parse time/errors), CSP reports.
Acceptance Criteria
- Tables are usable and compliant on mobile and with keyboards.
- Video/audio stream reliably; captions/transcripts enforced.
- Every submission ties to an immutable content version.
- Exports are consistent and safe; CSV is injection-safe.
Phase 3 — Collaboration, Localization, Enterprise Unlocks
- Localization & RTL: Per-locale variants under one content ID; status badges; RTL rendering toggle; font fallbacks.
- Personalization Tokens:
\{\{candidate_name\}\},\{\{evaluation_title\}\}allowed inside text spans only; server-side escaped substitution. - Lifecycle & Approvals: Draft → Review → Published; JSON diff viewer; inline reviewer comments.
- Optional Real-Time Collaboration: Upgrade from optimistic locking to Y.js (TipTap collab) if needed.
- Analytics Hooks: Lightweight content telemetry (e.g., video drop-off) wired to item analysis.
Acceptance Criteria
- Locale variants render correctly (including RTL) with progress indicators.
- Tokens render safely in delivery and exports.
- Review flow and diffs reduce accidental regressions.
Phase 4 — “Perfect” (Market Leadership)
- Content Snippets & Design Tokens: Reusable snippets (instructions, rubric notes) with centralized updates; enforce WCAG contrast via tokens (no ad-hoc inline colors).
- QTI 3.0 Export Adapter: Deterministic mapping for enterprise tenders.
- Policy-Driven Publishing: Per-tenant rules (max media size, allowed domains, captions mandatory) validated in preflight.
- Assisted authoring: Deterministic a11y/clarity suggestions (“add alt text”, “tighten heading structure”); AI optional.
Acceptance Criteria
- Snippets update globally; contrast always passes WCAG.
- QTI packages validate in target LMS/LTI contexts.
- Policy violations block publish with clear remediation steps.
5) Engineering Specs (Copy-Ready)
Data Model (Outline)
question_content(id,question_id,schema_version,blocks JSONB,content_text,content_tsvector,created_at,updated_at)assets(id,org_id,type,bytes,width,height,duration,hash,created_at,meta)evaluation_versions(id,evaluation_id,version,blocks JSONB,asset_versions JSONB,created_at,created_by)submissionsadd:content_version_id
API Contracts (Outline)
POST /content/validate→ schema & business-rule validation.POST /assets→ pre-signed upload;POST /assets/complete→ finalize (collect MIME/w×h/duration/hash).POST /evaluations/\{id\}/publish→ create version; returnsversion_id.GET /evaluations/\{id\}/versions/\{version_id\}→ immutable snapshot for audits.POST /import/spreadsheet→ users/groups/items with mapping and error CSV.
Renderer & Security
- Single RichRenderer; SSR for read views; strict no
\{@html}for user content. - CSP: lock
script-src 'self'; restrictimg-src/media-srcto CDN; disallow inline/event handlers. - Server-side validation of blocks; deny unknown marks/attrs; strip external media hosts by default.
Paste Pipeline (v1 → v2)
- Detect
text/html(table) ortext/tab-separated-values. - DOMParser → normalize (remove styles, resolve spans, collapse whitespace).
- Enforce caps (cells, depth, total text) with friendly truncation message.
- Convert → Evalium blocks; log metrics (source, size, duration, errors).
Accessibility Gates (Blocking)
- Images: non-empty
alt. - Video/Audio: captions or transcript.
- Tables:
caption, header cells withscope; responsive fallback enabled. - Headings: no level jumps.
- Contrast: enforced via theme tokens (no inline colors).
Performance & Observability
- Lazy-load editor bundles; SSR only for read.
- Budgets: Renderer TTI; paste parse < 200 ms for ≤ 30×15 tables (warn beyond).
- Metrics: paste size/source/time; CSP report-only → enforce.
6) Required Decisions (ADRs to Record)
- Canonical Schema: Evalium Block JSON + versioning.
- Tables Scope (MVP): No nested tables; limited spans; caps on cells.
- Media strategy: DIY S3/R2+CDN vs Managed Streaming; asset IDs in blocks.
- Collaboration Approach: Optimistic locking now; revisit Y.js later.
7) Test Matrix & Definition of Done (DoD)
Functional
- Round-trip TipTap ⇄ Evalium mapping (golden tests).
- Paste fuzz tests (random tables/marks).
- QA gate blocks on critical a11y failures.
Security
- No
\{@html}used in user content paths. - CSP violations monitored; zero high-severity findings.
- CSV export neutralizes formula injection.
Performance
- Renderer SSR TTFB/TTI within budget.
- Paste within budget for typical SMB content.
Data
-
content_text/tsvectormaintained by trigger; indexed queries stay fast. - Snapshots referenced by every submission.
8) What You Get
- A safe, fast MVP that keeps authors productive and candidates focused.
- A scalable path (a11y, media ops, snapshots, search) without rework.
- SMB-visible wins: paste-to-import wizard, content QA panel, consistent branding.