Skip to content

Commit

Permalink
fix: support quotes in custom theme font values (#321)
Browse files Browse the repository at this point in the history
  • Loading branch information
markdalgleish committed May 9, 2022
1 parent 9f06b5a commit 5213855
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 46 deletions.
5 changes: 5 additions & 0 deletions .changeset/strange-phones-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rainbow-me/rainbowkit': patch
---

Fix usage of quotes in custom theme font values
28 changes: 15 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -840,19 +840,21 @@ import {
const App = () => {
return (
<RainbowKitProvider theme={null} {...etc}>
<style>
{`
:root {
${cssStringFromTheme(lightTheme)}
}
html[data-dark] {
${cssStringFromTheme(darkTheme, {
extends: lightTheme,
})}
}
`}
</style>
<style
dangerouslySetInnerHTML={{
__html: `
:root {
${cssStringFromTheme(lightTheme)}
}
html[data-dark] {
${cssStringFromTheme(darkTheme, {
extends: lightTheme,
})}
}
`,
}}
/>

{/* ... */}
</RainbowKitProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ const idDataAttribute = 'data-rk-id';
const createThemeRootProps = (id: string | undefined) =>
id ? { [idDataAttribute]: id } : { [anonymousDataAttribute]: '' };

const createThemeRootSelector = (id: string | undefined) =>
id ? `[${idDataAttribute}="${id}"]` : `[${anonymousDataAttribute}]`;
const createThemeRootSelector = (id: string | undefined) => {
if (id && !/^[a-zA-Z0-9_]+$/.test(id)) {
throw new Error(`Invalid ID: ${id}`);
}

return id ? `[${idDataAttribute}="${id}"]` : `[${anonymousDataAttribute}]`;
};

export const useThemeRootProps = () => {
const id = useContext(ThemeIdContext);
Expand Down Expand Up @@ -82,20 +87,27 @@ export function RainbowKitProvider({
<ThemeIdContext.Provider value={id}>
{theme ? (
<div {...createThemeRootProps(id)}>
<style>
{[
`${selector}{${cssStringFromTheme(
'lightMode' in theme ? theme.lightMode : theme
)}}`,
<style
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
// Selectors are sanitized to only contain alphanumeric
// and underscore characters. Theme values generated by
// cssStringFromTheme are sanitized, removing
// characters that terminate values / HTML tags.
__html: [
`${selector}{${cssStringFromTheme(
'lightMode' in theme ? theme.lightMode : theme
)}}`,

'darkMode' in theme
? `@media(prefers-color-scheme:dark){${selector}{${cssStringFromTheme(
theme.darkMode,
{ extends: theme.lightMode }
)}}}`
: null,
].join('')}
</style>
'darkMode' in theme
? `@media(prefers-color-scheme:dark){${selector}{${cssStringFromTheme(
theme.darkMode,
{ extends: theme.lightMode }
)}}}`
: null,
].join(''),
}}
/>
{children}
</div>
) : (
Expand Down
2 changes: 1 addition & 1 deletion packages/rainbowkit/src/css/cssObjectFromTheme.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('cssObjectFromTheme', () => {
"--rk-colors-profileForeground": "rgba(60, 66, 66, 0.06)",
"--rk-colors-selectedOptionBorder": "rgba(60, 66, 66, 0.1)",
"--rk-colors-standby": "#FFD641",
"--rk-fonts-body": "SFRounded, ui-rounded, SF Pro Rounded, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol",
"--rk-fonts-body": "SFRounded, ui-rounded, \\"SF Pro Rounded\\", -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Helvetica, Arial, sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\"",
"--rk-radii-actionButton": "9999px",
"--rk-radii-connectButton": "12px",
"--rk-radii-menuButton": "12px",
Expand Down
18 changes: 17 additions & 1 deletion packages/rainbowkit/src/css/cssStringFromTheme.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,23 @@ import { cssStringFromTheme } from './cssStringFromTheme';
describe('cssStringFromTheme', () => {
it('converts themes to CSS strings', () => {
expect(cssStringFromTheme(lightTheme())).toMatchInlineSnapshot(
'"--rk-fonts-body:SFRounded, ui-rounded, SF Pro Rounded, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--rk-radii-actionButton:9999px;--rk-radii-connectButton:12px;--rk-radii-menuButton:12px;--rk-radii-modal:24px;--rk-radii-modalMobile:28px;--rk-colors-accentColor:#0E76FD;--rk-colors-accentColorForeground:#FFF;--rk-colors-actionButtonBorder:rgba(0, 0, 0, 0.04);--rk-colors-actionButtonBorderMobile:rgba(0, 0, 0, 0.06);--rk-colors-actionButtonSecondaryBackground:rgba(0, 0, 0, 0.06);--rk-colors-closeButton:rgba(60, 66, 66, 0.8);--rk-colors-closeButtonBackground:rgba(0, 0, 0, 0.06);--rk-colors-connectButtonBackground:#FFF;--rk-colors-connectButtonBackgroundError:#FF494A;--rk-colors-connectButtonInnerBackground:linear-gradient(0deg, rgba(0, 0, 0, 0.03), rgba(0, 0, 0, 0.06));--rk-colors-connectButtonText:#25292E;--rk-colors-connectButtonTextError:#FFF;--rk-colors-connectionIndicator:#30E000;--rk-colors-error:#FF494A;--rk-colors-generalBorder:rgba(0, 0, 0, 0.06);--rk-colors-generalBorderDim:rgba(0, 0, 0, 0.03);--rk-colors-menuItemBackground:rgba(60, 66, 66, 0.1);--rk-colors-modalBackdrop:rgba(0, 0, 0, 0.3);--rk-colors-modalBackground:#FFF;--rk-colors-modalBorder:transparent;--rk-colors-modalText:#25292E;--rk-colors-modalTextDim:rgba(60, 66, 66, 0.3);--rk-colors-modalTextSecondary:rgba(60, 66, 66, 0.6);--rk-colors-profileAction:#FFF;--rk-colors-profileActionHover:rgba(255, 255, 255, 0.5);--rk-colors-profileForeground:rgba(60, 66, 66, 0.06);--rk-colors-selectedOptionBorder:rgba(60, 66, 66, 0.1);--rk-colors-standby:#FFD641;--rk-shadows-connectButton:0px 4px 12px rgba(0, 0, 0, 0.1);--rk-shadows-dialog:0px 8px 32px rgba(0, 0, 0, 0.32);--rk-shadows-profileDetailsAction:0px 2px 6px rgba(37, 41, 46, 0.04);--rk-shadows-selectedOption:0px 2px 6px rgba(0, 0, 0, 0.24);--rk-shadows-selectedWallet:0px 2px 6px rgba(0, 0, 0, 0.12);--rk-shadows-walletLogo:0px 2px 16px rgba(0, 0, 0, 0.16);"'
'"--rk-fonts-body:SFRounded, ui-rounded, \\"SF Pro Rounded\\", -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", Roboto, Helvetica, Arial, sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\";--rk-radii-actionButton:9999px;--rk-radii-connectButton:12px;--rk-radii-menuButton:12px;--rk-radii-modal:24px;--rk-radii-modalMobile:28px;--rk-colors-accentColor:#0E76FD;--rk-colors-accentColorForeground:#FFF;--rk-colors-actionButtonBorder:rgba(0, 0, 0, 0.04);--rk-colors-actionButtonBorderMobile:rgba(0, 0, 0, 0.06);--rk-colors-actionButtonSecondaryBackground:rgba(0, 0, 0, 0.06);--rk-colors-closeButton:rgba(60, 66, 66, 0.8);--rk-colors-closeButtonBackground:rgba(0, 0, 0, 0.06);--rk-colors-connectButtonBackground:#FFF;--rk-colors-connectButtonBackgroundError:#FF494A;--rk-colors-connectButtonInnerBackground:linear-gradient(0deg, rgba(0, 0, 0, 0.03), rgba(0, 0, 0, 0.06));--rk-colors-connectButtonText:#25292E;--rk-colors-connectButtonTextError:#FFF;--rk-colors-connectionIndicator:#30E000;--rk-colors-error:#FF494A;--rk-colors-generalBorder:rgba(0, 0, 0, 0.06);--rk-colors-generalBorderDim:rgba(0, 0, 0, 0.03);--rk-colors-menuItemBackground:rgba(60, 66, 66, 0.1);--rk-colors-modalBackdrop:rgba(0, 0, 0, 0.3);--rk-colors-modalBackground:#FFF;--rk-colors-modalBorder:transparent;--rk-colors-modalText:#25292E;--rk-colors-modalTextDim:rgba(60, 66, 66, 0.3);--rk-colors-modalTextSecondary:rgba(60, 66, 66, 0.6);--rk-colors-profileAction:#FFF;--rk-colors-profileActionHover:rgba(255, 255, 255, 0.5);--rk-colors-profileForeground:rgba(60, 66, 66, 0.06);--rk-colors-selectedOptionBorder:rgba(60, 66, 66, 0.1);--rk-colors-standby:#FFD641;--rk-shadows-connectButton:0px 4px 12px rgba(0, 0, 0, 0.1);--rk-shadows-dialog:0px 8px 32px rgba(0, 0, 0, 0.32);--rk-shadows-profileDetailsAction:0px 2px 6px rgba(37, 41, 46, 0.04);--rk-shadows-selectedOption:0px 2px 6px rgba(0, 0, 0, 0.24);--rk-shadows-selectedWallet:0px 2px 6px rgba(0, 0, 0, 0.12);--rk-shadows-walletLogo:0px 2px 16px rgba(0, 0, 0, 0.16);"'
);
});

it('removes injection characters from values', () => {
const themeValues = lightTheme();

expect(
cssStringFromTheme({
...themeValues,
fonts: {
...themeValues.fonts,
body: 'comic sans ms; color: red;} *:after { content: "content" }</style><h1>html</h1>',
},
})
).toMatchInlineSnapshot(
'"--rk-fonts-body:comic sans ms color red *after content \\"content\\" styleh1htmlh1;--rk-radii-actionButton:9999px;--rk-radii-connectButton:12px;--rk-radii-menuButton:12px;--rk-radii-modal:24px;--rk-radii-modalMobile:28px;--rk-colors-accentColor:#0E76FD;--rk-colors-accentColorForeground:#FFF;--rk-colors-actionButtonBorder:rgba(0, 0, 0, 0.04);--rk-colors-actionButtonBorderMobile:rgba(0, 0, 0, 0.06);--rk-colors-actionButtonSecondaryBackground:rgba(0, 0, 0, 0.06);--rk-colors-closeButton:rgba(60, 66, 66, 0.8);--rk-colors-closeButtonBackground:rgba(0, 0, 0, 0.06);--rk-colors-connectButtonBackground:#FFF;--rk-colors-connectButtonBackgroundError:#FF494A;--rk-colors-connectButtonInnerBackground:linear-gradient(0deg, rgba(0, 0, 0, 0.03), rgba(0, 0, 0, 0.06));--rk-colors-connectButtonText:#25292E;--rk-colors-connectButtonTextError:#FFF;--rk-colors-connectionIndicator:#30E000;--rk-colors-error:#FF494A;--rk-colors-generalBorder:rgba(0, 0, 0, 0.06);--rk-colors-generalBorderDim:rgba(0, 0, 0, 0.03);--rk-colors-menuItemBackground:rgba(60, 66, 66, 0.1);--rk-colors-modalBackdrop:rgba(0, 0, 0, 0.3);--rk-colors-modalBackground:#FFF;--rk-colors-modalBorder:transparent;--rk-colors-modalText:#25292E;--rk-colors-modalTextDim:rgba(60, 66, 66, 0.3);--rk-colors-modalTextSecondary:rgba(60, 66, 66, 0.6);--rk-colors-profileAction:#FFF;--rk-colors-profileActionHover:rgba(255, 255, 255, 0.5);--rk-colors-profileForeground:rgba(60, 66, 66, 0.06);--rk-colors-selectedOptionBorder:rgba(60, 66, 66, 0.1);--rk-colors-standby:#FFD641;--rk-shadows-connectButton:0px 4px 12px rgba(0, 0, 0, 0.1);--rk-shadows-dialog:0px 8px 32px rgba(0, 0, 0, 0.32);--rk-shadows-profileDetailsAction:0px 2px 6px rgba(37, 41, 46, 0.04);--rk-shadows-selectedOption:0px 2px 6px rgba(0, 0, 0, 0.24);--rk-shadows-selectedWallet:0px 2px 6px rgba(0, 0, 0, 0.12);--rk-shadows-walletLogo:0px 2px 16px rgba(0, 0, 0, 0.16);"'
);
});
});
2 changes: 1 addition & 1 deletion packages/rainbowkit/src/css/cssStringFromTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ export function cssStringFromTheme(
options: { extends?: ThemeVars | (() => ThemeVars) } = {}
) {
return Object.entries(cssObjectFromTheme(theme, options))
.map(([key, value]) => `${key}:${value};`)
.map(([key, value]) => `${key}:${value.replace(/[:;{}</>]/g, '')};`)
.join('');
}
4 changes: 2 additions & 2 deletions packages/rainbowkit/src/themes/baseTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { ThemeVars } from '../css/sprinkles.css';
// Source: https://css-tricks.com/snippets/css/system-font-stack
// Note that quotes have been removed to avoid escaping and server/client mismatch issues
const systemFontStack =
'-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol';
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
const fontStacks = {
rounded: `SFRounded, ui-rounded, SF Pro Rounded, ${systemFontStack}`,
rounded: `SFRounded, ui-rounded, "SF Pro Rounded", ${systemFontStack}`,
system: systemFontStack,
} as const;
type FontStack = keyof typeof fontStacks;
Expand Down
28 changes: 15 additions & 13 deletions site/data/docs/custom-theme.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -139,19 +139,21 @@ import {
const App = () => {
return (
<RainbowKitProvider theme={null} {...etc}>
<style>
{`
:root {
${cssStringFromTheme(lightTheme)}
}
html[data-dark] {
${cssStringFromTheme(darkTheme, {
extends: lightTheme,
})}
}
`}
</style>
<style
dangerouslySetInnerHTML={{
__html: `
:root {
${cssStringFromTheme(lightTheme)}
}
html[data-dark] {
${cssStringFromTheme(darkTheme, {
extends: lightTheme,
})}
}
`,
}}
/>

{/* ... */}
</RainbowKitProvider>
Expand Down

2 comments on commit 5213855

@vercel
Copy link

@vercel vercel bot commented on 5213855 May 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 5213855 May 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.