From b5bcbb92bf8ce5e956bbc077e9a6f64805d6e75f Mon Sep 17 00:00:00 2001
From: WBH <48749219+wbh1328551759@users.noreply.github.com>
Date: Wed, 23 Jul 2025 19:35:54 +0800
Subject: [PATCH 01/45] update README and fix GitHub Actions
---
.github/workflows/pr-check.yml | 2 +-
README.md | 19 +++++++++++++++----
examples/with-vite/src/App.tsx | 2 +-
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml
index c5b987c..7d09b33 100644
--- a/.github/workflows/pr-check.yml
+++ b/.github/workflows/pr-check.yml
@@ -4,7 +4,7 @@ on:
pull_request:
branches: [main, develop]
push:
- branches: [main, develop]
+ branches: [develop]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/README.md b/README.md
index 7900a7b..10b3b15 100644
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@ npm install @luno-kit/react @tanstack/react-query
```tsx
import { LunoKitProvider, ConnectButton } from '@luno-kit/ui'
import { createConfig, defineChain, kusama, polkadot, polkadotjs, subwallet, westend, paseo } from '@luno-kit/react'
-import '@luno-kit/ui/dist/styles.css'
+import '@luno-kit/ui/styles.css'
const config = createConfig({
appName: 'My Luno App',
@@ -162,8 +162,10 @@ For full documentation and examples, visit [our documentation site](#) (coming s
The following examples are provided in the `examples` folder:
* `with-vite` - Vite integration (✅ Ready to run)
-* `with-next` - Next.js integration (coming soon)
-* `with-cra` - Create React App integration (coming soon)
+* `with-nextjs-app` - Next.js App Router integration (✅ Ready to run)
+* `with-nextjs-pages` - Next.js Pages Router integration (✅ Ready to run)
+* `with-cra` - Create React App integration (✅ Ready to run)
+* `with-remix` - Remix integration (coming soon)
### Running examples
@@ -176,8 +178,17 @@ cd LunoKit
# Install dependencies
pnpm install
-# Start with-vite server
+# Start with-vite example
pnpm --filter with-vite dev
+
+# Start with-nextjs-app example
+pnpm --filter with-nextjs-app dev
+
+# Start with-nextjs-pages example
+pnpm --filter with-nextjs-pages dev
+
+# Start with-cra example
+pnpm --filter with-cra start
```
## API Reference
diff --git a/examples/with-vite/src/App.tsx b/examples/with-vite/src/App.tsx
index 46927f2..7216937 100644
--- a/examples/with-vite/src/App.tsx
+++ b/examples/with-vite/src/App.tsx
@@ -392,7 +392,7 @@ const App: React.FC = () => {
Basic Usage
{`import { LunoKitProvider, ConnectButton } from '@luno-kit/ui';
-import '@luno-kit/ui/dist/styles.css';
+import '@luno-kit/ui/styles.css';
From 51c7cfda037381bf03836f2a120f4537e96be1f7 Mon Sep 17 00:00:00 2001
From: WBH <48749219+wbh1328551759@users.noreply.github.com>
Date: Tue, 29 Jul 2025 20:25:46 +0800
Subject: [PATCH 02/45] Chore/UI work (#24)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* fix(ui): resolve Radix UI Dialog TypeScript errors
- Add proper type interfaces for Dialog components
- Fix ModalContext missing closeConnectModal method
- Handle @tanstack/react-query v5 API changes
- Use React.createElement to bypass Radix UI type restrictions
* feat(ui): add new theme system with light/dark presets and CSS variables
* fix: resolve chain modal issues and optimize theme system
* feat: save current theme system state before refactoring
* fix: resolve UI build issues and restore theme system
- Fix base.css theme syntax to use @theme inline with var() instead of theme()
- Restore missing dark theme variables (spacing, typography, fonts, radii, blurs)
- Update theme context structure and remove defaultTheme.ts
- Update LunoKitProvider to use new theme system
- Fix PostCSS configuration for Tailwind CSS v4
* feat: remove toggleTheme and add auto system theme following
- Remove toggleTheme function from ThemeProvider
- Add useSystemTheme hook to detect system color scheme
- Add auto-following system theme when both light and dark are provided
- Add isAutoMode flag to indicate auto-following state
- Update example app to use setThemeMode instead of toggleTheme
- Maintain backward compatibility with existing theme system
* feat: optimize theme persistence logic - only persist when user has theme choice
* fix: remove defaultTheme from LunoKitProvider to match ThemeProvider interface
* feat(theme): remove localStorage persistence and fix dark theme logic
- Remove theme localStorage persistence
- Fix dark theme not applying with theme={{dark:{}}}
- Simplify theme initialization and switching logic
* refactor(ui): standardize design │
│ tokens and remove style duplicates
* feat(ui): Modify the rounded property of some components.
* refactor(AccountDetailsModal): simplify view transitions
to height-only animation
* refactor(ui):Optimize code structure, remove unused code
* feat(theme): enhance theme
system with auto-mode and user
preferences
- Add autoMode support for
system theme following
- Implement localStorage
persistence for user choices
- Add setThemeChoice API for
explicit user actions
- Remove unused dependencies
(postcss-import)
- Clean up redundant size props
and methods
- Fix hook functionality and
simplify API
- Improve theme initialization
priority logic
* refactor(ui): replace localStorage with
config.storage in theme system
* refactor(theme): optimize theme system
and eliminate flashing
- Extract CSS variable injection logic to
separate file (cssVariableInject.ts)
- Optimize initial theme state
calculation to prevent flashing
- Simplify theme preference interface
(isAuto, preferredTheme)
- Fix system theme detection with proper
initialization
- Remove redundant state management and
async loading
* refactor(theme): replace LunoStorage with localStorage for theme persistence
* feat(examples): Remove auto mode label
---------
Co-authored-by: “Gintma”
---
examples/with-vite/src/App.tsx | 28 +-
examples/with-vite/src/main.tsx | 161 ++++++++-
packages/ui/package.json | 2 +-
packages/ui/postcss.config.mjs | 2 +-
.../AccountDetailsModal/MainView.tsx | 18 +-
.../AccountDetailsModal/SwitchAccountView.tsx | 22 +-
.../AccountDetailsModal/SwitchChainView.tsx | 2 +-
.../components/AccountDetailsModal/index.tsx | 96 ++++--
.../ui/src/components/ChainIcon/index.tsx | 57 ++--
.../ui/src/components/ChainList/index.tsx | 26 +-
.../ui/src/components/ChainModal/index.tsx | 29 +-
.../ui/src/components/ChainSelector/index.tsx | 26 --
.../ui/src/components/ConnectButton/index.tsx | 59 +---
.../ui/src/components/ConnectModal/index.tsx | 50 ++-
packages/ui/src/components/Copy/index.tsx | 2 +-
packages/ui/src/components/Dialog/index.tsx | 35 +-
.../src/components/SpiralAnimation/index.tsx | 2 +-
packages/ui/src/hooks/cssVariableInject.ts | 135 ++++++++
packages/ui/src/providers/LunoKitProvider.tsx | 26 +-
packages/ui/src/providers/ModalContext.tsx | 1 +
packages/ui/src/providers/ThemeContext.tsx | 63 ----
packages/ui/src/providers/index.ts | 3 +-
packages/ui/src/styles/base.css | 321 ++++++++++++------
packages/ui/src/theme/context.tsx | 194 +++++++++++
packages/ui/src/theme/index.ts | 2 +
packages/ui/src/theme/types.ts | 104 ++++++
packages/ui/src/utils/cs.ts | 8 +-
pnpm-lock.yaml | 2 +-
28 files changed, 1069 insertions(+), 407 deletions(-)
delete mode 100644 packages/ui/src/components/ChainSelector/index.tsx
create mode 100644 packages/ui/src/hooks/cssVariableInject.ts
delete mode 100644 packages/ui/src/providers/ThemeContext.tsx
create mode 100644 packages/ui/src/theme/context.tsx
create mode 100644 packages/ui/src/theme/index.ts
create mode 100644 packages/ui/src/theme/types.ts
diff --git a/examples/with-vite/src/App.tsx b/examples/with-vite/src/App.tsx
index 7216937..0d6c468 100644
--- a/examples/with-vite/src/App.tsx
+++ b/examples/with-vite/src/App.tsx
@@ -31,7 +31,7 @@ const App: React.FC = () => {
const { sendTransactionAsync, data: sendTransactionData, isPending: isSendingTransaction, detailedStatus } = useSendTransaction();
const { api, isApiReady, apiError } = useApi();
- const { themeMode, toggleTheme } = useLunoTheme();
+ const { themeMode, setThemeChoice } = useLunoTheme();
const [transferForm, setTransferForm] = useState({
to: '',
@@ -137,12 +137,26 @@ const App: React.FC = () => {
Current Theme:
{themeMode.charAt(0).toUpperCase() + themeMode.slice(1)}
-
+
+
+
+
+
diff --git a/examples/with-vite/src/main.tsx b/examples/with-vite/src/main.tsx
index 67c1551..c63e46d 100644
--- a/examples/with-vite/src/main.tsx
+++ b/examples/with-vite/src/main.tsx
@@ -22,8 +22,167 @@ const lunoConfig = createConfig({
autoConnect: true,
});
+
+
createRoot(document.getElementById('root')!).render(
-
+
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 09e40f8..3d39e35 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -78,4 +78,4 @@
"publishConfig": {
"access": "public"
}
-}
+}
\ No newline at end of file
diff --git a/packages/ui/postcss.config.mjs b/packages/ui/postcss.config.mjs
index 7e0ba62..b776c62 100644
--- a/packages/ui/postcss.config.mjs
+++ b/packages/ui/postcss.config.mjs
@@ -2,4 +2,4 @@ export default {
plugins: {
"@tailwindcss/postcss": {},
}
-}
+}
\ No newline at end of file
diff --git a/packages/ui/src/components/AccountDetailsModal/MainView.tsx b/packages/ui/src/components/AccountDetailsModal/MainView.tsx
index bf951a4..8706967 100644
--- a/packages/ui/src/components/AccountDetailsModal/MainView.tsx
+++ b/packages/ui/src/components/AccountDetailsModal/MainView.tsx
@@ -24,13 +24,13 @@ export const MainView: React.FC = ({
key: 'Chain Name',
content: (
-
+
- {chain?.name || 'Polkadot'}
+ {chain?.name || 'Polkadot'}
@@ -45,7 +45,7 @@ export const MainView: React.FC
= ({
content: (
<>
- View on Explorer
+ View on Explorer
>
),
onClick: () => window.open(getExplorerUrl(chain?.blockExplorers?.default?.url!, address, 'address'))
@@ -55,7 +55,7 @@ export const MainView: React.FC = ({
content: (
<>
- Switch Account
+ Switch Account
>
),
onClick: () => onViewChange(AccountModalView.switchAccount)
@@ -69,8 +69,8 @@ export const MainView: React.FC = ({
};
return (
-
-
+
+
{items.map(i => (
{i.content}
@@ -80,7 +80,7 @@ export const MainView: React.FC = ({
- Disconnect
+ Disconnect
);
@@ -92,8 +92,8 @@ const SelectItem = ({ children, onClick }: { children: React.ReactNode; onClick?
type="button"
onClick={() => onClick?.()}
className={cs(
- 'w-full p-[14px] rounded-sm border-none text-left flex items-center gap-[8px] font-[500]',
- 'bg-[var(--color-connectorItemBackground)] hover:bg-[var(--color-connectorItemHover)] active:bg-[var(--color-connectorItemActive)]',
+ 'w-full p-3.5 rounded-accountActionItem border-none text-left flex items-center gap-2 font-medium',
+ 'bg-accountActionItemBackground hover:bg-accountActionItemBackgroundHover',
'transition-colors duration-200',
onClick ? 'cursor-pointer' : 'cursor-auto'
)}
diff --git a/packages/ui/src/components/AccountDetailsModal/SwitchAccountView.tsx b/packages/ui/src/components/AccountDetailsModal/SwitchAccountView.tsx
index c506b01..031c151 100644
--- a/packages/ui/src/components/AccountDetailsModal/SwitchAccountView.tsx
+++ b/packages/ui/src/components/AccountDetailsModal/SwitchAccountView.tsx
@@ -22,7 +22,7 @@ export const SwitchAccountView: ViewComponent = ({ onBack }) => {
}, [onBack])
return (
-
+
{accounts.map((acc) => (
= React.memo(({
type="button"
onClick={() => selectAccount(account)}
className={cs(
- 'px-[14px] py-[10px] w-full rounded-sm border-none',
- 'bg-[var(--color-connectorItemBackground)]',
- 'text-left flex items-center justify-between gap-[8px]',
+ 'px-3.5 py-2.5 w-full rounded-accountSelectItem border-none',
+ 'bg-accountSelectItemBackground',
+ 'text-left flex items-center justify-between gap-2',
'transition-colors duration-200',
- isSelected ? 'cursor-auto' : 'cursor-pointer hover:bg-[var(--color-connectorItemHover)] active:bg-[var(--color-connectorItemActive)]'
+ isSelected ? 'cursor-auto' : 'cursor-pointer hover:bg-accountSelectItemBackgroundHover'
)}
aria-label={account.name || address}
disabled={isSelected}
>
-
+
{connector?.icon &&

}
-
+
{account.name || formatAddress(address)}
-
+
{balance === undefined ? (
-
+
) : (
<>
{balance?.formattedTransferable || '0.00'} {chain?.nativeCurrency?.symbol || 'DOT'}
@@ -88,8 +88,8 @@ const AccountItem: React.FC = React.memo(({
{isSelected && (
-
-
+
)}
diff --git a/packages/ui/src/components/AccountDetailsModal/SwitchChainView.tsx b/packages/ui/src/components/AccountDetailsModal/SwitchChainView.tsx
index 866e12b..73b9714 100644
--- a/packages/ui/src/components/AccountDetailsModal/SwitchChainView.tsx
+++ b/packages/ui/src/components/AccountDetailsModal/SwitchChainView.tsx
@@ -11,7 +11,7 @@ interface SwitchChainViewProps {
export const SwitchChainView: ViewComponent = ({ onBack }) => {
return (
-
+
)
diff --git a/packages/ui/src/components/AccountDetailsModal/index.tsx b/packages/ui/src/components/AccountDetailsModal/index.tsx
index 13d1376..fedb024 100644
--- a/packages/ui/src/components/AccountDetailsModal/index.tsx
+++ b/packages/ui/src/components/AccountDetailsModal/index.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useMemo, useState } from 'react';
+import React, { useCallback, useMemo, useState, useRef } from 'react';
import { useAccount, useActiveConnector, useBalance, useChain } from '@luno-kit/react';
import { Dialog, DialogClose, DialogTitle } from '../Dialog';
import { cs } from '../../utils';
@@ -24,23 +24,59 @@ export const AccountDetailsModal: React.FC = () => {
const activeConnector = useActiveConnector()
const [currentView, setCurrentView] = useState
(AccountModalView.main);
+ const [isAnimating, setIsAnimating] = useState(false);
+ const containerRef = useRef(null);
+ const currentViewRef = useRef(null);
const handleViewChange = useCallback((view: AccountModalView) => {
+ if (view === currentView || isAnimating) return;
+
+ setIsAnimating(true);
+
+ if (!containerRef.current) {
+ setCurrentView(view);
+ setIsAnimating(false);
+ return;
+ }
+
+ const container = containerRef.current;
+ const currentHeight = container.offsetHeight;
+
setCurrentView(view);
- }, []);
+
+ // Wait for React to render new content, then animate height
+ requestAnimationFrame(() => {
+ if (!container || !currentViewRef.current) {
+ setIsAnimating(false);
+ return;
+ }
+
+ const newHeight = currentViewRef.current.offsetHeight;
+
+ container.animate([
+ { height: currentHeight + 'px' },
+ { height: newHeight + 'px' }
+ ], {
+ duration: 200,
+ easing: 'ease-out',
+ fill: 'forwards'
+ }).addEventListener('finish', () => {
+ setIsAnimating(false);
+ });
+ });
+ }, [currentView, isAnimating]);
const handleModalClose = useCallback(() => {
close();
setCurrentView(AccountModalView.main);
+ setIsAnimating(false);
}, [close]);
+
const viewTitle = useMemo(() => {
- const titleMap = {
- [AccountModalView.switchAccount]: SwitchAccountView.title,
- [AccountModalView.switchChain]: SwitchChainView.title,
- [AccountModalView.main]: null
- };
- return titleMap[currentView];
+ if (currentView === AccountModalView.switchAccount) return 'Switch Account';
+ if (currentView === AccountModalView.switchChain) return SwitchChainView.title;
+ return null;
}, [currentView]);
const viewComponents = useMemo(() => ({
@@ -58,6 +94,7 @@ export const AccountDetailsModal: React.FC = () => {
)
}), [handleViewChange, handleModalClose]);
+
return (