fix(web-shell): isolate component styles from host CSS - #7466
Conversation
Web Shell host style isolationScopeVerify that ordinary host-page selectors do not override Web Shell-owned UI in Host rules* {
padding: 0;
box-sizing: content-box;
}
h2 {
margin: 40px;
color: rgb(1, 2, 3);
font: 40px serif;
}
button {
padding: 0;
border-radius: 0;
background: red;
font: 30px serif;
}Expected behavior
ResultPassed against the production library artifact in Chromium. Computed styles |
|
Thanks for the PR! Template looks good ✓ Problem: This is an observed bug with clear evidence. CSS cascade layers lose to unlayered host styles per spec, so when Web Shell is embedded in a host page, ordinary Direction: Aligned. CSS isolation is fundamental to Web Shell's purpose as an embeddable component — if host styles can freely override component internals, the component is unreliable by design. No CHANGELOG reference needed here; this is a correctness fix for the component boundary. Size: Not applicable — no core paths touched. 8 files, 289 additions / 161 deletions (~317 production lines, ~133 test lines). Approach: The scope feels right. The core insight — unlayer the Tailwind imports and switch Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题: 这是一个有明确证据的已观测 bug。根据 CSS 规范,cascade layer 中的样式会输给未分层的宿主样式,因此当 Web Shell 嵌入宿主页面时,普通的 方向: 对齐。CSS 隔离是 Web Shell 作为可嵌入组件的基本需求——如果宿主样式可以随意覆盖组件内部样式,组件就不可靠。 规模: 不适用——未触及核心路径。8 个文件,289 行新增 / 161 行删除(约 317 行生产代码,约 133 行测试代码)。 方案: 范围合理。核心思路——取消 Tailwind 导入的 layer 并将根作用域的 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
Code ReviewIndependent proposal: Given "host CSS overrides Web Shell component styles," I'd (1) remove Comparison with the diff: The PR's approach matches this exactly. No simpler path missed. Findings: No critical blockers or AGENTS.md violations.
TestingBuild artifact tests (9/9 pass)The new ScheduledTasksDialog tests (41/41 pass)Includes the new Built CSS output verificationInspected CLI smoke test中文说明代码审查独立方案: 针对"宿主 CSS 覆盖 Web Shell 组件样式"的问题,我会 (1) 移除 Tailwind 导入的 与 diff 对比: PR 的方案与此完全一致。没有遗漏更简单的路径。 发现: 无关键阻塞问题或 AGENTS.md 违规。
测试构建产物测试 9/9 通过,ScheduledTasksDialog 测试 41/41 通过。构建 CSS 输出验证确认无 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 5/5 — Clean across every stage; would merge without hesitation. The problem is real and well-understood: CSS cascade layers lose to unlayered host styles per spec, so any host page with The implementation is focused and mechanical: every file serves the isolation goal, no drive-by refactors, no scope creep. The Tests are comprehensive: the build-artifact test locks in the cascade ordering (resets → utilities → CSS modules, all unlayered), and the ScheduledTasksDialog test covers the portal-root placement. The built CSS output confirms zero If I had to maintain this in six months, I'd thank the author — the approach is standard, the tests pin the important invariants, and the code is straightforward. 中文说明置信度:5/5 — 每个阶段都很干净,毫不犹豫地合并。 问题是真实且 well-understood 的:根据 CSS 规范,cascade layer 中的样式会输给未分层的宿主样式,因此任何带有 实现聚焦且机械化:每个文件都服务于隔离目标,没有顺手重构,没有范围蔓延。 测试全面:构建产物测试固定了级联顺序,ScheduledTasksDialog 测试覆盖了 portal-root 挂载。构建 CSS 输出确认零 — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
🖼️ web-shell visual previewRendered against a mock daemon (no real backend): the PR base vs this PR head Screenshots · before / afterℹ️ No screenshot changed against the PR base — but this PR edits 5 render-shaping files:
Either the change has no visual effect (logic, plumbing, a state the scenarios never reach), or no scenario renders this UI — in which case the preview cannot see it, and an empty result is a coverage gap rather than a clean bill of health. To make it visible, add a scenario to Full-resolution recordings (.webm) are attached to the workflow run. — Qwen Code · web-shell visuals |
chiga0
left a comment
There was a problem hiding this comment.
Code Review Overview (AI Generated)
PR: #7466 — fix(web-shell): isolate component styles from host CSS
Type: Bug Fix (CSS)
Change size: +289/-161 across 8 files
HEAD: 9ede81e6
Findings Summary
- Critical/Major/Minor: 0
- Nit: 1
Review
Well-executed CSS specificity isolation for embedded Web Shell components. Three coordinated changes:
1. Layer removal: Removed @layer theme, base, components, utilities declaration and unlayered Tailwind imports. This prevents Tailwind utilities from losing to unlayered host CSS in the cascade.
2. Selector strategy shift: Changed from :where(A, B) (zero specificity) to :is(A:where(B)) (specificity from [data-web-shell-root] attribute). The :is() pseudo-class takes the highest specificity from its arguments, so [data-web-shell-root] provides enough specificity to beat host *, h2, and button selectors, while :where([data-web-shell-shadcn]) keeps the shadcn qualifier at zero specificity for consumer customization.
3. Preflight extraction: Moved CSS reset from @layer base into unlayered preflight.css. Reset rules now use the same :is(...:where(...)) selector strategy, ensuring they beat unlayered host resets through specificity rather than cascade layers.
Verified correct:
- Build artifact test locks in reset → utilities → CSS Modules ordering
- No
@layerdeclarations in generated CSS (test asserts emptyconflictingLayers) - CSS Modules remain low-specificity (
:where(...)) for consumer customization - Import order moved to top of
App.tsxandWebShellTranscript.tsx— globals loaded before components - Scheduled task reference picker now renders inside
WebShellPortalRootContext— inherits portal styling
Nit
The preflight.css selectors are highly repetitive (each rule block repeats the same 4-line :is(...) selector prefix). A PostCSS plugin or build-time transformation could deduplicate this, but the explicit repetition is acceptable for a generated artifact that needs to be self-contained.
Final Verdict
LGTM. Clean CSS isolation strategy that correctly solves the embedded-component specificity problem without breaking consumer customization. The :is():where() pattern is the right choice for this use case.
This review was generated by QoderWork AI
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
— qwen3.7-max via Qwen Code /review
yiliang114
left a comment
There was a problem hiding this comment.
整体 LGTM,样式隔离方案清晰。几个小建议:
preflight.css中font-size: 14px硬编码会覆盖用户浏览器字体缩放偏好,建议改为0.875rem或 CSS 变量COMPONENT_SCOPE与COMPONENT_ROOT_SCOPE写法不一致(:wherevs:is),建议加注释说明设计意图ScheduledTasksDialog.tsx中portalRoot ?? document.body回退时样式会裸奔,建议 dev 模式加console.warntw-animate-css/shadcn/tailwind.css如果上游更新引入@layer,运行时行为会静默回退,建议关注
以上均为非阻塞建议,不影响合入。
What this PR does
This change gives Web Shell-owned UI a scoped baseline with enough specificity to beat ordinary host-page element and universal selectors, while keeping existing CSS Module scoping low-specificity for consumer customization. It also keeps Tailwind utilities out of cascade layers that would otherwise lose to unlayered host CSS and routes the scheduled-task reference picker through the shared Web Shell portal root.
Why it's needed
When Web Shell is embedded as a component, host rules such as
* { padding: 0 },h2 { ... }, andbutton { ... }can override Tailwind/shadcn styles even when the component CSS is loaded correctly. This makes Web Shell-owned controls and portal content visually inconsistent with the standalone application.Reviewer Test Plan
How to verify
*,h2, andbuttonrules after the component stylesheet. Confirm representative buttons and headings retain the same padding, margin, box sizing, typography, colors, background, and border radius in both the main root and dialog portal root.!importantrules and higher-specificity class/id rules remain outside the isolation guarantee.Evidence (Before & After)
Before: ordinary host universal and element selectors changed computed styles for Web Shell buttons and headings because layered Tailwind rules lost to unlayered host CSS.
After: adding the same hostile host rules after the production component CSS produced no computed-style differences for representative root and portal buttons/headings across padding, margin, box sizing, typography, colors, background, border radius, line height, and height.
Tested on
Environment (optional)
Production library build in Chromium, plus focused Vitest coverage for the generated CSS artifact and scheduled-task portal placement.
Risk & Scope
!importantdeclarations, higher-specificity class/id selectors, and arbitrary properties Web Shell does not declare.Linked Issues
N/A
中文说明
本 PR 做了什么
本次改动为 Web Shell 自有 UI 增加了带作用域且权重足以压过宿主普通元素选择器和通配选择器的基础样式,同时保持现有 CSS Modules 的低权重作用域,避免影响接入方定制。它还让 Tailwind utilities 不再处于会输给宿主未分层 CSS 的 cascade layer 中,并将定时任务引用选择器统一挂载到 Web Shell 的共享 portal root。
为什么需要
当 Web Shell 作为组件嵌入其他页面时,宿主的
* { padding: 0 }、h2 { ... }、button { ... }等规则可能覆盖 Tailwind/shadcn 样式,即使组件 CSS 已正确加载。结果是 Web Shell 自有控件和 portal 内容的视觉表现与独立应用不一致。Reviewer Test Plan
如何验证
*、h2和button规则。确认主 root 和 dialog portal root 中的代表性按钮、标题在 padding、margin、box sizing、字体、颜色、背景和圆角方面保持不变。!important规则和更高权重的 class/id 规则仍不在本次隔离保证范围内。证据(修改前后)
修改前:由于分层的 Tailwind 规则会输给宿主未分层 CSS,普通宿主通配选择器和元素选择器会改变 Web Shell 按钮和标题的计算样式。
修改后:在生产组件 CSS 之后添加同样的宿主冲突规则,主 root 和 portal 中代表性按钮、标题的计算样式均无差异;核对属性包括 padding、margin、box sizing、字体、颜色、背景、圆角、行高和高度。
测试平台
环境(可选)
在 Chromium 中验证生产 library build,并使用定向 Vitest 覆盖生成 CSS 产物和定时任务 portal 挂载位置。
风险与范围
!important声明、更高权重的 class/id 选择器,以及 Web Shell 自身未声明的任意属性。关联 Issue
N/A