1
0

vite-plugin-theme.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import { Plugin } from 'vite';
  2. type ThemeColors = {
  3. background?: string;
  4. foreground?: string;
  5. card?: string;
  6. 'card-foreground'?: string;
  7. popover?: string;
  8. 'popover-foreground'?: string;
  9. primary?: string;
  10. 'primary-foreground'?: string;
  11. secondary?: string;
  12. 'secondary-foreground'?: string;
  13. muted?: string;
  14. 'muted-foreground'?: string;
  15. accent?: string;
  16. 'accent-foreground'?: string;
  17. destructive?: string;
  18. 'destructive-foreground'?: string;
  19. success?: string;
  20. 'success-foreground'?: string;
  21. 'dev-mode'?: string;
  22. 'dev-mode-foreground'?: string;
  23. border?: string;
  24. input?: string;
  25. ring?: string;
  26. 'chart-1'?: string;
  27. 'chart-2'?: string;
  28. 'chart-3'?: string;
  29. 'chart-4'?: string;
  30. 'chart-5'?: string;
  31. radius?: string;
  32. sidebar?: string;
  33. 'sidebar-foreground'?: string;
  34. 'sidebar-primary'?: string;
  35. 'sidebar-primary-foreground'?: string;
  36. 'sidebar-accent'?: string;
  37. 'sidebar-accent-foreground'?: string;
  38. 'sidebar-border'?: string;
  39. 'sidebar-ring'?: string;
  40. brand?: string;
  41. 'brand-lighter'?: string;
  42. 'brand-darker'?: string;
  43. 'font-sans'?: string;
  44. 'font-mono'?: string;
  45. [key: string]: string | undefined;
  46. };
  47. export interface ThemeVariables {
  48. light?: ThemeColors;
  49. dark?: ThemeColors;
  50. }
  51. const defaultVariables: ThemeVariables = {
  52. light: {
  53. background: 'oklch(1.0000 0 0)',
  54. foreground: 'oklch(0.2103 0.0059 285.8852)',
  55. card: 'oklch(1.0000 0 0)',
  56. 'card-foreground': 'oklch(0.2103 0.0059 285.8852)',
  57. popover: 'oklch(1.0000 0 0)',
  58. 'popover-foreground': 'oklch(0.2103 0.0059 285.8852)',
  59. primary: 'oklch(0.7613 0.1503 231.1314)',
  60. 'primary-foreground': 'oklch(0.261 0.043 218.379)',
  61. secondary: 'oklch(0.9674 0.0013 286.3752)',
  62. 'secondary-foreground': 'oklch(0.2103 0.0059 285.8852)',
  63. muted: 'oklch(0.9674 0.0013 286.3752)',
  64. 'muted-foreground': 'oklch(0.5517 0.0138 285.9385)',
  65. accent: 'oklch(0.9674 0.0013 286.3752)',
  66. 'accent-foreground': 'oklch(0.2103 0.0059 285.8852)',
  67. destructive: 'oklch(0.505 0.188 27.325)',
  68. 'destructive-foreground': 'oklch(0.9851 0 0)',
  69. success: 'hsl(99deg 67.25% 33.2%)',
  70. 'success-foreground': 'hsl(0 0% 98%)',
  71. 'dev-mode': 'hsl(204, 76%, 62%)',
  72. 'dev-mode-foreground': 'hsl(0 0% 98%)',
  73. border: 'oklch(0.9197 0.0040 286.3202)',
  74. input: 'oklch(0.9197 0.0040 286.3202)',
  75. ring: 'oklch(0.7613 0.1503 231.1314)',
  76. 'chart-1': 'oklch(0.7613 0.1503 231.1314)',
  77. 'chart-2': 'oklch(0.5575 0.2525 302.3212)',
  78. 'chart-3': 'oklch(0.5858 0.2220 17.5846)',
  79. 'chart-4': 'oklch(0.6658 0.1574 58.3183)',
  80. 'chart-5': 'oklch(0.6271 0.1699 149.2138)',
  81. radius: '0.375rem',
  82. sidebar: 'oklch(0.9674 0.0013 286.3752)',
  83. 'sidebar-foreground': 'oklch(0.2103 0.0059 285.8852)',
  84. 'sidebar-primary': 'oklch(0.7613 0.1503 231.1314)',
  85. 'sidebar-primary-foreground': 'oklch(0.1408 0.0044 285.8229)',
  86. 'sidebar-accent': 'oklch(1.0000 0 0)',
  87. 'sidebar-accent-foreground': 'oklch(0.2103 0.0059 285.8852)',
  88. 'sidebar-border': 'oklch(0.9197 0.0040 286.3202)',
  89. 'sidebar-ring': 'oklch(0.7613 0.1503 231.1314)',
  90. brand: '#17c1ff',
  91. 'brand-lighter': '#e6f9ff',
  92. 'brand-darker': '#0099ff',
  93. 'font-sans': 'Inter, sans-serif',
  94. 'font-mono': 'Geist Mono, monospace',
  95. },
  96. dark: {
  97. background: 'oklch(0.1408 0.0044 285.8229)',
  98. foreground: 'oklch(0.9851 0 0)',
  99. card: 'oklch(0.2103 0.0059 285.8852)',
  100. 'card-foreground': 'oklch(0.9851 0 0)',
  101. popover: 'oklch(0.2103 0.0059 285.8852)',
  102. 'popover-foreground': 'oklch(0.9851 0 0)',
  103. primary: 'oklch(0.7613 0.1503 231.1314)',
  104. 'primary-foreground': 'oklch(0.1408 0.0044 285.8229)',
  105. secondary: 'oklch(0.2739 0.0055 286.0326)',
  106. 'secondary-foreground': 'oklch(0.9851 0 0)',
  107. muted: 'oklch(0.2739 0.0055 286.0326)',
  108. 'muted-foreground': 'oklch(0.7118 0.0129 286.0665)',
  109. accent: 'oklch(0.2739 0.0055 286.0326)',
  110. 'accent-foreground': 'oklch(0.9851 0 0)',
  111. destructive: 'oklch(0.4 0.15 27.11)',
  112. 'destructive-foreground': 'oklch(0.9851 0 0)',
  113. success: 'hsl(100 76.42% 22.21%)',
  114. 'success-foreground': 'hsl(0 0% 98%)',
  115. 'dev-mode': 'hsl(204, 86%, 53%)',
  116. 'dev-mode-foreground': 'hsl(0 0% 98%)',
  117. border: 'oklch(0.2739 0.0055 286.0326)',
  118. input: 'oklch(0.2739 0.0055 286.0326)',
  119. ring: 'oklch(0.7613 0.1503 231.1314)',
  120. 'chart-1': 'oklch(0.7613 0.1503 231.1314)',
  121. 'chart-2': 'oklch(0.6268 0.2325 303.9004)',
  122. 'chart-3': 'oklch(0.6450 0.2154 16.4393)',
  123. 'chart-4': 'oklch(0.7686 0.1647 70.0804)',
  124. 'chart-5': 'oklch(0.7227 0.1920 149.5793)',
  125. sidebar: 'oklch(0.2 0 0)',
  126. 'sidebar-foreground': 'oklch(0.9851 0 0)',
  127. 'sidebar-primary': 'oklch(0.7613 0.1503 231.1314)',
  128. 'sidebar-primary-foreground': 'oklch(0.1408 0.0044 285.8229)',
  129. 'sidebar-accent': 'oklch(0.2739 0.0055 286.0326)',
  130. 'sidebar-accent-foreground': 'oklch(0.9851 0 0)',
  131. 'sidebar-border': 'oklch(0.2739 0.0055 286.0326)',
  132. 'sidebar-ring': 'oklch(0.7613 0.1503 231.1314)',
  133. brand: '#17c1ff',
  134. 'brand-lighter': '#e6f9ff',
  135. 'brand-darker': '#0099ff',
  136. 'font-sans': 'Inter, sans-serif',
  137. 'font-mono': 'Geist Mono, monospace',
  138. },
  139. };
  140. export type ThemeVariablesPluginOptions = {
  141. theme?: ThemeVariables;
  142. };
  143. export function themeVariablesPlugin(options: ThemeVariablesPluginOptions): Plugin {
  144. const virtualModuleId = 'virtual:admin-theme';
  145. const resolvedVirtualModuleId = `\0${virtualModuleId}`;
  146. return {
  147. name: 'vendure:admin-theme',
  148. enforce: 'pre', // This ensures our plugin runs before other CSS processors
  149. transform(code, id) {
  150. // Only transform CSS files
  151. if (!id.endsWith('styles.css')) {
  152. return null;
  153. }
  154. // Replace the @import 'virtual:admin-theme'; with our theme variables
  155. if (
  156. code.includes('@import "virtual:admin-theme";') ||
  157. code.includes("@import 'virtual:admin-theme';")
  158. ) {
  159. const lightTheme = options.theme?.light || {};
  160. const darkTheme = options.theme?.dark || {};
  161. // Merge default themes with custom themes
  162. const mergedLightTheme = { ...defaultVariables.light, ...lightTheme };
  163. const mergedDarkTheme = { ...defaultVariables.dark, ...darkTheme };
  164. const themeCSS = `
  165. :root {
  166. ${Object.entries(mergedLightTheme)
  167. .filter(([key, value]) => value !== undefined)
  168. .map(([key, value]) => `--${key}: ${value as string};`)
  169. .join('\n')}
  170. }
  171. .dark {
  172. ${Object.entries(mergedDarkTheme)
  173. .filter(([key, value]) => value !== undefined)
  174. .map(([key, value]) => `--${key}: ${value as string};`)
  175. .join('\n')}
  176. }
  177. `;
  178. return code.replace(/@import ['"]virtual:admin-theme['"];?/, themeCSS);
  179. }
  180. return null;
  181. },
  182. };
  183. }