Skip to content

feat: Netflix-style expandable card — inline detail panel on result card expand #46

Description

@mekarpeles

Concept

Netflix shows a grid of title cards. Hovering or clicking a card reveals an inline expanded panel below the card row — a larger hero image, playback controls, genre tags, and a description — without navigating away. A styled ⌄ chevron button in the bottom-right of the expanded panel collapses it back.

Reference screenshot (Netflix, searched "lord of the rings"):

User-attached screenshot shows: normal thumbnail cards in a grid; one card (The Green Knight) is expanded — it floats taller and wider, breaking out of the grid row, with a still image, ▶ / + / 👍 action buttons, a ⌄ collapse chevron, rating badge (R), runtime (2h 16m), quality (HD), and descriptive tags "Violent • Epic • Visually Striking".


Proposed behaviour for Open Library

Where it applies

Both surfaces currently show book cards:

Surface Component Card element
Autocomplete panel (droppable) ol-search-bar_renderResults() .ac-row
Full search results page ol-search-pageol-book-card <ol-book-card>

The feature is most impactful on ol-book-card (full results page) since those cards are already larger. The autocomplete rows are space-constrained and may only need a lighter treatment (see open questions).

Interaction model

  1. Each card has a small ⌄ expand chevron button in its bottom-right corner (visible on hover, always visible on touch).
  2. Clicking the chevron expands that card in place — the card grows vertically to reveal additional content; surrounding cards reflow below it.
  3. Only one card is expanded at a time; expanding another collapses the current one.
  4. The same chevron (now ⌃) collapses back to the compact view.
  5. Keyboard: Enter/Space on the chevron toggles; Escape collapses.

Expanded content to show

Fields already available on the work object returned by the OL search API:

  • Larger cover image (use -L size suffix instead of -M)
  • First sentence / description (w.first_sentence or w.description — may need an additional /works/OLxxxW.json fetch if not in search results)
  • Subjects (w.subject — first 4–5 tags)
  • Series (w.series?.[0])
  • Edition count (w.edition_count)
  • OL work page link (full /works/OLxxxW URL)
  • Action buttons: "Read now" (if ebook_access >= borrowable), "Want to Read" / reading-log CTA, "More details →"

What may require an extra fetch

The OL search API does not return description or first_sentence for works. A lightweight fetch to /works/OLxxxW.json on expand (not on card render) could populate these. The fetch should be:

  • Triggered only on first expand (cache the result on the element)
  • Abortable if the card is collapsed before the response arrives
  • Shown as a skeleton/spinner state while loading

Open questions

  1. Autocomplete rows — should .ac-row in the droppable panel also get an expand chevron, or is the panel too space-constrained? A simpler option: clicking the row body navigates to the work page; the chevron is omitted in the panel.
  2. Extra fetch — is first_sentence / description worth the extra round-trip? Could use subjects + edition count as the expanded content without any additional request.
  3. Animation — smooth height transition (max-height CSS transition) vs. instant reflow. CSS transition is preferred for perceived quality but must not janky-scroll on mobile.
  4. Grid vs. list layoutol-search-page currently renders cards in a single column (flex-direction: column). The Netflix effect (card expands across multiple columns) requires a CSS grid layout. Is a grid refactor in scope here, or should the expand just grow vertically in the single column?

Implementation sketch

ol-book-card changes

  • Add _expanded = false reactive state property
  • Add chevron button ( / ) wired to _toggleExpand()
  • In _toggleExpand(): fire a custom ol-card-expand event (bubbles, composed) so the parent page can collapse any previously open card
  • Render expanded content block conditionally; trigger fetch on first expand

ol-search-page changes

  • Listen for ol-card-expand and set _expandedWork to the work key; re-render (Lit handles diffing)
  • Pass .expanded=${w.key === this._expandedWork} to each <ol-book-card>

Pure-logic additions (filters.js or new utils/works.js)

  • buildWorkUrl(work) — already exists as bestEdition; may need a workPageUrl variant
  • isReadable(work) — check ebook_access >= borrowable (already used in bestEdition)

Acceptance criteria

  • Each ol-book-card has a ▾ chevron; clicking it expands the card with additional metadata
  • Only one card expanded at a time
  • Expanded state shows cover (larger), subjects, edition count, action buttons
  • If description requires an extra fetch: loading skeleton shown; fetch aborted on collapse
  • Keyboard accessible (Enter/Space to toggle, Escape to collapse)
  • No layout shift on cards that are not expanded
  • Vitest contract test: ol-book-card declares _expanded reactive property and emits ol-card-expand

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions