diff --git a/CHANGELOG.md b/CHANGELOG.md
index e77334b03..24c193b98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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/).
## [Unreleased]
+
+## [2.0.0] - 2020-02-28
### Added
- 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)
@@ -16,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Emoji reactions for statuses
- MRF keyword policy disclosure
### Changed
+- Updated Pleroma default themes
- 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"
- 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
- 403 messaging
### 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.
- Single notifications left unread when hitting read on another device/tab
- Registration fixed
diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss
index f5b133227..a8f4430f2 100644
--- a/src/components/notifications/notifications.scss
+++ b/src/components/notifications/notifications.scss
@@ -81,6 +81,7 @@
.follow-text, .move-text {
padding: 0.5em 0;
+ overflow-wrap: break-word;
}
.status-el {
diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue
index fdbda0077..a83ca1e52 100644
--- a/src/components/registration/registration.vue
+++ b/src/components/registration/registration.vue
@@ -187,6 +187,9 @@
class="form-control"
type="text"
autocomplete="off"
+ autocorrect="off"
+ autocapitalize="off"
+ spellcheck="false"
>
diff --git a/src/components/status_popover/status_popover.js b/src/components/status_popover/status_popover.js
index cb55f67e9..159132a9e 100644
--- a/src/components/status_popover/status_popover.js
+++ b/src/components/status_popover/status_popover.js
@@ -5,6 +5,11 @@ const StatusPopover = {
props: [
'statusId'
],
+ data () {
+ return {
+ error: false
+ }
+ },
computed: {
status () {
return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
@@ -18,6 +23,8 @@ const StatusPopover = {
enter () {
if (!this.status) {
this.$store.dispatch('fetchStatus', this.statusId)
+ .then(data => (this.error = false))
+ .catch(e => (this.error = true))
}
}
}
diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue
index 11f6cb5a0..f5948207f 100644
--- a/src/components/status_popover/status_popover.vue
+++ b/src/components/status_popover/status_popover.vue
@@ -17,9 +17,15 @@
:statusoid="status"
:compact="true"
/>
+
+ {{ $t('status.status_unavailable') }}
+
@@ -50,7 +56,7 @@
border: none;
}
- .status-preview-loading {
+ .status-preview-no-content {
padding: 1em;
text-align: center;
diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js
index 2f6499105..1cdbd3fa7 100644
--- a/src/components/user_card/user_card.js
+++ b/src/components/user_card/user_card.js
@@ -4,7 +4,6 @@ import ProgressButton from '../progress_button/progress_button.vue'
import FollowButton from '../follow_button/follow_button.vue'
import ModerationTools from '../moderation_tools/moderation_tools.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 { mapGetters } from 'vuex'
@@ -30,21 +29,11 @@ export default {
}]
},
style () {
- const color = this.$store.getters.mergedConfig.customTheme.colors
- ? this.$store.getters.mergedConfig.customTheme.colors.bg // v2
- : this.$store.getters.mergedConfig.colors.bg // v1
-
- if (color) {
- 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(', ')
- }
+ return {
+ backgroundImage: [
+ `linear-gradient(to bottom, var(--profileTint), var(--profileTint))`,
+ `url(${this.user.cover_photo})`
+ ].join(', ')
}
},
isOtherUser () {
diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue
index 3988ff1c4..4ee040e8b 100644
--- a/src/components/user_card/user_card.vue
+++ b/src/components/user_card/user_card.vue
@@ -286,6 +286,7 @@
mask-size: 100% 60%;
border-top-left-radius: calc(var(--panelRadius) - 1px);
border-top-right-radius: calc(var(--panelRadius) - 1px);
+ background-color: var(--profileBg);
&.hide-bio {
mask-size: 100% 40px;
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 21102f8d0..e9d39467b 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -616,7 +616,8 @@
"reply_to": "Reply to",
"replies_list": "Replies:",
"mute_conversation": "Mute conversation",
- "unmute_conversation": "Unmute conversation"
+ "unmute_conversation": "Unmute conversation",
+ "status_unavailable": "Status unavailable"
},
"user_card": {
"approve": "Approve",
diff --git a/src/i18n/fi.json b/src/i18n/fi.json
index 9d079a88d..ba3c7eac0 100644
--- a/src/i18n/fi.json
+++ b/src/i18n/fi.json
@@ -290,7 +290,8 @@
"reply_to": "Vastaus",
"replies_list": "Vastaukset:",
"mute_conversation": "Hiljennä keskustelu",
- "unmute_conversation": "Poista hiljennys"
+ "unmute_conversation": "Poista hiljennys",
+ "status_unavailable": "Viesti ei saatavissa"
},
"user_card": {
"approve": "Hyväksy",
diff --git a/src/modules/config.js b/src/modules/config.js
index 466839079..6f26c89ef 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -103,6 +103,7 @@ const config = {
setPreset(value)
break
case 'customTheme':
+ case 'customThemeSource':
applyTheme(value)
}
}
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 0b59d38a5..e1206275e 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -1,5 +1,6 @@
import { set } from 'vue'
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'
const defaultState = {
@@ -160,7 +161,14 @@ const instance = {
// No need to apply theme if there's user theme already
const { customTheme } = rootState.config
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 }) {
diff --git a/src/modules/statuses.js b/src/modules/statuses.js
index 25b62ac7f..f1b7dcbda 100644
--- a/src/modules/statuses.js
+++ b/src/modules/statuses.js
@@ -616,7 +616,7 @@ const statuses = {
commit('setNotificationsSilence', { value })
},
fetchStatus ({ rootState, dispatch }, id) {
- rootState.api.backendInteractor.fetchStatus({ id })
+ return rootState.api.backendInteractor.fetchStatus({ id })
.then((status) => dispatch('addNewStatuses', { statuses: [status] }))
},
deleteStatus ({ rootState, commit }, status) {
diff --git a/src/modules/users.js b/src/modules/users.js
index ce3e595d2..df133be08 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -374,9 +374,9 @@ const users = {
return rootState.api.backendInteractor.unsubscribeUser({ id })
.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
- api(user)
+ api({ user })
.then(({ deactivated }) => commit('updateActivationStatus', { user, deactivated }))
},
registerPushNotifications (store) {
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index ec5b74e13..47559df13 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -402,8 +402,8 @@ const fetchStatus = ({ id, credentials }) => {
.then((data) => parseStatus(data))
}
-const tagUser = ({ tag, credentials, ...options }) => {
- const screenName = options.screen_name
+const tagUser = ({ tag, credentials, user }) => {
+ const screenName = user.screen_name
const form = {
nicknames: [screenName],
tags: [tag]
@@ -419,8 +419,8 @@ const tagUser = ({ tag, credentials, ...options }) => {
})
}
-const untagUser = ({ tag, credentials, ...options }) => {
- const screenName = options.screen_name
+const untagUser = ({ tag, credentials, user }) => {
+ const screenName = user.screen_name
const body = {
nicknames: [screenName],
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
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
return fetch(PERMISSION_GROUP_URL(screenName, right), {
@@ -478,7 +478,7 @@ const deactivateUser = ({ credentials, user: { screen_name: nickname } }) => {
}).then(response => get(response, 'users.0'))
}
-const deleteUser = ({ credentials, ...user }) => {
+const deleteUser = ({ credentials, user }) => {
const screenName = user.screen_name
const headers = authHeaders(credentials)
diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js
index 33a2ed574..0c1fe5433 100644
--- a/src/services/theme_data/pleromafe.js
+++ b/src/services/theme_data/pleromafe.js
@@ -8,6 +8,7 @@ export const LAYERS = {
undelay: null, // root
topBar: null, // no transparency support
badge: null, // no transparency support
+ profileTint: null, // doesn't matter
fg: null,
bg: 'underlay',
highlight: 'bg',
@@ -29,6 +30,7 @@ export const LAYERS = {
* this allows redefining it to something else
*/
export const DEFAULT_OPACITY = {
+ profileTint: 0.5,
alert: 0.5,
input: 0.5,
faint: 0.5,
@@ -119,6 +121,20 @@ export const SLOT_INHERITANCE = {
cGreen: '#00FF00',
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: {
depends: ['bg'],
color: (mod, bg) => brightness(5 * mod, bg).rgb
diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js
index 757687954..dd87e3cff 100644
--- a/src/services/theme_data/theme_data.service.js
+++ b/src/services/theme_data/theme_data.service.js
@@ -350,15 +350,47 @@ export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({
if (!outputColor) {
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]
- if (dependencySlot && colors[dependencySlot] === 'transparent') {
- outputColor.a = 0
+ const dependencyColor = dependencySlot && colors[dependencySlot]
+
+ 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 {
- 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) {
return {
colors: { ...colors, [key]: outputColor },
diff --git a/static/styles.json b/static/styles.json
index 3349a8377..23f57c65e 100644
--- a/static/styles.json
+++ b/static/styles.json
@@ -1,6 +1,6 @@
{
- "pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
- "pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
+ "pleroma-dark": "/static/themes/pleroma-dark.json",
+ "pleroma-light": "/static/themes/pleroma-light.json",
"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" ],
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
diff --git a/static/themes/pleroma-dark.json b/static/themes/pleroma-dark.json
new file mode 100644
index 000000000..2703fba11
--- /dev/null
+++ b/static/themes/pleroma-dark.json
@@ -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"
+ }
+ }
+}
diff --git a/static/themes/pleroma-light.json b/static/themes/pleroma-light.json
new file mode 100644
index 000000000..05fc300aa
--- /dev/null
+++ b/static/themes/pleroma-light.json
@@ -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"
+ }
+ }
+}