diff --git a/materialious/src/lib/components/settings/Interface.svelte b/materialious/src/lib/components/settings/Interface.svelte
index 6c55af03..80850964 100644
--- a/materialious/src/lib/components/settings/Interface.svelte
+++ b/materialious/src/lib/components/settings/Interface.svelte
@@ -1,24 +1,18 @@
+
+
+
+
+{#if colorPickerOpen}
+
+
+
+
+
+{/if}
+
+
+
+{#if $darkModeStore}
+
+
+
+{/if}
+
+{$_('layout.theme.advanced')}
+
+
+{#if currentThemeColors}
+ {#each Object.entries(currentThemeColors) as [themeVar, themeColor] (themeVar)}
+
+ await colorOnInput(color, themeVar as ThemeKey)}
+ />
+
+ {/each}
+{/if}
+
+
diff --git a/materialious/src/lib/css/global.css b/materialious/src/lib/css/global.css
index 08a08186..aa68c71a 100644
--- a/materialious/src/lib/css/global.css
+++ b/materialious/src/lib/css/global.css
@@ -103,7 +103,7 @@ menu {
right: 0;
}
-.color-picker .picker {
+.global-color-picker .picker {
display: none !important;
}
diff --git a/materialious/src/lib/i18n/locales/en.json b/materialious/src/lib/i18n/locales/en.json
index 686dfe62..7df36eb9 100644
--- a/materialious/src/lib/i18n/locales/en.json
+++ b/materialious/src/lib/i18n/locales/en.json
@@ -199,7 +199,8 @@
"darkMode": "Dark mode",
"lightMode": "Light mode",
"AmoledTheme": "Amoled theme",
- "color": "Color"
+ "color": "Color",
+ "advanced": "Advanced theming"
},
"searchSuggestions": "Search suggestions",
"previewVideoOnHover": "Preview video on hover",
diff --git a/materialious/src/lib/store.ts b/materialious/src/lib/store.ts
index 075f43d2..c4ce8d6c 100644
--- a/materialious/src/lib/store.ts
+++ b/materialious/src/lib/store.ts
@@ -24,6 +24,7 @@ import { ensureNoTrailingSlash, getPublicEnv } from './misc';
import type { EngineFallback } from './api/misc';
import type z from 'zod';
import type { zFilterSchema } from './filtering/index';
+import type { ThemeColors } from './theme';
function createListenerFunctions(): {
callListeners: (eventKey: string, newValue: any) => void;
@@ -260,6 +261,11 @@ export const interfaceAndroidUseNativeShare = persist(
createStorage(),
'androidUseNativeShare'
);
+export const interfaceAdvancedThemingStore: Writable = persist(
+ writable({}),
+ createStorage(),
+ 'advancedTheming'
+);
export const sponsorBlockStore = persist(writable(true), createStorage(), 'sponsorBlock');
export const sponsorBlockUrlStore: Writable = persist(
diff --git a/materialious/src/lib/theme.ts b/materialious/src/lib/theme.ts
index 331515e1..4ac0f303 100644
--- a/materialious/src/lib/theme.ts
+++ b/materialious/src/lib/theme.ts
@@ -4,16 +4,61 @@ import { get } from 'svelte/store';
import { SystemBars, SystemBarsStyle } from '@capacitor/core';
import { darkModeStore, interfaceAmoledTheme } from './store';
-export async function getDynamicTheme(mode?: string): Promise> {
+export type ThemeKey =
+ | '--primary'
+ | '--on-primary'
+ | '--primary-container'
+ | '--on-primary-container'
+ | '--secondary'
+ | '--on-secondary'
+ | '--secondary-container'
+ | '--on-secondary-container'
+ | '--tertiary'
+ | '--on-tertiary'
+ | '--tertiary-container'
+ | '--on-tertiary-container'
+ | '--error'
+ | '--on-error'
+ | '--error-container'
+ | '--on-error-container'
+ | '--background'
+ | '--on-background'
+ | '--surface'
+ | '--on-surface'
+ | '--surface-variant'
+ | '--on-surface-variant'
+ | '--outline'
+ | '--outline-variant'
+ | '--shadow'
+ | '--scrim'
+ | '--inverse-surface'
+ | '--inverse-on-surface'
+ | '--inverse-primary'
+ | '--surface-dim'
+ | '--surface-bright'
+ | '--surface-container-lowest'
+ | '--surface-container-low'
+ | '--surface-container'
+ | '--surface-container-high'
+ | '--surface-container-highest';
+
+export type ThemeColors = Partial>;
+
+export async function getDynamicTheme(mode?: string): Promise {
const givenSettings = await ui('theme');
+ if (typeof givenSettings !== 'object') return {};
+
// @ts-expect-error Works as expected
const themes: string = givenSettings[mode ? mode : (ui('mode') as string)];
const themeVars: Record = {};
- themes.split(';').forEach((keyVar) => {
+ for (const keyVar of themes.split(';')) {
const [key, value] = keyVar.split(':');
- themeVars[key] = value;
- });
+ themeVars[key] = window.getComputedStyle(document.body).getPropertyValue(key) ?? value;
+ }
+
+ delete themeVars[''];
+
return themeVars;
}
@@ -35,7 +80,7 @@ export function setAmoledTheme() {
const isDark = get(darkModeStore);
if (isAmoled && isDark) {
- const rootVars = [
+ const rootVars: ThemeKey[] = [
'--surface-container',
'--surface',
'--surface-container-lowest',
@@ -44,7 +89,7 @@ export function setAmoledTheme() {
'--surface-container-highest'
];
rootVars.forEach((varName) => {
- document.body.style.setProperty(varName, '#000');
+ setThemeColor(varName, '#000');
});
} else {
setTheme();
@@ -70,3 +115,53 @@ export function setTheme() {
}
}
}
+
+export const SUPPORTED_THEME_KEYS: ThemeKey[] = [
+ '--primary',
+ '--on-primary',
+ '--primary-container',
+ '--on-primary-container',
+ '--secondary',
+ '--on-secondary',
+ '--secondary-container',
+ '--on-secondary-container',
+ '--tertiary',
+ '--on-tertiary',
+ '--tertiary-container',
+ '--on-tertiary-container',
+ '--error',
+ '--on-error',
+ '--error-container',
+ '--on-error-container',
+ '--background',
+ '--on-background',
+ '--surface',
+ '--on-surface',
+ '--surface-variant',
+ '--on-surface-variant',
+ '--outline',
+ '--outline-variant',
+ '--shadow',
+ '--scrim',
+ '--inverse-surface',
+ '--inverse-on-surface',
+ '--inverse-primary',
+ '--surface-dim',
+ '--surface-bright',
+ '--surface-container-lowest',
+ '--surface-container-low',
+ '--surface-container',
+ '--surface-container-high',
+ '--surface-container-highest'
+] as const;
+
+export function setThemeColors(theme: ThemeColors) {
+ for (const [themeKey, themeValue] of Object.entries(theme)) {
+ setThemeColor(themeKey as ThemeKey, themeValue.trim());
+ }
+}
+
+export function setThemeColor(theme: ThemeKey, color: string) {
+ document.documentElement.style.setProperty(theme, color.trim());
+ document.body.style.setProperty(theme, color.trim());
+}