diff --git a/app/components/Package/WeeklyDownloadStats.vue b/app/components/Package/WeeklyDownloadStats.vue index 1a5b257b3..1eddaf45d 100644 --- a/app/components/Package/WeeklyDownloadStats.vue +++ b/app/components/Package/WeeklyDownloadStats.vue @@ -152,7 +152,7 @@ const config = computed(() => { backgroundColor: 'transparent', animation: { show: false }, area: { - color: colors.value.borderHover, + color: transparentizeOklch(accent.value, 0.2), useGradient: false, opacity: 10, }, @@ -163,7 +163,7 @@ const config = computed(() => { color: colors.value.fg, }, line: { - color: colors.value.borderHover, + color: isDarkMode.value ? darkenOklch(accent.value, 0.5) : darkenOklch(accent.value, 0.125), pulse: { show: true, loop: true, // runs only once if false diff --git a/app/utils/colors.ts b/app/utils/colors.ts index 1e6e4a95f..dae5ba17b 100644 --- a/app/utils/colors.ts +++ b/app/utils/colors.ts @@ -147,6 +147,66 @@ export function lightenOklch( : `oklch(${lightness} ${chroma} ${hue} / ${alpha})` } +/** + * Darken an OKLCH color by a given factor. + * + * Works with strict TypeScript settings including `noUncheckedIndexedAccess`, + * where `match[n]` is typed as `string | undefined`. + * + * @param oklch - Color in the form "oklch(L C H)" or "oklch(L C H / A)" + * @param factor - Darkening force in range [0, 1] + * @returns Darkened OKLCH color string (0.5 = 50% lighter) + */ +export function darkenOklch( + oklch: string | null | undefined, + factor: number, +): string | null | undefined { + if (oklch == null) { + return oklch + } + + const input = oklch.trim() + + const match = input.match( + /^oklch\(\s*([+-]?[\d.]+%?)\s+([+-]?[\d.]+)\s+([+-]?[\d.]+)(?:\s*\/\s*([+-]?[\d.]+%?))?\s*\)$/i, + ) + + if (!match) { + throw new Error('Invalid OKLCH color format') + } + + const [, lightnessText, chromaText, hueText, alphaText] = match + + if (lightnessText === undefined || chromaText === undefined || hueText === undefined) { + throw new Error('Invalid OKLCH color format') + } + + let lightness = lightnessText.endsWith('%') + ? Number.parseFloat(lightnessText) / 100 + : Number.parseFloat(lightnessText) + let chroma = Number.parseFloat(chromaText) + const hue = Number.parseFloat(hueText) + const alpha = + alphaText === undefined + ? null + : alphaText.endsWith('%') + ? Number.parseFloat(alphaText) / 100 + : Number.parseFloat(alphaText) + + const clampedFactor = Math.min(Math.max(factor, 0), 1) + lightness = lightness - (1 - lightness) * clampedFactor + + // Reduce chroma slightly as lightness increases + chroma = chroma * (1 + clampedFactor * 0.3) + + lightness = Math.min(Math.max(lightness, 0), 1) + chroma = Math.max(chroma, 0) + + return alpha === null + ? `oklch(${lightness} ${chroma} ${hue})` + : `oklch(${lightness} ${chroma} ${hue} / ${alpha})` +} + /** * Make an OKLCH color transparent by a given factor. *