2026-03-03 08:56:13 +00:00
# Overlay Migration Guide
2026-03-04 03:13:43 +00:00
This document tracks the migration away from legacy overlay APIs.
2026-03-03 08:56:13 +00:00
## Scope
2026-03-04 03:13:43 +00:00
- Deprecated imports:
- `@/app/components/base/portal-to-follow-elem`
- `@/app/components/base/tooltip`
- `@/app/components/base/modal`
2026-03-04 05:56:27 +00:00
- `@/app/components/base/confirm`
2026-03-04 03:13:43 +00:00
- `@/app/components/base/select` (including `custom` / `pure` )
2026-03-05 00:47:54 +00:00
- `@/app/components/base/popover`
- `@/app/components/base/dropdown`
- `@/app/components/base/dialog`
2026-03-17 10:56:36 +00:00
- `@/app/components/base/toast` (including `context` )
2026-03-03 08:56:13 +00:00
- Replacement primitives:
- `@/app/components/base/ui/tooltip`
- `@/app/components/base/ui/dropdown-menu`
2026-03-09 04:05:38 +00:00
- `@/app/components/base/ui/context-menu`
2026-03-03 08:56:13 +00:00
- `@/app/components/base/ui/popover`
- `@/app/components/base/ui/dialog`
2026-03-04 05:56:27 +00:00
- `@/app/components/base/ui/alert-dialog`
2026-03-03 08:56:13 +00:00
- `@/app/components/base/ui/select`
2026-03-17 10:56:36 +00:00
- `@/app/components/base/ui/toast`
2026-03-20 09:23:17 +00:00
- Tracking issue: < https: // github . com / langgenius / dify / issues / 32767 >
2026-03-03 08:56:13 +00:00
## ESLint policy
2026-03-04 03:13:43 +00:00
- `no-restricted-imports` blocks all deprecated imports listed above.
- The rule is enabled for normal source files (`.ts` / `.tsx` ) and test files are excluded.
- Legacy `app/components/base/*` callers are temporarily allowlisted in `OVERLAY_MIGRATION_LEGACY_BASE_FILES` (`web/eslint.constants.mjs`).
2026-03-03 08:56:13 +00:00
- New files must not be added to the allowlist without migration owner approval.
## Migration phases
1. Business/UI features outside `app/components/base/**`
2026-03-04 03:13:43 +00:00
- Migrate old calls to semantic primitives from `@/app/components/base/ui/**` .
- Keep deprecated imports out of newly touched files.
2026-03-03 08:56:13 +00:00
1. Legacy base components in allowlist
- Migrate allowlisted base callers gradually.
2026-03-04 03:13:43 +00:00
- Remove migrated files from `OVERLAY_MIGRATION_LEGACY_BASE_FILES` immediately.
2026-03-03 08:56:13 +00:00
1. Cleanup
2026-03-04 03:13:43 +00:00
- Remove remaining allowlist entries.
- Remove legacy overlay implementations when import count reaches zero.
2026-03-03 08:56:13 +00:00
2026-03-17 10:56:36 +00:00
## Toast migration strategy
- During migration, `@/app/components/base/toast` and `@/app/components/base/ui/toast` may coexist.
- All new toast usage must go through `@/app/components/base/ui/toast` .
- When a file with legacy toast usage is touched, prefer migrating that call site in the same change; full-repo toast cleanup is not required in one PR.
- `@/app/components/base/ui/toast` is the design-system stack toast host. Legacy `ToastContext` , `ToastProvider` , anchored toast behavior, and ad-hoc mount patterns stay in `base/toast` until their call sites are migrated away.
2026-03-04 03:13:43 +00:00
## Allowlist maintenance
2026-03-03 08:56:13 +00:00
- After each migration batch, run:
```sh
2026-03-04 03:13:43 +00:00
pnpm -C web lint:fix --prune-suppressions < changed-files >
2026-03-03 08:56:13 +00:00
```
2026-03-04 03:13:43 +00:00
- If a migrated file was in the allowlist, remove it from `web/eslint.constants.mjs` in the same PR.
- Never increase allowlist scope to bypass new code.
2026-03-03 08:56:13 +00:00
2026-03-10 05:46:38 +00:00
## z-index strategy
2026-03-17 10:56:36 +00:00
All new overlay primitives in `base/ui/` share a single z-index value:
**`z-[1002]`**, except Toast which stays at ** `z-[1101]` ** during migration.
2026-03-10 05:46:38 +00:00
### Why z-[1002]?
During the migration period, legacy and new overlays coexist. Legacy overlays
portal to `document.body` with explicit z-index values:
2026-03-20 09:23:17 +00:00
| Layer | z-index | Components |
| --------------------------------- | ---------------- | -------------------------------------------- |
| Legacy Drawer | `z-[30]` | `base/drawer` |
| Legacy Modal | `z-[60]` | `base/modal` (default) |
| Legacy PortalToFollowElem callers | up to `z-[1001]` | various business components |
| **New UI primitives** | ** `z-[1002]` ** | `base/ui/*` (Popover, Dialog, Tooltip, etc.) |
| Legacy Modal (highPriority) | `z-[1100]` | `base/modal` (`highPriority={true}`) |
| Toast (legacy + new) | `z-[1101]` | `base/toast` , `base/ui/toast` |
2026-03-10 05:46:38 +00:00
`z-[1002]` sits above all common legacy overlays, so new primitives always
render on top without needing per-call-site z-index hacks. Among themselves,
new primitives share the same z-index and rely on **DOM order** for stacking
(later portal = on top).
2026-03-17 10:56:36 +00:00
Toast intentionally stays one layer above the remaining legacy `highPriority`
modal path (`z-[1100]`) so notifications keep their current visibility without
falling back to `z-[9999]` .
2026-03-10 05:46:38 +00:00
### Rules
- **Do NOT add z-index overrides** (e.g. `className="z-[1003]"` ) on new
`base/ui/*` components. If you find yourself needing one, the parent legacy
overlay should be migrated instead.
- When migrating a legacy overlay that has a high z-index, remove the z-index
entirely — the new primitive's default `z-[1002]` handles it.
- `portalToFollowElemContentClassName` with z-index values (e.g. `z-[1000]` )
should be deleted when the surrounding legacy container is migrated.
### Post-migration cleanup
Once all legacy overlays are removed:
1. Reduce `z-[1002]` back to `z-50` across all `base/ui/` primitives.
2026-03-17 10:56:36 +00:00
1. Reduce Toast from `z-[1101]` to `z-[51]` .
2026-03-10 05:46:38 +00:00
1. Remove this section from the migration guide.
2026-03-03 08:56:13 +00:00
## React Refresh policy for base UI primitives
- We keep primitive aliases (for example `DropdownMenu = Menu.Root` ) in the same module.
- For `app/components/base/ui/**/*.tsx` , `react-refresh/only-export-components` is currently set to `off` in ESLint to avoid false positives and IDE noise during migration.
- Do not use file-level `eslint-disable` comments for this policy; keep control in the scoped ESLint override.