diff --git a/README.md b/README.md index c6e66d7..703139b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Bite Consent -Bite Consent is a lightweight React library for managing cookie consent banners with customizable descriptions and actions. It provides an easy way to implement cookie consent functionality in your React applications while ensuring compliance with privacy regulations. +Bite Consent is a lightweight React library for managing cookie consent views with customizable descriptions and actions. It provides an easy way to implement cookie consent functionality in your React applications while ensuring compliance with privacy regulations. ![Screenshot 2024-04-27 at 18 43 55](https://github.com/Seishin/bite-consent/assets/324076/a4df7470-86b4-49a8-89be-73549c410695) @@ -35,7 +35,10 @@ import { useBiteConsent } from 'bite-consent' function App() { const { consentCookie, show, revoke } = useBiteConsent({ - privacyPolicyUrl: 'https://example.com/privacy' + privacyPolicyUrl: 'https://example.com/privacy', + theme: { + mode: 'auto' + } }) return ( @@ -61,7 +64,7 @@ export default App | `visibility` | `auto` \| `visible` \| `hidden` | `auto` | Specifies the visibility of the consent banner. `auto` displays the view based on the availability of the consent cookie, whereas `visible` displays and `hidden` hides it. | | `position` | `Position` \| `CustomPosition` | `bottom-left` | Sets the position of the consent view on the page. See below for details. | | `cookieConfig` | `CookieConfig` | - | Configuration options for consent cookies. See below for details. | -| `themeMode` | `ThemeMode` | `auto` | Sets the theme mode. When `auto` is passed, the widget will automatically observe the theme mode. | +| `theme` | `Theme` | - | Theme object containing color configurations for the consent view. See below for details. | | `onAccept` | `function` | `() => void` | A function to be called when the user accepts the cookie policy. | ### Position @@ -94,6 +97,28 @@ export default App | `secure` | `boolean` | Indicates if the cookie should only be sent over secure connections. | | `sameSite` | `Strict` \| `Lax` \| `None` | The SameSite attribute of the consent cookie. | +### Theme + +The `theme` object allows customization of the colors used in the consent banner. + +| Property | Type | Description | +| -------- | ----------- | ------------------------------------ | +| `mode` | `ThemeMode` | The theme mode. | +| `light` | `ColorSet` | Color configurations for light mode. | +| `dark` | `ColorSet` | Color configurations for dark mode. | + +ColorSet Properties: + +- `background`: Background color of the consent banner. +- `text`: Text color of the consent banner. +- `shadow`: Box shadow of the consent banner. +- `primaryActionBackground`: Background color of primary action buttons. +- `primaryActionHoverBackground`: Background color of primary action buttons on hover. +- `primaryActionText`: Text color of primary action buttons. +- `secondaryActionBackground`: Background color of secondary action buttons. +- `secondaryActionText`: Text color of secondary action buttons. +- `secondaryActionHoverBackground`: Background color of secondary action buttons on hover. + ## License Bite Consent is released under the MIT License. diff --git a/example/src/App.jsx b/example/src/App.jsx index 71a21d3..42eb766 100644 --- a/example/src/App.jsx +++ b/example/src/App.jsx @@ -3,7 +3,10 @@ import React, { useEffect } from 'react' function App() { const { consentCookie, show, revoke } = useBiteConsent({ - privacyPolicyUrl: 'https://example.com/privacy' + privacyPolicyUrl: 'https://example.com/privacy', + theme: { + mode: 'auto' + } }) useEffect(() => { @@ -13,7 +16,14 @@ function App() { return (

Cookie Consent

{consentCookie ?? 'undefined'}

diff --git a/src/BiteConsent.tsx b/src/BiteConsent.tsx index 771f5f5..5166514 100644 --- a/src/BiteConsent.tsx +++ b/src/BiteConsent.tsx @@ -3,6 +3,7 @@ import React, { useEffect } from 'react' import CookieConfig from './CookieConfig' import { Cookies } from './Illustrations' import Position, { CustomPosition, isCustomPosition } from './Position' +import { useTheme } from './ThemeContext' const CONSENT_COOKIE_NAME = 'cookie_consent' @@ -17,6 +18,9 @@ interface Props { const BiteConsent = ({ privacyPolicyUrl, text, visibility = 'auto', position = 'bottom-left', cookieConfig, onAccept }: Props) => { const [visible, setVisible] = React.useState() + const theme = useTheme() + + const colorSet = theme.mode === 'light' ? theme.light : theme.dark useEffect(() => { if (typeof window === 'undefined' || typeof document === 'undefined') return @@ -93,8 +97,9 @@ const BiteConsent = ({ privacyPolicyUrl, text, visibility = 'auto', position = ' position: 'fixed', ...getPosition(), zIndex: 9999, - backgroundColor: '#ffffff', - boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)', + backgroundColor: colorSet.background, + color: colorSet.text, + boxShadow: colorSet.shadow, padding: '1rem', width: '18rem', maxHeight: '18rem', @@ -132,12 +137,13 @@ const BiteConsent = ({ privacyPolicyUrl, text, visibility = 'auto', position = ' { - const [mode, setMode] = useState(presetMode ?? 'auto') +const ThemeProvider = ({ theme: providedTheme, children }: { theme?: Theme; children: ReactNode }) => { + const [theme, setTheme] = useState({ + mode: providedTheme?.mode ?? 'auto', + light: { ...defaultTheme.light, ...providedTheme?.light }, + dark: { ...defaultTheme.dark, ...providedTheme?.dark } + }) useEffect(() => { - if (typeof window === 'undefined' || typeof document === 'undefined' || mode !== 'auto') return + if (typeof window === 'undefined' || typeof document === 'undefined' || theme.mode !== 'auto') return const themeQuery = window.matchMedia('(prefers-color-scheme: dark)') themeQuery.addEventListener('change', (e) => { - setMode(e.matches ? 'dark' : 'light') + setTheme({ ...theme, mode: e.matches ? 'dark' : 'light' }) }) return () => { themeQuery.removeEventListener('change', (e) => { - setMode(e.matches ? 'dark' : 'light') + setTheme({ ...theme, mode: e.matches ? 'dark' : 'light' }) }) } - }, [setMode]) + }, [setTheme]) - return {children} + return {children} } const useTheme = () => useContext(ThemeContext) export default ThemeProvider -export { ThemeMode as Theme, useTheme } +export { Theme, useTheme } diff --git a/src/useBiteConsent.tsx b/src/useBiteConsent.tsx index f2c8ab2..15904be 100644 --- a/src/useBiteConsent.tsx +++ b/src/useBiteConsent.tsx @@ -19,12 +19,12 @@ type BiteConsentOptions = { visibility?: 'auto' | 'visible' | 'hidden' position?: Position | CustomPosition cookieConfig?: CookieConfig - themeMode?: Theme + theme?: Theme onAccept?: () => void } const useBiteConsent = (options: BiteConsentOptions) => { - const { privacyPolicyUrl, text, visibility, position, cookieConfig, themeMode, onAccept } = options + const { privacyPolicyUrl, text, visibility, position, cookieConfig, theme, onAccept } = options const consentCookie = document.cookie.split(';').find((cookie) => cookie.trim().startsWith(cookieConfig?.name ?? CONSENT_COOKIE_NAME)) @@ -46,7 +46,7 @@ const useBiteConsent = (options: BiteConsentOptions) => { const shadowRoot = root.attachShadow({ mode: 'open' }) createRoot(shadowRoot).render( - +