@trishchuk/coolors-mcp 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.claude/settings.local.json +2 -6
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +20 -8
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +22 -8
  4. package/.github/pull_request_template.md +33 -8
  5. package/.github/workflows/ci.yml +97 -97
  6. package/.github/workflows/deploy-docs.yml +9 -9
  7. package/.github/workflows/release.yml +15 -15
  8. package/README.md +26 -1
  9. package/TOOLS_UK.md +233 -0
  10. package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js +30 -12
  11. package/docs/.vitepress/cache/deps/_metadata.json +1 -1
  12. package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -6
  13. package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js +2543 -1612
  14. package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js +3508 -2529
  15. package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js +1902 -1003
  16. package/docs/.vitepress/cache/deps/cytoscape.js +13303 -7347
  17. package/docs/.vitepress/cache/deps/dayjs.js +494 -272
  18. package/docs/.vitepress/cache/deps/debug.js +82 -38
  19. package/docs/.vitepress/cache/deps/prismjs.js +444 -272
  20. package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js +80 -73
  21. package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js +93 -62
  22. package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js +13 -13
  23. package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js +34 -27
  24. package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js +20 -17
  25. package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js +75 -41
  26. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +2005 -1438
  27. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +2 -2
  28. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +566 -229
  29. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +382 -270
  30. package/docs/.vitepress/cache/deps/vitepress___minisearch.js +334 -125
  31. package/docs/.vitepress/cache/deps/vue.js +2 -2
  32. package/docs/.vitepress/components/ClientGrid.vue +9 -3
  33. package/docs/.vitepress/components/CodeBlock.vue +51 -44
  34. package/docs/.vitepress/components/ConfigModal.vue +151 -67
  35. package/docs/.vitepress/components/DiagramModal.vue +186 -154
  36. package/docs/.vitepress/components/TroubleshootingModal.vue +101 -96
  37. package/docs/.vitepress/config.js +171 -141
  38. package/docs/.vitepress/theme/FundingLayout.vue +65 -54
  39. package/docs/.vitepress/theme/Layout.vue +21 -21
  40. package/docs/.vitepress/theme/components/AdBanner.vue +73 -52
  41. package/docs/.vitepress/theme/components/AdPlaceholder.vue +3 -3
  42. package/docs/.vitepress/theme/components/FundingEffects.vue +77 -53
  43. package/docs/.vitepress/theme/components/FundingHero.vue +78 -63
  44. package/docs/.vitepress/theme/components/SupportSection.vue +106 -89
  45. package/docs/.vitepress/theme/custom-app.css +19 -12
  46. package/docs/.vitepress/theme/custom.css +33 -25
  47. package/docs/.vitepress/theme/index.js +19 -16
  48. package/docs/concepts/accessibility.md +59 -47
  49. package/docs/concepts/color-spaces.md +28 -6
  50. package/docs/concepts/distance-metrics.md +45 -30
  51. package/docs/concepts/hct.md +30 -27
  52. package/docs/concepts/image-analysis.md +52 -21
  53. package/docs/concepts/material-design.md +43 -17
  54. package/docs/concepts/theme-matching.md +64 -40
  55. package/docs/examples/basic-colors.md +92 -108
  56. package/docs/examples/creating-themes.md +104 -108
  57. package/docs/examples/css-refactoring.md +33 -29
  58. package/docs/examples/image-extraction.md +145 -138
  59. package/docs/getting-started.md +45 -34
  60. package/docs/index.md +5 -1
  61. package/docs/installation.md +15 -1
  62. package/docs/tools/accessibility.md +74 -68
  63. package/docs/tools/image-extraction.md +62 -54
  64. package/docs/tools/theme-matching.md +45 -42
  65. package/note.md +1 -2
  66. package/package.json +2 -2
@@ -1,22 +1,29 @@
1
1
  <template>
2
2
  <div class="funding-layout">
3
3
  <transition name="fade">
4
- <button
4
+ <button
5
5
  v-if="showReturnButton"
6
6
  @click="goBack"
7
7
  class="return-button"
8
8
  title="Return to previous page"
9
9
  >
10
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
11
- <path d="M19 12H5M5 12L12 19M5 12L12 5"/>
10
+ <svg
11
+ width="20"
12
+ height="20"
13
+ viewBox="0 0 24 24"
14
+ fill="none"
15
+ stroke="currentColor"
16
+ stroke-width="2"
17
+ >
18
+ <path d="M19 12H5M5 12L12 19M5 12L12 5" />
12
19
  </svg>
13
20
  <span>Return</span>
14
21
  </button>
15
22
  </transition>
16
-
23
+
17
24
  <FundingHero />
18
-
19
- <div
25
+
26
+ <div
20
27
  class="funding-content"
21
28
  @mouseenter="handleMouseEnter"
22
29
  @mousemove="handleMouseMove"
@@ -25,75 +32,75 @@
25
32
  >
26
33
  <Content />
27
34
  </div>
28
-
35
+
29
36
  <FundingEffects />
30
37
  </div>
31
38
  </template>
32
39
 
33
40
  <script setup>
34
- import { ref, onMounted, onUnmounted } from 'vue'
35
- import { Content, useRouter } from 'vitepress'
36
- import FundingHero from './components/FundingHero.vue'
37
- import FundingEffects from './components/FundingEffects.vue'
41
+ import { ref, onMounted, onUnmounted } from "vue";
42
+ import { Content, useRouter } from "vitepress";
43
+ import FundingHero from "./components/FundingHero.vue";
44
+ import FundingEffects from "./components/FundingEffects.vue";
38
45
 
39
- const contentRef = ref(null)
40
- const showReturnButton = ref(false)
41
- const router = useRouter()
46
+ const contentRef = ref(null);
47
+ const showReturnButton = ref(false);
48
+ const router = useRouter();
42
49
 
43
50
  const goBack = () => {
44
51
  if (window.history.length > 1) {
45
- window.history.back()
52
+ window.history.back();
46
53
  } else {
47
- router.go('/coolors-mcp/')
54
+ router.go("/coolors-mcp/");
48
55
  }
49
- }
56
+ };
50
57
 
51
58
  // Show return button after scroll
52
59
  onMounted(() => {
53
60
  const handleScroll = () => {
54
- showReturnButton.value = window.scrollY > 100
55
- }
56
-
57
- window.addEventListener('scroll', handleScroll)
58
-
61
+ showReturnButton.value = window.scrollY > 100;
62
+ };
63
+
64
+ window.addEventListener("scroll", handleScroll);
65
+
59
66
  // Add page transition class
60
- document.body.classList.add('page-transition-active')
67
+ document.body.classList.add("page-transition-active");
61
68
  setTimeout(() => {
62
- document.body.classList.remove('page-transition-active')
63
- }, 600)
64
-
69
+ document.body.classList.remove("page-transition-active");
70
+ }, 600);
71
+
65
72
  onUnmounted(() => {
66
- window.removeEventListener('scroll', handleScroll)
67
- })
68
- })
73
+ window.removeEventListener("scroll", handleScroll);
74
+ });
75
+ });
69
76
 
70
77
  const handleMouseEnter = (e) => {
71
- if (!contentRef.value) return
72
-
73
- const rect = contentRef.value.getBoundingClientRect()
74
- const x = e.clientX - rect.left
75
- const y = e.clientY - rect.top
76
-
77
- contentRef.value.style.setProperty('--content-mouse-x', `${x}px`)
78
- contentRef.value.style.setProperty('--content-mouse-y', `${y}px`)
79
- contentRef.value.classList.add('mouse-over')
80
- }
78
+ if (!contentRef.value) return;
79
+
80
+ const rect = contentRef.value.getBoundingClientRect();
81
+ const x = e.clientX - rect.left;
82
+ const y = e.clientY - rect.top;
83
+
84
+ contentRef.value.style.setProperty("--content-mouse-x", `${x}px`);
85
+ contentRef.value.style.setProperty("--content-mouse-y", `${y}px`);
86
+ contentRef.value.classList.add("mouse-over");
87
+ };
81
88
 
82
89
  const handleMouseMove = (e) => {
83
- if (!contentRef.value) return
84
-
85
- const rect = contentRef.value.getBoundingClientRect()
86
- const x = e.clientX - rect.left
87
- const y = e.clientY - rect.top
88
-
89
- contentRef.value.style.setProperty('--content-mouse-x', `${x}px`)
90
- contentRef.value.style.setProperty('--content-mouse-y', `${y}px`)
91
- }
90
+ if (!contentRef.value) return;
91
+
92
+ const rect = contentRef.value.getBoundingClientRect();
93
+ const x = e.clientX - rect.left;
94
+ const y = e.clientY - rect.top;
95
+
96
+ contentRef.value.style.setProperty("--content-mouse-x", `${x}px`);
97
+ contentRef.value.style.setProperty("--content-mouse-y", `${y}px`);
98
+ };
92
99
 
93
100
  const handleMouseLeave = () => {
94
- if (!contentRef.value) return
95
- contentRef.value.classList.remove('mouse-over')
96
- }
101
+ if (!contentRef.value) return;
102
+ contentRef.value.classList.remove("mouse-over");
103
+ };
97
104
  </script>
98
105
 
99
106
  <style scoped>
@@ -169,7 +176,7 @@ const handleMouseLeave = () => {
169
176
  }
170
177
 
171
178
  .funding-content::before {
172
- content: '';
179
+ content: "";
173
180
  position: absolute;
174
181
  top: 0;
175
182
  left: -100px;
@@ -217,7 +224,11 @@ html:not(.dark) .funding-content::before {
217
224
  margin: 64px 0 32px;
218
225
  padding: 0 24px;
219
226
  text-align: center;
220
- background: linear-gradient(135deg, var(--vp-c-text-1) 0%, var(--vp-c-brand) 100%);
227
+ background: linear-gradient(
228
+ 135deg,
229
+ var(--vp-c-text-1) 0%,
230
+ var(--vp-c-brand) 100%
231
+ );
221
232
  -webkit-background-clip: text;
222
233
  background-clip: text;
223
234
  -webkit-text-fill-color: transparent;
@@ -248,4 +259,4 @@ html:not(.dark) .funding-content::before {
248
259
  .funding-layout strong {
249
260
  color: var(--vp-c-text-1);
250
261
  }
251
- </style>
262
+ </style>
@@ -5,15 +5,13 @@
5
5
  <span v-if="!isHomePage" class="replacement-title">Documentation</span>
6
6
  </template>
7
7
  <template #nav-bar-content-before>
8
- <div class="nav-warning">
9
- 🏷️ <span>1.0.0</span>
10
- </div>
8
+ <div class="nav-warning">🏷️ <span>1.0.0</span></div>
11
9
  </template>
12
10
  <template #sidebar-nav-after>
13
11
  <!-- Sidebar ad placement - most non-intrusive -->
14
- <AdBanner
12
+ <AdBanner
15
13
  v-if="!isHomePage"
16
- type="carbon"
14
+ type="carbon"
17
15
  position="sidebar"
18
16
  :dismissible="true"
19
17
  :hide-on-paths="['/', '/getting-started']"
@@ -27,21 +25,23 @@
27
25
  </template>
28
26
 
29
27
  <script setup>
30
- import { computed } from 'vue'
31
- import { useRoute, useData } from 'vitepress'
32
- import DefaultTheme from 'vitepress/theme'
33
- import AdBanner from './components/AdBanner.vue'
34
- import SupportSection from './components/SupportSection.vue'
35
- import FundingHero from './components/FundingHero.vue'
36
- import FundingEffects from './components/FundingEffects.vue'
37
- import FundingLayout from './FundingLayout.vue'
38
-
39
- const { Layout } = DefaultTheme
40
- const route = useRoute()
41
- const { frontmatter } = useData()
42
-
43
- const isHomePage = computed(() => route.path === '/' || route.path === '/coolors-mcp/')
44
- const isFundingPage = computed(() => route.path.includes('/funding'))
28
+ import { computed } from "vue";
29
+ import { useRoute, useData } from "vitepress";
30
+ import DefaultTheme from "vitepress/theme";
31
+ import AdBanner from "./components/AdBanner.vue";
32
+ import SupportSection from "./components/SupportSection.vue";
33
+ import FundingHero from "./components/FundingHero.vue";
34
+ import FundingEffects from "./components/FundingEffects.vue";
35
+ import FundingLayout from "./FundingLayout.vue";
36
+
37
+ const { Layout } = DefaultTheme;
38
+ const route = useRoute();
39
+ const { frontmatter } = useData();
40
+
41
+ const isHomePage = computed(
42
+ () => route.path === "/" || route.path === "/coolors-mcp/",
43
+ );
44
+ const isFundingPage = computed(() => route.path.includes("/funding"));
45
45
  </script>
46
46
 
47
47
  <style>
@@ -131,4 +131,4 @@ html.dark .nav-warning {
131
131
  display: none;
132
132
  }
133
133
  }
134
- </style>
134
+ </style>
@@ -4,8 +4,15 @@
4
4
  <div v-if="type === 'github-sponsors'" class="sponsor-banner">
5
5
  <div class="sponsor-content">
6
6
  <span class="sponsor-icon">💖</span>
7
- <span class="sponsor-text">Support this project on GitHub Sponsors</span>
8
- <a :href="githubSponsorsUrl" target="_blank" rel="noopener" class="sponsor-button">
7
+ <span class="sponsor-text"
8
+ >Support this project on GitHub Sponsors</span
9
+ >
10
+ <a
11
+ :href="githubSponsorsUrl"
12
+ target="_blank"
13
+ rel="noopener"
14
+ class="sponsor-button"
15
+ >
9
16
  Sponsor
10
17
  </a>
11
18
  </div>
@@ -17,100 +24,106 @@
17
24
  <!-- Carbon will inject content here -->
18
25
  </div>
19
26
  </div>
20
-
21
27
 
22
28
  <!-- Dismissible close button -->
23
- <button v-if="dismissible" @click="dismissAd" class="ad-close" aria-label="Close ad">
29
+ <button
30
+ v-if="dismissible"
31
+ @click="dismissAd"
32
+ class="ad-close"
33
+ aria-label="Close ad"
34
+ >
24
35
  ×
25
36
  </button>
26
37
  </div>
27
38
  </template>
28
39
 
29
40
  <script setup>
30
- import { ref, computed, onMounted } from 'vue'
41
+ import { ref, computed, onMounted } from "vue";
31
42
 
32
43
  const props = defineProps({
33
44
  type: {
34
45
  type: String,
35
- default: 'github-sponsors', // 'github-sponsors', 'carbon'
36
- validator: (value) => ['github-sponsors', 'carbon'].includes(value)
46
+ default: "github-sponsors", // 'github-sponsors', 'carbon'
47
+ validator: (value) => ["github-sponsors", "carbon"].includes(value),
37
48
  },
38
49
  position: {
39
50
  type: String,
40
- default: 'top', // 'top', 'bottom', 'sidebar', 'inline'
41
- validator: (value) => ['top', 'bottom', 'sidebar', 'inline'].includes(value)
51
+ default: "top", // 'top', 'bottom', 'sidebar', 'inline'
52
+ validator: (value) =>
53
+ ["top", "bottom", "sidebar", "inline"].includes(value),
42
54
  },
43
55
  githubSponsorsUrl: {
44
56
  type: String,
45
- default: 'https://github.com/sponsors/jamubc'
57
+ default: "https://github.com/sponsors/jamubc",
46
58
  },
47
59
  dismissible: {
48
60
  type: Boolean,
49
- default: true
61
+ default: true,
50
62
  },
51
63
  showOnPaths: {
52
64
  type: Array,
53
- default: () => [] // Empty array means show on all paths
65
+ default: () => [], // Empty array means show on all paths
54
66
  },
55
67
  hideOnPaths: {
56
68
  type: Array,
57
- default: () => ['/'] // Hide on home page by default
69
+ default: () => ["/"], // Hide on home page by default
58
70
  },
59
- })
71
+ });
60
72
 
61
- const isDismissed = ref(false)
62
- const storageKey = `ad-dismissed-${props.type}-${props.position}`
73
+ const isDismissed = ref(false);
74
+ const storageKey = `ad-dismissed-${props.type}-${props.position}`;
63
75
 
64
- const bannerClass = computed(() => `ad-banner--${props.position}`)
76
+ const bannerClass = computed(() => `ad-banner--${props.position}`);
65
77
 
66
78
  const shouldShowAd = computed(() => {
67
- if (isDismissed.value) return false
68
-
79
+ if (isDismissed.value) return false;
80
+
69
81
  // Check if we're in browser environment
70
- if (typeof window === 'undefined') return false
71
-
72
- const currentPath = window.location.pathname
73
-
82
+ if (typeof window === "undefined") return false;
83
+
84
+ const currentPath = window.location.pathname;
85
+
74
86
  // If showOnPaths is specified, only show on those paths
75
87
  if (props.showOnPaths.length > 0) {
76
- return props.showOnPaths.some(path => currentPath.includes(path))
88
+ return props.showOnPaths.some((path) => currentPath.includes(path));
77
89
  }
78
-
90
+
79
91
  // If hideOnPaths is specified, hide on those paths
80
92
  if (props.hideOnPaths.length > 0) {
81
- return !props.hideOnPaths.some(path => currentPath.includes(path))
93
+ return !props.hideOnPaths.some((path) => currentPath.includes(path));
82
94
  }
83
-
84
- return true
85
- })
95
+
96
+ return true;
97
+ });
86
98
 
87
99
  const dismissAd = () => {
88
- isDismissed.value = true
89
- if (typeof localStorage !== 'undefined') {
90
- localStorage.setItem(storageKey, 'true')
100
+ isDismissed.value = true;
101
+ if (typeof localStorage !== "undefined") {
102
+ localStorage.setItem(storageKey, "true");
91
103
  }
92
- }
104
+ };
93
105
 
94
106
  onMounted(() => {
95
107
  // Check if ad was previously dismissed
96
- if (typeof localStorage !== 'undefined') {
97
- const dismissed = localStorage.getItem(storageKey)
98
- if (dismissed === 'true') {
99
- isDismissed.value = true
108
+ if (typeof localStorage !== "undefined") {
109
+ const dismissed = localStorage.getItem(storageKey);
110
+ if (dismissed === "true") {
111
+ isDismissed.value = true;
100
112
  }
101
113
  }
102
-
114
+
103
115
  // Load Carbon Ads script if needed
104
- if (props.type === 'carbon' && !document.getElementById('carbon-script')) {
105
- const script = document.createElement('script')
106
- script.id = 'carbon-script'
107
- script.async = true
108
- script.type = 'text/javascript'
109
- script.src = '//cdn.carbonads.com/carbon.js?serve=YOUR_CARBON_ID&placement=YOUR_PLACEMENT'
110
- script.setAttribute('id', '_carbonads_js')
111
- document.head.appendChild(script)
116
+ if (props.type === "carbon" && !document.getElementById("carbon-script")) {
117
+ const script = document.createElement("script");
118
+ script.id = "carbon-script";
119
+ script.async = true;
120
+ script.type = "text/javascript";
121
+ script.src =
122
+ "//cdn.carbonads.com/carbon.js?serve=YOUR_CARBON_ID&placement=YOUR_PLACEMENT";
123
+ script.setAttribute("id", "_carbonads_js");
124
+ document.head.appendChild(script);
112
125
  }
113
- })
126
+ });
114
127
  </script>
115
128
 
116
129
  <style scoped>
@@ -164,7 +177,11 @@ onMounted(() => {
164
177
  display: flex;
165
178
  align-items: center;
166
179
  justify-content: space-between;
167
- background: linear-gradient(135deg, rgba(66, 184, 131, 0.1) 0%, rgba(53, 73, 94, 0.1) 100%);
180
+ background: linear-gradient(
181
+ 135deg,
182
+ rgba(66, 184, 131, 0.1) 0%,
183
+ rgba(53, 73, 94, 0.1) 100%
184
+ );
168
185
  }
169
186
 
170
187
  .sponsor-content {
@@ -294,13 +311,13 @@ onMounted(() => {
294
311
  max-width: none;
295
312
  margin: 16px;
296
313
  }
297
-
314
+
298
315
  .sponsor-content {
299
316
  flex-direction: column;
300
317
  gap: 8px;
301
318
  text-align: center;
302
319
  }
303
-
320
+
304
321
  .sponsor-text {
305
322
  font-size: 13px;
306
323
  }
@@ -312,6 +329,10 @@ html.dark .ad-banner--top {
312
329
  }
313
330
 
314
331
  html.dark .sponsor-banner {
315
- background: linear-gradient(135deg, rgba(66, 184, 131, 0.15) 0%, rgba(53, 73, 94, 0.15) 100%);
332
+ background: linear-gradient(
333
+ 135deg,
334
+ rgba(66, 184, 131, 0.15) 0%,
335
+ rgba(53, 73, 94, 0.15) 100%
336
+ );
316
337
  }
317
- </style>
338
+ </style>
@@ -9,8 +9,8 @@
9
9
  </p>
10
10
  </div>
11
11
  </div>
12
- <a
13
- href="https://github.com/sponsors/jamubc"
12
+ <a
13
+ href="https://github.com/sponsors/jamubc"
14
14
  class="placeholder-cta"
15
15
  target="_blank"
16
16
  rel="noopener"
@@ -75,4 +75,4 @@
75
75
  .VPSidebar .ad-placeholder {
76
76
  max-width: 250px;
77
77
  }
78
- </style>
78
+ </style>
@@ -5,43 +5,46 @@
5
5
  </template>
6
6
 
7
7
  <script setup>
8
- import { onMounted } from 'vue'
8
+ import { onMounted } from "vue";
9
9
 
10
10
  onMounted(() => {
11
11
  // Add intersection observer for scroll animations
12
- const observer = new IntersectionObserver((entries) => {
13
- entries.forEach(entry => {
14
- if (entry.isIntersecting) {
15
- entry.target.classList.add('visible')
16
- }
17
- })
18
- }, { threshold: 0.1 })
19
-
12
+ const observer = new IntersectionObserver(
13
+ (entries) => {
14
+ entries.forEach((entry) => {
15
+ if (entry.isIntersecting) {
16
+ entry.target.classList.add("visible");
17
+ }
18
+ });
19
+ },
20
+ { threshold: 0.1 },
21
+ );
22
+
20
23
  // Observe all cards
21
- const cards = document.querySelectorAll('.funding-card, .contribute-card')
22
- cards.forEach(card => observer.observe(card))
23
-
24
+ const cards = document.querySelectorAll(".funding-card, .contribute-card");
25
+ cards.forEach((card) => observer.observe(card));
26
+
24
27
  // Add hover effects to cards
25
- cards.forEach(card => {
26
- card.addEventListener('mouseenter', (e) => {
27
- const rect = card.getBoundingClientRect()
28
- const x = e.clientX - rect.left
29
- const y = e.clientY - rect.top
30
-
31
- card.style.setProperty('--card-mouse-x', `${x}px`)
32
- card.style.setProperty('--card-mouse-y', `${y}px`)
33
- })
34
-
35
- card.addEventListener('mousemove', (e) => {
36
- const rect = card.getBoundingClientRect()
37
- const x = e.clientX - rect.left
38
- const y = e.clientY - rect.top
39
-
40
- card.style.setProperty('--card-mouse-x', `${x}px`)
41
- card.style.setProperty('--card-mouse-y', `${y}px`)
42
- })
43
- })
44
- })
28
+ cards.forEach((card) => {
29
+ card.addEventListener("mouseenter", (e) => {
30
+ const rect = card.getBoundingClientRect();
31
+ const x = e.clientX - rect.left;
32
+ const y = e.clientY - rect.top;
33
+
34
+ card.style.setProperty("--card-mouse-x", `${x}px`);
35
+ card.style.setProperty("--card-mouse-y", `${y}px`);
36
+ });
37
+
38
+ card.addEventListener("mousemove", (e) => {
39
+ const rect = card.getBoundingClientRect();
40
+ const x = e.clientX - rect.left;
41
+ const y = e.clientY - rect.top;
42
+
43
+ card.style.setProperty("--card-mouse-x", `${x}px`);
44
+ card.style.setProperty("--card-mouse-y", `${y}px`);
45
+ });
46
+ });
47
+ });
45
48
  </script>
46
49
 
47
50
  <style>
@@ -67,7 +70,7 @@ onMounted(() => {
67
70
  }
68
71
 
69
72
  .funding-card::before {
70
- content: '';
73
+ content: "";
71
74
  position: absolute;
72
75
  top: 0;
73
76
  left: 0;
@@ -123,7 +126,7 @@ onMounted(() => {
123
126
  .support-button--primary:hover {
124
127
  background: var(--vp-c-brand-dark);
125
128
  transform: translateY(-2px);
126
- box-shadow:
129
+ box-shadow:
127
130
  0 4px 12px rgba(66, 184, 131, 0.3),
128
131
  0 0 20px rgba(218, 119, 86, 0.4),
129
132
  0 0 30px rgba(71, 150, 227, 0.3),
@@ -132,15 +135,16 @@ onMounted(() => {
132
135
  }
133
136
 
134
137
  @keyframes glow {
135
- 0%, 100% {
136
- box-shadow:
138
+ 0%,
139
+ 100% {
140
+ box-shadow:
137
141
  0 4px 12px rgba(66, 184, 131, 0.3),
138
142
  0 0 20px rgba(218, 119, 86, 0.4),
139
143
  0 0 30px rgba(71, 150, 227, 0.3),
140
144
  0 0 40px rgba(255, 140, 0, 0.2);
141
145
  }
142
- 50% {
143
- box-shadow:
146
+ 50% {
147
+ box-shadow:
144
148
  0 4px 16px rgba(66, 184, 131, 0.4),
145
149
  0 0 25px rgba(218, 119, 86, 0.5),
146
150
  0 0 35px rgba(71, 150, 227, 0.4),
@@ -188,7 +192,7 @@ onMounted(() => {
188
192
  }
189
193
 
190
194
  .contribute-card::before {
191
- content: '';
195
+ content: "";
192
196
  position: absolute;
193
197
  top: 0;
194
198
  left: 0;
@@ -248,7 +252,7 @@ onMounted(() => {
248
252
  }
249
253
 
250
254
  .progress-fill::after {
251
- content: '';
255
+ content: "";
252
256
  position: absolute;
253
257
  top: 0;
254
258
  left: 0;
@@ -264,12 +268,18 @@ onMounted(() => {
264
268
  }
265
269
 
266
270
  @keyframes progress {
267
- from { width: 0; }
271
+ from {
272
+ width: 0;
273
+ }
268
274
  }
269
275
 
270
276
  @keyframes shimmer-progress {
271
- 0% { transform: translateX(-100%); }
272
- 100% { transform: translateX(100%); }
277
+ 0% {
278
+ transform: translateX(-100%);
279
+ }
280
+ 100% {
281
+ transform: translateX(100%);
282
+ }
273
283
  }
274
284
 
275
285
  /* Light mode adjustments */
@@ -301,7 +311,7 @@ html:not(.dark) .contribute-card:hover {
301
311
 
302
312
  html:not(.dark) .support-button-secondary:hover {
303
313
  border-color: rgba(66, 139, 202, 0.6);
304
- box-shadow:
314
+ box-shadow:
305
315
  0 0 30px rgba(66, 139, 202, 0.1),
306
316
  inset 0 0 20px rgba(66, 139, 202, 0.03);
307
317
  }
@@ -311,12 +321,26 @@ html:not(.dark) .progress-fill {
311
321
  }
312
322
 
313
323
  /* Stagger animations */
314
- .funding-card:nth-child(1) { transition-delay: 0.1s; }
315
- .funding-card:nth-child(2) { transition-delay: 0.2s; }
316
- .funding-card:nth-child(3) { transition-delay: 0.3s; }
317
-
318
- .contribute-card:nth-child(1) { transition-delay: 0.1s; }
319
- .contribute-card:nth-child(2) { transition-delay: 0.2s; }
320
- .contribute-card:nth-child(3) { transition-delay: 0.3s; }
321
- .contribute-card:nth-child(4) { transition-delay: 0.4s; }
322
- </style>
324
+ .funding-card:nth-child(1) {
325
+ transition-delay: 0.1s;
326
+ }
327
+ .funding-card:nth-child(2) {
328
+ transition-delay: 0.2s;
329
+ }
330
+ .funding-card:nth-child(3) {
331
+ transition-delay: 0.3s;
332
+ }
333
+
334
+ .contribute-card:nth-child(1) {
335
+ transition-delay: 0.1s;
336
+ }
337
+ .contribute-card:nth-child(2) {
338
+ transition-delay: 0.2s;
339
+ }
340
+ .contribute-card:nth-child(3) {
341
+ transition-delay: 0.3s;
342
+ }
343
+ .contribute-card:nth-child(4) {
344
+ transition-delay: 0.4s;
345
+ }
346
+ </style>