Skip to content

feat(docs): fix theme flicker #4271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 17, 2025
Merged
1 change: 1 addition & 0 deletions packages/paste-website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
"micromark-extension-mdxjs": "^2.0.0",
"minimist": "^1.2.8",
"next": "^14.0.0",
"nookies": "^2.5.2",
"openai": "^4.79.1",
"pretty-format": "^28.1.0",
"prism-react-renderer": "^1.3.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const ColorGradient: React.FC<
> = ({ aliasPrefix, makeTall = "false", index = 0 }) => {
const [show, setShow] = React.useState(false);
const { theme } = useDarkModeContext();
const aliasValues = getAliasValuesFromPrefix(aliasPrefix, theme);
const aliasValues = getAliasValuesFromPrefix(aliasPrefix, theme || "twilio");
const count = aliasValues.length - 1;

function handleVisibilityChange(isVisible: boolean): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const LivePreview: React.FC<React.PropsWithChildren<LivePreviewProps>> = ({

const overflow = showOverflow ? "visible" : "auto";

if (!previewTheme) return null;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am now delaying the Live code preview rendering until we have correctly identified the theme. I could not fix the Theme provider with CSS variables as it's nested an needs to cahgne independent of the actual app. This was the only workaround to not getting the previews to do the flicker. Won't effect SEO


return (
<Box marginBottom="space110" data-cy="live-preview">
<LiveProvider
Expand Down
2 changes: 1 addition & 1 deletion packages/paste-website/src/context/PreviewThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react";

type PreviewThemeContextValue = {
theme: string;
theme?: string;
selectTheme: (theme: string) => void;
};

Expand Down
14 changes: 7 additions & 7 deletions packages/paste-website/src/hooks/useDarkMode.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { ValueOf } from "@twilio-paste/types";
import { parseCookies, setCookie } from "nookies";
import * as React from "react";

import { SimpleStorage } from "../utils/SimpleStorage";

export type UseDarkModeReturn = [ValidThemeName, () => void, boolean];
export type UseDarkModeReturn = [ValidThemeName | undefined, () => void, boolean];

const ValidThemes = {
DEFAULT: "twilio",
Expand All @@ -13,15 +12,16 @@ const ValidThemes = {
type ValidThemeName = ValueOf<typeof ValidThemes>;

const isValidTheme = (themeName: ValidThemeName): boolean => {
return themeName === ValidThemes.DEFAULT || themeName === ValidThemes.DARK;
return Object.values(ValidThemes).includes(themeName);
};

export const useDarkMode = (): UseDarkModeReturn => {
const [theme, setTheme] = React.useState<ValidThemeName>(ValidThemes.DEFAULT);
const [theme, setTheme] = React.useState<ValidThemeName>();
const [componentMounted, setComponentMounted] = React.useState(false);

const setMode = (mode: ValidThemeName): void => {
SimpleStorage.set("theme", mode);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No need to use local storage, all done via cookies now

setCookie(null, "paste-docs-theme", mode, { path: "/" });
document.body.dataset.theme = mode;
setTheme(mode);
};

Expand All @@ -34,7 +34,7 @@ export const useDarkMode = (): UseDarkModeReturn => {
};

React.useEffect(() => {
const localTheme = SimpleStorage.get("theme") as ValidThemeName;
const localTheme = parseCookies()["paste-docs-theme"] as ValidThemeName;

if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches && !localTheme) {
setMode(ValidThemes.DARK);
Expand Down
6 changes: 4 additions & 2 deletions packages/paste-website/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { datadogRum } from "@datadog/browser-rum";
import "@twilio-paste/design-tokens/dist/themes/twilio-dark/tokens.data-theme.css";
import "@twilio-paste/design-tokens/dist/themes/twilio/tokens.custom-properties.css";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

By using the tokens.custom.properties.css instead of tokens.data-theme.css it will set this theme to be applied by default and only overridden when the document.body.data.theme = "twilio-dark"

import { Theme } from "@twilio-paste/theme";
import type { AppProps } from "next/app";
import Head from "next/head";
Expand All @@ -22,7 +24,7 @@ const App = ({ Component, pageProps }: AppProps): React.ReactElement => {
const router = useRouter();
const localStorageKey = "cookie-consent-accepted";
const [theme, toggleMode, componentMounted] = useDarkMode();
const [previewTheme, setPreviewTheme] = React.useState("twilio");
const [previewTheme, setPreviewTheme] = React.useState<string | undefined>();
const [cookiesAccepted, setCookiesAccepted] = React.useState<null | string>();

React.useEffect(() => {
Expand Down Expand Up @@ -111,7 +113,7 @@ const App = ({ Component, pageProps }: AppProps): React.ReactElement => {
</>
)}
<Theme.Provider
theme={theme}
useCSSVariables={true}
customBreakpoints={SITE_BREAKPOINTS}
disableAnimations={inCypress()}
cacheProviderProps={{ key: "next" }}
Expand Down
16 changes: 16 additions & 0 deletions packages/paste-website/src/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ class _Document extends Document {
<link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512x512.png" />
</Head>
<body>
<script
type="text/javascript"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: `
let parts = typeof document !== "undefined" && document?.cookie.split("paste-docs-theme=");
let cookie = parts.length == 2 ?parts?.pop().split(";").shift() : null;
if(cookie){
document.body.dataset.theme = cookie;
}
else if(window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches){
document.body.dataset.theme = "twilio-dark";
}
`,
}}
/>
Comment on lines +36 to +51
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried doing this using the Next Script element but I didn't have any luck, flicker was there just shorted. Doing it this way was the only thing to ge tit to work

<noscript key="noscript">This app works best with JavaScript enabled.</noscript>
<Main />
<NextScript />
Expand Down
20 changes: 19 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -16312,6 +16312,7 @@ __metadata:
micromark-extension-mdxjs: ^2.0.0
minimist: ^1.2.8
next: ^14.0.0
nookies: ^2.5.2
openai: ^4.79.1
pretty-format: ^28.1.0
prism-react-renderer: ^1.3.5
Expand Down Expand Up @@ -22379,7 +22380,7 @@ __metadata:
languageName: node
linkType: hard

"cookie@npm:~0.4.1":
"cookie@npm:^0.4.1, cookie@npm:~0.4.1":
version: 0.4.2
resolution: "cookie@npm:0.4.2"
checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b
Expand Down Expand Up @@ -35778,6 +35779,16 @@ fsevents@^1.2.7:
languageName: node
linkType: hard

"nookies@npm:^2.5.2":
version: 2.5.2
resolution: "nookies@npm:2.5.2"
dependencies:
cookie: ^0.4.1
set-cookie-parser: ^2.4.6
checksum: 4cc6fd8d0ab9fce840ccdb161eefe9dee581f441429fc729f279924491ebd1c12c220f889b6e315d66f8ea00bef944966969c966c957a6b9918ced7d7f9cc8a8
languageName: node
linkType: hard

"nopt@npm:^4.0.3":
version: 4.0.3
resolution: "nopt@npm:4.0.3"
Expand Down Expand Up @@ -41682,6 +41693,13 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard

"set-cookie-parser@npm:^2.4.6":
version: 2.7.1
resolution: "set-cookie-parser@npm:2.7.1"
checksum: 2ef8b351094712f8f7df6d63ed4b10350b24a5b515772690e7dec227df85fcef5bc451c7765f484fd9f36694ece5438d1456407d017f237d0d3351d7dbbd3587
languageName: node
linkType: hard

"set-function-length@npm:^1.1.1":
version: 1.1.1
resolution: "set-function-length@npm:1.1.1"
Expand Down
Loading