fix(ui): ensure same width of usercards in grid (#6799)
Followup to https://codeberg.org/forgejo/forgejo/pulls/4760 * some refactoring * move rules out of repo.css to a new module * simplify selectors by omitting .list: it is now only used to style the list itself, they're still precise enough in scope of .user-cards * apply wrap/ellipsis to cards' content. Done via CSS to avoid spamming gt-ellipsis in the template * prevent cards with long content from taking horizontal space from other cards * prevent such cards from causing horizontal overflow on mobile * prevent varying card height, it doesn't look good even with text wrapping Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6799 Reviewed-by: Otto <otto@codeberg.org> Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org>
This commit is contained in:
		
					parent
					
						
							
								0dd605a8d3
							
						
					
				
			
			
				commit
				
					
						8b93f41aaa
					
				
			
		
					 7 changed files with 100 additions and 51 deletions
				
			
		| 
						 | 
				
			
			@ -17,3 +17,13 @@
 | 
			
		|||
  id: 4
 | 
			
		||||
  user_id: 31
 | 
			
		||||
  follow_id: 33
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 5
 | 
			
		||||
  user_id: 4
 | 
			
		||||
  follow_id: 8
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 6
 | 
			
		||||
  user_id: 5
 | 
			
		||||
  follow_id: 8
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,6 +53,7 @@
 | 
			
		|||
  login_source: 0
 | 
			
		||||
  login_name: user2
 | 
			
		||||
  type: 0
 | 
			
		||||
  website: https://keyoxide.org/eb114f5e6c0dc2bcdd183550a4b61a2dc5923710
 | 
			
		||||
  salt: ZogKvWdyEx
 | 
			
		||||
  max_repo_creation: -1
 | 
			
		||||
  is_active: true
 | 
			
		||||
| 
						 | 
				
			
			@ -143,7 +144,7 @@
 | 
			
		|||
  avatar_email: user4@example.com
 | 
			
		||||
  use_custom_avatar: true
 | 
			
		||||
  num_followers: 0
 | 
			
		||||
  num_following: 1
 | 
			
		||||
  num_following: 2
 | 
			
		||||
  num_stars: 0
 | 
			
		||||
  num_repos: 0
 | 
			
		||||
  num_teams: 0
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +182,7 @@
 | 
			
		|||
  avatar_email: user5@example.com
 | 
			
		||||
  use_custom_avatar: true
 | 
			
		||||
  num_followers: 0
 | 
			
		||||
  num_following: 0
 | 
			
		||||
  num_following: 1
 | 
			
		||||
  num_stars: 0
 | 
			
		||||
  num_repos: 1
 | 
			
		||||
  num_teams: 0
 | 
			
		||||
| 
						 | 
				
			
			@ -294,7 +295,7 @@
 | 
			
		|||
  avatar: ""
 | 
			
		||||
  avatar_email: user8@example.com
 | 
			
		||||
  use_custom_avatar: true
 | 
			
		||||
  num_followers: 1
 | 
			
		||||
  num_followers: 3
 | 
			
		||||
  num_following: 1
 | 
			
		||||
  num_stars: 0
 | 
			
		||||
  num_repos: 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@
 | 
			
		|||
					<a href="{{.HomeLink}}">
 | 
			
		||||
						{{ctx.AvatarUtils.Avatar .}}
 | 
			
		||||
					</a>
 | 
			
		||||
					<div>
 | 
			
		||||
					<div class="content">
 | 
			
		||||
						<h3 class="name">
 | 
			
		||||
							<a href="{{.HomeLink}}">{{.DisplayName}}</a>
 | 
			
		||||
						</h3>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										29
									
								
								tests/e2e/user-cards.test.e2e.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/e2e/user-cards.test.e2e.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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]);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -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";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										55
									
								
								web_src/css/modules/user-cards.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								web_src/css/modules/user-cards.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue