0 / 91
Week 9 · Day 63 of 91

Week 9 frontend milestone

React Frontend Fundamentals

Objective

Combine components, state, forms, effects, and routes into one coherent frontend.

A React component is a reusable functional block with inputs (props), internal state, and rendered output.

  • loading and error UX
  • responsive layout
  • accessibility review

Why this matters

Six days ago you had never written a component. You now have components, props, state, forms, effects, and routes — but they were each learned in isolation. Today you assemble them into one frontend and inspect it the way a reviewer would, before Week 10 connects it to the real API you secured in Week 8.

The work today is mostly finding what is missing: the screen that says nothing while it loads, the filter that empties the list with no explanation, the button nobody can reach with a keyboard.

Every data screen has four states

A screen that fetches data can be in exactly four conditions, and shipping only the fourth is the most common frontend defect there is.

State The user should see
Loading that something is happening, and roughly what
Success the data
Empty that there is genuinely nothing, and what to do about it
Error what failed, and one clear way forward

Day 61 built the branch structure. Today you make each branch say something worth reading.

Loading. Announce it, do not just show a blank gap: <p role="status">Loading equipment…</p>, so a screen reader user hears it too. Keep the layout stable if you can, so the page does not jump when data arrives.

Empty needs two different messages. "No equipment recorded yet" and "No equipment matches your filters" are different facts and want different actions — the first offers Add equipment, the second offers Clear filters. Showing the wrong one makes users think the app lost their data. Distinguish them by asking whether any filter is active:

const isFiltered = query !== "" || status !== "all";

Error. Say what failed, in a sentence, and give a retry. "Could not load equipment. Check your connection and try again." — not a raw exception, and never a silent blank page. Keep the technical detail in console.error for you and plain language on screen for the user. That is the same split Day 54 made on the server: log the detail, return the safe message.

Specify behaviour for every input condition

A design that only defines the output for nominal input is not finished. Real specifications cover out-of-range, open-circuit, and settling time as well, because those conditions occur in the field whether or not you designed for them. Loading, empty, and error are the frontend's out-of-range conditions. They are not edge cases; they are Tuesday.

The shop that never says "sorry, we're out"

A shelf that is simply empty tells you nothing: is the shop closed, is the product discontinued, did you look in the wrong aisle? A card saying "Out of stock — back Thursday" costs the shop one sentence and saves every customer the guesswork.

Responsive layout

Week 2 taught Flexbox and Grid. Nothing about them changes in React — you write the same CSS, you just scope it to components. Three rules carry most of the weight:

Design for the narrow screen first, then add. Start with a single column that works at 360px wide, then use a media query to add columns when there is room. Adding is easier than unpicking.

Let the grid decide how many columns fit. This one line replaces a stack of breakpoints:

.equipment-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}

Every card is at least 16rem wide and shares the leftover space equally; the browser fits as many per row as the container allows. Resize the window and the count changes on its own.

Reflow, do not hide. display: none on a "less important" column at narrow widths removes it for phone users entirely. Stack it below instead. And check that tables and long ids either wrap or scroll inside their own container, rather than pushing the whole page sideways.

Test it honestly: open DevTools' device toolbar (Day 6), set the width to 360px, and use the app. Anything you cannot tap or read there is not done.

The accessibility review

Run this as a procedure, in order, not as a vibe. Each step has an observable result.

  1. Keyboard only. Put the mouse away. Tab through the page. Every control you can click must be reachable, and Enter or Space must operate it. A control you cannot reach is almost always a <div onClick> that should be a <button>.
  2. Focus is visible. At every stop you must be able to see where you are. If a CSS reset removed the outline, put one back.
  3. Tab order follows the page. The sequence should match the reading order. Surprises usually mean the DOM order and the visual order disagree.
  4. Every input has a label. One <label htmlFor> per field, id matching. Placeholder text is not a label — it disappears the moment someone types.
  5. Headings form an outline. One <h1> per page, then <h2> for sections, no skipped levels. Screen reader users navigate by headings the way you skim by eye.
  6. Landmarks exist. <header>, <nav aria-label="Main">, <main>. One <main> per page.
  7. Nothing is signalled by colour alone. Your StatusBadge already carries text, which is why it passes.
  8. Changes are announced. Content that appears without a page change — loading lines, error summaries, "3 of 12 shown" — needs role="status" or role="alert", or a screen reader user never learns it happened.

Five minutes, right now

Do step 1 on your equipment browser: tab from the top of the page to the bottom, saying each control's name out loud as you land on it. Write down every stop where you could not tell what was focused, and every control you skipped past entirely. That list is your first finding set.

Component complexity

Before wiring to a real API, look at the size of what you built. Three signals that a component should be split, all from this week:

  • It holds more than about three pieces of state, and some of them only matter to one part of the markup.
  • It has both a useEffect that loads data and a hundred lines of detailed layout.
  • You scroll to read it.

The split that matters most for next week: keep data access in one module. Put your fetch calls in src/api/equipment.ts and have components call getEquipment() rather than fetch directly. On Day 64 you change one file's URL from /equipment.json to your Express API instead of hunting through components.

Walkthrough

Do a full review pass on one screen — the equipment list — and write findings in the format you would want from a colleague: location, problem, fix.

EquipmentBrowser.tsx — filter clears list with no message.
  Fix: render EmptyState with "No equipment matches your filters" + Clear filters button.

EquipmentCard.tsx — machine name is a <div onClick>; not reachable by Tab.
  Fix: change to <button type="button">.

EquipmentScreen.tsx — error branch prints caught.message raw.
  Fix: plain sentence on screen, console.error for the detail, add a Retry button.

Three findings, three fixes, each verifiable by doing the thing again afterwards. Fix them, then repeat step 1 of the review and confirm the finding is gone. A review you do not re-check is a list, not a fix.

Checkpoint

Force all four states on the equipment screen using the Day 61 techniques — throttle the network, empty the JSON, break the URL — and confirm each one produces a sentence a stranger could act on.

Your turn

Deliverable: a responsive frontend ready to connect to the real API.

  1. Move every fetch into src/api/equipment.ts, exporting getEquipment(): Promise<Equipment[]>. Components import that function and no longer mention URLs.

  2. Make all four states real on the equipment screen, including the two different empty messages and a Retry button on error.

  3. Apply the responsive grid to your card list. Check it at 360px, 768px, and full width.

  4. Run the eight-step accessibility review on every route: dashboard, list, detail, login, not-found. Write each finding as location / problem / fix.

  5. Fix every finding, then run the review again on the routes you changed.

  6. Do one uninterrupted keyboard-only walkthrough of a whole task: from the dashboard, navigate to equipment, filter the list, open one machine's detail page, go back, and add a new item with the form — no mouse at any point. Note where you got stuck.

  7. Confirm the app still builds cleanly:

    npm run build
    

    TypeScript errors fail the build. Fix them rather than loosening the types.

  8. Commit with a message naming what the frontend now does (Day 13).

You are done when

The keyboard-only walkthrough completes end to end, all four states are reachable and readable, the layout survives 360px, and npm run build succeeds.

Reviewer mode — after your own review, not before

Today's mode is reviewer, and the order matters: do your own pass first so you can judge the answers. Ask for specific, actionable findings with evidence from your files — not praise, not a rewrite.

"Review the UI for missing states, unclear actions, inaccessible controls, and component complexity."

Compare its list with yours. Findings you both caught confirm your eye. Findings only it caught go on your personal checklist. Findings only you caught are the reason you review your own work first.

Common pitfalls

  • Treating loading and empty as the same thing. An empty array while loading shows "no equipment" for a second and makes a working app look broken. Check isLoading before items.length === 0.
  • Showing the raw error object. TypeError: Failed to fetch tells the user nothing. Translate it; keep the original in the console.
  • Testing responsiveness by dragging the window a bit. Go to 360px. That is a real phone, and it is where layouts actually break.
  • Calling accessibility done because an automated checker passed. Tools catch missing labels; they cannot tell you the tab order is nonsense. The keyboard walkthrough is the test.

Verify it yourself

Open today's reference, React's Learn section, and find its guidance on conditional rendering and on keeping components pure.

  1. React shows several ways to render conditionally — if, the ternary, and &&. Find where && misbehaves with a number like 0 on the left, and check whether any of your empty-state checks has that bug.
  2. Find what React says about components staying pure, and write one sentence connecting it to why your data loading lives in an effect and your filtering does not.

Record both in notes/day-63.md, then write one sentence on what you expect to break first when this frontend meets the real API tomorrow. Predicting the failure before it happens is how Week 10 stays debuggable.

The hour

  1. 0–5 min Recall

    Without notes, state yesterday’s main idea and one unresolved question.

  2. 5–20 min Learn

    Read only the listed concept notes and official reference sections needed today.

  3. 20–48 min Build

    Finish the frontend using mock data and run a keyboard-only walkthrough.

  4. 48–55 min Explain and verify

    Run the result, inspect evidence, and explain the data/control flow in your own words.

  5. 55–60 min Quiz and commit

    Complete the quiz, record one lesson, and commit the verified change when applicable.

What to hand in

Deliverable

A responsive frontend ready to connect to the real API.

Working with AI today

AI as skeptical reviewer

Provide existing work and ask for concrete defects, risks, missing tests, and unsupported assumptions—not praise.

Review the UI for missing states, unclear actions, inaccessible controls, and component complexity.

References

End-of-day quiz

Q1 Which states should a data screen usually consider?
Q2 Which result best proves today’s work is complete?
Q3 What should an AI code review primarily produce?

Explain-back gate

Pass the quiz above to unlock completion.

Quiz + explain-back checks required.