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

feat(explorer): add darkmode support to footer #4148

Merged
Show file tree
Hide file tree
Changes from 5 commits
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
21 changes: 21 additions & 0 deletions apps/explorer/src/components/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useState, type PropsWithChildren } from 'react';
import { ThemeContext } from '~/contexts';
import { Theme } from '~/lib/ui';

export function ThemeProvider({ children }: PropsWithChildren): React.JSX.Element {
const [theme, setTheme] = useState<Theme>(Theme.Light);

return (
<ThemeContext.Provider
value={{
theme,
setTheme,
}}
>
{children}
</ThemeContext.Provider>
);
}
8 changes: 8 additions & 0 deletions apps/explorer/src/components/ThemedIotaLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { IotaLogoWeb } from '@iota/ui-icons';

export function ThemedIotaLogo(): React.JSX.Element {
return <IotaLogoWeb className="text-neutral-10 dark:text-neutral-92" width={137} height={36} />;
}
22 changes: 14 additions & 8 deletions apps/explorer/src/components/footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@

import { Divider } from '@iota/apps-ui-kit';
import { LegalLinks, LegalText } from './Legal';
import { IotaLogoWeb } from '@iota/ui-icons';
import { Link } from '~/components/ui';
import { FOOTER_LINKS } from '~/lib/constants';
import { ThemedIotaLogo } from '../ThemedIotaLogo';

function FooterLinks(): JSX.Element {
return (
<div className="flex flex-col items-center justify-center gap-6 md:flex-row md:justify-end">
<ul className="flex flex-wrap gap-4 md:flex-row md:gap-6">
{FOOTER_LINKS.map(({ title, href }) => (
<li key={href}>
<Link variant="text" href={href} className="text-body-md text-neutral-40">
<Link
variant="text"
href={href}
className="dark:text-neutral-60 text-body-md text-neutral-40"

Check failure on line 20 in apps/explorer/src/components/footer/Footer.tsx

View workflow job for this annotation

GitHub Actions / turborepo / Lint, Build, and Test

Replace `dark:text-neutral-60·text-body-md·text-neutral-4` with `text-body-md·text-neutral-40·dark:text-neutral-6`
>
{title}
</Link>
</li>
Expand All @@ -26,11 +30,11 @@

function Footer(): JSX.Element {
return (
<footer className="sticky top-[100%] bg-neutral-96 px-5 py-10 md:px-10 md:py-14">
<footer className="dark:bg-neutral-10 sticky top-[100%] bg-neutral-96 px-5 py-10 md:px-10 md:py-14">

Check failure on line 33 in apps/explorer/src/components/footer/Footer.tsx

View workflow job for this annotation

GitHub Actions / turborepo / Lint, Build, and Test

Replace `dark:bg-neutral-10·sticky·top-[100%]·bg-neutral-96·px-5·py-10·md:px-10·md:py-14` with `sticky·top-[100%]·bg-neutral-96·px-5·py-10·md:px-10·md:py-14·dark:bg-neutral-10`
<nav className="container flex flex-col justify-center gap-md md:gap-lg">
<div className="flex flex-col-reverse items-center gap-7.5 md:flex-row md:justify-between ">
<div className="hidden self-center text-neutral-10 md:flex md:self-start">
<IotaLogoWeb width={137} height={36} />
<div className="hidden self-center md:flex md:self-start">
<ThemedIotaLogo />
</div>
<div>
<FooterLinks />
Expand All @@ -42,10 +46,12 @@
<LegalLinks />
</div>
</nav>
<div className="mt-4 flex justify-center pt-5 text-neutral-10 md:hidden md:self-start">
<IotaLogoWeb width={137} height={36} />
<div className="mt-4 flex justify-center pt-5 md:hidden md:self-start">
<ThemedIotaLogo />
</div>
<p className="mt-8 w-full text-center text-body-sm text-neutral-40">{EXPLORER_REV}</p>
<p className="dark:text-neutral-60 mt-8 w-full text-center text-body-sm text-neutral-40">

Check failure on line 52 in apps/explorer/src/components/footer/Footer.tsx

View workflow job for this annotation

GitHub Actions / turborepo / Lint, Build, and Test

Replace `dark:text-neutral-60·mt-8·w-full·text-center·text-body-sm·text-neutral-4` with `mt-8·w-full·text-center·text-body-sm·text-neutral-40·dark:text-neutral-6`
{EXPLORER_REV}
</p>
</footer>
);
}
Expand Down
10 changes: 7 additions & 3 deletions apps/explorer/src/components/footer/Legal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
export function LegalText(): JSX.Element {
return (
<div className="flex justify-center md:justify-start">
<span className="text-body-md text-neutral-40">
<span className="dark:text-neutral-60 text-body-md text-neutral-40">

Check failure on line 12 in apps/explorer/src/components/footer/Legal.tsx

View workflow job for this annotation

GitHub Actions / turborepo / Lint, Build, and Test

Replace `dark:text-neutral-60·text-body-md·text-neutral-4` with `text-body-md·text-neutral-40·dark:text-neutral-6`
&copy;
{`${new Date().getFullYear()} IOTA Stiftung. All rights reserved.`}
</span>
Expand All @@ -24,7 +24,11 @@
<ul className="flex flex-col gap-3 md:flex-row md:gap-8">
{LEGAL_LINKS.map(({ title, href }) => (
<li className="flex items-center justify-center" key={href}>
<Link variant="text" href={href} className="text-body-md text-neutral-40">
<Link
variant="text"
href={href}
className="dark:text-neutral-60 dark:text-neutral-60. text-body-md text-neutral-40"

Check failure on line 30 in apps/explorer/src/components/footer/Legal.tsx

View workflow job for this annotation

GitHub Actions / turborepo / Lint, Build, and Test

Replace `·dark:text-neutral-60.·text-body-md·text-neutral-4` with `.·text-body-md·text-neutral-40·dark:text-neutral-6`
>
{title}
</Link>
</li>
Expand All @@ -34,7 +38,7 @@
<Link
variant="text"
data-cc="c-settings"
className="text-body-md text-neutral-40"
className="dark:text-neutral-60 dark:text-neutral-60. text-body-md text-neutral-40"

Check failure on line 41 in apps/explorer/src/components/footer/Legal.tsx

View workflow job for this annotation

GitHub Actions / turborepo / Lint, Build, and Test

Replace `·dark:text-neutral-60.·text-body-md·text-neutral-4` with `.·text-body-md·text-neutral-40·dark:text-neutral-6`
cpl121 marked this conversation as resolved.
Show resolved Hide resolved
>
Manage Cookies
</Link>
Expand Down
4 changes: 2 additions & 2 deletions apps/explorer/src/components/gas-breakdown/GasBreakdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ function GasPaymentLinks({ objectIds }: { objectIds: string[] }): JSX.Element {
function GasInfo({ label, info }: { label: string; info?: React.ReactNode }) {
return (
<div className="flex flex-col gap-2 md:flex-row md:gap-10">
<span className="w-full flex-shrink-0 text-label-lg text-neutral-40 dark:text-neutral-60 md:w-40">
<span className="w-full flex-shrink-0 text-label-lg text-neutral-40 md:w-40 dark:text-neutral-60">
{label}
</span>
{info ? (
info
) : (
<span className="text-label-lg text-neutral-40 dark:text-neutral-60 md:w-40">
<span className="text-label-lg text-neutral-40 md:w-40 dark:text-neutral-60">
--
</span>
)}
Expand Down
12 changes: 7 additions & 5 deletions apps/explorer/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { IotaLogoWeb } from '@iota/ui-icons';

import { NetworkSelector } from '../network';
import Search from '../search/Search';
import { LinkWithQuery } from '~/components/ui';
import { ThemeSwitcher, ThemedIotaLogo } from '~/components';

function Header(): JSX.Element {
return (
<header className="flex h-header justify-center overflow-visible bg-neutral-98">
<header className="flex h-header justify-center overflow-visible backdrop-blur-lg">
<div className="container flex h-full flex-1 items-center justify-between gap-5">
<LinkWithQuery
data-testid="nav-logo-button"
to="/"
className="flex flex-nowrap items-center gap-1 text-neutral-10"
>
<IotaLogoWeb width={137} height={36} />
<ThemedIotaLogo />
</LinkWithQuery>
<div className="flex w-[360px] justify-center">
<Search />
</div>
<NetworkSelector />
<div className="flex flex-row gap-xs">
<ThemeSwitcher />
<NetworkSelector />
</div>
</div>
</header>
);
Expand Down
59 changes: 59 additions & 0 deletions apps/explorer/src/components/header/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Button, ButtonType } from '@iota/apps-ui-kit';
import { DarkMode, LightMode } from '@iota/ui-icons';
import { useEffect, useLayoutEffect } from 'react';
import { useTheme } from '~/hooks';
import { Theme } from '~/lib/ui';

const ICON_MAP: Record<Theme, (props: React.SVGProps<SVGSVGElement>) => JSX.Element> = {
[Theme.Light]: LightMode,
[Theme.Dark]: DarkMode,
};

export function ThemeSwitcher(): React.JSX.Element {
const { theme, setTheme } = useTheme();

const ThemeIcon = ICON_MAP[theme];

function handleOnClick(): void {
const newTheme = theme === Theme.Light ? Theme.Dark : Theme.Light;
setTheme(newTheme);
saveThemeToLocalStorage(newTheme);
}

function saveThemeToLocalStorage(newTheme: Theme): void {
localStorage.setItem('theme', newTheme);
}

function updateDocumentClass(theme: Theme): void {
document.documentElement.classList.toggle('dark', theme === Theme.Dark);
}

useLayoutEffect(() => {
const storedTheme = localStorage.getItem('theme') as Theme | null;
if (storedTheme) {
setTheme(storedTheme);
updateDocumentClass(storedTheme);
} else {
const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
const preferredTheme = prefersDarkTheme ? Theme.Dark : Theme.Light;

setTheme(preferredTheme);
updateDocumentClass(preferredTheme);
}
}, []);

useEffect(() => {
updateDocumentClass(theme);
}, [theme]);

return (
<Button
type={ButtonType.Ghost}
onClick={handleOnClick}
icon={<ThemeIcon className="h-5 w-5" />}
/>
);
}
1 change: 1 addition & 0 deletions apps/explorer/src/components/header/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// SPDX-License-Identifier: Apache-2.0

export * from './Header';
export * from './ThemeSwitcher';
2 changes: 2 additions & 0 deletions apps/explorer/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ export * from './AreaGraph';
export * from './GraphTooltipContent';
export * from './IotaTokenCard';
export * from './TransactionsCardGraph';
export * from './ThemedIotaLogo';
export * from './ThemeProvider';
9 changes: 6 additions & 3 deletions apps/explorer/src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { NetworkContext } from '~/contexts';
import { useInitialPageView, useNetwork } from '~/hooks';
import { createIotaClient, persistableStorage, SupportedNetworks } from '~/lib/utils';
import { Toaster } from '../toaster';
import { ThemeProvider } from '../ThemeProvider';

export function Layout(): JSX.Element {
const [network, setNetwork] = useNetwork();
Expand Down Expand Up @@ -39,9 +40,11 @@ export function Layout(): JSX.Element {
<WalletProvider autoConnect enableUnsafeBurner={import.meta.env.DEV}>
<KioskClientProvider>
<NetworkContext.Provider value={[network, setNetwork]}>
<Outlet />
<Toaster />
<ReactQueryDevtools />
<ThemeProvider>
<Outlet />
<Toaster />
<ReactQueryDevtools />
</ThemeProvider>
</NetworkContext.Provider>
</KioskClientProvider>
</WalletProvider>
Expand Down
2 changes: 1 addition & 1 deletion apps/explorer/src/components/layout/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function PageLayout({ content, loading }: PageLayoutProps): JSX.Element {
: "The explorer is running slower than usual. We're working to fix the issue and appreciate your patience.";

return (
<div className="relative min-h-screen w-full bg-neutral-98">
<div className="relative min-h-screen w-full">
<section ref={headerRef} className="fixed top-0 z-20 flex w-full flex-col">
{renderNetworkDegradeBanner && (
<InfoBox
Expand Down
2 changes: 1 addition & 1 deletion apps/explorer/src/components/module/PkgModulesWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function PkgModulesWrapper({
isLoading={false}
suggestions={searchSuggestions}
renderSuggestion={(suggestion) => (
<div className="z-10 flex cursor-pointer justify-between bg-neutral-98">
<div className="z-10 flex cursor-pointer justify-between">
<ListItem
hideBottomBorder
onClick={() => onChangeModule(suggestion.label)}
Expand Down
2 changes: 1 addition & 1 deletion apps/explorer/src/components/object/ObjectFieldsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function ObjectFieldsCard({
}}
isLoading={false}
renderSuggestion={(suggestion) => (
<div className="flex cursor-pointer justify-between bg-neutral-98">
<div className="flex cursor-pointer justify-between">
<ListItem hideBottomBorder>
<div className="overflow-hidden text-ellipsis">
{suggestion.label}
Expand Down
2 changes: 1 addition & 1 deletion apps/explorer/src/components/search/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function Search(): JSX.Element {
isLoading={isPending || debouncedQuery !== query}
suggestions={results}
renderSuggestion={(suggestion) => (
<div className="flex cursor-pointer justify-between bg-neutral-98">
<div className="flex cursor-pointer justify-between">
<ListItem hideBottomBorder>
<div className="overflow-hidden text-ellipsis">{suggestion.label}</div>
<div className="break-words pl-xs text-caption font-medium uppercase text-steel">
Expand Down
1 change: 1 addition & 0 deletions apps/explorer/src/contexts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// SPDX-License-Identifier: Apache-2.0

export * from './networkContext';
export * from './themeContext';
15 changes: 15 additions & 0 deletions apps/explorer/src/contexts/themeContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { createContext } from 'react';
import { Theme } from '~/lib/ui/enums';

interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
}

export const ThemeContext = createContext<ThemeContextType>({
theme: Theme.Light,
setTheme: () => {},
});
1 change: 1 addition & 0 deletions apps/explorer/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ export * from './useRecognizedPackages';
export * from './useResolveVideo';
export * from './useSearch';
export * from './useVerifiedSourceCode';
export * from './useTheme';
13 changes: 13 additions & 0 deletions apps/explorer/src/hooks/useTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useContext } from 'react';
import { ThemeContext } from '~/contexts';

export function useTheme() {
if (!ThemeContext) {
throw new Error('useTheme must be used within a ThemeProvider');
}

return useContext(ThemeContext);
}
8 changes: 8 additions & 0 deletions apps/explorer/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ body,
--steel-dark: theme('colors.steel.dark');
}

:root {
@apply bg-neutral-98;
}

:root.dark {
@apply bg-neutral-4;
}

@layer base {
body {
@apply antialiased;
Expand Down
4 changes: 4 additions & 0 deletions apps/explorer/src/lib/ui/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './theme.enums';
7 changes: 7 additions & 0 deletions apps/explorer/src/lib/ui/enums/theme.enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export enum Theme {
Light = 'light',
Dark = 'dark',
}
1 change: 1 addition & 0 deletions apps/explorer/src/lib/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// SPDX-License-Identifier: Apache-2.0

export * from './utils';
export * from './enums';
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function BalanceChangeEntry({ change }: { change: BalanceChange }): JSX.Element
</Card>
{recipient && (
<div className="flex flex-wrap items-center justify-between px-sm py-xs">
<span className="w-full flex-shrink-0 text-label-lg text-neutral-40 dark:text-neutral-60 md:w-40">
<span className="w-full flex-shrink-0 text-label-lg text-neutral-40 md:w-40 dark:text-neutral-60">
Recipient
</span>
<AddressLink address={recipient} />
Expand Down
1 change: 1 addition & 0 deletions apps/explorer/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import uiKitResponsivePreset from '../../apps/ui-kit/src/lib/tailwind/responsive
export default {
presets: [preset, uiKitResponsivePreset],
content: ['./src/**/*.{js,jsx,ts,tsx}', './../ui-kit/src/lib/**/*.{js,jsx,ts,tsx}'],
darkMode: 'selector',
theme: {
// This COLOR are duplicated from @iota/core tailwind.config.ts!!!
// They are repeated here cause uiKitResponsivePreset overwrites the colors, and they are still used throughout Explorer
Expand Down
Loading