Skip to content

Commit f105874

Browse files
feat(ui): improve responsive event browsing layout
1 parent d6ea01b commit f105874

3 files changed

Lines changed: 172 additions & 22 deletions

File tree

assets/app.js

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ const state = {
1212

1313
const el = {
1414
subtitle: document.getElementById("subtitle"),
15+
subtitleCount: document.getElementById("subtitle-count"),
1516
notice: document.getElementById("notice"),
1617
error: document.getElementById("error"),
1718
loading: document.getElementById("loading"),
1819
empty: document.getElementById("empty"),
1920
tableWrap: document.getElementById("table-wrap"),
2021
tableBody: document.getElementById("table-body"),
22+
mobileList: document.getElementById("mobile-list"),
2123
yearSelect: document.getElementById("year-select"),
2224
searchInput: document.getElementById("search-input"),
2325
statusSelect: document.getElementById("status-select"),
@@ -276,15 +278,19 @@ function render() {
276278
const sorted = sortRows(filtered, state.sortKey, state.sortDir);
277279

278280
el.tableBody.innerHTML = "";
281+
el.mobileList.innerHTML = "";
282+
279283
for (const row of sorted) {
280284
el.tableBody.append(renderRow(row));
285+
el.mobileList.append(renderMobileCard(row));
281286
}
282287

283-
el.subtitle.textContent = `${state.year} · ${sorted.length} event${sorted.length === 1 ? "" : "s"}`;
288+
el.subtitleCount.textContent = `${state.year} · ${sorted.length} event${sorted.length === 1 ? "" : "s"}`;
284289

285290
const hasRows = sorted.length > 0;
286291
el.empty.classList.toggle("hidden", hasRows);
287292
el.tableWrap.classList.toggle("hidden", !hasRows);
293+
el.mobileList.classList.toggle("hidden", !hasRows);
288294
}
289295

290296
function getFilteredRows(rows) {
@@ -369,21 +375,45 @@ function renderStatusBadge(status) {
369375
return `<span class="${cls}" aria-label="Status: ${label}">${label}</span>`;
370376
}
371377

378+
function renderMobileCard(row) {
379+
const card = document.createElement("article");
380+
card.className = "event-card";
381+
382+
const dateRange = `${escapeHtml(row.startDateLabel)}${escapeHtml(row.endDateLabel)}`;
383+
const locationLine = [row.country, row.location].filter(Boolean).map(escapeHtml).join(" · ");
384+
385+
card.innerHTML = `
386+
<div class="event-title-row">
387+
<h3>${escapeHtml(row.subject)}</h3>
388+
${renderStatusBadge(row.status)}
389+
</div>
390+
<p class="event-line"><span class="event-label">Dates</span><span class="event-value event-dates">${dateRange}</span></p>
391+
<p class="event-line"><span class="event-label">Location</span><span class="event-value">${locationLine || "—"}</span></p>
392+
<p class="event-line"><span class="event-label">Venue</span><span class="event-value">${escapeHtml(row.venue) || "—"}</span></p>
393+
<div class="event-line"><span class="event-label">Links</span><div class="event-value">${renderLinks(row) || "—"}</div></div>
394+
`;
395+
396+
return card;
397+
}
398+
372399
function renderLinks(row) {
373400
const links = [
374-
["Website", row.websiteUrl],
375-
["CFP", row.proposalUrl],
376-
["Sponsor", row.sponsorshipUrl],
401+
["Website", row.websiteUrl, "link-website"],
402+
["CFP", row.proposalUrl, "link-cfp"],
403+
["Sponsor", row.sponsorshipUrl, "link-sponsor"],
377404
].filter((item) => item[1]);
378405

379406
if (links.length === 0) {
380407
return "";
381408
}
382409

383410
const items = links
384-
.map(([label, href]) => `<a href="${escapeAttr(href)}" target="_blank" rel="noopener noreferrer">${label}</a>`)
411+
.map(
412+
([label, href, cls]) =>
413+
`<a class="${cls}" href="${escapeAttr(href)}" target="_blank" rel="noopener noreferrer">${label}</a>`,
414+
)
385415
.join("");
386-
return `<div class="links">${items}</div>`;
416+
return `<div class="links links-vertical">${items}</div>`;
387417
}
388418

389419
function setSortIndicators() {

assets/styles.css

Lines changed: 125 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ body {
5151

5252
.control-grid {
5353
display: grid;
54-
gap: 0.75rem;
55-
grid-template-columns: repeat(4, minmax(0, 1fr));
56-
padding: 1rem;
54+
gap: 0.6rem;
55+
grid-template-columns: 130px minmax(220px, 1fr) 120px 110px;
56+
padding: 0.75rem;
5757
}
5858

5959
.field {
@@ -63,12 +63,12 @@ body {
6363
}
6464

6565
.field span {
66-
font-size: 0.85rem;
66+
font-size: 0.78rem;
6767
color: var(--muted-foreground);
6868
}
6969

7070
.field-grow {
71-
grid-column: span 2;
71+
grid-column: span 1;
7272
}
7373

7474
input,
@@ -83,8 +83,9 @@ button.sort {
8383

8484
input,
8585
select {
86-
min-height: 2.25rem;
87-
padding: 0.4rem 0.6rem;
86+
min-height: 1.95rem;
87+
padding: 0.25rem 0.5rem;
88+
font-size: 0.9rem;
8889
}
8990

9091
input:focus-visible,
@@ -118,6 +119,68 @@ button.sort:focus-visible {
118119
overflow-x: auto;
119120
}
120121

122+
.mobile-list {
123+
display: none;
124+
padding: 0.6rem;
125+
}
126+
127+
.event-card {
128+
border: 1px solid var(--border);
129+
border-radius: 8px;
130+
padding: 0.7rem;
131+
margin-bottom: 0.6rem;
132+
background: #fff;
133+
}
134+
135+
.event-title-row {
136+
display: flex;
137+
align-items: flex-start;
138+
justify-content: space-between;
139+
gap: 0.5rem;
140+
}
141+
142+
.event-card h3 {
143+
margin: 0;
144+
font-size: 1rem;
145+
line-height: 1.25;
146+
}
147+
148+
.event-meta {
149+
margin-top: 0.35rem;
150+
display: flex;
151+
align-items: center;
152+
gap: 0.45rem;
153+
flex-wrap: wrap;
154+
}
155+
156+
.event-dates {
157+
font-size: 0.84rem;
158+
color: var(--muted-foreground);
159+
white-space: nowrap;
160+
}
161+
162+
.event-line {
163+
margin: 0.4rem 0 0;
164+
font-size: 0.86rem;
165+
color: #262626;
166+
display: grid;
167+
grid-template-columns: 70px 1fr;
168+
gap: 0.45rem;
169+
align-items: start;
170+
}
171+
172+
.event-label {
173+
color: var(--muted-foreground);
174+
font-size: 0.78rem;
175+
text-transform: uppercase;
176+
letter-spacing: 0.03em;
177+
font-weight: 600;
178+
}
179+
180+
.event-value {
181+
min-width: 0;
182+
}
183+
121184
table {
122185
width: 100%;
123186
border-collapse: collapse;
@@ -142,6 +205,13 @@ tbody td {
142205
font-size: 0.92rem;
143206
}
144207

208+
#events-table th:nth-child(2),
209+
#events-table th:nth-child(3),
210+
#events-table td:nth-child(2),
211+
#events-table td:nth-child(3) {
212+
white-space: nowrap;
213+
}
214+
145215
tbody tr:hover {
146216
background: #fafafa;
147217
}
@@ -178,19 +248,21 @@ button.sort[data-sort-dir="desc"]::after {
178248
}
179249

180250
.badge-ongoing {
181-
background: #0a0a0a;
182-
color: #fff;
183-
border-color: #0a0a0a;
251+
background: #dcfce7;
252+
color: #166534;
253+
border-color: #86efac;
184254
}
185255

186256
.badge-upcoming {
187-
background: #fff;
188-
color: #0a0a0a;
257+
background: #dbeafe;
258+
color: #1d4ed8;
259+
border-color: #93c5fd;
189260
}
190261

191262
.badge-past {
192-
background: #f5f5f5;
193-
color: #525252;
263+
background: #f3f4f6;
264+
color: #4b5563;
265+
border-color: #d1d5db;
194266
}
195267

196268
.links {
@@ -199,6 +271,11 @@ button.sort[data-sort-dir="desc"]::after {
199271
gap: 0.35rem;
200272
}
201273

274+
.links-vertical {
275+
flex-direction: column;
276+
align-items: flex-start;
277+
}
278+
202279
.links a {
203280
border: 1px solid var(--border);
204281
padding: 0.1rem 0.4rem;
@@ -209,7 +286,25 @@ button.sort[data-sort-dir="desc"]::after {
209286
}
210287

211288
.links a:hover {
212-
background: var(--muted);
289+
filter: brightness(0.98);
290+
}
291+
292+
.links a.link-website {
293+
background: #eff6ff;
294+
border-color: #bfdbfe;
295+
color: #1d4ed8;
296+
}
297+
298+
.links a.link-cfp {
299+
background: #ecfeff;
300+
border-color: #a5f3fc;
301+
color: #0e7490;
302+
}
303+
304+
.links a.link-sponsor {
305+
background: #fff7ed;
306+
border-color: #fed7aa;
307+
color: #c2410c;
213308
}
214309

215310
.hidden {
@@ -236,6 +331,21 @@ button.sort[data-sort-dir="desc"]::after {
236331
}
237332
}
238333

334+
@media (max-width: 768px) {
335+
.table-wrap {
336+
display: none;
337+
}
338+
339+
.mobile-list {
340+
display: block;
341+
}
342+
343+
.links-vertical {
344+
flex-direction: row;
345+
flex-wrap: wrap;
346+
}
347+
}
348+
239349
@media (prefers-reduced-motion: reduce) {
240350
* {
241351
transition: none !important;

index.html

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@
1515
<header class="header">
1616
<div>
1717
<h1>Python Conferences</h1>
18-
<p id="subtitle" class="subtitle">Browse events by year</p>
18+
<p id="subtitle" class="subtitle">
19+
<span id="subtitle-count">Browse events by year</span>
20+
<span aria-hidden="true"> · </span>
21+
<span>
22+
Want to add a new event?
23+
<a href="https://github.com/python-organizers/conferences" target="_blank" rel="noopener noreferrer"
24+
>See repository instructions</a
25+
>.
26+
</span>
27+
</p>
1928
</div>
2029
</header>
2130

@@ -76,6 +85,7 @@ <h1>Python Conferences</h1>
7685
<tbody id="table-body"></tbody>
7786
</table>
7887
</div>
88+
<div id="mobile-list" class="mobile-list hidden" aria-live="polite"></div>
7989
<div id="empty" class="state hidden">No events match the current filters.</div>
8090
</section>
8191
</main>

0 commit comments

Comments
 (0)