feat: new user badges, ui consistency pass

This commit is contained in:
Prospector
2026-05-31 02:11:11 -07:00
parent 71286f7b2b
commit aec6d8803f
38 changed files with 838 additions and 125 deletions
+3
View File
@@ -104,6 +104,9 @@ export default defineNuxtConfig({
params: {
overrides: {
removeViewBox: false,
cleanupIds: {
minify: false,
},
},
},
},
@@ -1,18 +1,18 @@
/*
Cards and body styling
*/
// CARDS
.base-card {
padding: var(--spacing-card-lg);
padding: 1rem;
position: relative;
min-height: var(--font-size-2xl);
background-color: var(--color-raised-bg);
border-radius: var(--size-rounded-card);
background-color: var(--surface-3);
border-radius: var(--radius-lg);
border: 1px solid var(--surface-4);
margin-bottom: var(--spacing-card-md);
margin-bottom: var(--gap-md);
outline: 2px solid transparent;
outline-offset: -2px;
box-shadow: var(--shadow-card);
.card__overlay {
position: absolute;
@@ -25,6 +25,17 @@
z-index: 2;
}
&:where(&.warning, &.information) {
padding: 1.5rem;
line-height: 1.5;
min-height: 0;
a {
color: var(--color-blue);
text-decoration: underline;
}
}
&.moderation-card {
background-color: var(--color-warning-banner-bg);
}
@@ -3,7 +3,7 @@
<AutoLink
:to="currentAd.link"
:aria-label="currentAd.description"
class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 rounded-[inherit] bg-bg-raised"
class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 rounded-[inherit] border border-solid border-surface-4 bg-surface-3"
>
<img
:src="currentAd.light"
@@ -19,7 +19,7 @@
/>
</AutoLink>
<div
class="absolute top-0 flex items-center justify-center overflow-hidden rounded-2xl bg-bg-raised"
class="absolute top-0 flex items-center justify-center overflow-hidden rounded-2xl border border-solid border-surface-4 bg-surface-3"
>
<div id="modrinth-rail-1" />
</div>
+2 -1
View File
@@ -1,7 +1,8 @@
<template>
<nav :aria-label="ariaLabel" class="w-full">
<ul
class="card-shadow m-0 flex list-none flex-col items-start gap-1.5 rounded-2xl bg-bg-raised p-4"
class="card-shadow m-0 flex list-none flex-col items-start gap-1.5 rounded-2xl border border-solid border-surface-4 bg-surface-3 p-4"
:class="{ 'pt-3': filteredItems?.[0]?.type === 'heading' }"
>
<slot v-if="hasSlotContent" />
+24 -74
View File
@@ -460,8 +460,11 @@
</div>
</div>
<div class="normal-page__sidebar">
<div v-if="organizations?.length > 0" class="card flex-card">
<h2 class="text-lg text-contrast">
<div
v-if="organizations?.length > 0"
class="mb-4 rounded-2xl border border-solid border-surface-4 bg-surface-3 p-4 pt-3"
>
<h2 class="m-0 mb-2 text-lg text-contrast">
{{ formatMessage(messages.profileOrganizations) }}
</h2>
<div class="flex flex-wrap gap-2">
@@ -476,24 +479,14 @@
</nuxt-link>
</div>
</div>
<div v-if="badges.length > 0" class="card flex-card">
<h2 class="text-lg text-contrast">
{{ formatMessage(messages.profileBadges) }}
</h2>
<div class="flex flex-wrap gap-2">
<div v-for="badge in badges" :key="badge">
<StaffBadge v-if="badge === 'staff'" class="h-14 w-14" />
<ModBadge v-else-if="badge === 'mod'" class="h-14 w-14" />
<nuxt-link v-else-if="badge === 'plus'" to="/plus">
<PlusBadge class="h-14 w-14" />
</nuxt-link>
<TenMClubBadge v-else-if="badge === '10m-club'" class="h-14 w-14" />
<EarlyAdopterBadge v-else-if="badge === 'early-adopter'" class="h-14 w-14" />
<AlphaTesterBadge v-else-if="badge === 'alpha-tester'" class="h-14 w-14" />
<BetaTesterBadge v-else-if="badge === 'beta-tester'" class="h-14 w-14" />
</div>
</div>
</div>
<UserBadges
:downloads="sumDownloads"
:join-date="joinDate"
:role="user.role"
:badges="user.badges"
:earliest-project-by-type="earliestProjectByType"
class="mb-4 rounded-2xl border border-solid border-surface-4 bg-surface-3 p-4 pt-3"
/>
<AdPlaceholder v-if="!auth.user" />
</div>
</div>
@@ -540,6 +533,7 @@ import {
useCompactNumber,
useFormatDateTime,
useFormatNumber,
UserBadges,
useRelativeTime,
useVIntl,
} from '@modrinth/ui'
@@ -547,13 +541,6 @@ import { isAdmin, isStaff, UserBadge } from '@modrinth/utils'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { onServerPrefetch } from 'vue'
import TenMClubBadge from '~/assets/images/badges/10m-club.svg?component'
import AlphaTesterBadge from '~/assets/images/badges/alpha-tester.svg?component'
import BetaTesterBadge from '~/assets/images/badges/beta-tester.svg?component'
import EarlyAdopterBadge from '~/assets/images/badges/early-adopter.svg?component'
import ModBadge from '~/assets/images/badges/mod.svg?component'
import PlusBadge from '~/assets/images/badges/plus.svg?component'
import StaffBadge from '~/assets/images/badges/staff.svg?component'
import UpToDate from '~/assets/images/illustrations/up_to_date.svg?component'
import AdPlaceholder from '~/components/ui/AdPlaceholder.vue'
import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.vue'
@@ -859,57 +846,21 @@ const sumDownloads = computed(() => {
})
const joinDate = computed(() => new Date(user.value.created))
const MODRINTH_BETA_END_DATE = new Date('2022-02-27T08:00:00.000Z')
const MODRINTH_ALPHA_END_DATE = new Date('2020-11-30T08:00:00.000Z')
const badges = computed(() => {
const badges = []
if (user.value.role === 'admin') {
badges.push('staff')
}
if (user.value.role === 'moderator') {
badges.push('mod')
}
if (isPermission(user.value.badges, 1 << 0)) {
badges.push('plus')
}
if (sumDownloads.value > 10000000) {
badges.push('10m-club')
}
if (
isPermission(user.value.badges, 1 << 1) ||
isPermission(user.value.badges, 1 << 2) ||
isPermission(user.value.badges, 1 << 3)
) {
badges.push('early-adopter')
}
if (isPermission(user.value.badges, 1 << 4) || joinDate.value < MODRINTH_ALPHA_END_DATE) {
badges.push('alpha-tester')
} else if (isPermission(user.value.badges, 1 << 4) || joinDate.value < MODRINTH_BETA_END_DATE) {
badges.push('beta-tester')
}
if (isPermission(user.value.badges, 1 << 5)) {
badges.push('contributor')
}
if (isPermission(user.value.badges, 1 << 6)) {
badges.push('translator')
}
return badges
})
async function copyId() {
await navigator.clipboard.writeText(user.value.id)
}
const earliestProjectByType = computed(() => {
const obj = {}
for (const project of projects.value ?? []) {
obj[project.project_type] = new Date(project.published)
}
return obj
})
async function copyPermalink() {
await navigator.clipboard.writeText(`${config.public.siteUrl}/user/${user.value.id}`)
}
@@ -1012,7 +963,6 @@ export default defineNuxtComponent({
}
.description {
// Grow to take up remaining space
flex-grow: 1;
color: var(--color-text);