college-attendance is a production-ready College Attendance Management Web App built for a Class Representative (CR) who is responsible for the daily attendance of a class of 70–100+ students, and for the Students in that class who need to see their own attendance standing.
The product intent is calm, reliable bookkeeping under pressure: a CR standing in a lecture hall, phone held one-handed, marking a large roster quickly by USN, roll number, or name — by tap or by voice — while every percentage shown anywhere in the product is computed from the database and updates immediately after each committed change. The audience is two roles only: the CR, who owns all attendance-taking, correction, roster, subject, timetable, reporting, and audit work; and the Student, who reads their own overall percentage, subject-wise attendance, and attendance history and never edits attendance.
The application must be fully functional, fast, mobile-friendly, reliable, and production-quality — not a mockup or static prototype — with no placeholder or TODO functionality.
The system is a web application with a React + TypeScript + Tailwind/shadcn frontend, a Node.js + Express backend, PostgreSQL persistence through Prisma, and JWT authentication with role-based access for the CR and Student roles.
Current delivery covers:
Narrow exclusions and boundaries:
The product is delivered as a first-party web application with application-owned identity. Because the CR controls shared, consequential attendance records and the Student reads private attendance data, identity must be established before any protected surface is reachable, and the role bound to that identity determines which surfaces are available. The anonymous entry surface (Landing) and the identity surface (Login) are reachable without a session; every other page requires an authenticated session and the correct role.
First-use enrollment and returning verification both happen on Login, which is the single neutral identity surface for both roles; the role is established as part of that identity step, so no protected destination owns the interaction that grants access to itself. Attendance changes are only authoritative once committed transactionally to PostgreSQL; offline-safe attendance state is held locally and reconciled before it becomes an authoritative record. All percentages displayed anywhere are derived from the database, never from client-side arithmetic over a stale snapshot.
Everything described in this document is current. No future-horizon capability is claimed.
Not applicable — no reference directive in this project declares a content_source.
FR-1 — Role-based login. As a Class Representative or a Student, I should log in with role-based access so that I reach only the surfaces my role permits. (provenance: explicit; lifecycle: initiator = CR or Student; trigger = submitting credentials on Login; observable result = authenticated session bound to the correct role, routed to Dashboard for CR and Student Profile for Student; access state = anonymous entry, protected state unavailable until identity is established; failure/recovery = invalid credentials or duplicate identifier produce a single-line error and the form can be corrected and resubmitted, with rate-limited attempts surfaced clearly; continuation = the role's landing surface loads with database-derived data.)
FR-2 — First-use enrollment and returning verification. As a Class Representative or a Student, I should establish my identity on first use and verify it on return so that my role is bound before any protected access. (provenance: required_inference; lifecycle: initiator = CR or Student; trigger = first visit to Login or a later return; observable result = identity created with the correct role, or an existing session restored; access state = anonymous entry on the same neutral surface for both roles; failure/recovery = validation errors shown in place and correctable; continuation = routing by role.)
FR-3 — Dashboard totals and percentages. As a Class Representative, I should see total students, present count, absent count, and attendance percentage on the Dashboard so that I know the class standing at a glance. (provenance: explicit; lifecycle: initiator = CR; trigger = opening Dashboard; observable result = totals and percentage rendered from database-derived values; access state = authenticated CR; failure/recovery = load failure shows a single line of type with retry and never presents stale values as current; continuation = drill into Take Attendance or Reports.)
FR-4 — Dashboard trends. As a Class Representative, I should see attendance trends on the Dashboard so that I can judge direction over time. (provenance: explicit; lifecycle: initiator = CR; trigger = opening Dashboard; observable result = a single hairline sparkline of attendance over time; access state = authenticated CR; failure/recovery = trend unavailable shows a single line of type; continuation = open Reports for detail.)
FR-5 — Low-attendance alerts. As a Class Representative, I should see low-attendance alerts on the Dashboard so that I can identify students who need attention. (provenance: explicit; lifecycle: initiator = CR; trigger = opening Dashboard; observable result = students below the low-attendance threshold listed as typographic margin notes with their percentage in ink; access state = authenticated CR; failure/recovery = alert computation failure shows a single line of type with retry; continuation = open the student's context or Reports.)
FR-6 — Mobile-first Take Attendance with identifiers and statuses. As a Class Representative, I should take attendance on a mobile-first screen showing USN, roll number, and name with Present/Absent/Leave controls so that I can mark a large roster quickly one-handed. (provenance: explicit; lifecycle: initiator = CR; trigger = opening Take Attendance for a session; observable result = each student row shows USN, roll number, name, and three mark cells, and a tap sets the status; access state = authenticated CR; failure/recovery = save failure retains changes locally with a single-line error and retry; continuation = save the session or continue marking.)
FR-7 — Mark all present. As a Class Representative, I should mark all present in one action so that a mostly-present class is recorded quickly. (provenance: explicit; lifecycle: initiator = CR; trigger = invoking mark-all-present; observable result = every student in scope is set to Present and the present count updates; access state = authenticated CR; failure/recovery = undo reverses the action; continuation = adjust individual exceptions and save.)
FR-8 — Bulk actions. As a Class Representative, I should apply bulk actions to a filtered selection so that groups of students can be marked together. (provenance: explicit; lifecycle: initiator = CR; trigger = selecting students and choosing a bulk status; observable result = all selected students take the chosen status; access state = authenticated CR; failure/recovery = undo reverses the bulk change; continuation = save or continue editing.)
FR-9 — Undo. As a Class Representative, I should undo my last attendance change so that a mistaken mark is corrected immediately. (provenance: explicit; lifecycle: initiator = CR; trigger = invoking undo; observable result = the previous status is restored and the mark reverses over 120ms; access state = authenticated CR; failure/recovery = if undo is unavailable, the CR can set the status directly; continuation = continue marking or save.)
FR-10 — Filters. As a Class Representative, I should filter the roster and history so that I can find students and sessions quickly. (provenance: explicit; lifecycle: initiator = CR; trigger = applying a filter; observable result = the list narrows to matching students or sessions; access state = authenticated CR; failure/recovery = clearing the filter restores the full list; continuation = act on the filtered set.)
FR-11 — Edit previous attendance. As a Class Representative, I should edit previous attendance so that historical records can be corrected. (provenance: explicit; lifecycle: initiator = CR; trigger = opening a prior session from Attendance History; observable result = the prior session's statuses are editable through the same validation/state system and the change is persisted transactionally; access state = authenticated CR; failure/recovery = save failure retains edits locally with a single-line error and retry; continuation = the corrected record is reflected in percentages and in the Audit Log.)
FR-12 — Voice attendance marking. As a Class Representative, I should say roll numbers, USNs, or student names and have attendance marked instantly so that I can record a large class without tapping. (provenance: explicit; lifecycle: initiator = CR; trigger = speaking while listening is active; observable result = the matched student is marked through the same validation/state system as manual marking, with a single line of voice confirmation; access state = authenticated CR; failure/recovery = no match or low confidence produces no automatic marking and offers "Did you mean?"; continuation = resolve the candidate or continue speaking.)
FR-13 — Voice commands. As a Class Representative, I should issue the commands present, absent, leave, undo, clear, save, and mark-all-present by voice so that the whole session can be driven hands-free. (provenance: explicit; lifecycle: initiator = CR; trigger = speaking a command; observable result = the corresponding action is applied to the shared attendance state; access state = authenticated CR; failure/recovery = an unrecognized command produces a single line of type and no state change; continuation = repeat the command or act manually.)
FR-14 — Confidence-based matching with no automatic uncertain marking. As a Class Representative, I should have uncertain students never marked automatically, with a "Did you mean?" option shown instead, so that incorrect attendance is never recorded silently. (provenance: explicit; lifecycle: initiator = CR; trigger = a recognition result below the confidence threshold; observable result = no status change occurs and a candidate list is presented for the CR to choose from; access state = authenticated CR; failure/recovery = dismissing the candidates leaves the student unmarked; continuation = choose a candidate, repeat the input, or mark manually.)
FR-15 — Shared validation and state for voice and manual. As a Class Representative, I should have voice and manual attendance use the same validation and state system so that both paths produce identical, consistent records. (provenance: explicit; lifecycle: initiator = CR; trigger = any marking action from either path; observable result = the same validation rules, the same session state, and the same persistence path apply; access state = authenticated CR; failure/recovery = a validation rejection from either path surfaces the same single-line error; continuation = correct and retry.)
FR-16 — Voice engine, language, and modes. As a Class Representative, I should use the Web Speech API with an optional Whisper/Vosk fallback, en-IN recognition, continuous listening, and push-to-talk/wake-word modes so that voice attendance works in a real lecture hall. (provenance: explicit; lifecycle: initiator = CR; trigger = activating voice and choosing a mode; observable result = recognition runs in the selected mode with en-IN as the language; access state = authenticated CR; failure/recovery = if the primary engine is unavailable, the optional fallback is used when configured, otherwise a single line of type explains the limitation; continuation = continue in the working mode.)
FR-17 — Microphone testing. As a Class Representative, I should test my microphone before a session so that recognition failures are not discovered mid-lecture. (provenance: explicit; lifecycle: initiator = CR; trigger = running the microphone test; observable result = input level and recognition responsiveness are shown; access state = authenticated CR; failure/recovery = permission denial or no input is reported as a single line of type with guidance; continuation = proceed to voice attendance once the test passes.)
FR-18 — Confidence thresholds and voice confirmations. As a Class Representative, I should set confidence thresholds and receive voice confirmations so that recognition behaves predictably and I know what was recorded. (provenance: explicit; lifecycle: initiator = CR; trigger = adjusting the threshold or completing a marking action; observable result = the threshold governs the auto-mark boundary and a single line of confirmation states what was recorded; access state = authenticated CR; failure/recovery = a threshold that suppresses all auto-marking still allows "Did you mean?" resolution; continuation = continue the session.)
FR-19 — Duplicate and incorrect marking prevention. As a Class Representative, I should have duplicate or incorrect markings prevented so that the record stays trustworthy. (provenance: explicit; lifecycle: initiator = CR; trigger = a repeated or conflicting marking input; observable result = the existing status is not duplicated and the state remains consistent; access state = authenticated CR; failure/recovery = the CR can still change the status deliberately; continuation = continue the session.)
FR-20 — Student profiles with overall, subject-wise, and history. As a Student, I should see my overall percentage, subject-wise attendance, and attendance history so that I know my standing. (provenance: explicit; lifecycle: initiator = Student; trigger = opening Student Profile; observable result = overall percentage, per-subject percentages, and history render from database-derived values; access state = authenticated Student, own record only; failure/recovery = load failure shows a single line of type with retry; continuation = filter history by subject or date.)
FR-21 — Subject management. As a Class Representative, I should manage subjects so that attendance is recorded against the right subject. (provenance: explicit; lifecycle: initiator = CR; trigger = creating, editing, or removing a subject; observable result = the subject list reflects the change and subjects become selectable in attendance contexts; access state = authenticated CR; failure/recovery = validation failure or a delete blocked by existing records is reported as a single line of type; continuation = correct and retry.)
FR-22 — Timetable management. As a Class Representative, I should manage the timetable so that attendance sessions carry the correct subject and period context. (provenance: explicit; lifecycle: initiator = CR; trigger = adding, editing, or removing a timetable entry; observable result = the timetable reflects the change and is available when starting a session; access state = authenticated CR; failure/recovery = a conflicting or invalid entry is reported as a single line of type; continuation = adjust and retry.)
FR-23 — Reports and analytics. As a Class Representative, I should view reports and analytics so that I can analyse attendance across the class and per student. (provenance: explicit; lifecycle: initiator = CR; trigger = opening Reports and choosing filters; observable result = analytics render from database-derived values; access state = authenticated CR; failure/recovery = no data for the filter set shows a single line of type; continuation = adjust filters or export.)
FR-24 — CSV, Excel, and PDF export. As a Class Representative, I should export reports as CSV, Excel, and PDF so that attendance can be shared and archived. (provenance: explicit; lifecycle: initiator = CR; trigger = choosing an export format; observable result = a file in the chosen format is produced from the current report; access state = authenticated CR; failure/recovery = export generation failure shows a single line of type with retry; continuation = retry or change filters.)
FR-25 — Excel/CSV student import with validation and preview. As a Class Representative, I should import students from Excel or CSV with validation and preview so that the roster is correct before it is persisted. (provenance: explicit; lifecycle: initiator = CR; trigger = uploading a roster file; observable result = parsed rows are validated and previewed with per-row findings before confirmation; access state = authenticated CR; failure/recovery = unreadable files, wrong columns, or duplicate USNs are reported with the offending rows identified, and invalid rows can be excluded; continuation = confirm the import and see the roster updated.)
FR-26 — Audit log for every attendance edit. As a Class Representative, I should have every attendance edit recorded in an audit log so that changes are traceable. (provenance: explicit; lifecycle: initiator = CR (edits) and CR (review); trigger = any attendance edit; observable result = an audit entry records what changed, from what to what, when, and by whom, and is reviewable in Audit Log; access state = authenticated CR; failure/recovery = load failure shows a single line of type with retry; continuation = filter or open the related session.)
FR-27 — Optional QR attendance. As a Class Representative, I should optionally capture attendance by QR so that a second capture path is available when useful. (provenance: explicit, optional; lifecycle: initiator = CR; trigger = enabling QR capture within the attendance workspace; observable result = a QR-captured mark enters the same validation/state system as manual and voice marking; access state = authenticated CR; failure/recovery = an unreadable or invalid code produces no marking and a single line of type; continuation = mark manually or retry the code.)
FR-28 — Light/dark mode and accessible responsive UI. As a Class Representative or a Student, I should use the app in light or dark mode with an accessible, responsive interface so that it is usable in a lecture hall and on any device. (provenance: explicit; lifecycle: initiator = CR or Student; trigger = toggling the theme or resizing the viewport; observable result = the interface switches between the paper-light and ink-dark variants and remains fully legible and operable at 375px, 768px, and 1280px; access state = any authenticated surface; failure/recovery = a persisted theme preference that cannot be read falls back to light; continuation = continue working.)
FR-29 — Database-derived percentages that update immediately. As a Class Representative or a Student, I should see attendance percentages calculated from the database that update immediately after changes so that what I see is always current. (provenance: explicit; lifecycle: initiator = CR or Student; trigger = any committed attendance change or a page load; observable result = displayed percentages are database-derived and refresh after the change commits; access state = authenticated CR or Student; failure/recovery = if a refresh fails, stale values are not presented as current and a single line of type with retry is shown; continuation = the refreshed values are used.)
FR-30 — Secure authentication, validation, rate limiting, and transactions. As a Class Representative or a Student, I should have my session secured and my changes validated, rate-limited, and committed transactionally so that the record is trustworthy. (provenance: explicit; lifecycle: initiator = CR or Student; trigger = any authenticated request or attendance write; observable result = requests are authenticated and validated, abusive rates are limited, and attendance writes commit atomically; access state = authenticated session required for protected operations; failure/recovery = rejected requests return a clear single-line error and no partial write occurs; continuation = correct and retry.)
FR-31 — Offline-safe attendance state. As a Class Representative, I should have attendance state remain safe when connectivity drops so that a session is not lost mid-lecture. (provenance: explicit; lifecycle: initiator = CR; trigger = connectivity loss during marking; observable result = changes are held locally and the session remains usable with a visible offline indicator; access state = authenticated CR; failure/recovery = on reconnect, held changes reconcile before becoming authoritative persisted records, and conflicts are surfaced rather than silently overwritten; continuation = the reconciled session is saved and reflected in percentages.)
FR-32 — Seed data and demo credentials. As a Class Representative or a Student, I should have seed data for approximately 85 students and working demo credentials so that the application can be evaluated immediately. (provenance: explicit; lifecycle: initiator = CR or Student; trigger = running the seeded environment; observable result = a populated roster and usable demo credentials for both roles; access state = Login; failure/recovery = a failed seed run reports a single line of type and can be re-run; continuation = log in and use the app.)
FR-33 — Docker setup, tests, and README. As a developer or evaluator, I should have a Docker setup, tests, and a README so that the application can be run and verified. (provenance: explicit; lifecycle: initiator = developer/evaluator (non-persona delivery actor); trigger = following the README; observable result = the stack starts via Docker and the test suite runs; access state = not applicable; failure/recovery = startup or test failures are reported with actionable output; continuation = the running application is exercised.)
FR-34 — No placeholder or TODO functionality. As a Class Representative or a Student, I should encounter no placeholder or TODO functionality so that every visible capability actually works. (provenance: explicit; lifecycle: initiator = CR or Student; trigger = using any capability; observable result = the capability performs its real function against real persistence; access state = as per the surface; failure/recovery = genuine errors are reported as real errors, never as stubs; continuation = the workflow completes.)
Product context. The CR is the sole operator of attendance for a class of 70–100+ students. They work standing in a lecture hall, phone held one-handed, often speaking roll numbers over ambient noise, and they must not look flashy or distracted in front of a professor. Their tool must be quiet, fast, and legible in sunlight.
Primary goal. Record accurate attendance for the whole class quickly — by tap or by voice — and keep every percentage in the product correct and current.
Distinct accepted responsibilities. The CR logs in with role-based access and owns: reading the Dashboard (totals, present, absent, percentage, trends, low-attendance alerts); taking attendance on the mobile-first Take Attendance screen using USN, roll number, and name with Present/Absent/Leave controls; mark-all-present, bulk actions, undo, filters, and editing previous attendance; voice attendance by speaking roll numbers, USNs, or names with the commands present, absent, leave, undo, clear, save, and mark-all-present; confidence-based matching where an uncertain student is never marked automatically and a "Did you mean?" option is offered; managing subjects and the timetable; importing students from Excel/CSV with validation and preview; producing reports and analytics with CSV, Excel, and PDF export; and reviewing the audit log of every attendance edit. The CR may optionally use QR capture.
Relevant inputs and decisions. Which session and subject/period is being recorded; each student's status; whether a low-confidence voice match should be accepted via "Did you mean?" or repeated; whether to mark all present and then correct exceptions; whether an imported row is valid enough to persist; whether a previous session needs correction.
Interactions with other accepted participants. The CR's attendance decisions directly determine what each Student sees on Student Profile. The CR is the only participant who writes attendance; the Student is the only other participant and is a reader of the CR's records.
Observable success. Attendance percentages computed from the database update immediately after each change; low-attendance alerts identify the right students; a session survives connectivity loss and reconciles correctly; the audit log shows every edit; the interface stays legible and operable on a phone in a lecture hall.
Product context. The Student is a member of the class whose attendance is recorded by the CR. They check their standing between or after lectures, usually on a phone, and they have no role in recording attendance.
Primary goal. See an accurate, current picture of their own attendance standing.
Distinct accepted responsibilities. The Student logs in with role-based access and views their own attendance record on Student Profile: overall percentage, subject-wise attendance, and attendance history, all calculated from the database. They may filter their history by subject or date range.
Relevant inputs and decisions. Which subject or date range to inspect; whether their current standing warrants action with the CR outside the product.
Interactions with other accepted participants. The Student is the recipient of the CR's attendance decisions; the Student's view is entirely determined by records the CR created and edited. The Student never edits attendance and never sees other students' records.
Observable success. The overall percentage, subject-wise percentages, and history match the CR's records exactly and reflect the most recent committed changes.
The creative direction is authoritative for this section. The muse is Kenya Hara, and the headline idea is "Emptiness as attendance: paper-white ground, one ink mark per student, nothing that shouts." The register itself is the composition; the product's data is the design.
Light mode
| Role | Hex | Use |
|---|---|---|
| Background | #F7F5F0 | Unbleached paper ground; carries ~85% of every screen |
| Surface | #FFFFFF | The one active sheet (attendance list, voice panel) |
| Text | #23211E | All type and rules (ink) |
| Primary | #2F2C28 | Ink-black slabs; buttons are black with paper-white labels |
| Accent | #B4472E | Persimmon, rationed: the live listening pulse, the current-period marker, the low-attendance flag, destructive confirm |
| Muted | #8C877E | Secondary metadata only; never body copy |
| Hairline | #E3DFD7 | 1px rules that do the separating work cards usually do |
Dark mode — an ink ground, not a colour inversion gimmick: background #1C1A18, surface #23211E, text #F7F5F0, primary #F7F5F0 (paper-white slabs with ink labels), accent #B4472E unchanged, muted #8C877E, hairline #3A3733. Equally quiet.
No blue anywhere in the system — no #0057FF, #2563EB, #4F46E5 or their neighbours, including focus rings. Focus is a 2px ink outline offset 2px.
Status is coded by ink weight and mark, not colour: present = filled 16px ink dot; absent = hollow 1px ring; leave = a single thin diagonal stroke. No red/green traffic lights, no coloured status pills.
-0.01em, sentence case, never all-caps, never bold-shouting — size does the work, not weight.0.14em, uppercase, muted — the only place caps appear.Almost no shape. Hairline 1px rules in #E3DFD7 do the separating work. Where a container is unavoidable it is a plain white rectangle with a 2px radius — square proportions, no pill buttons, no soft-shadow floats. The only permitted curves are the circular student mark (16px filled dot / hollow ring) and the circular voice button (72px mobile, 96px desktop) with a 1px ink ring.
Centred single column on a paper field: max-width 720px for reading, 960px for the attendance sheet, with margins so generous that the first screen holds one idea. Take Attendance is a ruled register, not a card grid: each row is USN (tabular, muted, 96px fixed column) · name · three mark cells, separated by 1px hairlines, 56px tall on mobile for a thumb. The Dashboard is one enormous percentage numeral with a thin rule beneath it and three label/value pairs aligned on a shared baseline; trends are a single hairline sparkline with no filled area and no chart chrome. Section transitions are horizontal rules spanning the full column width. Navigation is a top hairline bar with a wordmark and a two-item ink/paper toggle — no sidebar, no icon soup.
No photography, no illustration, no 3D. The imagery is the interface's own marks: the filled/hollow/diagonal attendance symbols, the hairline register, the hairline sparkline, and a single small hand-drawn ink seal used as the save confirmation. Empty states are a large area of paper with one line of type and one thin rule — emptiness is the content.
The register as the hero. The public entry is not a marketing hero; it is the register itself, composed on a paper-white field.
This concept recomposes only accepted content, states, and controls: the class roster, the present count, the voice listening state, and the attendance marks. It introduces no new behaviour, page, or destination.
Interaction Model: Static Motion Tempo: still Hero Dimensionality: flat
prefers-reduced-motion the expanding ring becomes a static persimmon outline and the pulsing dot becomes a steady fill; crossfades and mark fills resolve instantly.No user-requested 3D/WebGL and no direction-derived webgl dimensionality apply, so no Canvas/R3F/Drei scene is required.
en-IN, continuous listening, push-to-talk/wake-word modes, microphone testing, confidence thresholds, and voice confirmations. (provenance: explicit; rationale: recognition must work in a real Indian lecture hall.)Source-specified choices are preserved exactly:
Kubernetes is not required by any source statement and is not included.
No completed page designs yet.
Completed design pages will appear here when they are ready to preview.
No completed page designs yet.
Completed design pages will appear here when they are ready to preview.
No comments yet. Be the first!