Skip to content

Conversation

ronjalh
Copy link
Member

@ronjalh ronjalh commented Oct 29, 2026 •

Summary

The Elements page (the search page, the start page of the site): the design from elements_page.png, and the page connected to the API with the search, the results and "Load more elements". The count of a search stops at 1001, so the page says "1000+" for more than 1000 results.

This is one pull request for the one issue, #110 (it replaces #118, #120 and #121, which were stacked on this one or on dev). The filters and the sort are the rest of #110: they are drawn, and say that they are not connected yet.

Part of #110.

What is in it

The design

  • The title, the search field, the filters, the sort button, the results (a table on a wide screen, cards below 768 px) and "Showing X of Y" with "Load more elements". It uses the whole window on every page (#root is not 1126 px any more) and the colors are tokens with a light and a dark value (variables.css). Dark mode comes from the color inspiration, and light mode is as in the design except that every other row is a tint of the blue accent instead of the purple.
  • The table: a real table with a caption and the headings Code, Element, Apparatus, Element group and Value. The whole row is one link; on hover and focus it gets taller (a shadow, so nothing moves and the width does not change), the text gets a very thin shadow (a little bolder), and an arrow comes in. In light mode no color changes on hover.
  • The cards: the code (and the name of the gymnast) with an arrow, the description, "Apparatus:", "Element group:" and "Value:" (a description list, so each label is connected to its value), "More variants", and the drawing below. Two columns from 600 px. A card moves up and gets a stronger shadow on hover, and the arrow is always there without hover (a phone). The whole card is one link.
  • The vault group: the name of a vault group is a long title (a description), so a vault is shown by the number of its group ("Group 4", elementGroup.number) and an element by the name of its group. Nothing in the database is changed. For 4 of the 87 vaults the description is only the name of the vault ("Yamashita"); there is no other field, so the page shows what the data has.
  • The difficulty badge (existing component): a number has one decimal and a smaller text than a letter. Its text is centered, and in dark mode it has the colors of "More variants".
  • Accessibility: every control is reached with Tab and shows where the focus is (the buttons hide their ring unless outline-solid is set), the button text keeps its color on hover (the Button turns it to --foreground, which is white in dark mode), the X in the search field has the color of the icon, and the table and the cards are shown one at a time (display: none), so a screen reader reads each result once.
  • The navbar: "Elements" is now "Search", and the logo and "Search" go to /elements.

The API

  • The results: the first page (first: 20) of searchCatalog through Apollo Client (searchOperations.ts, typed by hand), in the table and in the cards. "Showing X of Y" uses totalCount.
  • The search: sent when the user has stopped typing (300 ms) and at once with Enter. At most 80 characters; the message of the server is shown for more than 8 words. The search is in the address (?search=yurchenko), so a result can be shared, and the field follows the address when the user goes back or forward. The old results stay while a new search is on its way.
  • States: loading (role="status", aria-busy), error with "Try again" (describeError), and nothing found ("No elements match “zzz”.").
  • Load more elements: fetches the next page with the cursor of the last one (the query SearchCatalogMore, which does not ask for totalCount again), and the cache adds it to the list (cache.ts). The button is disabled while the page is loading ("Loading more…", aria-busy) and when everything is shown ("Showing N of N"); the count describes why (aria-describedby). An error is shown with the results kept, and the button is pressed again to try again. A new search starts from the first page.
  • 1000+: the page says "Showing 20 of 1000+" when totalCount is above 1000 (formatTotal). The server stops counting at 1001: count with take in backend/src/catalog/search.ts (MAX_TOTAL_COUNT is 1000), the schema is the same, and a value above 1000 means "more than 1000" (docs/API.md). The catalog has 562 entries, so the real page shows the number today.
  • The seven example rows are real rows from the catalog and are only in the tests (src/tests/elementRows.ts).

Where it differs from the design

  • "More variants" is in English (the design has "Flere varianter").
  • The search field and the buttons have no border, like the design. They are told apart by the shadow, the icon and the text only, so this may need a border for contrast (WCAG 1.4.11). It is one class (PILL in ElementsPage.tsx). In dark mode the search field has a tan line.
  • The text is at least 13 px and the buttons 40 px high, a little larger than the picture.

Testing

  • npx vitest run: 41 files, 408 tests passed (the frontend on the real Apollo Client with a stub for fetch, and the backend search against the database). New for the page: the table, the cards, the badge, 24 for the page (the first request, the fields of the API, loading, empty, error, the debounce, the address both ways, Enter, the old results, "Load more" with its states, 1000+), the mapping of a result, and 5 for the count cap.
  • I broke the important behaviors one at a time (the search text, the old results, the address, the 80 characters, following the address, the button when everything is shown, while loading, the cursor, the query for the next page, the error, the description, the count cap) and the tests fail without each of them.
  • eslint, tsc -b and npm run build pass.
  • Tried in a browser against the real backend: 20, 40 and 60 of 562 with "Load more", yurchenko (1 result, the button is disabled at once), handspring (two pages, 36 of 36, the button is disabled), no result, a search from the address and 9 words (the message of the server). The layout was checked in an emulated phone at 320, 360, 390, 600, 700, 844 (landscape), 768 and 1024 px (no sideways scroll), in light and dark mode. Not tried with a screen reader.

Open points

  • The filters and the sort are drawn and say that they are not connected yet (the rest of Build the Elements page and connect it to the GraphQL API #110).
  • The word matching is the one of the API: each search word has to be the start of a word, so "half" does not find "½".
  • The cache keeps the pages of a search, so going back to an earlier search shows the pages that were loaded for it, without a new request.
  • A client that has the old meaning of totalCount (the exact number) shows 1001 for a search with more than 1000 matches. The Elements page is the only client, and it is changed here.

🤖 Generated with Claude Code

@ronjalh ronjalh requested a review from a team as a code owner October 29, 2026 12:51
@ronjalh ronjalh self-assigned this Oct 29, 2026
Ronja added 22 commits October 29, 2026 13:51
# Conflicts:
#	src/App.test.tsx
#	vite.config.ts
…nd make More variants as tall as the label pills
Ronja added 2 commits October 29, 2026 15:49
…hen there is room, and separate apparatus and group columns in the table
…columns when there is room, and vault groups by number
Copy link
Member

@xeniag xeniag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, Ronja!

Just some minor accessibility issues that could be addressed:

High — Correct the mobile value heading (WCAG 1.3.1, Info and Relationships).
The heading always says “D-value,” including rows for non-vault elements that show a difficulty letter and value. Please use a heading that accurately describes both types, or provide a row-specific accessible label.
ResultsTable.tsx

Medium — Clarify the element-group label (WCAG 1.3.1, Info and Relationships).
The group name shares a table cell with the apparatus, and its “Group” label is hidden from screen readers on mobile. Please expose a clear “Element group” label and associate it with the value in both layouts.
ResultsTable.tsx · LabelBadge.tsx

@ronjalh
Copy link
Member Author

ronjalh commented Oct 29, 2026

D-value does accurately describe both as we discussed previously as a team.

@ronjalh
Copy link
Member Author

ronjalh commented Oct 29, 2026 •

I mean, for consistency I can change it to value but on each element it is labelled as d-value so probably should be unified at some point

@ronjalh ronjalh changed the title feat: build the Elements page with the results table feat: build the Elements page: the design, the search, the results and Load more Oct 29, 2026
@xeniag
Copy link
Member

xeniag commented Oct 29, 2026

I mean, for consistency I can change it to value but on each element it is labelled as d-value so probably should be unified at some point

Yeah, but it says value on laptop screens, but D-value only on mobile. Shouldn't it be consistent?

@anettkva
Copy link
Member

Regarding Value/D-value: commit acb278d made the list consistent (mobile and laptop both say "Value"), but the site still uses three names for the same thing: the filter says "Difficulty", the list says "Value", and the element page says "Difficulty" for an element and "D-value" for a vault. I think it should be the same everywhere.

It is confusing and easy to mix them up, but the term that describes all of them is "Difficulty": an element has a difficulty letter and value, and a vault has a D-value, which is its difficulty. As far as I remember, we decided on this wording when we reviewed the design, and agreed on using "Difficulty" and not D-value (which do not accurately describe both).

The change is small (the table heading and the card label in the list, and the vault label in ElementMetadataCard.tsx). It can be done before we merge, or in a separate issue, since it covers more than the element list?

Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants