fix(web): 图标尺寸覆盖(text-[Npx])在任何地方都不生效——CSS层级冲突
根因:Material Icons的Google Fonts样式表(index.html的<link>)自己的 .material-icons规则完全没有@layer包裹(纯普通CSS),而Tailwind v4的 每一个工具类都在@layer utilities里——CSS Cascade Layers规范规定,任 何未分层的声明,不管选择器优先级多高、样式表加载顺序多晚,永远优先于 任何分层声明。实测坐实:<Icon className="text-[14px]">的计算样式恒 定是font-size:24px,不管试图覆盖成几px都没用,因为Google那条未分层 规则永远赢。这不是某一处的问题,是Icon组件全局的、此前从未被发现的 系统性bug——历史上多轮视觉审查用截图核对布局,没人具体去量过某个 图标的computed font-size。 修复:Icon.tsx改用自己的im-icon类名,不再用material-icons这个 Google样式表实际瞄准的类名,这样那条未分层规则从一开始就不会匹配到 这些元素。index.css里用@layer base原样照抄了Material Icons自己那条 规则的视觉默认值(24px/line-height:1等),套上分层之后,Tailwind自己 的@layer utilities(在Tailwind的层注册顺序里排在base之后)就能正常 覆盖它了——和每一处写`text-[Npx]`时本来就该有的效果一致。 全局仓库搜索确认material-icons这个类名只有Icon.tsx一处引用,改动范围 可控、不涉及其它代码路径。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
776131b1de
commit
fb1fda689c
@@ -1,10 +1,15 @@
|
|||||||
/** Thin wrapper around a Material Icons ligature glyph (font loaded in index.html).
|
/** Thin wrapper around a Material Icons ligature glyph (font loaded in index.html).
|
||||||
* Always `aria-hidden` -- the glyph's DOM text is the icon's internal ligature name (e.g.
|
* Always `aria-hidden` -- the glyph's DOM text is the icon's internal ligature name (e.g.
|
||||||
* "arrow_back_ios_new"), never something a screen reader should announce. An icon-only
|
* "arrow_back_ios_new"), never something a screen reader should announce. An icon-only
|
||||||
* interactive element (button/link with no visible text) must set its own `aria-label`. */
|
* interactive element (button/link with no visible text) must set its own `aria-label`.
|
||||||
|
*
|
||||||
|
* Deliberately `im-icon`, NOT Google Fonts' own `.material-icons` class -- see index.css's own
|
||||||
|
* comment on that rule for why: Google's stylesheet ships that class completely unlayered, which
|
||||||
|
* under CSS Cascade Layers always outranks Tailwind's layered utilities regardless of specificity
|
||||||
|
* or source order, silently ignoring every `text-[Npx]` size override across the whole app. */
|
||||||
export function Icon({ name, className = '', style }: { name: string; className?: string; style?: React.CSSProperties }) {
|
export function Icon({ name, className = '', style }: { name: string; className?: string; style?: React.CSSProperties }) {
|
||||||
return (
|
return (
|
||||||
<i aria-hidden="true" className={`material-icons select-none ${className}`} style={style}>
|
<i aria-hidden="true" className={`im-icon select-none ${className}`} style={style}>
|
||||||
{name}
|
{name}
|
||||||
</i>
|
</i>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,35 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Material Icons' OWN Google Fonts stylesheet (index.html's `<link>`) ships its `.material-icons`
|
||||||
|
* class completely unlayered -- plain CSS, no `@layer`. Tailwind v4 (the `@import` above) puts every
|
||||||
|
* one of its own utilities inside `@layer utilities`. Per the CSS Cascade Layers spec, ANY unlayered
|
||||||
|
* declaration beats EVERY layered one for the same property on the same element, regardless of
|
||||||
|
* selector specificity or which stylesheet loads later in the document -- confirmed live: an
|
||||||
|
* `<Icon className="text-[14px]">` computed to `font-size: 24px` regardless of the requested size,
|
||||||
|
* every time, because Google's unlayered rule was always winning over Tailwind's layered
|
||||||
|
* `.text-\[14px\]` utility. Icon.tsx uses its own `im-icon` class (not `.material-icons`) specifically
|
||||||
|
* so Google's competing rule never matches these elements at all -- this is the same visual default
|
||||||
|
* (24px, line-height 1, etc., copied 1:1 from Google's own stylesheet), just declared inside `@layer
|
||||||
|
* base` so Tailwind's `@layer utilities` (which Tailwind itself registers after `base`) can actually
|
||||||
|
* override it, the way every call site sizing an icon via `text-[Npx]` already assumed it could.
|
||||||
|
*/
|
||||||
|
@layer base {
|
||||||
|
.im-icon {
|
||||||
|
font-family: 'Material Icons';
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
word-wrap: normal;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Design tokens ported 1:1 from docs/design_handoff_meetingim_web/README.md's "Design Tokens"
|
* Design tokens ported 1:1 from docs/design_handoff_meetingim_web/README.md's "Design Tokens"
|
||||||
* section. Values are exact hex/px from the design handoff, not semantic guesses -- keep them in
|
* section. Values are exact hex/px from the design handoff, not semantic guesses -- keep them in
|
||||||
|
|||||||
Reference in New Issue
Block a user