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

theme-system

Skill12k starsChanged yesterdayMITView on GitHub

Use when creating or modifying OpenChamber UI components, styling, colors, buttons, visual states, themes, or icons.

We split this skill into the rules it gives an agent

13 rules. 6 are common, 1 is distinctive, and 6 are very specific to this project.

  • 6

    Common

    Most of the 3 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use semantic OpenChamber theme tokens.

    line 10
  2. Common:

    Use shared UI primitives before introducing feature-local controls.

    line 11
  3. Very specific:

    Every dropdown-style value-picker trigger takes its chrome from dropdownTriggerVariants in …

    line 12
  4. Common:

    Use status colors only for actual status/feedback.

    line 14
  5. Common:

    Use selection tokens for selected state and primary tokens for primary actions.

    line 15
  6. Very specific:

    Prefer CSS variables/classes for component styling.

    line 36
  7. Very specific:

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

    line 40
  8. Common:

    Do not hardcode button height/padding when a size variant exists.

    line 60
  9. Very specific:

    Keep this behavior in shared components so callers inherit it.

    line 71
  10. Distinctive:

    Verify Ctrl+N/P alongside arrow keys in the real component, including search-input focus, submenus, and closed state.

    line 74
  11. Very specific:

    Never import icons directly from @remixicon/react.

    line 84
  12. Common:

    No hardcoded/palette colors were introduced.

    line 109
  13. Common:

    Every applicable contract and loaded task reference was verified with relevant visual/runtime validation and generated-asset checks.

    line 115

“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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.
  • 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.
  • 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

TaskRequired reference
Choosing colors/tokens or reviewing styled examplesreferences/tokens-and-examples.md
Adding, converting, storing, or generating iconsreferences/icons.md
Adding built-in or custom themesreferences/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.

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; do not create wrappers such as ButtonSmall or ButtonLarge.

VariantUse
defaultPrimary local action
outlineVisible secondary action
secondarySoft secondary action
ghostQuiet row/toolbar action
destructiveDestructive action
chipCompact selectable option with aria-pressed
linkRare inline text action
SizeUse
xsDense row/list control
smCompact action
defaultStandard action
lgProminent action
iconIcon-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.

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" />

Never import icons directly from @remixicon/react. 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. 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. Apply useWaitingBlink(active); it dims every other second. The splash logo steps instead: its roof tiers light up one at a time, once a second, through useWaitingPhase.
  • 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 call it in the indicator itself. 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".

Springs are the app's easing. lib/motion/springs.ts turns a spring (perceived duration, bounce) into a CSS linear() easing and writes it to --oc-motion-<name>-duration / -easing: popup for menus, selects and tooltips (oc-popup-motion, which transitions only opacity and transform; never transition-all on a popup), layout for a control easing into a new width. Change a spring in MOTION_SPRINGS and paste its generated values into design-system.css (its test keeps them in step). In development Cmd+Alt+M opens live knobs for them. A control whose width follows its value (a picker) uses useWidthMorph(ref) (lib/motion/useSizeMorph.ts): it watches the content, retargets from where the control stands when a part arrives late, and clips the text while it moves. An open popup whose content changes uses useHeightMorph(ref) on its box the same way; DropdownMenuContent calls it for every menu. Its positioner (base-ui) or bottom-full keeps the anchored edge in place in every painted frame, so the hook never moves the popup itself. These are the accepted size animations: once per content change, on a small control or an open popup.

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 visual/runtime validation and generated-asset checks.

Download now (ZIP)

Trending

More talk lately.about the whole repository

Dec 2025: 0 mentionsJan 2026: 0 mentionsFeb 2026: 0 mentionsMar 2026: 2 mentionsApr 2026: 1 mentionMay 2026: 5 mentionsJun 2026: 0 mentionsJul 2026: 1 mentionAug 2026: 11 mentionsSep 2026: 10 mentionsOct 2026: 31 mentionsNov 2026: 0 mentions
Dec 2025Jun 2026Nov 2026
Mentions a month of the openchamber/openchamber repository that our weekly crawl found. There is no adoption data for this file yet.

Who's talking about openchamber/openchamber

The biggest 5 of 61 mentions linking to the repository that our crawl found. Each line is their own headline.

See all 61

Talked about on GitHub, YouTube, Bluesky, the web and Hacker News (61 times).

On GitHub · 47

42 more on GitHub

On YouTube · 5

On Bluesky · 4

On the web · 4

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.