-
- {tabs.map((tab, i) => (
-
- ))}
+
+
+
+
-
-
-
- {activeCode}
-
-
+
+ {code}
+
+
+
+
+ {__("Looking for ES module or headless integration?")}
+ {" "}
+
+ {__("See our documentation for detailed integration guides.")}
+
+
+
);
}
diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/ThemePreview.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/ThemePreview.tsx
new file mode 100644
index 000000000..19decec3e
--- /dev/null
+++ b/apps/console/src/pages/organizations/cookie-banners/configuration/snippet/_components/ThemePreview.tsx
@@ -0,0 +1,277 @@
+// Copyright (c) 2026 Probo Inc
.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+import { useTranslate } from "@probo/i18n";
+import { Button, Card, Field, Input, useToast } from "@probo/ui";
+import { useCallback, useMemo, useState } from "react";
+
+type CSSVariable = {
+ key: string;
+ label: string;
+ defaultValue: string;
+ type: "color" | "text";
+};
+
+const CSS_VARIABLES: CSSVariable[] = [
+ { key: "--probo-bg", label: "Background", defaultValue: "#ffffff", type: "color" },
+ { key: "--probo-text", label: "Text", defaultValue: "#1a1a1a", type: "color" },
+ { key: "--probo-text-secondary", label: "Text Secondary", defaultValue: "#555555", type: "color" },
+ { key: "--probo-border", label: "Border", defaultValue: "#e0e0e0", type: "color" },
+ { key: "--probo-accent", label: "Accent", defaultValue: "#1a1a1a", type: "color" },
+ { key: "--probo-accent-text", label: "Accent Text", defaultValue: "#ffffff", type: "color" },
+ { key: "--probo-radius", label: "Border Radius", defaultValue: "12px", type: "text" },
+ { key: "--probo-btn-radius", label: "Button Radius", defaultValue: "8px", type: "text" },
+ { key: "--probo-font-size", label: "Font Size", defaultValue: "14px", type: "text" },
+ { key: "--probo-font-family", label: "Font Family", defaultValue: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif", type: "text" },
+ { key: "--probo-shadow", label: "Shadow", defaultValue: "0 4px 24px rgba(0, 0, 0, 0.12)", type: "text" },
+];
+
+function buildCSSSnippet(values: Record): string {
+ const overrides = CSS_VARIABLES
+ .filter(v => values[v.key] !== v.defaultValue)
+ .map(v => ` ${v.key}: ${values[v.key]};`);
+
+ if (overrides.length === 0) {
+ return "/* Using default theme — no overrides needed */";
+ }
+
+ return `probo-cookie-banner {\n${overrides.join("\n")}\n}`;
+}
+
+export function ThemePreview() {
+ const { __ } = useTranslate();
+ const { toast } = useToast();
+
+ const [values, setValues] = useState>(() => {
+ const initial: Record = {};
+ for (const v of CSS_VARIABLES) {
+ initial[v.key] = v.defaultValue;
+ }
+ return initial;
+ });
+
+ const setValue = useCallback((key: string, value: string) => {
+ setValues(prev => ({ ...prev, [key]: value }));
+ }, []);
+
+ const handleReset = useCallback(() => {
+ const initial: Record = {};
+ for (const v of CSS_VARIABLES) {
+ initial[v.key] = v.defaultValue;
+ }
+ setValues(initial);
+ }, []);
+
+ const cssSnippet = useMemo(() => buildCSSSnippet(values), [values]);
+
+ const handleCopyCSS = () => {
+ void navigator.clipboard.writeText(cssSnippet);
+ toast({
+ title: __("Copied"),
+ description: __("CSS snippet copied to clipboard"),
+ variant: "success",
+ });
+ };
+
+ const previewStyle = useMemo(() => {
+ const style: Record = {};
+ for (const v of CSS_VARIABLES) {
+ style[v.key] = values[v.key];
+ }
+ return style;
+ }, [values]);
+
+ const colorVariables = CSS_VARIABLES.filter(v => v.type === "color");
+ const textVariables = CSS_VARIABLES.filter(v => v.type === "text");
+
+ return (
+
+
+
{__("Theme")}
+
+
+
+
+
+
+ {colorVariables.map(v => (
+
+ ))}
+
+
+
+ {textVariables.map(v => (
+
+ setValue(v.key, e.target.value)}
+ />
+
+ ))}
+
+
+
+
+
{__("Preview")}
+
+
+
+
+
+
+
+
+
{__("CSS Snippet")}
+
+
+
+
+
+ {cssSnippet}
+
+
+
+ );
+}
+
+function BannerPreview() {
+ return (
+
+ );
+}