feat(frontend): refactor and modernize welcome page (#3614)

* feat(frontend): refactor and modernize welcome page - also fixes navbar issue.

Closes: #1533

* fix(frontend): lint issues & use standard variables instead of the constants from error.vue

* fix(frontend): remove creator count as it's not a count of all users

* fix(frontend): lang reshuffle

* feat: rinthbot

* fix: lint issues

* fix: sizing of bot on mobile & scss cleanup for error.vue

* fix: lint issues

* fix: ui lint
This commit is contained in:
Calum H. 2025-05-08 17:14:25 +01:00 committed by GitHub
parent b59f208e91
commit 6e46317a37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 228 additions and 110 deletions

View File

@ -5,12 +5,7 @@
<Logo404 /> <Logo404 />
</div> </div>
<div class="error-box" :class="{ 'has-bot': !is404 }"> <div class="error-box" :class="{ 'has-bot': !is404 }">
<img <img v-if="!is404" :src="SadRinthbot" alt="Sad Modrinth bot" class="error-box__sad-bot" />
v-if="!is404"
src="https://cdn-raw.modrinth.com/sad-bot.webp"
alt="Sad Modrinth bot"
class="error-box__sad-bot"
/>
<div v-if="!is404" class="error-box__top-glow" /> <div v-if="!is404" class="error-box__top-glow" />
<div class="error-box__body"> <div class="error-box__body">
<h1 class="error-box__title">{{ formatMessage(errorMessages.title) }}</h1> <h1 class="error-box__title">{{ formatMessage(errorMessages.title) }}</h1>
@ -55,6 +50,7 @@
<script setup> <script setup>
import { defineMessage, useVIntl } from "@vintl/vintl"; import { defineMessage, useVIntl } from "@vintl/vintl";
import { SadRinthbot } from "@modrinth/assets";
import Logo404 from "~/assets/images/404.svg"; import Logo404 from "~/assets/images/404.svg";
const { formatMessage } = useVIntl(); const { formatMessage } = useVIntl();
@ -272,6 +268,19 @@ const routeMessages = [
} }
} }
.error-graphic {
margin-bottom: 2rem;
display: flex;
justify-content: center;
svg {
fill: var(--color-text);
color: var(--color-text);
width: min(15rem, 100%);
height: auto;
}
}
.error-box { .error-box {
background-color: var(--color-raised-bg); background-color: var(--color-raised-bg);
border-radius: 1.25rem; border-radius: 1.25rem;
@ -281,51 +290,41 @@ const routeMessages = [
gap: 1.25rem; gap: 1.25rem;
box-shadow: var(--shadow-card); box-shadow: var(--shadow-card);
position: relative; position: relative;
}
.error-box.has-bot { &.has-bot {
margin-block: 120px; margin-block: 120px;
} }
.error-box p { p {
margin: 0; margin: 0;
} }
.error-box a { a {
color: var(--color-brand); color: var(--color-brand);
font-weight: 600; font-weight: 600;
}
.error-box a:hover, &:hover,
.error-box a:focus { &:focus {
filter: brightness(1.125); filter: brightness(1.125);
text-decoration: underline; text-decoration: underline;
} }
}
.error-graphic { &__sad-bot {
margin-bottom: 2rem;
display: flex;
justify-content: center;
}
.error-graphic svg {
fill: var(--color-text);
color: var(--color-text);
width: min(15rem, 100%);
height: auto;
}
.error-box__sad-bot {
--_bot-height: 112px; --_bot-height: 112px;
position: absolute; position: absolute;
top: calc(-1 * var(--_bot-height)); top: calc(-1 * var(--_bot-height));
right: 5rem; right: 5rem;
width: auto; width: auto;
height: var(--_bot-height); height: var(--_bot-height);
}
.error-box__top-glow { @media screen and (max-width: 768px) {
--_bot-height: 70px;
right: 2rem;
}
}
&__top-glow {
width: 100%; width: 100%;
height: 1px; height: 1px;
background: linear-gradient( background: linear-gradient(
@ -339,47 +338,48 @@ const routeMessages = [
top: 0; top: 0;
left: 0; left: 0;
opacity: 0.4; opacity: 0.4;
} }
.error-box__title { &__title {
font-size: 2rem; font-size: 2rem;
font-weight: 900; font-weight: 900;
margin: 0; margin: 0;
} }
.error-box__subtitle { &__subtitle {
font-size: 1.25rem; font-size: 1.25rem;
font-weight: 600; font-weight: 600;
} }
.error-box__body { &__body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.75rem; gap: 0.75rem;
} }
.error-box__list-title { &__list-title {
font-weight: 600; font-weight: 600;
} }
.error-box__list { &__list {
margin: 0; margin: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.25rem; gap: 0.25rem;
padding-left: 1.25rem; padding-left: 1.25rem;
} }
.error-box li { li {
line-height: 1.5; line-height: 1.5;
} }
.error-box__details { &__details {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
color: var(--color-secondary); color: var(--color-secondary);
gap: 0.25rem; gap: 0.25rem;
font-weight: 500; font-weight: 500;
font-size: 0.875rem; font-size: 0.875rem;
}
} }
</style> </style>

View File

@ -159,7 +159,7 @@
"message": "Subscribe to updates about Modrinth" "message": "Subscribe to updates about Modrinth"
}, },
"auth.welcome.description": { "auth.welcome.description": {
"message": "Thank you for creating an account. You can now follow and create projects, receive updates about your favorite projects, and more!" "message": "Youre now part of the awesome community of creators & explorers already building, downloading, and staying up-to-date with amazing mods."
}, },
"auth.welcome.label.tos": { "auth.welcome.label.tos": {
"message": "By creating an account, you have agreed to Modrinth's <terms-link>Terms</terms-link> and <privacy-policy-link>Privacy Policy</privacy-policy-link>." "message": "By creating an account, you have agreed to Modrinth's <terms-link>Terms</terms-link> and <privacy-policy-link>Privacy Policy</privacy-policy-link>."

View File

@ -1,10 +1,20 @@
<template> <template>
<div> <div class="welcome-box has-bot">
<h1>{{ formatMessage(messages.welcomeLongTitle) }}</h1> <img :src="WavingRinthbot" alt="Waving Modrinth Bot" class="welcome-box__waving-bot" />
<div class="welcome-box__top-glow" />
<div class="welcome-box__body">
<h1 class="welcome-box__title">
{{ formatMessage(messages.welcomeLongTitle) }}
</h1>
<section class="auth-form"> <p class="welcome-box__subtitle">
<p> <IntlFormatted :message-id="messages.welcomeDescription">
{{ formatMessage(messages.welcomeDescription) }} <template #bold="{ children }">
<strong>
<component :is="() => normalizeChildren(children)" />
</strong>
</template>
</IntlFormatted>
</p> </p>
<Checkbox <Checkbox
@ -14,11 +24,12 @@
:description="formatMessage(messages.subscribeCheckbox)" :description="formatMessage(messages.subscribeCheckbox)"
/> />
<button class="btn btn-primary continue-btn centered-btn" @click="continueSignUp"> <button class="btn btn-primary centered-btn" @click="continueSignUp">
{{ formatMessage(commonMessages.continueButton) }} <RightArrowIcon /> {{ formatMessage(commonMessages.continueButton) }}
<RightArrowIcon />
</button> </button>
<p> <p class="tos-text">
<IntlFormatted :message-id="messages.tosLabel"> <IntlFormatted :message-id="messages.tosLabel">
<template #terms-link="{ children }"> <template #terms-link="{ children }">
<NuxtLink to="/legal/terms" class="text-link"> <NuxtLink to="/legal/terms" class="text-link">
@ -32,12 +43,15 @@
</template> </template>
</IntlFormatted> </IntlFormatted>
</p> </p>
</section> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { Checkbox, commonMessages } from "@modrinth/ui"; import { Checkbox, commonMessages } from "@modrinth/ui";
import { RightArrowIcon } from "@modrinth/assets"; import { RightArrowIcon, WavingRinthbot } from "@modrinth/assets";
const route = useRoute();
const { formatMessage } = useVIntl(); const { formatMessage } = useVIntl();
@ -54,7 +68,7 @@ const messages = defineMessages({
welcomeDescription: { welcomeDescription: {
id: "auth.welcome.description", id: "auth.welcome.description",
defaultMessage: defaultMessage:
"Thank you for creating an account. You can now follow and create projects, receive updates about your favorite projects, and more!", "Youre now part of the awesome community of creators & explorers already building, downloading, and staying up-to-date with awazing mods.",
}, },
welcomeLongTitle: { welcomeLongTitle: {
id: "auth.welcome.long-title", id: "auth.welcome.long-title",
@ -72,20 +86,18 @@ useHead({
const subscribe = ref(true); const subscribe = ref(true);
async function continueSignUp() { onMounted(async () => {
const route = useRoute();
await useAuth(route.query.authToken); await useAuth(route.query.authToken);
await useUser(); await useUser();
});
async function continueSignUp() {
if (subscribe.value) { if (subscribe.value) {
try { try {
await useBaseFetch("auth/email/subscribe", { await useBaseFetch("auth/email/subscribe", {
method: "POST", method: "POST",
}); });
} catch { } catch {}
/* empty */
}
} }
if (route.query.redirect) { if (route.query.redirect) {
@ -95,3 +107,84 @@ async function continueSignUp() {
} }
} }
</script> </script>
<style lang="scss" scoped>
.welcome-box {
background-color: var(--color-raised-bg);
border-radius: var(--size-rounded-lg);
padding: 1.75rem 2rem;
display: flex;
flex-direction: column;
gap: 1.25rem;
box-shadow: var(--shadow-card);
position: relative;
&.has-bot {
margin-block: 120px;
}
p {
margin: 0;
}
a {
color: var(--color-brand);
font-weight: var(--weight-bold);
&:hover,
&:focus {
filter: brightness(1.125);
text-decoration: underline;
}
}
&__waving-bot {
--bot-height: 112px;
position: absolute;
top: calc(-1 * var(--bot-height));
right: 5rem;
height: var(--bot-height);
width: auto;
@media (max-width: 768px) {
--bot-height: 70px;
right: 2rem;
}
}
&__top-glow {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 1px;
opacity: 0.4;
background: linear-gradient(
to right,
transparent 2rem,
var(--color-green) calc(100% - 13rem),
var(--color-green) calc(100% - 5rem),
transparent calc(100% - 2rem)
);
}
&__body {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
&__title {
font-size: var(--text-32);
font-weight: var(--weight-extrabold);
margin: 0;
}
&__subtitle {
font-size: var(--text-18);
}
.tos-text {
font-size: var(--text-14);
line-height: 1.5;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

View File

@ -4,3 +4,8 @@ declare module '*.svg?component' {
const src: FunctionalComponent<SVGAttributes> const src: FunctionalComponent<SVGAttributes>
export default src export default src
} }
declare module '*.webp' {
const src: string
export default src
}

View File

@ -4,6 +4,16 @@
import _ModrinthIcon from './branding/logo.svg?component' import _ModrinthIcon from './branding/logo.svg?component'
import _FourOhFourNotFound from './branding/404.svg?component' import _FourOhFourNotFound from './branding/404.svg?component'
import _ModrinthPlusIcon from './branding/modrinth-plus.svg?component' import _ModrinthPlusIcon from './branding/modrinth-plus.svg?component'
import _AngryRinthbot from './branding/rinthbot/angry.webp'
import _AnnoyedRinthbot from './branding/rinthbot/annoyed.webp'
import _ConfusedRinthbot from './branding/rinthbot/confused.webp'
import _ExcitedRinthbot from './branding/rinthbot/excited.webp'
import _LaughingRinthbot from './branding/rinthbot/laughing.webp'
import _SadRinthbot from './branding/rinthbot/sad.webp'
import _SleepingRinthbot from './branding/rinthbot/sleeping.webp'
import _SobbingRinthbot from './branding/rinthbot/sobbing.webp'
import _ThinkingRinthbot from './branding/rinthbot/thinking.webp'
import _WavingRinthbot from './branding/rinthbot/waving.webp'
// External Icons // External Icons
import _SSODiscordIcon from './external/sso/discord.svg?component' import _SSODiscordIcon from './external/sso/discord.svg?component'
@ -217,6 +227,16 @@ import './omorphia.scss'
export const ModrinthIcon = _ModrinthIcon export const ModrinthIcon = _ModrinthIcon
export const FourOhFourNotFound = _FourOhFourNotFound export const FourOhFourNotFound = _FourOhFourNotFound
export const ModrinthPlusIcon = _ModrinthPlusIcon export const ModrinthPlusIcon = _ModrinthPlusIcon
export const AngryRinthbot = _AngryRinthbot
export const AnnoyedRinthbot = _AnnoyedRinthbot
export const ConfusedRinthbot = _ConfusedRinthbot
export const ExcitedRinthbot = _ExcitedRinthbot
export const LaughingRinthbot = _LaughingRinthbot
export const SadRinthbot = _SadRinthbot
export const SleepingRinthbot = _SleepingRinthbot
export const SobbingRinthbot = _SobbingRinthbot
export const ThinkingRinthbot = _ThinkingRinthbot
export const WavingRinthbot = _WavingRinthbot
export const SSODiscordIcon = _SSODiscordIcon export const SSODiscordIcon = _SSODiscordIcon
export const SSOGitHubIcon = _SSOGitHubIcon export const SSOGitHubIcon = _SSOGitHubIcon
export const SSOGitLabIcon = _SSOGitLabIcon export const SSOGitLabIcon = _SSOGitLabIcon