diff --git "a/docs/white-book/00-\345\277\205\350\257\273/best-practices.md" "b/docs/white-book/00-\345\277\205\350\257\273/best-practices.md"
index 5d56b5eb..e3afcd64 100644
--- "a/docs/white-book/00-\345\277\205\350\257\273/best-practices.md"
+++ "b/docs/white-book/00-\345\277\205\350\257\273/best-practices.md"
@@ -25,3 +25,5 @@
- 紧凑头部效果使用 `animation-range: 0 80px` 限制动画范围
- ⚠️ scroll-driven animations 是渐进增强:初始状态必须是可用的(如 opacity-0),不支持时保持初始状态
- E2E 截图变更后运行 `pnpm e2e:audit` 检查残留截图,详见白皮书 08-测试篇/03-Playwright配置/e2e-best-practices
+- 组件专属样式(动画、伪元素、复杂选择器)使用 CSS Modules:`component-name.module.css` + `import styles from './xxx.module.css'`
+- CSS Modules 仅用于 Tailwind 无法表达的场景(如 @keyframes、:global()、复杂层叠),简单样式优先用 Tailwind
diff --git a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png
index 562bff0b..c8a4d146 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png and b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png
index b4126d54..63f6e134 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png and b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png
index d980cc48..9d00f997 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png and b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png
index be6f24d8..72f8222b 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png and b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png
index 6b3508a5..1bc6d0ec 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png and b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png
index 80a4354b..f2e5c86c 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png and b/e2e/__screenshots__/Desktop-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png
index 69af7595..36ff3dc5 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png
index 69af7595..c803f2d1 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png
index 69af7595..744071af 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png
index 45ff96c5..d324fcff 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png
index ba2b7204..36ff3dc5 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png
index ba2b7204..c803f2d1 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png
index ba2b7204..6b9ae99b 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png differ
diff --git a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png
index 5059f7a6..62ab03f5 100644
Binary files a/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png and b/e2e/__screenshots__/Desktop-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png
index 1c145e9c..92d08fed 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png and b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02-ecosystem-tab-content.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png
index b870f232..c45f804a 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png and b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02b-ecosystem-my-tab.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png
index f6e9b22e..5f8ce362 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png and b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02c-ecosystem-context-menu.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02d-context-menu-open.png b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02d-context-menu-open.png
index 63617773..b70dcee5 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02d-context-menu-open.png and b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02d-context-menu-open.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png
index 024fbd4c..d05aaebc 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png and b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02e-context-menu-detail.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png
index e138c2b6..3297d235 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png and b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/02f-context-menu-remove.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png
index b121378c..f0a89b9c 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png and b/e2e/__screenshots__/Mobile-Chrome/ecosystem-miniapp.mock.spec.ts/16-miniapp-detail.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png
index 69af7595..36ff3dc5 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-01-connect.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png
index 69af7595..c803f2d1 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-02-connect-dark.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png
index 69af7595..744071af 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-03-green-theme.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png
index 69af7595..d324fcff 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/forge-04-dark-purple-theme.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png
index ba2b7204..36ff3dc5 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-01-connect.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png
index ba2b7204..c803f2d1 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-02-connect-dark.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png
index ba2b7204..6b9ae99b 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-03-connect-blue-theme.png differ
diff --git a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png
index ba2b7204..62ab03f5 100644
Binary files a/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png and b/e2e/__screenshots__/Mobile-Chrome/miniapp-ui.mock.spec.ts/teleport-04-green-theme.png differ
diff --git a/e2e/ecosystem-miniapp.mock.spec.ts b/e2e/ecosystem-miniapp.mock.spec.ts
index 4a924306..91177a59 100644
--- a/e2e/ecosystem-miniapp.mock.spec.ts
+++ b/e2e/ecosystem-miniapp.mock.spec.ts
@@ -1,4 +1,4 @@
-import { test, expect } from '@playwright/test'
+import { test, expect, type Page } from '@playwright/test'
/**
* Bio 小程序生态 E2E 截图测试
@@ -6,6 +6,20 @@ import { test, expect } from '@playwright/test'
* 测试用户故事并生成截图验证 UI 正确性
*/
+/**
+ * 滑动到"我的"页面 (Swiper 布局:从右向左滑动一次)
+ * 发现(0) → 我的(1) → 堆栈(2)
+ */
+async function swipeToMyAppsPage(page: Page) {
+ const viewport = page.viewportSize()!
+ // 从中右向中左滑动,距离适中,避免滑过头
+ await page.mouse.move(viewport.width * 0.7, viewport.height / 2)
+ await page.mouse.down()
+ await page.mouse.move(viewport.width * 0.3, viewport.height / 2, { steps: 20 })
+ await page.mouse.up()
+ await page.waitForTimeout(500)
+}
+
const TEST_WALLET_DATA = {
wallets: [
{
@@ -112,9 +126,8 @@ test.describe('生态 Tab 截图测试', () => {
await ecosystemTab.click()
await page.waitForTimeout(500)
- // 点击"我的" Tab
- const myTab = page.locator('button:has-text("我的")')
- await myTab.click()
+ // 滑动到"我的"页 (从右向左滑)
+ await swipeToMyAppsPage(page)
await page.waitForTimeout(500)
await expect(page).toHaveScreenshot('02b-ecosystem-my-tab.png')
@@ -138,9 +151,8 @@ test.describe('生态 Tab 截图测试', () => {
await ecosystemTab.click()
await page.waitForTimeout(500)
- // 点击"我的" Tab
- const myTab = page.locator('button:has-text("我的")')
- await myTab.click()
+ // 滑动到"我的"页
+ await swipeToMyAppsPage(page)
await page.waitForTimeout(500)
// 右键点击第一个应用图标触发 Context Menu
@@ -166,7 +178,7 @@ test.describe('生态 Tab 截图测试', () => {
// 进入生态 - 我的
await page.getByTestId('tab-ecosystem').click()
await page.waitForTimeout(300)
- await page.locator('button:has-text("我的")').click()
+ await swipeToMyAppsPage(page)
await page.waitForTimeout(300)
// 右键菜单 -> 打开
@@ -193,7 +205,7 @@ test.describe('生态 Tab 截图测试', () => {
// 进入生态 - 我的
await page.getByTestId('tab-ecosystem').click()
await page.waitForTimeout(300)
- await page.locator('button:has-text("我的")').click()
+ await swipeToMyAppsPage(page)
await page.waitForTimeout(300)
// 右键菜单 -> 详情
@@ -221,7 +233,7 @@ test.describe('生态 Tab 截图测试', () => {
// 进入生态 - 我的
await page.getByTestId('tab-ecosystem').click()
await page.waitForTimeout(300)
- await page.locator('button:has-text("我的")').click()
+ await swipeToMyAppsPage(page)
await page.waitForTimeout(300)
// 右键菜单 -> 移除
diff --git a/src/StackflowApp.tsx b/src/StackflowApp.tsx
index bfb054a3..53d47631 100644
--- a/src/StackflowApp.tsx
+++ b/src/StackflowApp.tsx
@@ -1,5 +1,28 @@
+import { useStore } from "@tanstack/react-store";
import { Stack } from "./stackflow";
+import { MiniappWindow, MiniappStackView } from "./components/ecosystem";
+import {
+ miniappRuntimeStore,
+ miniappRuntimeSelectors,
+ closeStackView,
+} from "./services/miniapp-runtime";
export function StackflowApp() {
- return
应用堆栈
+