/* 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; --switch-padding-inline: 0.75rem;}@media (pointer: coarse) { :root details.dropdown { --dropdown-item-min-height: 40px; --switch-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:hover { background: var(--color-hover);}details.dropdown[open] > summary,details.dropdown > .content > ul > li:focus-within { 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); }}/* General styling of list items */details.dropdown > .content > ul > li { width: 100%; background: none; > :is(a, button) { padding-block: 0; padding-inline: var(--switch-padding-inline); min-height: var(--dropdown-item-min-height); width: 100%; display: flex; gap: 0.75rem; align-items: center; color: var(--color-text); /* 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); } } /* Suppress default styling of <button> */ > button { background: none; }}/* 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; > ul > li { direction: ltr; } }}/* dir-rtl option - force right-to-left box direction */details.dropdown.dir-rtl > .content { inset-inline: 0 auto; direction: rtl; > ul > li { 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. */
/* 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;
--switch-padding-inline: 0.75rem;
}
@media (pointer: coarse) {
:root details.dropdown {
--dropdown-item-min-height: 40px;
--switch-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:hover {
background: var(--color-hover);
}
details.dropdown[open] > summary,
details.dropdown > .content > ul > li:focus-within {
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);
}
}
/* General styling of list items */
details.dropdown > .content > ul > li {
width: 100%;
background: none;
> :is(a, button) {
padding-block: 0;
padding-inline: var(--switch-padding-inline);
min-height: var(--dropdown-item-min-height);
width: 100%;
display: flex;
gap: 0.75rem;
align-items: center;
color: var(--color-text);
/* 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);
}
}
/* Suppress default styling of <button> */
> button {
background: none;
}
}
/* 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;
> ul > li {
direction: ltr;
}
}
}
/* dir-rtl option - force right-to-left box direction */
details.dropdown.dir-rtl > .content {
inset-inline: 0 auto;
direction: rtl;
> ul > li {
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. */