summaryrefslogtreecommitdiffstats
path: root/web_src/css/modules
diff options
context:
space:
mode:
Diffstat (limited to 'web_src/css/modules')
-rw-r--r--web_src/css/modules/animations.css116
-rw-r--r--web_src/css/modules/breadcrumb.css14
-rw-r--r--web_src/css/modules/button.css756
-rw-r--r--web_src/css/modules/card.css134
-rw-r--r--web_src/css/modules/checkbox.css121
-rw-r--r--web_src/css/modules/comment.css90
-rw-r--r--web_src/css/modules/container.css59
-rw-r--r--web_src/css/modules/divider.css43
-rw-r--r--web_src/css/modules/flexcontainer.css33
-rw-r--r--web_src/css/modules/grid.css513
-rw-r--r--web_src/css/modules/header.css176
-rw-r--r--web_src/css/modules/input.css197
-rw-r--r--web_src/css/modules/label.css303
-rw-r--r--web_src/css/modules/list.css193
-rw-r--r--web_src/css/modules/message.css114
-rw-r--r--web_src/css/modules/modal.css86
-rw-r--r--web_src/css/modules/navbar.css147
-rw-r--r--web_src/css/modules/normalize.css243
-rw-r--r--web_src/css/modules/segment.css203
-rw-r--r--web_src/css/modules/select.css25
-rw-r--r--web_src/css/modules/svg.css41
-rw-r--r--web_src/css/modules/table.css385
-rw-r--r--web_src/css/modules/tippy.css170
-rw-r--r--web_src/css/modules/toast.css77
24 files changed, 4239 insertions, 0 deletions
diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css
new file mode 100644
index 00000000..a86c9234
--- /dev/null
+++ b/web_src/css/modules/animations.css
@@ -0,0 +1,116 @@
+@keyframes isloadingspin {
+ 0% { transform: translate(-50%, -50%) rotate(0deg); }
+ 100% { transform: translate(-50%, -50%) rotate(360deg); }
+}
+
+.is-loading {
+ pointer-events: none !important;
+ position: relative !important;
+}
+
+.is-loading > * {
+ opacity: 0.3;
+}
+
+.btn.is-loading > *,
+.button.is-loading > * {
+ opacity: 0;
+}
+
+.is-loading::after {
+ content: "";
+ position: absolute;
+ display: block;
+ left: 50%;
+ top: 50%;
+ height: min(4em, 66.6%);
+ width: fit-content; /* compat: safari - https://bugs.webkit.org/show_bug.cgi?id=267625 */
+ aspect-ratio: 1;
+ transform: translate(-50%, -50%);
+ animation: isloadingspin 1000ms infinite linear;
+ border-width: 4px;
+ border-style: solid;
+ border-color: var(--color-secondary) var(--color-secondary) var(--color-secondary-dark-8) var(--color-secondary-dark-8);
+ border-radius: var(--border-radius-full);
+}
+
+.is-loading.loading-icon-2px::after {
+ border-width: 2px;
+}
+
+.is-loading.loading-icon-3px::after {
+ border-width: 3px;
+}
+
+/* for single form button, the loading state should be on the button, but not go semi-transparent, just replace the text on the button with the loader. */
+form.single-button-form.is-loading > * {
+ opacity: 1;
+}
+
+form.single-button-form.is-loading .button {
+ color: transparent;
+}
+
+.markup pre.is-loading,
+.editor-loading.is-loading,
+.pdf-content.is-loading {
+ height: var(--height-loading);
+}
+
+.markup .is-loading > * {
+ visibility: hidden;
+}
+
+.markup .is-loading {
+ color: transparent;
+ background: transparent;
+}
+
+/* TODO: not needed, use "is-loading loading-icon-2px" instead */
+code.language-math.is-loading::after {
+ padding: 0;
+ border-width: 2px;
+ width: 1.25rem;
+ height: 1.25rem;
+}
+
+@keyframes fadein {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes fadeout {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes pulse {
+ 0% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(1.8);
+ }
+ 100% {
+ transform: scale(1);
+ }
+}
+
+.pulse {
+ animation: pulse 2s linear;
+}
+
+.ui.modal,
+.ui.dimmer.transition {
+ animation-name: fadein;
+ animation-duration: 100ms;
+ animation-timing-function: ease-in-out;
+}
diff --git a/web_src/css/modules/breadcrumb.css b/web_src/css/modules/breadcrumb.css
new file mode 100644
index 00000000..ca488c21
--- /dev/null
+++ b/web_src/css/modules/breadcrumb.css
@@ -0,0 +1,14 @@
+.breadcrumb {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 3px;
+}
+
+.breadcrumb .breadcrumb-divider {
+ color: var(--color-text-light-2);
+}
+
+.breadcrumb > * {
+ display: inline;
+}
diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css
new file mode 100644
index 00000000..0799ab80
--- /dev/null
+++ b/web_src/css/modules/button.css
@@ -0,0 +1,756 @@
+/* this contains override styles for buttons and related elements */
+
+/* these styles changed the Fomantic UI's rules, Fomantic UI expects only "basic" buttons have borders */
+.ui.button {
+ background: var(--color-button);
+ border: 1px solid var(--color-light-border);
+ color: var(--color-text);
+}
+
+.ui.button:hover,
+.ui.button:focus {
+ background: var(--color-hover);
+ color: var(--color-text);
+}
+
+.page-content .ui.button {
+ box-shadow: none !important;
+}
+
+.ui.active.button,
+.ui.button:active,
+.ui.active.button:active,
+.ui.active.button:hover,
+.ui.active.button:focus {
+ background: var(--color-active);
+ color: var(--color-text);
+}
+
+.delete-button,
+.delete-button:hover,
+.delete-button:focus {
+ color: var(--color-red);
+}
+
+/* btn is a plain button without any opinionated styling, it only uses flex for vertical alignment like ".ui.button" in base.css */
+
+.btn {
+ background: transparent;
+ border-radius: var(--border-radius);
+ border: none;
+ color: inherit;
+ margin: 0;
+ padding: 0;
+}
+
+.btn:hover,
+.btn:active,
+.btn:focus {
+ background: none;
+ border: none;
+}
+
+a.btn,
+a.btn:hover {
+ color: inherit;
+}
+
+/* By default, Fomantic UI doesn't support "bordered" buttons group, but Gitea would like to use it.
+And the default buttons always have borders now (not the same as Fomantic UI's default buttons, see above).
+It needs some tricks to tweak the left/right borders with active state */
+
+.ui.buttons .button {
+ border-right: none;
+}
+
+.ui.buttons .button:hover {
+ border-color: var(--color-secondary-dark-2);
+}
+
+.ui.buttons .button:hover + .button {
+ border-left: 1px solid var(--color-secondary-dark-2);
+}
+
+/* TODO: these "tw-hidden" selectors are only used by "blame.tmpl" buttons: Raw/Normal View/History/Unescape, need to be refactored to a clear solution later */
+.ui.buttons .button:first-child,
+.ui.buttons .button.tw-hidden:first-child + .button {
+ border-left: 1px solid var(--color-light-border);
+}
+
+.ui.buttons .button:last-child,
+.ui.buttons .button:nth-last-child(2):has(+ .button.tw-hidden) {
+ border-right: 1px solid var(--color-light-border);
+}
+
+.ui.buttons .button.active {
+ border-left: 1px solid var(--color-light-border);
+ border-right: 1px solid var(--color-light-border);
+}
+
+.ui.buttons .button.active + .button {
+ border-left: none;
+}
+
+.ui.basic.buttons .button,
+.ui.basic.button,
+.ui.basic.buttons .button:hover,
+.ui.basic.button:hover {
+ box-shadow: none;
+}
+
+/* apply the vertical padding of .compact to non-compact buttons when they contain a svg as they
+ would otherwise appear too large. Seen on "RSS Feed" button on repo releases tab. */
+.ui.small.button:not(.compact):has(.svg) {
+ padding-top: 0.58928571em;
+ padding-bottom: 0.58928571em;
+}
+
+.ui.labeled.button.disabled > .button,
+.ui.basic.buttons .button,
+.ui.basic.button {
+ color: var(--color-text-light);
+ background: var(--color-button);
+}
+
+.ui.basic.buttons .button:hover,
+.ui.basic.button:hover,
+.ui.basic.buttons .button:focus,
+.ui.basic.button:focus {
+ color: var(--color-text);
+ background: var(--color-hover);
+ border-color: var(--color-secondary-dark-2);
+}
+
+.ui.basic.buttons .button:active,
+.ui.basic.button:active,
+.ui.basic.buttons .active.button,
+.ui.basic.active.button,
+.ui.basic.buttons .active.button:hover,
+.ui.basic.active.button:hover,
+.ui.basic.buttons .active.button:focus,
+.ui.basic.active.button:focus {
+ color: var(--color-text);
+ background: var(--color-active);
+}
+
+.ui.labeled.button > .label {
+ border-color: var(--color-light-border);
+}
+
+.ui.labeled.icon.buttons > .button > .icon,
+.ui.labeled.icon.button > .icon {
+ background: var(--color-hover);
+}
+
+/* primary */
+
+.ui.primary.labels .label,
+.ui.ui.ui.primary.label,
+.ui.primary.button,
+.ui.primary.buttons .button {
+ background: var(--color-primary);
+ color: var(--color-primary-contrast);
+}
+
+.ui.primary.button:hover,
+.ui.primary.buttons .button:hover,
+.ui.primary.button:focus,
+.ui.primary.buttons .button:focus {
+ background: var(--color-primary-hover);
+ color: var(--color-primary-contrast);
+}
+
+.ui.primary.button:active,
+.ui.primary.buttons .button:active {
+ background: var(--color-primary-active);
+}
+
+.ui.basic.primary.buttons .button,
+.ui.basic.primary.button {
+ color: var(--color-primary);
+ border-color: var(--color-primary);
+}
+
+.ui.basic.primary.buttons .button:hover,
+.ui.basic.primary.button:hover,
+.ui.basic.primary.buttons .button:focus,
+.ui.basic.primary.button:focus {
+ color: var(--color-primary-hover);
+ border-color: var(--color-primary-hover);
+}
+
+.ui.basic.primary.buttons .button:active,
+.ui.basic.primary.button:active {
+ color: var(--color-primary-active);
+ border-color: var(--color-primary-active);
+}
+
+/* secondary */
+
+.ui.secondary.labels .label,
+.ui.ui.ui.secondary.label,
+.ui.secondary.button,
+.ui.secondary.buttons .button,
+.ui.secondary.button:focus,
+.ui.secondary.buttons .button:focus {
+ background: var(--color-secondary-button);
+}
+
+.ui.secondary.button:hover,
+.ui.secondary.buttons .button:hover {
+ background: var(--color-secondary-hover);
+}
+
+.ui.secondary.button:active,
+.ui.secondary.buttons .button:active {
+ background: var(--color-secondary-active);
+}
+
+.ui.basic.secondary.buttons .button,
+.ui.basic.secondary.button {
+ color: var(--color-secondary-button);
+ border-color: var(--color-secondary-button);
+}
+
+.ui.basic.secondary.buttons .button:hover,
+.ui.basic.secondary.button:hover,
+.ui.basic.secondary.button:focus,
+.ui.basic.secondary.buttons .button:focus {
+ color: var(--color-secondary-hover);
+ border-color: var(--color-secondary-hover);
+}
+
+.ui.basic.secondary.buttons .button:active,
+.ui.basic.secondary.button:active {
+ color: var(--color-secondary-active);
+ border-color: var(--color-secondary-active);
+}
+
+/* red */
+
+.ui.red.labels .label,
+.ui.ui.ui.red.label,
+.ui.red.button,
+.ui.red.buttons .button {
+ background: var(--color-red);
+}
+
+.ui.red.button:hover,
+.ui.red.buttons .button:hover,
+.ui.red.button:focus,
+.ui.red.buttons .button:focus {
+ background: var(--color-red-dark-1);
+}
+
+.ui.red.button:active,
+.ui.red.buttons .button:active {
+ background: var(--color-red-dark-2);
+}
+
+.ui.basic.red.buttons .button,
+.ui.basic.red.button {
+ color: var(--color-red);
+ border-color: var(--color-red);
+}
+
+.ui.basic.red.buttons .button:hover,
+.ui.basic.red.button:hover,
+.ui.basic.red.buttons .button:focus,
+.ui.basic.red.button:focus {
+ color: var(--color-red-dark-1);
+ border-color: var(--color-red-dark-1);
+}
+
+.ui.basic.red.buttons .button:active,
+.ui.basic.red.button:active {
+ color: var(--color-red-dark-2);
+ border-color: var(--color-red-dark-2);
+}
+
+/* orange */
+
+.ui.orange.labels .label,
+.ui.ui.ui.orange.label,
+.ui.orange.button,
+.ui.orange.buttons .button,
+.ui.orange.button:focus,
+.ui.orange.buttons .button:focus {
+ background: var(--color-orange);
+}
+
+.ui.orange.button:hover,
+.ui.orange.buttons .button:hover {
+ background: var(--color-orange-dark-1);
+}
+
+.ui.orange.button:active,
+.ui.orange.buttons .button:active {
+ background: var(--color-orange-dark-2);
+}
+
+.ui.basic.orange.buttons .button,
+.ui.basic.orange.button,
+.ui.basic.orange.buttons .button:focus,
+.ui.basic.orange.button:focus {
+ color: var(--color-orange);
+ border-color: var(--color-orange);
+}
+
+.ui.basic.orange.buttons .button:hover,
+.ui.basic.orange.button:hover {
+ color: var(--color-orange-dark-1);
+ border-color: var(--color-orange-dark-1);
+}
+
+.ui.basic.orange.buttons .button:active,
+.ui.basic.orange.button:active {
+ color: var(--color-orange-dark-2);
+ border-color: var(--color-orange-dark-2);
+}
+
+/* yellow */
+
+.ui.yellow.labels .label,
+.ui.ui.ui.yellow.label,
+.ui.yellow.button,
+.ui.yellow.buttons .button,
+.ui.yellow.button:focus,
+.ui.yellow.buttons .button:focus {
+ background: var(--color-yellow);
+}
+
+.ui.yellow.button:hover,
+.ui.yellow.buttons .button:hover {
+ background: var(--color-yellow-dark-1);
+}
+
+.ui.yellow.button:active,
+.ui.yellow.buttons .button:active {
+ background: var(--color-yellow-dark-2);
+}
+
+.ui.basic.yellow.buttons .button,
+.ui.basic.yellow.button,
+.ui.basic.yellow.buttons .button:focus,
+.ui.basic.yellow.button:focus {
+ color: var(--color-yellow);
+ border-color: var(--color-yellow);
+}
+
+.ui.basic.yellow.buttons .button:hover,
+.ui.basic.yellow.button:hover {
+ color: var(--color-yellow-dark-1);
+ border-color: var(--color-yellow-dark-1);
+}
+
+.ui.basic.yellow.buttons .button:active,
+.ui.basic.yellow.button:active {
+ color: var(--color-yellow-dark-2);
+ border-color: var(--color-yellow-dark-2);
+}
+
+/* olive */
+
+.ui.olive.labels .label,
+.ui.ui.ui.olive.label,
+.ui.olive.button,
+.ui.olive.buttons .button,
+.ui.olive.button:focus,
+.ui.olive.buttons .button:focus {
+ background: var(--color-olive);
+}
+
+.ui.olive.button:hover,
+.ui.olive.buttons .button:hover {
+ background: var(--color-olive-dark-1);
+}
+
+.ui.olive.button:active,
+.ui.olive.buttons .button:active {
+ background: var(--color-olive-dark-2);
+}
+
+.ui.basic.olive.buttons .button,
+.ui.basic.olive.button,
+.ui.basic.olive.buttons .button:focus,
+.ui.basic.olive.button:focus {
+ color: var(--color-olive);
+ border-color: var(--color-olive);
+}
+
+.ui.basic.olive.buttons .button:hover,
+.ui.basic.olive.button:hover {
+ color: var(--color-olive-dark-1);
+ border-color: var(--color-olive-dark-1);
+}
+
+.ui.basic.olive.buttons .button:active,
+.ui.basic.olive.button:active {
+ color: var(--color-olive-dark-2);
+ border-color: var(--color-olive-dark-2);
+}
+
+/* green */
+
+.ui.green.labels .label,
+.ui.ui.ui.green.label,
+.ui.green.button,
+.ui.green.buttons .button,
+.ui.green.button:focus,
+.ui.green.buttons .button:focus {
+ background: var(--color-green);
+}
+
+.ui.green.button:hover,
+.ui.green.buttons .button:hover {
+ background: var(--color-green-dark-1);
+}
+
+.ui.green.button:active,
+.ui.green.buttons .button:active {
+ background: var(--color-green-dark-2);
+}
+
+.ui.basic.green.buttons .button,
+.ui.basic.green.button,
+.ui.basic.green.buttons .button:focus,
+.ui.basic.green.button:focus {
+ color: var(--color-green);
+ border-color: var(--color-green);
+}
+
+.ui.basic.green.buttons .button:hover,
+.ui.basic.green.button:hover {
+ color: var(--color-green-dark-1);
+ border-color: var(--color-green-dark-1);
+}
+
+.ui.basic.green.buttons .button:active,
+.ui.basic.green.button:active {
+ color: var(--color-green-dark-2);
+ border-color: var(--color-green-dark-2);
+}
+
+/* teal */
+
+.ui.teal.labels .label,
+.ui.ui.ui.teal.label,
+.ui.teal.button,
+.ui.teal.buttons .button,
+.ui.teal.button:focus,
+.ui.teal.buttons .button:focus {
+ background: var(--color-teal);
+}
+
+.ui.teal.button:hover,
+.ui.teal.buttons .button:hover {
+ background: var(--color-teal-dark-1);
+}
+
+.ui.teal.button:active,
+.ui.teal.buttons .button:active {
+ background: var(--color-teal-dark-2);
+}
+
+.ui.basic.teal.buttons .button,
+.ui.basic.teal.button,
+.ui.basic.teal.buttons .button:focus,
+.ui.basic.teal.button:focus {
+ color: var(--color-teal);
+ border-color: var(--color-teal);
+}
+
+.ui.basic.teal.buttons .button:hover,
+.ui.basic.teal.button:hover {
+ color: var(--color-teal-dark-1);
+ border-color: var(--color-teal-dark-1);
+}
+
+.ui.basic.teal.buttons .button:active,
+.ui.basic.teal.button:active {
+ color: var(--color-teal-dark-2);
+ border-color: var(--color-teal-dark-2);
+}
+
+/* blue */
+
+.ui.blue.labels .label,
+.ui.ui.ui.blue.label,
+.ui.blue.button,
+.ui.blue.buttons .button,
+.ui.blue.button:focus,
+.ui.blue.buttons .button:focus {
+ background: var(--color-blue);
+}
+
+.ui.blue.button:hover,
+.ui.blue.buttons .button:hover {
+ background: var(--color-blue-dark-1);
+}
+
+.ui.blue.button:active,
+.ui.blue.buttons .button:active {
+ background: var(--color-blue-dark-2);
+}
+
+.ui.basic.blue.buttons .button,
+.ui.basic.blue.button,
+.ui.basic.blue.buttons .button:focus,
+.ui.basic.blue.button:focus {
+ color: var(--color-blue);
+ border-color: var(--color-blue);
+}
+
+.ui.basic.blue.buttons .button:hover,
+.ui.basic.blue.button:hover {
+ color: var(--color-blue-dark-1);
+ border-color: var(--color-blue-dark-1);
+}
+
+.ui.basic.blue.buttons .button:active,
+.ui.basic.blue.button:active {
+ color: var(--color-blue-dark-2);
+ border-color: var(--color-blue-dark-2);
+}
+
+/* violet */
+
+.ui.violet.labels .label,
+.ui.ui.ui.violet.label,
+.ui.violet.button,
+.ui.violet.buttons .button,
+.ui.violet.button:focus,
+.ui.violet.buttons .button:focus {
+ background: var(--color-violet);
+}
+
+.ui.violet.button:hover,
+.ui.violet.buttons .button:hover {
+ background: var(--color-violet-dark-1);
+}
+
+.ui.violet.button:active,
+.ui.violet.buttons .button:active {
+ background: var(--color-violet-dark-2);
+}
+
+.ui.basic.violet.buttons .button,
+.ui.basic.violet.button,
+.ui.basic.violet.buttons .button:focus,
+.ui.basic.violet.button:focus {
+ color: var(--color-violet);
+ border-color: var(--color-violet);
+}
+
+.ui.basic.violet.buttons .button:hover,
+.ui.basic.violet.button:hover {
+ color: var(--color-violet-dark-1);
+ border-color: var(--color-violet-dark-1);
+}
+
+.ui.basic.violet.buttons .button:active,
+.ui.basic.violet.button:active {
+ color: var(--color-violet-dark-2);
+ border-color: var(--color-violet-dark-2);
+}
+
+/* purple */
+
+.ui.purple.labels .label,
+.ui.ui.ui.purple.label,
+.ui.purple.button,
+.ui.purple.buttons .button,
+.ui.purple.button:focus,
+.ui.purple.buttons .button:focus {
+ background: var(--color-purple);
+}
+
+.ui.purple.button:hover,
+.ui.purple.buttons .button:hover {
+ background: var(--color-purple-dark-1);
+}
+
+.ui.purple.button:active,
+.ui.purple.buttons .button:active {
+ background: var(--color-purple-dark-2);
+}
+
+.ui.basic.purple.buttons .button,
+.ui.basic.purple.button,
+.ui.basic.purple.buttons .button:focus,
+.ui.basic.purple.button:focus {
+ color: var(--color-purple);
+ border-color: var(--color-purple);
+}
+
+.ui.basic.purple.buttons .button:hover,
+.ui.basic.purple.button:hover {
+ color: var(--color-purple-dark-1);
+ border-color: var(--color-purple-dark-1);
+}
+
+.ui.basic.purple.buttons .button:active,
+.ui.basic.purple.button:active {
+ color: var(--color-purple-dark-2);
+ border-color: var(--color-purple-dark-2);
+}
+
+/* pink */
+
+.ui.pink.labels .label,
+.ui.ui.ui.pink.label,
+.ui.pink.button,
+.ui.pink.buttons .button,
+.ui.pink.button:focus,
+.ui.pink.buttons .button:focus {
+ background: var(--color-pink);
+}
+
+.ui.pink.button:hover,
+.ui.pink.buttons .button:hover {
+ background: var(--color-pink-dark-1);
+}
+
+.ui.pink.button:active,
+.ui.pink.buttons .button:active {
+ background: var(--color-pink-dark-2);
+}
+
+.ui.basic.pink.buttons .button,
+.ui.basic.pink.button,
+.ui.basic.pink.buttons .button:focus,
+.ui.basic.pink.button:focus {
+ color: var(--color-pink);
+ border-color: var(--color-pink);
+}
+
+.ui.basic.pink.buttons .button:hover,
+.ui.basic.pink.button:hover {
+ color: var(--color-pink-dark-1);
+ border-color: var(--color-pink-dark-1);
+}
+
+.ui.basic.pink.buttons .button:active,
+.ui.basic.pink.button:active {
+ color: var(--color-pink-dark-2);
+ border-color: var(--color-pink-dark-2);
+}
+
+/* brown */
+
+.ui.brown.labels .label,
+.ui.ui.ui.brown.label,
+.ui.brown.button,
+.ui.brown.buttons .button,
+.ui.brown.button:focus,
+.ui.brown.buttons .button:focus {
+ background: var(--color-brown);
+}
+
+.ui.brown.button:hover,
+.ui.brown.buttons .button:hover {
+ background: var(--color-brown-dark-1);
+}
+
+.ui.brown.button:active,
+.ui.brown.buttons .button:active {
+ background: var(--color-brown-dark-2);
+}
+
+.ui.basic.brown.buttons .button,
+.ui.basic.brown.button,
+.ui.basic.brown.buttons .button:focus,
+.ui.basic.brown.button:focus {
+ color: var(--color-brown);
+ border-color: var(--color-brown);
+}
+
+.ui.basic.brown.buttons .button:hover,
+.ui.basic.brown.button:hover {
+ color: var(--color-brown-dark-1);
+ border-color: var(--color-brown-dark-1);
+}
+
+.ui.basic.brown.buttons .button:active,
+.ui.basic.brown.button:active {
+ color: var(--color-brown-dark-2);
+ border-color: var(--color-brown-dark-2);
+}
+
+/* negative */
+
+.ui.negative.buttons .button,
+.ui.negative.button,
+.ui.negative.buttons .button:focus,
+.ui.negative.button:focus {
+ background: var(--color-red);
+}
+
+.ui.negative.buttons .button:hover,
+.ui.negative.button:hover {
+ background: var(--color-red-dark-1);
+}
+
+.ui.negative.buttons .button:active,
+.ui.negative.button:active {
+ background: var(--color-red-dark-2);
+}
+
+.ui.basic.negative.buttons .button,
+.ui.basic.negative.button,
+.ui.basic.negative.buttons .button:focus,
+.ui.basic.negative.button:focus {
+ color: var(--color-red);
+ border-color: var(--color-red);
+}
+
+.ui.basic.negative.buttons .button:hover,
+.ui.basic.negative.button:hover {
+ color: var(--color-red-dark-1);
+ border-color: var(--color-red-dark-1);
+}
+
+.ui.basic.negative.buttons .button:active,
+.ui.basic.negative.button:active {
+ color: var(--color-red-dark-2);
+ border-color: var(--color-red-dark-2);
+}
+
+/* positive */
+
+.ui.positive.buttons .button,
+.ui.positive.button,
+.ui.positive.buttons .button:focus,
+.ui.positive.button:focus {
+ background: var(--color-green);
+}
+
+.ui.positive.buttons .button:hover,
+.ui.positive.button:hover {
+ background: var(--color-green-dark-1);
+}
+
+.ui.positive.buttons .button:active,
+.ui.positive.button:active {
+ background: var(--color-green-dark-2);
+}
+
+.ui.basic.positive.buttons .button,
+.ui.basic.positive.button,
+.ui.basic.positive.buttons .button:focus,
+.ui.basic.positive.button:focus {
+ color: var(--color-green);
+ border-color: var(--color-green);
+}
+
+.ui.basic.positive.buttons .button:hover,
+.ui.basic.positive.button:hover {
+ color: var(--color-green-dark-1);
+ border-color: var(--color-green-dark-1);
+}
+
+.ui.basic.positive.buttons .button:active,
+.ui.basic.positive.button:active {
+ color: var(--color-green-dark-2);
+ border-color: var(--color-green-dark-2);
+}
diff --git a/web_src/css/modules/card.css b/web_src/css/modules/card.css
new file mode 100644
index 00000000..2406def6
--- /dev/null
+++ b/web_src/css/modules/card.css
@@ -0,0 +1,134 @@
+/* Below styles are a subset of the full fomantic card styles which are */
+/* needed to get all current uses of fomantic cards working. */
+/* TODO: remove all these styles and use custom styling instead */
+
+.ui.card:last-child {
+ margin-bottom: 0;
+}
+.ui.card:first-child {
+ margin-top: 0;
+}
+
+.ui.cards > .card,
+.ui.card {
+ display: flex;
+ flex-direction: column;
+ max-width: 100%;
+ width: 290px;
+ min-height: 0;
+ padding: 0;
+ background: var(--color-card);
+ border: 1px solid var(--color-secondary);
+ box-shadow: none;
+ word-wrap: break-word;
+}
+
+.ui.card {
+ margin: 1em 0;
+}
+
+.ui.cards {
+ display: flex;
+ margin: -0.875em -0.5em;
+ flex-wrap: wrap;
+}
+
+.ui.cards > .card {
+ display: flex;
+ margin: 0.875em 0.5em;
+ float: none;
+}
+
+.ui.cards > .card > .content,
+.ui.card > .content {
+ border-top: 1px solid var(--color-secondary);
+ max-width: 100%;
+ padding: 1em;
+ font-size: 1em;
+}
+
+.ui.cards > .card > .content > .meta + .description,
+.ui.cards > .card > .content > .header + .description,
+.ui.card > .content > .meta + .description,
+.ui.card > .content > .header + .description {
+ margin-top: .5em;
+}
+
+.ui.cards > .card > .content > .header:not(.ui),
+.ui.card > .content > .header:not(.ui) {
+ font-weight: var(--font-weight-medium);
+ font-size: 1.28571429em;
+ margin-top: -.21425em;
+ line-height: 1.28571429;
+}
+
+.ui.cards > .card > .content:first-child,
+.ui.card > .content:first-child {
+ border-top: none;
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+}
+
+.ui.cards > .card > :last-child,
+.ui.card > :last-child {
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+}
+
+.ui.cards > .card > :only-child,
+.ui.card > :only-child {
+ border-radius: var(--border-radius) !important;
+}
+
+.ui.cards > .card > .extra,
+.ui.card > .extra,
+.ui.cards > .card > .extra a:not(.ui),
+.ui.card > .extra a:not(.ui) {
+ color: var(--color-text);
+}
+
+.ui.cards > .card > .extra a:not(.ui):hover,
+.ui.card > .extra a:not(.ui):hover {
+ color: var(--color-primary);
+}
+
+.ui.cards > .card > .content > .header,
+.ui.card > .content > .header {
+ color: var(--color-text);
+}
+
+.ui.cards > .card > .content > .description,
+.ui.card > .content > .description {
+ color: var(--color-text);
+}
+
+.ui.cards > .card .meta > a:not(.ui),
+.ui.card .meta > a:not(.ui) {
+ color: var(--color-text-light-2);
+}
+
+.ui.cards > .card .meta > a:not(.ui):hover,
+.ui.card .meta > a:not(.ui):hover {
+ color: var(--color-text);
+}
+
+.ui.cards a.card:hover,
+a.ui.card:hover {
+ border: 1px solid var(--color-secondary);
+ background: var(--color-card);
+}
+
+.ui.cards > .card > .extra,
+.ui.card > .extra {
+ color: var(--color-text);
+ border-top-color: var(--color-secondary-light-1) !important;
+}
+
+.ui.three.cards {
+ margin-left: -1em;
+ margin-right: -1em;
+}
+
+.ui.three.cards > .card {
+ width: calc(33.33333333333333% - 2em);
+ margin-left: 1em;
+ margin-right: 1em;
+}
diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css
new file mode 100644
index 00000000..8d73573b
--- /dev/null
+++ b/web_src/css/modules/checkbox.css
@@ -0,0 +1,121 @@
+/* based on Fomantic UI checkbox module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+input[type="checkbox"],
+input[type="radio"] {
+ width: var(--checkbox-size);
+ height: var(--checkbox-size);
+}
+
+.ui.checkbox {
+ position: relative;
+ display: inline-block;
+ vertical-align: baseline;
+ min-height: var(--checkbox-size);
+ line-height: var(--checkbox-size);
+ min-width: var(--checkbox-size);
+ padding: 1px;
+}
+
+.ui.checkbox input[type="checkbox"],
+.ui.checkbox input[type="radio"] {
+ position: absolute;
+ top: 1px;
+ left: 0;
+ width: var(--checkbox-size);
+ height: var(--checkbox-size);
+}
+
+.ui.checkbox input[type="checkbox"]:enabled,
+.ui.checkbox input[type="radio"]:enabled,
+.ui.checkbox label:enabled {
+ cursor: pointer;
+}
+
+.ui.checkbox label {
+ cursor: auto;
+ position: relative;
+ display: block;
+ user-select: none;
+}
+
+.ui.checkbox label,
+.ui.radio.checkbox label {
+ margin-left: 1.85714em;
+}
+
+.ui.checkbox + label {
+ vertical-align: middle;
+}
+
+.ui.disabled.checkbox label,
+.ui.checkbox input[disabled] ~ label {
+ cursor: default !important;
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+.ui.radio.checkbox {
+ min-height: var(--checkbox-size);
+}
+
+/* "switch" styled checkbox */
+
+.ui.toggle.checkbox {
+ min-height: 1.5rem;
+}
+.ui.toggle.checkbox input {
+ width: 3.5rem;
+ height: 21px;
+ opacity: 0;
+ z-index: 3;
+}
+.ui.toggle.checkbox label {
+ min-height: 1.5rem;
+ padding-left: 4.5rem;
+ padding-top: 0.15em;
+}
+.ui.toggle.checkbox label::before {
+ display: block;
+ position: absolute;
+ content: "";
+ z-index: 1;
+ top: 0;
+ width: 49px;
+ height: 21px;
+ border-radius: 500rem;
+ left: 0;
+}
+.ui.toggle.checkbox label::after {
+ background: var(--color-white);
+ box-shadow: 1px 1px 4px 1px var(--color-shadow);
+ position: absolute;
+ content: "";
+ opacity: 1;
+ z-index: 2;
+ width: 18px;
+ height: 18px;
+ top: 1.5px;
+ left: 1.5px;
+ border-radius: 500rem;
+ transition: background 0.3s ease, left 0.3s ease;
+}
+.ui.toggle.checkbox input ~ label::after {
+ left: 1.5px;
+}
+.ui.toggle.checkbox input:checked ~ label::after {
+ left: 29px;
+}
+.ui.toggle.checkbox input:focus ~ label::before,
+.ui.toggle.checkbox label::before {
+ background: var(--color-input-toggle-background);
+}
+.ui.toggle.checkbox label,
+.ui.toggle.checkbox input:checked ~ label,
+.ui.toggle.checkbox input:focus:checked ~ label {
+ color: var(--color-text) !important;
+}
+.ui.toggle.checkbox input:checked ~ label::before,
+.ui.toggle.checkbox input:focus:checked ~ label::before {
+ background: var(--color-primary) !important;
+}
diff --git a/web_src/css/modules/comment.css b/web_src/css/modules/comment.css
new file mode 100644
index 00000000..799eeb85
--- /dev/null
+++ b/web_src/css/modules/comment.css
@@ -0,0 +1,90 @@
+/* These are the remnants of the fomantic comment module */
+/* TODO: remove all of these rules */
+
+.ui.comments {
+ margin: 1.5em 0;
+}
+
+.ui.comments:first-child {
+ margin-top: 0;
+}
+
+.ui.comments:last-child {
+ margin-bottom: 0;
+}
+
+.ui.comments .comment {
+ position: relative;
+ background: none;
+ margin: 0.5em 0 0;
+ padding: 0.5em 0 0;
+ border: none;
+ border-top: none;
+ line-height: 1.2;
+}
+
+.ui.comments .comment:first-child {
+ margin-top: 0;
+ padding-top: 0;
+}
+
+.ui.comments .comment > .comments {
+ margin: 0 0 0.5em 0.5em;
+ padding: 1em 0 1em 1em;
+}
+
+.ui.comments .comment > .comments::before {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.ui.comments .comment > .comments .comment {
+ border: none;
+ border-top: none;
+ background: none;
+}
+
+.ui.comments .comment .avatar {
+ float: left;
+ width: 2.5em;
+}
+
+.ui.comments .comment > .content {
+ display: block;
+}
+
+.ui.comments .comment > .avatar ~ .content {
+ margin-left: 3.5em;
+}
+
+.ui.comments .comment .author {
+ font-size: 1em;
+ font-weight: var(--font-weight-medium);
+}
+
+.ui.comments .comment a.author {
+ cursor: pointer;
+}
+
+.ui.comments .comment .metadata {
+ display: inline-block;
+ margin-left: 0.5em;
+ font-size: 0.875em;
+}
+
+.ui.comments .comment .metadata > * {
+ display: inline-block;
+ margin: 0 0.5em 0 0;
+}
+
+.ui.comments .comment .metadata > :last-child {
+ margin-right: 0;
+}
+
+.ui.comments .comment .text {
+ margin: 0.25em 0 0.5em;
+ font-size: 1em;
+ word-wrap: break-word;
+ line-height: 1.3;
+}
diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css
new file mode 100644
index 00000000..f394d6c0
--- /dev/null
+++ b/web_src/css/modules/container.css
@@ -0,0 +1,59 @@
+/* based on Fomantic UI container module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.container {
+ display: block;
+ max-width: 100%;
+}
+
+@media (max-width: 767.98px) {
+ .ui.ui.ui.container:not(.fluid) {
+ width: auto;
+ margin-left: 1em;
+ margin-right: 1em;
+ }
+}
+
+@media (min-width: 768px) and (max-width: 991.98px) {
+ .ui.ui.ui.container:not(.fluid) {
+ width: 723px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+@media (min-width: 992px) and (max-width: 1199.98px) {
+ .ui.ui.ui.container:not(.fluid) {
+ width: 933px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+@media (min-width: 1200px) {
+ .ui.ui.ui.container:not(.fluid) {
+ width: 1127px;
+ margin-left: auto;
+ margin-right: auto;
+ }
+}
+
+.ui.fluid.container {
+ width: 100%;
+}
+
+.ui[class*="center aligned"].container {
+ text-align: center;
+}
+
+/* overwrite width of containers inside the main page content div (div with class "page-content") */
+.page-content .ui.ui.ui.container:not(.fluid) {
+ width: 1280px;
+ max-width: calc(100% - calc(2 * var(--page-margin-x)));
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.ui.container.fluid.padded {
+ padding: 0 var(--page-margin-x);
+}
diff --git a/web_src/css/modules/divider.css b/web_src/css/modules/divider.css
new file mode 100644
index 00000000..acc8408f
--- /dev/null
+++ b/web_src/css/modules/divider.css
@@ -0,0 +1,43 @@
+.divider {
+ margin: 10px 0;
+ height: 0;
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text);
+ font-size: 1rem;
+ width: 100%;
+}
+
+h4.divider {
+ margin-top: 1.25rem;
+ margin-bottom: 1.25rem;
+}
+
+.divider:not(.divider-text) {
+ border-top: 1px solid var(--color-secondary);
+}
+
+.divider.divider-text {
+ display: flex;
+ align-items: center;
+ padding: 5px 0;
+}
+
+.divider.divider-text::before,
+.divider.divider-text::after {
+ content: "";
+ flex: 1;
+ border-top: 1px solid var(--color-secondary);
+}
+
+.divider.divider-text::before {
+ margin-right: .75em;
+}
+
+.divider.divider-text::after {
+ margin-left: .75em;
+}
+
+.ui.dropdown .menu > .divider {
+ border-top: 1px solid var(--color-secondary);
+ margin: 4px 0;
+}
diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css
new file mode 100644
index 00000000..5d4e12cc
--- /dev/null
+++ b/web_src/css/modules/flexcontainer.css
@@ -0,0 +1,33 @@
+/* container for full-page content with sidebar on left */
+
+.flex-container {
+ display: flex !important;
+ gap: var(--page-spacing);
+ margin-top: var(--page-spacing);
+}
+
+/* small options menu on the left, used in settings/admin pages */
+.flex-container-nav {
+ width: 240px;
+}
+
+/* wide sidebar on the right, used in frontpage */
+.flex-container-sidebar {
+ width: 35%;
+}
+
+.flex-container-main {
+ flex: 1;
+ min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */
+}
+
+@media (max-width: 767.98px) {
+ .flex-container {
+ flex-direction: column;
+ }
+ .flex-container-nav,
+ .flex-container-sidebar {
+ order: -1;
+ width: auto;
+ }
+}
diff --git a/web_src/css/modules/grid.css b/web_src/css/modules/grid.css
new file mode 100644
index 00000000..a2c55804
--- /dev/null
+++ b/web_src/css/modules/grid.css
@@ -0,0 +1,513 @@
+/* based on Fomantic UI grid module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.grid {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-items: stretch;
+ padding: 0;
+ margin: -1rem;
+}
+
+.ui.relaxed.grid {
+ margin-left: -1.5rem;
+ margin-right: -1.5rem;
+}
+.ui[class*="very relaxed"].grid {
+ margin-left: -2.5rem;
+ margin-right: -2.5rem;
+}
+
+.ui.grid + .grid {
+ margin-top: 1rem;
+}
+
+.ui.grid > .column:not(.row),
+.ui.grid > .row > .column {
+ position: relative;
+ display: inline-block;
+ width: 6.25%;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ vertical-align: top;
+}
+.ui.grid > * {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+
+.ui.grid > .row {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: inherit;
+ align-items: stretch;
+ width: 100% !important;
+ padding: 0;
+ padding-top: 1rem;
+ padding-bottom: 1rem;
+}
+
+.ui.grid > .column:not(.row) {
+ padding-top: 1rem;
+ padding-bottom: 1rem;
+}
+.ui.grid > .row > .column {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.ui.grid > .row > img,
+.ui.grid > .row > .column > img {
+ max-width: 100%;
+}
+
+.ui.grid > .ui.grid:first-child {
+ margin-top: 0;
+}
+.ui.grid > .ui.grid:last-child {
+ margin-bottom: 0;
+}
+
+.ui.grid .aligned.row > .column > .segment:not(.compact):not(.attached),
+.ui.aligned.grid .column > .segment:not(.compact):not(.attached) {
+ width: 100%;
+}
+
+.ui.grid .row + .ui.divider {
+ flex-grow: 1;
+ margin: 1rem;
+}
+.ui.grid .column + .ui.vertical.divider {
+ height: calc(50% - 1rem);
+}
+
+.ui.grid > .row > .column:last-child > .horizontal.segment,
+.ui.grid > .column:last-child > .horizontal.segment {
+ box-shadow: none;
+}
+
+@media only screen and (max-width: 767.98px) {
+ .ui.page.grid {
+ width: auto;
+ padding-left: 0;
+ padding-right: 0;
+ margin-left: 0;
+ margin-right: 0;
+ }
+}
+@media only screen and (min-width: 768px) and (max-width: 991.98px) {
+ .ui.page.grid {
+ width: auto;
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 2em;
+ padding-right: 2em;
+ }
+}
+@media only screen and (min-width: 992px) and (max-width: 1199.98px) {
+ .ui.page.grid {
+ width: auto;
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 3%;
+ padding-right: 3%;
+ }
+}
+@media only screen and (min-width: 1200px) and (max-width: 1919.98px) {
+ .ui.page.grid {
+ width: auto;
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 15%;
+ padding-right: 15%;
+ }
+}
+@media only screen and (min-width: 1920px) {
+ .ui.page.grid {
+ width: auto;
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 23%;
+ padding-right: 23%;
+ }
+}
+
+.ui.grid > .column:only-child,
+.ui.grid > .row > .column:only-child {
+ width: 100%;
+}
+
+.ui[class*="one column"].grid > .row > .column,
+.ui[class*="one column"].grid > .column:not(.row) {
+ width: 100%;
+}
+.ui[class*="two column"].grid > .row > .column,
+.ui[class*="two column"].grid > .column:not(.row) {
+ width: 50%;
+}
+.ui[class*="three column"].grid > .row > .column,
+.ui[class*="three column"].grid > .column:not(.row) {
+ width: 33.33333333%;
+}
+.ui[class*="four column"].grid > .row > .column,
+.ui[class*="four column"].grid > .column:not(.row) {
+ width: 25%;
+}
+.ui[class*="five column"].grid > .row > .column,
+.ui[class*="five column"].grid > .column:not(.row) {
+ width: 20%;
+}
+.ui[class*="six column"].grid > .row > .column,
+.ui[class*="six column"].grid > .column:not(.row) {
+ width: 16.66666667%;
+}
+.ui[class*="seven column"].grid > .row > .column,
+.ui[class*="seven column"].grid > .column:not(.row) {
+ width: 14.28571429%;
+}
+.ui[class*="eight column"].grid > .row > .column,
+.ui[class*="eight column"].grid > .column:not(.row) {
+ width: 12.5%;
+}
+.ui[class*="nine column"].grid > .row > .column,
+.ui[class*="nine column"].grid > .column:not(.row) {
+ width: 11.11111111%;
+}
+.ui[class*="ten column"].grid > .row > .column,
+.ui[class*="ten column"].grid > .column:not(.row) {
+ width: 10%;
+}
+.ui[class*="eleven column"].grid > .row > .column,
+.ui[class*="eleven column"].grid > .column:not(.row) {
+ width: 9.09090909%;
+}
+.ui[class*="twelve column"].grid > .row > .column,
+.ui[class*="twelve column"].grid > .column:not(.row) {
+ width: 8.33333333%;
+}
+.ui[class*="thirteen column"].grid > .row > .column,
+.ui[class*="thirteen column"].grid > .column:not(.row) {
+ width: 7.69230769%;
+}
+.ui[class*="fourteen column"].grid > .row > .column,
+.ui[class*="fourteen column"].grid > .column:not(.row) {
+ width: 7.14285714%;
+}
+.ui[class*="fifteen column"].grid > .row > .column,
+.ui[class*="fifteen column"].grid > .column:not(.row) {
+ width: 6.66666667%;
+}
+.ui[class*="sixteen column"].grid > .row > .column,
+.ui[class*="sixteen column"].grid > .column:not(.row) {
+ width: 6.25%;
+}
+
+.ui.grid > [class*="one column"].row > .column {
+ width: 100% !important;
+}
+.ui.grid > [class*="two column"].row > .column {
+ width: 50% !important;
+}
+.ui.grid > [class*="three column"].row > .column {
+ width: 33.33333333% !important;
+}
+.ui.grid > [class*="four column"].row > .column {
+ width: 25% !important;
+}
+.ui.grid > [class*="five column"].row > .column {
+ width: 20% !important;
+}
+.ui.grid > [class*="six column"].row > .column {
+ width: 16.66666667% !important;
+}
+.ui.grid > [class*="seven column"].row > .column {
+ width: 14.28571429% !important;
+}
+.ui.grid > [class*="eight column"].row > .column {
+ width: 12.5% !important;
+}
+.ui.grid > [class*="nine column"].row > .column {
+ width: 11.11111111% !important;
+}
+.ui.grid > [class*="ten column"].row > .column {
+ width: 10% !important;
+}
+.ui.grid > [class*="eleven column"].row > .column {
+ width: 9.09090909% !important;
+}
+.ui.grid > [class*="twelve column"].row > .column {
+ width: 8.33333333% !important;
+}
+.ui.grid > [class*="thirteen column"].row > .column {
+ width: 7.69230769% !important;
+}
+.ui.grid > [class*="fourteen column"].row > .column {
+ width: 7.14285714% !important;
+}
+.ui.grid > [class*="fifteen column"].row > .column {
+ width: 6.66666667% !important;
+}
+.ui.grid > [class*="sixteen column"].row > .column {
+ width: 6.25% !important;
+}
+
+.ui.grid > .row > [class*="one wide"].column,
+.ui.grid > .column.row > [class*="one wide"].column,
+.ui.grid > [class*="one wide"].column,
+.ui.column.grid > [class*="one wide"].column {
+ width: 6.25% !important;
+}
+.ui.grid > .row > [class*="two wide"].column,
+.ui.grid > .column.row > [class*="two wide"].column,
+.ui.grid > [class*="two wide"].column,
+.ui.column.grid > [class*="two wide"].column {
+ width: 12.5% !important;
+}
+.ui.grid > .row > [class*="three wide"].column,
+.ui.grid > .column.row > [class*="three wide"].column,
+.ui.grid > [class*="three wide"].column,
+.ui.column.grid > [class*="three wide"].column {
+ width: 18.75% !important;
+}
+.ui.grid > .row > [class*="four wide"].column,
+.ui.grid > .column.row > [class*="four wide"].column,
+.ui.grid > [class*="four wide"].column,
+.ui.column.grid > [class*="four wide"].column {
+ width: 25% !important;
+}
+.ui.grid > .row > [class*="five wide"].column,
+.ui.grid > .column.row > [class*="five wide"].column,
+.ui.grid > [class*="five wide"].column,
+.ui.column.grid > [class*="five wide"].column {
+ width: 31.25% !important;
+}
+.ui.grid > .row > [class*="six wide"].column,
+.ui.grid > .column.row > [class*="six wide"].column,
+.ui.grid > [class*="six wide"].column,
+.ui.column.grid > [class*="six wide"].column {
+ width: 37.5% !important;
+}
+.ui.grid > .row > [class*="seven wide"].column,
+.ui.grid > .column.row > [class*="seven wide"].column,
+.ui.grid > [class*="seven wide"].column,
+.ui.column.grid > [class*="seven wide"].column {
+ width: 43.75% !important;
+}
+.ui.grid > .row > [class*="eight wide"].column,
+.ui.grid > .column.row > [class*="eight wide"].column,
+.ui.grid > [class*="eight wide"].column,
+.ui.column.grid > [class*="eight wide"].column {
+ width: 50% !important;
+}
+.ui.grid > .row > [class*="nine wide"].column,
+.ui.grid > .column.row > [class*="nine wide"].column,
+.ui.grid > [class*="nine wide"].column,
+.ui.column.grid > [class*="nine wide"].column {
+ width: 56.25% !important;
+}
+.ui.grid > .row > [class*="ten wide"].column,
+.ui.grid > .column.row > [class*="ten wide"].column,
+.ui.grid > [class*="ten wide"].column,
+.ui.column.grid > [class*="ten wide"].column {
+ width: 62.5% !important;
+}
+.ui.grid > .row > [class*="eleven wide"].column,
+.ui.grid > .column.row > [class*="eleven wide"].column,
+.ui.grid > [class*="eleven wide"].column,
+.ui.column.grid > [class*="eleven wide"].column {
+ width: 68.75% !important;
+}
+.ui.grid > .row > [class*="twelve wide"].column,
+.ui.grid > .column.row > [class*="twelve wide"].column,
+.ui.grid > [class*="twelve wide"].column,
+.ui.column.grid > [class*="twelve wide"].column {
+ width: 75% !important;
+}
+.ui.grid > .row > [class*="thirteen wide"].column,
+.ui.grid > .column.row > [class*="thirteen wide"].column,
+.ui.grid > [class*="thirteen wide"].column,
+.ui.column.grid > [class*="thirteen wide"].column {
+ width: 81.25% !important;
+}
+.ui.grid > .row > [class*="fourteen wide"].column,
+.ui.grid > .column.row > [class*="fourteen wide"].column,
+.ui.grid > [class*="fourteen wide"].column,
+.ui.column.grid > [class*="fourteen wide"].column {
+ width: 87.5% !important;
+}
+.ui.grid > .row > [class*="fifteen wide"].column,
+.ui.grid > .column.row > [class*="fifteen wide"].column,
+.ui.grid > [class*="fifteen wide"].column,
+.ui.column.grid > [class*="fifteen wide"].column {
+ width: 93.75% !important;
+}
+.ui.grid > .row > [class*="sixteen wide"].column,
+.ui.grid > .column.row > [class*="sixteen wide"].column,
+.ui.grid > [class*="sixteen wide"].column,
+.ui.column.grid > [class*="sixteen wide"].column {
+ width: 100% !important;
+}
+
+.ui.centered.grid,
+.ui.centered.grid > .row,
+.ui.grid > .centered.row {
+ text-align: center;
+ justify-content: center;
+}
+.ui.centered.grid > .column:not(.aligned):not(.justified):not(.row),
+.ui.centered.grid > .row > .column:not(.aligned):not(.justified),
+.ui.grid .centered.row > .column:not(.aligned):not(.justified) {
+ text-align: left;
+}
+.ui.grid > .centered.column,
+.ui.grid > .row > .centered.column {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.ui.relaxed.grid > .column:not(.row),
+.ui.relaxed.grid > .row > .column,
+.ui.grid > .relaxed.row > .column {
+ padding-left: 1.5rem;
+ padding-right: 1.5rem;
+}
+.ui[class*="very relaxed"].grid > .column:not(.row),
+.ui[class*="very relaxed"].grid > .row > .column,
+.ui.grid > [class*="very relaxed"].row > .column {
+ padding-left: 2.5rem;
+ padding-right: 2.5rem;
+}
+
+.ui.relaxed.grid .row + .ui.divider,
+.ui.grid .relaxed.row + .ui.divider {
+ margin-left: 1.5rem;
+ margin-right: 1.5rem;
+}
+.ui[class*="very relaxed"].grid .row + .ui.divider,
+.ui.grid [class*="very relaxed"].row + .ui.divider {
+ margin-left: 2.5rem;
+ margin-right: 2.5rem;
+}
+
+.ui[class*="middle aligned"].grid > .column:not(.row),
+.ui[class*="middle aligned"].grid > .row > .column,
+.ui.grid > [class*="middle aligned"].row > .column,
+.ui.grid > [class*="middle aligned"].column:not(.row),
+.ui.grid > .row > [class*="middle aligned"].column {
+ flex-direction: column;
+ vertical-align: middle;
+ align-self: center !important;
+}
+
+.ui[class*="left aligned"].grid > .column,
+.ui[class*="left aligned"].grid > .row > .column,
+.ui.grid > [class*="left aligned"].row > .column,
+.ui.grid > [class*="left aligned"].column.column,
+.ui.grid > .row > [class*="left aligned"].column.column {
+ text-align: left;
+ align-self: inherit;
+}
+
+.ui[class*="center aligned"].grid > .column,
+.ui[class*="center aligned"].grid > .row > .column,
+.ui.grid > [class*="center aligned"].row > .column,
+.ui.grid > [class*="center aligned"].column.column,
+.ui.grid > .row > [class*="center aligned"].column.column {
+ text-align: center;
+ align-self: inherit;
+}
+.ui[class*="center aligned"].grid {
+ justify-content: center;
+}
+
+.ui[class*="right aligned"].grid > .column,
+.ui[class*="right aligned"].grid > .row > .column,
+.ui.grid > [class*="right aligned"].row > .column,
+.ui.grid > [class*="right aligned"].column.column,
+.ui.grid > .row > [class*="right aligned"].column.column {
+ text-align: right;
+ align-self: inherit;
+}
+
+.ui[class*="equal width"].grid > .column:not(.row),
+.ui[class*="equal width"].grid > .row > .column,
+.ui.grid > [class*="equal width"].row > .column {
+ display: inline-block;
+ flex-grow: 1;
+}
+.ui[class*="equal width"].grid > .wide.column,
+.ui[class*="equal width"].grid > .row > .wide.column,
+.ui.grid > [class*="equal width"].row > .wide.column {
+ flex-grow: 0;
+}
+
+@media only screen and (max-width: 767.98px) {
+ .ui[class*="mobile reversed"].grid,
+ .ui[class*="mobile reversed"].grid > .row,
+ .ui.grid > [class*="mobile reversed"].row {
+ flex-direction: row-reverse;
+ }
+ .ui.stackable[class*="mobile reversed"] {
+ flex-direction: column-reverse;
+ }
+}
+
+@media only screen and (max-width: 767.98px) {
+ .ui.stackable.grid {
+ width: auto;
+ margin-left: 0 !important;
+ margin-right: 0 !important;
+ }
+ .ui.stackable.grid > .row > .wide.column,
+ .ui.stackable.grid > .wide.column,
+ .ui.stackable.grid > .column.grid > .column,
+ .ui.stackable.grid > .column.row > .column,
+ .ui.stackable.grid > .row > .column,
+ .ui.stackable.grid > .column:not(.row),
+ .ui.grid > .stackable.stackable.stackable.row > .column {
+ width: 100% !important;
+ margin: 0 !important;
+ box-shadow: none !important;
+ padding: 1rem;
+ }
+ .ui.stackable.grid:not(.vertically) > .row {
+ margin: 0;
+ padding: 0;
+ }
+
+ .ui.container > .ui.stackable.grid > .column,
+ .ui.container > .ui.stackable.grid > .row > .column {
+ padding-left: 0 !important;
+ padding-right: 0 !important;
+ }
+
+ .ui.grid .ui.stackable.grid,
+ .ui.segment:not(.vertical) .ui.stackable.page.grid {
+ margin-left: -1rem !important;
+ margin-right: -1rem !important;
+ }
+}
+
+.ui.ui.ui.compact.grid > .column:not(.row),
+.ui.ui.ui.compact.grid > .row > .column {
+ padding-left: 0.5rem;
+ padding-right: 0.5rem;
+}
+.ui.ui.ui.compact.grid > * {
+ padding-left: 0.5rem;
+ padding-right: 0.5rem;
+}
+
+.ui.ui.ui.compact.grid > .row {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+}
+
+.ui.ui.ui.compact.grid > .column:not(.row) {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+}
diff --git a/web_src/css/modules/header.css b/web_src/css/modules/header.css
new file mode 100644
index 00000000..9cec5fcb
--- /dev/null
+++ b/web_src/css/modules/header.css
@@ -0,0 +1,176 @@
+/* based on Fomantic UI header module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.header {
+ color: var(--color-text);
+ border: none;
+ margin: calc(2rem - 0.1428571428571429em) 0 1rem;
+ padding: 0;
+ font-family: var(--fonts-regular);
+ font-weight: var(--font-weight-medium);
+ line-height: 1.28571429;
+}
+
+.ui.header:first-child {
+ margin-top: -0.14285714em;
+}
+
+.ui.header:last-child {
+ margin-bottom: 0;
+}
+
+.ui.header .ui.label {
+ margin-left: 0.25rem;
+ vertical-align: middle;
+}
+
+.ui.header > .ui.label.compact {
+ margin-top: inherit;
+}
+
+.ui.header .sub.header {
+ display: block;
+ font-weight: var(--font-weight-normal);
+ padding: 0;
+ margin: 0;
+ font-size: 1rem;
+ line-height: 1.2;
+ color: var(--color-text-light-1);
+}
+
+.ui.header > i.icon {
+ display: table-cell;
+ opacity: 1;
+ font-size: 1.5em;
+ padding-top: 0;
+ vertical-align: middle;
+}
+
+.ui.header > i.icon:only-child {
+ display: inline-block;
+ padding: 0;
+ margin-right: 0.75rem;
+}
+
+.ui.header + p {
+ margin-top: 0;
+}
+
+h2.ui.header {
+ font-size: 1.71428571rem;
+}
+h2.ui.header .sub.header {
+ font-size: 1.14285714rem;
+}
+
+h4.ui.header {
+ font-size: 1.07142857rem;
+}
+h4.ui.header .sub.header {
+ font-size: 1rem;
+}
+
+.ui.sub.header {
+ padding: 0;
+ margin-bottom: 0.14285714rem;
+ font-weight: var(--font-weight-normal);
+ font-size: 0.85714286em;
+}
+
+.ui.icon.header svg {
+ width: 3em;
+ height: 3em;
+ float: none;
+ display: block;
+ line-height: var(--line-height-default);
+ padding: 0;
+ margin: 0 auto 0.5rem;
+ opacity: 1;
+}
+
+.ui.header:not(h1,h2,h3,h4,h5,h6) {
+ font-size: 1.28571429em;
+}
+
+.ui.attached.header {
+ position: relative;
+ background: var(--color-box-header);
+ padding: 0.78571429rem 1rem;
+ margin: 0 -1px;
+ border-radius: 0;
+ border: 1px solid var(--color-secondary);
+}
+
+.ui.attached:not(.top).header {
+ border-top: none;
+}
+
+.ui.top.attached.header {
+ border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+
+.ui.bottom.attached.header {
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
+.ui.attached.header:not(h1,h2,h3,h4,h5,h6) {
+ font-size: 1em;
+}
+
+/* fix misaligned right buttons on box headers */
+.ui.attached.header > .ui.right {
+ position: absolute;
+ right: 0.78571429rem;
+ top: 0;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+ gap: 0.25em;
+}
+
+/* the default ".ui.attached.header > .ui.right" is only able to contain "tiny" buttons, other buttons are too large */
+.ui.attached.header > .ui.right .ui.tiny.button {
+ padding: 6px 10px;
+ font-weight: var(--font-weight-normal);
+}
+
+/* open dropdown menus to the left in right-attached headers */
+.ui.attached.header > .ui.right .ui.dropdown .menu {
+ right: 0;
+ left: auto;
+}
+
+/* if a .top.attached.header is followed by a .segment, add some margin */
+.ui.segments + .ui.top.attached.header,
+.ui.attached.segment + .ui.top.attached.header {
+ margin-top: 1rem;
+}
+
+.ui.dividing.header {
+ border-bottom-color: var(--color-secondary);
+}
+
+.ui.dividing.header .sub.header {
+ padding-bottom: 0.21428571rem;
+}
+
+.ui.dividing.header i.icon {
+ margin-bottom: 0;
+}
+
+.ui.error.header {
+ background: var(--color-error-bg) !important;
+ color: var(--color-error-text) !important;
+ border-color: var(--color-error-border) !important;
+}
+
+.ui.warning.header {
+ background: var(--color-warning-bg) !important;
+ color: var(--color-warning-text) !important;
+ border-color: var(--color-warning-border) !important;
+}
+
+.attention-header {
+ padding: 0.5em 0.75em !important;
+ color: var(--color-text) !important;
+}
diff --git a/web_src/css/modules/input.css b/web_src/css/modules/input.css
new file mode 100644
index 00000000..18b785ac
--- /dev/null
+++ b/web_src/css/modules/input.css
@@ -0,0 +1,197 @@
+/* based on Fomantic UI input module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.input {
+ position: relative;
+ font-weight: var(--font-weight-normal);
+ display: inline-flex;
+ color: var(--color-input-text);
+}
+.ui.input > input {
+ margin: 0;
+ max-width: 100%;
+ flex: 1 0 auto;
+ outline: none;
+ font-family: var(--fonts-regular);
+ padding: 0.67857143em 1em;
+ border: 1px solid var(--color-input-border);
+ color: var(--color-input-text);
+ border-radius: 0.28571429rem;
+ line-height: var(--line-height-default);
+ text-align: start;
+}
+
+.ui.disabled.input,
+.ui.input:not(.disabled) input[disabled] {
+ opacity: var(--opacity-disabled);
+}
+.ui.disabled.input > input,
+.ui.input:not(.disabled) input[disabled] {
+ pointer-events: none;
+}
+
+.ui.input.focus > input,
+.ui.input > input:focus {
+ border-color: var(--color-primary);
+}
+
+.ui.input.error > input {
+ background: var(--color-error-bg);
+ border-color: var(--color-error-border);
+ color: var(--color-error-text);
+}
+
+.ui.icon.input > i.icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: default;
+ position: absolute;
+ text-align: center;
+ top: 0;
+ right: 0;
+ margin: 0;
+ height: 100%;
+ width: 2.67142857em;
+ opacity: 0.5;
+ border-radius: 0 0.28571429rem 0.28571429rem 0;
+ pointer-events: none;
+ padding: 4px;
+}
+
+.ui.icon.input > i.icon.is-loading {
+ position: absolute !important;
+ height: 28px;
+ top: 4px;
+}
+
+.ui.icon.input > i.icon.is-loading > * {
+ visibility: hidden;
+}
+
+.ui.ui.ui.ui.icon.input > textarea,
+.ui.ui.ui.ui.icon.input > input {
+ padding-right: 2.67142857em;
+}
+.ui.icon.input > i.link.icon {
+ cursor: pointer;
+}
+.ui.icon.input > i.circular.icon {
+ top: 0.35em;
+ right: 0.5em;
+}
+
+.ui[class*="left icon"].input > i.icon {
+ right: auto;
+ left: 1px;
+ border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui[class*="left icon"].input > i.circular.icon {
+ right: auto;
+ left: 0.5em;
+}
+.ui.ui.ui.ui[class*="left icon"].input > textarea,
+.ui.ui.ui.ui[class*="left icon"].input > input {
+ padding-left: 2.67142857em;
+ padding-right: 1em;
+}
+
+.ui.icon.input > textarea:focus ~ .icon,
+.ui.icon.input > input:focus ~ .icon {
+ opacity: 1;
+}
+
+.ui.icon.input > textarea ~ i.icon {
+ height: 3em;
+}
+
+.ui.form .field.error > .ui.action.input > .ui.button,
+.ui.action.input.error > .ui.button {
+ border-top: 1px solid var(--color-error-border);
+ border-bottom: 1px solid var(--color-error-border);
+}
+
+.ui.action.input > .button,
+.ui.action.input > .buttons {
+ display: flex;
+ align-items: center;
+ flex: 0 0 auto;
+}
+.ui.action.input > .button,
+.ui.action.input > .buttons > .button {
+ padding-top: 0.78571429em;
+ padding-bottom: 0.78571429em;
+ margin: 0;
+}
+
+.ui.action.input:not([class*="left action"]) > input {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-right-color: transparent;
+}
+
+.ui.action.input > .dropdown:first-child,
+.ui.action.input > .button:first-child,
+.ui.action.input > .buttons:first-child > .button {
+ border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui.action.input > .dropdown:not(:first-child),
+.ui.action.input > .button:not(:first-child),
+.ui.action.input > .buttons:not(:first-child) > .button {
+ border-radius: 0;
+}
+.ui.action.input > .dropdown:last-child,
+.ui.action.input > .button:last-child,
+.ui.action.input > .buttons:last-child > .button {
+ border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+
+.ui.fluid.input {
+ display: flex;
+}
+.ui.fluid.input > input {
+ width: 0 !important;
+}
+
+.ui.tiny.input {
+ font-size: 0.85714286em;
+}
+.ui.small.input {
+ font-size: 0.92857143em;
+}
+
+.ui.action.input .ui.ui.button {
+ border-color: var(--color-input-border);
+ padding-top: 0; /* the ".action.input" is "flex + stretch", so let the buttons layout themselves */
+ padding-bottom: 0;
+}
+
+/* currently used for search bar dropdowns in repo search and explore code */
+.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection {
+ min-width: 10em;
+}
+.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(:focus) {
+ border-right: none;
+}
+.ui.action.input:not([class*="left action"]) > .ui.dropdown.selection:not(.active):hover {
+ border-color: var(--color-input-border);
+}
+.ui.action.input:not([class*="left action"]) .ui.dropdown.selection.upward.visible {
+ border-bottom-left-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+}
+.ui.action.input:not([class*="left action"]) > input,
+.ui.action.input:not([class*="left action"]) > input:hover {
+ border-right: none;
+}
+.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection,
+.ui.action.input:not([class*="left action"]) > input:focus + .ui.dropdown.selection:hover,
+.ui.action.input:not([class*="left action"]) > input:focus + .button,
+.ui.action.input:not([class*="left action"]) > input:focus + .button:hover,
+.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button,
+.ui.action.input:not([class*="left action"]) > input:focus + .icon + .button:hover {
+ border-left-color: var(--color-primary);
+}
+.ui.action.input:not([class*="left action"]) > input:focus {
+ border-right-color: var(--color-primary);
+}
diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css
new file mode 100644
index 00000000..bc30baaf
--- /dev/null
+++ b/web_src/css/modules/label.css
@@ -0,0 +1,303 @@
+/* based on Fomantic UI label module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.label {
+ display: inline-flex;
+ align-items: center;
+ gap: .25rem;
+ min-width: 0;
+ vertical-align: middle;
+ line-height: 1;
+ background: var(--color-label-bg);
+ color: var(--color-label-text);
+ padding: 0.3em 0.5em;
+ font-size: 0.85714286rem;
+ font-weight: var(--font-weight-medium);
+ border: 0 solid transparent;
+ border-radius: 0.28571429rem;
+ white-space: nowrap;
+}
+
+.ui.label:first-child {
+ margin-left: 0;
+}
+.ui.label:last-child {
+ margin-right: 0;
+}
+
+a.ui.label {
+ cursor: pointer;
+}
+
+.ui.label > a {
+ cursor: pointer;
+ color: inherit;
+ opacity: 0.75;
+}
+.ui.label > a:hover {
+ opacity: 1;
+}
+
+.ui.label > img {
+ width: auto;
+ vertical-align: middle;
+ height: 2.1666em;
+}
+
+.ui.label > .color-icon {
+ margin-left: 0;
+}
+
+.ui.label > .icon {
+ width: auto;
+ margin: 0 0.75em 0 0;
+}
+
+.ui.label > .detail {
+ display: inline-block;
+ vertical-align: top;
+ font-weight: var(--font-weight-medium);
+ margin-left: 1em;
+ opacity: 0.8;
+}
+.ui.label > .detail .icon {
+ margin: 0 0.25em 0 0;
+}
+
+.ui.label > .close.icon,
+.ui.label > .delete.icon {
+ cursor: pointer;
+ font-size: 0.92857143em;
+ opacity: 0.5;
+}
+.ui.label > .close.icon:hover,
+.ui.label > .delete.icon:hover {
+ opacity: 1;
+}
+
+.ui.label.left.icon > .close.icon,
+.ui.label.left.icon > .delete.icon {
+ margin: 0 0.5em 0 0;
+}
+.ui.label:not(.icon) > .close.icon,
+.ui.label:not(.icon) > .delete.icon {
+ margin: 0 0 0 0.5em;
+}
+
+.ui.header > .ui.label {
+ margin-top: -0.29165em;
+}
+
+a.ui.label:hover {
+ background: var(--color-label-hover-bg);
+ border-color: var(--color-label-hover-bg);
+ color: var(--color-label-text);
+}
+
+.ui.label.visible:not(.dropdown) {
+ display: inline-block !important;
+}
+
+.ugc-labels .item {
+ text-overflow: unset !important;
+}
+
+.ugc-labels .item .ui.label {
+ text-wrap: auto;
+ overflow-wrap: anywhere;
+}
+
+.ui.basic.label {
+ background: var(--color-button);
+ border: 1px solid var(--color-light-border);
+ color: var(--color-text-light);
+ padding: calc(0.5833em - 1px) calc(0.833em - 1px);
+}
+a.ui.basic.label:hover {
+ text-decoration: none;
+ color: var(--color-text);
+ border-color: var(--color-light-border);
+ background: var(--color-hover);
+}
+
+.ui.ui.ui.primary.label {
+ background: var(--color-primary);
+ border-color: var(--color-primary-dark-2);
+ color: var(--color-primary-contrast);
+}
+a.ui.ui.ui.primary.label:hover {
+ background: var(--color-primary-dark-3);
+ border-color: var(--color-primary-dark-3);
+ color: var(--color-primary-contrast);
+}
+.ui.ui.ui.basic.primary.label {
+ background: transparent;
+ border-color: var(--color-primary);
+ color: var(--color-primary);
+}
+a.ui.ui.ui.basic.primary.label:hover {
+ background: var(--color-hover);
+ border-color: var(--color-primary-dark-1);
+ color: var(--color-primary-dark-1);
+}
+
+.ui.ui.ui.red.label {
+ background: var(--color-red);
+ border-color: var(--color-red);
+ color: var(--color-white);
+}
+a.ui.ui.ui.red.label:hover {
+ background: var(--color-red-dark-1);
+ border-color: var(--color-red-dark-1);
+ color: var(--color-white);
+}
+.ui.ui.ui.basic.red.label {
+ background: transparent;
+ border-color: var(--color-red);
+ color: var(--color-red);
+}
+a.ui.ui.ui.basic.red.label:hover {
+ background: transparent;
+ border-color: var(--color-red-dark-1);
+ color: var(--color-red-dark-1);
+}
+
+.ui.ui.ui.orange.label {
+ background: var(--color-orange);
+ border-color: var(--color-orange);
+ color: var(--color-white);
+}
+a.ui.ui.ui.orange.label:hover {
+ background: var(--color-orange-dark-1);
+ border-color: var(--color-orange-dark-1);
+ color: var(--color-white);
+}
+.ui.ui.ui.basic.orange.label {
+ background: transparent;
+ border-color: var(--color-orange);
+ color: var(--color-orange);
+}
+a.ui.ui.ui.basic.orange.label:hover {
+ background: transparent;
+ border-color: var(--color-orange-dark-1);
+ color: var(--color-orange-dark-1);
+}
+
+.ui.ui.ui.yellow.label {
+ background: var(--color-yellow);
+ border-color: var(--color-yellow);
+ color: var(--color-white);
+}
+a.ui.ui.ui.yellow.label:hover {
+ background: var(--color-yellow-dark-1);
+ border-color: var(--color-yellow-dark-1);
+ color: var(--color-white);
+}
+.ui.ui.ui.basic.yellow.label {
+ background: transparent;
+ border-color: var(--color-yellow);
+ color: var(--color-yellow);
+}
+a.ui.ui.ui.basic.yellow.label:hover {
+ background: transparent;
+ border-color: var(--color-yellow-dark-1);
+ color: var(--color-yellow-dark-1);
+}
+.ui.ui.ui.olive.label {
+ background: var(--color-olive);
+ border-color: var(--color-olive);
+ color: var(--color-white);
+}
+
+.ui.ui.ui.green.label {
+ background: var(--color-green);
+ border-color: var(--color-green);
+ color: var(--color-white);
+}
+a.ui.ui.ui.green.label:hover {
+ background: var(--color-green-dark-1);
+ border-color: var(--color-green-dark-1);
+ color: var(--color-white);
+}
+.ui.ui.ui.basic.green.label {
+ background: transparent;
+ border-color: var(--color-green);
+ color: var(--color-green);
+}
+a.ui.ui.ui.basic.green.label:hover {
+ background: transparent;
+ border-color: var(--color-green-dark-1);
+ color: var(--color-green-dark-1);
+}
+
+.ui.ui.ui.purple.label {
+ background: var(--color-purple);
+ border-color: var(--color-purple);
+ color: var(--color-white);
+}
+a.ui.ui.ui.purple.label:hover {
+ background: var(--color-purple-dark-1);
+ border-color: var(--color-purple-dark-1);
+ color: var(--color-white);
+}
+.ui.ui.ui.basic.purple.label {
+ background: transparent;
+ border-color: var(--color-purple);
+ color: var(--color-purple);
+}
+a.ui.ui.ui.basic.purple.label:hover {
+ background: transparent;
+ border-color: var(--color-purple-dark-1);
+ color: var(--color-purple-dark-1);
+}
+
+.ui.ui.ui.grey.label {
+ background: var(--color-label-bg);
+ border-color: var(--color-label-bg);
+ color: var(--color-label-text);
+}
+a.ui.ui.ui.grey.label:hover {
+ background: var(--color-label-hover-bg);
+ border-color: var(--color-label-hover-bg);
+ color: var(--color-white);
+}
+.ui.ui.ui.basic.grey.label {
+ background: transparent;
+ border-color: var(--color-label-bg);
+ color: var(--color-label-text);
+}
+a.ui.ui.ui.basic.grey.label:hover {
+ background: transparent;
+ border-color: var(--color-label-hover-bg);
+ color: var(--color-label-hover-bg);
+}
+
+.ui.horizontal.label {
+ margin: 0 0.5em 0 0;
+ padding: 0.4em 0.833em;
+ min-width: 3em;
+ text-align: center;
+}
+
+.ui.circular.label {
+ min-width: 2em;
+ min-height: 2em;
+ padding: 0.5em !important;
+ line-height: 1;
+ text-align: center;
+ border-radius: 500rem;
+ justify-content: center;
+}
+
+.ui.mini.label {
+ font-size: 0.64285714rem;
+}
+.ui.tiny.label {
+ font-size: 0.71428571rem;
+}
+.ui.small.label {
+ font-size: 0.78571429rem;
+}
+.ui.large.label {
+ font-size: 1rem;
+}
diff --git a/web_src/css/modules/list.css b/web_src/css/modules/list.css
new file mode 100644
index 00000000..32c71e80
--- /dev/null
+++ b/web_src/css/modules/list.css
@@ -0,0 +1,193 @@
+/* based on Fomantic UI list module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.list {
+ list-style-type: none;
+ margin: 1em 0;
+ padding: 0;
+ font-size: 1em;
+}
+
+.ui.list:first-child {
+ margin-top: 0;
+ padding-top: 0;
+}
+
+.ui.list:last-child {
+ margin-bottom: 0;
+ padding-bottom: 0;
+}
+
+.ui.list > .item,
+.ui.list .list > .item {
+ display: list-item;
+ table-layout: fixed;
+ list-style-type: none;
+ list-style-position: outside;
+}
+
+.ui.list > .list > .item::after,
+.ui.list > .item::after {
+ content: "";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+
+.ui.list .list:not(.icon) {
+ clear: both;
+ margin: 0;
+ padding: 0.75em 0 0.25em 0.5em;
+}
+
+.ui.list .list > .item {
+ padding: 0.14285714em 0;
+}
+
+.ui.list .list > .item > i.icon,
+.ui.list > .item > i.icon {
+ display: table-cell;
+ min-width: 1.55em;
+ padding-top: 0;
+ transition: color 0.1s ease;
+ padding-right: 0.28571429em;
+ vertical-align: top;
+}
+.ui.list .list > .item > i.icon:only-child,
+.ui.list > .item > i.icon:only-child {
+ display: inline-block;
+ min-width: auto;
+ vertical-align: top;
+}
+
+.ui.list .list > .item > .image,
+.ui.list > .item > .image {
+ display: table-cell;
+ background-color: transparent;
+ vertical-align: top;
+}
+.ui.list .list > .item > .image:not(:only-child):not(img),
+.ui.list > .item > .image:not(:only-child):not(img) {
+ padding-right: 0.5em;
+}
+.ui.list .list > .item > .image img,
+.ui.list > .item > .image img {
+ vertical-align: top;
+}
+.ui.list .list > .item > img.image,
+.ui.list .list > .item > .image:only-child,
+.ui.list > .item > img.image,
+.ui.list > .item > .image:only-child {
+ display: inline-block;
+}
+
+.ui.list .list > .item > .content,
+.ui.list > .item > .content {
+ color: var(--color-text);
+}
+.ui.list .list > .item > .image + .content,
+.ui.list .list > .item > i.icon + .content,
+.ui.list > .item > .image + .content,
+.ui.list > .item > i.icon + .content {
+ display: table-cell;
+ width: 100%;
+ padding: 0 0 0 0.5em;
+ vertical-align: top;
+}
+.ui.list .list > .item > img.image + .content,
+.ui.list > .item > img.image + .content {
+ display: inline-block;
+ width: auto;
+}
+.ui.list .list > .item > .content > .list,
+.ui.list > .item > .content > .list {
+ margin-left: 0;
+ padding-left: 0;
+}
+
+.ui.list .list > .item .header,
+.ui.list > .item .header {
+ display: block;
+ margin: 0;
+ font-family: var(--fonts-regular);
+ font-weight: var(--font-weight-medium);
+ color: var(--color-text-dark);
+}
+
+.ui.list .list > .item .description,
+.ui.list > .item .description {
+ display: block;
+ color: var(--color-text);
+}
+
+.ui.list > .item a,
+.ui.list .list > .item a {
+ cursor: pointer;
+}
+
+.ui.list .list > .item [class*="right floated"],
+.ui.list > .item [class*="right floated"] {
+ float: right;
+ margin: 0 0 0 1em;
+}
+
+.ui.menu .ui.list > .item,
+.ui.menu .ui.list .list > .item {
+ display: list-item;
+ table-layout: fixed;
+ background-color: transparent;
+ list-style-type: none;
+ list-style-position: outside;
+ padding: 0.21428571em 0;
+}
+.ui.menu .ui.list .list > .item::before,
+.ui.menu .ui.list > .item::before {
+ border: none;
+ background: none;
+}
+.ui.menu .ui.list .list > .item:first-child,
+.ui.menu .ui.list > .item:first-child {
+ padding-top: 0;
+}
+.ui.menu .ui.list .list > .item:last-child,
+.ui.menu .ui.list > .item:last-child {
+ padding-bottom: 0;
+}
+
+.ui.list .list > .disabled.item,
+.ui.list > .disabled.item {
+ pointer-events: none;
+ opacity: var(--opacity-disabled);
+}
+
+.ui.list .list > a.item:hover > .icons,
+.ui.list > a.item:hover > .icons,
+.ui.list .list > a.item:hover > i.icon,
+.ui.list > a.item:hover > i.icon {
+ color: var(--color-text-dark);
+}
+
+.ui.divided.list > .item {
+ border-top: 1px solid var(--color-secondary);
+}
+.ui.divided.list .list > .item {
+ border-top: none;
+}
+.ui.divided.list .item .list > .item {
+ border-top: none;
+}
+.ui.divided.list .list > .item:first-child,
+.ui.divided.list > .item:first-child {
+ border-top: none;
+}
+.ui.divided.list .list > .item:first-child {
+ border-top-width: 1px;
+}
+
+.ui.relaxed.list > .item:not(:first-child) {
+ padding-top: 0.42857143em;
+}
+.ui.relaxed.list > .item:not(:last-child) {
+ padding-bottom: 0.42857143em;
+}
diff --git a/web_src/css/modules/message.css b/web_src/css/modules/message.css
new file mode 100644
index 00000000..c62dbddd
--- /dev/null
+++ b/web_src/css/modules/message.css
@@ -0,0 +1,114 @@
+/* based on Fomantic UI message module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.message {
+ background: var(--color-box-body);
+ color: var(--color-text);
+ border: 1px solid var(--color-secondary);
+ position: relative;
+ min-height: 1em;
+ margin: 1em 0;
+ padding: 1em 1.5em;
+ border-radius: var(--border-radius);
+}
+
+.ui.message:first-child {
+ margin-top: 0;
+}
+
+.ui.message:last-child {
+ margin-bottom: 0;
+}
+
+.ui.attached.message {
+ margin-bottom: -1px;
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+ margin-left: -1px;
+ margin-right: -1px;
+}
+
+.ui.attached + .ui.attached.message:not(.top):not(.bottom) {
+ margin-top: -1px;
+ border-radius: 0;
+}
+
+.ui.bottom.attached.message {
+ margin-top: -1px;
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+}
+
+.ui.bottom.attached.message:not(:last-child) {
+ margin-bottom: 1em;
+}
+
+.ui.info.message .header,
+.ui.blue.message .header {
+ color: var(--color-blue);
+}
+
+.ui.info.message,
+.ui.attached.info.message,
+.ui.blue.message,
+.ui.attached.blue.message {
+ background: var(--color-info-bg);
+ color: var(--color-info-text);
+ border-color: var(--color-info-border);
+}
+
+.ui.success.message .header,
+.ui.positive.message .header,
+.ui.green.message .header {
+ color: var(--color-green);
+}
+
+.ui.success.message,
+.ui.attached.success.message,
+.ui.positive.message,
+.ui.attached.positive.message {
+ background: var(--color-success-bg);
+ color: var(--color-success-text);
+ border-color: var(--color-success-border);
+}
+
+.ui.error.message .header,
+.ui.negative.message .header,
+.ui.red.message .header {
+ color: var(--color-red);
+}
+
+.ui.error.message,
+.ui.attached.error.message,
+.ui.red.message,
+.ui.attached.red.message,
+.ui.negative.message,
+.ui.attached.negative.message {
+ background: var(--color-error-bg);
+ color: var(--color-error-text);
+ border-color: var(--color-error-border);
+}
+
+.ui.warning.message .header,
+.ui.yellow.message .header {
+ color: var(--color-yellow);
+}
+
+.ui.warning.message,
+.ui.attached.warning.message,
+.ui.yellow.message,
+.ui.attached.yellow.message {
+ background: var(--color-warning-bg);
+ color: var(--color-warning-text);
+ border-color: var(--color-warning-border);
+}
+
+.ui.message > .close.icon {
+ cursor: pointer;
+ position: absolute;
+ top: 9px;
+ right: 9px;
+ opacity: .7;
+}
+
+.ui.message > .close.icon:hover {
+ opacity: 1;
+}
diff --git a/web_src/css/modules/modal.css b/web_src/css/modules/modal.css
new file mode 100644
index 00000000..54a4ef81
--- /dev/null
+++ b/web_src/css/modules/modal.css
@@ -0,0 +1,86 @@
+.ui.modal.g-modal-confirm {
+ max-width: min(800px, 90vw);
+ width: fit-content;
+}
+
+.ui.modal.g-modal-confirm > .inside.close.icon {
+ padding: 0;
+ width: 1em;
+ height: 1em;
+ top: 1.2em;
+}
+
+.ui.modal > .close.icon[height="16"] {
+ top: 0.7em; /* fomantic uses absolute layout, so if we have special icon size, it needs this trick to align vertically */
+ color: var(--color-text-dark);
+}
+
+.ui.modal > .header {
+ /* can't use display:flex, because some headers have space-separated elements, eg: delete branch modal */
+ color: var(--color-text-dark);
+ background: var(--color-body);
+ border-color: var(--color-secondary);
+ border-top-left-radius: var(--border-radius);
+ border-top-right-radius: var(--border-radius);
+ vertical-align: middle;
+}
+
+.ui.modal > .header .svg {
+ vertical-align: middle;
+ display: inline-block;
+}
+
+.ui.modal {
+ background: var(--color-body);
+ box-shadow: 1px 3px 3px 0 var(--color-shadow), 1px 3px 15px 2px var(--color-shadow);
+}
+
+/* Gitea sometimes use a form in a modal dialog, then the "positive" button could submit the form directly
+Fomantic UI only supports the layout: <div .modal><div .content/><div .actions/></div>
+However, Gitea uses the following layouts:
+* <div .modal><div .content><div .actions/></div></div>
+* <div .modal><form><div .content/><div .actions/></form></div>
+* <div .modal><div .content><form><div .actions/></form></div></div>
+* <div .modal><div .content></div><form><div .actions/></form></div>
+* ...
+These inconsistent layouts should be refactored to simple ones.
+*/
+
+.ui.modal > .content,
+.ui.modal form > .content {
+ padding: 1.5em;
+ background: var(--color-body);
+}
+
+.ui.modal > .actions,
+.ui.modal .content + .actions,
+.ui.modal .content + form > .actions {
+ background: var(--color-secondary-bg);
+ border-color: var(--color-secondary);
+ padding: 1rem;
+ text-align: right;
+}
+
+.ui.modal .content > .actions {
+ padding-top: 1em; /* if the "actions" is in the "content", some paddings are already added by the "content" */
+ text-align: right;
+}
+
+/* positive/negative action buttons */
+.ui.modal .actions > .ui.button {
+ display: inline-flex;
+ align-items: center;
+ padding: 10px 12px 10px 10px;
+ margin-right: 0;
+}
+
+.ui.modal .actions > .ui.button.danger {
+ display: block;
+ width: 100%;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.ui.modal .actions > .ui.button .svg {
+ margin-right: 5px;
+}
diff --git a/web_src/css/modules/navbar.css b/web_src/css/modules/navbar.css
new file mode 100644
index 00000000..02d470f5
--- /dev/null
+++ b/web_src/css/modules/navbar.css
@@ -0,0 +1,147 @@
+#navbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: var(--color-nav-bg);
+ border-bottom: 1px solid var(--color-secondary);
+ margin: 0 !important;
+ padding: 0 10px;
+}
+
+#navbar,
+#navbar .navbar-left,
+#navbar .navbar-right {
+ min-height: 49px; /* +1px border-bottom */
+}
+
+#navbar .navbar-left,
+#navbar .navbar-right {
+ margin: 0;
+ display: flex;
+ align-items: center;
+}
+
+#navbar-logo {
+ margin: 0;
+}
+
+#navbar .item {
+ min-height: 36px;
+ min-width: 36px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ display: flex;
+}
+
+#navbar > .menu > .item {
+ color: var(--color-nav-text);
+}
+
+#navbar .dropdown .item {
+ justify-content: stretch;
+}
+
+#navbar a.item:hover, #navbar a.item:focus,
+#navbar button.item:hover, #navbar button.item:focus {
+ background: var(--color-nav-hover-bg);
+}
+
+#navbar .secondary.menu > .item > .svg,
+#navbar .right.menu > .item > .svg {
+ margin-right: 0;
+}
+
+@media (max-width: 767.98px) {
+ #navbar {
+ align-items: stretch;
+ }
+ /* hide all items */
+ #navbar .item {
+ display: none;
+ }
+ #navbar #navbar-logo {
+ display: flex;
+ }
+ /* show the first navbar item (logo and its mobile right items) */
+ #navbar .navbar-left {
+ flex: 1;
+ display: flex;
+ justify-content: space-between;
+ }
+ #navbar .navbar-mobile-right {
+ display: flex;
+ margin-left: auto !important;
+ width: auto !important;
+ }
+ #navbar .navbar-mobile-right > .item {
+ display: flex;
+ width: auto !important;
+ }
+ /* show items if the navbar is open */
+ #navbar.navbar-menu-open {
+ padding-bottom: 8px;
+ }
+ #navbar.navbar-menu-open,
+ #navbar.navbar-menu-open .navbar-right {
+ flex-direction: column;
+ }
+ #navbar.navbar-menu-open .navbar-left {
+ display: flex;
+ flex-wrap: wrap;
+ }
+ #navbar.navbar-menu-open .item {
+ display: flex;
+ width: 100%;
+ margin: 0;
+ }
+ #navbar.navbar-menu-open .navbar-left #navbar-logo {
+ justify-content: flex-start;
+ width: auto;
+ }
+ #navbar.navbar-menu-open .navbar-left .navbar-mobile-right {
+ justify-content: flex-end;
+ width: 50%;
+ min-height: 48px;
+ }
+ #navbar #mobile-notifications-icon {
+ margin-right: 6px !important;
+ }
+}
+
+#navbar a.item .notification_count {
+ color: var(--color-nav-bg);
+ padding: 0 3.75px;
+ font-size: 12px;
+ line-height: 12px;
+ font-weight: var(--font-weight-bold);
+}
+
+#navbar a.item:hover .notification_count,
+#navbar a.item:hover .header-stopwatch-dot {
+ border-color: var(--color-nav-hover-bg);
+}
+
+#navbar a.item .notification_count,
+#navbar a.item .header-stopwatch-dot {
+ background: var(--color-primary);
+ border: 2px solid var(--color-nav-bg);
+ position: absolute;
+ left: 6px;
+ top: -9px;
+ min-width: 17px;
+ height: 17px;
+ border-radius: 11px; /* (height + 2 * borderThickness) / 2 */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1; /* prevent menu button background from overlaying icon */
+}
+
+.secondary-nav {
+ background: var(--color-secondary-nav-bg) !important; /* important because of .ui.secondary.menu */
+}
+
+.issue-navbar {
+ display: flex;
+ justify-content: space-between;
+}
diff --git a/web_src/css/modules/normalize.css b/web_src/css/modules/normalize.css
new file mode 100644
index 00000000..63fb04a5
--- /dev/null
+++ b/web_src/css/modules/normalize.css
@@ -0,0 +1,243 @@
+/*
+ This is copy of modern-normalize with these changes done:
+
+ - Remove html font-family, we set our own
+ - Remove html tab-size, we set our own
+ - Remove b,strong font-weight, we set our own
+ - Remove b,code,samp,pre font-size, we set our own
+*/
+
+/*! modern-normalize v2.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */
+
+/*
+Document
+========
+*/
+
+/**
+Use a better box model (opinionated).
+*/
+
+*,
+::before,
+::after {
+ box-sizing: border-box;
+}
+
+html {
+ line-height: normal; /* 1. (not following the "modern-normalize") Do not change the browser's default line-height, the default value is font-dependent and roughly 1.2 */
+ -webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */
+}
+
+/*
+Sections
+========
+*/
+
+body {
+ margin: 0; /* Remove the margin in all browsers. */
+}
+
+/*
+Grouping content
+================
+*/
+
+/**
+1. Add the correct height in Firefox.
+2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
+*/
+
+hr {
+ height: 0; /* 1 */
+ color: inherit; /* 2 */
+}
+
+/*
+Text-level semantics
+====================
+*/
+
+/**
+Add the correct text decoration in Chrome, Edge, and Safari.
+*/
+
+abbr[title] {
+ text-decoration: underline dotted;
+}
+
+/**
+Add the correct font size in all browsers.
+*/
+
+small {
+ font-size: 80%;
+}
+
+/**
+Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
+*/
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/*
+Tabular data
+============
+*/
+
+/**
+1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
+2. Correct table border color inheritance in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
+*/
+
+table {
+ text-indent: 0; /* 1 */
+ border-color: inherit; /* 2 */
+}
+
+/*
+Forms
+=====
+*/
+
+/**
+1. Change the font styles in all browsers.
+2. Remove the margin in Firefox and Safari.
+*/
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit; /* 1 */
+ font-size: 100%; /* 1 */
+ line-height: 1.15; /* 1 */
+ margin: 0; /* 2 */
+}
+
+/**
+Remove the inheritance of text transform in Edge and Firefox.
+*/
+
+button,
+select {
+ text-transform: none;
+}
+
+/**
+Correct the inability to style clickable types in iOS and Safari.
+*/
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+/**
+Remove the inner border and padding in Firefox.
+*/
+
+::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+
+/**
+Restore the focus styles unset by the previous rule.
+*/
+
+:-moz-focusring {
+ outline: 1px dotted ButtonText;
+}
+
+/**
+Remove the additional ':invalid' styles in Firefox.
+See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737
+*/
+
+:-moz-ui-invalid {
+ box-shadow: none;
+}
+
+/**
+Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
+*/
+
+legend {
+ padding: 0;
+}
+
+/**
+Add the correct vertical alignment in Chrome and Firefox.
+*/
+
+progress {
+ vertical-align: baseline;
+}
+
+/**
+Correct the cursor style of increment and decrement buttons in Safari.
+*/
+
+::-webkit-inner-spin-button,
+::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+1. Correct the odd appearance in Chrome and Safari.
+2. Correct the outline style in Safari.
+*/
+
+[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ outline-offset: -2px; /* 2 */
+}
+
+/**
+Remove the inner padding in Chrome and Safari on macOS.
+*/
+
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Change font properties to 'inherit' in Safari.
+*/
+
+::-webkit-file-upload-button {
+ -webkit-appearance: button; /* 1 */
+ font: inherit; /* 2 */
+}
+
+/*
+Interactive
+===========
+*/
+
+/*
+Add the correct display in Chrome and Safari.
+*/
+
+summary {
+ display: list-item;
+}
diff --git a/web_src/css/modules/segment.css b/web_src/css/modules/segment.css
new file mode 100644
index 00000000..04543f09
--- /dev/null
+++ b/web_src/css/modules/segment.css
@@ -0,0 +1,203 @@
+/* based on Fomantic UI segment module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.segment {
+ position: relative;
+ margin: 1rem 0;
+ padding: 1em;
+ border-radius: 0.28571429rem;
+ border: 1px solid var(--color-secondary);
+ background: var(--color-box-body);
+ color: var(--color-text);
+}
+.ui.segment:first-child {
+ margin-top: 0;
+}
+.ui.segment:last-child {
+ margin-bottom: 0;
+}
+
+.ui.grid.segment {
+ margin: 1rem 0;
+ border-radius: 0.28571429rem;
+}
+
+.ui.segment.tab:last-child {
+ margin-bottom: 1rem;
+}
+
+.ui.segments {
+ flex-direction: column;
+ position: relative;
+ margin: 1rem 0;
+ border: 1px solid var(--color-secondary);
+ border-radius: 0.28571429rem;
+ background: var(--color-box-body);
+ color: var(--color-text);
+}
+.ui.segments:first-child {
+ margin-top: 0;
+}
+.ui.segments:last-child {
+ margin-bottom: 0;
+}
+
+.ui.segments > .segment {
+ top: 0;
+ bottom: 0;
+ border-radius: 0;
+ margin: 0;
+ width: auto;
+ box-shadow: none;
+ border: none;
+ border-top: 1px solid var(--color-secondary);
+}
+.ui.segments:not(.horizontal) > .segment:first-child {
+ top: 0;
+ bottom: 0;
+ border-top: none;
+ margin-top: 0;
+ margin-bottom: 0;
+ border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+
+.ui.segments:not(.horizontal) > .segment:last-child {
+ top: 0;
+ bottom: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
+.ui.segments:not(.horizontal) > .segment:only-child {
+ border-radius: 0.214285717rem;
+}
+.ui.segments:not(.horizontal) > .segment:has(~ .tw-hidden) { /* workaround issue with :last-child ignoring hidden elements */
+ border-radius: 0.28571429rem;
+}
+
+.ui.segments > .ui.segments {
+ border-top: 1px solid var(--color-secondary);
+ margin: 1rem;
+}
+.ui.segments > .segments:first-child {
+ border-top: none;
+}
+.ui.segments > .segment + .segments:not(.horizontal) {
+ margin-top: 0;
+}
+
+.ui.horizontal.segments {
+ display: flex;
+ flex-direction: row;
+ background-color: transparent;
+ padding: 0;
+ margin: 1rem 0;
+ border-radius: 0.28571429rem;
+ border: 1px solid var(--color-secondary);
+}
+
+.ui.horizontal.segments > .segment {
+ margin: 0;
+ min-width: 0;
+ border-radius: 0;
+ border: none;
+ box-shadow: none;
+ border-left: 1px solid var(--color-secondary);
+}
+
+.ui.segments > .horizontal.segments:first-child {
+ border-top: none;
+}
+.ui.horizontal.segments:not(.stackable) > .segment:first-child {
+ border-left: none;
+}
+.ui.horizontal.segments > .segment:first-child {
+ border-radius: 0.28571429rem 0 0 0.28571429rem;
+}
+.ui.horizontal.segments > .segment:last-child {
+ border-radius: 0 0.28571429rem 0.28571429rem 0;
+}
+
+.ui.clearing.segment::after {
+ content: "";
+ display: block;
+ clear: both;
+}
+
+.ui[class*="left aligned"].segment {
+ text-align: left;
+}
+.ui[class*="center aligned"].segment {
+ text-align: center;
+}
+
+.ui.secondary.segment {
+ background: var(--color-secondary-bg);
+ color: var(--color-text-light);
+}
+
+.ui.attached.segment {
+ top: 0;
+ bottom: 0;
+ border-radius: 0;
+ margin: 0 -1px;
+ width: calc(100% + 2px);
+ max-width: calc(100% + 2px);
+ box-shadow: none;
+ border: 1px solid var(--color-secondary);
+ background: var(--color-box-body);
+ color: var(--color-text);
+}
+.ui.attached:not(.message) + .ui.attached.segment:not(.top) {
+ border-top: none;
+}
+
+.ui.attached.segment:has(+ .ui[class*="top attached"].header),
+.ui.attached.segment:last-child {
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
+.ui[class*="top attached"].segment {
+ bottom: 0;
+ margin-bottom: 0;
+ top: 0;
+ margin-top: 1rem;
+ border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+.ui.segment[class*="top attached"]:first-child {
+ margin-top: 0;
+}
+
+.ui.segment[class*="bottom attached"] {
+ bottom: 0;
+ margin-top: 0;
+ top: 0;
+ margin-bottom: 1rem;
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+.ui.segment[class*="bottom attached"]:last-child {
+ margin-bottom: 1rem;
+}
+
+.ui.fitted.segment:not(.horizontally) {
+ padding-top: 0;
+ padding-bottom: 0;
+}
+.ui.fitted.segment:not(.vertically) {
+ padding-left: 0;
+ padding-right: 0;
+}
+
+.ui.segments .segment,
+.ui.segment {
+ font-size: 1rem;
+}
+
+.ui.error.segment {
+ border-color: var(--color-error-border) !important;
+}
+
+.ui.warning.segment {
+ border-color: var(--color-warning-border) !important;
+}
diff --git a/web_src/css/modules/select.css b/web_src/css/modules/select.css
new file mode 100644
index 00000000..1d7d749d
--- /dev/null
+++ b/web_src/css/modules/select.css
@@ -0,0 +1,25 @@
+.gitea-select {
+ position: relative;
+}
+
+.gitea-select select {
+ appearance: none; /* hide default triangle */
+}
+
+/* ::before and ::after pseudo elements don't work on select elements,
+ so we need to put it on the parent. */
+.gitea-select::after {
+ position: absolute;
+ top: 12px;
+ right: 8px;
+ pointer-events: none;
+ content: "";
+ width: 14px;
+ height: 14px;
+ mask-size: cover;
+ -webkit-mask-size: cover;
+ mask-image: var(--octicon-chevron-right);
+ -webkit-mask-image: var(--octicon-chevron-right);
+ transform: rotate(90deg); /* point the chevron down */
+ background: currentcolor;
+}
diff --git a/web_src/css/modules/svg.css b/web_src/css/modules/svg.css
new file mode 100644
index 00000000..b3060bdd
--- /dev/null
+++ b/web_src/css/modules/svg.css
@@ -0,0 +1,41 @@
+.svg {
+ display: inline-block;
+ vertical-align: text-top;
+ fill: currentcolor;
+}
+
+.middle .svg {
+ vertical-align: middle;
+}
+
+/* prevent SVGs from shrinking, like in space-starved flexboxes. the sizes
+ here are cherry-picked for our use cases, feel free to add more. after
+ https://developer.mozilla.org/en-US/docs/Web/CSS/attr#type-or-unit is
+ supported in browsers, use `attr(width px)` instead for a generic
+ solution. */
+
+.svg[height="12"] { min-height: 12px; }
+.svg[height="13"] { min-height: 13px; }
+.svg[height="14"] { min-height: 14px; }
+.svg[height="15"] { min-height: 15px; }
+.svg[height="16"] { min-height: 16px; }
+.svg[height="18"] { min-height: 18px; }
+.svg[height="20"] { min-height: 20px; }
+.svg[height="22"] { min-height: 22px; }
+.svg[height="24"] { min-height: 24px; }
+.svg[height="36"] { min-height: 36px; }
+.svg[height="48"] { min-height: 48px; }
+.svg[height="56"] { min-height: 56px; }
+
+.svg[width="12"] { min-width: 12px; }
+.svg[width="13"] { min-width: 13px; }
+.svg[width="14"] { min-width: 14px; }
+.svg[width="15"] { min-width: 15px; }
+.svg[width="16"] { min-width: 16px; }
+.svg[width="18"] { min-width: 18px; }
+.svg[width="20"] { min-width: 20px; }
+.svg[width="22"] { min-width: 22px; }
+.svg[width="24"] { min-width: 24px; }
+.svg[width="36"] { min-width: 36px; }
+.svg[width="48"] { min-width: 48px; }
+.svg[width="56"] { min-width: 56px; }
diff --git a/web_src/css/modules/table.css b/web_src/css/modules/table.css
new file mode 100644
index 00000000..4fb9d421
--- /dev/null
+++ b/web_src/css/modules/table.css
@@ -0,0 +1,385 @@
+/* based on Fomantic UI segment module, with just the parts extracted that we use. If you find any
+ unused rules here after refactoring, please remove them. */
+
+.ui.table {
+ width: 100%;
+ margin: 1em 0;
+ border: 1px solid var(--color-secondary);
+ border-radius: 0.28571429rem;
+ vertical-align: middle;
+ border-collapse: separate;
+ border-spacing: 0;
+ color: var(--color-text);
+ background: var(--color-box-body);
+ border-color: var(--color-secondary);
+ text-align: start;
+}
+
+.ui.table:first-child {
+ margin-top: 0;
+}
+.ui.table:last-child {
+ margin-bottom: 0;
+}
+.ui.table > thead,
+.ui.table > tbody {
+ text-align: inherit;
+ vertical-align: inherit;
+}
+
+.ui.table > thead > tr > th {
+ background: var(--color-box-header);
+ text-align: inherit;
+ color: var(--color-text);
+ padding: 6px 5px;
+ vertical-align: inherit;
+ font-weight: var(--font-weight-normal);
+ border-bottom: 1px solid var(--color-secondary);
+ border-left: none;
+}
+.ui.table > thead > tr > th:first-child {
+ border-left: none;
+}
+.ui.table > thead > tr:first-child > th:first-child {
+ border-radius: 0.28571429rem 0 0;
+}
+.ui.table > thead > tr:first-child > th:last-child {
+ border-radius: 0 0.28571429rem 0 0;
+}
+.ui.table > thead > tr:first-child > th:only-child {
+ border-radius: 0.28571429rem 0.28571429rem 0 0;
+}
+
+.ui.table > tfoot > tr > th,
+.ui.table > tfoot > tr > td {
+ border-top: 1px solid var(--color-secondary);
+ background: var(--color-box-body);
+ text-align: inherit;
+ color: var(--color-text);
+ padding: 0.78571429em;
+ vertical-align: inherit;
+ font-weight: var(--font-weight-normal);
+}
+.ui.table > tfoot > tr > th:first-child,
+.ui.table > tfoot > tr > td:first-child {
+ border-left: none;
+}
+.ui.table > tfoot > tr:first-child > th:first-child,
+.ui.table > tfoot > tr:first-child > td:first-child {
+ border-radius: 0 0 0 0.28571429rem;
+}
+.ui.table > tfoot > tr:first-child > th:last-child,
+.ui.table > tfoot > tr:first-child > td:last-child {
+ border-radius: 0 0 0.28571429rem;
+}
+.ui.table > tfoot > tr:first-child > th:only-child,
+.ui.table > tfoot > tr:first-child > td:only-child {
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+
+.ui.table > tr > td,
+.ui.table > tbody > tr > td {
+ border-top: 1px solid var(--color-secondary-alpha-50);
+ padding: 6px 5px;
+ text-align: inherit;
+}
+.ui.table > tr:first-child > td,
+.ui.table > tbody > tr:first-child > td {
+ border-top: none;
+}
+
+.ui.table.segment {
+ padding: 0;
+}
+.ui.table.segment::after {
+ display: none;
+}
+
+@media only screen and (max-width: 767.98px) {
+ .ui.table:not(.unstackable) {
+ width: 100%;
+ padding: 0;
+ }
+ .ui.table:not(.unstackable) > thead,
+ .ui.table:not(.unstackable) > thead > tr,
+ .ui.table:not(.unstackable) > tfoot,
+ .ui.table:not(.unstackable) > tfoot > tr,
+ .ui.table:not(.unstackable) > tbody,
+ .ui.table:not(.unstackable) > tr,
+ .ui.table:not(.unstackable) > tbody > tr,
+ .ui.table:not(.unstackable) > tr > th,
+ .ui.table:not(.unstackable) > thead > tr > th,
+ .ui.table:not(.unstackable) > tbody > tr > th,
+ .ui.table:not(.unstackable) > tfoot > tr > th,
+ .ui.table:not(.unstackable) > tr > td,
+ .ui.table:not(.unstackable) > tbody > tr > td,
+ .ui.table:not(.unstackable) > tfoot > tr > td {
+ display: block !important;
+ width: auto !important;
+ }
+ .ui.table:not(.unstackable) > thead {
+ display: block;
+ }
+ .ui.table:not(.unstackable) > tfoot {
+ display: block;
+ }
+ .ui.ui.ui.ui.table:not(.unstackable) > tr,
+ .ui.ui.ui.ui.table:not(.unstackable) > thead > tr,
+ .ui.ui.ui.ui.table:not(.unstackable) > tbody > tr,
+ .ui.ui.ui.ui.table:not(.unstackable) > tfoot > tr {
+ padding-top: 1em;
+ padding-bottom: 1em;
+ }
+ .ui.ui.ui.ui.table:not(.unstackable) > tr > th,
+ .ui.ui.ui.ui.table:not(.unstackable) > thead > tr > th,
+ .ui.ui.ui.ui.table:not(.unstackable) > tbody > tr > th,
+ .ui.ui.ui.ui.table:not(.unstackable) > tfoot > tr > th,
+ .ui.ui.ui.ui.table:not(.unstackable) > tr > td,
+ .ui.ui.ui.ui.table:not(.unstackable) > tbody > tr > td,
+ .ui.ui.ui.ui.table:not(.unstackable) > tfoot > tr > td {
+ background: none;
+ border: none;
+ padding: 0.25em 0.75em;
+ }
+ .ui.table:not(.unstackable) > tr > th:first-child,
+ .ui.table:not(.unstackable) > thead > tr > th:first-child,
+ .ui.table:not(.unstackable) > tbody > tr > th:first-child,
+ .ui.table:not(.unstackable) > tfoot > tr > th:first-child,
+ .ui.table:not(.unstackable) > tr > td:first-child,
+ .ui.table:not(.unstackable) > tbody > tr > td:first-child,
+ .ui.table:not(.unstackable) > tfoot > tr > td:first-child {
+ font-weight: var(--font-weight-normal);
+ }
+}
+
+.ui.table[class*="left aligned"],
+.ui.table [class*="left aligned"] {
+ text-align: left;
+}
+
+.ui.table[class*="center aligned"],
+.ui.table [class*="center aligned"] {
+ text-align: center;
+}
+
+.ui.table[class*="right aligned"],
+.ui.table [class*="right aligned"] {
+ text-align: right;
+}
+
+.ui.table[class*="top aligned"],
+.ui.table [class*="top aligned"] {
+ vertical-align: top;
+}
+
+.ui.table[class*="middle aligned"],
+.ui.table [class*="middle aligned"] {
+ vertical-align: middle;
+}
+
+.ui.table th.collapsing,
+.ui.table td.collapsing {
+ width: 1px;
+ white-space: nowrap;
+}
+
+.ui.fixed.table {
+ table-layout: fixed;
+}
+.ui.fixed.table th,
+.ui.fixed.table td {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.ui.attached.table {
+ top: 0;
+ bottom: 0;
+ border-radius: 0;
+ margin: 0 -1px;
+ width: calc(100% + 2px);
+ max-width: calc(100% + 2px);
+ border: 1px solid var(--color-secondary);
+}
+.ui.attached + .ui.attached.table:not(.top) {
+ border-top: none;
+}
+
+.ui[class*="bottom attached"].table {
+ bottom: 0;
+ margin-top: 0;
+ top: 0;
+ margin-bottom: 1em;
+ border-radius: 0 0 0.28571429rem 0.28571429rem;
+}
+.ui[class*="bottom attached"].table:last-child {
+ margin-bottom: 0;
+}
+
+.ui.striped.table > tr:nth-child(2n),
+.ui.striped.table > tbody > tr:nth-child(2n) {
+ background: var(--color-light);
+}
+
+.ui.table[class*="single line"],
+.ui.table [class*="single line"] {
+ white-space: nowrap;
+}
+
+/* Column Width */
+.ui.table th.one.wide,
+.ui.table td.one.wide {
+ width: 6.25%;
+}
+.ui.table th.two.wide,
+.ui.table td.two.wide {
+ width: 12.5%;
+}
+.ui.table th.three.wide,
+.ui.table td.three.wide {
+ width: 18.75%;
+}
+.ui.table th.four.wide,
+.ui.table td.four.wide {
+ width: 25%;
+}
+.ui.table th.five.wide,
+.ui.table td.five.wide {
+ width: 31.25%;
+}
+.ui.table th.six.wide,
+.ui.table td.six.wide {
+ width: 37.5%;
+}
+.ui.table th.seven.wide,
+.ui.table td.seven.wide {
+ width: 43.75%;
+}
+.ui.table th.eight.wide,
+.ui.table td.eight.wide {
+ width: 50%;
+}
+.ui.table th.nine.wide,
+.ui.table td.nine.wide {
+ width: 56.25%;
+}
+.ui.table th.ten.wide,
+.ui.table td.ten.wide {
+ width: 62.5%;
+}
+.ui.table th.eleven.wide,
+.ui.table td.eleven.wide {
+ width: 68.75%;
+}
+.ui.table th.twelve.wide,
+.ui.table td.twelve.wide {
+ width: 75%;
+}
+.ui.table th.thirteen.wide,
+.ui.table td.thirteen.wide {
+ width: 81.25%;
+}
+.ui.table th.fourteen.wide,
+.ui.table td.fourteen.wide {
+ width: 87.5%;
+}
+.ui.table th.fifteen.wide,
+.ui.table td.fifteen.wide {
+ width: 93.75%;
+}
+.ui.table th.sixteen.wide,
+.ui.table td.sixteen.wide {
+ width: 100%;
+}
+
+.ui.basic.table {
+ background: transparent;
+ border: 1px solid var(--color-secondary);
+}
+.ui.basic.table > thead > tr > th,
+.ui.basic.table > tbody > tr > th,
+.ui.basic.table > tfoot > tr > th,
+.ui.basic.table > tr > th {
+ background: transparent;
+ border-left: none;
+}
+.ui.basic.table > tbody > tr {
+ border-bottom: 1px solid var(--color-secondary);
+}
+.ui.basic.table > tbody > tr > td,
+.ui.basic.table > tfoot > tr > td,
+.ui.basic.table > tr > td {
+ background: transparent;
+}
+.ui.basic.striped.table > tbody > tr:nth-child(2n) {
+ background: var(--color-light);
+}
+
+.ui[class*="very basic"].table {
+ border: none;
+}
+.ui[class*="very basic"].table:not(.striped) > tr > th:first-child,
+.ui[class*="very basic"].table:not(.striped) > thead > tr > th:first-child,
+.ui[class*="very basic"].table:not(.striped) > tbody > tr > th:first-child,
+.ui[class*="very basic"].table:not(.striped) > tfoot > tr > th:first-child,
+.ui[class*="very basic"].table:not(.striped) > tr > td:first-child,
+.ui[class*="very basic"].table:not(.striped) > tbody > tr > td:first-child,
+.ui[class*="very basic"].table:not(.striped) > tfoot > tr > td:first-child {
+ padding-left: 0;
+}
+.ui[class*="very basic"].table:not(.striped) > tr > th:last-child,
+.ui[class*="very basic"].table:not(.striped) > thead > tr > th:last-child,
+.ui[class*="very basic"].table:not(.striped) > tbody > tr > th:last-child,
+.ui[class*="very basic"].table:not(.striped) > tfoot > tr > th:last-child,
+.ui[class*="very basic"].table:not(.striped) > tr > td:last-child,
+.ui[class*="very basic"].table:not(.striped) > tbody > tr > td:last-child,
+.ui[class*="very basic"].table:not(.striped) > tfoot > tr > td:last-child {
+ padding-right: 0;
+}
+.ui[class*="very basic"].table:not(.striped) > thead > tr:first-child > th {
+ padding-top: 0;
+}
+
+.ui.celled.table > tr > th,
+.ui.celled.table > thead > tr > th,
+.ui.celled.table > tbody > tr > th,
+.ui.celled.table > tfoot > tr > th,
+.ui.celled.table > tr > td,
+.ui.celled.table > tbody > tr > td,
+.ui.celled.table > tfoot > tr > td {
+ border-left: 1px solid var(--color-secondary-alpha-50);
+}
+.ui.celled.table > tr > th:first-child,
+.ui.celled.table > thead > tr > th:first-child,
+.ui.celled.table > tbody > tr > th:first-child,
+.ui.celled.table > tfoot > tr > th:first-child,
+.ui.celled.table > tr > td:first-child,
+.ui.celled.table > tbody > tr > td:first-child,
+.ui.celled.table > tfoot > tr > td:first-child {
+ border-left: none;
+}
+
+.ui.compact.table > tr > th,
+.ui.compact.table > thead > tr > th,
+.ui.compact.table > tbody > tr > th,
+.ui.compact.table > tfoot > tr > th {
+ padding-left: 0.7em;
+ padding-right: 0.7em;
+}
+.ui.compact.table > tr > td,
+.ui.compact.table > tbody > tr > td,
+.ui.compact.table > tfoot > tr > td {
+ padding: 0.5em 0.7em;
+}
+
+/* use more horizontal padding on first and last items for visuals */
+.ui.table > thead > tr > th:first-of-type,
+.ui.table > tbody > tr > td:first-of-type,
+.ui.table > tr > td:first-of-type {
+ padding-left: 10px;
+}
+.ui.table > thead > tr > th:last-of-type,
+.ui.table > tbody > tr > td:last-of-type,
+.ui.table > tr > td:last-of-type {
+ padding-right: 10px;
+}
diff --git a/web_src/css/modules/tippy.css b/web_src/css/modules/tippy.css
new file mode 100644
index 00000000..6ac7c37d
--- /dev/null
+++ b/web_src/css/modules/tippy.css
@@ -0,0 +1,170 @@
+/* styles are based on node_modules/tippy.js/dist/tippy.css */
+
+/* class to hide tippy target elements on page load */
+.tippy-target {
+ display: none !important;
+}
+
+/* show target element once it's been moved by tippy.js */
+.tippy-content .tippy-target {
+ display: unset !important;
+}
+
+[data-tippy-root] {
+ max-width: calc(100vw - 32px);
+}
+
+.tippy-box {
+ position: relative;
+ background-color: var(--color-body);
+ color: var(--color-secondary-dark-6);
+ border: 1px solid var(--color-secondary);
+ border-radius: var(--border-radius);
+ font-size: 1rem;
+}
+
+.tippy-content {
+ position: relative;
+ padding: 1rem; /* if you need different padding, use different data-theme */
+ z-index: 1;
+}
+
+/* bare theme, no styling at all, except box-shadow */
+.tippy-box[data-theme="bare"] {
+ border: none;
+ box-shadow: 0 6px 18px var(--color-shadow);
+}
+
+.tippy-box[data-theme="bare"] .tippy-content {
+ padding: 0;
+ background: transparent;
+}
+
+/* tooltip theme for text tooltips */
+
+.tippy-box[data-theme="tooltip"] {
+ background-color: var(--color-tooltip-bg);
+ color: var(--color-tooltip-text);
+ border: none;
+}
+
+.tippy-box[data-theme="tooltip"] .tippy-content {
+ padding: 0.5rem 1rem;
+}
+
+.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-inner,
+.tippy-box[data-theme="tooltip"] .tippy-svg-arrow-outer {
+ fill: var(--color-tooltip-bg);
+}
+
+/* menu theme for .ui.menu */
+
+.tippy-box[data-theme="menu"] {
+ background-color: var(--color-menu);
+ color: var(--color-text);
+ box-shadow: 0 6px 18px var(--color-shadow);
+}
+
+.tippy-box[data-theme="menu"] .tippy-content {
+ padding: 4px 0;
+}
+
+.tippy-box[data-theme="menu"] .tippy-svg-arrow-inner {
+ fill: var(--color-menu);
+}
+
+.tippy-box[data-theme="menu"] .item {
+ display: flex;
+ align-items: center;
+ padding: 9px 18px;
+ color: inherit;
+ text-decoration: none;
+ gap: 10px;
+}
+
+.tippy-box[data-theme="menu"] .item:hover {
+ background: var(--color-hover);
+}
+
+.tippy-box[data-theme="menu"] .item:focus {
+ background: var(--color-active);
+}
+
+/* box-with-header theme to look like .ui.attached.segment. can contain .ui.attached.header */
+
+.tippy-box[data-theme="box-with-header"] {
+ box-shadow: 0 6px 18px var(--color-shadow);
+}
+
+.tippy-box[data-theme="box-with-header"] .tippy-content {
+ background: var(--color-box-body);
+ border-radius: var(--border-radius);
+ padding: 0;
+}
+
+.tippy-box[data-theme="box-with-header"][data-placement^="top"] .tippy-svg-arrow-inner {
+ fill: var(--color-box-body);
+}
+
+.tippy-box[data-theme="box-with-header"][data-placement^="bottom"] .tippy-svg-arrow-inner {
+ fill: var(--color-box-header);
+}
+
+.tippy-box[data-placement^="top"] > .tippy-svg-arrow {
+ bottom: 0;
+}
+
+.tippy-box[data-placement^="top"] > .tippy-svg-arrow::after,
+.tippy-box[data-placement^="top"] > .tippy-svg-arrow > svg {
+ top: 16px;
+ transform: rotate(180deg);
+}
+
+.tippy-box[data-placement^="bottom"] > .tippy-svg-arrow {
+ top: 0;
+}
+
+.tippy-box[data-placement^="bottom"] > .tippy-svg-arrow > svg {
+ bottom: 16px;
+}
+
+.tippy-box[data-placement^="left"] > .tippy-svg-arrow {
+ right: 0;
+}
+
+.tippy-box[data-placement^="left"] > .tippy-svg-arrow::after,
+.tippy-box[data-placement^="left"] > .tippy-svg-arrow > svg {
+ transform: rotate(90deg);
+ top: calc(50% - 3px);
+ left: 11px;
+}
+
+.tippy-box[data-placement^="right"] > .tippy-svg-arrow {
+ left: 0;
+}
+
+.tippy-box[data-placement^="right"] > .tippy-svg-arrow::after,
+.tippy-box[data-placement^="right"] > .tippy-svg-arrow > svg {
+ transform: rotate(-90deg);
+ top: calc(50% - 3px);
+ right: 11px;
+}
+
+.tippy-svg-arrow {
+ width: 16px;
+ height: 16px;
+ text-align: initial;
+}
+
+.tippy-svg-arrow,
+.tippy-svg-arrow > svg {
+ position: absolute;
+}
+
+.tippy-svg-arrow-outer {
+ fill: var(--color-secondary);
+}
+
+.tippy-svg-arrow-inner {
+ fill: var(--color-body);
+}
diff --git a/web_src/css/modules/toast.css b/web_src/css/modules/toast.css
new file mode 100644
index 00000000..2a9f78e0
--- /dev/null
+++ b/web_src/css/modules/toast.css
@@ -0,0 +1,77 @@
+.toastify {
+ color: var(--color-white);
+ position: fixed;
+ opacity: 0;
+ transition: all .2s ease;
+ z-index: 500;
+ border-radius: var(--border-radius);
+ box-shadow: 0 8px 24px var(--color-shadow);
+ display: flex;
+ max-width: 50vw;
+ min-width: 300px;
+ padding: 4px;
+}
+
+.toastify.on {
+ opacity: 1;
+}
+
+.toast-body {
+ flex: 1;
+ padding: 5px 0;
+ overflow-wrap: anywhere;
+}
+
+.toast-close,
+.toast-icon {
+ color: currentcolor;
+ border-radius: var(--border-radius);
+ background: transparent;
+ border: none;
+ display: flex;
+ width: 30px;
+ height: 30px;
+ justify-content: center;
+ align-items: center;
+}
+
+.toast-close:hover {
+ background: var(--color-hover);
+}
+
+.toast-close:active {
+ background: var(--color-active);
+}
+
+.toastify-right {
+ right: 15px;
+}
+
+.toastify-left {
+ left: 15px;
+}
+
+.toastify-top {
+ top: -150px;
+}
+
+.toastify-bottom {
+ bottom: -150px;
+}
+
+.toastify-center {
+ margin-left: auto;
+ margin-right: auto;
+ left: 0;
+ right: 0;
+}
+
+@media (max-width: 360px) {
+ .toastify-right, .toastify-left {
+ margin-left: auto;
+ margin-right: auto;
+ left: 0;
+ right: 0;
+ max-width: fit-content;
+ }
+}