Skip to content

Commit

Permalink
[dapp-kit] set up basic theming definitions (MystenLabs#14056)
Browse files Browse the repository at this point in the history
## Description 

This PR sets up the basic theming definitions for dapp-kit. I learned
that theming is actually pretty hard to get right, and I looked
thoroughly at Radix UI themes, PandaCSS, Tamagui, Shadcn, Rainbow Kit,
and ConnectKit for inspiration 😅.

I settled on the premise that a theme should just be a collection of
mostly specific CSS variable definitions (e.g., the background color for
the modal) with some generic definitions (e.g., there's a scale of small
- xlarge border radii). For someone to theme the UI in the short term,
they'll need to manually pass through a `Theme` object which is this big
collection of variables (we can provide helpers for overriding existing
themes to make this not a PITA). In the long term, I think there are
opportunities to build on this by using a color scale/system where you
might just specify a scale of accent colors and gray colors and we map
those to our internal variable definitions. We might also provide preset
themes or a theme builder UI as well 🎨

As far as functionality goes to support theming, there are three primary
ways folks use themed components:
 - They want just one static theme used for the UI 
 - They want light/dark theme toggled based on system preference
 - They want light/dark/pink/blue/etc. theme toggled manually in the UI

Note 1: There might be some polishing in regards to theme variables,
I'll play around more with some test themes later
Note 2: I kind of winged and created the design system myself since
we're short on resources, I didn't like the Sui styling 🙃

<img width="239" alt="image"
src="https://github.com/MystenLabs/sui/assets/7453188/98003385-7eef-4fd5-8077-5310324c4290">
<img width="830" alt="image"
src="https://github.com/MystenLabs/sui/assets/7453188/e158862f-e9ba-46f4-ba24-c1db46491647">
<img width="253" alt="image"
src="https://github.com/MystenLabs/sui/assets/7453188/f369c3a0-cd78-403c-8318-81f5211080ae">

## Test Plan 
- Manual testing

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
williamrobertson13 authored Oct 3, 2023
1 parent 8e9590a commit b29f66f
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/forty-actors-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@mysten/dapp-kit': minor
---

Add theme definitions for our UI components
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sdk/dapp-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.5",
"@radix-ui/react-slot": "^1.0.2",
"@vanilla-extract/css": "^1.13.0",
"@vanilla-extract/dynamic": "^2.0.3",
"clsx": "^2.0.0",
"zustand": "^4.4.1"
},
Expand Down
9 changes: 9 additions & 0 deletions sdk/dapp-kit/src/components/WalletProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import { useAutoConnectWallet } from '../hooks/wallet/useAutoConnectWallet.js';
import { useUnsafeBurnerWallet } from '../hooks/wallet/useUnsafeBurnerWallet.js';
import { useWalletPropertiesChanged } from '../hooks/wallet/useWalletPropertiesChanged.js';
import { useWalletsChanged } from '../hooks/wallet/useWalletsChanged.js';
import { lightTheme } from '../themes/lightTheme.js';
import type { Theme } from '../themes/themeContract.js';
import { getRegisteredWallets } from '../utils/walletUtils.js';
import { createWalletStore } from '../walletStore.js';
import { InjectedThemeStyles } from './styling/InjectedThemeStyles.js';

type WalletProviderProps = {
/** A list of wallets that are sorted to the top of the wallet list, if they are available to connect to. By default, wallets are sorted by the order they are loaded in. */
Expand All @@ -33,6 +36,9 @@ type WalletProviderProps = {
/** The key to use to store the most recently connected wallet account. */
storageKey?: string;

/** The theme to use for styling UI components. Defaults to using the light theme. */
theme?: Theme | null;

children: ReactNode;
};

Expand All @@ -51,6 +57,7 @@ export function WalletProvider({
storageKey = DEFUALT_STORAGE_KEY,
enableUnsafeBurner = false,
autoConnect = false,
theme = lightTheme,
children,
}: WalletProviderProps) {
const storeRef = useRef(
Expand All @@ -69,6 +76,8 @@ export function WalletProvider({
enableUnsafeBurner={enableUnsafeBurner}
autoConnect={autoConnect}
>
{/* TODO: We ideally don't want to inject styles if people aren't using the UI components */}
{theme ? <InjectedThemeStyles theme={theme} /> : null}
{children}
</WalletConnectionManager>
</WalletContext.Provider>
Expand Down
49 changes: 49 additions & 0 deletions sdk/dapp-kit/src/components/styling/InjectedThemeStyles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { assignInlineVars } from '@vanilla-extract/dynamic';

import { styleDataAttributeSelector } from '../../constants/styleDataAttribute.js';
import { themeVars } from '../../themes/themeContract.js';
import type { DynamicTheme, Theme, ThemeVars } from '../../themes/themeContract.js';

type InjectedThemeStylesProps = {
theme: Theme;
};

export function InjectedThemeStyles({ theme }: InjectedThemeStylesProps) {
const themeStyles = Array.isArray(theme)
? getDynamicThemeStyles(theme)
: getStaticThemeStyles(theme);

return (
<style
dangerouslySetInnerHTML={{
__html: themeStyles,
}}
/>
);
}

function getDynamicThemeStyles(themes: DynamicTheme[]) {
return themes
.map(({ mediaQuery, selector, variables }) => {
const themeStyles = getStaticThemeStyles(variables);
const themeStylesWithSelectorPrefix = selector ? `${selector} ${themeStyles}` : themeStyles;

return mediaQuery
? `@media ${mediaQuery}{${themeStylesWithSelectorPrefix}}`
: themeStylesWithSelectorPrefix;
})
.join(' ');
}

function getStaticThemeStyles(theme: ThemeVars) {
return `${styleDataAttributeSelector} {${cssStringFromTheme(theme)}}`;
}

function cssStringFromTheme(theme: ThemeVars) {
return Object.entries(assignInlineVars(themeVars, theme))
.map(([key, value]) => `${key}:${value};`)
.join('');
}
1 change: 1 addition & 0 deletions sdk/dapp-kit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export * from './hooks/wallet/useSignAndExecuteTransactionBlock.js';
export * from './hooks/useSuiClientMutation.js';
export * from './hooks/useSuiClientQuery.js';
export * from './hooks/useSuiClientInfiniteQuery.js';
export * from './themes/lightTheme.js';
export * from './types.js';
58 changes: 58 additions & 0 deletions sdk/dapp-kit/src/themes/lightTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import type { ThemeVars } from './themeContract.js';

export const lightTheme: ThemeVars = {
blurs: {
modalOverlay: 'blur(0)',
},
backgroundColors: {
primaryButton: '#F6F7F9',
primaryButtonHover: '#F0F2F5',
outlineButtonHover: '#F4F4F5',
modalOverlay: 'rgba(24 36 53 / 20%)',
modalPrimary: 'white',
modalSecondary: '#F7F8F8',
iconButton: 'transparent',
iconButtonHover: '#F0F1F2',
dropdownMenu: '#FFFFFF',
dropdownMenuSeparator: '#F3F6F8',
walletItemSelected: 'white',
walletItemHover: '#3C424226',
},
borderColors: {
outlineButton: '#E4E4E7',
},
colors: {
primaryButton: '#373737',
outlineButton: '#373737',
iconButton: '#000000',
body: '#182435',
bodyMuted: '#767A81',
bodyDanger: '#FF794B',
},
radii: {
small: '6px',
medium: '8px',
large: '12px',
xlarge: '16px',
},
shadows: {
primaryButton: '0px 4px 12px rgba(0, 0, 0, 0.1)',
walletItemSelected: '0px 2px 6px rgba(0, 0, 0, 0.05)',
},
fontWeights: {
normal: '400',
medium: '500',
bold: '600',
},
typography: {
fontFamily:
'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',
fontSize: '14px',
fontStyle: 'normal',
lineHeight: '1.3',
letterSpacing: '1',
},
};
92 changes: 92 additions & 0 deletions sdk/dapp-kit/src/themes/themeContract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { createGlobalThemeContract } from '@vanilla-extract/css';

const themeContractValues = {
blurs: {
modalOverlay: '',
},
backgroundColors: {
primaryButton: '',
primaryButtonHover: '',
outlineButtonHover: '',
walletItemHover: '',
walletItemSelected: '',
modalOverlay: '',
modalPrimary: '',
modalSecondary: '',
iconButton: '',
iconButtonHover: '',
dropdownMenu: '',
dropdownMenuSeparator: '',
},
borderColors: {
outlineButton: '',
},
colors: {
primaryButton: '',
outlineButton: '',
body: '',
bodyMuted: '',
bodyDanger: '',
iconButton: '',
},
radii: {
small: '',
medium: '',
large: '',
xlarge: '',
},
shadows: {
primaryButton: '',
walletItemSelected: '',
},
fontWeights: {
normal: '',
medium: '',
bold: '',
},
typography: {
fontFamily: '',
fontSize: '',
fontStyle: '',
lineHeight: '',
letterSpacing: '',
},
};

export type ThemeVars = typeof themeContractValues;

/**
* A custom theme that is enabled when various conditions are
*/
export type DynamicTheme = {
/**
* An optional media query required for the given theme to be enabled. This is useful
* when you want the theme of your application to automatically switch depending on
* a media feature.
*
* @example '(prefers-color-scheme: dark)'
*/
mediaQuery?: string;

/**
* An optional CSS selector required for the given theme to be enabled. This is useful
* when you have a manual theme switcher on your application that sets a top-level
* class name or data-attribute to control the current theme.
*
* @example '.data-dark'
*/
selector?: string;

/** The theme definitions that will be set when the selector and mediaQuery criteria are matched. */
variables: ThemeVars;
};

export type Theme = ThemeVars | DynamicTheme[];

export const themeVars = createGlobalThemeContract(
themeContractValues,
(_, path) => `dapp-kit-${path.join('-')}`,
);

0 comments on commit b29f66f

Please sign in to comment.