Conversation
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
There was a problem hiding this comment.
Pull request overview
Updates the badges UI to match the “badge design QA” direction by refining badge presentation (grid/layout), share affordances, and adding small visual effects/animations, along with a popover click-outside behavior tweak.
Changes:
- Adjust popover click-outside handling to use capture-phase document listeners.
- Improve badges share UI with tooltips and refine rank label presentation.
- Add badge shine utility styles and apply shine/animation effects to badge imagery and popovers.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/app/components/uikit/popover/popover.vue | Changes document click listener registration to capture phase for click-outside behavior. |
| frontend/app/components/modules/badges/components/share/badges-share-socials.vue | Wraps share icon buttons in tooltips and adds tooltip import. |
| frontend/app/components/modules/badges/components/share/badges-share-preview.vue | Conditionally adds “Rank:” prefix based on computed layout heuristic. |
| frontend/app/components/modules/badges/components/download/card-download-preview.vue | Always displays “Rank: #…” label in download preview. |
| frontend/app/components/modules/badges/components/badges.vue | Switches badges list layout from flex to grid with explicit column sizing. |
| frontend/app/components/modules/badges/components/badges-popover.vue | Adds popover entrance animation and applies badge shine wrapper to the image. |
| frontend/app/components/modules/badges/components/badges-item.vue | Applies badge shine wrapper to badge thumbnails. |
| frontend/app/assets/styles/utils/index.scss | Registers the new badge shine utility stylesheet. |
| frontend/app/assets/styles/utils/_badge-shine.scss | Introduces the .badge-shine animated overlay utility. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const badgeImage = computed(() => props.badge.config.badgeImages[props.badge.tier]); | ||
| const showRankLabel = computed( | ||
| () => props.badge.config.title.length + String(props.badge.rank).length <= 22 || props.badge.rank < 1000, | ||
| ); |
There was a problem hiding this comment.
showRankLabel relies on a hard-coded character-count threshold (22) and string lengths, which is brittle (font differences/localization) and hard to reason about later. Consider extracting the threshold into a named constant with a short comment, or using a layout/CSS-based approach (e.g., nowrap/ellipsis) to decide when to show the "Rank:" prefix.
There was a problem hiding this comment.
I think it is better to use ellipsis here rather than the fix character length check.
| @@ -43,7 +44,8 @@ SPDX-License-Identifier: MIT | |||
| <!-- Badges Grid --> | |||
| <div | |||
| v-else | |||
| class="flex flex-wrap items-start gap-x-4 gap-y-2" | |||
| class="grid gap-x-4 gap-y-2 items-start" | |||
| style="grid-template-columns: repeat(auto-fill, minmax(52px, 1fr))" | |||
| > | |||
There was a problem hiding this comment.
The grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)) inline style is duplicated for loading vs. loaded states. Consider extracting this into a shared class/style binding (or a Tailwind arbitrary value if supported) to avoid divergence and keep the template easier to maintain.
| <div class="badge-shine relative overflow-hidden size-20"> | ||
| <img | ||
| :src="badgeImage" | ||
| :alt="badge.config.title" | ||
| class="size-20" | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
.badge-shine already sets position: relative and overflow: hidden, so adding relative overflow-hidden here is redundant. Consider removing the extra utility classes so the source of these behaviors stays in one place.
| &::after { | ||
| content: ''; | ||
| top: 0; | ||
| transform: translateX(100%); | ||
| width: 100%; | ||
| height: 100%; | ||
| position: absolute; | ||
| z-index: 1; | ||
| animation: badge-shine 5s infinite; | ||
| background: linear-gradient( |
There was a problem hiding this comment.
The ::after overlay is position: absolute with top: 0 but no left/right (or inset). That can leave its horizontal position dependent on the static position and can misalign the shine overlay. Set left: 0 (or inset: 0) to ensure it consistently covers the badge.
| const openPopover = async () => { | ||
| isVisible.value = true; | ||
| if (props.triggerEvent === 'click') { | ||
| document.addEventListener('click', handleClickOutside); | ||
| document.addEventListener('click', handleClickOutside, true); | ||
| } | ||
| }; | ||
|
|
||
| const closePopover = () => { | ||
| isVisible.value = false; | ||
| if (props.triggerEvent === 'click') { | ||
| document.removeEventListener('click', handleClickOutside); | ||
| document.removeEventListener('click', handleClickOutside, true); | ||
| } |
There was a problem hiding this comment.
The document click listener is only added/removed via openPopover()/closePopover(). If visibility is controlled externally (v-model / prop updates) or the component unmounts while open, the listener can be left attached and click-outside handling can be out of sync. Consider managing the listener in a watcher/onBeforeUnmount based on isVisible (and always removing it on unmount) so it stays consistent regardless of how visibility changes.
| <lfx-tooltip content="X/Twitter"> | ||
| <button | ||
| type="button" | ||
| class="size-9 rounded-full bg-black flex items-center justify-center hover:opacity-80 transition-opacity" | ||
| @click="shareToX" | ||
| > |
There was a problem hiding this comment.
These are icon-only buttons. Wrapping them in a tooltip doesn’t provide an accessible name for screen readers; please add an aria-label (or visually hidden text) to each button so the share targets are announced correctly.
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
emlimlf
left a comment
There was a problem hiding this comment.
There is one minor feedback from copilot, but it would be better for future maintainability.
| const badgeImage = computed(() => props.badge.config.badgeImages[props.badge.tier]); | ||
| const showRankLabel = computed( | ||
| () => props.badge.config.title.length + String(props.badge.rank).length <= 22 || props.badge.rank < 1000, | ||
| ); |
There was a problem hiding this comment.
I think it is better to use ellipsis here rather than the fix character length check.
No description provided.