diff --git a/tests/e2e/user-cards.test.e2e.ts b/tests/e2e/user-cards.test.e2e.ts
new file mode 100644
index 0000000000..22c31965d6
--- /dev/null
+++ b/tests/e2e/user-cards.test.e2e.ts
@@ -0,0 +1,29 @@
+// Copyright 2025 The Forgejo Authors. All rights reserved.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// @watch start
+// templates/repo/user_cards.tmpl
+// web_src/css/modules/user-cards.css
+// @watch end
+
+import {expect} from '@playwright/test';
+import {test} from './utils_e2e.ts';
+
+test('Usercards width', async ({page}) => {
+ await page.goto('/user8?tab=followers');
+
+ // Regardless of whether cards in a grid or flex mode, they should be ~same
+ // width. Verifying this relies on fixtures with users that have long website
+ // link or other content that could push the card width.
+ const widths = [];
+ const amount = 3;
+
+ for (let i = 1; i <= amount; i++) {
+ const card = await page.locator(`.user-cards .card:nth-child(${i})`).boundingBox();
+ widths.push(Math.round(card.width));
+ }
+
+ for (const width of widths) {
+ expect(width).toBe(widths[0]);
+ }
+});
diff --git a/web_src/css/index.css b/web_src/css/index.css
index 0a5a0180aa..88aa9bbf4a 100644
--- a/web_src/css/index.css
+++ b/web_src/css/index.css
@@ -27,6 +27,7 @@
@import "./modules/toast.css";
@import "./modules/svg.css";
@import "./modules/flexcontainer.css";
+@import "./modules/user-cards.css";
@import "./shared/flex-list.css";
@import "./shared/milestone.css";
diff --git a/web_src/css/modules/user-cards.css b/web_src/css/modules/user-cards.css
new file mode 100644
index 0000000000..d89ea4588c
--- /dev/null
+++ b/web_src/css/modules/user-cards.css
@@ -0,0 +1,55 @@
+.user-cards .list {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 15px;
+ margin: 0 0 10px;
+ padding: 0;
+}
+
+@media (max-width: 767.98px) {
+ .user-cards .list {
+ grid-template-columns: repeat(1, 1fr);
+ }
+}
+
+@media (max-width: 900px) {
+ .user.profile .user-cards .list {
+ grid-template-columns: repeat(1, 1fr);
+ }
+}
+
+.user-cards .card {
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ margin: 0;
+ padding: 14px;
+ border-radius: 0.28571429rem;
+ border: 1px solid var(--color-secondary);
+ background: var(--color-box-body);
+}
+
+.user-cards .card,
+.user-cards .card .content,
+.user-cards .card .name,
+.user-cards .card .meta {
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.user-cards .card .avatar {
+ width: 48px;
+ height: 48px;
+ margin-right: 14px;
+}
+
+.user-cards .card .name {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-weight: var(--font-weight-normal);
+}
+
+.user-cards .card .meta {
+ margin-top: 5px;
+}
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index 35133b965f..651923e60b 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2010,53 +2010,6 @@ details.repo-search-result summary::marker {
padding: 0 10px;
}
-.user-cards .list {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 15px;
- margin: 0 0 10px;
- padding: 0;
-}
-
-@media (max-width: 767.98px) {
- .user-cards .list {
- grid-template-columns: repeat(1, 1fr);
- }
-}
-
-@media (max-width: 900px) {
- .user.profile .user-cards .list {
- grid-template-columns: repeat(1, 1fr);
- }
-}
-
-.user-cards .list .card {
- display: flex;
- flex-direction: row;
- width: 100%;
- margin: 0;
- padding: 14px;
- border-radius: 0.28571429rem;
- border: 1px solid var(--color-secondary);
- background: var(--color-box-body);
-}
-
-.user-cards .list .card .avatar {
- width: 48px;
- height: 48px;
- margin-right: 14px;
-}
-
-.user-cards .list .card .name {
- margin-top: 0;
- margin-bottom: 0;
- font-weight: var(--font-weight-normal);
-}
-
-.user-cards .list .card .meta {
- margin-top: 5px;
-}
-
#search-user-box .results .result .image {
order: 0;
margin-right: 12px;