Agent Skills › openchamber/openchamber › theme-system

theme-system

GitHub

定义OpenChamber前端UI组件的样式与主题规范,强制使用语义化Token、共享Button和Icon,规定颜色选择逻辑及引用文档加载流程,确保视觉一致性。

.agents/skills/theme-system/SKILL.md openchamber/openchamber

Trigger Scenarios

创建或修改OpenChamber UI组件 调整样式、颜色、按钮或图标 配置主题或视觉状态

Install

npx skills add openchamber/openchamber --skill theme-system -g -y
More Options

Non-standard path

npx skills add https://github.com/openchamber/openchamber/tree/main/.agents/skills/theme-system -g -y

Use without installing

npx skills use openchamber/openchamber@theme-system

指定 Agent (Claude Code)

npx skills add openchamber/openchamber --skill theme-system -a claude-code -g -y

安装 repo 全部 skill

npx skills add openchamber/openchamber --all -g -y

预览 repo 内 skill

npx skills add openchamber/openchamber --list

SKILL.md

Frontmatter
{
    "name": "theme-system",
    "description": "Use when creating or modifying OpenChamber UI components, styling, colors, buttons, visual states, themes, or icons."
}

Theme System

Core Rules

  • Use semantic OpenChamber theme tokens; never hardcode hex colors or generic Tailwind palette colors.
  • Use shared UI primitives before introducing feature-local controls.
  • Use the shared Button; do not create button wrappers such as ButtonSmall or ButtonLarge.
  • Every dropdown-style value-picker trigger takes its chrome from dropdownTriggerVariants in packages/ui/src/components/ui/dropdown-trigger.ts; call sites add layout classes only. Deliberately chrome-less pickers in composers or headers are the exception.
  • Use the sprite-based Icon; never import icons directly from @remixicon/react.
  • Apply hover tokens only to interactive elements.
  • Use status colors only for actual status/feedback.
  • Use selection tokens for selected state and primary tokens for primary actions.

Load References By Task

Task Required reference
Choosing colors/tokens or reviewing styled examples references/tokens-and-examples.md
Adding, converting, storing, or generating icons references/icons.md
Adding built-in or custom themes references/adding-themes.md

Load every matching reference before editing. How a screen should look and behave (depth and borders, which surface class to use, layout stability, sizing, composition, text density) is owned by openchamber-ui-guidance; load it for any visible UI change. User-facing or accessible text must load locale-ui-patterns. Spacing, positioning and directional icons follow rtl-layout, which owns how a layout works in both directions. Settings composition is owned by settings-ui-patterns, which declares theme-system as its one-way companion.

Token Decision

  1. Code display -> syntax.*
  2. Error/warning/success/info -> status.*
  3. Primary CTA -> primary.*
  4. Hover/pressed/focus -> interactive.*
  5. Selected/active state -> interactive.selection*
  6. Background/text/border layer -> surface.* and semantic utility classes

Prefer CSS variables/classes for component styling. Use useThemeSystem() only when an API requires resolved color values.

Button Contract

Use Button from packages/ui/src/components/ui/button.tsx.

Variant Use
default Primary local action
outline Visible secondary action
secondary Soft secondary action
ghost Quiet row/toolbar action
destructive Destructive action
chip Compact selectable option with aria-pressed
link Rare inline text action
Size Use
xs Dense row/list control
sm Compact action
default Standard action
lg Prominent action
icon Icon-only square action

Do not hardcode button height/padding when a size variant exists. Do not recreate selection/destructive styling with ad-hoc classes.

A button that opens a page, panel, or search field is a toggle: while what it opened is on screen it carries aria-pressed and the selection tokens, and the next click closes it. Closing a search field also clears its query, so the list never stays filtered by text nobody can see.

Controls in one row share one height. Every dropdown trigger on desktop is 28px (dropdownTriggerVariants() default, SelectTrigger), in forms and toolbars alike. Toolbars, filter rows and the context panel header use 28px throughout: Button size="sm", <Input inputSize="compact">, and the active-pill tab strip (its default). Form inputs and buttons stay 32px (default). Pick the size variant instead of overriding h-* at the call site to match a neighbour; mobile/touch branches keep their own larger sizes (mobile, touch).

Extensions draw these controls from their own copy, the SDK UI kit in packages/sdk/src/ui. A change to a shared primitive's size, radius, or states (button, input, dropdown trigger, select item, checkbox, tabs, badge) also changes its twin in packages/sdk/src/ui/style.ts in the same change: appearance in GUEST_UI_LOOK_CSS, which the server serves to installed extensions, sizes in UI_SIZE_CSS, which reach an extension when its author rebuilds. Then rebuild the SDK example bundles (packages/sdk/DOCUMENTATION.md).

Keyboard Navigation Contract

  • Menus, selects, and autocomplete pickers with ArrowDown/ArrowUp navigation must also support Ctrl+N/Ctrl+P, including submenus and searchable lists.
  • Keep this behavior in shared components so callers inherit it. Use the keyboard mapping in packages/ui/src/components/ui/dropdown-navigation.ts; feature code must not duplicate key detection.
  • Lists that own their active option or stop keyboard propagation must call the shared navigation helper at their own event boundary. Wrapping a custom list in a dropdown does not guarantee that its navigation events reach the wrapper.
  • Route both key pairs through the same selection logic, preserving disabled-item skipping, boundary or wrap behavior, highlight, and scroll visibility. Consume each navigation event once, only while the menu or picker is active; preserve IME text entry and other modifier chords.
  • Verify Ctrl+N/P alongside arrow keys in the real component, including search-input focus, submenus, and closed state. A key-mapping unit test alone does not verify event propagation or focus behavior.

Icon Contract

import { Icon } from '@/components/icon/Icon';

<Icon name="check" className="size-4" />

Use IconName for icon values stored in arrays, objects, state, or config. Icon has no size prop. Run bun run icons:generate when introducing a sprite name, and never edit sprite.ts manually. Load references/icons.md for the complete workflow.

Animation Contract

Animate only transform and opacity. Use transform: rotate(...), not the individual rotate property. Non-composited properties recalculate style continuously; geometry also triggers layout, and wrappers, will-change, contain, or stepped timing do not remove that cost. Animate only while conveying live information.

Motion that can stay on screen for minutes follows the state it shows:

  • Steady state stands still: connected, failed, degraded, "Unavailable", the provider logo while the agent works, read-aloud playing (its icon turns --primary-text). Show a static icon in the state's color.
  • A wait that can last spins on the shared clock: a running project action, worktree setup or removal, a space being made, CI checks, loading sessions or models, walkthrough generation, an OpenCode update. Render Spinner (components/ui/spinner.tsx): eight bars, four steps a second.
  • A wait that cannot spin blinks: a status dot (6 px, size-1.5), the recording dot, a loading skeleton, the splash logo's glow. Apply useWaitingBlink(active); it dims every other second.
  • Waiting text gets dots: "Thinking", "Waiting for approval". Put BusyDots (components/ui/busy-dots.tsx) after the label; drop any "…" from the string.
  • A spinner that lasts seconds (a button after a click, a quick load, refresh) keeps animate-spin.
  • Hidden stops. A surface kept mounted while hidden wraps its content in PauseClock (components/ui/pause-clock.tsx); the context panel's zones already do. An inert subtree pauses its CSS animations (index.css).

Spinner, useWaitingBlink, BusyDots and live counters (useDurationTickerNow) share one clock (hooks/useDurationTicker.ts): a page draws four frames a second while a spinner shows and one otherwise, however many indicators are on screen. Each tick re-renders the component that calls the hook, so it goes in the indicator itself, never in the page around it (performance-engineering, "Motion That Stays On Screen"). lib/infiniteAnimations.test.ts fails on any new infinite CSS animation outside the short ones it lists. The cost behind these rules is in performance-engineering, "Motion That Stays On Screen".

For any other technique, load performance-engineering and scripts/perf/DOCUMENTATION.md, measure it with bun run profile:animation, and add a fixture variant when needed. This skill owns animation styling; performance-engineering owns performance evidence.

Completion Criteria

  • Animations are limited to transform and opacity, or their cost was measured and accepted.
  • Nothing that can stay on screen for minutes runs an infinite CSS animation: steady states stand still, waits use Spinner, useWaitingBlink or BusyDots.
  • No hardcoded/palette colors were introduced.
  • Buttons use shared variants and sizes.
  • Menus and pickers satisfy the keyboard navigation contract without caller-specific key handling for standard shared components.
  • Icons use Icon/IconName, and generated sprite changes are intentional.
  • Hover, selection, primary, and status semantics are distinct.
  • Light/dark/high-contrast and long-text states remain legible.
  • Every applicable contract and loaded task reference was verified with relevant type-check, visual/runtime validation, and generated-asset checks.

Version History

  • bf977d2 Current 2026-10-19 13:05

    新增关于时钟hook的性能优化说明,明确tick重渲染范围;修复UI spinner和loading指示器的CPU占用问题,通过共享1秒tick停止稳态动画以优化性能。

  • 2471b29 2026-09-09 11:57

    新增键盘导航契约,要求菜单和选择器支持 Ctrl+N/P 快捷键,并统一使用共享导航辅助函数。

  • 2db90f7 2026-08-20 04:55

    新增动画性能契约,明确仅使用transform和opacity以优化性能;细化Dropdown Trigger的尺寸与样式约束。

  • 74b1bd8 2026-07-25 10:37

Same Skill Collection

.agents/skills/changelog-authoring/SKILL.md
.agents/skills/clack-cli-patterns/SKILL.md
.agents/skills/communication-style/SKILL.md
.agents/skills/desktop-shell/SKILL.md
.agents/skills/drag-to-reorder/SKILL.md
.agents/skills/enterprise-boundary/SKILL.md
.agents/skills/isolated-space-boundary/SKILL.md
.agents/skills/locale-ui-patterns/SKILL.md
.agents/skills/openchamber-change-discipline/SKILL.md
.agents/skills/opencode-v2/SKILL.md
.agents/skills/performance-engineering/SKILL.md
.agents/skills/pr-review/SKILL.md
.agents/skills/relay-transport/SKILL.md
.agents/skills/rtl-layout/SKILL.md
.agents/skills/serve-sim/SKILL.md
.agents/skills/settings-ui-patterns/SKILL.md
.agents/skills/sync-state-invariants/SKILL.md
.agents/skills/triage-issues/SKILL.md
.agents/skills/triage-prs/SKILL.md
.agents/skills/ui-api-decoupling/SKILL.md
.agents/skills/update-changelog/SKILL.md
.agents/skills/writing-for-agents/SKILL.md

Metadata

Files
0
Version
bf977d2
Hash
fe8355f7
Indexed
2026-07-25 10:37

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-11-02 06:03
浙ICP备14020137号-1