From fa6deebc3785d1c5c10dbb3485fef345f07b7755 Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Mon, 21 Sep 2026 07:26:27 +0300
Subject: [PATCH 01/16] Restyle global status indicator exclamation mark
---
.../global-status/components/GlobalStatusIndicator.module.css | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/features/global-status/components/GlobalStatusIndicator.module.css b/app/features/global-status/components/GlobalStatusIndicator.module.css
index e21c64b70..8b832abc1 100644
--- a/app/features/global-status/components/GlobalStatusIndicator.module.css
+++ b/app/features/global-status/components/GlobalStatusIndicator.module.css
@@ -60,5 +60,7 @@
}
.alertBadge {
- color: var(--color-warning);
+ background-color: var(--color-warning-low);
+ border: 1.5px solid var(--color-warning);
+ color: var(--color-warning-high);
}
From 807ab2eb96149ad70d8495d97215cb5b198abfe3 Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Mon, 21 Sep 2026 08:03:57 +0300
Subject: [PATCH 02/16] Rework verified social links badge
---
.../user-page/UserRepository.server.ts | 35 ++++++--
.../user-page/components/Widget.module.css | 52 ++++++++----
app/features/user-page/components/Widget.tsx | 79 ++++++++++++-------
.../2026-09-21-social-links-widget-names.md | 5 ++
4 files changed, 121 insertions(+), 50 deletions(-)
create mode 100644 changelog/2026-09-21-social-links-widget-names.md
diff --git a/app/features/user-page/UserRepository.server.ts b/app/features/user-page/UserRepository.server.ts
index a6d13927a..537d6ef93 100644
--- a/app/features/user-page/UserRepository.server.ts
+++ b/app/features/user-page/UserRepository.server.ts
@@ -1525,24 +1525,45 @@ export async function findSocialLinksByUserId(userId: number) {
if (!user) return [];
const links: Array<
- | { type: "url"; value: string }
- | { type: "popover"; platform: "discord"; value: string }
+ | {
+ type: "url";
+ platform: "twitch" | "youtube" | "bsky";
+ /** Account name on the platform, null if only an id is known */
+ name: string | null;
+ url: string;
+ }
+ | { type: "text"; platform: "discord"; name: string }
> = [];
if (user.twitch) {
- links.push({ type: "url", value: twitchUrl(user.twitch) });
+ links.push({
+ type: "url",
+ platform: "twitch",
+ name: user.twitch,
+ url: twitchUrl(user.twitch),
+ });
}
if (user.youtubeId) {
- links.push({ type: "url", value: youtubeUrl(user.youtubeId) });
+ links.push({
+ type: "url",
+ platform: "youtube",
+ name: null,
+ url: youtubeUrl(user.youtubeId),
+ });
}
if (user.bsky) {
- links.push({ type: "url", value: bskyUrl(user.bsky) });
+ links.push({
+ type: "url",
+ platform: "bsky",
+ name: user.bsky,
+ url: bskyUrl(user.bsky),
+ });
}
if (user.discordUniqueName) {
links.push({
- type: "popover",
+ type: "text",
platform: "discord",
- value: user.discordUniqueName,
+ name: user.discordUniqueName,
});
}
diff --git a/app/features/user-page/components/Widget.module.css b/app/features/user-page/components/Widget.module.css
index 287cc8dd9..8750d72e0 100644
--- a/app/features/user-page/components/Widget.module.css
+++ b/app/features/user-page/components/Widget.module.css
@@ -310,11 +310,44 @@
}
}
-.socialLinksIcons {
+.socialLinksList {
display: flex;
- gap: var(--s-2);
- justify-content: center;
- flex-wrap: wrap;
+ flex-direction: column;
+ gap: var(--s-1);
+}
+
+.socialLinkIconCircle {
+ border-radius: var(--radius-full);
+ padding: 0;
+ width: 24px;
+ height: 24px;
+
+ & svg {
+ width: 14px;
+ height: 14px;
+ }
+}
+
+.linkRow {
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ gap: var(--s-1-5);
+ width: 100%;
+ height: auto;
+ padding: var(--s-0-5) var(--s-1);
+ border-radius: var(--radius-field);
+ color: var(--color-text);
+ font-size: var(--font-sm);
+ font-weight: var(--weight-body);
+
+ &:is(a, button):hover {
+ background-color: var(--color-bg-high);
+ }
+}
+
+.socialLinkName {
+ overflow-wrap: anywhere;
}
.gameBadgeGrid {
@@ -337,16 +370,7 @@
.friendsList {
display: flex;
flex-direction: column;
- gap: var(--s-2);
-}
-
-.friendLink {
- padding: var(--s-0-5) var(--s-1);
- border-radius: var(--radius-field);
-
- &:hover {
- background-color: var(--color-bg-higher);
- }
+ gap: var(--s-1);
}
.mapModePreferences {
diff --git a/app/features/user-page/components/Widget.tsx b/app/features/user-page/components/Widget.tsx
index 4122e9349..ab43ee2d2 100644
--- a/app/features/user-page/components/Widget.tsx
+++ b/app/features/user-page/components/Widget.tsx
@@ -830,6 +830,28 @@ const urlToIcon = (url: string) => {
return
{`${SEARCH_TYPE_TO_PREFIX[searchType]}.`}
diff --git a/app/components/layout/SearchResults.module.css b/app/components/layout/SearchResults.module.css
index 5428cefca..04e57d572 100644
--- a/app/components/layout/SearchResults.module.css
+++ b/app/components/layout/SearchResults.module.css
@@ -1,5 +1,5 @@
.listBox {
- max-height: 325px;
+ min-height: 0;
overflow-y: auto;
padding: var(--s-2);
outline: none;
diff --git a/app/components/layout/TopNavMenus.browser.test.tsx b/app/components/layout/TopNavMenus.browser.test.tsx
new file mode 100644
index 000000000..8a8f38a52
--- /dev/null
+++ b/app/components/layout/TopNavMenus.browser.test.tsx
@@ -0,0 +1,57 @@
+import type * as React from "react";
+import { createMemoryRouter, RouterProvider } from "react-router";
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import { page, userEvent } from "vitest/browser";
+import { render } from "vitest-browser-react";
+import { TopNavMenus } from "./TopNavMenus";
+
+const viewportBefore = { width: window.innerWidth, height: window.innerHeight };
+
+// the top nav is the desktop navigation, shown from the tablet breakpoint up
+beforeEach(async () => {
+ await page.viewport(1280, 720);
+});
+
+afterEach(async () => {
+ await page.viewport(viewportBefore.width, viewportBefore.height);
+});
+
+function withRouter(element: React.ReactElement) {
+ const router = createMemoryRouter([{ path: "*", element }], {
+ initialEntries: ["/"],
+ });
+ return