bendoidic

TAP Prototype — Reusable Process Skills

- Close button: Figma `4744:143502` — 4px padding, 4px border-radius, gray-02 icon, hover: gray-06 bg, pressed: gray-05 bg

bendoidic 0 Updated 1mo ago

Resources

15
GitHub

Install

npx skillscat add bendoidic/lego-blocks-interviewer

Install via the SkillsCat registry.

SKILL.md

TAP Prototype — Reusable Process Skills

Status note (2026-06-03): This file documents the single-file HTML prototype workflow used across TAP prototypes. The Lego Block Interviewer is migrating to a Next.js + Supabase + Vercel app (see CLAUDE.md), so the single-file/python -m http.server/activity-page sections below are legacy for this repo. What stays relevant: the font setup, icon tinting with CSS filters, local-assets-first rule, and the Figma capture workflow — these carry over to the React app. Brand tokens live in TAP-BRAND.md.

This file contains process knowledge that applies to any TAP prototype project.
Project-specific details (filenames, Figma URLs, activity IDs) live in each project's own README.md.
Brand tokens and component styles live in TAP-BRAND.md.


Stack

Plain HTML + CSS — single-file prototypes, no framework, no build step.

  • One .html file per prototype variant (e.g. prototype-v2.html, prototype-v3.html)
  • All CSS lives in a <style> block in <head>
  • All JS lives in a <script> block before </body>
  • No npm, no bundler, no dependencies beyond Google Fonts and the Figma capture script

Font Setup

Circular Std — TAP brand font (paid, not on Google Fonts). OTF files live in the font/ folder next to the prototype file.

/* In <head> — always include both */
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,700&display=swap" rel="stylesheet">

/* In <style> — @font-face declarations */
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-Book.otf') format('opentype'); font-weight: 400; font-style: normal; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-BookItalic.otf') format('opentype'); font-weight: 400; font-style: italic; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-Medium.otf') format('opentype'); font-weight: 500; font-style: normal; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-MediumItalic.otf') format('opentype'); font-weight: 500; font-style: italic; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-Bold.otf') format('opentype'); font-weight: 700; font-style: normal; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-BoldItalic.otf') format('opentype'); font-weight: 700; font-style: italic; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-Light.otf') format('opentype'); font-weight: 300; font-style: normal; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-LightItalic.otf') format('opentype'); font-weight: 300; font-style: italic; }
@font-face { font-family: 'Circular Std'; src: url('font/CircularStd-Black.otf') format('opentype'); font-weight: 900; font-style: normal; }

/* CSS variable */
--font: 'Circular Std', 'DM Sans', sans-serif;

Weights: 300 Light · 400 Book · 500 Medium · 700 Bold · 900 Black (+ italic variants where available).
DM Sans is the active fallback when Circular Std files are absent.


Figma ↔ Prototype Workflow

Prototype → Figma (push capture)

CRITICAL — how the toolbar works:
The Figma capture toolbar does NOT appear just by loading the page. It only appears when the page is opened with a special hash URL containing a live captureId. The capture.js script tag in the HTML is required but passive — it waits for the hash params to activate it.
Every new session requires a fresh captureId. Always follow all steps below.

Authentication: The figma-remote-mcp server requires Figma OAuth. If claude mcp list shows "Needs authentication", the user must re-authenticate via the /plugin command in Claude Code (not claude mcp auth — that command doesn't exist). This may need to be done after each Claude Code restart.

When to run this

Run the full steps below whenever the user asks to:

  • "spin up the prototype"
  • "connect to Figma"
  • "send to Figma"
  • start a new session where Figma capture is expected

Step 0 — Verify capture script is in the HTML

Every prototype file must have this line in <head>:

<script src="https://mcp.figma.com/mcp/html-to-design/capture.js" async></script>

Step 1 — Start the dev server

Check if already running:

lsof -i :8080

If not running:

python3 -m http.server 8080

Run from the project root directory.

Step 2 — Generate a captureId

Call the MCP tool with the project's target Figma file and node (from the project's README.md):

mcp__figma-remote-mcp__generate_figma_design
  outputMode: "existingFile"
  fileKey:    "<fileKey from README>"
  nodeId:     "<nodeId from README>"

This returns a captureId UUID. Each captureId is single-use.

Step 3 — Open the page with the hash URL

On macOS:

open "http://localhost:8080/<prototype-filename>.html#figmacapture=<captureId>&figmaendpoint=https%3A%2F%2Fmcp.figma.com%2Fmcp%2Fcapture%2F<captureId>%2Fsubmit&figmadelay=1000"

Replace both <captureId> instances and <prototype-filename> with the actual values.

Step 4 — Poll until completed

Call the tool every 5 seconds with captureId only, up to 10 times:

mcp__figma-remote-mcp__generate_figma_design
  captureId: "<captureId>"

Stop when status is completed. The response includes the Figma node URL where the frame landed. Update the project README.md with the new node ID.

Step 5 — Toolbar persists for re-capture

After the first capture completes, the toolbar stays in the browser tab. The user can re-capture at any time without Claude — the toolbar auto-generates new captureIds.

For component-level capture: use the crosshair/selector icon in the toolbar to pick a specific element before submitting.


Figma → Prototype (pull design changes)

  1. User right-clicks a component in Figma → "Copy link to selection"
  2. User shares the URL: "Update the prototype to match this Figma node: [URL]"
  3. Extract fileKey and nodeId from the URL (convert - to : in nodeId)
  4. Call mcp__figma-remote-mcp__get_design_context with fileKey and nodeId
  5. Translate the returned React+Tailwind reference code into plain HTML+CSS matching the project's token system
  6. Apply changes to the active prototype file

Icons & Logos — Local Assets First

CRITICAL: Always prefer local assets over inline SVGs or remote Figma asset URLs.

When rendering any icon or logo in the prototype, follow this priority order:

  1. Check assets/icons/ and assets/logo/ first — look for a matching local SVG file before writing inline SVGs or using Figma-hosted asset URLs
  2. Use <img> tags referencing local paths (e.g. <img src="assets/icons/start.svg" />)
  3. Fall back to inline SVG only when no suitable local asset exists

Available local assets

Folder Contents
assets/icons/ UI icons: dashboard.svg, course.svg, session-viewer.svg, assessment.svg, tutor.svg, support.svg, start.svg, complete.svg, success.svg, open-in-new.svg, lock.svg, locked.svg, unlocked.svg, chevron-left.svg, chevron-right.svg, arrow-down.svg, close.svg, info.svg, warning.svg, search.svg, and many more
assets/logo/ TAP logos: tap-logo.svg, tap-logo-with-text.svg, and individual logo parts

Color tinting with CSS filters

Local SVGs loaded via <img> tags cannot inherit color or currentColor. Use CSS filter to tint them:

/* Gray-02 (#455764) — default icon color */
filter: brightness(0) saturate(100%) invert(31%) sepia(12%) saturate(1122%) hue-rotate(163deg) brightness(95%) contrast(88%);

/* Primary-02 (#0069e4) — blue active/hover state */
filter: brightness(0) saturate(100%) invert(28%) sepia(98%) saturate(1825%) hue-rotate(203deg) brightness(97%) contrast(101%);

/* White — for icons on filled blue buttons */
filter: brightness(0) invert(1);

When translating Figma designs

When get_design_context returns Figma-hosted asset URLs (https://www.figma.com/api/mcp/asset/...), do not use them directly in the prototype. Instead:

  1. Identify what the icon represents from the Figma code context (node name, alt text, surrounding labels)
  2. Search assets/icons/ and assets/logo/ for a matching local file
  3. Use the local file with an appropriate CSS filter for color
  4. Only use inline SVG as a last resort if no local asset matches

Button Disabled States — Figma Reference

All six button variants (Filled, Gray, White, Tinted, Red, Dark Grey) have defined disabled states in Figma node 18:177. Key patterns:

Variant Disabled background Disabled text Figma node
Filled primary-04 (#88B9F2) gray-06 (#F5F5F5) 18:190
Gray gray-06 (#F5F5F5) gray-04 (#A8B1B7) 18:241
Dark Grey 40% white overlay on gray-04 gray-06 (#F5F5F5) 3472:101981
Red 40% white overlay on red gray-06 (#F5F5F5) 2671:24615

Dark Grey & Red disabled use a gradient overlay pattern:

background: linear-gradient(90deg, rgba(255,255,255,0.4) 0%, rgba(255,255,255,0.4) 100%), <base-color>;

Always add both :disabled and [disabled] selectors, plus cursor: not-allowed.


CSS Filter Tints for Icon Colors

When using <img> tags for SVG icons, currentColor doesn't work. Use CSS filter to tint icons.

Common filters used in TAP prototypes:

Target color Hex CSS filter
Gray-02 (default) #455764 brightness(0) saturate(100%) invert(31%) sepia(12%) saturate(1122%) hue-rotate(163deg) brightness(95%) contrast(88%)
Gray-04 (disabled) #A8B1B7 brightness(0) saturate(100%) invert(75%) sepia(5%) saturate(531%) hue-rotate(163deg) brightness(93%) contrast(87%)
Primary-02 (blue) #0069E4 brightness(0) saturate(100%) invert(28%) sepia(98%) saturate(1825%) hue-rotate(203deg) brightness(97%) contrast(101%)
Green (success) #34C759 brightness(0) saturate(100%) invert(48%) sepia(42%) saturate(1600%) hue-rotate(115deg) brightness(95%) contrast(88%)
White #FFFFFF brightness(0) invert(1)

To generate a new filter for a hex color, use https://codepen.io/sosuke/pen/Pjoqqp or manually calculate.


Activity Page Creation Pattern

When creating a new activity page, follow this standard structure:

1. File setup

  • Single HTML file, same stack as course overviews (no framework, no build step)
  • Same font-face declarations and CSS variables as other activity pages
  • Include Figma capture script in <head>

2. URL params + localStorage integration

Every activity page must read actId and storageKey from URL params and write completion to localStorage:

const _params = new URLSearchParams(window.location.search);
const _actId = _params.get('actId');
const _storageKey = _params.get('storageKey');

function markCompleteAndGoBack() {
  if (_actId && _storageKey) {
    const completed = new Set(JSON.parse(localStorage.getItem(_storageKey) || '[]'));
    completed.add(_actId);
    localStorage.setItem(_storageKey, JSON.stringify([...completed]));
  }
  if (document.referrer) {
    window.history.back();
  } else {
    window.close();
  }
}

3. View toggle pattern (for two-view activities)

Activities with a default view + completion view use CSS class toggling or direct JS style manipulation:

// Direct style toggle (preferred when inline styles are present)
function endActivity() {
  document.querySelector('.view-default').style.display = 'none';
  document.querySelector('.view-complete').style.display = 'flex';
}

Important: If views use inline style="display:flex", CSS class-based toggling (.is-complete .view-default { display: none }) won't work because inline styles have higher specificity. Use JS direct style manipulation instead.

4. Wiring to course overviews

Add routing in the course overview's startActivity() function:

function startActivity(actId, encodedTitle, type, subtype) {
  const qs = '?actId=' + actId + '&storageKey=' + STORAGE_KEY;
  let page;
  if (actId === 'act-1') page = 'activity-pre-assessment.html';
  else if (actId === 'act-10') page = 'activity-post-assessment.html';
  else if (type === 'sim' && subtype === 'vr') page = 'activity-vr-pairing.html';
  else if (type === 'sim') page = 'activity-webgl.html';
  else if (type === 'reading') page = 'activity-e-learning.html';
  else page = 'activity-e-learning.html';
  window.open(page + qs, '_blank');
}

5. Cross-tab detection

Course overviews detect completion via the storage event — no additional wiring needed as long as the activity page writes to the correct storageKey.


Figma Asset Export Tips

When exporting images from Figma for activity pages:

  • Use get_design_context to get asset URLs, then download with curl
  • Check dimensions — Figma may export the full frame (1920×1080) instead of just the target node
  • Crop if needed — use sips -c <height> <width> --cropOffset <y> <x> on macOS to crop to the node's bounds
  • Calculate crop coordinates from the Figma CSS positioning data (w-[%], h-[%], left-[%], top-[%])
  • Verify visually — use the Read tool on the cropped image to confirm it looks correct

Project File Conventions

Every TAP prototype project should have:

File / Folder Purpose
README.md Project-specific: active prototype filename, Figma URLs, component rules, activity IDs, changelog
TAP-BRAND.md Shared brand reference: color tokens, typography, spacing, buttons — do not modify per-project
SKILL.md This file — reusable process knowledge, shared across projects
font/ Circular Std OTF files — shared, do not duplicate
assets/icons/ SVG icons — filled + outlined variants
assets/logo/ TAP logo assets
assets/images/ Activity page images (3D scenes, badges, backgrounds)
activities/ Shared activity HTML pages (opened in new tabs from course overviews)

Folder Structure & Path Rules

Course Overview Prototype/
├── *.html                    ← active prototypes (root)
├── activities/               ← shared activity pages
├── assets/icons/             ← SVG icons
├── assets/logo/              ← TAP logos
├── assets/images/            ← activity page images
├── font/                     ← Circular Std OTF files
├── March 17 User Testing/    ← archived prototypes
├── Past Explorations/        ← archived older prototypes

Path rules for startActivity():

  • Root prototypes → activities/activity-*.html
  • March 17 User Testing/ prototypes → ../activities/activity-*.html

Path rules for assets/fonts:

  • Root prototypes → assets/..., font/...
  • March 17 User Testing/ prototypes → ../assets/..., ../font/...
  • activities/ pages → ../assets/..., ../font/...

Activity Card Layout Variants

Two layout styles for the activity list:

Separated cards (inline3)

Each activity is its own bordered card with gaps between them.

.activities-list { display: flex; flex-direction: column; gap: 12px; }
.activity-row {
  border: 1px solid var(--gray-05); border-radius: 12px;
  padding: 12px 25px 12px 41px;
}

Connected list (inline2, inline4, multimeter)

All activities in one shared bordered container with divider lines.

.activities-list { border-radius: 12px; overflow: hidden; border: 1px solid var(--gray-05); }
.activity-row { border-bottom: 1px solid var(--gray-05); padding: 12px 25px 12px 41px; }
.activity-row:last-child { border-bottom: none; }

Activity Button Style Variants

Tinted text buttons (inline3, inline4, multimeter3)

Light blue background, blue text, fixed width. Used for Start/Learn/VR/WebGL labels.

.btn-tinted { background: var(--primary-06); color: var(--primary-02); }
.btn-tinted:disabled { background: var(--gray-06); color: var(--gray-04); cursor: not-allowed; }
.act-btn { width: 88px; justify-content: center; }

Filled buttons for focused/current state (sequential prototypes)

When an activity row has primary-06 background (focused/current), tinted buttons blend in. Use btn-filled (solid blue, white text) instead for contrast.

Icon-only buttons (multimeter2, sequential prototypes)

Round icon buttons using btn-icon (outline) and btn-icon-filled (solid blue). Used in older prototypes; newer variants prefer tinted text buttons.


Activity Title Type Prefixes

Some prototypes (inline2, sequential-inline2) prefix activity titles with the type:

  • Simulation activities → "Simulation: <title>"
  • Reading activities → "E-Learning: <title>"
  • Assessment titles remain unprefixed

This is done in the HTML directly (not JS-generated), so update the <span class="act-title"> content.


Confirmation Modal Pattern

Used in sequential-multimeter2.html for AI assessment activities. Shows a "Before You Begin" modal before launching voice-based activities.

When to use

Add to any prototype where assessment activities use voice/microphone (AI conversation). Do NOT add to Leak Checking prototypes — their assessments are standard e-learning, not voice.

Implementation

  1. CSS — Add .modal-overlay, .modal-card, .modal-body, .modal-content, .modal-close-row, .modal-close, .modal-icon, .modal-title, .modal-desc, .modal-footer styles before the CELEBRATION section
  2. HTML — Add #confirm-modal div before <script> tag
  3. JS — Add openModal(onContinue) and closeModal() functions before startActivity()
  4. Intercept — In startActivity(), check if (type === 'ai-assessment') → call openModal(() => window.open(...)) instead of opening directly

Design references

  • Modal layout: Figma TAP Web 3092:74506 (Alert Pop Up)
  • Info icon: assets/icons/info-blue.svg (exported from Figma 4748:156387)
  • Close button: Figma 4744:143502 — 4px padding, 4px border-radius, gray-02 icon, hover: gray-06 bg, pressed: gray-05 bg

Categories