Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2022-04-22 13:32:14 +0300
committerGitHub <noreply@github.com>2022-04-22 13:32:14 +0300
commit9a76f06ecadf05ef1d26bd735df1bea0dfb15d59 (patch)
treeebdd458b0969c07d5ce77b21684a8bdf1db8434d /core
parente1cb1bdce94fa2a6395b2d3a35556944111e66b1 (diff)
parent850d8ac1cd9e5b28e37668469237d8daa5c5d51d (diff)
Merge pull request #31751 from nextcloud/theming-providers
Diffstat (limited to 'core')
-rw-r--r--core/css/apps.scss3
-rw-r--r--core/css/guest.css65
-rw-r--r--core/css/header.scss63
-rw-r--r--core/css/icons.scss8
-rw-r--r--core/css/styles.scss1
-rw-r--r--core/shipped.json1
-rw-r--r--core/src/components/login/LoginButton.vue62
-rw-r--r--core/src/components/login/LoginForm.vue6
-rw-r--r--core/src/components/login/PasswordLessLoginForm.vue5
-rw-r--r--core/src/components/login/ResetPassword.vue21
-rw-r--r--core/src/components/login/UpdatePassword.vue23
-rw-r--r--core/src/views/Login.vue6
-rw-r--r--core/src/views/UnifiedSearch.vue17
-rw-r--r--core/templates/layout.guest.php4
-rw-r--r--core/templates/layout.user.php9
15 files changed, 130 insertions, 164 deletions
diff --git a/core/css/apps.scss b/core/css/apps.scss
index 15742786b0e..84179a94633 100644
--- a/core/css/apps.scss
+++ b/core/css/apps.scss
@@ -166,6 +166,7 @@ kbd {
&,
> a {
background-color: var(--color-primary-light);
+ color: var(--color-primary-text);
}
}
@@ -285,6 +286,8 @@ kbd {
margin-right: 11px;
width: 16px;
height: 16px;
+ // Legacy invert if bright background
+ filter: var(--background-invert-if-dark);
}
/* counter can also be inside the link */
diff --git a/core/css/guest.css b/core/css/guest.css
index d48713061ec..a6d2abf9820 100644
--- a/core/css/guest.css
+++ b/core/css/guest.css
@@ -15,20 +15,18 @@ a, a *, input, input *, select, .button span, label { cursor:pointer; }
ul { list-style:none; }
body {
- background-color: #ffffff;
font-weight: normal;
/* bring the default font size up to 14px */
font-size: .875em;
line-height: 1.6em;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
- color: #000;
+ color: var(--color-primary-text);
text-align: center;
- background-color: #0082c9;
- background-image: url('../img/background.png?v=2');
- background-image: url('../img/background.png?v=2'), linear-gradient(40deg, #0082c9 0%, rgba(28,175,255,1) 100%);
+ background-color: var(--color-primary);
+ background-image: var(--image-background, var(--image-background-plain, url('../../../core/img/background.svg'), linear-gradient(40deg, #0082c9 0%, #30b6ff 100%)));
background-position: 50% 50%;
background-repeat: repeat;
- background-size: 275px, contain;
+ background-size: var(--image-background-size, 275px, contain);
background-attachment: fixed; /* fix background gradient */
min-height: 100%; /* fix sticky footer */
height: auto;
@@ -36,7 +34,7 @@ body {
/* Various fonts settings */
#body-login a {
- color: #fff;
+ color: var(--color-primary-text);
font-weight: 600;
}
#body-login a:not(.button):hover,
@@ -46,7 +44,7 @@ body {
}
#showAdvanced {
- color: #fff;
+ color: var(--color-primary-text);
}
em {
@@ -80,13 +78,12 @@ body {
}
#header .logo {
- background-image: url('../img/logo/logo.svg?v=1');
+ background-image: var(--image-logo, url('../../core/img/logo/logo.svg'));
background-repeat: no-repeat;
- background-size: 175px;
+ background-size: contain;
background-position: center;
- width: 256px;
- min-height: 128px;
- max-height: 200px;
+ width: 175px;
+ height: 130px;
margin: 0 auto;
position: relative;
left: unset;
@@ -185,7 +182,7 @@ form #datadirField legend {
}
.alternative-logins .button {
- color: #ffffff;
+ color: var(--color-text-lighter);
padding: 12px 20px;
}
@@ -263,9 +260,9 @@ select {
width: auto;
min-width: 25px;
padding: 12px;
- background-color: white;
+ background-color: var(--color-main-background);
font-weight: bold;
- color: #555;
+ color: var(--color-text-lighter);
border: none;
border-radius: 100px; /* --border-radius-pill */
cursor: pointer;
@@ -280,7 +277,7 @@ input[type='password'],
input[type='email'] {
width: 249px;
background: #fff;
- color: #555;
+ color: var(--color-text-lighter);
cursor: text;
font-family: inherit;
-webkit-appearance: textfield;
@@ -338,9 +335,9 @@ input::-moz-focus-inner {
input.primary,
button.primary,
a.primary {
- border: 1px solid #fff;
- background-color: #0082c9;
- color: #fff;
+ border: 1px solid var(--color-primary-text);
+ background-color: var(--color-primary);
+ color: var(--color-primary-text);
transition: color 100ms ease-in-out;
}
@@ -350,8 +347,8 @@ button.primary:not(:disabled):hover,
button.primary:not(:disabled):focus,
a.primary:not(:disabled):hover,
a.primary:not(:disabled):focus {
- color: rgba(255, 255, 255, 1);
- background-image: linear-gradient(40deg, #0082c9 0%, #30b6ff 100%);
+ color: var(--color-primary-text);
+ background-image: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
background-position: initial;
}
@@ -479,13 +476,13 @@ form fieldset legend,
form fieldset .warning-info,
form input[type='checkbox']+label {
text-align: center;
- color: #fff;
+ color: var(--color-primary-text);
}
/* overrides another !important statement that sets this to unreadable black */
form .warning input[type='checkbox']:hover+label,
form .warning input[type='checkbox']:focus+label,
form .warning input[type='checkbox']+label {
- color: #fff !important;
+ color: var(--color-primary-text) !important;
}
.body-login-container.two-factor {
@@ -559,7 +556,7 @@ form .warning input[type='checkbox']+label {
display: inline-block;
font-weight: normal !important;
padding: 12px;
- color: #fff;
+ color: var(--color-primary-text);
cursor: pointer;
text-shadow: 0 0 2px rgba(0, 0, 0, .4); /* better readability on bright background */
}
@@ -569,7 +566,7 @@ form .warning input[type='checkbox']+label {
#forgot-password {
padding: 11px;
float: right;
- color: #fff;
+ color: var(--color-primary-text);
}
/* Alternative Logins */
@@ -587,16 +584,16 @@ form .warning input[type='checkbox']+label {
display: inline-block;
text-align: center;
box-sizing: border-box;
- border: 2px solid #ffffff;
- background-color: #0082c9;
- color: #ffffff;
+ border: 2px solid var(--color-primary-text);
+ background-color: var(--color-primary);
+ color: var(--color-primary-text);
border-radius: 100px; /* --border-radius-pill */
}
.alternative-logins a.button:focus,
.alternative-logins li a:focus {
- border: 2px solid #000000;
- background-image: linear-gradient(40deg, #0082c9 0%, #30b6ff 100%);
+ border: 2px solid var(--color-primary-hover);
+ background-image: linear-gradient(40deg, var(--color-primary) 0%, var(--color-primary-light) 100%);
background-position: initial;
}
@@ -854,6 +851,7 @@ p.info {
}
.loading, .loading-small, .icon-loading, .icon-loading-dark, .icon-loading-small, .icon-loading-small-dark {
position: relative;
+ filter: var(--background-invert-if-dark)
}
.loading:after, .loading-small:after, .icon-loading:after, .icon-loading-dark:after, .icon-loading-small:after, .icon-loading-small-dark:after {
z-index: 2;
@@ -871,6 +869,9 @@ p.info {
-ms-transform-origin: center;
transform-origin: center;
}
+.primary .loading,.primary+.loading,.primary .loading-small,.primary+.loading-small,.primary .icon-loading,.primary+.icon-loading,.primary .icon-loading-dark,.primary+.icon-loading-dark,.primary .icon-loading-small,.primary+.icon-loading-small,.primary .icon-loading-small-dark,.primary+.icon-loading-small-dark {
+ filter: var(--primary-invert-if-bright)
+}
.loading:after, .loading-small:after, .icon-loading:after, .icon-loading-dark:after, .icon-loading-small:after, .icon-loading-small-dark:after {
border: 2px solid rgba(150, 150, 150, 0.5);
border-top-color: #646464;
@@ -920,7 +921,7 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading-
/* FOOTER */
footer {
- height: 70px;
+ min-height: 70px;
margin-top: auto;
}
diff --git a/core/css/header.scss b/core/css/header.scss
index 27a8fe289fa..4c107c0d58f 100644
--- a/core/css/header.scss
+++ b/core/css/header.scss
@@ -165,6 +165,7 @@
margin-right: 10px;
height: 16px;
width: 16px;
+ filter: var(--background-invert-if-dark);
}
}
}
@@ -172,7 +173,7 @@
}
.logo {
display: inline-flex;
- background-image: var(--image-logo);
+ background-image: var(--image-logoheader, var(--image-logo, url('../img/logo/logo.svg')));
background-repeat: no-repeat;
background-size: contain;
background-position: center;
@@ -449,6 +450,11 @@ nav[role='navigation'] {
// Make sure most app names don’t ellipsize
letter-spacing: -0.5px;
font-size: 12px;
+
+ // If the primary is too bright, invert the app icons
+ svg image {
+ filter: var(--primary-invert-if-bright);
+ }
}
/* focused app visual feedback */
@@ -653,61 +659,6 @@ nav[role='navigation'] {
}
}
-/* SEARCHBOX --------------------------------------------------------------- */
-.searchbox {
- position: relative;
- display: flex;
- align-items: center;
- input[type='search'] {
- position: relative;
- font-size: 1.2em;
- padding: 3px;
- padding-left: 25px;
- padding-right: 20px;
- background-color: transparent;
- color: var(--color-primary-text);
- border: 0;
- border-radius: var(--border-radius-pill);
- height: 34px;
- width: 0;
- cursor: pointer;
- transition: width 100ms, opacity 100ms;
- opacity: .6;
- &:focus, &:active, &:valid {
- background-position-x: 6px;
- color: var(--color-primary-text);
- width: 155px;
- cursor: text;
- background-color: transparent !important;
- border: 1px solid var(--color-primary-text) !important;
- }
- &:hover, &:focus, &:active {
- opacity: 1;
- }
- & ~ .icon-close-white {
- display: inline;
- position: absolute;
- width: 30px;
- height: 100%;
- right: 0;
- top: 0;
- margin: 0;
- &, &:focus, &:active, &:hover {
- border: none;
- background-color: transparent;
- }
- }
- &:not(:valid) ~ .icon-close-white {
- display: none;
- }
- &::-webkit-search-cancel-button {
- -webkit-appearance: none;
- }
- }
- .icon-search-force-white {
- @include icon-color('search', 'actions', '#fffffe', 1, true);
- }
-}
/* Empty content messages in the header e.g. notifications, contacts menu, … */
header #emptycontent,
diff --git a/core/css/icons.scss b/core/css/icons.scss
index 1468c480046..78522895cf5 100644
--- a/core/css/icons.scss
+++ b/core/css/icons.scss
@@ -64,6 +64,14 @@
transform-origin: center;
border: 2px solid var(--color-loading-light);
border-top-color: var(--color-loading-dark);
+ // revert if background is too bright
+ filter: var(--background-invert-if-dark);
+
+ .primary &,
+ .primary + & {
+ // revert if primary is too bright
+ filter: var(--primary-invert-if-bright);
+ }
}
}
diff --git a/core/css/styles.scss b/core/css/styles.scss
index 8a15cfa19d8..27e5675b53a 100644
--- a/core/css/styles.scss
+++ b/core/css/styles.scss
@@ -975,6 +975,7 @@ span.ui-icon {
background-size: 20px 20px;
padding: 14px;
cursor: pointer;
+ filter: var(--primary-invert-if-bright);
&:hover,
&:focus,
diff --git a/core/shipped.json b/core/shipped.json
index 14ee28a71d0..8a63cf20248 100644
--- a/core/shipped.json
+++ b/core/shipped.json
@@ -1,6 +1,5 @@
{
"shippedApps": [
- "accessibility",
"activity",
"admin_audit",
"circles",
diff --git a/core/src/components/login/LoginButton.vue b/core/src/components/login/LoginButton.vue
index f7d426e6c63..e99398ba09a 100644
--- a/core/src/components/login/LoginButton.vue
+++ b/core/src/components/login/LoginButton.vue
@@ -20,25 +20,33 @@
-->
<template>
- <div id="submit-wrapper" @click="$emit('click')">
- <input id="submit-form"
- type="submit"
- class="login primary"
+ <div class="submit-wrapper" @click="$emit('click')">
+ <input type="submit"
+ class="submit-wrapper__input primary"
title=""
- :value="!loading ? t('core', 'Log in') : t('core', 'Logging in …')">
- <div class="submit-icon"
- :class="{
- 'icon-confirm-white': !loading,
- 'icon-loading-small': loading && invertedColors,
- 'icon-loading-small-dark': loading && !invertedColors,
- }" />
+ :value="!loading ? value : valueLoading">
+ <div v-if="loading" class="submit-wrapper__icon icon-loading-small-dark" />
+ <ArrowRight v-else class="submit-wrapper__icon" />
</div>
</template>
<script>
+import ArrowRight from 'vue-material-design-icons/ArrowRight.vue'
+
export default {
name: 'LoginButton',
+ components: {
+ ArrowRight,
+ },
props: {
+ value: {
+ type: String,
+ default: t('core', 'Log in'),
+ },
+ valueLoading: {
+ type: String,
+ default: t('core', 'Logging in …'),
+ },
loading: {
type: Boolean,
required: true,
@@ -51,6 +59,36 @@ export default {
}
</script>
-<style scoped>
+<style scoped lang="scss">
+.submit-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 10px 5px;
+ position: relative;
+ margin: 0 auto;
+ &__input {
+ width: 260px;
+ height: 50px;
+ }
+
+ &__icon {
+ display: flex;
+ position: absolute;
+ right: 24px;
+ transition: right 100ms ease-in-out;
+ /* The submit icon is positioned on the submit button.
+ From the user point of view the icon is part of the
+ button, so the clicks on the icon have to be
+ applied to the button instead. */
+ pointer-events: none;
+ }
+
+ &__input:hover + &__icon:not(.icon-loading-small-dark),
+ &__input:focus + &__icon:not(.icon-loading-small-dark),
+ &__input:active + &__icon:not(.icon-loading-small-dark) {
+ right: 20px;
+ }
+}
</style>
diff --git a/core/src/components/login/LoginForm.vue b/core/src/components/login/LoginForm.vue
index e702961d50e..9114c0a728a 100644
--- a/core/src/components/login/LoginForm.vue
+++ b/core/src/components/login/LoginForm.vue
@@ -89,7 +89,7 @@
</a>
</p>
- <LoginButton :loading="loading" :inverted-colors="invertedColors" />
+ <LoginButton :loading="loading" />
<p v-if="invalidPassword"
class="warning wrongPasswordMsg">
@@ -158,10 +158,6 @@ export default {
type: Number,
default: 0,
},
- invertedColors: {
- type: Boolean,
- default: false,
- },
autoCompleteAllowed: {
type: Boolean,
default: true,
diff --git a/core/src/components/login/PasswordLessLoginForm.vue b/core/src/components/login/PasswordLessLoginForm.vue
index 94e7b35fa0e..3c0013936f0 100644
--- a/core/src/components/login/PasswordLessLoginForm.vue
+++ b/core/src/components/login/PasswordLessLoginForm.vue
@@ -25,7 +25,6 @@
<LoginButton v-if="validCredentials"
:loading="loading"
- :inverted-colors="invertedColors"
@click="authenticate" />
</fieldset>
</form>
@@ -74,10 +73,6 @@ export default {
type: [String, Boolean],
default: false,
},
- invertedColors: {
- type: Boolean,
- default: false,
- },
autoCompleteAllowed: {
type: Boolean,
default: true,
diff --git a/core/src/components/login/ResetPassword.vue b/core/src/components/login/ResetPassword.vue
index 79589a96aca..7a499baa2f0 100644
--- a/core/src/components/login/ResetPassword.vue
+++ b/core/src/components/login/ResetPassword.vue
@@ -37,17 +37,7 @@
<label for="user" class="infield">{{ t('core', 'Username or email') }}</label>
</p>
<div id="reset-password-wrapper">
- <input id="reset-password-submit"
- type="submit"
- class="login primary"
- title=""
- :value="t('core', 'Reset password')">
- <div class="submit-icon"
- :class="{
- 'icon-confirm-white': !loading,
- 'icon-loading-small': loading && invertedColors,
- 'icon-loading-small-dark': loading && !invertedColors,
- }" />
+ <LoginButton :value="t('core', 'Reset password')" />
</div>
<p v-if="message === 'send-success'"
class="update">
@@ -77,11 +67,14 @@
<script>
import axios from '@nextcloud/axios'
-
import { generateUrl } from '@nextcloud/router'
+import LoginButton from './LoginButton.vue'
export default {
name: 'ResetPassword',
+ components: {
+ LoginButton,
+ },
props: {
username: {
type: String,
@@ -91,10 +84,6 @@ export default {
type: String,
required: true,
},
- invertedColors: {
- type: Boolean,
- default: false,
- },
},
data() {
return {
diff --git a/core/src/components/login/UpdatePassword.vue b/core/src/components/login/UpdatePassword.vue
index 95778b993c8..36a63a6254a 100644
--- a/core/src/components/login/UpdatePassword.vue
+++ b/core/src/components/login/UpdatePassword.vue
@@ -49,18 +49,9 @@
</label>
</div>
- <div id="submit-wrapper">
- <input id="submit"
- type="submit"
- class="login primary"
- title=""
- :value="!loading ? t('core', 'Reset password') : t('core', 'Resetting password')">
- <div class="submit-icon"
- :class="{
- 'icon-loading-small': loading && invertedColors,
- 'icon-loading-small-dark': loading && !invertedColors
- }" />
- </div>
+ <LoginButton :loading="loading"
+ :value="t('core', 'Reset password')"
+ :value-loading="t('core', 'Resetting password')" />
<p v-if="error && message" :class="{warning: error}">
{{ message }}
@@ -71,9 +62,13 @@
<script>
import Axios from '@nextcloud/axios'
+import LoginButton from './LoginButton.vue'
export default {
name: 'UpdatePassword',
+ components: {
+ LoginButton,
+ },
props: {
username: {
type: String,
@@ -83,10 +78,6 @@ export default {
type: String,
required: true,
},
- invertedColors: {
- type: Boolean,
- default: false,
- },
},
data() {
return {
diff --git a/core/src/views/Login.vue b/core/src/views/Login.vue
index 70ac8da6f55..e2ca484126c 100644
--- a/core/src/views/Login.vue
+++ b/core/src/views/Login.vue
@@ -30,7 +30,6 @@
:messages="messages"
:errors="errors"
:throttle-delay="throttleDelay"
- :inverted-colors="invertedColors"
:auto-complete-allowed="autoCompleteAllowed"
@submit="loading = true" />
<a v-if="canResetPassword && resetPasswordLink !== ''"
@@ -68,7 +67,6 @@
class="login-additional">
<PasswordLessLoginForm :username.sync="user"
:redirect-url="redirectUrl"
- :inverted-colors="invertedColors"
:auto-complete-allowed="autoCompleteAllowed"
:is-https="isHttps"
:is-localhost="isLocalhost"
@@ -85,14 +83,12 @@
<ResetPassword v-if="resetPassword"
:username.sync="user"
:reset-password-link="resetPasswordLink"
- :inverted-colors="invertedColors"
@abort="resetPassword = false" />
</div>
</div>
<div v-else-if="resetPasswordTarget !== ''">
<UpdatePassword :username.sync="user"
:reset-password-target="resetPasswordTarget"
- :inverted-colors="invertedColors"
@done="passwordResetFinished" />
</div>
</transition>
@@ -150,7 +146,6 @@ export default {
messages: loadState('core', 'loginMessages', []),
redirectUrl: loadState('core', 'loginRedirectUrl', false),
throttleDelay: loadState('core', 'loginThrottleDelay', 0),
- invertedColors: OCA.Theming && OCA.Theming.inverted,
canResetPassword: loadState('core', 'loginCanResetPassword', false),
resetPasswordLink: loadState('core', 'loginResetPasswordLink', ''),
autoCompleteAllowed: loadState('core', 'loginAutocomplete', true),
@@ -165,6 +160,7 @@ export default {
hideLoginForm: loadState('core', 'hideLoginForm', false),
}
},
+
methods: {
passwordResetFinished() {
this.resetPasswordTarget = ''
diff --git a/core/src/views/UnifiedSearch.vue b/core/src/views/UnifiedSearch.vue
index 89f5d2a0bab..2d2d2d0a254 100644
--- a/core/src/views/UnifiedSearch.vue
+++ b/core/src/views/UnifiedSearch.vue
@@ -30,7 +30,7 @@
<!-- Header icon -->
<template #trigger>
<Magnify class="unified-search__trigger"
- :size="20"
+ :size="22/* fit better next to other 20px icons */"
fill-color="var(--color-primary-text)" />
</template>
@@ -81,15 +81,21 @@
<!-- Loading placeholders -->
<SearchResultPlaceholders v-if="isLoading" />
- <EmptyContent v-else-if="isValidQuery" icon="icon-search">
+ <EmptyContent v-else-if="isValidQuery">
<Highlight v-if="triggered" :text="t('core', 'No results for {query}', { query })" :search="query" />
<div v-else>
{{ t('core', 'Press enter to start searching') }}
</div>
+ <template #icon>
+ <Magnify />
+ </template>
</EmptyContent>
- <EmptyContent v-else-if="!isLoading || isShortQuery" icon="icon-search">
+ <EmptyContent v-else-if="!isLoading || isShortQuery">
{{ t('core', 'Start typing to search') }}
+ <template #icon>
+ <Magnify />
+ </template>
<template v-if="isShortQuery" #desc>
{{ n('core',
'Please enter {minSearchLength} character or more to search',
@@ -677,11 +683,6 @@ $input-height: 34px;
$input-padding: 6px;
.unified-search {
- &__trigger {
- width: 20px;
- height: 20px;
- }
-
&__input-wrapper {
position: sticky;
// above search results
diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php
index b97181d9457..980d9abb7c4 100644
--- a/core/templates/layout.guest.php
+++ b/core/templates/layout.guest.php
@@ -36,10 +36,6 @@
<h1 class="hidden-visually">
<?php p($theme->getName()); ?>
</h1>
- <?php if (\OC::$server->getConfig()->getSystemValue('installed', false)
- && \OC::$server->getConfig()->getAppValue('theming', 'logoMime', false)): ?>
- <img src="<?php p($theme->getLogo()); ?>"/>
- <?php endif; ?>
</div>
</div>
</header>
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 2b84c89fca6..4efe072a5bb 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -40,7 +40,9 @@ $getUserAvatar = static function (int $size) use ($_): string {
<?php emit_script_loading_tags($_); ?>
<?php print_unescaped($_['headers']); ?>
</head>
- <body id="<?php p($_['bodyid']);?>">
+ <body id="<?php p($_['bodyid']);?>" <?php foreach ($_['enabledThemes'] as $themeId) {
+ p("data-theme-$themeId ");
+ }?>>
<?php include 'layout.noscript.warning.php'; ?>
<?php foreach ($_['initialStates'] as $app => $initialState) { ?>
@@ -64,7 +66,7 @@ $getUserAvatar = static function (int $size) use ($_): string {
</div>
</a>
- <ul id="appmenu" <?php if ($_['themingInvertMenu']) { ?>class="inverted"<?php } ?>>
+ <ul id="appmenu">
<?php foreach ($_['navigation'] as $entry): ?>
<li data-id="<?php p($entry['id']); ?>" class="hidden" tabindex="-1">
<a href="<?php print_unescaped($entry['href']); ?>"
@@ -73,13 +75,12 @@ $getUserAvatar = static function (int $size) use ($_): string {
aria-label="<?php p($entry['name']); ?>">
<svg width="24" height="20" viewBox="0 0 24 20" alt=""<?php if ($entry['unread'] !== 0) { ?> class="has-unread"<?php } ?>>
<defs>
- <?php if ($_['themingInvertMenu']) { ?><filter id="invertMenuMain-<?php p($entry['id']); ?>"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter><?php } ?>
<mask id="hole">
<rect width="100%" height="100%" fill="white"/>
<circle r="4.5" cx="21" cy="3" fill="black"/>
</mask>
</defs>
- <image x="2" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet"<?php if ($_['themingInvertMenu']) { ?> filter="url(#invertMenuMain-<?php p($entry['id']); ?>)"<?php } ?> xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" style="<?php if ($entry['unread'] !== 0) { ?>mask: url("#hole");<?php } ?>" class="app-icon"></image>
+ <image x="2" y="0" width="20" height="20" preserveAspectRatio="xMinYMin meet" xlink:href="<?php print_unescaped($entry['icon'] . '?v=' . $_['versionHash']); ?>" style="<?php if ($entry['unread'] !== 0) { ?>mask: url("#hole");<?php } ?>" class="app-icon"></image>
<circle class="app-icon-notification" r="3" cx="21" cy="3" fill="red"/>
</svg>
<div class="unread-counter" aria-hidden="true"><?php p($entry['unread']); ?></div>