From f49c61d9bfd1785992b3087f1ab1c787ad2ad3d2 Mon Sep 17 00:00:00 2001 From: lebaudantoine Date: Mon, 3 Aug 2026 20:46:25 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(sdk)=20allow=20passing=20a=20backgrou?= =?UTF-8?q?nd=20color=20to=20the=20calendar=20iframe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let integrators pass a custom background color to the iframe used by the calendar SDK, so it can match the surrounding product's theme. --- CHANGELOG.md | 1 + .../sdk/routes/CreateMeetingButton.tsx | 63 +++++++++++++++---- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ac4ffb1..419e7776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to - ✨(frontend) let users set default configuration for generated links - ✨(frontend) expose media state to external gateways - ✨(frontend) add connection test feature +- ✨(sdk) allow passing a background color to the calendar iframe ### Changed diff --git a/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx b/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx index bf37f938..ca8445bb 100644 --- a/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx +++ b/src/frontend/src/features/sdk/routes/CreateMeetingButton.tsx @@ -43,6 +43,34 @@ const CreateMeetingButton = () => { if (room?.slug) return getRouteUrl('room', room.slug) }, [room]) + const backgroundColor = useMemo(() => { + const param = searchParams.get('backgroundColor') + if (!param) return 'transparent' + + const value = param.trim() + + // Allow raw hex passed without '#' (e.g. ?backgroundColor=ff0000) + if ( + /^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{4}$|^[0-9a-fA-F]{6}$|^[0-9a-fA-F]{8}$/.test( + value + ) + ) { + return `#${value}` + } + + // Already-valid hex (e.g. URL-encoded %23ff0000 → '#ff0000') + if (/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value)) { + return value + } + + // Fallback: only allow simple named colors, block anything injectable + if (/^[a-zA-Z]+$/.test(value)) { + return value + } + + return 'transparent' + }, [searchParams]) + useEffect(() => { if (!data?.room?.slug) return setRoom(data.room) @@ -78,20 +106,27 @@ const CreateMeetingButton = () => { if (isPending) { return (
- -
) } @@ -104,6 +139,8 @@ const CreateMeetingButton = () => { justifyContent: 'start', alignItems: 'start', border: 'none', + backgroundColor: backgroundColor, + height: '100%', }} > {roomUrl && room?.slug ? (