Skip to content

Put the feed bar on Liquid Glass and collapse it in landscape - #577

Merged
bradleymackey merged 4 commits into
mainfrom
feed-bar-glass-compact
Sep 18, 2026
Merged

bradleymackey merged 4 commits into
mainfrom
feed-bar-glass-compact

Conversation

@bradleymackey

@bradleymackey bradleymackey commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Summary

The bottom feed bar (item count / active filter / Clear + Edit) sat on a 5% primary fill, so the grid bled straight through and it read as nearly transparent; the tag pills had no backing at all. The two rows also ran ~117pt tall, which eats a big slice of an iPhone in landscape.

  • Liquid Glass: the status bar and each tag pill now render on .regular glass. A system-background wash (Color(.systemBackground).opacity(0.85)) sits behind the glass so tile text underneath stays blurred out in both light and dark mode.
    • This is a backdrop rather than Glass.tint — a tinted glass renders as a fully empty image under CALayer.render(in:), which blanked every snapshot test that includes the feed.
    • The pills' GlassEffectContainer lives inside the horizontal scroll view: glass renders at the container's level, so a container outside the scroll view let pills draw past its clip and over the bar in the landscape row.
  • Compact everywhere: dropped the 44pt min-height and tightened paddings. TagPillView takes smaller insets when controlSize is .small/.mini, so the detail-view pills (and their snapshots) are untouched. Portrait is ~85pt now (was ~117pt).
  • Single row in landscape: when verticalSizeClass == .compact the tag row and the bar share one ~40pt row — pills scroll on the leading side, the bar hugs trailing. With no tags the bar still sits trailing.
  • Constant bar height: the buttons size the row, so a hidden zero-width Edit button always sits behind the cluster — the bar is the same height whether the feed is empty, filtered to nothing, or populated. Clear now follows the filter (not the results), so a filter that matches nothing can still be cleared from the bar; Edit still needs items.
  • Short filter-change animation in the bar: the default .snappy spring on itemsFilteringByTags faded the filter name / Clear button out over ~430ms while the glass capsule morphed, which read as the bar lagging the tap. A .snappy(duration: 0.2) spring keeps the morph but the text settles ~200ms after the pill (measured from 60fps simulator recordings of the old, none, and new variants).
  • Pill insets: the pill stroke straddles the capsule edge, and both the glass container and (in the landscape row) the scroll view clip at their bounds — so the pills carry a 2pt vertical inset inside the container and a 2pt horizontal inset in the landscape row. Portrait row spacing is 8pt (10pt visible gap to the bar).
  • Tests: framedForLandscapeTest() helper (and framedForTest now pins the regular vertical size class — UIKit reports compact for short frames); landscape snapshots; height pairs with/without Edit in both layouts on a grey backdrop so the bar surface is visible; a zero-result-filter case keeping Clear; a landscape overflow case clipping before the bar. Feed, main-navigation and autofill snapshots re-recorded. Glass itself doesn't render in the layer.render-based snapshots, so they verify layout — the look was checked in the Xcode preview (light/dark/landscape).
  • Preview: the feed preview now has tags and coloured tiles so the glass can be eyeballed over real content.

Verification

  • make format / make lint clean.
  • VaultItemFeedViewSnapshotTests, VaultMainNavigationViewSnapshotTests, TagPillViewSnapshotTests, OTPCodeDetailViewSnapshotTests, SecureNoteDetailViewSnapshotTests, VaultAutofillCodeSelectorViewSnapshotTests pass on iPhone 18 Pro Max / iOS 27.0 (recorded, then re-run to confirm stable).
  • Rendered the VaultItemFeedView preview in portrait, landscape and dark mode.
  • Recorded the simulator while toggling a tag pill on the old and new bar and measured the label's change frame relative to the pill's.

🤖 Generated with Claude Code

bradleymackey and others added 4 commits September 18, 2026 12:49
The bottom feed bar sat on a 5% primary fill, so the grid bled through
and it read as almost transparent; the tag pills had no backing at all.
Together the two rows ran ~117pt tall, which is a big slice of an iPhone
in landscape.

- Status bar and each tag pill now render on `.regular` Liquid Glass
  inside one `GlassEffectContainer`. A system-background wash sits
  behind the glass so text underneath stays blurred out. This is a
  backdrop rather than `Glass.tint` because tinted glass renders as an
  empty image under `CALayer.render(in:)`, blanking every snapshot that
  includes the feed.
- Drop the 44pt min-height and tighten paddings; `TagPillView` takes
  smaller insets under `.controlSize(.small)` so the detail-view pills
  are unchanged. Portrait is ~85pt now.
- In compact vertical size class (iPhone landscape) the tag row and the
  bar share a single ~40pt row; the bar hugs trailing and the pills
  scroll in the remaining width.
- Add `framedForLandscapeTest()` and two landscape snapshots; re-record
  the feed, main-navigation and autofill snapshots for the new layout.
- Give the feed preview tags and coloured tiles so the glass can be
  checked over real content.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two follow-ups from review:

- The bar's height came from the Clear/Edit buttons, so it shrank by
  ~6pt whenever the feed was empty and Edit was hidden. A hidden,
  zero-width Edit button now sits behind the button cluster so the row
  is always button-height. Clear now follows the filter rather than the
  results, so a filter that matches nothing can still be cleared from
  the bar; Edit still needs items.
- Deselecting a tag looked laggy: the `.snappy` animation on
  `itemsFilteringByTags` faded the filter name and Clear button out over
  ~430ms while the glass capsule morphed. Measured from a simulator
  screen recording, the text now disappears within ~50ms of the pill.
- `GlassEffectContainer` moves inside the tag scroll view. Glass renders
  at the container's level, so with the container outside, pills drew
  past the scroll view's clip and over the bar in the landscape row.

Tests: snapshot pairs (portrait and landscape) proving the bar height is
identical with and without Edit, a zero-result filter keeping Clear, and
a landscape overflow case clipping before the bar. `framedForTest` now
pins the regular vertical size class, since UIKit reports compact for
short frames.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pill stroke straddles the capsule edge and the glass container
clips to its bounds, so the top and bottom of every pill border were
shaved off. Two points of vertical padding inside the container keep
the stroke (and the interactive glass bounce) clear of the clip.

Portrait row spacing goes from 6pt to 8pt, which with the new inset puts
10pt between the pills and the bar instead of 6pt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The single-row layout gave the pill row no horizontal inset, so the
first pill's stroke sat on the scroll view's clip edge and its leading
arc was shaved flat. Two points of inset keep it clear.

Filter changes in the bar animate again, with a 0.2s spring instead of
the default: the filter name and Clear button now settle ~200ms after
the pill flips (default spring: ~430ms, which read as lag; none: ~50ms,
which read as a snap).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bradleymackey
bradleymackey merged commit 1f02838 into main Sep 18, 2026
2 of 5 checks passed
@bradleymackey
bradleymackey deleted the feed-bar-glass-compact branch September 18, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant