mirror of
https://github.com/unraid/api.git
synced 2025-12-31 21:49:57 -06:00
fix(theme): update theme class naming and scoping logic
- Changed theme class names from `.theme-*` to `.Theme--*` for consistency. - Updated scoping logic to prevent scoping of `.Theme--` classes, ensuring they remain global. - Enhanced theme store logic to check for existing `.Theme--` classes before applying new theme classes, preventing conflicts. - Adjusted class cleaning logic to retain `.Theme--` classes when necessary.
This commit is contained in:
@@ -5,8 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Default/White Theme */
|
/* Default/White Theme */
|
||||||
:root,
|
.Theme--white {
|
||||||
.theme-white {
|
|
||||||
--header-text-primary: #ffffff;
|
--header-text-primary: #ffffff;
|
||||||
--header-text-secondary: #999999;
|
--header-text-secondary: #999999;
|
||||||
--header-background-color: #1c1b1b;
|
--header-background-color: #1c1b1b;
|
||||||
@@ -20,8 +19,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Black Theme */
|
/* Black Theme */
|
||||||
.theme-black,
|
.Theme--black,
|
||||||
.theme-black.dark {
|
.Theme--black.dark {
|
||||||
--header-text-primary: #1c1b1b;
|
--header-text-primary: #1c1b1b;
|
||||||
--header-text-secondary: #999999;
|
--header-text-secondary: #999999;
|
||||||
--header-background-color: #f2f2f2;
|
--header-background-color: #f2f2f2;
|
||||||
@@ -35,7 +34,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Gray Theme */
|
/* Gray Theme */
|
||||||
.theme-gray {
|
.Theme--gray {
|
||||||
--header-text-primary: #ffffff;
|
--header-text-primary: #ffffff;
|
||||||
--header-text-secondary: #999999;
|
--header-text-secondary: #999999;
|
||||||
--header-background-color: #1c1b1b;
|
--header-background-color: #1c1b1b;
|
||||||
@@ -49,7 +48,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Azure Theme */
|
/* Azure Theme */
|
||||||
.theme-azure {
|
.Theme--azure {
|
||||||
--header-text-primary: #1c1b1b;
|
--header-text-primary: #1c1b1b;
|
||||||
--header-text-secondary: #999999;
|
--header-text-secondary: #999999;
|
||||||
--header-background-color: #f2f2f2;
|
--header-background-color: #f2f2f2;
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ const DEFAULT_INCLUDE_ROOT = true;
|
|||||||
|
|
||||||
const KEYFRAME_AT_RULES = new Set(['keyframes']);
|
const KEYFRAME_AT_RULES = new Set(['keyframes']);
|
||||||
const NON_SCOPED_AT_RULES = new Set(['font-face', 'page']);
|
const NON_SCOPED_AT_RULES = new Set(['font-face', 'page']);
|
||||||
const MERGE_WITH_SCOPE_PATTERNS: RegExp[] = [/^\.theme-/, /^\.has-custom-/, /^\.dark\b/];
|
const MERGE_WITH_SCOPE_PATTERNS: RegExp[] = [/^\.has-custom-/, /^\.dark\b/];
|
||||||
|
const UNSCOPED_PATTERNS: RegExp[] = [/^\.Theme--/];
|
||||||
|
|
||||||
function shouldScopeRule(rule: Rule, targetLayers: Set<string>, includeRootRules: boolean): boolean {
|
function shouldScopeRule(rule: Rule, targetLayers: Set<string>, includeRootRules: boolean): boolean {
|
||||||
const hasSelectorString = typeof rule.selector === 'string' && rule.selector.length > 0;
|
const hasSelectorString = typeof rule.selector === 'string' && rule.selector.length > 0;
|
||||||
@@ -104,6 +105,12 @@ function prefixSelector(selector: string, scope: string): string {
|
|||||||
return trimmed;
|
return trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not scope Theme-- classes - they should remain global
|
||||||
|
const firstToken = trimmed.split(/[\s>+~]/, 1)[0] ?? '';
|
||||||
|
if (!firstToken.includes('\\:') && UNSCOPED_PATTERNS.some((pattern) => pattern.test(firstToken))) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
if (trimmed === ':root') {
|
if (trimmed === ':root') {
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
@@ -112,7 +119,6 @@ function prefixSelector(selector: string, scope: string): string {
|
|||||||
return `${scope}${trimmed.slice(':root'.length)}`;
|
return `${scope}${trimmed.slice(':root'.length)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstToken = trimmed.split(/[\s>+~]/, 1)[0] ?? '';
|
|
||||||
const shouldMergeWithScope =
|
const shouldMergeWithScope =
|
||||||
!firstToken.includes('\\:') && MERGE_WITH_SCOPE_PATTERNS.some((pattern) => pattern.test(firstToken));
|
!firstToken.includes('\\:') && MERGE_WITH_SCOPE_PATTERNS.some((pattern) => pattern.test(firstToken));
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,11 @@ export const useThemeStore = defineStore(
|
|||||||
const setCssVars = () => {
|
const setCssVars = () => {
|
||||||
const selectedTheme = theme.value.name;
|
const selectedTheme = theme.value.name;
|
||||||
|
|
||||||
|
// Check if Unraid PHP has already set a Theme-- class
|
||||||
|
const hasExistingThemeClass =
|
||||||
|
typeof document !== 'undefined' &&
|
||||||
|
Array.from(document.documentElement.classList).some((cls) => cls.startsWith('Theme--'));
|
||||||
|
|
||||||
// Prepare Tailwind v4 theme classes
|
// Prepare Tailwind v4 theme classes
|
||||||
const themeClasses: string[] = [];
|
const themeClasses: string[] = [];
|
||||||
const customClasses: string[] = [];
|
const customClasses: string[] = [];
|
||||||
@@ -177,8 +182,10 @@ export const useThemeStore = defineStore(
|
|||||||
themeClasses.push('dark');
|
themeClasses.push('dark');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply theme-specific class for Tailwind v4 theme variants
|
// Only apply theme-specific class if Unraid PHP hasn't already set it
|
||||||
themeClasses.push(`theme-${selectedTheme}`);
|
if (!hasExistingThemeClass) {
|
||||||
|
themeClasses.push(`Theme--${selectedTheme}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Only set CSS variables for dynamic/user-configured values from GraphQL
|
// Only set CSS variables for dynamic/user-configured values from GraphQL
|
||||||
// Static theme values are handled by Tailwind v4 theme classes in @tailwind-shared
|
// Static theme values are handled by Tailwind v4 theme classes in @tailwind-shared
|
||||||
@@ -220,22 +227,33 @@ export const useThemeStore = defineStore(
|
|||||||
...Array.from(document.querySelectorAll<HTMLElement>('.unapi')),
|
...Array.from(document.querySelectorAll<HTMLElement>('.unapi')),
|
||||||
];
|
];
|
||||||
|
|
||||||
const cleanClassList = (classList: string) =>
|
const cleanClassList = (classList: string, isDocumentElement: boolean) => {
|
||||||
classList
|
// Don't remove Theme-- classes from documentElement if Unraid PHP set them
|
||||||
|
if (isDocumentElement && hasExistingThemeClass) {
|
||||||
|
return classList
|
||||||
|
.split(' ')
|
||||||
|
.filter((c) => c !== 'dark' && !c.startsWith('has-custom-') && c !== 'has-banner-gradient')
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
// For .unapi roots or when we're managing the theme class, clean everything
|
||||||
|
return classList
|
||||||
.split(' ')
|
.split(' ')
|
||||||
.filter(
|
.filter(
|
||||||
(c) =>
|
(c) =>
|
||||||
!c.startsWith('theme-') &&
|
!c.startsWith('Theme--') &&
|
||||||
c !== 'dark' &&
|
c !== 'dark' &&
|
||||||
!c.startsWith('has-custom-') &&
|
!c.startsWith('has-custom-') &&
|
||||||
c !== 'has-banner-gradient'
|
c !== 'has-banner-gradient'
|
||||||
)
|
)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
};
|
||||||
|
|
||||||
// Apply theme and custom classes to html element and all .unapi roots
|
// Apply theme and custom classes to html element and all .unapi roots
|
||||||
scopedTargets.forEach((target) => {
|
scopedTargets.forEach((target) => {
|
||||||
target.className = cleanClassList(target.className);
|
const isDocumentElement = target === document.documentElement;
|
||||||
|
target.className = cleanClassList(target.className, isDocumentElement);
|
||||||
[...themeClasses, ...customClasses].forEach((cls) => target.classList.add(cls));
|
[...themeClasses, ...customClasses].forEach((cls) => target.classList.add(cls));
|
||||||
|
|
||||||
if (darkMode.value) {
|
if (darkMode.value) {
|
||||||
|
|||||||
Reference in New Issue
Block a user