Guide · Frontend
Frontend System Design Interview: A Five-Step Framework
28 min readUpdated By Saket Bhatnagar
Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.
In short
- A frontend system design interview asks you to design, out loud, the client side of a product: the part that runs in the user's browser. A typical prompt is a shopping search page.
- Work in five steps: requirements, components, data and state, APIs, then speed and accessibility. RADIO, a prep framework published by GreatFrontEnd, uses the same order under different names.
- Interviewers score trade-offs, not library names. For each big choice, say what you picked, what you rejected and why, because a reason you never say out loud earns nothing.
- Finish with goals you can measure, such as Core Web Vitals, Google's three scores for loading, tap response and layout jumps. Good means 2.5 seconds, 200 milliseconds and a jump score of 0.1 or less.
- Beginners can stop after the accessibility checks and skip to the 45-minute plan. Engineers then get traps we tested in Chromium, the open-source browser behind Chrome: a cached answer overwritten by an older search in 20 of 20 runs.
What goes wrong when you start a frontend design round by drawing components?
You end up designing for guesses. Questions about users, devices and scale shape every later choice. Skip them, and the interviewer asks them later, when your plan has no room left. This round rewards structure more than coding speed.
Picture a common setup. An engineer with three years of building dashboards in React, a JavaScript library for user interfaces, gets a one-line prompt: design the search results page for a shopping app. Within a minute, they are drawing a component tree. The setup is made up, but the gaps it exposes are the ones this round scores.
Twenty minutes in, the interviewer asks three things the plan never covered. How many products can one search return? What happens on a slow connection? How does a screen reader user, someone who hears the page read aloud by software, know the results changed?
Each question points at a step the candidate skipped: requirements, performance and accessibility. This guide builds that same search page step by step, the way you would in the room.
What is a frontend system design interview?
A frontend system design interview is a discussion where you design the client side of a product: the part running in the user's browser or app. You cover what users need, how the screen splits into parts, where data lives, how it reaches the server and how it stays fast.
Prompts are a whole product or one complex widget. GreatFrontEnd's question list, as of 19 October 2026, includes a news feed, autocomplete, a chat app, an image carousel, a rich text editor and an e-commerce marketplace. You draw boxes on a whiteboard or an online canvas, and you may write a few lines of code.
It is not a machine coding round. In machine coding, you build one working widget, such as an OTP (one-time password) input box, within a fixed time. In a design round, you explain how a whole page works and why you built it that way. If the invite only says frontend design, ask the recruiter which kind it is.
Our advice is to plan to drive the round yourself, especially for mid-level roles such as SDE-2 (Software Development Engineer 2) and above. Lead, pick a direction without waiting for hints, and check in with the interviewer at the end of each step.
How is frontend system design different from backend system design?
Frontend system design focuses on what runs on the user's device: components, state, network calls, rendering and user experience. Backend system design focuses on servers, databases, queues and spreading work across many machines.
The same prompt can lead to two very different rounds. Asked to design a news feed, a backend candidate talks about copying each new post into followers' feeds and splitting the database. A frontend candidate talks about endless scrolling, image loading and keeping the feed smooth on a cheap phone.
| Topic | Backend design round | Frontend design round |
|---|---|---|
| Main question | How do servers handle millions of searches? | How does one page stay fast and easy to use? |
| What scale means | Requests per second and data size | Items on screen, phone memory, slow networks |
| Data | Database tables, indexes, splitting data | Client state, cache and URL parameters |
| APIs | Designing endpoints (the server URLs a page calls) for many clients | Using endpoints well: pages, cancelling, retries |
| Performance | Server response time and load | Load time, tap response, layout jumps |
| Users with disabilities | Usually out of scope | Expected: keyboard, screen readers, contrast |
| Failures | Server crashes and backup servers | Offline, slow networks, error and empty states |
What is a good framework for a frontend system design interview?
A good framework is five steps in a fixed order: requirements, components, data and state, APIs, then speed and accessibility. GreatFrontEnd's RADIO framework uses the same order: requirements exploration, architecture, data model, interface definition and optimisations.
The order matters because each step feeds the next. You cannot choose components before you know what the page must do. You cannot place state before you know which components need it.
Think of an interior designer planning a 2BHK flat, one with two bedrooms, a hall and a kitchen. She does not start by ordering a sofa. Each part of her work maps to one step.
- Meeting the family and learning how they live is requirements.
- The floor plan, with one main job per room, is components.
- The wiring plan is data and state. Moving a switchboard after plastering means breaking a finished wall, just as moving state later means rewriting components.
- The water and power lines from the building are the APIs. She connects to them but does not build them.
- Lights, ventilation and a ramp at the door are speed and accessibility.
Where the picture breaks: a flat is planned once, but an interviewer can change a requirement halfway through. Then you go back to step one for a minute, not to the start.
The diagram shows the five steps with suggested times for a 45-minute slot. Follow the arrows from left to right, and notice the question box near the end.
Five steps for a 45-minute frontend round
Drag to exploreThese timings are this guide's suggestion, and they match the plan near the end. A new requirement sends you back to step one for a minute, not to the start.
Diagram connections:
- 1. Requirements to 2. Components
- 2. Components to 3. Data and state
- 3. Data and state to 4. APIs
- 4. APIs to 5. Speed and accessibility
- 5. Speed and accessibility to New requirement?
- New requirement? to 1. Requirements (Yes, revisit briefly)
- New requirement? to Wrap up (No)
- 1. Requirements: About 7 min: features, users, phones, scale
- 2. Components: About 9 min: screen sketch and component tree
- 3. Data and state: About 7 min: what is stored, where, in what shape
- 4. APIs: About 7 min: endpoints, pages, errors, cancelling
- 5. Speed and accessibility: About 12 min: targets, techniques, checks
- New requirement?: For example, the page must work offline
- Wrap up: About 3 min: main trade-offs and next steps
Other guides split the time differently. GreatFrontEnd's RADIO page, as of 19 October 2026, suggests no more than 10% of the session on requirements and roughly 40% on optimisations and deep dive. Treat any split as a starting point, and follow the interviewer's interest.
How do you gather requirements in a frontend design round?
You gather requirements by asking short questions about features, users, devices, network and scale, and writing the answers on the board. Spend about five to seven minutes here, and agree on what is out of scope.
For the shopping search page, the candidate now asks before drawing. The answers go into two columns.
Functional requirements: what the page does
Functional requirements describe the features. Confirm each one with the interviewer and write it down.
- Users type a query and see matching products as a grid of cards with image, name, price and rating.
- Users filter by price, brand and rating, and sort by price or relevance.
- More results load as the user scrolls, with no page numbers.
- Tapping a card opens the product page, and the back button returns to the same scroll spot.
- Out of scope for today: checkout, reviews and the seller dashboard.
Non-functional requirements: how well it must work
Non-functional requirements describe qualities such as speed, reliability, accessibility and reach. They shape the rest of the round, so they get their own column.
- Most users are on mid-range Android phones, often on patchy mobile data.
- A popular query, such as running shoes, can match 50,000 products (a made-up number for this exercise).
- The page must work with a keyboard alone and with a screen reader.
- Search engines should be able to read category pages, which affects where the page is rendered.
- The app is in English and Hindi, and prices show in rupees.
How do you break a page into components in a frontend design?
You break a page into components by sketching the screen, boxing each area that has one job, and deciding which boxes own data. Keep the tree shallow, and point out the parts other pages can reuse.
A component is a self-contained piece of the interface, like a product card. It takes inputs called props: values a parent component passes down to a child. Sketch the phone screen first, then box it from the outside in.
The page shell is the part every screen shares: header, search box and layout. In the diagram, look at which boxes talk to the data store and which only receive props.
Components of the shopping search page
Drag to exploreOnly the store calls the search API. Cards just display the props they are given, which makes them easy to test and reuse.
Diagram connections:
- Page shell to Search bar
- Page shell to Filter panel
- Page shell to Results grid
- Results grid to Product card (Product as props)
- Search bar to Search data store (Query changed)
- Filter panel to Search data store (Filters changed)
- Search data store to Results grid (Results)
- Search data store to Search API (Fetch when not cached)
- Product card to Image CDN (Image request)
- Page shell: Header, layout, shared on every screen
- Search bar: Holds typed text, waits for a pause
- Filter panel: Price, brand, rating; saved in the URL
- Results grid: Shows the cards; loads more on scroll
- Product card: Image, name, price, rating; props only
- Search data store: Caches results by query, filters and sort
- Search API: Returns one page of products at a time
- Image CDN: Nearby servers that send images resized for phones
Three choices here are worth saying out loud. The product card takes only props, so the cart and wishlist pages can reuse it. The filters live in the URL, so a shared link opens the same results. Only the store calls the API, so components share one cached copy instead of each fetching their own.
Where should state and data live in a frontend app?
State should live in the smallest place that every part using it can reach. Keep it inside a component when only that component needs it, move it up when siblings share it, and keep server data in a cache layer.
State is any data that can change while the page is open and that the screen depends on. Sort the search page's state into four kinds, because each kind has a natural home.
- Local UI state, such as whether the filter drawer is open. Only the panel cares, so it stays inside the panel.
- URL state: the query, filters and sort order. Keeping them in the URL makes links shareable and the back button work.
- Server cache: the product results. The server owns them, so a cache layer, a nearby store of copies, keeps, refreshes and reuses them.
- Form state: the text being typed, before it becomes a real search after a short pause.
The back-button requirement needs one more piece. Keep the loaded pages and the scroll position in the store, keyed by the search URL, and restore both when the user comes back.
Next, define the data model: the shapes of the data the page keeps. A few lines of TypeScript, which is JavaScript with type labels, make the talk concrete.
type Product = {
id: string;
title: string;
pricePaise: number; // whole paise, never fractional rupees
imageUrl: string;
rating: number | null; // null when there are no reviews yet
};
// Key built from query + filters + sort, e.g. "shoes|brand=x|price-asc"
type SearchKey = string;
type SearchCache = Record<SearchKey, {
productIds: string[];
nextCursor: string | null;
fetchedAt: number; // ms timestamp, used to decide if it is stale
}>;
type ProductsById = Record<string, Product>;Three lines matter. First, prices are whole paise (100 paise make a rupee), because decimals drift. In JavaScript, 10.10 + 20.20 gives 30.299999999999997, while 1010 + 2020 gives exactly 3030. Whole paise stay exact for sums and quantities, but percentage discounts still need one agreed rounding rule.
Second, each search keeps only product ids, while full products live once in ProductsById. Third, the fetchedAt time lets the store decide when cached results are too old to show.
Storing each product once and pointing to it by id is called normalising. One pair of shoes can appear in three searches. If its price changes, you update one record, and every search shows the new price.
Which API questions come up in a frontend system design round?
Expect questions on the response shape, pagination, request races, caching, errors and live updates. You rarely design the server itself, but you must use its API well and say what you need from it.
Pagination: offsets or cursors
Pagination means fetching a long list one page at a time. With offset pagination, you ask for items 41 to 60. With cursor pagination, the server returns a cursor, a bookmark you send back to get the next page.
Pick cursors for a list that changes while people scroll. Sellers add and remove products all day. New products push the rest down and deleted ones pull them up, so offset pages repeat or skip items. The engineer sections below measure how many.
Request races and cancelling
A race condition happens when two requests are in flight and the older one finishes last. Say a user types sho, then shoes. If the reply for sho arrives second, the grid shows results for the wrong query.
Use two guards. A debounce waits until typing pauses, for about 300 milliseconds, before it searches at all. Then an AbortController, a built-in browser object that cancels a request, stops the older search.
Cancelling makes the browser stop waiting for that reply, so the promise fetch returned (its pending result) rejects. It does not undo work the server has already started: in our test, the server's handler still ran to the end. The diagram follows one search through both guards.
One search, from keystroke to screen
Drag to exploreNotice that cancelling comes before the cache check. If a cache hit skipped it, an older search could still land and overwrite the right results.
Diagram connections:
- User types shoes to Wait for a typing pause
- Wait for a typing pause to Cancel the older search
- Cancel the older search to Fresh results in cache?
- Fresh results in cache? to Show cached results (Cache hit)
- Fresh results in cache? to Fetch the first page (Cache miss)
- Fetch the first page to Save and show results
- User types shoes: Each keystroke updates the search bar
- Wait for a typing pause: A debounce of about 300 ms
- Cancel the older search: Always first, even when the cache can answer
- Fresh results in cache?: Store looks up the query key
- Show cached results: No network call at all
- Fetch the first page: Search API returns a page and a cursor
- Save and show results: Store saves it, then the grid redraws
Here is the fetch part. It reuses the Product type from earlier. The params value holds the query, filters, sort and cursor, already built by the store.
type SearchPage = { products: Product[]; nextCursor: string | null };
let inFlight: AbortController | null = null;
async function fetchSearch(params: URLSearchParams): Promise<SearchPage | null> {
inFlight?.abort(); // cancel the older search
const controller = new AbortController();
inFlight = controller;
try {
const res = await fetch(`/api/search?${params}`, { signal: controller.signal });
if (!res.ok) throw new Error(`Search failed with status ${res.status}`);
return (await res.json()) as SearchPage;
} catch (err) {
if (err instanceof DOMException && err.name === "AbortError") return null;
throw err; // a real failure: show retry
} finally {
if (inFlight === controller) inFlight = null;
}
}The first line inside the function cancels the previous search. When abort() is called with no reason, as here, fetch rejects with a DOMException, the browser's built-in error type, named AbortError. The catch returns null for that case, meaning replaced, not broken. Any other error goes up to the caller, which shows a retry button.
Errors and live updates
When the API fails halfway through scrolling, keep the results already loaded and show a small retry card at the bottom. Retry on your own only once. Every list also gets a loading state and an empty state.
For price and stock changes, a quick refresh when the user returns to the tab is usually enough. Updates pushed live from the server suit chat or live scores better.
How do you cover performance and accessibility in a frontend interview?
Cover performance by naming measurable goals, such as the Core Web Vitals, and the techniques that meet them. Cover accessibility by explaining keyboard use, screen reader support and contrast for your specific page.
Performance goals you can measure
Core Web Vitals are three user-focused scores documented on web.dev, Google's site for web developers. Largest Contentful Paint (LCP) is the time from the start of loading until the biggest image or text block in view is drawn. Good is 2.5 seconds or less.
Interaction to Next Paint (INP) measures how long the page takes to show a response after taps, clicks and key presses during the visit. Good is 200 milliseconds or less. Cumulative Layout Shift (CLS) scores how much visible content jumps unexpectedly, and good is 0.1 or less.
Each target applies at the 75th percentile of page loads, segmented across mobile and desktop, so three in four visits should meet it. INP replaced an older measure, First Input Delay, as a Core Web Vital on 12 March 2024. These thresholds are current as of 19 October 2026.
Techniques that meet those goals
Tie each technique to the goal it helps, because that link is what the interviewer listens for.
- Render the first page of results on the server, so products arrive in the first HTML instead of waiting for JavaScript. This often helps LCP, and search engines get the products without running your code.
- Give every product image a width and height, so the browser reserves the space before the image arrives. This protects CLS.
- Lazy-load images below the first screen by setting the img element's loading attribute to lazy. Keep the first row loading normally, because web.dev warns against lazy-loading images that are visible at load.
- Serve images from a CDN, a network of servers near users, resized for phones.
- Use the Intersection Observer API, a browser feature that reports when an element scrolls into view, to load the next page. It avoids running your own code on every scroll event.
- Virtualise the grid: keep only the cards near the screen in the DOM, the browser's tree of page elements. Fewer elements mean less style and layout work after each tap, which helps INP. The cost: cards off the screen are not in the page, so the browser's find cannot see them. Keep the result count announced for screen readers.
- Split the code, so the filter drawer's JavaScript downloads only when someone opens it.
Accessibility checks for this page
Accessibility means people with disabilities can use the page, including people who use only a keyboard or a screen reader. The usual reference is WCAG 2.2, the W3C's accessibility guidelines, in its version of 12 December 2024. Name checks that fit this page, not a generic list.
- Every action works with a keyboard alone, from the filters to the cards. This is success criterion 2.1.1, at WCAG's most basic level, Level A.
- Normal text has a contrast ratio of at least 4.5 to 1 against its background, and large text at least 3 to 1. This is Level AA.
- Tap targets such as filter chips are at least 24 by 24 CSS pixels, the size unit your styles use. Smaller targets can still pass with enough space around them, among a few other exceptions. This is Level AA too.
- When results change, a live region, an area that screen readers announce without moving focus, reads out the new count. WCAG calls this a status message (4.1.3, Level AA).
- If the filter drawer opens as a dialog, closing it returns keyboard focus to the button that opened it, as the W3C's dialog pattern recommends.
Why can a cache hit show the wrong search results?
Because a cache hit returns early, and an early return can skip the cancel step. The older request is still in flight, and when it lands, it overwrites the right results.
We ran it in headless Chromium 141, meaning the browser ran without a window, driven by Playwright 1.56.0. The test server answered sneakers after 600 milliseconds (a made-up delay). The grid showed shoes, then sneakers replaced it, in 20 of 20 runs.
The fix is to cancel first, every time, then look in the cache. The same 20 runs with this order showed shoes and nothing else.
function cancelSearch(): void {
inFlight?.abort(); // no reason given, so fetch rejects with AbortError
inFlight = null;
}
const pages = new Map<SearchKey, SearchPage>(); // simplified cache of whole pages
async function onQueryChange(key: SearchKey, params: URLSearchParams): Promise<void> {
cancelSearch(); // first, even when the cache will answer
const cached = pages.get(key);
if (cached) return render(cached);
const page = await fetchSearch(params);
if (page === null) return; // a newer search replaced this one
pages.set(key, page);
render(page);
}What just happened? The cache hit felt safe because it skipped the network. But the older sneakers request never heard about the new search. Cancelling belongs to the user's new search, not to the network call, so it runs before any branch.
Why the abort reason matters
abort() also takes an optional reason, which can be any value. In our Chromium 141 test, abort("replaced") made fetch reject with the string replaced, not a DOMException. A name check like the one in fetchSearch would then rethrow, and a harmless cancel would show a retry button.
If your code passes reasons, check controller.signal.aborted in the catch instead of the error's name. That check returned null correctly in the same test. A timeout from AbortSignal.timeout() rejects with a different name, TimeoutError, which you probably want to show as a real failure.
Aborting after fetch has resolved, but before the body is read, also rejects. It happens at res.json(), with AbortError. MDN's AbortSignal page documents this, and our test matched it.
Do cursors fix every pagination problem in an endless list?
No. Cursors stop repeats and skips while the list changes, but products added at the top stay hidden until the user reloads. We simulated both kinds of pagination in Node.js 22, with 100 products sorted newest first and 20 per page.
| Change between page 1 and page 2 | Offset pagination | Cursor pagination |
|---|---|---|
| 5 new products added at the top | Page 2 repeated 5 of its 20 items | No repeats, but the 5 new products never appeared |
| 5 products from page 1 deleted | 5 products skipped, never shown | No skips and no repeats |
| 2 products share one timestamp at the page edge | No skips while the list stayed the same | A time-only cursor skipped 1 product; time plus id skipped none |
The cursor in this test holds the last product's creation time and its id. The id breaks ties, because two products can share a timestamp. With time alone and a strictly older comparison, the product sharing page 1's last timestamp was skipped. With older or equal, page 1's last product repeated instead.
So the client needs one extra piece: a small new results button at the top. Show it when a fresh first page has products the user has not seen, and reload from the top when it is tapped.
How do you prove the speed claims instead of just naming them?
Measure them with the same tools the scores come from. We built a test page of 12 product cards and loaded it in Chromium 141 at phone width, 360 by 740 CSS pixels. We measured with web-vitals 6.2.3, the open-source library from Google's Chrome team for these scores.
Without width and height on the images, CLS was 0.44 to 0.58 over six first loads. That is far past 0.25, where web-vitals marks a score poor. With width and height set, CLS was 0 in every run.
Scrolling does not excuse jumps. The CLS rules skip shifts within 500 milliseconds of a tap, click or key press, but web.dev says scrolls do not count as input. While we scrolled and new cards loaded, CLS reached 0.61 to 0.88 without image sizes, and stayed 0 with them.
Check layout shifts on your own page
Open Chrome DevTools, the browser's built-in developer panel, and go to the Console tab after the page loads. Paste this, press Enter, then scroll. Each line printed is one shift. The word counts means no tap, click or key press came just before it, so it adds to CLS.
new PerformanceObserver((list) => {
for (const shift of list.getEntries()) {
console.log(shift.value.toFixed(3), shift.hadRecentInput ? "after input" : "counts");
}
}).observe({ type: "layout-shift", buffered: true });On our unsized test page, it printed three lines: 0.183, 0.018 and 0.236, all marked counts. CLS adds shifts that come close together and reports the worst burst, so that run scored 0.437. On the sized page, it printed nothing.
DOM size and request counts
The DOM claim behind virtualising is easy to count. With our card markup, 30 cards made 186 elements, and 2,000 cards made 12,006 elements (20,011 nodes once text is counted). web.dev's guide to DOM size, as of 19 October 2026, calls a DOM excessive above 1,400 nodes.
The debounce claim is countable too. Typing running shoes at one key every 150 milliseconds sent 13 searches without a debounce, one per key. A 300 millisecond debounce sent 1. A 400 millisecond pause after the word running sent 2, because the debounce fired during the pause.
How do you run a 45-minute frontend system design interview from start to finish?
You run it by giving each of the five steps a time box and saying aloud when you move on. Say every trade-off aloud too. GreatFrontEnd's scoring guide, as of 19 October 2026, says interviewers score observed signals, not unspoken reasoning.
Minutes 0 to 7: requirements
Ask about features, users, phones, networks and scale. Write a functional list, a non-functional list and what is out of scope.
Minutes 7 to 16: components
Sketch the screen, box the components and draw the tree. Mark which ones read from the store and which only take props.
Minutes 16 to 23: data and state
Sort state into local, URL, form and server cache. Write the main types, and explain why products are stored once by id.
Minutes 23 to 30: APIs
Propose the response shape and choose cursor pagination. Explain the typing pause, cancelling before the cache check, and what each error looks like.
Minutes 30 to 42: speed and accessibility
State the Core Web Vitals targets, then the techniques that meet them. Cover keyboard use, screen readers, contrast and tap targets.
Minutes 42 to 45: wrap up
Recap the two or three biggest trade-offs. Name what you would add with more time, such as offline support or usage tracking.
Mistakes that cost the most points
- Drawing components before asking a single question about users or scale.
- Naming a library instead of explaining the idea, such as saying you would use a state library without saying what state it holds.
- Designing for a fast laptop when most users are on budget phones and patchy networks.
- Forgetting loading, empty and error states. Every list on every screen needs all three.
- Leaving security out. Mention escaping user text (showing it as plain characters, never as code), which blocks cross-site scripting, where an attacker sneaks a script into your page.
Questions people ask
- How long is a frontend system design interview?
- It depends on the company, so ask the recruiter. This guide plans for a 45-minute slot, because a plan that fits 45 minutes stretches easily to 60. Take a minute or two for introductions out of that time. Use any extra time to go deeper on performance or accessibility.
- Do I need to write code in a frontend system design interview?
- Usually only a little. Most of the discussion is about your design and trade-offs. Writing a few lines, such as the main data types or an API response shape, makes your ideas concrete and shows you can turn them into real code.
- What is the RADIO framework for frontend system design?
- RADIO is an answer order published by GreatFrontEnd: requirements exploration, architecture, data model, interface definition, and optimisations and deep dive. It matches the five steps in this guide. The name matters less than the habit of asking first, then designing from the outside in.
- Which frontend system design questions are asked most often?
- GreatFrontEnd's practice list, as of 19 October 2026, includes a news feed, autocomplete, a chat app, an image carousel and a rich text editor. It also lists an e-commerce marketplace and a video streaming site. A feed tests long lists, while chat tests live updates.
- How should I prepare for a frontend system design interview?
- Learn the five-step framework, then practise it out loud on one prompt every few days. Time yourself and ask a friend to play the interviewer. After each mock, study the questions you could not answer.
- Should I name specific libraries during the interview?
- Name a library only after you explain the idea it solves. Say you need a cache for server data that drops stale results, then mention a tool you have used. Interviewers want to see that you understand the problem.
- Is frontend system design asked for junior roles?
- It depends on the company and the level, so ask the recruiter what the loop contains. Junior candidates may get a lighter version, with more hints from the interviewer. Learning the framework early still helps you think more clearly about everyday features.
Sources and further reading
- Web Vitals: Core Web Vitals metrics, thresholds and the 75th percentile (web.dev)
- Interaction to Next Paint becomes a Core Web Vital on March 12 (web.dev blog)
- Cumulative Layout Shift: session windows and recent input (web.dev)
- Optimize Cumulative Layout Shift: width and height on images (web.dev)
- Browser-level image lazy loading (web.dev)
- How large DOM sizes affect interactivity (web.dev)
- Web Content Accessibility Guidelines (WCAG) 2.2, W3C Recommendation
- Understanding Target Size (Minimum), WCAG 2.2 (W3C)
- Understanding Status Messages, success criterion 4.1.3, WCAG 2.2 (W3C)
- Dialog (Modal) Pattern, ARIA Authoring Practices Guide (W3C)
- AbortController: abort() method and its reason (MDN Web Docs)
- AbortSignal: AbortError, TimeoutError and aborting during body reads (MDN Web Docs)
- Intersection Observer API (MDN Web Docs)
- The RADIO Framework, Front End System Design Playbook (GreatFrontEnd)
- Evaluation Criteria for Front End System Design Interviews (GreatFrontEnd)
- Front End System Design Questions (GreatFrontEnd)