Unify header nav - #13508
Merged
Merged
Conversation
…tton Unify the header row on a shared --header-control-height token (large on touch widths for tap targets, the standard medium control height at desktop) so the search field, My Books and Browse buttons, and the account/menu button all line up exactly. - Give the search bar a height lock (border-box, so its border is included) and swap its background-image magnifier for the Lucide `search` sprite glyph, which inherits the trigger's text color. - Extract the Browse trigger's ghost styling into a shared .nav-ghost-btn class and apply it to the My Books link (header_dropdown now accepts an optional link-class prop for its singleton anchor), so both nav items render as matching ghost buttons. - Group the avatar and hamburger icon into a single ghost button: the summary becomes a transparent pill that fills on hover and while the drawer is open, with the avatar shrunk to sit beside the 24px Lucide menu glyph and the pending-merge badge pinned to its corner. - Replace the down-arrow.png dropdown chevron with the Lucide chevron-down sprite glyph. - Keep the inlined header mirrors in legacy.css in step. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Ra2Ro6hPFJVqRkXaLJNAyz
Give the Browse and My Books ghost buttons muted leading icons (book-open-text, bookmark), swap the Subjects tile glyph from the grid to the more playful wand and soften the featured-tile icons to blue-400, and replace the archive.org avatar image in the account button with a loose circle-user-round glyph, dropping its border/sizing CSS and the now-unused --account-icon-border token. Keep the large control height at every width and tighten the mobile tooltip and drawer typography.
…out-styling-r55xpr
Full text color for the Search label and the muted icon color for the magnifier, so the search field reads as part of the same control set as the Browse and My Books buttons. The barcode-scan glyph follows the magnifier to the muted color to stay matched on touch layouts.
…55xpr' into claude/header-layout-styling-r55xpr
50 tasks
The two auth links were the last header controls with their own look: a bare .btn text link and a 5px-radius blue block, neither locked to the header control height. Log In now uses the same .nav-ghost-btn as My Books and Browse, and Sign Up gets a .nav-primary-btn twin of <ol-button variant="primary"> — same fill, radius, lift, and specular top edge. The ghost rules move out of the browse-popover section and drop their .navigation-component scope so the auth links can share them.
Log In and Sign Up inherited the body type while My Books and Browse were 15px/500, so the row read as two sets. The one-off size moves onto --header-control-font-size beside --header-control-height, and both control classes pin it instead of inheriting.
38px was a touch tall next to the 32px medium step; 36 keeps the same touch-friendly floor while tightening the header row and every other large control (ol-button, ol-segmented-control, ol-select-popover, inputs).
My Books and Browse are both .nav-ghost-btn, whose inline-flex centering is what puts the label in the middle of the control. Two blanket rules left over from the old <details> dropdowns outranked it and forced display: block, so the My Books anchor sat ~10px above the Browse button (which still centers its own content the way a <button> does) at every width, and stretched its hover fill across the full half-width of the row. Removing those two declarations lets the class do its job. The nav no longer contains a <details>, a <summary> or a .dropdown-menu at all since Browse became an <ol-popover> and My Books collapsed to a single link, so the rules that styled them are unreachable and go with it.
lokesh
marked this pull request as ready for review
September 9, 2026 22:05
…out-styling-r55xpr # Conflicts: # openlibrary/i18n/messages.pot # openlibrary/templates/lib/browse_popover.html # openlibrary/templates/lib/header_dropdown.html # openlibrary/templates/lib/nav_head.html # static/css/components/header-bar--desktop.css # static/css/components/header-bar.css # static/css/legacy.css
The account button assumed every signed-in patron had an avatar to show. A patron with no linked archive.org account has none, and that URL 403s, so the button rendered a broken image. Render the glyph instead when there is no itemname, and swap to it from an onerror handler when the request fails for some other reason. The glyph gets its own class rather than reusing account__icon, whose 2px border frames a photo and looks wrong around a glyph. A linked account that never uploaded a picture still returns 200 -- archive.org serves its own silhouette there, which we can't tell apart from a real photo without a round-trip, so that case is unchanged.
The avatar was a rounded square, but the glyph it falls back to when a patron has no picture is a circle, so the button changed shape between the two states. Round the avatar to match. It also had no object-fit while being forced into a 30px square, which stretched the portraits and landscapes archive.org serves alongside square images; cover crops them instead.
…out-styling-r55xpr # Conflicts: # static/icons/src/lucide/bookmark.svg
The unified header styles add about 140 bytes gzipped to every page bundle, which pushed page-plain.css roughly 90 bytes over its 27KB limit. The other page bundles still fit under theirs.
The avatar's frame was the only consumer of a one-off primitive alias. --border-width-thick and --color-border-muted say the same thing in the shared vocabulary, so the token list loses an entry nothing else used.
Durations and easings in two tiers, like colors: named Penner curves as primitives, and semantic tokens for the question a component actually has -- is something entering, leaving, moving, or changing state. The press scale is tiered by control width so a pressed edge always travels about 1.5px: 3% on a 32px icon button is sub-pixel, and 3% on a 500px search bar is a lurch. Shared with the design-polish branch verbatim, so whichever lands second merges as a no-op.
The hamburger drawer and the Browse popover are the two overlays the header owns, and neither read as the same gesture as the rest of the site's modal surfaces. The drawer now takes the shared scrim: --overlay-backdrop-color at 32% with a 3px blur. The blur is what lets the dim stay light -- 32% over a page of covers still reads as content; blurred, it doesn't. Its panel moves to --color-surface-header and --box-shadow-overlay. The popover panel restates --border-overlay rather than zeroing the UA's [popover] border, since the hairline is what separates a floating panel from the page under it. Its tray radius and drag handle take tokens too. Both files are byte-identical to the design-polish branch.
A menu row is now inset from its panel by --menu-row-inset and rounded, so its hover fill reads as a pill rather than a band running to the panel's edges. The row gives that inset back as --menu-row-padding-inline, which keeps the label column at 16px. Light-DOM panels that can't set a margin on the row -- the Browse popover, the hamburger drawer -- put the inset on the panel's own side padding instead. Drawer rows become column flex so a single-line row centres on the shared row height and the one row with a subheading still stacks; that turns the My Books <br> into an empty flex item, so it becomes two spans. The merge badge leaves the float for absolute positioning, since a flex row pushes off a float rather than letting it sit over the trailing edge. With a mouse there is no thumb to land, so rows drop to the medium control height and a menu reads as a stack of medium controls inside a panel. The header's own controls take the press and motion tokens: the ghost and primary nav buttons, the hamburger trigger, the search bar, the chevron, and the drawer spinner. Hover transitions come off -- hover changes are instant. The barcode button's reduced-motion override goes with the transition it guarded.
The pill menu rows and popover edge added about 200 bytes gzipped to every page bundle, which pushed page-edit.css about 60 bytes over its 27KB limit. It now matches page-plain.css and page-form.css.
The hardcoded press scales become the tiered tokens from --press-scale: text-width controls (ol-button, ol-chip, ol-segmented-control, the static button) take the default tier, and narrow ones (icon buttons, pagination items, carousel arrows, the banner close, markdown toolbar buttons, search result covers) take --press-scale-compact. A full-width ol-button takes --press-scale-wide, since 3% across a stretched button reads as a lurch rather than a press. ol-toggle's button variant gains a press of its own -- it has its own border, fill, and raised shadow, so it should squeeze like a button; its transition joins the reduced-motion override. The two Lit popovers get the pill menu rows the drawer and Browse panels already have: --menu-row-inset as a margin, --menu-row-padding-inline giving that inset back so the control column stays at 16px, and a rounded row. A nested row's indent is measured from the panel, so it subtracts the inset it already carries. Selected-row styling comes off both popovers. The radio or checkbox already is the state, and the select popover pins chosen items to the top of the panel besides; a tint plus a blue bolded label on top of that says the same thing three times, and the tint made the selected row look hovered. The search modal's Clear all becomes a ghost ol-button instead of a hand-rolled one, leaving only its place in the row to the modal. Removing the header's blanket .auth-component link color fixes the Sign Up label, which had been painting --color-text over the primary button's blue fill -- the rule outweighed .nav-primary-btn on specificity. Log In is unchanged; .nav-ghost-btn already declares the same color.
…out-styling-r55xpr
The panel a composer slots into ol-popover clamped itself to a width meant for the anchored case, so in tray mode the sheet went full-bleed while its content stopped at 360px, leaving dead space down one side. The width cap now comes from --ol-popover-content-max-width, which the tray clears; the height cap comes from --ol-popover-content-max-height, which the tray replaces with its own so an inner scroll region still resolves. Keying that off the .tray class rather than a media query keeps it from drifting away from the JS breakpoint that sets the class. Handing the content the tray's full height made the tray itself scroll by the height of the drag handle above the slot, so the tray's height and its handle's now come from shared variables the content cap subtracts against. The min(90vw, ...) half of each old cap went with it: the tray owns every viewport below 768px, so 90vw never won.
<ol-button> declares no font-weight in either OLButton.js or its pre-upgrade sheet, so it renders at the page's normal weight. The header's ghost, primary and search controls were pinned to 500, leaving the row a step heavier than every button beside it. They ask for `normal` explicitly rather than dropping the declaration, because two of them are native <button>s that would otherwise take the UA stylesheet's weight instead of inheriting the page's.
The search label was still on --font-size-label-large (14px) while My Books, Browse and the auth links had moved to --header-control-font-size (15px), so the one control that sits between them was a step smaller. It was missed when that token was introduced, not held back deliberately.
--menu-row-height is applied as a min-height everywhere else, and it now changes with the pointer -- 36px for a thumb, 32px for a mouse. The rating row was the one consumer taking it as a fixed height, so its 24px star targets and caption had no room if the row height landed under them. The token's comment now says min-height, never a fixed height, so the next consumer doesn't have to rediscover that.
RayBB
requested changes
Sep 10, 2026
header-bar--tablet.css and header-bar--desktop.css were imported behind media queries by six page entry points and hand-inlined a second time in legacy.css, which the LESS deprecation left behind. The two copies had already drifted: tablet padding-top was 5px in legacy.css against the token's 4px, and the desktop logo padding-right 15px against 16px, so pages on legacy.css rendered the header a pixel off from the six that aren't. Both blocks now live at the foot of header-bar.css, last in the file so they still outrank the max-width: 960px block above as they did when they were trailing imports. The header rule set every bundle resolves to is unchanged. The three .header-bar .navigation-component .dropdown-menu rules that only ever existed in legacy.css are dropped rather than carried over: header_dropdown.html only renders that <ul> in its non-singleton branch, and both callers pass a single link, so nothing in the header has matched them since.
The badge is pinned 6px outside the hamburger trigger's corner, but the component's 4px gutter is narrower than that, so on a phone -- where the header spans the full viewport -- the last 2px ran off the screen. Widen the gutter to 8px so the badge clears it.
Four easing primitives had no semantic token referencing them, which the file's own rule forbids: --ease-out-quart, --ease-out-expo, --ease-in-cubic and --ease-in-out-quart are dropped. --ease-move and --ease-state stay and are noted as the published vocabulary rather than dead entries. The migration had also missed ol-button.css and OlShelfButton.js, which still carried the hard-coded 0.08s and scale() values the tokens replaced. The save button is a 32px icon control, so it takes the compact tier.
The header re-implemented ol-button's ghost and primary variants in the light DOM, down to a third copy of the specular-edge recipe, under a comment asking the next person to keep them in step with OLButton.js by hand. ol-button.css already solves this for the secondary look: .ol-button-static joins the pre-upgrade selector lists so the two can't drift. Add --ghost and --primary modifiers the same way and the header keeps only what it actually does differently -- its taller control height, its one-off label size, and a hover toned to the header's own canvas. The neutral hover is now scoped away from the two modifiers, mirroring how OLButton.js scopes hover per variant; without that the primary button would take the grey secondary fill alongside its brightness(). These stay light-DOM classes rather than becoming <ol-button>s: they are real <a href>s that must navigate with JS blocked, and <ol-button href> renders no anchor until it upgrades.
The $if ia_id gate above it is what actually fixes the broken avatar: an account with no linked archive.org identity no longer requests one. A linked account's image 200s -- archive.org serves its own silhouette when there's no picture -- so the handler was guarding a case that doesn't arise, at the cost of putting JavaScript back in the markup on every logged-in page.
The same three-line explanation sat above the same four declarations in all three popovers, differing only in which column it named. OlMenuPopover holds the explanation; the other two point at it.
OlDrawer.js cites design.md#blur-follows-modality-not-viewport-width, which was never written. Write it: blur tracks whether the page behind is reachable, which is why ol-popover blurs as a modal mobile tray and not as a non-modal desktop popover. The token-file table listed four of the twelve files, and the Animations section still taught the hard-coded 0.08s and scale(0.97) that motion.css and press.css replaced. The anti-pattern sample keeps its raw value -- it is code you shouldn't write, so it shouldn't model token use.
Collaborator
Author
|
Of the 8 issues you noted, 6 are fixed fully, 1 partly, and 1 deferred.
Net −129 lines of CSS. |
Several of the new comment blocks ran to four, five and six lines where two or three carry the same reasoning, and the motion.css and press.css headers were the longest in tokens/ against a 5-13 line house style. Trimmed the ones this branch introduced; the pre-existing long blocks elsewhere are left alone. Comments only -- stripping all comments from both revisions leaves the files identical.
6 tasks
…out-styling-r55xpr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Part of #13240. The header row was a mix of controls at three different heights with several glyphs that weren't sprite icons, and the two menus behind it (the hamburger drawer and the Browse popover) didn't match each other. This unifies them, and picks up a handful of design fixes along the way.
Header consistency
--header-control-height,--header-control-font-size). They were 45px, 40px and 30px at three different sizes..nav-ghost-btn) instead of three.down-arrow.pngchevron, the CSS background-image magnifier, and the archive.org avatar photo — which also drops a third-party image request from every logged-in page load.Menu consistency (drawer + popovers)
ol-dialogand the popover tray.Shared tokens replacing hard-coded values
static/css/tokens/motion.css(durations + easing) andpress.css(press-scale tiers). Seven components —ol-button,ol-chip,ol-carousel,ol-pagination,ol-segmented-control,ol-toggle, the markdown editor — carried five different hard-codedscale()values on:activeand now read from three tiers.--menu-row-height,--menu-row-inset,--menu-row-padding-inline) lands incontrol-heights.css;--control-height-largesteps 38px → 36px.--account-icon-bordertoken and the dead header-dropdown CSS. The account-icon rules inlined intolegacy.cssare kept in step.Fixes
<br>inside a flex column, where a line break is an empty flex item rather than a break. Two spans instead.<ol-button variant="ghost">.Testing
Screenshot
See above.
Stakeholders
@cdrini