Files
Genarrative/UI_CODING_STANDARD.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

179 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 宜各约 **810px**(需与素材圆角/浮雕厚度对齐),留出 **约 812px** 高的中间行供纵向拉伸。
- 横条在 UI 里被拉得很高时,中间带主要靠 **纵向 stretch**`border-image-repeat` 对边可用 `stretch`(条形成品常见),按观感在 `round` / `stretch` 间微调。
- 仅在确认图源中心 **确实透明**RGBA且引擎式叠色才是需求时再考虑 `baseColor` 或换一张中心有底的按钮图。
**维护提醒**:新增 9-slice 配置前,先读 **宽高**,再填 slice避免 `top+bottom``left+right` 把中间切片挤没。