Paste the Prompt for Claude Code section into Claude Code from the Konjo repo root (galenjauss/Konjo, branch master). The rest of this file is the spec it references. Keep this folder next to the repo (e.g. docs/design/handoff-teach-card/) so Claude Code can open the reference file.
Prompt for Claude Code
Implement the Teach-tab redesign described in
README.mdin this folder. The reference design isTakeB.dc.html(open it in a browser alongsidesupport.js; it is an HTML design reference, not code to port). Recreate it in this React Native / Expo codebase using the existing patterns: tokens only throughuseTheme()/createThemedStyles(never a hex),Iconfromsrc/components/Icon.tsx(Phosphor),BeltDot, existing hooks (useLiveClass,useFollowUpBadge,useMyFollowUps,useAttendanceRoster,useClassOrganizer,useStudentAttendanceStats,useStudentContact,useClassTechniqueDocumentation,useConfidenceDivergence,useStudentSafetyFlags,useExpectedTrials,useAssignmentForInstance). Do the work in the order of the Implementation plan below, one PR-sized commit per step, runningnpm run typecheckafter each. Where the spec says "assumption", implement as described but put a// ASSUMPTION:comment at the call site. Do not touch Studio, billing, the schedule editor, belt-test grading or the incident form. When a screen's behaviour is not in this README, it does not exist — ask rather than invent.
Overview
Konjo's Teach tab (instructor surface, mobile) is a hero followed by twelve equal-weight rows, two competing run-class flows, and no student profile. This redesign keeps one flow (Class Mode), one inbox (To do), one Student profile, and a short home: a white hero card, one tonal To do tile, and a 2×2 grid of doors (Classes · Students · Plan · Insights). Phone (390pt) and tablet (~1024pt) are equals. Light theme is rendered; every token has a dark pair and nothing here hardcodes a colour.
About the design files
TakeB.dc.html + support.js render the full Take B board: three home states, To do (with a failed section), Class Mode Check in and Teach on phone, the Teach step on a 1024pt tablet, a never-attended student profile, a mapping table, assumptions and rationale. Captions under every screen name the tokens used. Recreate in RN, do not copy markup.
Fidelity
High-fidelity. Spacing, type, colour and component structure are final and map 1:1 to packages/design-tokens/tokens.ts. Copy is final; use it verbatim.
Vocabulary (fixed — rename existing labels)
| Use | Not |
|---|---|
| Gaps | Focus, What each class needs |
| Subs · "1 class needs a sub" · Request a sub · Claim | Coverage, cover |
| To do (Follow-ups is a section of it) | Your work, Inbox |
| Class Mode · CTAs Run class / Start class / Open class | Take attendance, Organizer |
| Attendance | Attendance history / insights |
| Plan, Insights | Plan & curriculum, Schedule & insights |
| Class plan · dojo · rank/belt · test · owner / head instructor / instructor / front desk | lesson, gym, level, grading, admin/coach |
Voice: plain, second person, no exclamation marks, no emoji in chrome, no first person from the app.
Design law (enforced per screen)
Budgets — ceilings, zero is legal:
- Loud block (inverted surface or display type) — max 1.
- Red role — max 1 meaning: the primary action or a live/urgent state. A glyph repeated down a list is one meaning.
- Filled primary button — max 1. Everything else is a bordered control or text link.
- Uppercase register —
typography.eyebrowonly, ≤3 words.
Chrome (tab bar, header icons, screen title) and control state (a checked-in tile filled ink) are exempt.
Other rules: white cards on the tinted page (surface.card on surface.page), never the reverse; no shadow/blur/gradient; border.hairline divides rows inside a card (inset to text edge), border.control is the edge of anything tappable; ≥44pt targets; text.tertiary is card-only (fails 4.5:1 on the page); never disable a submit; destructive actions arm on first tap, fire on second; a floating surface gets overlay.dim + hairline, never a shadow.
Tokens (from packages/design-tokens/tokens.ts — import, never retype)
Colour, light | dark
| Token | Light | Dark | Used for |
|---|---|---|---|
brand.red |
#D62828 | #D62828 | red fill (tab badge; white on it) |
accent.red |
#C42020 | #FF5252 | red text on page/card (live eyebrow, overdue count) |
accent.onHero |
#FF5252 | #C42020 | red on surface.hero |
brand.ink |
#111111 | #111111 | header glyphs, chevrons |
surface.page |
#F2F2F3 | #0E0E10 | screen root |
surface.card |
#FFFFFF | #1C1C22 | hero card, doors, all cards |
surface.hero |
#111111 | #F2F2F2 | filled primary; checked-in tile; active step/segment; "already here" avatar |
surface.muted |
#FAFAFA | #16161A | the To do tonal tile on surface.page; selected grid column on surface.card |
surface.tag |
#F4F4F4 | #232327 | avatar initials (card-only) |
text.primary/secondary/tertiary |
#1F1F1F / #6B6B6B / #737373 | #F2F2F2 / #A8A8AE / #909094 | tertiary card-only |
text.onHero / text.onHeroMuted |
#FFFFFF / #A8A8AE | #131313 / #5A5A60 | on ink fills |
border.hairline / border.control / border.input |
#E8E8E8 / #88888C / #D8D8D8 | #2C2C2F / #6A6A72 / #3A3A40 | row divider / tappable edge / input edge, readiness-ring track on surface.card, ungraded cell on surface.card |
feedback.warningFill/Border/Text |
#FFF6E0 / #9C7718 / #8A6300 | #332A0E / #9C7718 / #E8C35A | fading, safety flag, 8–21 day recency, conflict cell |
feedback.successFill/Text |
#E6F4EC / #1A7A42 | #11301E / #5BC487 | ≤7 day recency |
feedback.errorFill/Text |
#FDECEE / #B73232 | #3A1518 / #FF6B6B | >21 day recency |
Retired — never use: band.*, text.quaternary as text, surface.white, typography.displayTitle, typography.sectionLabel.
Type (typography.*): display 34/800/-0.8 lh38 · title 22/700/-0.3 lh28 · metric 28/800/-0.6 lh32 · rowTitle 16/600 lh22 · body 15/400 lh22 · caption 13/400 lh18 · label 13/700 ls0.6 lh16 (buttons, chips, segments; sentence case) · micro 12/500 lh16 · eyebrow 11/800 ls1.2 uppercase lh14. Font: platform UI face (fontFamily.body).
Spacing: page 20 · section 24 · card 16 · tight 8 · 12 between grouped cards · 6 only inside a component · 2 optical (title→caption).
Radius: input 8 · card 12 · pill 999. Heights: controlHeight.primary 48 · control 44 · chip 32 (+hitSlop 6) · row 64. Icons: 16 inline / 19 status / 24 header. Avatars: 32 row / 88 profile. Belt swatch: 10 ranked / 16 roster / 28 subject; always 1px border.control ring. Pressed opacity 0.85. Motion 100–350ms, exits faster.
Screens
1. Teach home (TeachHomeScreen.tsx — rewrite)
Root surface.page. Header: "Teach" display, left; right: search (magnifyingGlass, opens student search → profile) and more (⋯ → sheet with "Report an incident", feature-flagged). 44×44 targets, 24 glyphs. Scroll content padding page horizontal, 12 between blocks, bottom = tab bar + 32.
Hero card — surface.card, radius.card, padding card, gap 12. Content by useLiveClass status:
| Status | Eyebrow | Title (title) |
Meta (caption, secondary) |
Extras | CTA |
|---|---|---|---|---|---|
| live | LIVE NOW, 8pt dot + accent.red eyebrow |
class name | "Started 6:00 PM · plan · N blocks" | avatars + blocks | Run class filled |
| soon | STARTS IN 12 MIN / STARTING NOW, same | class name | "6:00 PM · Green Stripe Prep · 4 blocks" | avatars + blocks | Start class filled |
| next (later today / this week) — the reader's own next class, i.e. the first upcoming class she is rostered on | NEXT CLASS, text.tertiary |
class name | "Mon at 4:30 PM · Kata Night · 3 blocks · nothing scheduled today" | none | Open class bordered |
| none, on roster — nothing of hers this week | NEXT AT THE DOJO, text.tertiary (the To do tile leads; the hero names the dojo's next class, never as hers) |
class name | "Mon at 4:30 PM · Elena Vasquez · Kata Night · 3 blocks" — the colleague's name, as the Classes list writes it | body: "You're not on a class this week." |
Open class bordered |
| none, not on roster | BLUE HERON MARTIAL ARTS (dojo name) | "You're set up to teach at Blue Heron." | body: "A head instructor adds you to a class; then it appears here." |
none | Message {head instructor name} filled → opens sms:/mailto: to the head instructor of record (assumption) |
Avatar row (live/soon only): 32pt circles, 2px surface.card ring, −8 overlap, initials micro/700. Ink (surface.hero, text.onHero) = already checked in (any of the five check-in paths); surface.tag = expected. Trailing "+N". Caption under: "5 already here · 14 expected · 1 trial visitor, Lee (adult)" — trial visitor (from useExpectedTrials) and plan strip are folded into this meta, not separate banners. Plan blocks: compact list, caption, name left / duration right (secondary), hairline between rows; if no plan, one row "No class plan · Add plan" (text link → SchedulePlan). Two classes live at once → hero shows one; the other appears as the first row behind the Classes door with a LIVE marker.
CTA: filled primary surface.hero, 48 tall, radius.input, padding 0 20, rowTitle label, hugs left. Bordered variant: 44 tall, 1px border.control, label.
To do tile — surface.muted, radius.card, padding card, tappable → To do. Three columns, each metric count over a caption label: "follow-ups" · "needs a sub" · "ready to test"; trailing chevron. When any follow-up is overdue the first count becomes the overdue number in accent.red with label "overdue", and when there is no class today the tile also lists up to two overdue people (rowTitle name + caption fact, e.g. "Last attended Aug 11 · quiet 23 days · due Sep 1"). Empty: one rowTitle line "Nothing waiting on you." + chevron. Counts come from useFollowUpBadge (open/overdue) plus sub requests and ready-to-test queries (see To do).
Doors — 2×2 grid, gap 12, surface.card, radius.card, padding card, min-height 72, rowTitle title + caption line:
- Classes — "2 today · Kids Beginners recorded" / "None today · 4 this week" → this week's classes, view only (today first). Row tap → Class Mode. Row ⋯ (head instructor/owner) → sheet: Cancel class (reason; two-tap arm), Send announcement. Also lists "Request a sub" per upcoming class I teach.
- Students — "48 at Blue Heron · 2 fading" → searchable dojo roster (avatar, belt dot 16, "last attended"; warning tint after the dojo threshold, default 21 days) → Student profile.
- Plan — "Class plans · Calendar · Test requirements · Gaps" → hub of four rows (existing PlansLibrary, CurriculumCalendar, TestRequirements, Focus→rename Gaps).
- Insights — "Attendance · Analytics · Subs" → hub of three rows (AttendanceInsights→Attendance, TeachingAnalytics→Analytics, Coverage→Subs; give Subs a back button).
New instructor (not on dojo_instructors): identical layout, nothing greyed or hidden; one caption line under the doors: "Everything here is readable. Running a class, grading and check-in open once you're on a class." Read-only screens (Test requirements, Curriculum calendar) say so in one caption line, not a banner.
Error: a failed section keeps its slot with body "Couldn't load today's classes." + bordered Try again; if the whole screen fails, title "Couldn't load your classes." + filled Try again, left-aligned in flow. Loading: nothing under 1s, then skeleton (border.input) where layout is known.
Tab bar unchanged (chrome): Teach badge = open follow-ups, cap "9+".
2. To do (MyFollowUpsScreen.tsx → ToDoScreen)
Back chevron (no bar title) → display "To do" → caption "N waiting on you" (or body "Nothing waiting on you." when empty). Sections, this order, each an eyebrow (text.secondary on page) + cards, 24 between sections:
- Needs a sub — open requests at the dojo, soonest first. Card:
rowTitleclass,caption"Fri 7:00 PM · Marcus Bell asked for a sub", bordered Claim (+ Assign, Cancel for head instructor/owner),microtertiary "Reversible until the class starts. Assign is head instructor only." Claim/assign notify requester and coverer; reversible until start. - Ready to test — students with every requirement for next rank graded ≥3 (nightly). Row ≥64: belt dot 16 (or avatar 32), name, caption "Every requirement for {next rank label} graded 3 or better", chevron → profile.
- Follow-ups — buckets Overdue · Today · Upcoming · Someday (collapse today's six). Card grammar from
followUpCopy.ts: name leads (rowTitle), fact line (caption) "Last attended Aug 11 · quiet 23 days · due Sep 1" / "Signed up 3 weeks ago and hasn't started"; never "fading" on a never-attended row. Actions, all bordered 44: Call · Text · Email (omit missing; footnote "No phone on file…"), then Complete · Snooze (Tomorrow / In 3 days / Next week) · Not needed (typed reason). Undo toast on every mutation. Footnotemicrotertiary. Incident tasks read-only ("finished in Konjo Studio"). - Snoozed, 5. Done, last 7 days — collapsed rows with counts; Reopen / Bring back inside.
A failed section: body "Couldn't load sub requests." + bordered Try again in its slot; other sections still work. No filled primary and no red role on this screen.
3. Class Mode (ClassModeScreen.tsx, ClassModeCheckIn.tsx, ClassModeTeach.tsx, ClassModeWrapUp.tsx)
Entry: any class anywhere (hero CTA, Classes door row, Attendance → Classes → session) opens Class Mode; delete ClassPicker, ClassOrganizerScreen as a route (its grid becomes the tablet Teach layout), and the "Take attendance" path. Header: x (close) · caption secondary "Adults All Levels · 6:00 PM" centred · firstAid glyph 24 (incident, flagged). Step bar: three equal segments 44 tall in one border.control outline, radius.input; active = surface.hero fill/text.onHero; labels label "1 Check in · 2 Teach · 3 Wrap up" (tablet: "Check in · 8" with a check glyph once saved). Freely tappable.
Check in — display "8 of 15 here", caption "14 expected · 1 trial visitor". "N need attention" card row (warning glyph 19 accent.red, names + reasons, chevron → filters grid to flagged). Grid: 3 across at 390 (more columns on tablet), 8 gap, tiles ~111×92, radius.card. Present = surface.hero fill, name text.onHero; not yet = surface.card + 1px border.control. Belt dot 16 top-left (ring), one status glyph 19 top-right (safety warning fill → accent.red/accent.onHero; trial userPlus; said "I'm here" handWaving; booked calendarCheck), name 15/600 bottom-left. Trial visitor tile: ring-only dot, "Lee" + caption "trial". Bottom bar (surface.card, hairline top, padding 12 20 28): bordered Scan (camera QR) · bordered Add visitor (walk-in not on roster — the old "Late student") · filled Teach → (flex 1). Toggling writes optimistically via bulk_check_in; removal allowed, audited.
Teach, phone — segmented By student / By technique (44, same style as step bar). Chips 32 (label, surface.card + border.control; selected surface.hero), belt dot 10, wrap, never scroll, "+N" overflow chip. Safety banner (warning fill/border/text, radius.card, padding 12 16) for the selected student. One surface.card with a section per technique: rowTitle name + recency micro right (success/warning/error text by band; "Never drilled"); divergence glyph warning 16 in feedback.warningText when student confidence and instructor mastery differ by ≥2, with micro "Maya rates herself 4"; MasteryDial: six cells flex 1, 52 tall, radius.input, 1px border.control, 22/700; selected = surface.hero fill (not red). Sync caption micro text.tertiary on surface.card: "Waiting to sync" / "Not saved — tap to retry" / "Changed on another device — review". Bottom bar: bordered + Add item (CurriculumItemPicker) · filled Wrap up →. Empty: rowTitle "Nothing graded yet." + body "Add what you're teaching to start grading." + filled Add item, left-aligned in flow (then Wrap up is bordered — one filled per screen).
Teach, tablet (≥700pt) — header row: close · class rowTitle + caption "6:00 PM · Green Stripe Prep · started 23 min ago" · step bar centred (420 wide) · first-aid. Under it: chips Tonight's plan / Next belt gaps (swap column set; the second reuses the Gaps engine's by-student lens — assumption) left; bordered Add item right. Grid in a surface.card: sticky first column 240 (avatar 32 surface.tag initials + rowTitle name + micro belt label/flag with 10pt dot); technique columns header rowTitle + micro "block N"; cells 48 tall radius.input 22/700 with recency fill+text pair (≤7d success, 8–21 warning, >21 error), ungraded = 1px border.input "–" tertiary, pending = 1px dashed border.control + micro "pending", conflict = warning fill + 1px feedback.warningBorder + micro "conflict", divergence glyph inline. Tap cell → MasteryDial popover (sheet on tablet); tap column header → By technique for that item. Bottom bar: caption sync summary "1 pending · 1 conflict · everything else saved" left; filled Wrap up → right.
Wrap up — unchanged logic; layout: participants with role chips (lead · assistant · substitute), plan blocks covered toggles, row "Notes, feedback and homework" → per-student composer (Debrief), one filled Finish. Nothing else filled.
4. Student profile (new StudentProfileScreen, replaces AttendanceStudentDetailScreen as the target of every tapped name)
Back chevron + ⋯ (no bar title). Content padding page, gap 12.
Header card (surface.card, padding card, gap 16): avatar 88 surface.tag initials 28/800 left; right: eyebrow row (belt dot 16 + eyebrow tertiary using the dojo's own rank label, e.g. "White belt · 10th kyu"), display name, caption meta ("Last attended Tue · 14 classes since promotion" or "No attendance recorded yet · signed up Aug 13"). Readiness row: 72pt ring (SVG, 6 stroke, track border.input, progress text.primary, centre rowTitle "4/18"), beside it rowTitle state + caption explanation ("Nothing graded yet" / "12 requirements for One Green Stripe. Grades appear after their first class." / "4 of 18 left for One Brown Stripe · gaps: Kata 1, Kata 2, …"). Actions: one filled primary chosen by state — fading → Text (or Email if no phone); never attended → Email; otherwise → Send feedback or homework — plus bordered Write a note. micro tertiary footnote when phone missing: "No phone on file. Numbers are added on the member record in Konjo Studio."
Rows card — one surface.card, rows ≥64 with inset hairlines, each rowTitle + caption + chevron: Readiness (list of open requirements with grade + recency) · Mastery (by category, 0–5 pips 10pt + recency; "Nothing graded yet. Grades come from Class Mode.") · Attendance (stats last 30 / since promotion / all time as metric, per-class chips, attended sessions only — absences never listed) · Notes (staff-only notes, private; latest previewed in caption) · Send feedback or homework (reuses the post-class composer; student is notified) · Flag ready to test ("Not yet · tells the head instructor" — assumption: creates a head-instructor task) · Contact (call / text / email, or guardian for a minor).
Fading student: a warning banner (fill/border/text) above the rows card: "Quiet past the dojo's 21-day threshold. Follow-up open in To do, due Sep 1." Never-attended: every line says "No attendance recorded yet"; no fading language anywhere.
Interactions & behaviour
- Pressed state opacity 0.85 everywhere; chevrons
caretRight16brand.ink. - Sheets: grabber, left title, rows ≥64 with check on selected,
radius.sheettop,overlay.dimscrim only. - Dates in the reader's local time (see
followUpCopy.tsnote ondays_since). - Dynamic Type: no fixed-height text containers; rows and tiles grow.
- Reduce-motion: cross-fade instead of movement.
State & data
- Home:
useLiveClass(status, option, liveOptions),useAssignmentForInstance(plan title/blocks),useExpectedTrials,useFollowUpBadge, newuseSubRequests(open at dojo), newuseReadyToTest(nightly flag), checked-in set for the avatar row (useAttendanceRoster(instanceId).checkedIds). - To do:
useMyFollowUps+ the two new hooks; mutations keep Undo semantics fromMyFollowUpsScreen. - Class Mode: existing hooks; the tablet grid is
useClassOrganizerrendered wide; per-cell sync fromclassMutationQueue/useClassTechniqueDocumentation.getSyncState. - Profile:
useStudentAttendanceStats,useAttendanceHistory,useStudentContact, mastery from the Analytics student lookup, requirements fromuseDojoBeltRequirements, notes/feedback from the Debrief tables, safety flags fromuseStudentSafetyFlags.
Implementation plan (one commit each)
- Rename routes/labels to the fixed vocabulary (Focus→Gaps, Coverage→Subs, Attendance history→Attendance, Plan & curriculum→Plan, Schedule & insights→Insights); add back button to Subs. Typecheck.
- Retire
band.*anddisplayTitlefrom Teach; header grammar: home = leftdisplay; children = back chevron, no bar title,displaytitle in content. StudentProfileScreen+ route; point every name tap (follow-ups, attendance lists, Class Mode tiles/rows, Students door) at it. KeepAttendanceStudentDetailScreenreachable only from the profile's Attendance row until parity, then delete.ToDoScreen: sections Needs a sub / Ready to test / Follow-ups / Snoozed / Done; new hooks; Teach badge = open follow-ups.- Class Mode: Check in tiles + bottom bar (Scan, Add visitor, Teach); Teach dial restyle (ink selected, bordered Add item); tablet grid layout at ≥700pt; delete ClassPicker/Organizer/Take-attendance entries and both
brand.redCTAs. - Teach home rewrite: hero card, To do tile, 2×2 doors, Plan and Insights hubs, new-instructor state, section/screen error states, skeletons.
- Update
.maestro/teach-home.yaml,class-mode.yaml,my-follow-ups.yamlande2e/*.spec.tstestIDs; runnpm run e2e.
Assumptions (flag in code with // ASSUMPTION:)
- "Flag ready to test" creates a task for the head instructor — not a decided product fact.
- "Message {head instructor}" opens the device's text/email to the head instructor of record; no in-app messaging.
- Students door = Attendance → Students segment given its own door; no new data.
- "Next belt gaps" column set inside the grid reuses the Gaps engine's By-student lens.
- Avatars are initials on
surface.tag; ink fill = already checked in. - Demo content (Blue Heron, Kids Beginners on a Tuesday, plan block durations, filler names Ana Duarte, Ben Kowalski, Chloe Park, Devon Hill, Ines Moreau, Kai Tanaka, Tomas Rivera, techniques Kata 1 / Kata 2 / Front snap kick / Wrist escapes) is placeholder, not seed data.
Out of scope (do not build)
Money, payroll, memberships, CRM, message blasts/automations, kiosk device management, recurring-schedule editor, belt-test grading, incident review/export, dojo switcher, photo heroes, swipe-to-act.
Assets
- Icons: Phosphor via
src/components/Icon.tsx(magnifyingGlass,more,caretLeft/Right,close,firstAid,warning,camera,plus,check,checkCircle,phone,comment→Text,envelope,handWaving,calendar,arrowRight; adduserPlus,calendarCheck,trayto the map). - Belt swatches:
BeltDotwithswatchSize.roster/subject; no new art. - No images or fonts.
Screenshots (screenshots/, 2× PNG)
01-home-starting-soon.png— Teach home, class starts in 12 min02-home-nothing-today.png— Teach home, nothing scheduled today03-home-new-instructor.png— Teach home, instructor not yet on a roster04-to-do.png— To do, Needs-a-sub section failed05-class-mode-check-in.png— Class Mode step 1, phone06-class-mode-teach-phone.png— Class Mode step 2, phone, By student07-class-mode-teach-tablet.png— Class Mode step 2, 1024pt tablet grid08-student-profile.png— Student profile, never attended
Files in this folder
screenshots/— the eight PNGs above.TakeB.dc.html— the Take B board (open in a browser; needssupport.jsalongside).support.js— runtime for the reference file.README.md— this spec.