2
0

(theme) Add container theme options: border, shadow, filter (#1436)

Closes #1332
This commit is contained in:
Baptiste Arnaud
2024-04-10 10:19:54 +02:00
committed by GitHub
parent 75dd554ac2
commit 5c3c7c2b64
46 changed files with 2126 additions and 549 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/js",
"version": "0.2.64",
"version": "0.2.65",
"description": "Javascript library to display typebots on your website",
"type": "module",
"main": "dist/index.js",
@ -31,6 +31,7 @@
"@typebot.io/env": "workspace:*",
"@typebot.io/lib": "workspace:*",
"@typebot.io/schemas": "workspace:*",
"@typebot.io/theme": "workspace:*",
"@typebot.io/tsconfig": "workspace:*",
"@types/dompurify": "3.0.3",
"autoprefixer": "10.4.14",

View File

@ -2,47 +2,6 @@
@tailwind components;
@tailwind utilities;
:host {
--typebot-container-bg-image: none;
--typebot-container-bg-color: transparent;
--typebot-container-font-family: 'Open Sans';
--typebot-container-color: #303235;
--typebot-button-bg-color: #0042da;
--typebot-button-bg-color-rgb: 0, 66, 218;
--typebot-button-color: #ffffff;
--typebot-checkbox-bg-color: #ffffff;
--typebot-host-bubble-bg-color: #f7f8ff;
--typebot-host-bubble-color: #303235;
--typebot-guest-bubble-bg-color: #ff8e21;
--typebot-guest-bubble-color: #ffffff;
--typebot-input-bg-color: #ffffff;
--typebot-input-color: #303235;
--typebot-input-placeholder-color: #9095a0;
--typebot-header-bg-color: #ffffff;
--typebot-header-color: #303235;
--selectable-base-alpha: 0;
--typebot-border-radius: 6px;
--typebot-progress-bar-position: fixed;
--typebot-progress-bar-bg-color: #f7f8ff;
--typebot-progress-bar-color: #0042da;
--typebot-progress-bar-height: 6px;
--typebot-progress-bar-top: 0;
--typebot-progress-bar-bottom: auto;
/* Phone input */
--PhoneInputCountryFlag-borderColor: transparent;
--PhoneInput-color--focus: transparent;
}
/* Hide scrollbar for Chrome, Safari and Opera */
.scrollable-container::-webkit-scrollbar {
display: none;
@ -161,67 +120,100 @@ pre {
.typebot-container {
background-image: var(--typebot-container-bg-image);
background-color: var(--typebot-container-bg-color);
background-position: center;
background-size: cover;
font-family: var(--typebot-container-font-family), -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
container-type: inline-size;
}
.typebot-chat-view {
max-width: var(--typebot-chat-container-max-width);
background-color: rgba(
var(--typebot-chat-container-bg-rgb),
var(--typebot-chat-container-opacity)
);
color: rgb(var(--typebot-chat-container-color));
min-height: 100%;
backdrop-filter: blur(var(--typebot-chat-container-blur));
border-width: var(--typebot-chat-container-border-width);
border-color: rgba(
var(--typebot-chat-container-border-rgb),
var(--typebot-chat-container-border-opacity)
);
padding-left: 20px;
padding-right: 20px;
box-shadow: var(--typebot-chat-container-box-shadow);
}
@container (min-width: 480px) {
.typebot-chat-view {
min-height: var(--typebot-chat-container-max-height);
max-height: var(--typebot-chat-container-max-height);
border-radius: var(--typebot-chat-container-border-radius);
}
}
.typebot-button {
color: var(--typebot-button-color);
background-color: var(--typebot-button-bg-color);
border: 1px solid var(--typebot-button-bg-color);
border-radius: var(--typebot-border-radius);
background-color: rgba(
var(--typebot-button-bg-rgb),
var(--typebot-button-opacity)
);
border-width: var(--typebot-button-border-width);
border-color: rgba(
var(--typebot-button-border-rgb),
var(--typebot-button-border-opacity)
);
border-radius: var(--typebot-button-border-radius);
box-shadow: var(--typebot-button-box-shadow);
backdrop-filter: blur(var(--typebot-button-blur));
transition: all 0.3s ease;
}
.typebot-button.selectable {
color: var(--typebot-host-bubble-color);
background-color: var(--typebot-host-bubble-bg-color);
border: 1px solid var(--typebot-button-bg-color);
}
.typebot-selectable {
border: 1px solid
rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.25)
);
border-radius: var(--typebot-border-radius);
color: var(--typebot-container-color);
border-width: var(--typebot-button-border-width);
border-color: rgba(
var(--typebot-button-border-rgb),
calc(var(--selectable-alpha-ratio) * 0.25)
);
border-radius: var(--typebot-button-border-radius);
color: rgb(var(--typebot-chat-container-color));
background-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.08)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.08)
);
transition: all 0.3s ease;
backdrop-filter: blur(2px);
}
.typebot-selectable:hover {
background-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.12)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.12)
);
border-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.3)
var(--typebot-button-border-rgb),
calc(var(--selectable-alpha-ratio) * 0.3)
);
}
.typebot-selectable.selected {
background-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.18)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.18)
);
border-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.35)
var(--typebot-button-border-rgb),
calc(var(--selectable-alpha-ratio) * 0.35)
);
}
.typebot-checkbox {
border: 1px solid var(--typebot-button-bg-color);
border-radius: var(--typebot-border-radius);
background-color: var(--typebot-checkbox-bg-color);
border: 1px solid
rgba(var(--typebot-button-bg-rgb), var(--typebot-button-opacity));
border-radius: var(--typebot-button-border-radius);
background-color: rgba(var(--typebot-checkbox-bg-rgb));
color: var(--typebot-button-color);
padding: 1px;
border-radius: 2px;
@ -229,7 +221,7 @@ pre {
}
.typebot-checkbox.checked {
background-color: var(--typebot-button-bg-color);
background-color: rgb(var(--typebot-button-bg-rgb));
}
.typebot-host-bubble {
@ -237,22 +229,56 @@ pre {
}
.typebot-host-bubble > .bubble-typing {
background-color: var(--typebot-host-bubble-bg-color);
border: var(--typebot-host-bubble-border);
background-color: rgba(
var(--typebot-host-bubble-bg-rgb),
var(--typebot-host-bubble-opacity)
);
border-width: var(--typebot-host-bubble-border-width);
border-color: rgba(
var(--typebot-host-bubble-border-rgb),
var(--typebot-host-bubble-border-opacity)
);
border-radius: var(--typebot-host-bubble-border-radius);
box-shadow: var(--typebot-host-bubble-box-shadow);
backdrop-filter: blur(var(--typebot-host-bubble-blur));
}
.typebot-host-bubble img,
.typebot-host-bubble video,
.typebot-host-bubble iframe {
border-radius: 6px;
}
.typebot-guest-bubble {
color: var(--typebot-guest-bubble-color);
background-color: var(--typebot-guest-bubble-bg-color);
border-radius: 6px;
background-color: rgba(
var(--typebot-guest-bubble-bg-rgb),
var(--typebot-guest-bubble-opacity)
);
border-width: var(--typebot-guest-bubble-border-width);
border-color: rgba(
var(--typebot-guest-bubble-border-rgb),
var(--typebot-guest-bubble-border-opacity)
);
border-radius: var(--typebot-guest-bubble-border-radius);
box-shadow: var(--typebot-guest-bubble-box-shadow);
backdrop-filter: blur(var(--typebot-guest-bubble-blur));
}
.typebot-input {
color: var(--typebot-input-color);
background-color: var(--typebot-input-bg-color);
box-shadow: 0 2px 6px -1px rgba(0, 0, 0, 0.1);
border-radius: var(--typebot-border-radius);
background-color: rgba(
var(--typebot-input-bg-rgb),
var(--typebot-input-opacity)
);
border-width: var(--typebot-input-border-width);
border-color: rgba(
var(--typebot-input-border-rgb),
var(--typebot-input-border-opacity)
);
border-radius: var(--typebot-input-border-radius);
box-shadow: var(--typebot-input-box-shadow);
backdrop-filter: blur(var(--typebot-input-blur));
}
.typebot-input-error-message {
@ -263,24 +289,20 @@ pre {
fill: var(--typebot-button-color);
}
.typebot-chat-view {
max-width: 900px;
}
.ping span {
background-color: var(--typebot-button-bg-color);
background-color: rgb(var(--typebot-button-bg-rgb));
}
.rating-icon-container svg {
width: 42px;
height: 42px;
stroke: var(--typebot-button-bg-color);
stroke: rgb(var(--typebot-button-bg-rgb));
fill: var(--typebot-host-bubble-bg-color);
transition: fill 100ms ease-out;
}
.rating-icon-container.selected svg {
fill: var(--typebot-button-bg-color);
fill: rgb(var(--typebot-button-bg-rgb));
}
.rating-icon-container:hover svg {
@ -292,59 +314,60 @@ pre {
}
.upload-progress-bar {
background-color: var(--typebot-button-bg-color);
border-radius: var(--typebot-border-radius);
background-color: rgb(var(--typebot-button-bg-rgb));
border-radius: var(--typebot-input-border-radius);
}
.total-files-indicator {
background-color: var(--typebot-button-bg-color);
background-color: rgb(var(--typebot-button-bg-rgb));
color: var(--typebot-button-color);
font-size: 10px;
}
.typebot-upload-input {
transition: border-color 100ms ease-out;
border-radius: var(--typebot-border-radius);
border-radius: var(--typebot-input-border-radius);
}
.typebot-upload-input.dragging-over {
border-color: var(--typebot-button-bg-color);
border-color: rgb(var(--typebot-button-bg-rgb));
}
.secondary-button {
background-color: var(--typebot-host-bubble-bg-color);
color: var(--typebot-host-bubble-color);
border-radius: var(--typebot-border-radius);
border-radius: var(--typebot-button-border-radius);
}
.typebot-country-select {
color: var(--typebot-input-color);
background-color: var(--typebot-input-bg-color);
border-radius: var(--typebot-border-radius);
border-radius: var(--typebot-button-border-radius);
}
.typebot-date-input {
color-scheme: light;
color: var(--typebot-input-color);
background-color: var(--typebot-input-bg-color);
border-radius: var(--typebot-border-radius);
border-radius: var(--typebot-input-border-radius);
}
.typebot-popup-blocked-toast {
border-radius: var(--typebot-border-radius);
border-radius: var(--typebot-input-border-radius);
}
.typebot-picture-button {
color: var(--typebot-button-color);
background-color: var(--typebot-button-bg-color);
border-radius: var(--typebot-border-radius);
background-color: rgb(var(--typebot-button-bg-rgb));
border-radius: var(--typebot-button-border-radius);
transition: all 0.3s ease;
width: 236px;
}
.typebot-picture-button > img,
.typebot-selectable-picture > img {
border-radius: var(--typebot-border-radius) var(--typebot-border-radius) 0 0;
border-radius: var(--typebot-button-border-radius)
var(--typebot-button-border-radius) 0 0;
min-width: 200px;
width: 100%;
max-height: 200px;
@ -362,14 +385,14 @@ pre {
.typebot-selectable-picture {
border: 1px solid
rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.25)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.25)
);
border-radius: var(--typebot-border-radius);
color: var(--typebot-container-color);
border-radius: var(--typebot-button-border-radius);
color: rgb(var(--typebot-chat-container-color));
background-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.08)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.08)
);
transition: all 0.3s ease;
width: 236px;
@ -377,23 +400,23 @@ pre {
.typebot-selectable-picture:hover {
background-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.12)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.12)
);
border-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.3)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.3)
);
}
.typebot-selectable-picture.selected {
background-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.18)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.18)
);
border-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.35)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.35)
);
}
@ -404,8 +427,8 @@ select option {
.typebot-progress-bar-container {
background-color: rgba(
var(--typebot-button-bg-color-rgb),
calc(var(--selectable-base-alpha) + 0.12)
var(--typebot-button-bg-rgb),
calc(var(--selectable-alpha-ratio) * 0.12)
);
height: var(--typebot-progress-bar-height);

View File

@ -16,7 +16,6 @@ import {
import { setCssVariablesValue } from '@/utils/setCssVariablesValue'
import immutableCss from '../assets/immutable.css'
import { Font, InputBlock, StartFrom } from '@typebot.io/schemas'
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
import { clsx } from 'clsx'
import { HTTPError } from 'ky'
import { injectFont } from '@/utils/injectFont'
@ -25,6 +24,11 @@ import { Portal } from 'solid-js/web'
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
import { persist } from '@/utils/persist'
import { setBotContainerHeight } from '@/utils/botContainerHeightSignal'
import {
defaultFontFamily,
defaultFontType,
defaultProgressBarPosition,
} from '@typebot.io/schemas/features/typebot/theme/constants'
export type BotProps = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -262,8 +266,10 @@ const BotContent = (props: BotContentProps) => {
createEffect(() => {
injectFont(
props.initialChatReply.typebot.theme.general?.font ??
defaultTheme.general.font
props.initialChatReply.typebot.theme.general?.font ?? {
type: defaultFontType,
family: defaultFontFamily,
}
)
if (!botContainer) return
setCssVariablesValue(
@ -282,7 +288,7 @@ const BotContent = (props: BotContentProps) => {
<div
ref={botContainer}
class={clsx(
'relative flex w-full h-full text-base overflow-hidden bg-cover bg-center flex-col items-center typebot-container @container',
'relative flex w-full h-full text-base overflow-hidden flex-col justify-center items-center typebot-container',
props.class
)}
>
@ -296,8 +302,7 @@ const BotContent = (props: BotContentProps) => {
when={
props.progressBarRef &&
(props.initialChatReply.typebot.theme.general?.progressBar
?.position ?? defaultTheme.general.progressBar.position) ===
'fixed'
?.position ?? defaultProgressBarPosition) === 'fixed'
}
fallback={<ProgressBar value={progressValue() as number} />}
>
@ -306,7 +311,7 @@ const BotContent = (props: BotContentProps) => {
</Portal>
</Show>
</Show>
<div class="flex w-full h-full justify-center">
<div class="flex w-full h-full justify-center items-center">
<ConversationContainer
context={props.context}
initialChatReply={props.initialChatReply}

View File

@ -6,8 +6,11 @@ import { HostBubble } from '../bubbles/HostBubble'
import { InputChatBlock } from '../InputChatBlock'
import { AvatarSideContainer } from './AvatarSideContainer'
import { StreamingBubble } from '../bubbles/StreamingBubble'
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
import {
defaultGuestAvatarIsEnabled,
defaultHostAvatarIsEnabled,
} from '@typebot.io/schemas/features/typebot/theme/constants'
type Props = Pick<ContinueChatResponse, 'messages' | 'input'> & {
theme: Theme
@ -77,7 +80,7 @@ export const ChatChunk = (props: Props) => {
<Show
when={
(props.theme.chat?.hostAvatar?.isEnabled ??
defaultTheme.chat.hostAvatar.isEnabled) &&
defaultHostAvatarIsEnabled) &&
props.messages.length > 0
}
>
@ -93,7 +96,7 @@ export const ChatChunk = (props: Props) => {
style={{
'max-width':
props.theme.chat?.guestAvatar?.isEnabled ??
defaultTheme.chat.guestAvatar.isEnabled
defaultGuestAvatarIsEnabled
? isMobile()
? 'calc(100% - 32px - 32px)'
: 'calc(100% - 48px - 48px)'
@ -131,7 +134,7 @@ export const ChatChunk = (props: Props) => {
chunkIndex={props.index}
hasHostAvatar={
props.theme.chat?.hostAvatar?.isEnabled ??
defaultTheme.chat.hostAvatar.isEnabled
defaultHostAvatarIsEnabled
}
guestAvatar={props.theme.chat?.guestAvatar}
context={props.context}
@ -151,7 +154,7 @@ export const ChatChunk = (props: Props) => {
<Show
when={
props.theme.chat?.hostAvatar?.isEnabled ??
defaultTheme.chat.hostAvatar.isEnabled
defaultHostAvatarIsEnabled
}
>
<AvatarSideContainer
@ -165,7 +168,7 @@ export const ChatChunk = (props: Props) => {
style={{
'max-width':
props.theme.chat?.hostAvatar?.isEnabled ??
defaultTheme.chat.hostAvatar.isEnabled
defaultHostAvatarIsEnabled
? isMobile()
? 'calc(100% - 32px - 32px)'
: 'calc(100% - 48px - 48px)'

View File

@ -284,7 +284,7 @@ export const ConversationContainer = (props: Props) => {
return (
<div
ref={chatContainer}
class="flex flex-col overflow-y-auto w-full min-h-full px-3 pt-10 relative scrollable-container typebot-chat-view scroll-smooth gap-2"
class="flex flex-col overflow-y-auto w-full px-3 pt-10 relative scrollable-container typebot-chat-view scroll-smooth gap-2"
>
<For each={chatChunks()}>
{(chatChunk, index) => (

View File

@ -2,7 +2,7 @@ import { Theme } from '@typebot.io/schemas'
import { Show } from 'solid-js'
import { LoadingBubble } from '../bubbles/LoadingBubble'
import { AvatarSideContainer } from './AvatarSideContainer'
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
import { defaultHostAvatarIsEnabled } from '@typebot.io/schemas/features/typebot/theme/constants'
type Props = {
theme: Theme
@ -15,7 +15,7 @@ export const LoadingChunk = (props: Props) => (
<Show
when={
props.theme.chat?.hostAvatar?.isEnabled ??
defaultTheme.chat.hostAvatar.isEnabled
defaultHostAvatarIsEnabled
}
>
<AvatarSideContainer

View File

@ -35,8 +35,8 @@ import { MultiplePictureChoice } from '@/features/blocks/inputs/pictureChoice/Mu
import { formattedMessages } from '@/utils/formattedMessagesSignal'
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
import { defaultPaymentInputOptions } from '@typebot.io/schemas/features/blocks/inputs/payment/constants'
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
import { persist } from '@/utils/persist'
import { defaultGuestAvatarIsEnabled } from '@typebot.io/schemas/features/typebot/theme/constants'
type Props = {
ref: HTMLDivElement | undefined
@ -82,8 +82,7 @@ export const InputChatBlock = (props: Props) => {
<GuestBubble
message={formattedMessage() ?? (answer() as string)}
showAvatar={
props.guestAvatar?.isEnabled ??
defaultTheme.chat.guestAvatar.isEnabled
props.guestAvatar?.isEnabled ?? defaultGuestAvatarIsEnabled
}
avatarSrc={props.guestAvatar?.url && props.guestAvatar.url}
/>

View File

@ -1,7 +1,7 @@
import { SendButton } from '@/components/SendButton'
import { InputSubmitContent } from '@/types'
import type { RatingInputBlock } from '@typebot.io/schemas'
import { createSignal, For, Match, Switch } from 'solid-js'
import { createSignal, For, Match, Switch, Show } from 'solid-js'
import { isDefined, isEmpty, isNotDefined } from '@typebot.io/lib'
import { Button } from '@/components/Button'
import { defaultRatingInputOptions } from '@typebot.io/schemas/features/blocks/inputs/rating/constants'
@ -107,17 +107,24 @@ const RatingButton = (props: RatingButtonProps) => {
'Numbers'
}
>
<Button
on:click={handleClick}
class={
props.isOneClickSubmitEnabled ||
(isDefined(props.rating) && props.idx <= props.rating)
? ''
: 'selectable'
}
>
{props.idx}
</Button>
<Show when={props.isOneClickSubmitEnabled}>
<Button on:click={handleClick}>{props.idx}</Button>
</Show>
<Show when={!props.isOneClickSubmitEnabled}>
<div
role="checkbox"
aria-checked={isDefined(props.rating) && props.idx <= props.rating}
on:click={handleClick}
class={
'py-2 px-4 font-semibold focus:outline-none cursor-pointer select-none typebot-selectable' +
(isDefined(props.rating) && props.idx <= props.rating
? ' selected'
: '')
}
>
{props.idx}
</div>
</Show>
</Match>
<Match
when={

View File

@ -6,6 +6,7 @@ import { isMobile } from '@/utils/isMobileSignal'
import type { TextInputBlock } from '@typebot.io/schemas'
import { createSignal, onCleanup, onMount } from 'solid-js'
import { defaultTextInputOptions } from '@typebot.io/schemas/features/blocks/inputs/text/constants'
import clsx from 'clsx'
type Props = {
block: TextInputBlock
@ -55,7 +56,10 @@ export const TextInput = (props: Props) => {
return (
<div
class={'flex items-end justify-between pr-2 typebot-input w-full'}
class={clsx(
'flex justify-between pr-2 typebot-input w-full',
props.block.options?.isLong ? 'items-end' : 'items-center'
)}
data-testid="input"
style={{
'max-width': props.block.options?.isLong ? undefined : '350px',

View File

@ -1,6 +1,6 @@
import { isNotEmpty } from '@typebot.io/lib'
import { Font } from '@typebot.io/schemas'
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
import { defaultFontFamily } from '@typebot.io/schemas/features/typebot/theme/constants'
const googleFontCdnBaseUrl = 'https://fonts.bunny.net/css2'
const elementId = 'typebot-font'
@ -10,8 +10,7 @@ export const injectFont = (font: Font) => {
if (typeof font === 'string' || font.type === 'Google') {
const fontFamily =
(typeof font === 'string' ? font : font.family) ??
defaultTheme.general.font.family
(typeof font === 'string' ? font : font.family) ?? defaultFontFamily
if (existingFont?.getAttribute('href')?.includes(fontFamily)) return
existingFont?.remove()
const fontElement = document.createElement('link')

View File

@ -1,24 +1,51 @@
import {
Background,
ChatTheme,
ContainerColors,
ContainerBorderTheme,
ContainerTheme,
GeneralTheme,
InputColors,
InputTheme,
Theme,
} from '@typebot.io/schemas'
import { isLight, hexToRgb } from '@typebot.io/lib/hexToRgb'
import { isNotEmpty } from '@typebot.io/lib'
import { isDefined, isEmpty } from '@typebot.io/lib'
import {
BackgroundType,
defaultTheme,
defaultBackgroundColor,
defaultBackgroundType,
defaultButtonsBackgroundColor,
defaultButtonsColor,
defaultButtonsBorderThickness,
defaultContainerBackgroundColor,
defaultContainerMaxHeight,
defaultContainerMaxWidth,
defaultDarkTextColor,
defaultFontFamily,
defaultGuestBubblesBackgroundColor,
defaultGuestBubblesColor,
defaultHostBubblesBackgroundColor,
defaultHostBubblesColor,
defaultInputsBackgroundColor,
defaultInputsColor,
defaultInputsPlaceholderColor,
defaultLightTextColor,
defaultProgressBarBackgroundColor,
defaultProgressBarColor,
defaultProgressBarPlacement,
defaultProgressBarPosition,
defaultProgressBarThickness,
defaultInputsShadow,
defaultOpacity,
defaultBlur,
defaultRoundness,
} from '@typebot.io/schemas/features/typebot/theme/constants'
import { isChatContainerLight } from '@typebot.io/theme/isChatContainerLight'
const cssVariableNames = {
general: {
bgImage: '--typebot-container-bg-image',
bgColor: '--typebot-container-bg-color',
fontFamily: '--typebot-container-font-family',
color: '--typebot-container-color',
progressBar: {
position: '--typebot-progress-bar-position',
color: '--typebot-progress-bar-color',
@ -29,28 +56,67 @@ const cssVariableNames = {
},
},
chat: {
container: {
maxWidth: '--typebot-chat-container-max-width',
maxHeight: '--typebot-chat-container-max-height',
bgColor: '--typebot-chat-container-bg-rgb',
color: '--typebot-chat-container-color',
borderRadius: '--typebot-chat-container-border-radius',
borderWidth: '--typebot-chat-container-border-width',
borderColor: '--typebot-chat-container-border-rgb',
borderOpacity: '--typebot-chat-container-border-opacity',
opacity: '--typebot-chat-container-opacity',
blur: '--typebot-chat-container-blur',
boxShadow: '--typebot-chat-container-box-shadow',
},
hostBubbles: {
bgColor: '--typebot-host-bubble-bg-color',
bgColor: '--typebot-host-bubble-bg-rgb',
color: '--typebot-host-bubble-color',
borderRadius: '--typebot-host-bubble-border-radius',
borderWidth: '--typebot-host-bubble-border-width',
borderColor: '--typebot-host-bubble-border-rgb',
borderOpacity: '--typebot-host-bubble-border-opacity',
opacity: '--typebot-host-bubble-opacity',
blur: '--typebot-host-bubble-blur',
boxShadow: '--typebot-host-bubble-box-shadow',
},
guestBubbles: {
bgColor: '--typebot-guest-bubble-bg-color',
bgColor: '--typebot-guest-bubble-bg-rgb',
color: '--typebot-guest-bubble-color',
borderRadius: '--typebot-guest-bubble-border-radius',
borderWidth: '--typebot-guest-bubble-border-width',
borderColor: '--typebot-guest-bubble-border-rgb',
borderOpacity: '--typebot-guest-bubble-border-opacity',
opacity: '--typebot-guest-bubble-opacity',
blur: '--typebot-guest-bubble-blur',
boxShadow: '--typebot-guest-bubble-box-shadow',
},
inputs: {
bgColor: '--typebot-input-bg-color',
bgColor: '--typebot-input-bg-rgb',
color: '--typebot-input-color',
placeholderColor: '--typebot-input-placeholder-color',
borderRadius: '--typebot-input-border-radius',
borderWidth: '--typebot-input-border-width',
borderColor: '--typebot-input-border-rgb',
borderOpacity: '--typebot-input-border-opacity',
opacity: '--typebot-input-opacity',
blur: '--typebot-input-blur',
boxShadow: '--typebot-input-box-shadow',
},
buttons: {
bgColor: '--typebot-button-bg-color',
bgColorRgb: '--typebot-button-bg-color-rgb',
bgRgb: '--typebot-button-bg-rgb',
color: '--typebot-button-color',
borderRadius: '--typebot-button-border-radius',
borderWidth: '--typebot-button-border-width',
borderColor: '--typebot-button-border-rgb',
borderOpacity: '--typebot-button-border-opacity',
opacity: '--typebot-button-opacity',
blur: '--typebot-button-blur',
boxShadow: '--typebot-button-box-shadow',
},
checkbox: {
bgColor: '--typebot-checkbox-bg-color',
color: '--typebot-checkbox-color',
baseAlpha: '--selectable-base-alpha',
bgRgb: '--typebot-checkbox-bg-rgb',
alphaRatio: '--selectable-alpha-ratio',
},
},
} as const
@ -63,43 +129,31 @@ export const setCssVariablesValue = (
if (!theme) return
const documentStyle = container?.style
if (!documentStyle) return
setGeneralTheme(
theme.general ?? defaultTheme.general,
documentStyle,
isPreview
)
setChatTheme(theme.chat ?? defaultTheme.chat, documentStyle)
setGeneralTheme(theme.general, documentStyle, isPreview)
setChatTheme(theme.chat, theme.general?.background, documentStyle)
}
const setGeneralTheme = (
generalTheme: GeneralTheme,
generalTheme: GeneralTheme | undefined,
documentStyle: CSSStyleDeclaration,
isPreview?: boolean
) => {
setTypebotBackground(
generalTheme.background ?? defaultTheme.general.background,
documentStyle
)
setGeneralBackground(generalTheme?.background, documentStyle)
documentStyle.setProperty(
cssVariableNames.general.fontFamily,
(typeof generalTheme.font === 'string'
(typeof generalTheme?.font === 'string'
? generalTheme.font
: generalTheme.font?.family) ?? defaultTheme.general.font.family
)
setProgressBar(
generalTheme.progressBar ?? defaultTheme.general.progressBar,
documentStyle,
isPreview
: generalTheme?.font?.family) ?? defaultFontFamily
)
setProgressBar(generalTheme?.progressBar, documentStyle, isPreview)
}
const setProgressBar = (
progressBar: NonNullable<GeneralTheme['progressBar']>,
progressBar: GeneralTheme['progressBar'],
documentStyle: CSSStyleDeclaration,
isPreview?: boolean
) => {
const position =
progressBar.position ?? defaultTheme.general.progressBar.position
const position = progressBar?.position ?? defaultProgressBarPosition
documentStyle.setProperty(
cssVariableNames.general.progressBar.position,
@ -107,22 +161,20 @@ const setProgressBar = (
)
documentStyle.setProperty(
cssVariableNames.general.progressBar.color,
progressBar.color ?? defaultTheme.general.progressBar.color
progressBar?.color ?? defaultProgressBarColor
)
documentStyle.setProperty(
cssVariableNames.general.progressBar.colorRgb,
hexToRgb(
progressBar.backgroundColor ??
defaultTheme.general.progressBar.backgroundColor
progressBar?.backgroundColor ?? defaultProgressBarBackgroundColor
).join(', ')
)
documentStyle.setProperty(
cssVariableNames.general.progressBar.height,
`${progressBar.thickness ?? defaultTheme.general.progressBar.thickness}px`
`${progressBar?.thickness ?? defaultProgressBarThickness}px`
)
const placement =
progressBar.placement ?? defaultTheme.general.progressBar.placement
const placement = progressBar?.placement ?? defaultProgressBarPlacement
documentStyle.setProperty(
cssVariableNames.general.progressBar.top,
@ -136,123 +188,428 @@ const setProgressBar = (
}
const setChatTheme = (
chatTheme: ChatTheme,
chatTheme: ChatTheme | undefined,
generalBackground: GeneralTheme['background'],
documentStyle: CSSStyleDeclaration
) => {
setHostBubbles(
chatTheme.hostBubbles ?? defaultTheme.chat.hostBubbles,
documentStyle
setChatContainer(
chatTheme?.container,
generalBackground,
documentStyle,
chatTheme?.roundness
)
setGuestBubbles(
chatTheme.guestBubbles ?? defaultTheme.chat.guestBubbles,
documentStyle
setHostBubbles(chatTheme?.hostBubbles, documentStyle, chatTheme?.roundness)
setGuestBubbles(chatTheme?.guestBubbles, documentStyle, chatTheme?.roundness)
setButtons(chatTheme?.buttons, documentStyle, chatTheme?.roundness)
setInputs(chatTheme?.inputs, documentStyle, chatTheme?.roundness)
setCheckbox(chatTheme?.container, generalBackground, documentStyle)
}
const setChatContainer = (
container: ChatTheme['container'],
generalBackground: GeneralTheme['background'],
documentStyle: CSSStyleDeclaration,
legacyRoundness?: ChatTheme['roundness']
) => {
const chatContainerBgColor =
container?.backgroundColor ?? defaultContainerBackgroundColor
const isBgDisabled =
chatContainerBgColor === 'transparent' || isEmpty(chatContainerBgColor)
documentStyle.setProperty(
cssVariableNames.chat.container.bgColor,
isBgDisabled ? '0, 0, 0' : hexToRgb(chatContainerBgColor).join(', ')
)
setButtons(chatTheme.buttons ?? defaultTheme.chat.buttons, documentStyle)
setInputs(chatTheme.inputs ?? defaultTheme.chat.inputs, documentStyle)
setRoundness(
chatTheme.roundness ?? defaultTheme.chat.roundness,
documentStyle
documentStyle.setProperty(
cssVariableNames.chat.container.color,
hexToRgb(
container?.color ??
(isChatContainerLight({
chatContainer: container,
generalBackground,
})
? defaultLightTextColor
: defaultDarkTextColor)
).join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.container.maxWidth,
container?.maxWidth ?? defaultContainerMaxWidth
)
documentStyle.setProperty(
cssVariableNames.chat.container.maxHeight,
container?.maxHeight ?? defaultContainerMaxHeight
)
const opacity = isBgDisabled
? '1'
: (container?.opacity ?? defaultOpacity).toString()
documentStyle.setProperty(
cssVariableNames.chat.container.opacity,
isBgDisabled ? '0' : (container?.opacity ?? defaultOpacity).toString()
)
documentStyle.setProperty(
cssVariableNames.chat.container.blur,
opacity === '1' || isBgDisabled
? '0xp'
: `${container?.blur ?? defaultBlur}px`
)
setShadow(
container?.shadow,
documentStyle,
cssVariableNames.chat.container.boxShadow
)
setBorderRadius(
container?.border ?? {
roundeness: legacyRoundness ?? defaultRoundness,
},
documentStyle,
cssVariableNames.chat.container.borderRadius
)
documentStyle.setProperty(
cssVariableNames.chat.container.borderWidth,
isDefined(container?.border?.thickness)
? `${container?.border?.thickness}px`
: '0'
)
documentStyle.setProperty(
cssVariableNames.chat.container.borderOpacity,
isDefined(container?.border?.opacity)
? container.border.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.container.borderColor,
hexToRgb(container?.border?.color ?? '').join(', ')
)
}
const setHostBubbles = (
hostBubbles: ContainerColors,
documentStyle: CSSStyleDeclaration
hostBubbles: ContainerTheme | undefined,
documentStyle: CSSStyleDeclaration,
legacyRoundness?: ChatTheme['roundness']
) => {
documentStyle.setProperty(
cssVariableNames.chat.hostBubbles.bgColor,
hostBubbles.backgroundColor ?? defaultTheme.chat.hostBubbles.backgroundColor
hexToRgb(
hostBubbles?.backgroundColor ?? defaultHostBubblesBackgroundColor
).join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.hostBubbles.color,
hostBubbles.color ?? defaultTheme.chat.hostBubbles.color
hostBubbles?.color ?? defaultHostBubblesColor
)
setBorderRadius(
hostBubbles?.border ?? {
roundeness: legacyRoundness ?? defaultRoundness,
},
documentStyle,
cssVariableNames.chat.hostBubbles.borderRadius
)
documentStyle.setProperty(
cssVariableNames.chat.hostBubbles.borderWidth,
isDefined(hostBubbles?.border?.thickness)
? `${hostBubbles?.border?.thickness}px`
: '0'
)
documentStyle.setProperty(
cssVariableNames.chat.hostBubbles.borderColor,
hexToRgb(hostBubbles?.border?.color ?? '').join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.hostBubbles.opacity,
isDefined(hostBubbles?.opacity)
? hostBubbles.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.hostBubbles.borderOpacity,
isDefined(hostBubbles?.border?.opacity)
? hostBubbles.border.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.hostBubbles.blur,
isDefined(hostBubbles?.blur)
? `${hostBubbles.blur ?? 0}px`
: defaultBlur.toString()
)
setShadow(
hostBubbles?.shadow,
documentStyle,
cssVariableNames.chat.hostBubbles.boxShadow
)
}
const setGuestBubbles = (
guestBubbles: ContainerColors,
documentStyle: CSSStyleDeclaration
guestBubbles: ContainerTheme | undefined,
documentStyle: CSSStyleDeclaration,
legacyRoundness?: ChatTheme['roundness']
) => {
documentStyle.setProperty(
cssVariableNames.chat.guestBubbles.bgColor,
guestBubbles.backgroundColor ??
defaultTheme.chat.guestBubbles.backgroundColor
hexToRgb(
guestBubbles?.backgroundColor ?? defaultGuestBubblesBackgroundColor
).join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.guestBubbles.color,
guestBubbles.color ?? defaultTheme.chat.guestBubbles.color
guestBubbles?.color ?? defaultGuestBubblesColor
)
setBorderRadius(
guestBubbles?.border ?? {
roundeness: legacyRoundness ?? defaultRoundness,
},
documentStyle,
cssVariableNames.chat.guestBubbles.borderRadius
)
documentStyle.setProperty(
cssVariableNames.chat.guestBubbles.borderWidth,
isDefined(guestBubbles?.border?.thickness)
? `${guestBubbles?.border?.thickness}px`
: '0'
)
documentStyle.setProperty(
cssVariableNames.chat.guestBubbles.borderColor,
hexToRgb(guestBubbles?.border?.color ?? '').join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.guestBubbles.borderOpacity,
isDefined(guestBubbles?.border?.opacity)
? guestBubbles.border.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.guestBubbles.opacity,
isDefined(guestBubbles?.opacity)
? guestBubbles.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.guestBubbles.blur,
isDefined(guestBubbles?.blur)
? `${guestBubbles.blur ?? 0}px`
: defaultBlur.toString()
)
setShadow(
guestBubbles?.shadow,
documentStyle,
cssVariableNames.chat.guestBubbles.boxShadow
)
}
const setButtons = (
buttons: ContainerColors,
documentStyle: CSSStyleDeclaration
buttons: ContainerTheme | undefined,
documentStyle: CSSStyleDeclaration,
legacyRoundness?: ChatTheme['roundness']
) => {
const bgColor =
buttons.backgroundColor ?? defaultTheme.chat.buttons.backgroundColor
documentStyle.setProperty(cssVariableNames.chat.buttons.bgColor, bgColor)
const bgColor = buttons?.backgroundColor ?? defaultButtonsBackgroundColor
documentStyle.setProperty(
cssVariableNames.chat.buttons.bgColorRgb,
cssVariableNames.chat.buttons.bgRgb,
hexToRgb(bgColor).join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.buttons.bgRgb,
hexToRgb(bgColor).join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.buttons.color,
buttons.color ?? defaultTheme.chat.buttons.color
buttons?.color ?? defaultButtonsColor
)
setBorderRadius(
buttons?.border ?? {
roundeness: legacyRoundness ?? defaultRoundness,
},
documentStyle,
cssVariableNames.chat.buttons.borderRadius
)
documentStyle.setProperty(
cssVariableNames.chat.buttons.borderWidth,
isDefined(buttons?.border?.thickness)
? `${buttons?.border?.thickness}px`
: `${defaultButtonsBorderThickness}px`
)
documentStyle.setProperty(
cssVariableNames.chat.buttons.borderColor,
hexToRgb(
buttons?.border?.color ??
buttons?.backgroundColor ??
defaultButtonsBackgroundColor
).join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.buttons.borderOpacity,
isDefined(buttons?.border?.opacity)
? buttons.border.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.buttons.opacity,
isDefined(buttons?.opacity)
? buttons.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.buttons.blur,
isDefined(buttons?.blur) ? `${buttons.blur ?? 0}px` : defaultBlur.toString()
)
setShadow(
buttons?.shadow,
documentStyle,
cssVariableNames.chat.buttons.boxShadow
)
}
const setInputs = (inputs: InputColors, documentStyle: CSSStyleDeclaration) => {
const setInputs = (
inputs: InputTheme | undefined,
documentStyle: CSSStyleDeclaration,
legacyRoundness?: ChatTheme['roundness']
) => {
documentStyle.setProperty(
cssVariableNames.chat.inputs.bgColor,
inputs.backgroundColor ?? defaultTheme.chat.inputs.backgroundColor
hexToRgb(inputs?.backgroundColor ?? defaultInputsBackgroundColor).join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.inputs.color,
inputs.color ?? defaultTheme.chat.inputs.color
inputs?.color ?? defaultInputsColor
)
documentStyle.setProperty(
cssVariableNames.chat.inputs.placeholderColor,
inputs.placeholderColor ?? defaultTheme.chat.inputs.placeholderColor
inputs?.placeholderColor ?? defaultInputsPlaceholderColor
)
setBorderRadius(
inputs?.border ?? {
roundeness: legacyRoundness ?? defaultRoundness,
},
documentStyle,
cssVariableNames.chat.inputs.borderRadius
)
documentStyle.setProperty(
cssVariableNames.chat.inputs.borderWidth,
isDefined(inputs?.border?.thickness)
? `${inputs?.border?.thickness}px`
: '0'
)
documentStyle.setProperty(
cssVariableNames.chat.inputs.borderColor,
hexToRgb(inputs?.border?.color ?? '').join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.inputs.opacity,
isDefined(inputs?.opacity)
? inputs.opacity.toString()
: defaultOpacity.toString()
)
documentStyle.setProperty(
cssVariableNames.chat.inputs.blur,
isDefined(inputs?.blur) ? `${inputs.blur ?? 0}px` : defaultBlur.toString()
)
setShadow(
inputs?.shadow ?? defaultInputsShadow,
documentStyle,
cssVariableNames.chat.inputs.boxShadow
)
}
const setTypebotBackground = (
background: Background,
const setCheckbox = (
container: ChatTheme['container'],
generalBackground: GeneralTheme['background'],
documentStyle: CSSStyleDeclaration
) => {
const chatContainerBgColor =
container?.backgroundColor ?? defaultContainerBackgroundColor
const isChatBgTransparent =
chatContainerBgColor === 'transparent' ||
isEmpty(chatContainerBgColor) ||
(container?.opacity ?? defaultOpacity) <= 0.2
if (isChatBgTransparent) {
const bgType = generalBackground?.type ?? defaultBackgroundType
documentStyle.setProperty(
cssVariableNames.chat.checkbox.bgRgb,
bgType === BackgroundType.IMAGE
? 'rgba(255, 255, 255, 0.75)'
: hexToRgb(
(bgType === BackgroundType.COLOR
? generalBackground?.content
: '#ffffff') ?? '#ffffff'
).join(', ')
)
if (bgType === BackgroundType.IMAGE) {
documentStyle.setProperty(cssVariableNames.chat.checkbox.alphaRatio, '3')
} else {
documentStyle.setProperty(
cssVariableNames.chat.checkbox.alphaRatio,
generalBackground?.content && isLight(generalBackground?.content)
? '1'
: '2'
)
}
} else {
documentStyle.setProperty(
cssVariableNames.chat.checkbox.bgRgb,
hexToRgb(chatContainerBgColor)
.concat(container?.opacity ?? 1)
.join(', ')
)
documentStyle.setProperty(
cssVariableNames.chat.checkbox.alphaRatio,
isLight(chatContainerBgColor) ? '1' : '2'
)
}
}
const setGeneralBackground = (
background: Background | undefined,
documentStyle: CSSStyleDeclaration
) => {
documentStyle.setProperty(cssVariableNames.general.bgImage, null)
documentStyle.setProperty(cssVariableNames.general.bgColor, null)
documentStyle.setProperty(
background?.type === BackgroundType.IMAGE
(background?.type ?? defaultBackgroundType) === BackgroundType.IMAGE
? cssVariableNames.general.bgImage
: cssVariableNames.general.bgColor,
parseBackgroundValue(background)
parseBackgroundValue({
type: background?.type ?? defaultBackgroundType,
content: background?.content ?? defaultBackgroundColor,
})
)
documentStyle.setProperty(
cssVariableNames.chat.checkbox.bgColor,
background?.type === BackgroundType.IMAGE
? 'rgba(255, 255, 255, 0.75)'
: (background?.type === BackgroundType.COLOR
? background.content
: '#ffffff') ?? '#ffffff'
)
const backgroundColor =
background.type === BackgroundType.IMAGE
? '#000000'
: background?.type === BackgroundType.COLOR &&
isNotEmpty(background.content)
? background.content
: '#ffffff'
documentStyle.setProperty(
cssVariableNames.general.color,
isLight(backgroundColor) ? '#303235' : '#ffffff'
)
if (background.type === BackgroundType.IMAGE) {
documentStyle.setProperty(cssVariableNames.chat.checkbox.baseAlpha, '0.40')
} else {
documentStyle.setProperty(cssVariableNames.chat.checkbox.baseAlpha, '0')
}
}
const parseBackgroundValue = ({ type, content }: Background = {}) => {
@ -261,25 +618,80 @@ const parseBackgroundValue = ({ type, content }: Background = {}) => {
return 'transparent'
case undefined:
case BackgroundType.COLOR:
return content ?? defaultTheme.general.background.content
return content ?? defaultBackgroundColor
case BackgroundType.IMAGE:
return `url(${content})`
}
}
const setRoundness = (
roundness: NonNullable<ChatTheme['roundness']>,
documentStyle: CSSStyleDeclaration
const setBorderRadius = (
border: ContainerBorderTheme,
documentStyle: CSSStyleDeclaration,
variableName: string
) => {
switch (roundness) {
switch (border?.roundeness ?? defaultRoundness) {
case 'none': {
documentStyle.setProperty(variableName, '0')
break
}
case 'medium': {
documentStyle.setProperty(variableName, '6px')
break
}
case 'large': {
documentStyle.setProperty(variableName, '20px')
break
}
case 'custom': {
documentStyle.setProperty(
variableName,
`${border.customRoundeness ?? 6}px`
)
break
}
}
}
// Props taken from https://tailwindcss.com/docs/box-shadow
const setShadow = (
shadow: ContainerTheme['shadow'],
documentStyle: CSSStyleDeclaration,
variableName: string
) => {
if (shadow === undefined) {
documentStyle.setProperty(variableName, '0 0 #0000')
return
}
switch (shadow) {
case 'none':
documentStyle.setProperty('--typebot-border-radius', '0')
documentStyle.setProperty(variableName, '0 0 #0000')
break
case 'medium':
documentStyle.setProperty('--typebot-border-radius', '6px')
case 'sm':
documentStyle.setProperty(variableName, '0 1px 2px 0 rgb(0 0 0 / 0.05)')
break
case 'large':
documentStyle.setProperty('--typebot-border-radius', '20px')
case 'md':
documentStyle.setProperty(
variableName,
'0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
)
break
case 'lg':
documentStyle.setProperty(
variableName,
'0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)'
)
break
case 'xl':
documentStyle.setProperty(
variableName,
'0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)'
)
break
case '2xl':
documentStyle.setProperty(
variableName,
'0 25px 50px -12px rgb(0 0 0 / 0.25)'
)
break
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/nextjs",
"version": "0.2.64",
"version": "0.2.65",
"description": "Convenient library to display typebots on your Next.js website",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@typebot.io/react",
"version": "0.2.64",
"version": "0.2.65",
"description": "Convenient library to display typebots on your React app",
"main": "dist/index.js",
"types": "dist/index.d.ts",