Skip to content

feat: improve micro-interactions across CTA, pagination, and likes cache - #62

Merged
fringe4life merged 1 commit into
mainfrom
feat/micro-interactions
Aug 3, 2026
Merged

feat: improve micro-interactions across CTA, pagination, and likes cache#62
fringe4life merged 1 commit into
mainfrom
feat/micro-interactions

Conversation

@fringe4life

Copy link
Copy Markdown
Owner

Summary

✨ Added arrowRecipe and enhanced CTA, back-link, pagination chevron micro-interactions
🎨 Adopted customizable <select> for pagination limit control
🔀 Made pagination View Transition slide distance responsive on small screens
♻️ Replaced invalidateModel with invalidateAllModels for like cache correctness (simple option for now)
🎨 Split brandOutline from outline so GitHub sign-in stays neutral
🐛 Fixed hasPreviousPage / prev-button disabled logic
📝 Documented simple models cache approach in README and docs/MODEL_CACHE_SPLIT.md
📦 Bumped Biome, Playwright, React types, Varlock

Closes #59
Closes #55
Closes #56
Closes #57
Closes #58
Closes #60
Closes #61

Test plan

  • Home CTA: hover/focus/active moves arrow; brand border + outline on focus-visible
  • Model detail back link: left arrow slides on hover/focus within group
  • Pagination prev/next: chevrons animate; prev disabled on page 0; next disabled when no next page
  • Pagination limit: customizable select opens with picker transition; changing limit updates URL/results
  • Mobile vs md+: page change View Transition uses full-viewport slide on small screens, compact slide from md
  • Sort idle pills use brandOutline; GitHub sign-in button keeps neutral outline
  • Toggle like: lists/popular reorder on refresh; no leftover invalidateModel usage
  • bun run type and bun run check pass

Made with Cursor

✨ Added arrowRecipe and enhanced CTA, back-link, pagination chevron micro-interactions
🎨 Adopted customizable select for pagination limit control
🔀 Made pagination View Transition slide distance responsive on small screens
♻️ Replaced invalidateModel with invalidateAllModels for like cache correctness
🎨 Split brandOutline from outline so GitHub sign-in stays neutral
🐛 Fixed hasPreviousPage / prev-button disabled logic
📝 Documented simple models cache approach in README and MODEL_CACHE_SPLIT.md
📦 Bumped Biome, Playwright, React types, Varlock

Co-authored-by: Cursor <cursoragent@cursor.com>
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit af102ba.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Micro-interactions for CTA/pagination + like-cache invalidation fix

🐞 Bug fix ✨ Enhancement 📝 Documentation ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Added arrowRecipe and applied hover/focus/active micro-interactions to CTA, back-link, and
 pagination chevrons.
• Replaced fixed-size limit ` with a customizable  (appearance: base-select`, picker
 transitions) and made pagination View Transition slide distance responsive on small screens.
• Fixed hasPreviousPage/prev-button disabled logic bug and replaced invalidateModel with broader
 invalidateAllModels for like-cache correctness.
• Split brandOutline from outline button variants so GitHub sign-in stays neutral while sort
 pills get brand styling.
• Documented the simplified models cache invalidation approach in README and new
 docs/MODEL_CACHE_SPLIT.md.
• Bumped Biome, Playwright, React types, and Varlock dependency versions.
Diagram

graph TD
  A["Home CTA / Back Link"] --> B["arrowRecipe"] --> C["buttonRecipe"]
  D["Pagination Page Control"] --> B
  E["Pagination Limit Control"] --> F["Customizable select"]
  D --> G["Pagination Offset Transition"]
  H["toggleLike action"] --> I["invalidateAllModels"] --> J[("models cache tag")]
  K["Sort Option"] --> C
  subgraph Legend
    direction LR
    _mod["Component"] ~~~ _db[(Cache tag)]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Per-model tag invalidation (previous invalidateModel)
  • ➕ Cheaper — only expires the affected model's cache entry
  • ➕ Avoids re-fetching unrelated list pages
  • ➖ Does not invalidate list/category pages showing updated like counts
  • ➖ Breaks sort=popular ordering correctness after a like toggle
2. Split content vs. likes cache tags with dedicated popular-order tag
  • ➕ Avoids re-serializing stable content on every like toggle
  • ➕ Keeps popular-order correctness without invalidating unrelated content
  • ➖ Significantly more implementation complexity (separate tags, cache layers)
  • ➖ Requires restructuring DAL/query caching boundaries — deferred intentionally per docs

Recommendation: The PR explicitly chooses broad invalidation (invalidateAllModels) over per-model/per-tag invalidation as a deliberate, documented simplification given that likes affect both counts and sort=popular ordering; this is a reasonable tradeoff for a portfolio-scale project and the tradeoffs are well documented in docs/MODEL_CACHE_SPLIT.md, so no change is recommended now.

Files changed (18) +495 / -87

Enhancement (9) +343 / -31
page.tsxAdd arrow micro-interaction to home CTA link +42/-13

Add arrow micro-interaction to home CTA link

• Added a FaArrowRight icon styled with arrowRecipe and group-based hover/focus/active states to the 'Browse Models' CTA link, including focus-visible outline and brand border treatment.

src/app/page.tsx

arrow-recipe.tsNew arrowRecipe CVA for directional arrow icons +129/-0

New arrowRecipe CVA for directional arrow icons

• Added a new Panda CSS CVA recipe defining directional (left/right), distance (compact/default), and size variants for arrow icons with group-hover/focus/active translate and color transitions.

src/components/arrow-recipe.ts

button-recipe.tsSplit brandOutline variant from outline; add density variant +34/-7

Split brandOutline variant from outline; add density variant

• Added a new brandOutline button variant with brand-colored hover state distinct from the now-neutral outline variant, and introduced a density variant (default/compact) controlling gap spacing.

src/components/button-recipe.ts

button.tsxPass density prop through Button component +2/-1

Pass density prop through Button component

• Added density prop support to the shared Button component, forwarding it to buttonRecipe.

src/components/button.tsx

model-back-link.tsxAdd arrow micro-interaction to model back link +14/-2

Add arrow micro-interaction to model back link

• Wrapped the back-link button in a group and styled the FaArrowLeft icon with arrowRecipe for compact left-slide hover/focus interactions, using compact button density.

src/features/models/back-link/model-back-link.tsx

sort-option.tsxUse brandOutline variant for idle sort pills +1/-1

Use brandOutline variant for idle sort pills

• Changed the unchecked sort option button variant from outline to the new brandOutline for brand-colored idle styling.

src/features/models/sort/components/sort-option.tsx

pagination-button.tsxAdd group class to pagination button for arrow micro-interactions +13/-2

Add group class to pagination button for arrow micro-interactions

• Added a 'group' class and className merging to PaginationButton so nested arrow icons can respond to group hover/focus/active states.

src/features/pagination/components/pagination-button.tsx

pagination-limit-control.tsxAdopt customizable select with picker transitions for limit control +92/-1

Adopt customizable select with picker transitions for limit control

• Replaced the plain select styling with a customizable <select> using appearance: base-select, a selectedcontent button slot, and animated ::picker(select) open/close transitions and option checkmark styling.

src/features/pagination/components/pagination-limit-control.tsx

pagination-offset-transition.tsxMake View Transition slide distance responsive +16/-4

Make View Transition slide distance responsive

• Updated enter/exit left/right View Transition keyframe variables to use a full-viewport slide distance on small screens and a compact fixed distance from the md breakpoint.

src/features/pagination/components/pagination-offset-transition.tsx

Bug fix (3) +26 / -7
toggle-like.tsReplace invalidateModel with invalidateAllModels on like toggle +7/-2

Replace invalidateModel with invalidateAllModels on like toggle

• Switched toggleLike to call the new invalidateAllModels() instead of the removed invalidateModel(slug), ensuring list counts and sort=popular ordering stay consistent, with a doc reference comment.

src/features/models/likes/actions/toggle-like.ts

pagination-page-control.tsxFix prev-button disabled logic and add chevron micro-interactions +18/-4

Fix prev-button disabled logic and add chevron micro-interactions

• Fixed inverted disabled logic on the previous-page button (was using hasPreviousPage directly instead of negated) and replaced static chevron icons with arrowRecipe-styled animated chevrons for both prev/next buttons.

src/features/pagination/components/pagination-page-control.tsx

pagination.tsxFix hasPreviousPage calculation +1/-1

Fix hasPreviousPage calculation

• Corrected hasPreviousPage to be true when page > 0 instead of page < 1, fixing prev-button enabled/disabled state.

src/features/pagination/components/pagination.tsx

Refactor (1) +8 / -4
cache-invalidation.tsReplace per-model invalidation with invalidateAllModels +8/-4

Replace per-model invalidation with invalidateAllModels

• Removed invalidateModel(modelId) and added invalidateAllModels() which broadly expires the 'models' cache tag, with documentation comments linking to the new cache split doc.

src/utils/cache-invalidation.ts

Documentation (2) +82 / -18
README.mdUpdate docs for cache, styling, and dependency changes +21/-18

Update docs for cache, styling, and dependency changes

• Updated dependency version badges (Biome, Varlock), documented arrowRecipe, brandOutline, responsive slide distance, and the invalidateAllModels cache approach with a pointer to the new MODEL_CACHE_SPLIT.md doc.

README.md

MODEL_CACHE_SPLIT.mdAdd new doc explaining models cache invalidation strategy +61/-0

Add new doc explaining models cache invalidation strategy

• New document describing current broad invalidateAllModels behavior, rationale for popular-sort correctness, and a longer-term plan to split stable content vs volatile like counters into separate cache tags.

docs/MODEL_CACHE_SPLIT.md

Other (3) +36 / -27
bun.lockLockfile updates for dependency bumps +20/-20

Lockfile updates for dependency bumps

• Regenerated lockfile entries for Biome, Playwright, @types/react, @types/react-dom, and Varlock version bumps.

bun.lock

package.jsonBump Biome, Playwright, React types, and Varlock versions +5/-5

Bump Biome, Playwright, React types, and Varlock versions

• Updated devDependency and dependency versions for @biomejs/biome, @playwright/test, @types/react, @types/react-dom, and varlock.

package.json

global.d.tsDeclare selectedcontent JSX intrinsic element type +11/-2

Declare selectedcontent JSX intrinsic element type

• Added a TypeScript module augmentation declaring the selectedcontent custom element used by the new customizable select for TSX type-checking.

src/global.d.ts

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 8 rules

Grey Divider


Remediation recommended

1. No select fallback 🐞 Bug ☼ Reliability
Description
PaginationLimitControl now relies on customizable-select-only children (<button> /
<selectedcontent>) without feature detection, so browsers lacking that feature may render the
limit control incorrectly or make it hard/impossible to change the page size. This component is
SSR-rendered via ModelsViewInner and then hydrated, increasing the chance of cross-browser
inconsistencies surfacing in real usage.
Code

src/features/pagination/components/pagination-limit-control.tsx[R103-106]

+      <button type="button">
+        <selectedcontent
+          // biome-ignore assist/source/useSortedKeys: starting style needs to be last usually
+          className={css({
Evidence
The limit control includes customizable-select-only children inside the <select> and explicitly
documents partial browser support; it is also rendered from a server component tree, so any
unsupported-browser behavior is user-facing in SSR + hydration flows.

src/features/pagination/components/pagination-limit-control.tsx[14-127]
src/features/models/components/models-view.tsx[26-76]
src/features/pagination/components/pagination.tsx[13-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PaginationLimitControl` uses the new customizable-select structure (`appearance: base-select` + `<button>` + `<selectedcontent>` + picker pseudo-elements) but does not guard it behind feature detection or provide a classic `<select>` fallback.

## Issue Context
- The file comment itself notes partial browser support.
- This control is rendered inside the listing view (`ModelsViewInner`), so it is server-rendered and then hydrated on the client.

## Fix Focus Areas
- src/features/pagination/components/pagination-limit-control.tsx[14-127]
- src/features/models/components/models-view.tsx[26-76]

## Suggested fix
- Implement progressive enhancement:
 - Render a standards-only `<select>` (only `<option>` children) by default/SSR.
 - On the client, detect support (e.g. `CSS?.supports?.('appearance', 'base-select')`) and only then render the customizable-select children (`<button>`/`<selectedcontent>`) and related styling.
- Keep behavior identical (still calls `onLimitChange` on change) in both modes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +103 to +106
<button type="button">
<selectedcontent
// biome-ignore assist/source/useSortedKeys: starting style needs to be last usually
className={css({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. No select fallback 🐞 Bug ☼ Reliability

PaginationLimitControl now relies on customizable-select-only children (<button> /
<selectedcontent>) without feature detection, so browsers lacking that feature may render the
limit control incorrectly or make it hard/impossible to change the page size. This component is
SSR-rendered via ModelsViewInner and then hydrated, increasing the chance of cross-browser
inconsistencies surfacing in real usage.
Agent Prompt
## Issue description
`PaginationLimitControl` uses the new customizable-select structure (`appearance: base-select` + `<button>` + `<selectedcontent>` + picker pseudo-elements) but does not guard it behind feature detection or provide a classic `<select>` fallback.

## Issue Context
- The file comment itself notes partial browser support.
- This control is rendered inside the listing view (`ModelsViewInner`), so it is server-rendered and then hydrated on the client.

## Fix Focus Areas
- src/features/pagination/components/pagination-limit-control.tsx[14-127]
- src/features/models/components/models-view.tsx[26-76]

## Suggested fix
- Implement progressive enhancement:
  - Render a standards-only `<select>` (only `<option>` children) by default/SSR.
  - On the client, detect support (e.g. `CSS?.supports?.('appearance', 'base-select')`) and only then render the customizable-select children (`<button>`/`<selectedcontent>`) and related styling.
- Keep behavior identical (still calls `onLimitChange` on change) in both modes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@fringe4life

Copy link
Copy Markdown
Owner Author

Qodo, if the new customisable select doesn't work you just get the old select, this is a progressive enhancement

@qodo-code-review

Copy link
Copy Markdown

Qodo, if the new customisable select doesn't work you just get the old select, this is a progressive enhancement

That gives you a native <select>/<option> fallback when appearance: base-select and picker styling are unsupported. However, finding 1 is still relevant because the custom <button>/<selectedcontent> markup should be verified in unsupported browsers; an explicit fallback or feature-gated enhancement would make that guarantee reliable.

@fringe4life
fringe4life merged commit da8a1f5 into main Aug 3, 2026
2 checks passed
@fringe4life
fringe4life deleted the feat/micro-interactions branch August 3, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant