This commit is contained in:
178
UI_CODING_STANDARD.md
Normal file
178
UI_CODING_STANDARD.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# 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';
|
||||
|
||||
<PixelIcon
|
||||
src={active ? TAB_ICONS.inventory.active : TAB_ICONS.inventory.inactive}
|
||||
className="h-4 w-4"
|
||||
/>
|
||||
```
|
||||
|
||||
## 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` 把中间切片挤没。
|
||||
Reference in New Issue
Block a user