Skip to content

UI polish: framed stage, typography, motion#28

Open
renjith100 wants to merge 19 commits intomainfrom
ui-polish
Open

UI polish: framed stage, typography, motion#28
renjith100 wants to merge 19 commits intomainfrom
ui-polish

Conversation

@renjith100
Copy link
Copy Markdown
Owner

@renjith100 renjith100 commented Apr 29, 2026

Closes #27

Summary

Polish pass on the TabFlow new-tab carousel — framed stage, typography scale, and motion design (diff-based filter transitions + center-out staggered card reveal). 17 commits across four feature areas; no changes to carousel physics or core interaction.

What changed

Framed stage (3 commits)

  • b37dbd8 ambient stage glow scaled to viewport
  • 5c17935 edge vignette to frame the stage
  • ab60e30 shelf extended into a viewport-wide horizon

Typography pass (4 commits + spec)

  • e6741a3 design spec
  • a2c4c50 search bar promoted as visual entry point
  • a8a9a69 footer counter and hint label boosted
  • 6acc57d card title/domain scaled up for at-a-glance reading
  • f6e84e0 unified detail/empty/group text scale

Filter & group transition animations (4 commits + spec)

  • 600a92a design spec
  • 299ff07 refactor: extract createCardElement, switch handlers to live-index lookup
  • f28e602 .card.is-leaving CSS hook
  • f626ac0 applyFilterDiff — survivors slide, leavers fade-and-shrink, enterers fade-and-scale-up
  • bcfd6c0 cross-fade carousel container on group enter/exit

Staggered card reveal (4 commits + spec)

  • 021997d design spec
  • b261aa7 STAGGER_MS=50, STAGGER_CAP=5, staggerDelayMs(i, active) helper
  • 574da28 buildCards({ stagger: true }) opt-in mode (default unchanged → first-paint stays instant)
  • 26d91c0 group enter/exit triggers staggered reveal
  • 961becb filter enterers stagger center-out from active

Mechanism notes

  • Stagger uses per-card inline transition-delay on top of the existing .card { transition: transform 0.4s, opacity 0.4s } rule. No new keyframes.
  • Cleanup at 700ms clears transition-delay after a staggered reveal so subsequent arrow-key navigation doesn't inherit the delay.
  • Cap at active ± 5 lines up with the carousel's visible window — cards beyond that are already at opacity 0 in the depth fog.
  • Filter diff preserves DOM for survivors so they slide instead of disappear-and-reappear. leavingByKey Map tracks in-flight leavers so a fast re-match cancels the leave.

Non-goals

  • No changes to POSITIONS, reflection math, drag-to-close, undo toast, keyboard shortcuts, or carousel physics
  • First-paint stays instant (default buildCards() behavior preserved)

Test plan

  • Filter clear (the original ask): backspace to clear filter → cards reveal center-out from active
  • Group enter: click group card → cross-fade out, then center-out reveal
  • Group exit: from inside a group → cross-fade out, then center-out reveal from previously-entered slot
  • Empty → enterers: type no-match string, backspace → cards stagger in
  • First paint: fresh new-tab opens instantly with no stagger
  • Re-match cancel: type gi fast then backspace to g → leaving cards snap back without stagger delay
  • Filter survivors: partial filter → survivors slide immediately, no delay
  • Carousel physics post-stagger: ← → after a backspace reveal → smooth, no inherited delay
  • Heavy tab count: only ±5 window around active visibly staggers; far cards stay invisible
  • Carousel regression: ← → glide, Enter open-flash, / focuses search, drag-to-close, Escape close-tab arc

Visual verification confirmed by maintainer (LGTM).

Summary by CodeRabbit

Release Notes

  • New Features

    • Smooth animation effects when filtering search results
    • Staggered card reveal animations for enhanced visual feedback
    • Improved cross-fade animations during group transitions
  • Visual Improvements

    • Enhanced background lighting with strengthened glow and vignette effects
    • Improved typography with larger, more readable text throughout
    • More prominent search bar with refined visual styling
    • Expanded shelf horizon line for improved visual balance

renjith100 and others added 19 commits April 28, 2026 11:23
Spec for making the new-tab viewport feel intentional via three
CSS-only layers (ambient glow, vignette, horizon line) without
touching the carousel physics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Glow was sized for the old 800x500 popup. Make it viewport-relative
(min(1400px, 90vw) by min(700px, 70vh)) and slightly stronger
(violet alpha 0.13 -> 0.18) so it scales with screen size while
staying soft.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a body::after radial gradient that fades from transparent at the
center to rgba(2,2,7,0.55) at the corners, framing the carousel and
making the surrounding void feel composed rather than empty.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the bounded left:0/right:0 shelf with width:100vw centered
via translateX(-50%). The vertical anchor (bottom of .stage-wrap)
is unchanged so card reflections still align. The existing fade
gradient dissolves the line into the void on each side.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Confident type scale for the new-tab UI: bigger search bar (400x42 at
15px / weight 500) and a clearer label ladder (counter 13/600,
hints 12/500, card title 14/600, domain 11). Contrast bumps on
dim-text labels.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bump from 256x38 / 13px / 400 to 400x42 / 15px / 500. Search icon
grows 15->18 with contrast 0.38->0.55 so it reads at a glance.
Padding adjusted (0 22px 0 46px) and icon left offset to 16px so
the icon is centered inside the new bar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Counter 11->13 / 0.38->0.55. Hint 11->12 / weight 400->500 / 0.55.
Key pill 10->11 / weight 600->700 / 0.50->0.65. The 16px hints gap
absorbs the slightly taller pills without reflow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Card title 12->14 / weight 500->600 / inactive 0.75->0.82.
Active card title stays full white. Domain 10->11 / 0.38->0.50.
Existing text-overflow ellipsis handles the slight reduction
in characters that fit in a 176px-wide card.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Detail 12->13 / 0.38->0.55 (height 18->20 to fit). Detail .hl 0.55->0.70.
Empty state 13->14 / 0.38->0.55. Group name 13->14 (matches card title).
Group count 10->11 / 0.38->0.50 (matches card domain).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Diff-based animation for filter (keep survivors, animate leavers
out and enterers in) plus cross-fade for group enter/exit. Reuses
the existing .card CSS transition; only one new class hook.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pulls per-card creation out of buildCards into a standalone
createCardElement(item) helper. Click and drag handlers now look up the
live index via cardEls.indexOf(card) instead of capturing the loop
index, so a card's position can change without re-creating the element.

Behavior is identical for the existing wholesale-rebuild flow; this is
a correctness fix for the upcoming diff-based filter path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pure CSS hook for the upcoming filter-out animation. The fade-and-shrink
visual is driven by inline opacity and transform in JS; this rule just
prevents leaving cards from intercepting clicks or covering survivors.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the wholesale rebuild on filter with a diff that keeps surviving
DOM nodes, fades and shrinks leavers, and seeds enterers at scale(0.6) /
opacity 0 so they animate up to their Cover Flow target via the existing
.card transition.

A leavingByKey map tracks cards still animating out so a fast backspace
re-match cancels the leave and reuses the same DOM node — no duplicates.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wraps enterGroup and exitGroup bodies in a crossFade helper that fades
the cards container out (180ms), runs the wholesale swap, then fades
back in. Group transitions no longer snap; total perceived duration
matches the filter diff animation so the two feel like one family.

groupIdx is captured before the closure runs because the swap clears
activeGroup.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Center-out per-card transition-delay for filter enterers and group
transitions. Reuses the existing .card transition; adds two constants
and a helper. Caps stagger at ±5 from active so heavy tab counts don't
slow the reveal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
STAGGER_MS=50 and STAGGER_CAP=5 control the per-card reveal delay.
staggerDelayMs(index, activeIdx) computes a center-out delay or returns
null for cards beyond the cap. clearAllTransitionDelays wipes residual
delays so subsequent arrow-key navigation glides cleanly.

No callers yet — wired up in following commits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When stagger is true, in-window cards are seeded at opacity 0 and
scale(0.6) with a per-card transition-delay derived from distance to
active. updatePositions then runs animated (not instant) so the
existing .card transition reveals each card after its delay. Cleanup
fires at 700ms to clear residual delays.

Default behavior unchanged — buildCards() still uses instant positioning.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
enterGroup and exitGroup now build with stagger=true so the new view's
cards fade in center-out from active rather than appearing all at once
when the cross-fade settles. enterGroup pops position 0 first; exitGroup
pops the previously-entered group card (groupIdx) first, neighbors
cascade outward at 50ms steps.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Enterers in applyFilterDiff get a per-card transition-delay derived
from their final position in newCardEls (active becomes 0 after a
filter, so the helper is called with activeIdx=0). Cards beyond the
±5 cap are not seeded and animate as before.

Survivors and re-matches explicitly clear any residual transitionDelay
so a stale value can't bleed across diffs. Leavers also clear
transitionDelay before the fade-and-shrink runs. Cleanup at 700ms wipes
any remaining delays so subsequent arrow-key navigation glides cleanly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

Introduces a comprehensive polish pass on the TabFlow new-tab carousel with visual framing (ambient glow, vignette, viewport-wide horizon line), typography adjustments (search prominence, label readability), and motion design (diff-based filter animations with DOM retention, staggered center-out card reveals). Four design specifications document the implementation details alongside CSS, HTML, and JavaScript changes.

Changes

Cohort / File(s) Summary
Design Specifications
docs/superpowers/specs/2026-04-28-filter-animation-design.md, docs/superpowers/specs/2026-04-28-framed-stage-design.md, docs/superpowers/specs/2026-04-28-stagger-animation-design.md, docs/superpowers/specs/2026-04-28-typography-pass-design.md
Four new spec documents defining visual framing, typography updates, filter animation mechanics (diff-based DOM retention with fade/scale transitions for leavers and enterers), and staggered center-out card reveal (50ms per card distance cap at active ± 5).
Visual & Styling Updates
newtab.css
Resized/strengthened body::before radial glow; added body::after vignette; converted shelf horizon to viewport-wide (100vw); increased search bar width/height/font-weight/size; bumped card title/domain/counter/label fonts and opacity; added .card.is-leaving state (pointer-events/z-index).
UI Component Minor Update
newtab.html
Increased search icon SVG width from 15 to 18.
Animation & Diff Logic
newtab.js
Replaced rebuild-on-filter with key-based diff: applyFilterDiff retains DOM for survivors, seeds enterers with opacity/scale and stagger delays, animates leavers with fade/shrink before removal. Added leavingByKey tracking for re-match cancellation; extracted createCardElement() factory; wrapped group transitions in crossFade container fade; integrated staggered buildCards({ stagger }) with delay cleanup.

Sequence Diagram

sequenceDiagram
    participant User
    participant applyFilter
    participant applyFilterDiff
    participant DOM as DOM Elements
    participant CSS as CSS Transitions
    participant Cleanup as Cleanup Timer

    User->>applyFilter: Filter input changed
    applyFilter->>applyFilterDiff: Compute newFiltered items
    applyFilterDiff->>DOM: Retain survivors (no DOM change)
    applyFilterDiff->>DOM: Seed enterers with opacity:0, scale(0.6)
    applyFilterDiff->>DOM: Assign transitionDelay to enterers
    applyFilterDiff->>CSS: Force reflow, trigger updatePositions()
    CSS->>DOM: Animate enterers fade-in, scale-up
    applyFilterDiff->>DOM: Add is-leaving class to leavers
    CSS->>DOM: Animate leavers fade-out, shrink
    CSS->>Cleanup: Wait for animation duration
    Cleanup->>DOM: Remove leaver elements from DOM
    Cleanup->>DOM: Clear stale transitionDelay
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Main 2 #21: Modifies carousel animation and DOM update code (e.g., buildCards/updatePositions, element removal/animation flows in newtab.js), sharing implementation patterns with filter diff and staggered reveal logic.

Poem

🐰✨ Cards pop in waves, center-out so bright,
Leavers fade with grace, enterers take flight!
Glows frame the stage, type stands tall and clear,
A polish so fine—the carousel shines here! 🎠

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'UI polish: framed stage, typography, motion' directly and concisely summarizes the three main feature areas of the changeset.
Linked Issues check ✅ Passed The PR implements all four primary objectives from issue #27: framed stage (viewport glow, vignette, horizon shelf), typography pass (search bar promotion, card text scaling, footer label boosting), filter & group transition animations (diff-based motion with .card.is-leaving hook, createCardElement refactor), and staggered card reveal (center-out 50ms steps, STAGGER_CAP=5, buildCards({ stagger: true }), 700ms cleanup).
Out of Scope Changes check ✅ Passed All code changes in newtab.js, newtab.html, and newtab.css align with the four specified feature areas; no unrelated refactoring, physics changes, keyboard shortcuts, drag-to-close, or undo toast modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ui-polish

Review rate limit: 4/5 reviews remaining, refill in 12 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
newtab.js (1)

688-697: Derive leaver cleanup from the actual transition timing.

The leave path removes the node after 280ms, but .card is still transitioning transform and opacity for 400ms. That makes the exit timing fragile and can clip the fade/shrink if the CSS easing or duration changes. Prefer transitionend or a shared duration constant/custom property here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@newtab.js` around lines 688 - 697, The removal timeout uses a hardcoded 280ms
which can clip the CSS transition; change the cleanup in the oldByKey.forEach
leave path to rely on the card's actual transition timing by listening for the
transitionend event on the element added in card.classList.add('is-leaving')
(and only act when the transitioned property is opacity or transform), then
remove the node and call leavingByKey.delete(key) in that handler; also add a
safe fallback by computing the computedStyle transitionDuration/transitionDelay
(or reading a shared CSS custom property) and using that calculated timeout if
transitionend doesn't fire.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/superpowers/specs/2026-04-28-framed-stage-design.md`:
- Around line 44-45: The spec text incorrectly instructs moving the .shelf
element out of .stage-wrap; update the sentence to match the PR: state that
.shelf remains in the HTML inside .stage-wrap but is made viewport-wide via CSS
(e.g., positioned/fitted to span left→right in the viewport) rather than being
relocated in the DOM, and keep the existing fade-out gradient stops (transparent
→ violet → transparent) and the vertical positioning guidance (match the
on-screen y where the bounded shelf sits). Reference the .shelf and .stage-wrap
selectors when making this edit.

In `@newtab.css`:
- Around line 99-109: The font-family declaration inside the .search rule uses
quoted family name 'Syne' which trips stylelint; update the .search CSS rule's
font-family property to use Syne without quotes (font-family: Syne, sans-serif)
so the unquoted family name is lint-compliant and keeps the fallback intact.

In `@newtab.js`:
- Around line 248-250: Before wiping the DOM in buildCards({stagger}), cancel
and clear any pending "leaver" state: iterate leavingByKey (the map/object used
to track pending removals), call clearTimeout on each stored timer, remove any
references to detached nodes, then clear leavingByKey, and only after that set
cardsEl.innerHTML = '' and reset cardEls = []; this ensures timers won't later
try to restore nodes that have been removed and avoids resurrecting detached
elements in applyFilterDiff().

---

Nitpick comments:
In `@newtab.js`:
- Around line 688-697: The removal timeout uses a hardcoded 280ms which can clip
the CSS transition; change the cleanup in the oldByKey.forEach leave path to
rely on the card's actual transition timing by listening for the transitionend
event on the element added in card.classList.add('is-leaving') (and only act
when the transitioned property is opacity or transform), then remove the node
and call leavingByKey.delete(key) in that handler; also add a safe fallback by
computing the computedStyle transitionDuration/transitionDelay (or reading a
shared CSS custom property) and using that calculated timeout if transitionend
doesn't fire.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 80c35767-cf5c-424e-9af3-2bb2ac51dc72

📥 Commits

Reviewing files that changed from the base of the PR and between d808f55 and 961becb.

📒 Files selected for processing (7)
  • docs/superpowers/specs/2026-04-28-filter-animation-design.md
  • docs/superpowers/specs/2026-04-28-framed-stage-design.md
  • docs/superpowers/specs/2026-04-28-stagger-animation-design.md
  • docs/superpowers/specs/2026-04-28-typography-pass-design.md
  • newtab.css
  • newtab.html
  • newtab.js

Comment on lines +44 to +45
- Move `.shelf` out of `.stage-wrap` in the HTML and reposition it as a fixed element spanning `left: 0; right: 0`. Vertical position: match the on-screen y where the bounded shelf currently sits (the bottom edge of `.stage-wrap`, which in the current centered layout is approximately the vertical midline of the viewport plus ~136px). Use `top: 50%` plus a fixed offset, or measure once and hardcode — whichever is cleaner once implementing.
- Keep the existing fade-out gradient stops (transparent → violet → transparent) so the line dies into the void naturally on wide screens.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update the shelf step to match the implementation.

This still says .shelf should move out of .stage-wrap and become fixed, but the PR keeps the HTML unchanged and makes the shelf viewport-wide in CSS instead. Please align the spec so future work does not chase the wrong structure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/specs/2026-04-28-framed-stage-design.md` around lines 44 -
45, The spec text incorrectly instructs moving the .shelf element out of
.stage-wrap; update the sentence to match the PR: state that .shelf remains in
the HTML inside .stage-wrap but is made viewport-wide via CSS (e.g.,
positioned/fitted to span left→right in the viewport) rather than being
relocated in the DOM, and keep the existing fade-out gradient stops (transparent
→ violet → transparent) and the vertical positioning guidance (match the
on-screen y where the bounded shelf sits). Reference the .shelf and .stage-wrap
selectors when making this edit.

Comment thread newtab.css
Comment on lines 99 to +109
.search {
width: 256px;
height: 38px;
padding: 0 18px 0 40px;
width: 400px;
height: 42px;
padding: 0 22px 0 46px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 20px;
color: var(--text);
font-family: 'Syne', sans-serif;
font-size: 13px;
font-weight: 400;
font-size: 15px;
font-weight: 500;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove the quotes around Syne in this hunk.

Stylelint is flagging font-family-name-quotes here, so this block will keep failing lint until the family name is unquoted.

Suggested fix
-  font-family: 'Syne', sans-serif;
+  font-family: Syne, sans-serif;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.search {
width: 256px;
height: 38px;
padding: 0 18px 0 40px;
width: 400px;
height: 42px;
padding: 0 22px 0 46px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 20px;
color: var(--text);
font-family: 'Syne', sans-serif;
font-size: 13px;
font-weight: 400;
font-size: 15px;
font-weight: 500;
.search {
width: 400px;
height: 42px;
padding: 0 22px 0 46px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 20px;
color: var(--text);
font-family: Syne, sans-serif;
font-size: 15px;
font-weight: 500;
🧰 Tools
🪛 Stylelint (17.9.0)

[error] 107-107: Expected no quotes around "Syne" (font-family-name-quotes)

(font-family-name-quotes)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@newtab.css` around lines 99 - 109, The font-family declaration inside the
.search rule uses quoted family name 'Syne' which trips stylelint; update the
.search CSS rule's font-family property to use Syne without quotes (font-family:
Syne, sans-serif) so the unquoted family name is lint-compliant and keeps the
fallback intact.

Comment thread newtab.js
Comment on lines +248 to 250
function buildCards({ stagger = false } = {}) {
cardsEl.innerHTML = '';
cardEls = [];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clear pending leaver state before a wholesale rebuild.

buildCards() blows away the DOM and resets cardEls, but any entries already parked in leavingByKey keep their timers and detached nodes. If a re-match lands before those timers fire, applyFilterDiff() can restore a card that is no longer attached and never re-append it. Cancel and clear leavingByKey before cardsEl.innerHTML = ''.

Suggested fix
+function clearLeavingCards() {
+  leavingByKey.forEach(({ timer }) => clearTimeout(timer));
+  leavingByKey.clear();
+}
+
 function buildCards({ stagger = false } = {}) {
+  clearLeavingCards();
   cardsEl.innerHTML = '';
   cardEls = [];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@newtab.js` around lines 248 - 250, Before wiping the DOM in
buildCards({stagger}), cancel and clear any pending "leaver" state: iterate
leavingByKey (the map/object used to track pending removals), call clearTimeout
on each stored timer, remove any references to detached nodes, then clear
leavingByKey, and only after that set cardsEl.innerHTML = '' and reset cardEls =
[]; this ensures timers won't later try to restore nodes that have been removed
and avoids resurrecting detached elements in applyFilterDiff().

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.

Polish pass on TabFlow new-tab UI

1 participant