# UI Coding Standard > **会话交接 / 改动总览**:见 `docs/experience/AGENT_UI_CHANGELOG.md`(文件映射、9-slice 架构、已知坑、未收尾项)。 ## Goal This project should treat `public/UI` and `public/Icons` as the single source of truth for fantasy UI chrome and item icon semantics. New UI code should match asset naming first, then map product meaning onto those assets through a small semantic layer in code. ## Asset Folders - `public/UI`: UI chrome, tabs, buttons, frames, arrows, HUD, equipment-slot markers, map controls. - `public/Icons`: item and ability icons. Most files are semantic item glyphs with numeric prefixes. ## Naming Rules Observed ### `public/UI` - `1_*`: base interaction assets and compact function glyphs. - `11_*`: directional arrows and small action markers. - `_s`: small size. - `_xs`: extra-small size. - `_pick` / `_picked`: selected, active, or pressed state. - `_d`: disabled, dimmed, or inactive state. - `_on` / `_off`: explicit toggle state. - `Hud_icon_*`: HUD/tab-friendly icons. - `Icon_Eq_*`: equipment-slot semantics. - `Map_*`: map-only controls or chrome. - `Frame_*`, `Popup_*`, `Dialogue_*`, `Inventory_*`, `Quest_*`, `Skill_*`: domain-specific container assets. ### `public/Icons` - Pattern is usually `NN_name.png`. - The numeric prefix is an atlas/catalog index, not UI meaning. - Semantic matching should be based on the name segment, not the number. - Prefer item-like meanings such as `sword`, `magic`, `potion`, `relic`, `treasure`, `crystal`, `shield`. ## Required Coding Pattern - Do not hardcode random asset filenames directly in feature components. - Put semantic mappings in `src/uiAssets.ts`. - Render pixel UI assets through `src/components/PixelIcon.tsx`. - Pick icons by UI meaning, not by whichever file "looks close enough" in one screen. - If a state exists in art, wire both active and inactive assets instead of tinting one image in CSS. - Major UI chrome should use authored textures from `public/UI`, not only plain Tailwind borders. - Do not mix `background` shorthand with `backgroundImage` / `backgroundRepeat` / `backgroundPosition` / `backgroundSize` in the same inline `style` object; use longhand fields consistently to avoid React rerender warnings and stale paint bugs. ## Layout Rules For Icon UI - Navigation/tab icons should be visually larger than body text. - Preferred structure is `icon above + label below`, centered, instead of inline icon-text rows for compact nav. - Tab labels should stay readable on mobile with `clamp()`-based sizing. - Touch targets should remain comfortable on phones; do not ship tiny icon buttons. ## Responsive Rules For UI Images - Do not stretch framed pixel UI with `background-size: 100% 100%`. - Framed buttons, panels, tabs, modal shells, and title plates must use 9-slice scaling. - Corners stay fixed, edges only stretch or repeat on one axis, and the center fills independently. - Use `clamp()` for padding, min-height, icon size, and text size when the same control appears on desktop and mobile. - Avoid fixed pixel-only widths for primary panels and buttons unless the element is purely decorative. - For modal or framed panels, allow internal scrolling on small screens instead of overflowing the viewport. - Decorative UI should never block content readability; add a dark tint layer when needed. - Avoid non-integer `scale()` hover effects on pixel-framed controls; prefer `translateY`, brightness, or shadow shifts. ## Pixel Rendering Rules - Pixel icons must use point-sampled rendering such as `image-rendering: pixelated`. - Pixel-framed UI should keep consistent slice thickness on both axes; never uniformly squash the whole frame texture. - Any large information plate inside a popup must also be skinned with UI art, not a plain dark rectangle. ## Semantic Mapping Used Now ### Tabs - `character` -> `1_armor` / `1_armor_d` - `adventure` -> `1_weapon` / `1_weapon_d` - `inventory` -> `Hud_icon_inventory` / `Hud_icon_inventory_d` ### World Select - `wuxia` -> `38_sword` - `xianxia` -> `72_magic` ### Shared Chrome - option arrow -> `11_right_arrow` - map header -> `Map_icon_action` - close action -> `1_exit_s` ### UI Shell / Panels - app shell -> `Background_fill` - world selection buttons -> `1_orange_button`, `1_violet_button`(极扁条形图,切片须符合尺寸约束,见「已知问题」) - character selection card -> `pick_hero_frame` + `pick_hero_bg` - tab shell -> `Shop_tab`, `Shop_tab_picked` - section panel -> `Frame_bg_big_2` - story panel -> `Dialogue_frame` - inventory panel -> `Inventory_bg` - stats panel -> `Stats_bar` - choice button -> `Options_bar` - modal shell -> `Popup_window` - map info plate -> `Dialogue_frame` - map node cells -> `Map_frame` - map diagram canvas -> `Frame_bg_big_2` - scene title -> `Title_frame_m` - app / root chrome -> `Background_fill` + light tint(避免整块纯深蓝底) ### Equipment / Inventory - `武器` -> `Icon_Eq_Weapon` - `护甲` -> `Icon_Eq_Chest` - `饰品` -> `Icon_Eq_ring` - `消耗品` -> `12_potion` - `稀有品` -> `68_relic` - `专属品` -> `47_treasure` - `材料` -> `45_crystal` ## State Handling - Selected tab/button: prefer `_pick` or `_picked`. - Disabled/unavailable action: prefer `_d`. - Toggle widgets: prefer `_on` / `_off`. - Size changes must use authored sizes like `_s` / `_xs`; avoid CSS-downscaling a large ornamental asset when a small authored version exists. ## Assets To Avoid By Default These names are ambiguous and should not be used as standards until art is confirmed: - `* copy` - `Avatar_ref` - `map_ref` - `special_button` - `Skill_bar_long_nopt` - `Skill_bar_noColbs` - `Options_button_save_p` - `11_Q_d` ## Review Checklist For New UI - Does the component use `src/uiAssets.ts` instead of ad-hoc file paths? - Does the chosen icon name match the feature meaning? - If the control has selected/disabled states, are those states backed by asset variants? - Is the asset from `UI` for chrome and `Icons` for item semantics? - Is pixel rendering preserved? ## Example ```tsx import { PixelIcon } from './components/PixelIcon'; import { TAB_ICONS } from './uiAssets'; ``` ## Known issues / 已知问题 ### 开局「武侠 / 仙侠」按钮中部发空 / 像透明 **现象**:世界选择页上,`1_orange_button` / `1_violet_button` 套 9-slice 后,中间区域异常,像没画上或透出背景。 **正确原因(切片几何,而非文字冲突)**: 1. **图源尺寸**:`1_orange_button.png` / `1_violet_button.png` 实际为 **125×28** 的横向条(可用设计工具或脚本读 IHDR 确认)。 2. **非法切片**:若 `border-image-slice` 在竖直方向取 `top + bottom >= 高度(28)`,则图源里用于「中间横条」的像素行数为 **0**。此时 `fill` 没有可拉伸的中间带,浏览器无法正确铺满内容区,中间会发空或表现异常。 3. **错误补救**:用 `baseColor` 铺底虽能盖住背景,但**与素材本身的渐变/内描边不一致**,且中间没有原画的边框层次,视觉上「只有一块平色 + 外框」,不符合预期。 **不是**:`PixelIcon` 与 `pixel-world-button__label` 抢层级或颜色把中间「挡住」;二者在内容区内,不会导致 9-slice 中间带消失。 **正确修改方式**: - **先按像素量体裁衣**:保证 `slice.top + slice.bottom < 图高`,`slice.left + slice.right < 图宽`。对 28px 高的条,上下 slice 宜各约 **8~10px**(需与素材圆角/浮雕厚度对齐),留出 **约 8~12px** 高的中间行供纵向拉伸。 - 横条在 UI 里被拉得很高时,中间带主要靠 **纵向 stretch**;`border-image-repeat` 对边可用 `stretch`(条形成品常见),按观感在 `round` / `stretch` 间微调。 - 仅在确认图源中心 **确实透明**(RGBA)且引擎式叠色才是需求时,再考虑 `baseColor` 或换一张中心有底的按钮图。 **维护提醒**:新增 9-slice 配置前,先读 **宽高**,再填 slice,避免 `top+bottom` 或 `left+right` 把中间切片挤没。