feat(ui): responsive releases list (#11080)

## Changes

I've made releases list more usable on narrow viewports while trying keep the layout unchanged for desktop viewports.

To support these changes large amount of Tailwind classes were converted to regular CSS to be applied conditionally via `@media`. While it was possible to just adjust the Tailwind classes to achieve the same behavior, there's a positive effect which is that the repeating HTML of releases generated by template's range is much less verbose and contains fewer long duplicated lines of Tailwind classes.

## Preview

### Desktop

Not much changed, but the dot between tag and release name is no more.

|Before|After|
|-|-|
|![bd](/attachments/a87bf050-03ba-4035-8a0e-7ab4f6e877a2)|![2d](/attachments/e0c2e052-01c6-4078-b06e-f8ef7b803690)|

### Mobile

|Before|After|
|-|-|
|![bm](/attachments/f63a606f-f3f8-435d-8378-0979e93cf7bd)|![2m](/attachments/0cb6086d-1def-4a86-bb0e-4131e50aae3b)|

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11080
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: Beowulf <beowulf@beocode.eu>
This commit is contained in:
0ko 2026-02-07 19:15:14 +01:00
parent f24a97f719
commit 7e4619df83
7 changed files with 243 additions and 141 deletions

View file

@ -1750,31 +1750,6 @@ details.repo-search-result summary::marker {
border-bottom: 1px solid var(--color-warning-border);
}
.repository .release-tag-name .ui.label.isSigned,
.repository .release-list-title .ui.label.isSigned {
padding: 0 0.5em;
box-shadow: none;
}
.repository .release-tag-name .ui.label.isSigned .avatar,
.repository .release-list-title .ui.label.isSigned .avatar {
margin-left: .5rem;
}
.repository .release-tag-name .ui.label.isSigned.isVerified,
.repository .release-list-title .ui.label.isSigned.isVerified {
border: 1px solid var(--color-success-border);
background-color: var(--color-success-bg);
color: var(--color-success-text);
}
.repository .release-tag-name .ui.label.isSigned.isWarning,
.repository .release-list-title .ui.label.isSigned.isWarning {
border: 1px solid var(--color-warning-border);
background-color: var(--color-warning-bg);
color: var(--color-warning-text);
}
.repository .segment.reactions.dropdown .menu,
.repository .select-reaction.dropdown .menu {
right: 0 !important;

View file

@ -1,75 +1,145 @@
.repository.releases #release-list {
margin-top: 12px;
padding-top: 12px;
padding-left: 0;
#release-list {
margin-top: 10px; /* Overriding browser default for <ul>, same value as divider */
padding-left: 0; /* Unset browser default */
}
.repository.releases #release-list .release-list-title {
#release-list > li {
.meta {
display: flex;
gap: 1em;
}
.detail .desc {
margin-block-start: 1rem;
}
}
/* Column layout (desktop) */
@media (min-width: 768px) {
#release-list > li {
display: grid;
grid-template-columns: 25% 75%;
justify-content: end;
.meta {
grid-column: 1;
grid-row: 1 / span 2;
flex-direction: column;
text-align: right;
/* Align contents of meta column with release name */
padding-top: 11px;
}
.detail {
grid-column: 2;
grid-row: 2;
padding-block-end: 1.5rem;
}
:is(.detail, .release-title-wrap) {
/* Line separating columns on wide screen */
border-left: 1px solid var(--color-secondary);
padding-inline-start: 1rem;
margin-inline-start: 1rem;
}
.detail .download summary {
margin-block: 1rem;
}
}
}
/* Row layout (mobile) */
@media (max-width: 767.98px) {
#release-list > li {
display: flex;
flex-direction: column;
.meta {
order: 1;
flex-direction: row;
align-items: center;
margin-block-end: 0.75rem;
}
.detail {
order: 2;
}
.detail .text {
/* Same as `summary` */
margin-block-end: 0.75rem;
}
.detail .download summary {
margin-block: 0.75rem;
}
}
.release-list-search {
order: 2 !important;
}
.release-list-buttons {
margin-left: auto;
}
}
#release-list .release-title-wrap {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding-block-end: 0.5rem;
}
#release-list .release-title-wrap h4 {
font-size: 2rem;
font-weight: var(--font-weight-normal);
display: flex;
align-items: center;
gap: 0.25em;
margin: 0;
}
.repository.releases #release-list > li .meta {
padding-top: 25px;
position: relative;
text-align: right;
display: flex;
flex-direction: column;
gap: 1em;
}
.repository.releases #release-list > li .detail {
padding-bottom: 20px;
border-left: 1px solid var(--color-secondary);
overflow-wrap: anywhere;
}
.repository.releases #release-list > li .detail .author img {
margin-bottom: 2px; /* the legacy trick to align the avatar vertically, no better solution at the moment */
}
.repository.releases #release-list > li .detail .download .list {
/* List of downloads */
#release-list > li .detail .download .list {
/* Override <ul> default */
padding-left: 0;
/* Compensate emptiness that opener provides when <details> is closed */
padding-block-end: 1rem;
hr {
height: 8px;
margin: 0;
}
li {
background: var(--color-light);
border: 1px solid var(--color-secondary);
border-top: none;
display: flex;
justify-content: space-between;
padding: 8px;
}
:is(li:first-child, .start-gap + hr + li) {
border-top: 1px solid var(--color-secondary);
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
}
:is(li:last-child, .start-gap) {
border-bottom: 1px solid var(--color-secondary);
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
}
.repository.releases #release-list > li .detail .download .list li {
background: var(--color-light);
border: 1px solid var(--color-secondary);
border-top: none;
display: flex;
justify-content: space-between;
padding: 8px;
}
.repository.releases #release-list > li .detail .download .list :is(li:first-child, .start-gap + hr + li) {
border-top: 1px solid var(--color-secondary);
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
}
.repository.releases #release-list > li .detail .download .list :is(li:last-child, .start-gap) {
border-bottom: 1px solid var(--color-secondary);
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
.repository.releases #release-list > li .detail .download .list hr {
height: 8px;
margin: 0;
}
.repository.releases #release-list > li .detail .dot {
width: 10px;
height: 10px;
background-color: var(--color-secondary-dark-3);
position: absolute;
left: -5.5px;
top: 30px;
border-radius: var(--border-radius-full);
border: 2.5px solid var(--color-body);
@media (max-width: 640px) {
#release-list > li .detail .download .list .attachment {
flex-direction: column;
gap: 0.5rem;
}
}
.repository.tags #tags-table .tag {
@ -85,10 +155,6 @@
min-width: 500px;
}
.repository.new.release .target #tag-name {
margin-top: -4px;
}
.repository.new.release .target .at {
margin-left: -5px;
margin-right: 5px;
@ -99,15 +165,6 @@
padding-bottom: 10px;
}
@media (max-width: 767.98px) {
.release-list-search {
order: 2 !important;
}
.release-list-buttons {
margin-left: auto;
}
}
.repository.new.release .field .attachment_edit {
max-width: 48em;
}
@ -128,3 +185,28 @@
.ui.ui.ui.tag-label.IsRelease:hover {
border-color: var(--color-primary-dark-1);
}
.repository .release-tag-name .ui.label.isSigned,
.repository .release-title-wrap .ui.label.isSigned {
padding: 0 0.5em;
box-shadow: none;
}
.repository .release-tag-name .ui.label.isSigned .avatar,
.repository .release-title-wrap .ui.label.isSigned .avatar {
margin-left: .5rem;
}
.repository .release-tag-name .ui.label.isSigned.isVerified,
.repository .release-title-wrap .ui.label.isSigned.isVerified {
border: 1px solid var(--color-success-border);
background-color: var(--color-success-bg);
color: var(--color-success-text);
}
.repository .release-tag-name .ui.label.isSigned.isWarning,
.repository .release-title-wrap .ui.label.isSigned.isWarning {
border: 1px solid var(--color-warning-border);
background-color: var(--color-warning-bg);
color: var(--color-warning-text);
}