diff --git a/packages/kits/src/components/ThemeSwitch/ThemeSwitch.test.tsx b/packages/kits/src/components/ThemeSwitch/ThemeSwitch.test.tsx
index 21522d5..f99bed4 100644
--- a/packages/kits/src/components/ThemeSwitch/ThemeSwitch.test.tsx
+++ b/packages/kits/src/components/ThemeSwitch/ThemeSwitch.test.tsx
@@ -1,7 +1,8 @@
+import { LogProvider } from '@ermnvldmr/ui';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
-import { describe, expect, it } from 'vitest';
+import { describe, expect, it, vi } from 'vitest';
import { ThemeSwitch } from './ThemeSwitch';
@@ -38,4 +39,24 @@ describe('ThemeSwitch', () => {
expect(systemButton).toBeInTheDocument();
});
+
+ it('dispatches telemetry events to LogProvider', async () => {
+ const user = userEvent.setup();
+ const loggerMock = vi.fn();
+
+ render(
+
+
+
+ );
+
+ const switchControl = screen.getByRole('switch');
+ const systemButton = screen.getByRole('button');
+
+ await user.click(switchControl);
+ expect(loggerMock).toHaveBeenCalledWith('theme-toggle', { preference: 'dark' });
+
+ await user.click(systemButton);
+ expect(loggerMock).toHaveBeenCalledWith('theme-toggle', { preference: 'system' });
+ });
});
diff --git a/packages/kits/src/components/ThemeSwitch/ThemeSwitch.tsx b/packages/kits/src/components/ThemeSwitch/ThemeSwitch.tsx
index 6f9b9d2..9246fee 100644
--- a/packages/kits/src/components/ThemeSwitch/ThemeSwitch.tsx
+++ b/packages/kits/src/components/ThemeSwitch/ThemeSwitch.tsx
@@ -1,4 +1,4 @@
-import { Button, HStack, Switch, useTheme } from '@ermnvldmr/ui';
+import { Button, HStack, Switch, useLogger, useTheme } from '@ermnvldmr/ui';
import React from 'react';
/**
@@ -27,28 +27,42 @@ export function ThemeSwitch({
systemIcon,
}: ThemeSwitchProps = {}): React.JSX.Element {
const { preference, setPreference, resolvedTheme } = useTheme();
+ const log = useLogger();
const isDark = resolvedTheme === 'dark';
+ const handleToggle = (isSelected: boolean): void => {
+ const nextTheme = isSelected ? 'dark' : 'light';
+ setPreference(nextTheme);
+ log('theme-toggle', { preference: nextTheme });
+ };
+
+ const handleSystem = (): void => {
+ setPreference('system');
+ log('theme-toggle', { preference: 'system' });
+ };
+
return (
{lightIcon}
setPreference(isSelected ? 'dark' : 'light')}
+ onChange={handleToggle}
/>
{darkIcon}
);
diff --git a/packages/ssg/src/lib/config/analytics/index.ts b/packages/ssg/src/lib/config/analytics/index.ts
new file mode 100644
index 0000000..f8ec63d
--- /dev/null
+++ b/packages/ssg/src/lib/config/analytics/index.ts
@@ -0,0 +1,2 @@
+export * from './types.js';
+export * from './umami.js';
diff --git a/packages/ssg/src/lib/config/analytics/types.ts b/packages/ssg/src/lib/config/analytics/types.ts
new file mode 100644
index 0000000..b626b02
--- /dev/null
+++ b/packages/ssg/src/lib/config/analytics/types.ts
@@ -0,0 +1,35 @@
+/**
+ * HTML script or link tag descriptor for analytics injection.
+ */
+export interface AnalyticsHtmlTag {
+ /** The HTML tag name. */
+ tag: string;
+ /** Attributes to attach to the tag element. */
+ attrs?: Record;
+ /** Optional inline text content of the tag. */
+ children?: string;
+ /** Whether the tag should be injected into the document . */
+ head?: boolean;
+ /** Whether the tag should be appended at the end. */
+ append?: boolean;
+}
+
+/**
+ * Interface representing a configurable analytics provider for SSG services.
+ */
+export interface AnalyticsProvider {
+ /** Provider identifier (e.g. 'umami', 'plausible'). */
+ readonly name: string;
+ /**
+ * Generates the HTML tags required for this provider.
+ *
+ * @param isProduction - Whether the current build is in production mode.
+ * @returns Array of HTML tag descriptors.
+ */
+ getHtmlTags: (isProduction: boolean) => AnalyticsHtmlTag[];
+}
+
+/**
+ * Key-value mapping for analytics event metadata.
+ */
+export type AnalyticsEventData = Record;
diff --git a/packages/ssg/src/lib/config/analytics/umami.ts b/packages/ssg/src/lib/config/analytics/umami.ts
new file mode 100644
index 0000000..a1e3c91
--- /dev/null
+++ b/packages/ssg/src/lib/config/analytics/umami.ts
@@ -0,0 +1,99 @@
+import type { AnalyticsHtmlTag, AnalyticsProvider } from './types.js';
+
+/**
+ * Configuration options for the Umami Analytics provider.
+ */
+export interface UmamiAnalyticsOptions {
+ /** Base URL of the Umami server (e.g., 'https://umami.ermnvldmr.com'). */
+ serverUrl: string;
+ /** Unique website ID UUID assigned by Umami. */
+ websiteId: string;
+ /** Comma-separated list of allowed domains (e.g., 'ermnvldmr.com,www.ermnvldmr.com'). */
+ domains?: string;
+ /** Custom tracker script name. Defaults to 'script.js'. */
+ scriptName?: string;
+ /** Whether to respect the browser's Do Not Track setting. Defaults to true. */
+ doNotTrack?: boolean;
+ /** Optional data tag for event categorization. */
+ tag?: string;
+ /** Optional custom host URL for event endpoint. */
+ hostUrl?: string;
+ /** Whether to auto-track page views. Defaults to true. */
+ autoTrack?: boolean;
+ /** Whether to force tag injection in non-production builds. Defaults to false. */
+ force?: boolean;
+}
+
+/**
+ * Creates an Umami Analytics provider for SSG service configurations.
+ *
+ * Automatically produces `