@@ -45,6 +45,7 @@ import type {
4545 NeutralColors ,
4646 PrimaryColors ,
4747 ThemeConfig ,
48+ ColorShades ,
4849 ThemeConfigText ,
4950 ThemeConfigOutline ,
5051 ThemeConfigRounding ,
@@ -359,6 +360,40 @@ export function normalizeThemeConfig(theme: any): MergedThemeConfig {
359360 } ;
360361}
361362
363+ function isColorShadesObject ( color : unknown ) : color is Partial < ColorShades > {
364+ return typeof color === "object" && color !== null && ! Array . isArray ( color ) ;
365+ }
366+
367+ function validateColorShades ( color : Partial < ColorShades > , colorType : string ) {
368+ const validShades = COLOR_SHADES ;
369+ const providedShades = Object . keys ( color ) ;
370+
371+ const invalidShades = providedShades . filter ( ( shade ) => ! validShades . includes ( Number ( shade ) ) ) ;
372+ const missingShades = validShades . filter ( ( shade ) => ! ( String ( shade ) in color ) ) ;
373+
374+ if ( invalidShades . length > 0 ) {
375+ const invalidShadesStr = invalidShades . join ( ", " ) ;
376+ const validShadesStr = validShades . join ( ", " ) ;
377+
378+ // eslint-disable-next-line no-console
379+ console . warn (
380+ `[vueless] Invalid shade keys found in ${ colorType } color object: ${ invalidShadesStr } . ` +
381+ `Valid shades are: ${ validShadesStr } .` ,
382+ ) ;
383+ }
384+
385+ if ( missingShades . length > 0 ) {
386+ const missingShadesStr = missingShades . join ( ", " ) ;
387+ const validShadesStr = validShades . join ( ", " ) ;
388+
389+ // eslint-disable-next-line no-console
390+ console . warn (
391+ `[vueless] Missing shade keys in ${ colorType } color object: ${ missingShadesStr } . ` +
392+ `All shades (${ validShadesStr } ) should be defined.` ,
393+ ) ;
394+ }
395+ }
396+
362397/**
363398 * Determines if the provided color mode configuration has a primary color
364399 * that differs from the default color mode configuration.
@@ -377,13 +412,35 @@ function hasPrimaryColor(
377412
378413/**
379414 * Retrieve primary color value and save them to cookie and localStorage.
380- * @return string - primary color.
415+ * @return string | Partial<ColorShades> - primary color.
381416 */
382417function getPrimaryColor ( primary ?: PrimaryColors ) {
383418 const storageKey = `vl-${ PRIMARY_COLOR } ` ;
384419
420+ const storedValue = getStored ( storageKey ) ;
421+ let parsedStoredValue : PrimaryColors | undefined ;
422+
423+ if ( storedValue ) {
424+ try {
425+ parsedStoredValue = JSON . parse ( storedValue ) ;
426+ } catch {
427+ parsedStoredValue = storedValue ;
428+ }
429+ }
430+
385431 let primaryColor : PrimaryColors =
386- primary ?? getStored ( storageKey ) ?? vuelessConfig . primary ?? DEFAULT_PRIMARY_COLOR ;
432+ primary ?? parsedStoredValue ?? vuelessConfig . primary ?? DEFAULT_PRIMARY_COLOR ;
433+
434+ if ( isColorShadesObject ( primaryColor ) ) {
435+ validateColorShades ( primaryColor , PRIMARY_COLOR ) ;
436+
437+ if ( isCSR && primary ) {
438+ setCookie ( storageKey , JSON . stringify ( primaryColor ) ) ;
439+ localStorage . setItem ( storageKey , JSON . stringify ( primaryColor ) ) ;
440+ }
441+
442+ return primaryColor ;
443+ }
387444
388445 const isPrimaryColor =
389446 PRIMARY_COLORS . some ( ( color ) => color === primaryColor ) || primaryColor === GRAYSCALE_COLOR ;
@@ -405,13 +462,35 @@ function getPrimaryColor(primary?: PrimaryColors) {
405462
406463/**
407464 * Retrieve neutral color value and save them to cookie and localStorage.
408- * @return string - neutral color.
465+ * @return string | Partial<ColorShades> - neutral color.
409466 */
410467function getNeutralColor ( neutral ?: NeutralColors ) {
411468 const storageKey = `vl-${ NEUTRAL_COLOR } ` ;
412469
470+ const storedValue = getStored ( storageKey ) ;
471+ let parsedStoredValue : NeutralColors | undefined ;
472+
473+ if ( storedValue ) {
474+ try {
475+ parsedStoredValue = JSON . parse ( storedValue ) ;
476+ } catch {
477+ parsedStoredValue = storedValue ;
478+ }
479+ }
480+
413481 let neutralColor : NeutralColors =
414- neutral ?? getStored ( storageKey ) ?? vuelessConfig . neutral ?? DEFAULT_NEUTRAL_COLOR ;
482+ neutral ?? parsedStoredValue ?? vuelessConfig . neutral ?? DEFAULT_NEUTRAL_COLOR ;
483+
484+ if ( isColorShadesObject ( neutralColor ) ) {
485+ validateColorShades ( neutralColor , NEUTRAL_COLOR ) ;
486+
487+ if ( isCSR && neutral ) {
488+ setCookie ( storageKey , JSON . stringify ( neutralColor ) ) ;
489+ localStorage . setItem ( storageKey , JSON . stringify ( neutralColor ) ) ;
490+ }
491+
492+ return neutralColor ;
493+ }
415494
416495 const isNeutralColor = NEUTRAL_COLORS . some ( ( color ) => color === neutralColor ) ;
417496
@@ -733,14 +812,34 @@ export function setRootCSSVariables(vars: MergedThemeConfig) {
733812 "--vl-disabled-opacity" : `${ vars . disabledOpacity } %` ,
734813 } ;
735814
736- for ( const shade of COLOR_SHADES ) {
737- variables [ `--vl-${ PRIMARY_COLOR } -${ shade } ` as keyof VuelessCssVariables ] =
738- `var(--color-${ vars . primary } -${ shade } )` ;
815+ if ( isColorShadesObject ( vars . primary ) ) {
816+ for ( const shade of COLOR_SHADES ) {
817+ const shadeValue = vars . primary [ shade as keyof ColorShades ] ;
818+
819+ if ( shadeValue ) {
820+ variables [ `--vl-${ PRIMARY_COLOR } -${ shade } ` as keyof VuelessCssVariables ] = shadeValue ;
821+ }
822+ }
823+ } else {
824+ for ( const shade of COLOR_SHADES ) {
825+ variables [ `--vl-${ PRIMARY_COLOR } -${ shade } ` as keyof VuelessCssVariables ] =
826+ `var(--color-${ vars . primary } -${ shade } )` ;
827+ }
739828 }
740829
741- for ( const shade of COLOR_SHADES ) {
742- variables [ `--vl-${ NEUTRAL_COLOR } -${ shade } ` as keyof VuelessCssVariables ] =
743- `var(--color-${ vars . neutral } -${ shade } )` ;
830+ if ( isColorShadesObject ( vars . neutral ) ) {
831+ for ( const shade of COLOR_SHADES ) {
832+ const shadeValue = vars . neutral [ shade as keyof ColorShades ] ;
833+
834+ if ( shadeValue ) {
835+ variables [ `--vl-${ NEUTRAL_COLOR } -${ shade } ` as keyof VuelessCssVariables ] = shadeValue ;
836+ }
837+ }
838+ } else {
839+ for ( const shade of COLOR_SHADES ) {
840+ variables [ `--vl-${ NEUTRAL_COLOR } -${ shade } ` as keyof VuelessCssVariables ] =
841+ `var(--color-${ vars . neutral } -${ shade } )` ;
842+ }
744843 }
745844
746845 const [ light , dark ] = generateCSSColorVariables ( vars . lightTheme ?? { } , vars . darkTheme ?? { } ) ;
0 commit comments