Skip to content

Epic: UI Modernization — consistency, mobile, accessibility #13240

Description

@lokesh

Summary

Make Open Library look and feel maintained, trustworthy, and usable everywhere — one design language across the site, accessible and mobile-solid, measured through Core Vitals.

Two things follow from that, and they run through every workstream below: content gets the screen, chrome gets out of the way. Headers, toolbars, filter rows, and droppers should take as few vertical pixels as they can and recede visually — consistent heights, one hover treatment, quiet colors — so the eye lands on books, not on the frame around them. Today chrome takes 62% of the viewport on mobile search results (#13482), and each surface invents its own control heights and hover states.

Inconsistent typography and dated UI signals "unmaintained." A patron who lands on a page with four heading styles, three button treatments, and a layout that breaks at 375px doesn't file a bug — they leave, and they don't trust us with their reading data. This work is the substrate every other H2 patron-facing initiative ships on: Prompts (#12858), the Genre Explorer (#13158), and carousel/browsing work (#9828, #10512) all need a component vocabulary and an accessibility baseline to build against. Doing it once here is cheaper than each of those inventing its own buttons, dialogs, and focus handling.

This epic supersedes #11555 (two-tiered design tokens) and absorbs its remaining checklist — see Foundation below.

Success criteria

  • Core Vitals (CLS/LCP/INP) tracked and non-regressing on the four highest-traffic templates: home, search, book, subject
  • No new raw hex colors, magic-number font sizes, or ad-hoc z-index values merged (enforced by stylelint, not by review vigilance)
  • Zero critical/serious axe violations on those same four templates
  • Every ol-* component has a docs page and a stated mobile + keyboard contract

In flight

Open PRs already doing this work:

Recently landed: #13228 (search results toolbar onto web components, adding <ol-menu-popover> and the Details/Grid segmented control), #13238 (ol-pagination size variants), #13102 (color tokens), #13247 (z-index tokens), #13006 (jest-axe CI gate).


Workstream 1 — Foundation

Design tokens, the ol-* web component family, and living docs. This is the part everything else depends on.

Tokens (absorbed from #11555)

Two-tier structure — primitives (a small curated set of raw values) → semantic tokens (design intent, e.g. --radius-card, --color-surface-raised). Semantic tokens are what templates and components reference, so a category-wide change is a one-line edit. Tokens live in static/css/tokens/, aggregated by static/css/tokens.css.

Create tokens for…

Apply tokens across existing CSS…

Remaining foundation work:

ol-* web components

20 Lit components exist today in openlibrary/components/lit/ol-button, ol-dialog, ol-popover, ol-select-popover, ol-options-popover, ol-menu-popover, ol-segmented-control, ol-toggle, ol-chip/ol-chip-group, ol-toast/ol-toast-region, ol-banner, ol-tooltip, ol-carousel, ol-pagination, ol-scorecard, ol-read-more, ol-markdown-editor, and others. The family is real; what's missing is consistency guarantees across it.

  • Shadow-boundary contract documented and honored across all components — focus/tabbing, CSS custom-property surface, form association, ARIA. Several components already implement pieces of this (form-associated mixin, non-modal popover); it isn't yet uniform or written down.
  • Lint the Lit css template literals. lint:css globs only static/**/*.css and openlibrary/**/*.css, so styles authored inside .js are unlinted — which is why 42 raw hexes survive in shadow DOM. Until this exists, "no new raw hex" is unenforceable there. Drafted in Stylelint coverage for Lit + Vue; retire raw hex from web components #13110 (closed).
  • Every component consumes semantic tokens only — no raw hex inside shadow DOM
  • Add font-size to stylelint's declaration-strict-value list once a type scale lands — currently the only success criterion with no enforcement path
  • Retire slick, the last jQuery widget duplicating an ol-* component (js/carousel/Carousel.js, static/css/lib/slick*.css) — blocked on Rebuild ol-carousel on native scroll snapping #13220
  • Migrate any other legacy jQuery widgets that duplicate an existing ol-* component

Living docs

The design system docs live at /developers/design, assembled by openlibrary/plugins/openlibrary/design.py from 19 component pages under openlibrary/templates/design/components/, with tokens parsed straight out of the token CSS.

  • A docs page for every shipped component (only ol-icon's page is outstanding, in Add unified icon system #12955)
  • Token reference page — primitives and semantics, browsable
  • Each page states the mobile behavior and keyboard contract, not only the prop table — today 4 of 19 pages mention keyboard behavior and 2 mention mobile

Workstream 2 — A11y audit

A systematic WCAG 2.1 AA sweep, building on fixes already shipped rather than starting cold. Recently landed: search-modal dialog naming + arrow-key result nav + live region (52da434), ReadButton dropdown summary name (#13116), generic-dropper arrow button label (#13029), OlSelectPopover role=group (#13037), iframe titles (#13012), decorative star ratings hidden (#13028), several contrast fixes (#13030, #13027), and the interactive-element audits for the book page (#4906) and Want To Read (#4911).

The CI gate matters more than any individual fix: without it, this list regenerates itself every 18 months. jest-axe covers component ARIA patterns; #13251 extends that to whole pages.


Workstream 3 — Mobile pass

Touch behavior, keyboard handling, and layout at small sizes. Extends the shipped search-modal mobile work (full-height modal, equal-height filter row, text-entry auto-zoom rules) and carousel touch gestures (#13101).


Measurement

Core Vitals are the outside check on whether this is working, since "looks maintained" isn't measurable on its own:

  • CLS — reserved image space, no late-loading font swaps
  • LCP — self-hosted subset fonts, no render-blocking token cascade
  • INP — component interaction cost, especially carousels and poppers

Nothing measures these today — there's no vitals instrumentation in the repo yet. #13252 (read-only Matomo client) and #13213 (Core Vitals Retention Score) are the proposed reporting path. This ties into item 2 (Metrics/Core Vitals) of #13194.


Related

Stakeholders

@lokesh @cdrini @mekarpeles @jimchamp @RayBB


Instructions for Contributors

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Affects: UIIssues with the web site's user interface. [managed]Lead: @lokeshIssues pertaining to front-end design system [css, js, components]Team: Front-endIssues belonging to the Front-end team [experimental tag]Theme: AccessibilityWork related to disability accessibility. [managed]Theme: CSSIssues related to CSS stylesheets. [managed]Theme: DesignIssues related to UI design, branding, etc. [managed]Type: EpicA feature or refactor that is big enough to require subissues. [managed]

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions