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

8.0 KiB
Raw Blame History

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

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 铺底虽能盖住背景,但与素材本身的渐变/内描边不一致,且中间没有原画的边框层次,视觉上「只有一块平色 + 外框」,不符合预期。

不是PixelIconpixel-world-button__label 抢层级或颜色把中间「挡住」;二者在内容区内,不会导致 9-slice 中间带消失。

正确修改方式

  • 先按像素量体裁衣:保证 slice.top + slice.bottom < 图高slice.left + slice.right < 图宽。对 28px 高的条,上下 slice 宜各约 810px(需与素材圆角/浮雕厚度对齐),留出 约 812px 高的中间行供纵向拉伸。
  • 横条在 UI 里被拉得很高时,中间带主要靠 纵向 stretchborder-image-repeat 对边可用 stretch(条形成品常见),按观感在 round / stretch 间微调。
  • 仅在确认图源中心 确实透明RGBA且引擎式叠色才是需求时再考虑 baseColor 或换一张中心有底的按钮图。

维护提醒:新增 9-slice 配置前,先读 宽高,再填 slice避免 top+bottomleft+right 把中间切片挤没。