diff --git a/web/src/FAQ/FAQ.css b/web/src/FAQ/FAQ.css
index ee1b763..c7f1b95 100644
--- a/web/src/FAQ/FAQ.css
+++ b/web/src/FAQ/FAQ.css
@@ -1,28 +1,262 @@
-@import url(..//reset.css);
-@import "tailwindcss";
+@import url(../reset.css);
+@import url(../colors.css);
+@import url(../glass.css);
* {
overscroll-behavior: none;
}
+/* Like the About page and unlike the single-screen homepage, this page scrolls.
+ The noise canvas is position: fixed, so it stays put behind the panels. */
body {
font-family: "Azeret Mono";
margin: 0;
- overflow: hidden;
+ overflow-x: hidden;
+ overflow-y: auto;
}
-#notication-text {
+/* Sit above the fixed noise canvas (z-index: 0). */
+.faq {
+ position: relative;
+ z-index: 1;
+ max-width: 1100px;
+ margin: 0 auto;
+ padding: clamp(2rem, 6vh, 4rem) 5vw clamp(3rem, 8vh, 5rem);
+ display: flex;
+ flex-direction: column;
+ gap: clamp(1.25rem, 3vh, 2rem);
+}
+
+/* --- Hero: the HELP label above the wordmark, matching the About page ------ */
+
+.faq__head {
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: clamp(0.5rem, 2vh, 1.25rem);
+ margin-bottom: clamp(0.5rem, 2vh, 1.5rem);
+}
+
+.faq__eyebrow {
font-family: "Monomaniac One";
- font-size: 96px;
+ font-size: clamp(24px, 4vh, 40px);
text-transform: uppercase;
+ letter-spacing: 0.35em;
+ /* letter-spacing pads the right edge; pull it back to stay optically centered */
+ text-indent: 0.35em;
+ color: var(--color-white);
+}
+
+/* Same size as the About page's wordmark so the two read as sibling pages. */
+#logo-faq {
+ width: min(92vw, 620px);
+}
+
+.faq__tagline {
+ max-width: 46ch;
+ line-height: 1.6;
+ color: var(--color-white);
+ opacity: 0.85;
}
-.bottom-page {
- width: 100vw;
- height: 94vh;
+/* --- Panels --------------------------------------------------------------- */
+
+.faq__panel {
+ padding: clamp(1.5rem, 3.5vh, 2.25rem) clamp(1.25rem, 3vw, 2rem);
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.faq__h2 {
+ font-family: "Monomaniac One";
+ font-size: clamp(22px, 3.2vh, 30px);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: var(--color-white);
+ padding-bottom: 0.6rem;
+ border-bottom: 2px solid var(--color-process-cyan);
+ /* Let the accent rule stop at the text rather than span the panel. */
+ align-self: flex-start;
+}
+
+/* --- Accordion ------------------------------------------------------------ */
+
+.faq__items {
+ display: flex;
+ flex-direction: column;
+}
+
+/* Hairline separators between rows, but not above the first one. */
+.faq__item + .faq__item {
+ border-top: 1px solid rgba(255, 255, 255, 0.12);
+}
+
+.faq__q {
+ width: 100%;
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 1rem 0.5rem;
+ background: none;
+ border: none;
+ border-radius: 10px;
+ cursor: pointer;
+ text-align: left;
+ font-family: "Azeret Mono";
+ font-size: 1rem;
+ line-height: 1.5;
+ color: var(--color-white);
+ transition: background 0.15s ease;
+}
+
+.faq__q:hover {
+ background: rgba(255, 255, 255, 0.06);
+}
+
+/* Nothing else in the app styles focus, and this page is meant to be usable
+ from the keyboard, so make the ring unmistakable. */
+.faq__q:focus-visible {
+ outline: 2px solid var(--color-process-cyan);
+ outline-offset: 2px;
+}
+
+.faq__q-arrow {
+ flex: none;
+ color: var(--color-process-cyan);
+ /* Nudge onto the first line of a question that wraps to two. */
+ line-height: 1.5;
+}
+
+.faq__a {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ padding: 0 0.5rem 1.25rem;
+ line-height: 1.65;
+ opacity: 0.92;
+}
+
+/* reset.css strips list styling globally, so put it back inside answers. */
+.faq__a ul {
+ list-style: disc outside;
+ padding-left: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.faq__a code {
+ font-family: "Azeret Mono";
+ font-size: 0.875em;
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.14);
+ border-radius: 6px;
+ padding: 0.1em 0.4em;
+ /* Long inline code (the special-character list) must not widen the page.
+ When it does wrap, give each fragment its own border and rounding rather
+ than leaving the chip sliced open at the line break. */
+ overflow-wrap: break-word;
+ -webkit-box-decoration-break: clone;
+ box-decoration-break: clone;
+}
+
+.faq__a strong {
+ font-weight: 700;
+ color: var(--color-white);
+}
+
+.faq__a em {
+ font-style: italic;
+}
+
+/* Caveats and gotchas: a cyan edge marks "read this bit" without shouting. */
+.faq__a .faq__warn {
+ border-left: 3px solid var(--color-process-cyan);
+ padding-left: 0.85rem;
+}
+
+/* --- Expand all ----------------------------------------------------------- */
+
+.faq__controls {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.faq__toggle-all {
+ background: none;
+ border: none;
+ padding: 0.25rem;
+ cursor: pointer;
+ font-family: "Azeret Mono";
+ font-size: 0.875rem;
+ /* This control sits on the bare noise field rather than on a glass panel,
+ where cyan-on-cyan gets thin; the dark halo keeps it readable. */
+ color: var(--color-white);
+ text-decoration: underline;
+ text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
+}
+
+.faq__toggle-all:hover {
+ color: var(--color-aero-blue);
+}
+
+.faq__toggle-all:focus-visible {
+ outline: 2px solid var(--color-process-cyan);
+ outline-offset: 2px;
+}
+
+/* --- Closing note and footer ---------------------------------------------- */
+
+.faq__note {
+ max-width: 70ch;
+ margin: 0 auto;
+ text-align: center;
+ line-height: 1.65;
+ font-size: 0.9375rem;
+ color: var(--color-white);
+ opacity: 0.8;
+}
+
+.faq__footer {
display: flex;
flex-direction: column;
- flex-wrap: wrap;
align-items: center;
+ gap: 1.5rem;
+ padding-top: clamp(1rem, 3vh, 2rem);
+}
+
+.faq__cta {
+ text-decoration: none;
+}
+
+.faq__cta .blueButton {
+ padding: 0.9rem 2.75rem;
+}
+
+.faq__links {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
justify-content: center;
-}
\ No newline at end of file
+}
+
+/* --- Narrow screens ------------------------------------------------------- */
+
+@media (max-width: 800px) {
+ .faq__eyebrow {
+ letter-spacing: 0.2em;
+ text-indent: 0.2em;
+ }
+
+ .faq__q {
+ font-size: 0.9375rem;
+ padding: 0.875rem 0.25rem;
+ }
+
+ .faq__a {
+ padding: 0 0.25rem 1rem;
+ }
+}
diff --git a/web/src/FAQ/FAQ.html b/web/src/FAQ/FAQ.html
index d19af5a..6c83dac 100644
--- a/web/src/FAQ/FAQ.html
+++ b/web/src/FAQ/FAQ.html
@@ -3,14 +3,14 @@
-
+
- UDCS Autograder
+ Help / FAQ · UDCS Autograder
diff --git a/web/src/FAQ/FAQ.tsx b/web/src/FAQ/FAQ.tsx
index 9321711..0acba82 100644
--- a/web/src/FAQ/FAQ.tsx
+++ b/web/src/FAQ/FAQ.tsx
@@ -1,14 +1,579 @@
-import { StrictMode } from 'react'
+import { StrictMode, useEffect, useState } from 'react'
import { createRoot } from 'react-dom/client'
import Navbar from "../components/navbar/Navbar"
+import Logo from "../components/logo/Logo"
+import NoiseBackground from "../components/background/NoiseBackground"
+import BlueButton from "../components/buttons/BlueButton"
+import FaqItem from './FaqItem'
import './FAQ.css'
+/* Answers are JSX rather than strings because several need inline code, lists,
+ or more than one paragraph. Sections render in order as glass panels. */
+const SECTIONS = [
+ {
+ title: "Getting started",
+ items: [
+ {
+ q: "I got an email inviting me to Autograder. What is this?",
+ a: <>
+
+ Autograder is the platform your instructor uses for programming
+ assignments. You write your solution in the browser, submit it, and it
+ is run against the instructor's test cases and graded automatically —
+ usually within seconds.
+
+
+ The email means an instructor has added you to one of their courses.
+ Follow the link in it to create your account, and your classes will
+ already be waiting for you.
+
+ >,
+ },
+ {
+ q: "How do I get an account?",
+ a: <>
+
+ You cannot sign yourself up. An instructor or an administrator invites
+ you by email address, and that email contains a link that creates your
+ account.
+
+
+ Invitation links are valid for 7 days. If yours has
+ expired, ask your instructor to have a new one sent.
+
+ >,
+ },
+ {
+ q: "My invitation link says \"Invalid invite\".",
+ a: <>
+
That means the link is no longer usable. The usual reasons:
+
+
It is more than 7 days old.
+
You have already used it to create your account — try signing in instead.
+
+ You were invited more than once. If you received two invitation
+ emails, only the first link works.
+
+
+
Ask your instructor to check with an administrator and re-issue it.
+ >,
+ },
+ {
+ q: "What are the password rules?",
+ a: <>
+
+ Between 8 and 64 characters, with at least one uppercase letter, one
+ lowercase letter, one digit, and one special character.
+
+ Watch out: -=[]{" "}
+ \| and ` do not{" "}
+ count as special characters. A password like Passw0rd- will
+ be rejected even though it looks like it should qualify.
+
+ >,
+ },
+ {
+ q: "I forgot my password.",
+ a: <>
+
+ Use Forgot Password? on the sign-in page. If you are
+ already signed in, there is a Reset Password button in
+ Account Settings.
+
+
+ You will get an email with a reset link. It is valid for 7 days and can
+ only be used once. For your privacy the confirmation message looks the
+ same whether or not an account exists for that address, so it is not a
+ way to check whether someone is registered.
+
+ >,
+ },
+ {
+ q: "Why was I signed out?",
+ a:
+ Sessions are short-lived, so a tab left open for a while will ask you to
+ sign in again. Your work is not lost — code you have typed is saved
+ automatically as you go.
+
,
+ },
+ ],
+ },
+ {
+ title: "Using Autograder as a student",
+ items: [
+ {
+ q: "How do I join a class?",
+ a: <>
+
+ You don't, and there is no join code to enter. Your
+ instructor adds you to the class by email address, and it appears on
+ your dashboard automatically.
+
+
+ If a class is missing, the usual cause is an address mismatch: your
+ enrolment only connects if you registered with the exact address your
+ instructor typed. Check with them which one they used.
+
+ >,
+ },
+ {
+ q: "Where are my assignments?",
+ a: <>
+
+ Your dashboard groups your classes into Enrolled (term
+ hasn't started), Active, and{" "}
+ Expired, based on the dates your instructor set. Open a
+ class to see its assignments, and click one to open its questions.
+
+
+ You only see assignments the instructor has published. Anything still in
+ draft is invisible to you.
+
+ >,
+ },
+ {
+ q: "Do I have to save my work?",
+ a: <>
+
+ No — there is no Save button. Your code is saved automatically a few
+ seconds after you stop typing, and periodically while you type.
+ Submitting saves it too.
+
+
+ There is no "saved" indicator, so give it a moment before closing the
+ tab on something you just finished typing.
+
+ >,
+ },
+ {
+ q: "Can I run my code before submitting it?",
+ a:
+ Not currently. Submitting is the only way to run your code, and there is
+ no scratch pad for trying your own inputs. Since there is no limit on
+ submissions, submitting is how you test — but you will get more
+ out of it by running your code locally first.
+
,
+ },
+ {
+ q: "Can I choose which language to use?",
+ a:
+ No. Each question is set to one language by your instructor, shown above
+ the editor as Language: python. Autograder supports Python,
+ C, Java, and Racket, but which one a given question uses is not your
+ choice.
+
,
+ },
+ {
+ q: "How many times can I submit?",
+ a: <>
+
As many times as you like. There is no attempt limit and no cooldown.
+
+ But each submission replaces your previous result,
+ including a worse one. Autograder does not keep your best attempt, so
+ don't submit an experiment you haven't checked.
+
+ It replaces everything in the editor with the starter code your
+ instructor provided — immediately, with no confirmation prompt. Your work
+ is not recoverable afterwards, so copy it somewhere first if you might
+ want it back.
+
,
+ },
+ {
+ q: "Can I see my earlier submissions?",
+ a:
+ Not at the moment. You see the score and feedback from your most recent
+ submission. Your instructor can see the full history of your attempts, so
+ ask them if you need to know what an earlier one scored.
+
,
+ },
+ {
+ q: "What happens if I submit after the deadline?",
+ a: <>
+
+ Late submissions are accepted and graded normally. Nothing is blocked,
+ and Autograder does not deduct any marks by itself — but your instructor
+ sees the submission flagged as late, and how they handle that is up to
+ them.
+
+
+ Assignments are due at 11:59:59 pm US Central time on
+ the date shown in your class sidebar.
+
+ >,
+ },
+ ],
+ },
+ {
+ title: "Understanding your results",
+ items: [
+ {
+ q: "How is my code graded?",
+ a:
+ Each test case runs your program once, feeding that test case's input to
+ it on standard input, then compares what your program prints to the
+ expected output. Each test case is worth points, and your score for the
+ question is the total of the ones that pass.
+
Before comparing, Autograder tidies both sides a little. It ignores:
+
+
Windows vs. Unix line endings.
+
Trailing spaces and tabs at the end of a line.
+
Blank lines at the very start or very end of your output.
+
+
Everything else has to match exactly. In particular these all matter:
+
+
Spaces inside a line — 1 2 3 is not 1 2 3.
+
Upper and lower case.
+
Blank lines in the middle of your output.
+
+ >,
+ },
+ {
+ q: "My output looks right but the test still failed.",
+ a: <>
+
Three causes account for almost all of these:
+
+
+ Invisible whitespace inside a line — a double space,
+ or a tab where a space was expected.
+
+
+ Your program exited with an error status. A test fails
+ even with perfect output if the program didn't finish cleanly — an
+ uncaught exception, sys.exit(1), or{" "}
+ return 1 from main all count.
+
+
+ Too much output. Anything past about a megabyte is cut
+ off, which then reads as a wrong answer. Remove debug printing before
+ you submit.
+
+
+ >,
+ },
+ {
+ q: "What does a hidden test case show me?",
+ a: <>
+
+ Only the verdict and the points, like{" "}
+ Test "edge cases" (hidden): FAILED (0/10 points). The
+ input, the expected output, and your output are all withheld.
+
+
+ One consequence worth knowing: if every test case on a question is
+ hidden, error messages and compile errors are withheld too, so a failing
+ submission gives you nothing to go on. If you are stuck with no
+ explanation at all, ask your instructor to make one test case visible.
+
+ >,
+ },
+ {
+ q: "Why does it say Timeout?",
+ a:
+ Your program ran longer than the time limit for that test case. Each test
+ case has its own limit, set by your instructor, so a slow or looping
+ program only loses the cases it actually hits — the rest still run and
+ still count.
+
,
+ },
+ {
+ q: "Are there gotchas for particular languages?",
+ a: <>
+
+
+ Java — your public class must be named{" "}
+ Main. Any other name is a compile error.
+
+
+ Racket — if you leave out #lang racket{" "}
+ it is added for you. Don't put a comment above it, though, or you will
+ end up with two #lang lines.
+
+
+ C — compiled with gcc -O2 and linked
+ against the maths library, so you don't need to do anything special to
+ use math.h.
+
+
+ Python — run with python3. Only the
+ standard library is available.
+
+
+ >,
+ },
+ {
+ q: "My submission says \"error\". What did I do?",
+ a:
+ Nothing — that status means the grader itself could not run your
+ submission, most often because the question has no test cases yet. It is
+ not a verdict on your code. Let your instructor know.
+
,
+ },
+ ],
+ },
+ {
+ title: "For instructors and TAs",
+ items: [
+ {
+ q: "How do I create a course?",
+ a:
+ Use + Create New Classroom on your dashboard — available
+ to instructors and administrators. You set a name, a course code (up to 16
+ characters), start and end dates, and a description. A blank assignment is
+ created alongside it so you have somewhere to start.
+
,
+ },
+ {
+ q: "How do I add students?",
+ a: <>
+
+ Manage Classroom → Students →{" "}
+ + Add Student, then type an email address and save.
+ Students who don't have an account yet are invited automatically and
+ join the class the moment they register, so you can build a full roster
+ before term starts.
+
+
+ Two limitations to plan around: it is one address at a time — there is
+ no CSV import or bulk paste — and a pending enrolment only connects if
+ the student registers with the exact address you entered.
+
+ >,
+ },
+ {
+ q: "How do I add a co-instructor?",
+ a:
+ Not through the interface, currently. The classroom roster only offers the
+ student and assistant roles. Granting someone the instructor role has to
+ be done by an administrator through the API.
+
,
+ },
+ {
+ q: "What can a TA (assistant) do?",
+ a:
+ Less than intended, at the moment. Assistants are granted most instructor
+ permissions by the server, but the Manage Classroom screens are currently
+ restricted to instructors and administrators — so in practice an assistant
+ cannot manage a classroom today. Note also that assistants appear in the
+ gradebook alongside students.
+
,
+ },
+ {
+ q: "How do I publish an assignment?",
+ a:
+ Each assignment has a visibility setting: Draft or{" "}
+ Visible. Draft assignments are invisible to students and
+ cannot be opened even with a direct link, so you can build one in the open
+ without anyone seeing it. Switch it to Visible to release it.
+
,
+ },
+ {
+ q: "How do points work?",
+ a:
+ Points are set per test case, and a question is worth the
+ total of its test cases. There is no separate question-level points field.
+ A question with no test cases is worth nothing and will report an error
+ when a student submits, so add at least one before publishing.
+
,
+ },
+ {
+ q: "Do I have to type the expected output by hand?",
+ a: <>
+
+ No. Write a reference solution in the Solution tab,
+ then use Generate Expected Output to run it against a
+ test case's input and fill in the expected output for you. It asks
+ before overwriting anything you have already entered.
+
+
+ Run Tests on Solution checks your solution against every
+ test case at once — a good sanity check before publishing. Neither
+ button touches student grades, though both do save the question.
+
+ >,
+ },
+ {
+ q: "Can I export grades to a spreadsheet?",
+ a:
+ Not currently — there is no CSV or spreadsheet export. Grades are viewable
+ in Manage Classroom → Grades, which you can pivot by
+ assignment or by student.
+
,
+ },
+ {
+ q: "Can I change a grade by hand?",
+ a:
+ Yes. On a student's row, tick Manual Grade, enter a score,
+ and press Update Grade. The automatically computed score
+ is kept underneath, so unticking the box restores it — an override is never
+ destructive.
+
,
+ },
+ {
+ q: "Can I re-run grading for a student?",
+ a:
+ Yes, with Resubmit Code on their row. It re-grades their
+ current code and updates the score, but deliberately does not add an entry
+ to their submission history or flag them as late, so your re-runs never
+ look like their attempts.
+
,
+ },
+ {
+ q: "Can I duplicate an assignment or copy one between classes?",
+ a:
+ Not yet. Test cases can be copied within a question, but there is no
+ duplicate for assignments or questions, no copying between classrooms, and
+ no assignment templates.
+
,
+ },
+ {
+ q: "Can I write shell-script test cases?",
+ a:
+ No. Autograder currently supports one kind of test case: supply input,
+ compare the program's output to what you expect. Multi-line input is fine,
+ and each test case has its own points, time limit, and hidden setting.
+
,
+ },
+ {
+ q: "Does Autograder email students about anything else?",
+ a:
+ No. It sends exactly two kinds of email: account invitations and password
+ resets. There are no deadline reminders, no "your submission has been
+ graded" notices, and no grade notifications — so tell your class where to
+ look rather than expecting Autograder to nudge them.
+
,
+ },
+ {
+ q: "What can't I delete?",
+ a: <>
+
+ An assignment must always have at least one question, and a question at
+ least one test case, so the delete option is unavailable when only one
+ is left. The same applies to the last assignment in a classroom.
+
+
+ Deleting an assignment permanently destroys the code, grades, and
+ submission history your students accumulated under it. There is no undo.
+
+ Still stuck? Ask your instructor — they can see your submissions and
+ test results in full detail, including the parts hidden from you.
+ Instructors: contact your department administrator.
+
+ ,
-)
\ No newline at end of file
+)
diff --git a/web/src/FAQ/FaqItem.tsx b/web/src/FAQ/FaqItem.tsx
new file mode 100644
index 0000000..a9827c9
--- /dev/null
+++ b/web/src/FAQ/FaqItem.tsx
@@ -0,0 +1,36 @@
+import { useId } from "react";
+
+/* One question/answer row. Open state is owned by the page rather than this
+ component so the "Expand all" control can drive every row at once.
+
+ A real