update with develop

This commit is contained in:
Shpuld Shpuldson 2020-03-17 14:45:05 +02:00
commit 5292832d6d
19 changed files with 518 additions and 38 deletions

View file

@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
## [2.0.0] - 2020-02-28
### Added ### Added
- Tons of color slots including ones for hover/pressed/toggled buttons - Tons of color slots including ones for hover/pressed/toggled buttons
- Experimental `--variable[,mod]` syntax support for color slots in themes. the `mod` makes color brighter/darker depending on background color (makes darker color brighter/darker depending on background color) - Experimental `--variable[,mod]` syntax support for color slots in themes. the `mod` makes color brighter/darker depending on background color (makes darker color brighter/darker depending on background color)
@ -16,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Emoji reactions for statuses - Emoji reactions for statuses
- MRF keyword policy disclosure - MRF keyword policy disclosure
### Changed ### Changed
- Updated Pleroma default themes
- theme engine update to 3 (themes v2.1 introduction) - theme engine update to 3 (themes v2.1 introduction)
- massive internal changes in theme engine - slowly away from "generate things separately with spaghetti code" towards "feed all data into single 'generateTheme' function and declare slot inheritance and all in a separate file" - massive internal changes in theme engine - slowly away from "generate things separately with spaghetti code" towards "feed all data into single 'generateTheme' function and declare slot inheritance and all in a separate file"
- Breezy theme updates to make it closer to actual Breeze in some aspects - Breezy theme updates to make it closer to actual Breeze in some aspects
@ -25,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Notifications column now cleans itself up to optimize performance when tab is left open for a long time - Notifications column now cleans itself up to optimize performance when tab is left open for a long time
- 403 messaging - 403 messaging
### Fixed ### Fixed
- Fixed loader-spinner not disappearing when a status preview fails to load
- anon viewers won't get theme data saved to local storage, so admin changing default theme will have an effect for users coming back to instance. - anon viewers won't get theme data saved to local storage, so admin changing default theme will have an effect for users coming back to instance.
- Single notifications left unread when hitting read on another device/tab - Single notifications left unread when hitting read on another device/tab
- Registration fixed - Registration fixed

View file

@ -81,6 +81,7 @@
.follow-text, .move-text { .follow-text, .move-text {
padding: 0.5em 0; padding: 0.5em 0;
overflow-wrap: break-word;
} }
.status-el { .status-el {

View file

@ -187,6 +187,9 @@
class="form-control" class="form-control"
type="text" type="text"
autocomplete="off" autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
> >
</template> </template>
</div> </div>

View file

@ -5,6 +5,11 @@ const StatusPopover = {
props: [ props: [
'statusId' 'statusId'
], ],
data () {
return {
error: false
}
},
computed: { computed: {
status () { status () {
return find(this.$store.state.statuses.allStatuses, { id: this.statusId }) return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
@ -18,6 +23,8 @@ const StatusPopover = {
enter () { enter () {
if (!this.status) { if (!this.status) {
this.$store.dispatch('fetchStatus', this.statusId) this.$store.dispatch('fetchStatus', this.statusId)
.then(data => (this.error = false))
.catch(e => (this.error = true))
} }
} }
} }

View file

@ -17,9 +17,15 @@
:statusoid="status" :statusoid="status"
:compact="true" :compact="true"
/> />
<div
v-else-if="error"
class="status-preview-no-content faint"
>
{{ $t('status.status_unavailable') }}
</div>
<div <div
v-else v-else
class="status-preview-loading" class="status-preview-no-content"
> >
<i class="icon-spin4 animate-spin" /> <i class="icon-spin4 animate-spin" />
</div> </div>
@ -50,7 +56,7 @@
border: none; border: none;
} }
.status-preview-loading { .status-preview-no-content {
padding: 1em; padding: 1em;
text-align: center; text-align: center;

View file

@ -4,7 +4,6 @@ import ProgressButton from '../progress_button/progress_button.vue'
import FollowButton from '../follow_button/follow_button.vue' import FollowButton from '../follow_button/follow_button.vue'
import ModerationTools from '../moderation_tools/moderation_tools.vue' import ModerationTools from '../moderation_tools/moderation_tools.vue'
import AccountActions from '../account_actions/account_actions.vue' import AccountActions from '../account_actions/account_actions.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
@ -30,21 +29,11 @@ export default {
}] }]
}, },
style () { style () {
const color = this.$store.getters.mergedConfig.customTheme.colors return {
? this.$store.getters.mergedConfig.customTheme.colors.bg // v2 backgroundImage: [
: this.$store.getters.mergedConfig.colors.bg // v1 `linear-gradient(to bottom, var(--profileTint), var(--profileTint))`,
`url(${this.user.cover_photo})`
if (color) { ].join(', ')
const rgb = (typeof color === 'string') ? hex2rgb(color) : color
const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)`
return {
backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`,
backgroundImage: [
`linear-gradient(to bottom, ${tintColor}, ${tintColor})`,
`url(${this.user.cover_photo})`
].join(', ')
}
} }
}, },
isOtherUser () { isOtherUser () {

View file

@ -286,6 +286,7 @@
mask-size: 100% 60%; mask-size: 100% 60%;
border-top-left-radius: calc(var(--panelRadius) - 1px); border-top-left-radius: calc(var(--panelRadius) - 1px);
border-top-right-radius: calc(var(--panelRadius) - 1px); border-top-right-radius: calc(var(--panelRadius) - 1px);
background-color: var(--profileBg);
&.hide-bio { &.hide-bio {
mask-size: 100% 40px; mask-size: 100% 40px;

View file

@ -616,7 +616,8 @@
"reply_to": "Reply to", "reply_to": "Reply to",
"replies_list": "Replies:", "replies_list": "Replies:",
"mute_conversation": "Mute conversation", "mute_conversation": "Mute conversation",
"unmute_conversation": "Unmute conversation" "unmute_conversation": "Unmute conversation",
"status_unavailable": "Status unavailable"
}, },
"user_card": { "user_card": {
"approve": "Approve", "approve": "Approve",

View file

@ -290,7 +290,8 @@
"reply_to": "Vastaus", "reply_to": "Vastaus",
"replies_list": "Vastaukset:", "replies_list": "Vastaukset:",
"mute_conversation": "Hiljennä keskustelu", "mute_conversation": "Hiljennä keskustelu",
"unmute_conversation": "Poista hiljennys" "unmute_conversation": "Poista hiljennys",
"status_unavailable": "Viesti ei saatavissa"
}, },
"user_card": { "user_card": {
"approve": "Hyväksy", "approve": "Hyväksy",

View file

@ -103,6 +103,7 @@ const config = {
setPreset(value) setPreset(value)
break break
case 'customTheme': case 'customTheme':
case 'customThemeSource':
applyTheme(value) applyTheme(value)
} }
} }

View file

@ -1,5 +1,6 @@
import { set } from 'vue' import { set } from 'vue'
import { getPreset, applyTheme } from '../services/style_setter/style_setter.js' import { getPreset, applyTheme } from '../services/style_setter/style_setter.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { instanceDefaultProperties } from './config.js' import { instanceDefaultProperties } from './config.js'
const defaultState = { const defaultState = {
@ -160,7 +161,14 @@ const instance = {
// No need to apply theme if there's user theme already // No need to apply theme if there's user theme already
const { customTheme } = rootState.config const { customTheme } = rootState.config
if (customTheme) return if (customTheme) return
applyTheme(themeData.theme)
// New theme presets don't have 'theme' property, they use 'source'
const themeSource = themeData.source
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) {
applyTheme(themeSource)
} else {
applyTheme(themeData.theme)
}
}) })
}, },
fetchEmoji ({ dispatch, state }) { fetchEmoji ({ dispatch, state }) {

View file

@ -616,7 +616,7 @@ const statuses = {
commit('setNotificationsSilence', { value }) commit('setNotificationsSilence', { value })
}, },
fetchStatus ({ rootState, dispatch }, id) { fetchStatus ({ rootState, dispatch }, id) {
rootState.api.backendInteractor.fetchStatus({ id }) return rootState.api.backendInteractor.fetchStatus({ id })
.then((status) => dispatch('addNewStatuses', { statuses: [status] })) .then((status) => dispatch('addNewStatuses', { statuses: [status] }))
}, },
deleteStatus ({ rootState, commit }, status) { deleteStatus ({ rootState, commit }, status) {

View file

@ -374,9 +374,9 @@ const users = {
return rootState.api.backendInteractor.unsubscribeUser({ id }) return rootState.api.backendInteractor.unsubscribeUser({ id })
.then((relationship) => commit('updateUserRelationship', [relationship])) .then((relationship) => commit('updateUserRelationship', [relationship]))
}, },
toggleActivationStatus ({ rootState, commit }, user) { toggleActivationStatus ({ rootState, commit }, { user }) {
const api = user.deactivated ? rootState.api.backendInteractor.activateUser : rootState.api.backendInteractor.deactivateUser const api = user.deactivated ? rootState.api.backendInteractor.activateUser : rootState.api.backendInteractor.deactivateUser
api(user) api({ user })
.then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated })) .then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated }))
}, },
registerPushNotifications (store) { registerPushNotifications (store) {

View file

@ -402,8 +402,8 @@ const fetchStatus = ({ id, credentials }) => {
.then((data) => parseStatus(data)) .then((data) => parseStatus(data))
} }
const tagUser = ({ tag, credentials, ...options }) => { const tagUser = ({ tag, credentials, user }) => {
const screenName = options.screen_name const screenName = user.screen_name
const form = { const form = {
nicknames: [screenName], nicknames: [screenName],
tags: [tag] tags: [tag]
@ -419,8 +419,8 @@ const tagUser = ({ tag, credentials, ...options }) => {
}) })
} }
const untagUser = ({ tag, credentials, ...options }) => { const untagUser = ({ tag, credentials, user }) => {
const screenName = options.screen_name const screenName = user.screen_name
const body = { const body = {
nicknames: [screenName], nicknames: [screenName],
tags: [tag] tags: [tag]
@ -436,7 +436,7 @@ const untagUser = ({ tag, credentials, ...options }) => {
}) })
} }
const addRight = ({ right, credentials, ...user }) => { const addRight = ({ right, credentials, user }) => {
const screenName = user.screen_name const screenName = user.screen_name
return fetch(PERMISSION_GROUP_URL(screenName, right), { return fetch(PERMISSION_GROUP_URL(screenName, right), {
@ -446,7 +446,7 @@ const addRight = ({ right, credentials, ...user }) => {
}) })
} }
const deleteRight = ({ right, credentials, ...user }) => { const deleteRight = ({ right, credentials, user }) => {
const screenName = user.screen_name const screenName = user.screen_name
return fetch(PERMISSION_GROUP_URL(screenName, right), { return fetch(PERMISSION_GROUP_URL(screenName, right), {
@ -478,7 +478,7 @@ const deactivateUser = ({ credentials, user: { screen_name: nickname } }) => {
}).then(response => get(response, 'users.0')) }).then(response => get(response, 'users.0'))
} }
const deleteUser = ({ credentials, ...user }) => { const deleteUser = ({ credentials, user }) => {
const screenName = user.screen_name const screenName = user.screen_name
const headers = authHeaders(credentials) const headers = authHeaders(credentials)

View file

@ -8,6 +8,7 @@ export const LAYERS = {
undelay: null, // root undelay: null, // root
topBar: null, // no transparency support topBar: null, // no transparency support
badge: null, // no transparency support badge: null, // no transparency support
profileTint: null, // doesn't matter
fg: null, fg: null,
bg: 'underlay', bg: 'underlay',
highlight: 'bg', highlight: 'bg',
@ -29,6 +30,7 @@ export const LAYERS = {
* this allows redefining it to something else * this allows redefining it to something else
*/ */
export const DEFAULT_OPACITY = { export const DEFAULT_OPACITY = {
profileTint: 0.5,
alert: 0.5, alert: 0.5,
input: 0.5, input: 0.5,
faint: 0.5, faint: 0.5,
@ -119,6 +121,20 @@ export const SLOT_INHERITANCE = {
cGreen: '#00FF00', cGreen: '#00FF00',
cOrange: '#E3FF00', cOrange: '#E3FF00',
profileBg: {
depends: ['bg'],
color: (mod, bg) => ({
r: Math.floor(bg.r * 0.53),
g: Math.floor(bg.g * 0.56),
b: Math.floor(bg.b * 0.59)
})
},
profileTint: {
depends: ['bg'],
layer: 'profileTint',
opacity: 'profileTint'
},
highlight: { highlight: {
depends: ['bg'], depends: ['bg'],
color: (mod, bg) => brightness(5 * mod, bg).rgb color: (mod, bg) => brightness(5 * mod, bg).rgb

View file

@ -350,15 +350,47 @@ export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({
if (!outputColor) { if (!outputColor) {
throw new Error('Couldn\'t generate color for ' + key) throw new Error('Couldn\'t generate color for ' + key)
} }
const opacitySlot = getOpacitySlot(key)
if (opacitySlot && outputColor.a === undefined) { const opacitySlot = value.opacity || getOpacitySlot(key)
const ownOpacitySlot = value.opacity
if (ownOpacitySlot === null) {
outputColor.a = 1
} else if (sourceColor === 'transparent') {
outputColor.a = 0
} else {
const opacityOverriden = ownOpacitySlot && sourceOpacity[opacitySlot] !== undefined
const dependencySlot = deps[0] const dependencySlot = deps[0]
if (dependencySlot && colors[dependencySlot] === 'transparent') { const dependencyColor = dependencySlot && colors[dependencySlot]
outputColor.a = 0
if (!ownOpacitySlot && dependencyColor && !value.textColor && ownOpacitySlot !== null) {
// Inheriting color from dependency (weird, i know)
// except if it's a text color or opacity slot is set to 'null'
outputColor.a = dependencyColor.a
} else if (!dependencyColor && !opacitySlot) {
// Remove any alpha channel if no dependency and no opacitySlot found
delete outputColor.a
} else { } else {
outputColor.a = Number(sourceOpacity[opacitySlot]) || OPACITIES[opacitySlot].defaultValue || 1 // Otherwise try to assign opacity
if (dependencyColor && dependencyColor.a === 0) {
// transparent dependency shall make dependents transparent too
outputColor.a = 0
} else {
// Otherwise check if opacity is overriden and use that or default value instead
outputColor.a = Number(
opacityOverriden
? sourceOpacity[opacitySlot]
: (OPACITIES[opacitySlot] || {}).defaultValue
)
}
} }
} }
if (Number.isNaN(outputColor.a) || outputColor.a === undefined) {
outputColor.a = 1
}
if (opacitySlot) { if (opacitySlot) {
return { return {
colors: { ...colors, [key]: outputColor }, colors: { ...colors, [key]: outputColor },

View file

@ -1,6 +1,6 @@
{ {
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "pleroma-dark": "/static/themes/pleroma-dark.json",
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "pleroma-light": "/static/themes/pleroma-light.json",
"pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"], "pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"],
"classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"], "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],

View file

@ -0,0 +1,191 @@
{
"_pleroma_theme_version": 2,
"name": "Pleroma Dark",
"source": {
"themeEngineVersion": 3,
"fonts": {},
"shadows": {
"buttonHover": [
{
"x": 0,
"y": 0,
"blur": "1",
"spread": "2",
"color": "#b9b9ba",
"alpha": "0.4",
"inset": true
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"buttonPressed": [
{
"x": 0,
"y": 0,
"blur": 4,
"spread": 0,
"color": "#000000",
"alpha": 1,
"inset": true
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": 0,
"blur": "2",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": 1
}
],
"panelHeader": [
{
"x": 0,
"y": "1",
"blur": "3",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": "0.4"
},
{
"x": "0",
"y": "1",
"blur": "0",
"spread": 0,
"inset": true,
"color": "#ffffff",
"alpha": "0.2"
}
],
"panel": [
{
"x": "0",
"y": "0",
"blur": "3",
"spread": 0,
"color": "#000000",
"alpha": "0.5"
},
{
"x": "0",
"y": "4",
"blur": "6",
"spread": "3",
"inset": false,
"color": "#000000",
"alpha": "0.3"
}
],
"button": [
{
"x": 0,
"y": 0,
"blur": 2,
"spread": 0,
"color": "#000000",
"alpha": 1
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"topBar": [
{
"x": 0,
"y": "1",
"blur": 4,
"spread": 0,
"color": "#000000",
"alpha": "0.4"
},
{
"x": 0,
"y": "2",
"blur": "7",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": "0.3"
}
]
},
"opacity": {
"underlay": "0.6"
},
"colors": {
"bg": "#0f161e",
"fg": "#151e2b",
"text": "#b9b9ba",
"underlay": "#090e14",
"accent": "#e2b188",
"cBlue": "#81beea",
"cRed": "#d31014",
"cGreen": "#5dc94a",
"cOrange": "#ffc459",
"border": "--fg,3",
"topBarText": "--text,-9.75",
"topBarLink": "--topBarText",
"btnToggled": "--accent,-24.2",
"alertErrorText": "--text,21.2",
"badgeNotification": "#e15932",
"badgeNotificationText": "#ffffff"
},
"radii": {
"btn": "3",
"input": "3",
"panel": "3",
"avatar": "3",
"attachment": "3"
}
}
}

View file

@ -0,0 +1,219 @@
{
"_pleroma_theme_version": 2,
"name": "Pleroma Light",
"source": {
"themeEngineVersion": 3,
"fonts": {},
"shadows": {
"button": [
{
"x": 0,
"y": 0,
"blur": 2,
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": "0.5",
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"buttonHover": [
{
"x": 0,
"y": 0,
"blur": "2",
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": 0,
"y": "0",
"blur": "1",
"spread": "2",
"color": "#ffc39f",
"alpha": "1",
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"input": [
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": 0,
"blur": "2",
"inset": true,
"spread": 0,
"color": "#000000",
"alpha": "0.15"
}
],
"panel": [
{
"x": "0",
"y": 1,
"blur": "3",
"spread": 0,
"color": "#000000",
"alpha": "0.5"
},
{
"x": "0",
"y": "3",
"blur": "6",
"spread": "1",
"inset": false,
"color": "#000000",
"alpha": "0.2"
}
],
"panelHeader": [
{
"x": 0,
"y": "1",
"blur": 0,
"spread": 0,
"inset": true,
"color": "#ffffff",
"alpha": "0.5"
},
{
"x": 0,
"y": "1",
"blur": "3",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": "0.3"
}
],
"buttonPressed": [
{
"x": 0,
"y": 0,
"blur": 4,
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": 0,
"y": 1,
"blur": "1",
"spread": "2",
"color": "#000000",
"alpha": "0.3",
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
}
],
"popup": [
{
"x": "1",
"y": "2",
"blur": "2",
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": "1",
"y": "3",
"blur": "7",
"spread": "0",
"inset": false,
"color": "#000000",
"alpha": "0.2"
}
],
"avatarStatus": [
{
"x": 0,
"y": "1",
"blur": "4",
"spread": "0",
"inset": false,
"color": "#000000",
"alpha": "0.2"
}
]
},
"opacity": {
"underlay": "0.4"
},
"colors": {
"bg": "#f2f6f9",
"fg": "#d6dfed",
"text": "#304055",
"underlay": "#5d6086",
"accent": "#f55b1b",
"cBlue": "#0095ff",
"cRed": "#d31014",
"cGreen": "#0fa00f",
"cOrange": "#ffa500",
"border": "#d8e6f9",
"topBarText": "#304055",
"topBarLink": "--topBarText",
"btnToggled": "--accent,-24.2",
"input": "#dee3ed",
"badgeNotification": "#e83802"
},
"radii": {
"btn": "3",
"input": "3",
"panel": "3",
"avatar": "3",
"attachment": "3"
}
}
}