mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-02-03 20:51:07 -05:00
* convert the dropdown (overflow menu) to the JS-less one
* the "link" still relies on JS to make a POST, changing this is not in scope of this PR
* fixed the weird behavior where opener changes it's color when hovering over the "link"
* the bug where the script that refreshes the list once in a while closes an open dropdown is not fixed and is not in scope of this PR
* use border in the opener
* it might not look as sleek but it is easier to see and better for the user to be able to understand that this is an active intractable element
* global dropdown improvements
* add rounding rules for dropdowns with only one item - the first such case
* add testing for rounding rules
* added a devtest page to play with the dropdown component on
Preview
B: https://codeberg.org/forgejo/forgejo/attachments/1462cdda-71f5-45d0-a206-33bb17740cb8
A: https://codeberg.org/forgejo/forgejo/attachments/d3c265cb-6b77-40c8-9944-d9327f3bec65
B: https://codeberg.org/forgejo/forgejo/attachments/17f17c29-4dcd-4015-b5b9-6d438bd2eb0b
A: https://codeberg.org/forgejo/forgejo/attachments/d94e196c-725e-47de-b4de-ed97b148ceb6
B: https://codeberg.org/forgejo/forgejo/attachments/1813ded9-f619-47d9-bf15-ad4bcd3535ab
A: https://codeberg.org/forgejo/forgejo/attachments/09042e58-331e-414d-ac8f-0f1091033b7f
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10133
Reviewed-by: Otto <otto@codeberg.org>
189 lines
5.4 KiB
CSS
189 lines
5.4 KiB
CSS
/* Copyright 2025 The Forgejo Authors. All rights reserved.
|
|
* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
|
|
/* This is an implementation of a dropdown menu based on details HTML tag.
|
|
* It is inspired by https://picocss.com/docs/dropdown.
|
|
*
|
|
* NoJS mode could be improved by forcing the same [name] onto all dropdowns, so
|
|
* that the browser will automatically close all but the one that was just opened
|
|
* using keyboard. But the code doing that will not be as clean.
|
|
*
|
|
* Note: when implementing this dropdown, please use `dropdown` as the 1st class,
|
|
* so it is possible to search for all dropdowns with `details class="dropdown`
|
|
*/
|
|
|
|
:root details.dropdown {
|
|
--dropdown-box-shadow: 0 6px 18px var(--color-shadow);
|
|
--dropdown-item-min-height: 34px;
|
|
--dropdown-padding-inline: 0.75rem;
|
|
}
|
|
|
|
@media (pointer: coarse) {
|
|
:root details.dropdown {
|
|
--dropdown-item-min-height: 40px;
|
|
--dropdown-padding-inline: 1rem;
|
|
}
|
|
}
|
|
|
|
details.dropdown {
|
|
position: relative;
|
|
}
|
|
|
|
/* Opener */
|
|
details.dropdown > summary {
|
|
/* Optional flex+gap in case summary contains multiple elements */
|
|
display: flex;
|
|
gap: 0.25rem;
|
|
align-items: center;
|
|
justify-content: center;
|
|
/* Main visual properties */
|
|
border-radius: var(--border-radius);
|
|
padding: 0.5rem;
|
|
/* Unset unwanted default properties */
|
|
user-select: none;
|
|
list-style-type: none;
|
|
|
|
/* Display a border around opener */
|
|
&.border {
|
|
border: 1px solid var(--color-light-border);
|
|
}
|
|
/* Increase inline padding - for openers with text, like filter menus */
|
|
&.options {
|
|
padding-inline: 0.75rem;
|
|
}
|
|
}
|
|
|
|
/* NoJS mode: create a virtual fullscreen area which closes the dropdown when clicked on */
|
|
.no-js details.dropdown[open] > summary::before {
|
|
z-index: 1;
|
|
position: fixed;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
inset: 0;
|
|
background: 0 0;
|
|
content: "";
|
|
cursor: default;
|
|
}
|
|
|
|
details.dropdown > summary:hover,
|
|
details.dropdown > .content > ul > li > :is(a, button):hover {
|
|
background: var(--color-hover);
|
|
}
|
|
|
|
details.dropdown[open] > summary,
|
|
details.dropdown > .content > ul > li:focus-within > :is(a, button) {
|
|
background: var(--color-active);
|
|
}
|
|
|
|
details.dropdown > .content {
|
|
z-index: 99;
|
|
position: absolute;
|
|
min-width: max-content;
|
|
border-radius: var(--border-radius);
|
|
background: var(--color-body);
|
|
box-shadow: var(--dropdown-box-shadow);
|
|
border: 1px solid var(--color-secondary);
|
|
margin-top: 0.5rem;
|
|
|
|
/* ToDo: upstream to base.css, remove from normalize.css */
|
|
hr {
|
|
height: 1px;
|
|
margin-block: 0.25rem;
|
|
background-color: var(--color-secondary);
|
|
}
|
|
}
|
|
|
|
details.dropdown > .content > ul {
|
|
/* Suppress default styling of <ul> */
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style-type: none;
|
|
|
|
/* Round first item of first list and last item of last list. Each of these
|
|
* selectors should only resolve to one element in any .content */
|
|
&:first-of-type > li:first-child {
|
|
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
|
}
|
|
&:last-of-type > li:last-child {
|
|
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
|
}
|
|
&:only-of-type > li:only-child {
|
|
border-radius: var(--border-radius);
|
|
}
|
|
}
|
|
|
|
/* General styling of list items */
|
|
details.dropdown > .content > ul > li {
|
|
width: 100%;
|
|
|
|
> :is(a, button) {
|
|
min-height: var(--dropdown-item-min-height);
|
|
padding-block: 0;
|
|
|
|
width: 100%;
|
|
padding-inline: var(--dropdown-padding-inline);
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
align-items: center;
|
|
color: var(--color-text);
|
|
|
|
/* Interactable items should be transparent by default. <button> can also have a default background set by the browser */
|
|
background: none;
|
|
|
|
/* Same rounding should apply to both <li> and it's items with paintable backgrounds */
|
|
border-radius: inherit;
|
|
|
|
/* Suppress underline - hover is indicated by background color */
|
|
text-decoration: none;
|
|
|
|
/* Items that are pre-selected in template or by JS */
|
|
&.active {
|
|
background: var(--color-active);
|
|
font-weight: var(--font-weight-medium);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Special styling for "headers" */
|
|
/* A few dropdowns contain such headers, however, they are not semantically considered as headers */
|
|
details.dropdown > .content > .header {
|
|
display: flex;
|
|
padding-block: 0.5rem;
|
|
padding-inline: var(--dropdown-padding-inline);
|
|
font-weight: var(--font-weight-medium);
|
|
text-transform: uppercase;
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
/* Decrease bottom padding if an <hr> follows, which adds it's own vertical padding */
|
|
details.dropdown > .content > .header:has(+ hr) {
|
|
padding-bottom: 0.25rem;
|
|
}
|
|
|
|
/* dir-auto option - switch the direction at a width point where most of layout changes occur */
|
|
@media (max-width: 767.98px) {
|
|
details.dropdown.dir-auto > .content {
|
|
inset-inline: 0 auto;
|
|
direction: rtl;
|
|
> :is(span, ul) {
|
|
direction: ltr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* dir-rtl option - force right-to-left box direction */
|
|
details.dropdown.dir-rtl > .content {
|
|
inset-inline: 0 auto;
|
|
direction: rtl;
|
|
> :is(span, ul) {
|
|
direction: ltr;
|
|
}
|
|
}
|
|
|
|
/* Note: CSS anchor positioning will be a huge help in content positioning w/o JS
|
|
* - https://css-tricks.com/css-anchor-positioning-guide/
|
|
* - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning/
|
|
* - https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning/Using
|
|
* It can already be implemented if the implementation won't interfere with the
|
|
* normal behavior on unsupported browsers. Or it can wait until Firefox gets
|
|
* starts supporting it. FF145 got this feature behind a feature flag. */
|