Skip to content
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

fix: theme hydration issues in various browsers #153

Merged
merged 2 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions components/react/sideNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ interface SideNavProps {
}

export default function SideNav({ isDrawerVisible, setDrawerVisible }: SideNavProps) {
const [isdark, setIsdark] = useState(false);
const [isContactsOpen, setContactsOpen] = useState(false);

const { toggleTheme, theme } = useTheme();
const [isContactsOpen, setContactsOpen] = useState(false);

const toggleDrawer = () => setDrawerVisible(!isDrawerVisible);
const version = packageInfo.version;
Expand Down Expand Up @@ -82,15 +80,21 @@ export default function SideNav({ isDrawerVisible, setDrawerVisible }: SideNavPr
const modal = document.getElementById('endpoint_selector_modal') as HTMLDialogElement;
if (modal) modal.showModal();
}}
className="flex justify-center w-full text-[#00000066] dark:text-[#FFFFFF66] hover:text-primary dark:hover:text-primary transition-all duration-300 ease-in-out"
className="relative group flex justify-center w-full text-[#00000066] dark:text-[#FFFFFF66] hover:text-primary dark:hover:text-primary transition-all duration-300 ease-in-out"
>
<MdOutlineNetworkPing className="w-8 h-8" />
<span className="tooltip fixed z-[9999] left-[6.8rem] px-3 py-2 bg-primary text-white text-sm font-medium rounded-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 ease-in-out whitespace-nowrap">
Endpoints
</span>
</button>
<button
onClick={() => setContactsOpen(true)}
className="flex justify-center w-full text-[#00000066] dark:text-[#FFFFFF66] hover:text-primary dark:hover:text-primary transition-all duration-300 ease-in-out"
className="relative group flex justify-center w-full text-[#00000066] dark:text-[#FFFFFF66] hover:text-primary dark:hover:text-primary transition-all duration-300 ease-in-out"
>
<MdContacts className="w-8 h-8" />
<span className="tooltip fixed z-[9999] left-[6.8rem] px-3 py-2 bg-primary text-white text-sm font-medium rounded-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 ease-in-out whitespace-nowrap">
Contacts
</span>
</button>
<div className="flex justify-center w-full text-[#00000066] dark:text-[#FFFFFF66]">
<IconWallet chainName="manifest" />
Expand All @@ -99,15 +103,11 @@ export default function SideNav({ isDrawerVisible, setDrawerVisible }: SideNavPr
<input
type="checkbox"
className="theme-controller hidden"
value="light"
checked={isdark}
onChange={() => {
setIsdark(!isdark);
toggleTheme();
}}
checked={theme === 'dark'}
onChange={toggleTheme}
/>
<DarkIcon className="swap-on fill-current w-9 h-9 duration-300" />
<LightIcon className="swap-off fill-current w-9 h-9 duration-300" />
<LightIcon className="swap-on fill-current w-9 h-9 duration-300" />
<DarkIcon className="swap-off fill-current w-9 h-9 duration-300" />
</label>
</div>
</div>
Expand All @@ -128,7 +128,7 @@ export default function SideNav({ isDrawerVisible, setDrawerVisible }: SideNavPr
className={`flex items-center p-2 text-base font-normal rounded-lg transition duration-300 ease-in-out ${
isActive
? 'bg-primary text-white'
: 'text-[#00000066] dark:text-[#FFFFFF66] hover:bg-base-300 hover:text-primary dark:hover:text-primary dark:hover:bg-base-300'
: 'text-[#00000066] dark:text-[#FFFFFF66] hover:bg-[#0000000A] hover:text-primary dark:hover:text-primary dark:hover:bg-base-300'
}`}
>
<Icon className="w-8 h-8 mr-6" />
Expand Down Expand Up @@ -160,15 +160,15 @@ export default function SideNav({ isDrawerVisible, setDrawerVisible }: SideNavPr
const modal = document.getElementById('endpoint_selector_modal') as HTMLDialogElement;
if (modal) modal.showModal();
}}
className="flex items-center p-2 text-base font-normal rounded-lg text-[#00000066] dark:text-[#FFFFFF66] hover:bg-base-300 hover:text-primary dark:hover:text-primary dark:hover:bg-base-300 transition duration-300 ease-in-out"
className="flex items-center p-2 text-base font-normal rounded-lg text-[#00000066] dark:text-[#FFFFFF66] hover:bg-[#0000000A] hover:text-primary dark:hover:text-primary dark:hover:bg-base-300 transition duration-300 ease-in-out"
>
<MdOutlineNetworkPing className="w-8 h-8 mr-6" />
<span className="text-xl">Endpoints</span>
</button>

<button
onClick={() => setContactsOpen(true)}
className="flex items-center p-2 text-base font-normal rounded-lg text-[#00000066] dark:text-[#FFFFFF66] hover:bg-base-300 hover:text-primary dark:hover:text-primary dark:hover:bg-base-300 transition duration-300 ease-in-out"
className="flex items-center p-2 text-base font-normal rounded-lg text-[#00000066] dark:text-[#FFFFFF66] hover:bg-[#0000000A] hover:text-primary dark:hover:text-primary dark:hover:bg-base-300 transition duration-300 ease-in-out"
>
<MdContacts className="w-8 h-8 mr-6" />
<span className="text-xl">Contacts</span>
Expand All @@ -182,24 +182,24 @@ export default function SideNav({ isDrawerVisible, setDrawerVisible }: SideNavPr
<input
type="checkbox"
className="sr-only peer"
checked={isdark}
onChange={() => {
setIsdark(!isdark);
toggleTheme();
}}
checked={theme === 'dark'}
onChange={toggleTheme}
/>

<div className="flex items-center justify-center w-1/2 h-full z-10">
<DarkIcon
className={`w-8 h-8 ${theme === 'dark' ? 'text-white' : 'text-gray-500'} transition-colors duration-300`}
<LightIcon
className={`w-8 h-8 ${theme === 'light' ? 'text-white' : 'text-gray-500'} transition-colors duration-300`}
/>
</div>
<div className="flex items-center justify-center w-1/2 h-full z-10">
<LightIcon
className={`w-8 h-8 ${theme === 'light' ? 'text-white' : 'text-gray-500'} transition-colors duration-300`}
<DarkIcon
className={`w-8 h-8 ${theme === 'dark' ? 'text-white' : 'text-gray-500'} transition-colors duration-300`}
/>
</div>
<span className="absolute left-1 w-[calc(50%-0.5rem)] h-12 bg-primary rounded-xl transition-all duration-300 ease-in-out transform peer-checked:translate-x-[calc(100%+0.5rem)]" />
<span
className={`absolute left-1 w-[calc(50%-0.5rem)] h-12 bg-primary rounded-xl transition-all duration-300 ease-in-out transform ${
theme === 'dark' ? 'translate-x-[calc(100%+0.5rem)]' : ''
}`}
/>
</label>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions components/wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

export const WalletSection: React.FC<WalletSectionProps> = ({ chainName }) => {
const { connect, openView, status, username, address } = useChain(chainName);

Check warning on line 38 in components/wallet.tsx

View check run for this annotation

Codecov / codecov/patch

components/wallet.tsx#L38

Added line #L38 was not covered by tests
const [localStatus, setLocalStatus] = useState(status);

useEffect(() => {
Expand Down
70 changes: 32 additions & 38 deletions contexts/theme.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createContext, ReactNode, useContext, useState, useEffect, useCallback } from 'react';
import { createContext, ReactNode, useContext, useState, useEffect } from 'react';

export interface ThemeContext {
theme: 'light' | 'dark';
Expand All @@ -10,51 +10,45 @@
toggleTheme: () => {},
});

const getInitialTheme = (): 'light' | 'dark' => {
if (typeof window === 'undefined') return 'light';

// Move localStorage and system preference check to client-side only

Check warning on line 16 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L13-L16

Added lines #L13 - L16 were not covered by tests
return 'light'; // Default for initial render
};

export const ThemeProvider = ({ children }: { children: ReactNode }) => {
const [theme, setTheme] = useState<'dark' | 'light'>('light');
const [theme, setTheme] = useState<'light' | 'dark'>(getInitialTheme);
const [isInitialized, setIsInitialized] = useState(false);

Check warning on line 22 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L21-L22

Added lines #L21 - L22 were not covered by tests

// New effect to handle client-side initialization

Check warning on line 24 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L24

Added line #L24 was not covered by tests
useEffect(() => {
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.classList.add('light');
setTheme('light');
const stored = localStorage.getItem('theme');
if (stored === 'dark' || stored === 'light') {
setTheme(stored);

Check warning on line 28 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L26-L28

Added lines #L26 - L28 were not covered by tests
} else {
document.documentElement.classList.remove('light');
setTheme('dark');
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
setTheme(isDark ? 'dark' : 'light');

Check warning on line 31 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L30-L31

Added lines #L30 - L31 were not covered by tests
}
setIsInitialized(true);

Check warning on line 33 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L33

Added line #L33 was not covered by tests
}, []);

// Only apply theme changes after initialization

Check warning on line 36 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L36

Added line #L36 was not covered by tests
useEffect(() => {
switch (theme) {
case 'light':
document.documentElement.classList.remove('dark');
break;
case 'dark':
document.documentElement.classList.add('dark');
break;
}
}, [theme]);

const toggleTheme = useCallback(() => {
switch (theme) {
case 'light':
setTheme('dark');
break;
case 'dark':
setTheme('light');
break;
}
}, [theme]);

return (
<Theme.Provider
value={{
theme,
toggleTheme,
}}
>
{children}
</Theme.Provider>
);
if (!isInitialized) return;

const root = document.documentElement;
root.classList.remove('light', 'dark');
root.classList.add(theme);
root.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme, isInitialized]);

const toggleTheme = () => {
setTheme(current => (current === 'light' ? 'dark' : 'light'));
};

Check warning on line 50 in contexts/theme.tsx

View check run for this annotation

Codecov / codecov/patch

contexts/theme.tsx#L38-L50

Added lines #L38 - L50 were not covered by tests
return <Theme.Provider value={{ theme, toggleTheme }}>{children}</Theme.Provider>;
};

export const useTheme = (): ThemeContext => useContext(Theme);
22 changes: 21 additions & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
manifestTestnetAssets,
} from '@/config';
import { SignerOptions, wallets } from 'cosmos-kit';
import { wallets as cosmosExtensionWallets } from '@cosmos-kit/cosmos-extension-metamask';
import { ChainProvider } from '@cosmos-kit/react';
import { Registry } from '@cosmjs/proto-signing';
import { TailwindModal } from '../components';
Expand Down Expand Up @@ -47,6 +48,15 @@ type ManifestAppProps = AppProps & {
};

function ManifestApp({ Component, pageProps }: ManifestAppProps) {
const { theme } = useTheme();

useEffect(() => {
if (typeof window !== 'undefined') {
console.log({ localStorageTheme: localStorage?.getItem('theme') });
}
console.log({ theme });
}, [theme]);

const [isDrawerVisible, setDrawerVisible] = useState(() => {
// Initialize from localStorage if available, otherwise default to true
if (typeof window !== 'undefined') {
Expand Down Expand Up @@ -145,6 +155,16 @@ function ManifestApp({ Component, pageProps }: ManifestAppProps) {
name: 'Reddit',
logo: '/reddit',
},
{
provider: 'email_passwordless',
name: 'Email',
logo: '/email',
},
{
provider: 'sms_passwordless',
name: 'SMS',
logo: '/sms',
},
],

client: {
Expand All @@ -167,7 +187,7 @@ function ManifestApp({ Component, pageProps }: ManifestAppProps) {
);

// combine the web3auth wallets with the other wallets
const combinedWallets = [...web3AuthWallets, ...wallets];
const combinedWallets = [...web3AuthWallets, ...wallets, ...cosmosExtensionWallets];

// this is stop ssr errors when we render the web3auth signing modal
const [isBrowser, setIsBrowser] = useState(false);
Expand Down
18 changes: 7 additions & 11 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ module.exports = {
'100%': { opacity: 1, transform: 'translateX(0%)' },
},
},

fontFamily: {
sans: ['Manrope', 'sans-serif'],
body: ['Manrope', 'sans-serif'],
Expand All @@ -76,14 +75,6 @@ module.exports = {
},
},
daisyui: {
themes: false,
darkTheme: 'dark',
base: true,
styled: true,
utils: true,
prefix: '',
logs: true,
themeRoot: ':root',
themes: [
{
light: {
Expand All @@ -108,8 +99,6 @@ module.exports = {
error: '#E53935',
'--shadow-color-light': 'rgba(0, 0, 0, 0.1)',
},
},
{
dark: {
primary: '#A087FF',
'primary-content': '#FFFFFF',
Expand All @@ -134,6 +123,13 @@ module.exports = {
},
},
],
darkTheme: 'dark',
base: true,
styled: true,
utils: true,
prefix: '',
logs: false,
themeRoot: ':root',
},
plugins: [
require('@tailwindcss/typography'),
Expand Down
Loading