diff options
Diffstat (limited to 'src/components')
32 files changed, 1251 insertions, 450 deletions
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue index 0748b2f0..a7e217c1 100644 --- a/src/components/attachment/attachment.vue +++ b/src/components/attachment/attachment.vue @@ -130,6 +130,8 @@ .placeholder { margin-right: 8px; margin-bottom: 4px; + color: $fallback--link; + color: var(--postLink, $fallback--link); } .nsfw-placeholder { diff --git a/src/components/autosuggest/autosuggest.vue b/src/components/autosuggest/autosuggest.vue index 1f86e996..f283ab82 100644 --- a/src/components/autosuggest/autosuggest.vue +++ b/src/components/autosuggest/autosuggest.vue @@ -40,8 +40,8 @@ top: 100%; right: 0; max-height: 400px; - background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); border-style: solid; border-width: 1px; border-color: $fallback--border; diff --git a/src/components/checkbox/checkbox.vue b/src/components/checkbox/checkbox.vue index 1113f81d..03375b2f 100644 --- a/src/components/checkbox/checkbox.vue +++ b/src/components/checkbox/checkbox.vue @@ -87,13 +87,13 @@ export default { &:checked + .checkbox-indicator::before { color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--inputText, $fallback--text); } &:indeterminate + .checkbox-indicator::before { content: '–'; color: $fallback--text; - color: var(--text, $fallback--text); + color: var(--inputText, $fallback--text); } } diff --git a/src/components/color_input/color_input.scss b/src/components/color_input/color_input.scss new file mode 100644 index 00000000..8e9923cf --- /dev/null +++ b/src/components/color_input/color_input.scss @@ -0,0 +1,68 @@ +@import '../../_variables.scss'; + +.color-input { + display: inline-flex; + + &-field.input { + display: inline-flex; + flex: 0 0 0; + max-width: 9em; + align-items: stretch; + padding: .2em 8px; + + input { + background: none; + color: $fallback--lightText; + color: var(--inputText, $fallback--lightText); + border: none; + padding: 0; + margin: 0; + + &.textColor { + flex: 1 0 3em; + min-width: 3em; + padding: 0; + } + + &.nativeColor { + flex: 0 0 2em; + min-width: 2em; + align-self: center; + height: 100%; + } + } + .computedIndicator, + .transparentIndicator { + flex: 0 0 2em; + min-width: 2em; + align-self: center; + height: 100%; + } + .transparentIndicator { + // forgot to install counter-strike source, ooops + background-color: #FF00FF; + position: relative; + &::before, &::after { + display: block; + content: ''; + background-color: #000000; + position: absolute; + height: 50%; + width: 50%; + } + &::after { + top: 0; + left: 0; + } + &::before { + bottom: 0; + right: 0; + } + } + } + + .label { + flex: 1 1 auto; + } + +} diff --git a/src/components/color_input/color_input.vue b/src/components/color_input/color_input.vue index 9db62e81..8fb16113 100644 --- a/src/components/color_input/color_input.vue +++ b/src/components/color_input/color_input.vue @@ -1,6 +1,6 @@ <template> <div - class="color-control style-control" + class="color-input style-control" :class="{ disabled: !present || disabled }" > <label @@ -9,46 +9,100 @@ > {{ label }} </label> - <input - v-if="typeof fallback !== 'undefined'" - :id="name + '-o'" - class="opt exlcude-disabled" - type="checkbox" + <Checkbox + v-if="typeof fallback !== 'undefined' && showOptionalTickbox" :checked="present" - @input="$emit('input', typeof value === 'undefined' ? fallback : undefined)" - > - <label - v-if="typeof fallback !== 'undefined'" - class="opt-l" - :for="name + '-o'" + :disabled="disabled" + class="opt" + @change="$emit('input', typeof value === 'undefined' ? fallback : undefined)" /> - <input - :id="name" - class="color-input" - type="color" - :value="value || fallback" - :disabled="!present || disabled" - @input="$emit('input', $event.target.value)" - > - <input - :id="name + '-t'" - class="text-input" - type="text" - :value="value || fallback" - :disabled="!present || disabled" - @input="$emit('input', $event.target.value)" - > + <div class="input color-input-field"> + <input + :id="name + '-t'" + class="textColor unstyled" + type="text" + :value="value || fallback" + :disabled="!present || disabled" + @input="$emit('input', $event.target.value)" + > + <input + v-if="validColor" + :id="name" + class="nativeColor unstyled" + type="color" + :value="value || fallback" + :disabled="!present || disabled" + @input="$emit('input', $event.target.value)" + > + <div + v-if="transparentColor" + class="transparentIndicator" + /> + <div + v-if="computedColor" + class="computedIndicator" + :style="{backgroundColor: fallback}" + /> + </div> </div> </template> - +<style lang="scss" src="./color_input.scss"></style> <script> +import Checkbox from '../checkbox/checkbox.vue' +import { hex2rgb } from '../../services/color_convert/color_convert.js' export default { - props: [ - 'name', 'label', 'value', 'fallback', 'disabled' - ], + components: { + Checkbox + }, + props: { + // Name of color, used for identifying + name: { + required: true, + type: String + }, + // Readable label + label: { + required: true, + type: String + }, + // Color value, should be required but vue cannot tell the difference + // between "property missing" and "property set to undefined" + value: { + required: false, + type: String, + default: undefined + }, + // Color fallback to use when value is not defeind + fallback: { + required: false, + type: String, + default: undefined + }, + // Disable the control + disabled: { + required: false, + type: Boolean, + default: false + }, + // Show "optional" tickbox, for when value might become mandatory + showOptionalTickbox: { + required: false, + type: Boolean, + default: true + } + }, computed: { present () { return typeof this.value !== 'undefined' + }, + validColor () { + return hex2rgb(this.value || this.fallback) + }, + transparentColor () { + return this.value === 'transparent' + }, + computedColor () { + return this.value && this.value.startsWith('--') } } } diff --git a/src/components/contrast_ratio/contrast_ratio.vue b/src/components/contrast_ratio/contrast_ratio.vue index 15a450a2..ba92bc17 100644 --- a/src/components/contrast_ratio/contrast_ratio.vue +++ b/src/components/contrast_ratio/contrast_ratio.vue @@ -37,9 +37,17 @@ <script> export default { - props: [ - 'large', 'contrast' - ], + props: { + large: { + required: false + }, + // TODO: Make theme switcher compute theme initially so that contrast + // component won't be called without contrast data + contrast: { + required: false, + type: Object + } + }, computed: { hint () { const levelVal = this.contrast.aaa ? 'aaa' : (this.contrast.aa ? 'aa' : 'bad') diff --git a/src/components/dialog_modal/dialog_modal.vue b/src/components/dialog_modal/dialog_modal.vue index 55d7a7d2..3241ce3e 100644 --- a/src/components/dialog_modal/dialog_modal.vue +++ b/src/components/dialog_modal/dialog_modal.vue @@ -75,18 +75,18 @@ .dialog-modal-content { margin: 0; padding: 1rem 1rem; - background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); white-space: normal; } .dialog-modal-footer { margin: 0; padding: .5em .5em; - background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); - border-top: 1px solid $fallback--bg; - border-top: 1px solid var(--bg, $fallback--bg); + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + border-top: 1px solid $fallback--border; + border-top: 1px solid var(--border, $fallback--border); display: flex; justify-content: flex-end; diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue index a7215670..e9ac09c3 100644 --- a/src/components/emoji_input/emoji_input.vue +++ b/src/components/emoji_input/emoji_input.vue @@ -109,10 +109,16 @@ box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); box-shadow: var(--popupShadow); min-width: 75%; - background: $fallback--bg; - background: var(--bg, $fallback--bg); - color: $fallback--lightText; - color: var(--lightText, $fallback--lightText); + background-color: $fallback--bg; + background-color: var(--popover, $fallback--bg); + color: $fallback--link; + color: var(--popoverText, $fallback--link); + --faint: var(--popoverFaintText, $fallback--faint); + --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); + --postLink: var(--popoverPostLink, $fallback--link); + --postFaintLink: var(--popoverPostFaintLink, $fallback--link); + --icon: var(--popoverIcon, $fallback--icon); } } @@ -157,7 +163,12 @@ &.highlighted { background-color: $fallback--fg; - background-color: var(--lightBg, $fallback--fg); + background-color: var(--selectedMenuPopover, $fallback--fg); + color: var(--selectedMenuPopoverText, $fallback--text); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); + --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); + --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); } } } diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index 6608f393..8bd07e45 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -8,6 +8,15 @@ left: 0; margin: 0 !important; z-index: 1; + background-color: $fallback--bg; + background-color: var(--popover, $fallback--bg); + color: $fallback--link; + color: var(--popoverText, $fallback--link); + --lightText: var(--popoverLightText, $fallback--faint); + --faint: var(--popoverFaintText, $fallback--faint); + --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); + --icon: var(--popoverIcon, $fallback--icon); .keep-open, .too-many-emoji { diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index e5b6d9f5..b25c9716 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -128,7 +128,7 @@ } .picked-reaction { - border: 1px solid var(--link, $fallback--link); + border: 1px solid var(--accent, $fallback--link); margin-left: -1px; // offset the border, can't use inset shadows either margin-right: calc(0.5em - 1px); } diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue index 20c6f569..ae00487f 100644 --- a/src/components/export_import/export_import.vue +++ b/src/components/export_import/export_import.vue @@ -42,7 +42,7 @@ export default { }, methods: { exportData () { - const stringified = JSON.stringify(this.exportObject) // Pretty-print and indent with 2 spaces + const stringified = JSON.stringify(this.exportObject, null, 2) // Pretty-print and indent with 2 spaces // Create an invisible link with a data url and simulate a click const e = document.createElement('a') diff --git a/src/components/follow_button/follow_button.vue b/src/components/follow_button/follow_button.vue index f0cbb94b..bfdc137b 100644 --- a/src/components/follow_button/follow_button.vue +++ b/src/components/follow_button/follow_button.vue @@ -1,7 +1,7 @@ <template> <button class="btn btn-default follow-button" - :class="{ pressed: isPressed }" + :class="{ toggled: isPressed }" :disabled="inProgress" :title="title" @click="onClick" diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue index 006d6373..e78e05f1 100644 --- a/src/components/moderation_tools/moderation_tools.vue +++ b/src/components/moderation_tools/moderation_tools.vue @@ -123,7 +123,7 @@ </div> <button class="btn btn-default btn-block" - :class="{ pressed: showDropDown }" + :class="{ toggled: showDropDown }" > {{ $t('user_card.admin_menu.moderation') }} </button> diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue index 0f3296eb..8cd04dc7 100644 --- a/src/components/nav_panel/nav_panel.vue +++ b/src/components/nav_panel/nav_panel.vue @@ -100,13 +100,25 @@ &:hover { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedMenu, $fallback--lightBg); + color: $fallback--link; + color: var(--selectedMenuText, $fallback--link); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + --icon: var(--selectedMenuIcon, $fallback--icon); } &.router-link-active { font-weight: bolder; background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedMenu, $fallback--lightBg); + color: $fallback--text; + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + --icon: var(--selectedMenuIcon, $fallback--icon); &:hover { text-decoration: underline; diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss index 8d819a56..f5b13322 100644 --- a/src/components/notifications/notifications.scss +++ b/src/components/notifications/notifications.scss @@ -68,6 +68,9 @@ a { color: var(--faintLink); } + .status-content a { + color: var(--postFaintLink); + } } padding: 0; .media-body { diff --git a/src/components/opacity_input/opacity_input.vue b/src/components/opacity_input/opacity_input.vue index c677f18c..3cc3942b 100644 --- a/src/components/opacity_input/opacity_input.vue +++ b/src/components/opacity_input/opacity_input.vue @@ -9,18 +9,12 @@ > {{ $t('settings.style.common.opacity') }} </label> - <input + <Checkbox v-if="typeof fallback !== 'undefined'" - :id="name + '-o'" - class="opt exclude-disabled" - type="checkbox" :checked="present" - @input="$emit('input', !present ? fallback : undefined)" - > - <label - v-if="typeof fallback !== 'undefined'" - class="opt-l" - :for="name + '-o'" + :disabled="disabled" + class="opt" + @change="$emit('input', !present ? fallback : undefined)" /> <input :id="name" @@ -37,7 +31,11 @@ </template> <script> +import Checkbox from '../checkbox/checkbox.vue' export default { + components: { + Checkbox + }, props: [ 'name', 'value', 'fallback', 'disabled' ], diff --git a/src/components/poll/poll.vue b/src/components/poll/poll.vue index db8e33b3..56e91cca 100644 --- a/src/components/poll/poll.vue +++ b/src/components/poll/poll.vue @@ -104,8 +104,10 @@ .result-fill { height: 100%; position: absolute; + color: $fallback--text; + color: var(--pollText, $fallback--text); background-color: $fallback--lightBg; - background-color: var(--linkBg, $fallback--lightBg); + background-color: var(--poll, $fallback--lightBg); border-radius: $fallback--panelRadius; border-radius: var(--panelRadius, $fallback--panelRadius); top: 0; diff --git a/src/components/popper/popper.scss b/src/components/popper/popper.scss index 06daa871..99b7e6fc 100644 --- a/src/components/popper/popper.scss +++ b/src/components/popper/popper.scss @@ -9,7 +9,15 @@ border-radius: $fallback--btnRadius; border-radius: var(--btnRadius, $fallback--btnRadius); background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); + background-color: var(--popover, $fallback--bg); + color: $fallback--text; + color: var(--popoverText, $fallback--text); + --faint: var(--popoverFaintText, $fallback--faint); + --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); + --postLink: var(--popoverPostLink, $fallback--link); + --postFaintLink: var(--popoverPostFaintLink, $fallback--link); + --icon: var(--popoverIcon, $fallback--icon); } .popover-arrow { @@ -129,6 +137,8 @@ width: 100%; height: 100%; + --btnText: var(--popoverText, $fallback--text); + &-icon { padding-left: 0.5rem; @@ -137,11 +147,18 @@ } } - &:hover { - // TODO: improve the look on breeze themes - background-color: $fallback--fg; - background-color: var(--btn, $fallback--fg); - box-shadow: none; + &:active, &:hover { + background-color: $fallback--lightBg; + background-color: var(--selectedMenuPopover, $fallback--lightBg); + color: $fallback--link; + color: var(--selectedMenuPopoverText, $fallback--link); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); + --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); + --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); + i { + color: var(--selectedMenuPopoverIcon, $fallback--icon); + } } } } diff --git a/src/components/range_input/range_input.vue b/src/components/range_input/range_input.vue index aaa2ed26..5857a5c1 100644 --- a/src/components/range_input/range_input.vue +++ b/src/components/range_input/range_input.vue @@ -12,7 +12,7 @@ <input v-if="typeof fallback !== 'undefined'" :id="name + '-o'" - class="opt exclude-disabled" + class="opt" type="checkbox" :checked="present" @input="$emit('input', !present ? fallback : undefined)" diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue index d9ec7ece..a9bb12a1 100644 --- a/src/components/selectable_list/selectable_list.vue +++ b/src/components/selectable_list/selectable_list.vue @@ -68,7 +68,12 @@ &-item-selected-inner { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedMenu, $fallback--lightBg); + color: var(--selectedMenuText, $fallback--text); + --faint: var(--selectedMenuFaintText, $fallback--faint); + --faintLink: var(--selectedMenuFaintLink, $fallback--faint); + --lightText: var(--selectedMenuLightText, $fallback--lightText); + --icon: var(--selectedMenuIcon, $fallback--icon); } &-header { diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue index 60cb8a87..9e14b449 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -76,9 +76,9 @@ <li> <Checkbox v-model="useStreamingApi"> {{ $t('settings.useStreamingApi') }} - <br/> + <br> <small> - {{ $t('settings.useStreamingApiWarning') }} + {{ $t('settings.useStreamingApiWarning') }} </small> </Checkbox> </li> diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index 44e4a22f..f9e7b985 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -3,6 +3,17 @@ import OpacityInput from '../opacity_input/opacity_input.vue' import { getCssShadow } from '../../services/style_setter/style_setter.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' +const toModel = (object = {}) => ({ + x: 0, + y: 0, + blur: 0, + spread: 0, + inset: false, + color: '#000000', + alpha: 1, + ...object +}) + export default { // 'Value' and 'Fallback' can be undefined, but if they are // initially vue won't detect it when they become something else @@ -15,7 +26,7 @@ export default { return { selectedId: 0, // TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason) - cValue: this.value || this.fallback || [] + cValue: (this.value || this.fallback || []).map(toModel) } }, components: { @@ -24,12 +35,12 @@ export default { }, methods: { add () { - this.cValue.push(Object.assign({}, this.selected)) + this.cValue.push(toModel(this.selected)) this.selectedId = this.cValue.length - 1 }, del () { this.cValue.splice(this.selectedId, 1) - this.selectedId = this.cValue.length === 0 ? undefined : this.selectedId - 1 + this.selectedId = this.cValue.length === 0 ? undefined : Math.max(this.selectedId - 1, 0) }, moveUp () { const movable = this.cValue.splice(this.selectedId, 1)[0] @@ -46,19 +57,24 @@ export default { this.cValue = this.value || this.fallback }, computed: { + anyShadows () { + return this.cValue.length > 0 + }, + anyShadowsFallback () { + return this.fallback.length > 0 + }, selected () { - if (this.ready && this.cValue.length > 0) { + if (this.ready && this.anyShadows) { return this.cValue[this.selectedId] } else { - return { - x: 0, - y: 0, - blur: 0, - spread: 0, - inset: false, - color: '#000000', - alpha: 1 - } + return toModel({}) + } + }, + currentFallback () { + if (this.ready && this.anyShadowsFallback) { + return this.fallback[this.selectedId] + } else { + return toModel({}) } }, moveUpValid () { @@ -80,7 +96,7 @@ export default { }, style () { return this.ready ? { - boxShadow: getCssShadow(this.cValue) + boxShadow: getCssShadow(this.fallback) } : {} } } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index de8a42d1..815a9e59 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -191,15 +191,20 @@ v-model="selected.color" :disabled="!present" :label="$t('settings.style.common.color')" + :fallback="currentFallback.color" + :show-optional-tickbox="false" name="shadow" /> <OpacityInput v-model="selected.alpha" :disabled="!present" /> - <p> - {{ $t('settings.style.shadows.hint') }} - </p> + <i18n + path="settings.style.shadows.hintV3" + tag="p" + > + <code>--variable,mod</code> + </i18n> </div> </div> </template> diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue index 28637afc..df1d22a4 100644 --- a/src/components/side_drawer/side_drawer.vue +++ b/src/components/side_drawer/side_drawer.vue @@ -223,7 +223,13 @@ box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); box-shadow: var(--panelShadow); background-color: $fallback--bg; - background-color: var(--bg, $fallback--bg); + background-color: var(--popover, $fallback--bg); + color: $fallback--link; + color: var(--popoverText, $fallback--link); + --faint: var(--popoverFaintText, $fallback--faint); + --faintLink: var(--popoverFaintLink, $fallback--faint); + --lightText: var(--popoverLightText, $fallback--lightText); + --icon: var(--popoverIcon, $fallback--icon); .button-icon:before { width: 1.1em; @@ -289,7 +295,13 @@ &:hover { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedMenuPopover, $fallback--lightBg); + color: $fallback--text; + color: var(--selectedMenuPopoverText, $fallback--text); + --faint: var(--selectedMenuPopoverFaintText, $fallback--faint); + --faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint); + --lightText: var(--selectedMenuPopoverLightText, $fallback--lightText); + --icon: var(--selectedMenuPopoverIcon, $fallback--icon); } } } diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 83f07dac..76b038d9 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -468,7 +468,15 @@ $status-margin: 0.75em; &_focused { background-color: $fallback--lightBg; - background-color: var(--lightBg, $fallback--lightBg); + background-color: var(--selectedPost, $fallback--lightBg); + color: $fallback--text; + color: var(--selectedPostText, $fallback--text); + --lightText: var(--selectedPostLightText, $fallback--light); + --faint: var(--selectedPostFaintText, $fallback--faint); + --faintLink: var(--selectedPostFaintLink, $fallback--faint); + --postLink: var(--selectedPostPostLink, $fallback--faint); + --postFaintLink: var(--selectedPostFaintPostLink, $fallback--faint); + --icon: var(--selectedPostIcon, $fallback--icon); } .timeline & { @@ -596,8 +604,6 @@ $status-margin: 0.75em; overflow: hidden; text-overflow: ellipsis; margin: 0 0.4em 0 0.2em; - color: $fallback--faint; - color: var(--faint, $fallback--faint); } .replies-separator { @@ -659,6 +665,11 @@ $status-margin: 0.75em; line-height: 1.4em; white-space: pre-wrap; + a { + color: $fallback--link; + color: var(--postLink, $fallback--link); + } + img, video { max-width: 100%; max-height: 400px; diff --git a/src/components/sticker_picker/sticker_picker.vue b/src/components/sticker_picker/sticker_picker.vue index 3863908a..dc449ccb 100644 --- a/src/components/sticker_picker/sticker_picker.vue +++ b/src/components/sticker_picker/sticker_picker.vue @@ -51,7 +51,7 @@ img { height: 100%; &:hover { - filter: drop-shadow(0 0 5px var(--link, $fallback--link)); + filter: drop-shadow(0 0 5px var(--accent, $fallback--link)); } } } diff --git a/src/components/style_switcher/preview.vue b/src/components/style_switcher/preview.vue index 101a32bd..9d984659 100644 --- a/src/components/style_switcher/preview.vue +++ b/src/components/style_switcher/preview.vue @@ -1,101 +1,117 @@ <template> - <div class="panel dummy"> - <div class="panel-heading"> - <div class="title"> - {{ $t('settings.style.preview.header') }} - <span class="badge badge-notification"> - 99 + <div class="preview-container"> + <div class="underlay underlay-preview" /> + <div class="panel dummy"> + <div class="panel-heading"> + <div class="title"> + {{ $t('settings.style.preview.header') }} + <span class="badge badge-notification"> + 99 + </span> + </div> + <span class="faint"> + {{ $t('settings.style.preview.header_faint') }} + </span> + <span class="alert error"> + {{ $t('settings.style.preview.error') }} </span> + <button class="btn"> + {{ $t('settings.style.preview.button') }} + </button> </div> - <span class="faint"> - {{ $t('settings.style.preview.header_faint') }} - </span> - <span class="alert error"> - {{ $t('settings.style.preview.error') }} - </span> - <button class="btn"> - {{ $t('settings.style.preview.button') }} - </button> - </div> - <div class="panel-body theme-preview-content"> - <div class="post"> - <div class="avatar"> - ( ͡° ͜ʖ ͡°) - </div> - <div class="content"> - <h4> - {{ $t('settings.style.preview.content') }} - </h4> + <div class="panel-body theme-preview-content"> + <div class="post"> + <div class="avatar still-image"> + ( ͡° ͜ʖ ͡°) + </div> + <div class="content"> + <h4> + {{ $t('settings.style.preview.content') }} + </h4> - <i18n path="settings.style.preview.text"> - <code style="font-family: var(--postCodeFont)"> - {{ $t('settings.style.preview.mono') }} - </code> - <a style="color: var(--link)"> - {{ $t('settings.style.preview.link') }} - </a> - </i18n> + <i18n path="settings.style.preview.text"> + <code style="font-family: var(--postCodeFont)"> + {{ $t('settings.style.preview.mono') }} + </code> + <a style="color: var(--link)"> + {{ $t('settings.style.preview.link') }} + </a> + </i18n> - <div class="icons"> - <i - style="color: var(--cBlue)" - class="button-icon icon-reply" - /> - <i - style="color: var(--cGreen)" - class="button-icon icon-retweet" - /> - <i - style="color: var(--cOrange)" - class="button-icon icon-star" - /> - <i - style="color: var(--cRed)" - class="button-icon icon-cancel" - /> + <div class="icons"> + <i + style="color: var(--cBlue)" + class="button-icon icon-reply" + /> + <i + style="color: var(--cGreen)" + class="button-icon icon-retweet" + /> + <i + style="color: var(--cOrange)" + class="button-icon icon-star" + /> + <i + style="color: var(--cRed)" + class="button-icon icon-cancel" + /> + </div> </div> </div> - </div> - <div class="after-post"> - <div class="avatar-alt"> - :^) - </div> - <div class="content"> - <i18n - path="settings.style.preview.fine_print" - tag="span" - class="faint" - > - <a style="color: var(--faintLink)"> - {{ $t('settings.style.preview.faint_link') }} - </a> - </i18n> + <div class="after-post"> + <div class="avatar-alt"> + :^) + </div> + <div class="content"> + <i18n + path="settings.style.preview.fine_print" + tag="span" + class="faint" + > + <a style="color: var(--faintLink)"> + {{ $t('settings.style.preview.faint_link') }} + </a> + </i18n> + </div> </div> - </div> - <div class="separator" /> + <div class="separator" /> - <span class="alert error"> - {{ $t('settings.style.preview.error') }} - </span> - <input - :value="$t('settings.style.preview.input')" - type="text" - > - - <div class="actions"> - <span class="checkbox"> - <input - id="preview_checkbox" - checked="very yes" - type="checkbox" - > - <label for="preview_checkbox">{{ $t('settings.style.preview.checkbox') }}</label> + <span class="alert error"> + {{ $t('settings.style.preview.error') }} </span> - <button class="btn"> - {{ $t('settings.style.preview.button') }} - </button> + <input + :value="$t('settings.style.preview.input')" + type="text" + > + + <div class="actions"> + <span class="checkbox"> + <input + id="preview_checkbox" + checked="very yes" + type="checkbox" + > + <label for="preview_checkbox">{{ $t('settings.style.preview.checkbox') }}</label> + </span> + <button class="btn"> + {{ $t('settings.style.preview.button') }} + </button> + </div> </div> </div> </div> </template> + +<style lang="scss"> +.preview-container { + position: relative; +} +.underlay-preview { + position: absolute; + top: 0; + bottom: 0; + left: 10px; + right: 10px; +} +</style> diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js index ebde4475..a7f586f4 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/style_switcher/style_switcher.js @@ -1,6 +1,29 @@ -import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js' import { set, delete as del } from 'vue' -import { generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js' +import { + rgb2hex, + hex2rgb, + getContrastRatioLayers +} from '../../services/color_convert/color_convert.js' +import { + DEFAULT_SHADOWS, + generateColors, + generateShadows, + generateRadii, + generateFonts, + composePreset, + getThemes, + shadows2to3, + colors2to3 +} from '../../services/style_setter/style_setter.js' +import { + SLOT_INHERITANCE +} from '../../services/theme_data/pleromafe.js' +import { + CURRENT_VERSION, + OPACITIES, + getLayers, + getOpacitySlot +} from '../../services/theme_data/theme_data.service.js' import ColorInput from '../color_input/color_input.vue' import RangeInput from '../range_input/range_input.vue' import OpacityInput from '../opacity_input/opacity_input.vue' @@ -24,11 +47,22 @@ const v1OnlyNames = [ 'cOrange' ].map(_ => _ + 'ColorLocal') +const colorConvert = (color) => { + if (color.startsWith('--') || color === 'transparent') { + return color + } else { + return hex2rgb(color) + } +} + export default { data () { return { availableStyles: [], selected: this.$store.getters.mergedConfig.theme, + themeWarning: undefined, + tempImportFile: undefined, + engineVersion: 0, previewShadows: {}, previewColors: {}, @@ -45,51 +79,13 @@ export default { keepRoundness: false, keepFonts: false, - textColorLocal: '', - linkColorLocal: '', - - bgColorLocal: '', - bgOpacityLocal: undefined, - - fgColorLocal: '', - fgTextColorLocal: undefined, - fgLinkColorLocal: undefined, - - btnColorLocal: undefined, - btnTextColorLocal: undefined, - btnOpacityLocal: undefined, - - inputColorLocal: undefined, - inputTextColorLocal: undefined, - inputOpacityLocal: undefined, + ...Object.keys(SLOT_INHERITANCE) + .map(key => [key, '']) + .reduce((acc, [key, val]) => ({ ...acc, [ key + 'ColorLocal' ]: val }), {}), - panelColorLocal: undefined, - panelTextColorLocal: undefined, - panelLinkColorLocal: undefined, - panelFaintColorLocal: undefined, - panelOpacityLocal: undefined, - - topBarColorLocal: undefined, - topBarTextColorLocal: undefined, - topBarLinkColorLocal: undefined, - - alertErrorColorLocal: undefined, - alertWarningColorLocal: undefined, - - badgeOpacityLocal: undefined, - badgeNotificationColorLocal: undefined, - - borderColorLocal: undefined, - borderOpacityLocal: undefined, - - faintColorLocal: undefined, - faintOpacityLocal: undefined, - faintLinkColorLocal: undefined, - - cRedColorLocal: '', - cBlueColorLocal: '', - cGreenColorLocal: '', - cOrangeColorLocal: '', + ...Object.keys(OPACITIES) + .map(key => [key, '']) + .reduce((acc, [key, val]) => ({ ...acc, [ key + 'OpacityLocal' ]: val }), {}), shadowSelected: undefined, shadowsLocal: {}, @@ -108,69 +104,105 @@ export default { created () { const self = this - getThemes().then((themesComplete) => { - self.availableStyles = themesComplete - }) + getThemes() + .then((promises) => { + return Promise.all( + Object.entries(promises) + .map(([k, v]) => v.then(res => [k, res])) + ) + }) + .then(themes => themes.reduce((acc, [k, v]) => { + if (v) { + return { + ...acc, + [k]: v + } + } else { + return acc + } + }, {})) + .then((themesComplete) => { + self.availableStyles = themesComplete + }) }, mounted () { - this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme) + this.loadThemeFromLocalStorage() if (typeof this.shadowSelected === 'undefined') { this.shadowSelected = this.shadowsAvailable[0] } }, computed: { + themeWarningHelp () { + if (!this.themeWarning) return + const t = this.$t + const pre = 'settings.style.switcher.help.' + const { + origin, + themeEngineVersion, + type, + noActionsPossible + } = this.themeWarning + if (origin === 'file') { + // Loaded v2 theme from file + if (themeEngineVersion === 2 && type === 'wrong_version') { + return t(pre + 'v2_imported') + } + if (themeEngineVersion > CURRENT_VERSION) { + return t(pre + 'future_version_imported') + ' ' + + ( + noActionsPossible + ? t(pre + 'snapshot_missing') + : t(pre + 'snapshot_present') + ) + } + if (themeEngineVersion < CURRENT_VERSION) { + return t(pre + 'future_version_imported') + ' ' + + ( + noActionsPossible + ? t(pre + 'snapshot_missing') + : t(pre + 'snapshot_present') + ) + } + } else if (origin === 'localStorage') { + if (type === 'snapshot_source_mismatch') { + return t(pre + 'snapshot_source_mismatch') + } + // FE upgraded from v2 + if (themeEngineVersion === 2) { + return t(pre + 'upgraded_from_v2') + } + // Admin downgraded FE + if (themeEngineVersion > CURRENT_VERSION) { + return t(pre + 'fe_downgraded') + ' ' + + ( + noActionsPossible + ? t(pre + 'migration_snapshot_ok') + : t(pre + 'migration_snapshot_gone') + ) + } + // Admin upgraded FE + if (themeEngineVersion < CURRENT_VERSION) { + return t(pre + 'fe_upgraded') + ' ' + + ( + noActionsPossible + ? t(pre + 'migration_snapshot_ok') + : t(pre + 'migration_snapshot_gone') + ) + } + } + }, selectedVersion () { return Array.isArray(this.selected) ? 1 : 2 }, currentColors () { - return { - bg: this.bgColorLocal, - text: this.textColorLocal, - link: this.linkColorLocal, - - fg: this.fgColorLocal, - fgText: this.fgTextColorLocal, - fgLink: this.fgLinkColorLocal, - - panel: this.panelColorLocal, - panelText: this.panelTextColorLocal, - panelLink: this.panelLinkColorLocal, - panelFaint: this.panelFaintColorLocal, - - input: this.inputColorLocal, - inputText: this.inputTextColorLocal, - - topBar: this.topBarColorLocal, - topBarText: this.topBarTextColorLocal, - topBarLink: this.topBarLinkColorLocal, - - btn: this.btnColorLocal, - btnText: this.btnTextColorLocal, - - alertError: this.alertErrorColorLocal, - alertWarning: this.alertWarningColorLocal, - badgeNotification: this.badgeNotificationColorLocal, - - faint: this.faintColorLocal, - faintLink: this.faintLinkColorLocal, - border: this.borderColorLocal, - - cRed: this.cRedColorLocal, - cBlue: this.cBlueColorLocal, - cGreen: this.cGreenColorLocal, - cOrange: this.cOrangeColorLocal - } + return Object.keys(SLOT_INHERITANCE) + .map(key => [key, this[key + 'ColorLocal']]) + .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {}) }, currentOpacity () { - return { - bg: this.bgOpacityLocal, - btn: this.btnOpacityLocal, - input: this.inputOpacityLocal, - panel: this.panelOpacityLocal, - topBar: this.topBarOpacityLocal, - border: this.borderOpacityLocal, - faint: this.faintOpacityLocal - } + return Object.keys(OPACITIES) + .map(key => [key, this[key + 'OpacityLocal']]) + .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {}) }, currentRadii () { return { @@ -193,75 +225,66 @@ export default { }, // This needs optimization maybe previewContrast () { - if (!this.previewTheme.colors.bg) return {} - const colors = this.previewTheme.colors - const opacity = this.previewTheme.opacity - if (!colors.bg) return {} - const hints = (ratio) => ({ - text: ratio.toPrecision(3) + ':1', - // AA level, AAA level - aa: ratio >= 4.5, - aaa: ratio >= 7, - // same but for 18pt+ texts - laa: ratio >= 3, - laaa: ratio >= 4.5 - }) - - // fgsfds :DDDD - const fgs = { - text: hex2rgb(colors.text), - panelText: hex2rgb(colors.panelText), - panelLink: hex2rgb(colors.panelLink), - btnText: hex2rgb(colors.btnText), - topBarText: hex2rgb(colors.topBarText), - inputText: hex2rgb(colors.inputText), - - link: hex2rgb(colors.link), - topBarLink: hex2rgb(colors.topBarLink), - - red: hex2rgb(colors.cRed), - green: hex2rgb(colors.cGreen), - blue: hex2rgb(colors.cBlue), - orange: hex2rgb(colors.cOrange) - } - - const bgs = { - bg: hex2rgb(colors.bg), - btn: hex2rgb(colors.btn), - panel: hex2rgb(colors.panel), - topBar: hex2rgb(colors.topBar), - input: hex2rgb(colors.input), - alertError: hex2rgb(colors.alertError), - alertWarning: hex2rgb(colors.alertWarning), - badgeNotification: hex2rgb(colors.badgeNotification) - } - - /* This is a bit confusing because "bottom layer" used is text color - * This is done to get worst case scenario when background below transparent - * layer matches text color, making it harder to read the lower alpha is. - */ - const ratios = { - bgText: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.text), fgs.text), - bgLink: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.link), fgs.link), - bgRed: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.red), fgs.red), - bgGreen: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.green), fgs.green), - bgBlue: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.blue), fgs.blue), - bgOrange: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.orange), fgs.orange), - - tintText: getContrastRatio(alphaBlend(bgs.bg, 0.5, fgs.panelText), fgs.text), - - panelText: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText), - panelLink: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelLink), fgs.panelLink), - - btnText: getContrastRatio(alphaBlend(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText), - - inputText: getContrastRatio(alphaBlend(bgs.input, opacity.input, fgs.inputText), fgs.inputText), - - topBarText: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText), - topBarLink: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink) + try { + if (!this.previewTheme.colors.bg) return {} + const colors = this.previewTheme.colors + const opacity = this.previewTheme.opacity + if (!colors.bg) return {} + const hints = (ratio) => ({ + text: ratio.toPrecision(3) + ':1', + // AA level, AAA level + aa: ratio >= 4.5, + aaa: ratio >= 7, + // same but for 18pt+ texts + laa: ratio >= 3, + laaa: ratio >= 4.5 + }) + const colorsConverted = Object.entries(colors).reduce((acc, [key, value]) => ({ ...acc, [key]: colorConvert(value) }), {}) + + const ratios = Object.entries(SLOT_INHERITANCE).reduce((acc, [key, value]) => { + const slotIsBaseText = key === 'text' || key === 'link' + const slotIsText = slotIsBaseText || ( + typeof value === 'object' && value !== null && value.textColor + ) + if (!slotIsText) return acc + const { layer, variant } = slotIsBaseText ? { layer: 'bg' } : value + const background = variant || layer + const opacitySlot = getOpacitySlot(background) + const textColors = [ + key, + ...(background === 'bg' ? ['cRed', 'cGreen', 'cBlue', 'cOrange'] : []) + ] + + const layers = getLayers( + layer, + variant || layer, + opacitySlot, + colorsConverted, + opacity + ) + + return { + ...acc, + ...textColors.reduce((acc, textColorKey) => { + const newKey = slotIsBaseText + ? 'bg' + textColorKey[0].toUpperCase() + textColorKey.slice(1) + : textColorKey + return { + ...acc, + [newKey]: getContrastRatioLayers( + colorsConverted[textColorKey], + layers, + colorsConverted[textColorKey] + ) + } + }, {}) + } + }, {}) + + return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) + } catch (e) { + console.warn('Failure computing contrasts', e) } - - return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) }, previewRules () { if (!this.preview.rules) return '' @@ -272,7 +295,7 @@ export default { ].join(';') }, shadowsAvailable () { - return Object.keys(this.previewTheme.shadows).sort() + return Object.keys(DEFAULT_SHADOWS).sort() }, currentShadowOverriden: { get () { @@ -287,7 +310,7 @@ export default { } }, currentShadowFallback () { - return this.previewTheme.shadows[this.shadowSelected] + return (this.previewTheme.shadows || {})[this.shadowSelected] }, currentShadow: { get () { @@ -309,27 +332,34 @@ export default { !this.keepColor ) - const theme = {} + const source = { + themeEngineVersion: CURRENT_VERSION + } if (this.keepFonts || saveEverything) { - theme.fonts = this.fontsLocal + source.fonts = this.fontsLocal } if (this.keepShadows || saveEverything) { - theme.shadows = this.shadowsLocal + source.shadows = this.shadowsLocal } if (this.keepOpacity || saveEverything) { - theme.opacity = this.currentOpacity + source.opacity = this.currentOpacity } if (this.keepColor || saveEverything) { - theme.colors = this.currentColors + source.colors = this.currentColors } if (this.keepRoundness || saveEverything) { - theme.radii = this.currentRadii + source.radii = this.currentRadii + } + + const theme = { + themeEngineVersion: CURRENT_VERSION, + ...this.previewTheme } return { - // To separate from other random JSON files and possible future theme formats - _pleroma_theme_version: 2, theme + // To separate from other random JSON files and possible future source formats + _pleroma_theme_version: 2, theme, source } } }, @@ -346,10 +376,128 @@ export default { Checkbox }, methods: { + loadTheme ( + { + theme, + source, + _pleroma_theme_version: fileVersion + }, + origin, + forceUseSource = false + ) { + this.dismissWarning() + if (!source && !theme) { + throw new Error('Can\'t load theme: empty') + } + const version = (origin === 'localStorage' && !theme.colors) + ? 'l1' + : fileVersion + const snapshotEngineVersion = (theme || {}).themeEngineVersion + const themeEngineVersion = (source || {}).themeEngineVersion || 2 + const versionsMatch = themeEngineVersion === CURRENT_VERSION + const sourceSnapshotMismatch = ( + theme !== undefined && + source !== undefined && + themeEngineVersion !== snapshotEngineVersion + ) + // Force loading of source if user requested it or if snapshot + // is unavailable + const forcedSourceLoad = (source && forceUseSource) || !theme + if (!(versionsMatch && !sourceSnapshotMismatch) && + !forcedSourceLoad && + version !== 'l1' && + origin !== 'defaults' + ) { + if (sourceSnapshotMismatch && origin === 'localStorage') { + this.themeWarning = { + origin, + themeEngineVersion, + type: 'snapshot_source_mismatch' + } + } else if (!theme) { + this.themeWarning = { + origin, + noActionsPossible: true, + themeEngineVersion, + type: 'no_snapshot_old_version' + } + } else if (!versionsMatch) { + this.themeWarning = { + origin, + noActionsPossible: !source, + themeEngineVersion, + type: 'wrong_version' + } + } + } + this.normalizeLocalState(theme, version, source, forcedSourceLoad) + }, + forceLoadLocalStorage () { + this.loadThemeFromLocalStorage(true) + }, + dismissWarning () { + this.themeWarning = undefined + this.tempImportFile = undefined + }, + forceLoad () { + const { origin } = this.themeWarning + switch (origin) { + case 'localStorage': + this.loadThemeFromLocalStorage(true) + break + case 'file': + this.onImport(this.tempImportFile, true) + break + } + this.dismissWarning() + }, + forceSnapshot () { + const { origin } = this.themeWarning + switch (origin) { + case 'localStorage': + this.loadThemeFromLocalStorage(false, true) + break + case 'file': + console.err('Forcing snapshout from file is not supported yet') + break + } + this.dismissWarning() + }, + loadThemeFromLocalStorage (confirmLoadSource = false, forceSnapshot = false) { + const { + customTheme: theme, + customThemeSource: source + } = this.$store.getters.mergedConfig + if (!theme && !source) { + // Anon user or never touched themes + this.loadTheme( + this.$store.state.instance.themeData, + 'defaults', + confirmLoadSource + ) + } else { + this.loadTheme( + { + theme, + source: forceSnapshot ? theme : source + }, + 'localStorage', + confirmLoadSource + ) + } + }, setCustomTheme () { this.$store.dispatch('setOption', { name: 'customTheme', value: { + themeEngineVersion: CURRENT_VERSION, + ...this.previewTheme + } + }) + this.$store.dispatch('setOption', { + name: 'customThemeSource', + value: { + themeEngineVersion: CURRENT_VERSION, shadows: this.shadowsLocal, fonts: this.fontsLocal, opacity: this.currentOpacity, @@ -358,21 +506,27 @@ export default { } }) }, - onImport (parsed) { - if (parsed._pleroma_theme_version === 1) { - this.normalizeLocalState(parsed, 1) - } else if (parsed._pleroma_theme_version === 2) { - this.normalizeLocalState(parsed.theme, 2) - } + updatePreviewColorsAndShadows () { + this.previewColors = generateColors({ + opacity: this.currentOpacity, + colors: this.currentColors + }) + this.previewShadows = generateShadows( + { shadows: this.shadowsLocal, opacity: this.previewTheme.opacity, themeEngineVersion: this.engineVersion }, + this.previewColors.theme.colors, + this.previewColors.mod + ) + }, + onImport (parsed, forceSource = false) { + this.tempImportFile = parsed + this.loadTheme(parsed, 'file', forceSource) }, importValidator (parsed) { const version = parsed._pleroma_theme_version return version >= 1 || version <= 2 }, clearAll () { - const state = this.$store.getters.mergedConfig.customTheme - const version = state.colors ? 2 : 'l1' - this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme, version) + this.loadThemeFromLocalStorage() }, // Clears all the extra stuff when loading V1 theme @@ -411,19 +565,37 @@ export default { /** * This applies stored theme data onto form. Supports three versions of data: + * v3 (version >= 3) - newest version of themes which supports snapshots for better compatiblity * v2 (version = 2) - newer version of themes. * v1 (version = 1) - older version of themes (import from file) * v1l (version = l1) - older version of theme (load from local storage) * v1 and v1l differ because of way themes were stored/exported. - * @param {Object} input - input data + * @param {Object} theme - theme data (snapshot) * @param {Number} version - version of data. 0 means try to guess based on data. "l1" means v1, locastorage type + * @param {Object} source - theme source - this will be used if compatible + * @param {Boolean} source - by default source won't be used if version doesn't match since it might render differently + * this allows importing source anyway */ - normalizeLocalState (input, version = 0) { - const colors = input.colors || input + normalizeLocalState (theme, version = 0, source, forceSource = false) { + let input + if (typeof source !== 'undefined') { + if (forceSource || source.themeEngineVersion === CURRENT_VERSION) { + input = source + version = source.themeEngineVersion + } else { + input = theme + } + } else { + input = theme + } + const radii = input.radii || input const opacity = input.opacity const shadows = input.shadows || {} const fonts = input.fonts || {} + const colors = !input.themeEngineVersion + ? colors2to3(input.colors || input) + : input.colors || input if (version === 0) { if (input.version) version = input.version @@ -437,6 +609,8 @@ export default { } } + this.engineVersion = version + // Stuff that differs between V1 and V2 if (version === 1) { this.fgColorLocal = rgb2hex(colors.btn) @@ -445,7 +619,7 @@ export default { if (!this.keepColor) { this.clearV1() - const keys = new Set(version !== 1 ? Object.keys(colors) : []) + const keys = new Set(version !== 1 ? Object.keys(SLOT_INHERITANCE) : []) if (version === 1 || version === 'l1') { keys .add('bg') @@ -457,7 +631,17 @@ export default { } keys.forEach(key => { - this[key + 'ColorLocal'] = rgb2hex(colors[key]) + const color = colors[key] + const hex = rgb2hex(colors[key]) + this[key + 'ColorLocal'] = hex === '#aN' ? color : hex + }) + } + + if (opacity && !this.keepOpacity) { + this.clearOpacity() + Object.entries(opacity).forEach(([k, v]) => { + if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return + this[k + 'OpacityLocal'] = v }) } @@ -472,7 +656,11 @@ export default { if (!this.keepShadows) { this.clearShadows() - this.shadowsLocal = shadows + if (version === 2) { + this.shadowsLocal = shadows2to3(shadows, this.previewTheme.opacity) + } else { + this.shadowsLocal = shadows + } this.shadowSelected = this.shadowsAvailable[0] } @@ -480,14 +668,6 @@ export default { this.clearFonts() this.fontsLocal = fonts } - - if (opacity && !this.keepOpacity) { - this.clearOpacity() - Object.entries(opacity).forEach(([k, v]) => { - if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return - this[k + 'OpacityLocal'] = v - }) - } } }, watch: { @@ -502,8 +682,9 @@ export default { }, shadowsLocal: { handler () { + if (Object.getOwnPropertyNames(this.previewColors).length === 1) return try { - this.previewShadows = generateShadows({ shadows: this.shadowsLocal }) + this.updatePreviewColorsAndShadows() this.shadowsInvalid = false } catch (e) { this.shadowsInvalid = true @@ -526,27 +707,24 @@ export default { }, currentColors () { try { - this.previewColors = generateColors({ - opacity: this.currentOpacity, - colors: this.currentColors - }) + this.updatePreviewColorsAndShadows() this.colorsInvalid = false + this.shadowsInvalid = false } catch (e) { this.colorsInvalid = true + this.shadowsInvalid = true console.warn(e) } }, currentOpacity () { try { - this.previewColors = generateColors({ - opacity: this.currentOpacity, - colors: this.currentColors - }) + this.updatePreviewColorsAndShadows() } catch (e) { console.warn(e) } }, selected () { + this.dismissWarning() if (this.selectedVersion === 1) { if (!this.keepRoundness) { this.clearRoundness() @@ -573,7 +751,7 @@ export default { this.cOrangeColorLocal = this.selected[8] } } else if (this.selectedVersion >= 2) { - this.normalizeLocalState(this.selected.theme, 2) + this.normalizeLocalState(this.selected.theme, 2, this.selected.source) } } } diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/style_switcher/style_switcher.scss index 135c113a..d2a40d13 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/style_switcher/style_switcher.scss @@ -1,5 +1,15 @@ @import '../../_variables.scss'; .style-switcher { + .theme-warning { + display: flex; + align-items: baseline; + margin-bottom: .5em; + .buttons { + .btn { + margin-bottom: .5em; + } + } + } .preset-switcher { margin-right: 1em; } @@ -15,26 +25,23 @@ &.disabled { input, select { - &:not(.exclude-disabled) { - opacity: .5 - } + opacity: .5 } } + .opt { + margin: .5em; + } + + .color-input { + flex: 0 0 0; + } + input, select { min-width: 3em; margin: 0; flex: 0; - &[type=color] { - padding: 1px; - cursor: pointer; - height: 29px; - min-width: 2em; - border: none; - align-self: stretch; - } - &[type=number] { min-width: 5em; } @@ -42,13 +49,6 @@ &[type=range] { flex: 1; min-width: 3em; - } - - &[type=checkbox] + label { - margin: 6px 0; - } - - &:not([type=number]):not([type=text]) { align-self: flex-start; } } diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index ad032041..62c8e634 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -2,7 +2,53 @@ <div class="style-switcher"> <div class="presets-container"> <div class="save-load"> - <export-import + <div + v-if="themeWarning" + class="theme-warning" + > + <div class="alert warning"> + {{ themeWarningHelp }} + </div> + <div class="buttons"> + <template v-if="themeWarning.type === 'snapshot_source_mismatch'"> + <button + class="btn" + @click="forceLoad" + > + {{ $t('settings.style.switcher.use_source') }} + </button> + <button + class="btn" + @click="forceSnapshot" + > + {{ $t('settings.style.switcher.use_snapshot') }} + </button> + </template> + <template v-else-if="themeWarning.noActionsPossible"> + <button + class="btn" + @click="dismissWarning" + > + {{ $t('general.dismiss') }} + </button> + </template> + <template v-else> + <button + class="btn" + @click="forceLoad" + > + {{ $t('settings.style.switcher.load_theme') }} + </button> + <button + class="btn" + @click="dismissWarning" + > + {{ $t('settings.style.switcher.keep_as_is') }} + </button> + </template> + </div> + </div> + <ExportImport :export-object="exportedTheme" :export-label="$t("settings.export_theme")" :import-label="$t("settings.import_theme")" @@ -27,8 +73,8 @@ :key="style.name" :value="style" :style="{ - backgroundColor: style[1] || style.theme.colors.bg, - color: style[3] || style.theme.colors.text + backgroundColor: style[1] || (style.theme || style.source).colors.bg, + color: style[3] || (style.theme || style.source).colors.text }" > {{ style[0] || style.name }} @@ -38,7 +84,7 @@ </label> </div> </template> - </export-import> + </ExportImport> </div> <div class="save-load-options"> <span class="keep-option"> @@ -70,9 +116,7 @@ </div> </div> - <div class="preview-container"> - <preview :style="previewRules" /> - </div> + <preview :style="previewRules" /> <keep-alive> <tab-switcher key="style-tweak"> @@ -106,7 +150,7 @@ <OpacityInput v-model="bgOpacityLocal" name="bgOpacity" - :fallback="previewTheme.opacity.bg || 1" + :fallback="previewTheme.opacity.bg" /> <ColorInput v-model="textColorLocal" @@ -115,9 +159,18 @@ /> <ContrastRatio :contrast="previewContrast.bgText" /> <ColorInput + v-model="accentColorLocal" + name="accentColor" + :fallback="previewTheme.colors.link" + :label="$t('settings.accent')" + :show-optional-tickbox="typeof linkColorLocal !== 'undefined'" + /> + <ColorInput v-model="linkColorLocal" name="linkColor" + :fallback="previewTheme.colors.accent" :label="$t('settings.links')" + :show-optional-tickbox="typeof accentColorLocal !== 'undefined'" /> <ContrastRatio :contrast="previewContrast.bgLink" /> </div> @@ -148,13 +201,13 @@ name="cRedColor" :label="$t('settings.cRed')" /> - <ContrastRatio :contrast="previewContrast.bgRed" /> + <ContrastRatio :contrast="previewContrast.bgCRed" /> <ColorInput v-model="cBlueColorLocal" name="cBlueColor" :label="$t('settings.cBlue')" /> - <ContrastRatio :contrast="previewContrast.bgBlue" /> + <ContrastRatio :contrast="previewContrast.bgCBlue" /> </div> <div class="color-item"> <ColorInput @@ -162,13 +215,13 @@ name="cGreenColor" :label="$t('settings.cGreen')" /> - <ContrastRatio :contrast="previewContrast.bgGreen" /> + <ContrastRatio :contrast="previewContrast.bgCGreen" /> <ColorInput v-model="cOrangeColorLocal" name="cOrangeColor" :label="$t('settings.cOrange')" /> - <ContrastRatio :contrast="previewContrast.bgOrange" /> + <ContrastRatio :contrast="previewContrast.bgCOrange" /> </div> <p>{{ $t('settings.theme_help_v2_2') }}</p> </div> @@ -193,6 +246,14 @@ </button> </div> <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.post') }}</h4> + <ColorInput + v-model="postLinkColorLocal" + name="postLinkColor" + :fallback="previewTheme.colors.accent" + :label="$t('settings.links')" + /> + <ContrastRatio :contrast="previewContrast.postLink" /> <h4>{{ $t('settings.style.advanced_colors.alert') }}</h4> <ColorInput v-model="alertErrorColorLocal" @@ -200,14 +261,53 @@ :label="$t('settings.style.advanced_colors.alert_error')" :fallback="previewTheme.colors.alertError" /> - <ContrastRatio :contrast="previewContrast.alertError" /> + <ColorInput + v-model="alertErrorTextColorLocal" + name="alertErrorText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.alertErrorText" + /> + <ContrastRatio + :contrast="previewContrast.alertErrorText" + large="true" + /> <ColorInput v-model="alertWarningColorLocal" name="alertWarning" :label="$t('settings.style.advanced_colors.alert_warning')" :fallback="previewTheme.colors.alertWarning" /> - <ContrastRatio :contrast="previewContrast.alertWarning" /> + <ColorInput + v-model="alertWarningTextColorLocal" + name="alertWarningText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.alertWarningText" + /> + <ContrastRatio + :contrast="previewContrast.alertWarningText" + large="true" + /> + <ColorInput + v-model="alertNeutralColorLocal" + name="alertNeutral" + :label="$t('settings.style.advanced_colors.alert_neutral')" + :fallback="previewTheme.colors.alertNeutral" + /> + <ColorInput + v-model="alertNeutralTextColorLocal" + name="alertNeutralText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.alertNeutralText" + /> + <ContrastRatio + :contrast="previewContrast.alertNeutralText" + large="true" + /> + <OpacityInput + v-model="alertOpacityLocal" + name="alertOpacity" + :fallback="previewTheme.opacity.alert" + /> </div> <div class="color-item"> <h4>{{ $t('settings.style.advanced_colors.badge') }}</h4> @@ -217,19 +317,30 @@ :label="$t('settings.style.advanced_colors.badge_notification')" :fallback="previewTheme.colors.badgeNotification" /> + <ColorInput + v-model="badgeNotificationTextColorLocal" + name="badgeNotificationText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.badgeNotificationText" + /> + <ContrastRatio + :contrast="previewContrast.badgeNotificationText" + large="true" + /> </div> <div class="color-item"> <h4>{{ $t('settings.style.advanced_colors.panel_header') }}</h4> <ColorInput v-model="panelColorLocal" name="panelColor" - :fallback="fgColorLocal" + :fallback="previewTheme.colors.panel" :label="$t('settings.background')" /> <OpacityInput v-model="panelOpacityLocal" name="panelOpacity" - :fallback="previewTheme.opacity.panel || 1" + :fallback="previewTheme.opacity.panel" + :disabled="panelColorLocal === 'transparent'" /> <ColorInput v-model="panelTextColorLocal" @@ -239,7 +350,7 @@ /> <ContrastRatio :contrast="previewContrast.panelText" - large="1" + large="true" /> <ColorInput v-model="panelLinkColorLocal" @@ -249,7 +360,7 @@ /> <ContrastRatio :contrast="previewContrast.panelLink" - large="1" + large="true" /> </div> <div class="color-item"> @@ -257,7 +368,7 @@ <ColorInput v-model="topBarColorLocal" name="topBarColor" - :fallback="fgColorLocal" + :fallback="previewTheme.colors.topBar" :label="$t('settings.background')" /> <ColorInput @@ -280,13 +391,14 @@ <ColorInput v-model="inputColorLocal" name="inputColor" - :fallback="fgColorLocal" + :fallback="previewTheme.colors.input" :label="$t('settings.background')" /> <OpacityInput v-model="inputOpacityLocal" name="inputOpacity" - :fallback="previewTheme.opacity.input || 1" + :fallback="previewTheme.opacity.input" + :disabled="inputColorLocal === 'transparent'" /> <ColorInput v-model="inputTextColorLocal" @@ -301,13 +413,14 @@ <ColorInput v-model="btnColorLocal" name="btnColor" - :fallback="fgColorLocal" + :fallback="previewTheme.colors.btn" :label="$t('settings.background')" /> <OpacityInput v-model="btnOpacityLocal" name="btnOpacity" - :fallback="previewTheme.opacity.btn || 1" + :fallback="previewTheme.opacity.btn" + :disabled="btnColorLocal === 'transparent'" /> <ColorInput v-model="btnTextColorLocal" @@ -316,6 +429,124 @@ :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.btnText" /> + <ColorInput + v-model="btnPanelTextColorLocal" + name="btnPanelTextColor" + :fallback="previewTheme.colors.btnPanelText" + :label="$t('settings.style.advanced_colors.panel_header')" + /> + <ContrastRatio :contrast="previewContrast.btnPanelText" /> + <ColorInput + v-model="btnTopBarTextColorLocal" + name="btnTopBarTextColor" + :fallback="previewTheme.colors.btnTopBarText" + :label="$t('settings.style.advanced_colors.top_bar')" + /> + <ContrastRatio :contrast="previewContrast.btnTopBarText" /> + <h5>{{ $t('settings.style.advanced_colors.pressed') }}</h5> + <ColorInput + v-model="btnPressedColorLocal" + name="btnPressedColor" + :fallback="previewTheme.colors.btnPressed" + :label="$t('settings.background')" + /> + <ColorInput + v-model="btnPressedTextColorLocal" + name="btnPressedTextColor" + :fallback="previewTheme.colors.btnPressedText" + :label="$t('settings.text')" + /> + <ContrastRatio :contrast="previewContrast.btnPressedText" /> + <ColorInput + v-model="btnPressedPanelTextColorLocal" + name="btnPressedPanelTextColor" + :fallback="previewTheme.colors.btnPressedPanelText" + :label="$t('settings.style.advanced_colors.panel_header')" + /> + <ContrastRatio :contrast="previewContrast.btnPressedPanelText" /> + <ColorInput + v-model="btnPressedTopBarTextColorLocal" + name="btnPressedTopBarTextColor" + :fallback="previewTheme.colors.btnPressedTopBarText" + :label="$t('settings.style.advanced_colors.top_bar')" + /> + <ContrastRatio :contrast="previewContrast.btnPressedTopBarText" /> + <h5>{{ $t('settings.style.advanced_colors.disabled') }}</h5> + <ColorInput + v-model="btnDisabledColorLocal" + name="btnDisabledColor" + :fallback="previewTheme.colors.btnDisabled" + :label="$t('settings.background')" + /> + <ColorInput + v-model="btnDisabledTextColorLocal" + name="btnDisabledTextColor" + :fallback="previewTheme.colors.btnDisabledText" + :label="$t('settings.text')" + /> + <ColorInput + v-model="btnDisabledPanelTextColorLocal" + name="btnDisabledPanelTextColor" + :fallback="previewTheme.colors.btnDisabledPanelText" + :label="$t('settings.style.advanced_colors.panel_header')" + /> + <ColorInput + v-model="btnDisabledTopBarTextColorLocal" + name="btnDisabledTopBarTextColor" + :fallback="previewTheme.colors.btnDisabledTopBarText" + :label="$t('settings.style.advanced_colors.top_bar')" + /> + <h5>{{ $t('settings.style.advanced_colors.toggled') }}</h5> + <ColorInput + v-model="btnToggledColorLocal" + name="btnToggledColor" + :fallback="previewTheme.colors.btnToggled" + :label="$t('settings.background')" + /> + <ColorInput + v-model="btnToggledTextColorLocal" + name="btnToggledTextColor" + :fallback="previewTheme.colors.btnToggledText" + :label="$t('settings.text')" + /> + <ContrastRatio :contrast="previewContrast.btnToggledText" /> + <ColorInput + v-model="btnToggledPanelTextColorLocal" + name="btnToggledPanelTextColor" + :fallback="previewTheme.colors.btnToggledPanelText" + :label="$t('settings.style.advanced_colors.panel_header')" + /> + <ContrastRatio :contrast="previewContrast.btnToggledPanelText" /> + <ColorInput + v-model="btnToggledTopBarTextColorLocal" + name="btnToggledTopBarTextColor" + :fallback="previewTheme.colors.btnToggledTopBarText" + :label="$t('settings.style.advanced_colors.top_bar')" + /> + <ContrastRatio :contrast="previewContrast.btnToggledTopBarText" /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.tabs') }}</h4> + <ColorInput + v-model="tabColorLocal" + name="tabColor" + :fallback="previewTheme.colors.tab" + :label="$t('settings.background')" + /> + <ColorInput + v-model="tabTextColorLocal" + name="tabTextColor" + :fallback="previewTheme.colors.tabText" + :label="$t('settings.text')" + /> + <ContrastRatio :contrast="previewContrast.tabText" /> + <ColorInput + v-model="tabActiveTextColorLocal" + name="tabActiveTextColor" + :fallback="previewTheme.colors.tabActiveText" + :label="$t('settings.text')" + /> + <ContrastRatio :contrast="previewContrast.tabActiveText" /> </div> <div class="color-item"> <h4>{{ $t('settings.style.advanced_colors.borders') }}</h4> @@ -328,7 +559,8 @@ <OpacityInput v-model="borderOpacityLocal" name="borderOpacity" - :fallback="previewTheme.opacity.border || 1" + :fallback="previewTheme.opacity.border" + :disabled="borderColorLocal === 'transparent'" /> </div> <div class="color-item"> @@ -336,7 +568,7 @@ <ColorInput v-model="faintColorLocal" name="faintColor" - :fallback="previewTheme.colors.faint || 1" + :fallback="previewTheme.colors.faint" :label="$t('settings.text')" /> <ColorInput @@ -354,8 +586,145 @@ <OpacityInput v-model="faintOpacityLocal" name="faintOpacity" - :fallback="previewTheme.opacity.faint || 0.5" + :fallback="previewTheme.opacity.faint" + /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.underlay') }}</h4> + <ColorInput + v-model="underlayColorLocal" + name="underlay" + :label="$t('settings.style.advanced_colors.underlay')" + :fallback="previewTheme.colors.underlay" + /> + <OpacityInput + v-model="underlayOpacityLocal" + name="underlayOpacity" + :fallback="previewTheme.opacity.underlay" + :disabled="underlayOpacityLocal === 'transparent'" + /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.poll') }}</h4> + <ColorInput + v-model="pollColorLocal" + name="poll" + :label="$t('settings.background')" + :fallback="previewTheme.colors.poll" + /> + <ColorInput + v-model="pollTextColorLocal" + name="pollText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.pollText" + /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.icons') }}</h4> + <ColorInput + v-model="iconColorLocal" + name="icon" + :label="$t('settings.style.advanced_colors.icons')" + :fallback="previewTheme.colors.icon" + /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.highlight') }}</h4> + <ColorInput + v-model="highlightColorLocal" + name="highlight" + :label="$t('settings.background')" + :fallback="previewTheme.colors.highlight" + /> + <ColorInput + v-model="highlightTextColorLocal" + name="highlightText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.highlightText" + /> + <ContrastRatio :contrast="previewContrast.highlightText" /> + <ColorInput + v-model="highlightLinkColorLocal" + name="highlightLink" + :label="$t('settings.links')" + :fallback="previewTheme.colors.highlightLink" + /> + <ContrastRatio :contrast="previewContrast.highlightLink" /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.popover') }}</h4> + <ColorInput + v-model="popoverColorLocal" + name="popover" + :label="$t('settings.background')" + :fallback="previewTheme.colors.popover" + /> + <OpacityInput + v-model="popoverOpacityLocal" + name="popoverOpacity" + :fallback="previewTheme.opacity.popover" + :disabled="popoverOpacityLocal === 'transparent'" + /> + <ColorInput + v-model="popoverTextColorLocal" + name="popoverText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.popoverText" + /> + <ContrastRatio :contrast="previewContrast.popoverText" /> + <ColorInput + v-model="popoverLinkColorLocal" + name="popoverLink" + :label="$t('settings.links')" + :fallback="previewTheme.colors.popoverLink" + /> + <ContrastRatio :contrast="previewContrast.popoverLink" /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.selectedPost') }}</h4> + <ColorInput + v-model="selectedPostColorLocal" + name="selectedPost" + :label="$t('settings.background')" + :fallback="previewTheme.colors.selectedPost" + /> + <ColorInput + v-model="selectedPostTextColorLocal" + name="selectedPostText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.selectedPostText" + /> + <ContrastRatio :contrast="previewContrast.selectedPostText" /> + <ColorInput + v-model="selectedPostLinkColorLocal" + name="selectedPostLink" + :label="$t('settings.links')" + :fallback="previewTheme.colors.selectedPostLink" + /> + <ContrastRatio :contrast="previewContrast.selectedPostLink" /> + </div> + <div class="color-item"> + <h4>{{ $t('settings.style.advanced_colors.selectedMenu') }}</h4> + <ColorInput + v-model="selectedMenuColorLocal" + name="selectedMenu" + :label="$t('settings.background')" + :fallback="previewTheme.colors.selectedMenu" + /> + <ColorInput + v-model="selectedMenuTextColorLocal" + name="selectedMenuText" + :label="$t('settings.text')" + :fallback="previewTheme.colors.selectedMenuText" + /> + <ContrastRatio :contrast="previewContrast.selectedMenuText" /> + <ColorInput + v-model="selectedMenuLinkColorLocal" + name="selectedMenuLink" + :label="$t('settings.links')" + :fallback="previewTheme.colors.selectedMenuLink" /> + <ContrastRatio :contrast="previewContrast.selectedMenuLink" /> </div> </div> @@ -491,7 +860,7 @@ {{ $t('settings.style.switcher.clear_all') }} </button> </div> - <shadow-control + <ShadowControl v-model="currentShadow" :ready="!!currentShadowFallback" :fallback="currentShadowFallback" diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index 3e5eacd5..df585faa 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -52,6 +52,11 @@ margin-bottom: 6px - 99px; white-space: nowrap; + color: $fallback--text; + color: var(--tabText, $fallback--text); + background-color: $fallback--fg; + background-color: var(--tab, $fallback--fg); + &:not(.active) { z-index: 4; @@ -63,6 +68,8 @@ &.active { background: transparent; z-index: 5; + color: $fallback--text; + color: var(--tabActiveText, $fallback--text); } img { diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 93d55fff..3988ff1c 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -151,7 +151,7 @@ </ProgressButton> <ProgressButton v-else - class="btn btn-default pressed" + class="btn btn-default toggled" :click="unsubscribeUser" :title="$t('user_card.unsubscribe')" > @@ -162,7 +162,7 @@ <div> <button v-if="user.muted" - class="btn btn-default btn-block pressed" + class="btn btn-default btn-block toggled" @click="unmuteUser" > {{ $t('user_card.muted') }} @@ -299,6 +299,11 @@ &-bio { text-align: center; + a { + color: $fallback--link; + color: var(--postLink, $fallback--link); + } + img { object-fit: contain; vertical-align: middle; @@ -460,14 +465,13 @@ color: var(--text, $fallback--text); } - // TODO use proper colors .staff { flex: none; text-transform: capitalize; color: $fallback--text; - color: var(--btnText, $fallback--text); + color: var(--alertNeutralText, $fallback--text); background-color: $fallback--fg; - background-color: var(--btn, $fallback--fg); + background-color: var(--alertNeutral, $fallback--fg); } } @@ -538,12 +542,6 @@ button { margin: 0; - - &.pressed { - // TODO: This should be themed. - border-bottom-color: rgba(255, 255, 255, 0.2); - border-top-color: rgba(0, 0, 0, 0.2); - } } } } |
