Skip to content

Commit 6e2c8bb

Browse files
jonfroehlichclaude
andcommitted
Mobile project page: funder sizing, info-list spacing, former leads (#1271)
Three post-release tweaks to the 2.23.0 mobile project-page redesign: 1. Funding logos rendered at wildly different sizes — wide wordmarks (e.g. Mapillary) were squished into the square cropped thumbnail and shrank to a sliver. Generate the funder thumbnail aspect-preserving (drop crop=True) and pin a uniform 30px display height, so badges and wordmarks read at consistent weight. 2. On metadata-light projects (e.g. homesound), the info-list's first top border crowded the description because the block had no top margin when it followed the prose directly (no featured video between). Add margin-top. 3. Team now surfaces former leads: current leads render inline and former (ended-role) leads collapse into the "+N former lead(s)" <details>. The view splits leadership into project_leads_current / project_leads_former. Regression tests updated for the current-vs-former split. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 35f4e39 commit 6e2c8bb

4 files changed

Lines changed: 81 additions & 45 deletions

File tree

website/static/website/css/project.css

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,13 @@
762762

763763
/* --- Labeled info-list (Links / Team / Funding) ---------------------------- */
764764

765+
/* Space above the info-list so its first row's top border doesn't crowd the
766+
preceding prose on metadata-light projects where it follows the description
767+
directly (no featured video between them) (#1271). */
768+
.project-meta-info {
769+
margin-top: var(--space-6);
770+
}
771+
765772
.project-info-list {
766773
margin: 0;
767774
}
@@ -955,9 +962,14 @@
955962
justify-content: center;
956963
}
957964

965+
/* Uniform display height with aspect preserved (the thumbnail is generated
966+
un-cropped), so badge logos and wide wordmarks read at consistent weight and
967+
the wide ones aren't shrunk. max-width keeps an unusually wide logo from
968+
overflowing the column. */
958969
.project-funder-img {
959-
max-height: 38px;
970+
height: 30px;
960971
width: auto;
972+
max-width: 100%;
961973
object-fit: contain;
962974
}
963975

website/templates/website/project.html

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,12 @@ <h1 class="sr-only">{{ project.name }}</h1>
185185

186186
{% comment %}
187187
Mobile-only metadata info-list (#1271): Links (as clickable chips), Team
188-
(leads, with "+N more" collapsed into a native <details>), and Funding.
189-
Placed below the description + featured video so this supporting context no
190-
longer sits above the description (the core complaint in #1271). Hidden
191-
≥992px, where the desktop sidebar renders the same data.
188+
(current leads inline, former leads collapsed into a native <details>),
189+
and Funding. Placed below the description + featured video so this
190+
supporting context no longer sits above the description (the core complaint
191+
in #1271). Hidden ≥992px, where the desktop sidebar renders the same data.
192192
{% endcomment %}
193-
{% if website or featured_code_repo_url or data_url or active_leads_ordered or sponsors %}
193+
{% if website or featured_code_repo_url or data_url or project_leads_current or project_leads_former or sponsors %}
194194
<div class="project-mobile-meta project-meta-info">
195195
<dl class="project-info-list">
196196

@@ -219,21 +219,21 @@ <h1 class="sr-only">{{ project.name }}</h1>
219219
</div>
220220
{% endif %}
221221

222-
{% if active_leads_ordered %}
222+
{% if project_leads_current or project_leads_former %}
223223
<div class="project-info-row">
224224
<dt class="project-info-label">Team</dt>
225225
<dd class="project-info-value">
226-
{% for lead in active_leads_ordered|slice:":4" %}
226+
{% for lead in project_leads_current %}
227227
{% include "snippets/display_project_team_member_mobile.html" with lead=lead %}
228228
{% endfor %}
229-
{% if active_leads_ordered|length > 4 %}
229+
{% if project_leads_former %}
230230
<details class="project-team-more">
231231
<summary class="project-team-more-summary">
232232
<span class="project-team-more-caret" aria-hidden="true"></span>
233-
<span class="project-team-more-show">+ {{ active_leads_ordered|length|add:"-4" }} more</span>
233+
<span class="project-team-more-show">{{ project_leads_former|length }} former lead{{ project_leads_former|length|pluralize }}</span>
234234
<span class="project-team-more-hide">Show fewer</span>
235235
</summary>
236-
{% for lead in active_leads_ordered|slice:"4:" %}
236+
{% for lead in project_leads_former %}
237237
{% include "snippets/display_project_team_member_mobile.html" with lead=lead %}
238238
{% endfor %}
239239
</details>
@@ -251,8 +251,14 @@ <h1 class="sr-only">{{ project.name }}</h1>
251251
{% if sponsor.icon %}
252252
<div class="project-funder">
253253
{% if sponsor.url %}<a href="{{ sponsor.url }}" aria-label="Visit {{ sponsor.name }} website">{% endif %}
254-
<img class="project-funder-img"
255-
src="{% thumbnail sponsor.icon sponsor.get_thumbnail_size_as_str box=sponsor.icon_cropping crop=True upscale=True %}"
254+
{% comment %}
255+
Aspect-preserving (no crop=True): scale each logo to fit within
256+
the box so wide wordmarks (e.g. Mapillary) render full-width
257+
instead of being squished into a square and shrinking. CSS pins
258+
a uniform display height (#1271).
259+
{% endcomment %}
260+
<img class="project-funder-img"
261+
src="{% thumbnail sponsor.icon '600x60' box=sponsor.icon_cropping upscale=True %}"
256262
alt="{{ sponsor.get_icon_alt_text }}"
257263
title="{{ sponsor.name }}">
258264
{% if sponsor.url %}</a>{% endif %}

website/tests/test_project_page.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@
2727
class ProjectPageMobileMetaTests(DatabaseTestCase):
2828
"""Renders a fully-populated, visible project and asserts the mobile blocks."""
2929

30-
# The Team list shows this many leads before collapsing the rest into
31-
# <details>. Kept in sync with project.html's slice value.
32-
VISIBLE_LEADS = 4
33-
3430
def setUp(self):
3531
# Ongoing (no end_date) → "Active"; start in 2021 → date range "2021–Present".
3632
self.project = self.make_project(
@@ -42,22 +38,30 @@ def setUp(self):
4238
data_url="https://example.org/sidewalk/data",
4339
)
4440

45-
# 1 PI + 6 student leads = 7 leads, which exceeds VISIBLE_LEADS so the
46-
# "+N more" <details> overflow is exercised.
41+
# Current leads (ongoing roles) render inline; former leads (ended roles)
42+
# collapse into the "+N former" <details>. 1 PI + 2 active student leads
43+
# are current; 3 ended student-lead roles are former.
4744
self.pi = self.make_person(first_name="Jon", last_name="Froehlich")
4845
ProjectRole.objects.create(
4946
person=self.pi, project=self.project,
5047
lead_project_role=LeadProjectRoleTypes.PI, start_date=date(2021, 1, 1),
5148
)
52-
self.leads = []
53-
for i in range(6):
54-
person = self.make_person(first_name=f"Lead{i}", last_name="Student")
49+
for i in range(2):
50+
person = self.make_person(first_name=f"Current{i}", last_name="Lead")
5551
ProjectRole.objects.create(
5652
person=person, project=self.project,
5753
lead_project_role=LeadProjectRoleTypes.STUDENT_LEAD,
5854
start_date=date(2021, 1, 1),
5955
)
60-
self.leads.append(person)
56+
self.former_leads = []
57+
for i in range(3):
58+
person = self.make_person(first_name=f"Former{i}", last_name="Lead")
59+
ProjectRole.objects.create(
60+
person=person, project=self.project,
61+
lead_project_role=LeadProjectRoleTypes.STUDENT_LEAD,
62+
start_date=date(2018, 1, 1), end_date=date(2019, 1, 1),
63+
)
64+
self.former_leads.append(person)
6165

6266
# One sponsor via a grant so the Funding row renders.
6367
sponsor = Sponsor.objects.create(
@@ -99,13 +103,16 @@ def test_links_render_as_clickable_chips(self):
99103
self.assertIn("https://example.org/sidewalk", html)
100104
self.assertIn("https://example.org/sidewalk/data", html)
101105

102-
def test_team_overflow_collapses_into_details(self):
106+
def test_current_leads_render_and_former_collapse_into_details(self):
103107
html = self._get()
104-
# The PI is among the always-visible leads.
108+
# Current leads (PI + active student leads) render inline.
105109
self.assertIn("Jon Froehlich", html)
106-
# With 7 leads > VISIBLE_LEADS, the overflow disclosure must appear.
110+
# Former leads collapse into the "+N former" <details> disclosure.
107111
self.assertIn("project-team-more", html)
108112
self.assertIn("<details", html)
113+
self.assertIn("former lead", html.lower())
114+
# A specific former lead is present (inside the disclosure).
115+
self.assertIn("Former0 Lead", html)
109116

110117
def test_funding_row_renders_sponsor(self):
111118
html = self._get()

website/views/project.py

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,29 +99,39 @@ def project(request, project_name):
9999
project_leadership = project.get_project_leadership()
100100
_logger.debug(f"The project leadership for {project_name}: {project_leadership}")
101101

102-
# Flat, de-duplicated, seniority-ordered list of active leads for the mobile
103-
# "Team" list (#1271). The desktop sidebar still renders each lead group
104-
# separately; this convenience list lets the mobile template show the first
105-
# few leads and collapse the rest into a <details> without per-group logic.
106-
# Order mirrors the sidebar: PIs → Co-PIs → research scientists → postdocs
107-
# student leads. Each role carries a short inline label ("PI"/"Co-PI"/"Lead").
102+
# Flat, de-duplicated, seniority-ordered lead lists for the mobile "Team"
103+
# list (#1271). The desktop sidebar still renders each lead group separately;
104+
# these convenience lists let the mobile template show current leads inline
105+
# and collapse former leads into a <details> without per-group logic. Order
106+
# within each list mirrors the sidebar: PIs → Co-PIs → research scientists →
107+
# postdocs → student leads. Each role carries a short inline label.
108108
_lead_labels = {
109109
LeadProjectRoleTypes.PI: 'PI',
110110
LeadProjectRoleTypes.CO_PI: 'Co-PI',
111111
}
112-
active_leads_ordered = []
112+
_active_lead_groups = ('active_PIs', 'active_CoPIs',
113+
'active_research_scientist_leads',
114+
'active_postdoc_leads', 'active_student_leads')
115+
_inactive_lead_groups = ('inactive_PIs', 'inactive_CoPIs',
116+
'inactive_research_scientist_leads',
117+
'inactive_postdoc_leads', 'inactive_student_leads')
118+
119+
def _flatten_leads(group_keys, seen):
120+
leads = []
121+
for _key in group_keys:
122+
for _role in project_leadership[_key]:
123+
if _role.person_id in seen:
124+
continue
125+
seen.add(_role.person_id)
126+
# Transient attribute read by the mobile team snippet; not persisted.
127+
_role.mobile_role_label = _lead_labels.get(_role.lead_project_role, 'Lead')
128+
leads.append(_role)
129+
return leads
130+
113131
_seen_lead_person_ids = set()
114-
for _role in (project_leadership['active_PIs']
115-
+ project_leadership['active_CoPIs']
116-
+ project_leadership['active_research_scientist_leads']
117-
+ project_leadership['active_postdoc_leads']
118-
+ project_leadership['active_student_leads']):
119-
if _role.person_id in _seen_lead_person_ids:
120-
continue
121-
_seen_lead_person_ids.add(_role.person_id)
122-
# Transient attribute read by the mobile team snippet; not persisted.
123-
_role.mobile_role_label = _lead_labels.get(_role.lead_project_role, 'Lead')
124-
active_leads_ordered.append(_role)
132+
project_leads_current = _flatten_leads(_active_lead_groups, _seen_lead_person_ids)
133+
# Former leads are collapsed behind the "+N former" disclosure on mobile.
134+
project_leads_former = _flatten_leads(_inactive_lead_groups, _seen_lead_person_ids)
125135

126136
# Query for related projects. Limit to top 5
127137
# Get all candidates first
@@ -157,7 +167,8 @@ def project(request, project_name):
157167
'inactive_student_leads': project_leadership["inactive_student_leads"],
158168
'active_postdoc_leads': project_leadership["active_postdoc_leads"],
159169
'active_research_scientist_leads': project_leadership["active_research_scientist_leads"],
160-
'active_leads_ordered': active_leads_ordered,
170+
'project_leads_current': project_leads_current,
171+
'project_leads_former': project_leads_former,
161172
'related_projects': related_projects,
162173
'has_videos_beyond_featured_video': has_videos_beyond_featured_video,
163174
'debug': settings.DEBUG}

0 commit comments

Comments
 (0)