Agent Skills › stella/stella › conventions-ux

conventions-ux

GitHub

规范面向用户的UI组件开发,涵盖品牌视觉、色彩纪律、微交互、图标语义及移动端适配规则,确保界面专业、简洁且体验流畅。

.ai/local-skills/conventions-ux/SKILL.md stella/stella

Trigger Scenarios

构建或修改用户界面组件 前端样式与交互设计审查

Install

npx skills add stella/stella --skill conventions-ux -g -y
More Options

Non-standard path

npx skills add https://github.com/stella/stella/tree/main/.ai/local-skills/conventions-ux -g -y

Use without installing

npx skills use stella/stella@conventions-ux

指定 Agent (Claude Code)

npx skills add stella/stella --skill conventions-ux -a claude-code -g -y

安装 repo 全部 skill

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

预览 repo 内 skill

npx skills add stella/stella --list

SKILL.md

Frontmatter
{
    "name": "conventions-ux",
    "description": "Apply when building or modifying user-facing UI components."
}

UX Conventions

Apply when building or modifying user-facing UI components.

Brand & Visual Identity

Keywords: crystal-clear, subtle, detail-oriented, high-precision. Visual inspiration: glass-like surfaces. Clean, translucent, precise.

Default palette: clean neutral grays on white. Accent palettes (Nord, Flexoki) available as user preferences; brand stays monochrome with the Stella § mark.

Brand colours (reference, not for hard-coding):

Name Hex Usage
Black #000 Primary text, logo mark
Soft Blue #cae1fb Illustration/accent background
Pale Blue #e2f6fd Light tint surfaces
Blue Accent #59a1d4 Accent in marketing material

Use semantic tokens (bg-muted, text-foreground, border) rather than raw colour values.

Colour Discipline

  • One accent per surface. A screen region gets at most one saturated accent; everything else is neutral or semantic status colours. Competing accents create visual noise.
  • No pure #000000 backgrounds in the app chrome. Use the --background token (neutral-950 blend in dark mode) so surfaces retain subtle warmth and depth.
  • Saturation ceiling: accents should stay under 80% saturation in oklch. Neon, oversaturated gradients, and glowing outer shadows are off-limits; they read as consumer/gaming, not professional.

Core Beliefs

  • Users notice the little things
  • Every interaction should feel smooth
  • Good UX is invisible; it just works
  • Minimum clutter; keep users in the flow

Micro-interactions

Small, almost invisible touches. Linear is a good reference.

  • Number/count transitions: subtle fade (~200ms). No flashy slot-machine animations.
  • State transitions (loading, success, error) should feel continuous, not jarring.
  • When in doubt, simpler is better, or skip it.

Icon Semantics

  • Every icon comes from @stll/ui/icons (packages/ui/src/icons.ts), the only module that imports lucide-react (no-direct-lucide-import enforces it). Reuse an entry first; a product concept gets a semantic entry with a one-line reason, and bun scripts/codemod-icons.ts rewrites any direct import.
  • A skill or saved prompt is SkillIcon (the open book) everywhere it appears: menus, composer chip, sent message, landing.
  • AiActionIcon (the magic wand) is for AI actions: rewrite, refine, redraft, search query refinement, prompt editing, AI rename. Do not use generic sparkles for this function, and never for a skill.
  • Never draw scales of justice or a gavel (lucide Scale, Gavel). Legal cliché is decoration every product in the category already uses; name the concept instead — a court is Landmark, a decision FileText (Library for a collection of them), and the case-law section is CaseLawIcon. no-legal-cliche-glyph enforces it.

Review chrome

Review-like surfaces (document review, playbook findings, AI suggestions, skill proposals, version history, comments) render their status badges, severity dots, accept/reject controls, comment cards, out-of-date notices and diff text through @stll/ui/review-*. Do not mint a local chip class map, a local <ins>/<del> style, or a bespoke accept/reject button pair: map the surface's vocabulary onto ReviewStatusTone / ReviewSeverityLevel with a total as const satisfies Record<...> map and pass it in.

Editors

Two editors, two jobs, and they never swap:

  • Document model → Folio. Anything that is a document (.docx, drafts, templates, redlines, tracked changes) is edited in Folio.
  • Source text → the hybrid markdown editor. Skills, prompts and knowledge files are plain source; they are edited in the hybrid markdown editor.

Never route one through the other: a document forced through the markdown editor loses its formatting model, and source text forced through Folio gains one it does not have.

Reduce Visual Noise

  • Secondary information (counts, metadata) subtle by default. Use opacity transitions to reveal details on hover.
  • Don't compete for attention; let the content speak.

Typography Polish

  • -webkit-font-smoothing: antialiased on macOS for crisper text on light backgrounds. Apply globally in the base stylesheet.
  • font-variant-numeric: tabular-nums on any element whose number changes dynamically (counters, timers, tables with numeric columns). Prevents layout shift.
  • text-wrap: balance on headings and short text blocks to distribute lines evenly. text-wrap: pretty on body/paragraph text to avoid orphans.

Surfaces & Depth

  • Concentric border radius: when nesting rounded elements, outer radius = inner radius + padding between them. Hard-coding both to the same value produces a visual mismatch.
  • Shadows over borders: prefer layered semi-transparent box-shadow for depth and separation. Reserve border for semantic boundaries (inputs, dividers), not for creating depth.
  • The chrome owns the divider under the breadcrumb bar. A page root, or a nested layout's first pane, never draws its own top border (border-t, border-y, border) on that line; it stacks on the header's border-b as one 2px rule. The shell's content slot (WorkspaceShell) strips the top border off its first child, and route smoke fails any route that still paints one there (findDoubledChromeDividers, apps/web/e2e/helpers/chrome-divider.ts).
  • Image outlines: for images on white/light backgrounds, add a subtle outline: 1px solid rgb(0 0 0 / 0.06) to define the edge without a heavy border.
  • App chrome scrolls through ScrollArea (@stll/ui/scroll-area), not a raw overflow-auto/overflow-scroll utility, so every scrollbar in the shell matches; viewportRef hands the scrolling element to a virtualizer or scroll restoration. Textareas, <pre> blocks, editor canvases and wide tables keep their native scroller. no-raw-overflow-scroll enforces it.

Interactions & Animations

  • Never use transition: all. Always specify exact properties (e.g., transition: opacity 150ms, transform 150ms). all triggers unnecessary repaints and can animate properties you didn't intend. (enforced by no-layout-motion-classes/no-layout-motion-classes and stylelint)
  • GPU-friendly properties only. Animate transform and opacity exclusively. Do not animate width, height, top, left, padding, or margin; these trigger layout recalc. Use scale/translate instead. One exemption: a surface that changes its own box (a disclosure panel or accordion, a collapsible rail, a popup positioner repositioning on collision, a progress bar) may transition the box property it owns, because a transform moves the paint but not what siblings lay out against. Each retained layout utility is listed in the rule's allowance with its file and reason. (the layout-motion prohibition is enforced by no-layout-motion-classes/no-layout-motion-classes and stylelint; existing paint-property utilities and CSS declarations are decrease-only ratchets)
  • Interruptible animations: use CSS transition for interactive state changes (hover, press) so the browser can interrupt mid-animation. Reserve @keyframes for staged sequences (page load, modals).
  • Frequency decides whether it animates. What people repeat many times a day (keyboard shortcuts, command palette open/close, list keyboard navigation) gets no animation; occasional surfaces (dialogs, sheets, toasts) get the standard one.
  • Easing: entering and exiting UI uses ease-out, movement on screen ease-in-out; both are strong curves in the theme and ease-out is the default. Never ease-in for UI.
  • Duration ≤ 300ms: press 100–160ms, tooltip or small popover 125–200ms, dropdown 150–250ms, dialog or sheet up to 300ms; exits run faster than enters. Decorative or teaching motion is listed in the rule's allowance with a reason.
  • Never enter from scale-0: start near full size (scale-95 to scale-98) with opacity 0. Popovers, menus and tooltips scale from their trigger (origin-(--transform-origin)); dialogs stay centred. (easing, duration and scale enforced by no-layout-motion-classes)
  • Tooltips open instantly once one is open: the app-level TooltipProvider groups them; do not mount another provider.
  • Pressable controls give feedback on press. The shared Button already scales slightly while enabled; do not add per-call-site press effects.
  • Staggered list entry: when a list or grid appears (page load, filter change), cascade items with a small stagger delay (30–60ms per item, capped at ~8 items) so the group reads as a cohesive reveal rather than a sudden block.
  • Optical alignment over geometric: icons inside circles, play-button triangles, and asymmetric glyphs should be nudged visually until they look centred, even if that means offset from the geometric centre.

Control States

  • Every control answers every state: hover, pressed, focus-visible, disabled and, where it loads, pending. A press with no visible response reads as a missed click.
  • Show where people are. The current item in navigation and breadcrumbs is styled as current and carries aria-current. A tab shows its selected state through aria-selected, which @stll/ui/tabs supplies; never add aria-current to it.
  • No native alert, confirm or prompt. Confirm a destructive action with @stll/ui/destructive-confirm-dialog, report an outcome with @stll/ui/toast, and put a field error next to the field.
  • A dialog only when the work has to stop. A small edit happens inline; a side task that needs the page behind it opens in a @stll/ui/sheet.
  • Hidden means out of reach. Content hidden from everyone (a collapsed panel, an inactive rail) leaves the tab order (inert, hidden, or unmounted); aria-hidden alone still lets focus land inside. The control that reveals it stays reachable, keeps an accessible name, and reports aria-expanded. A control that is visually hidden until focus reveals it (a skip link, the data table's keyboard row action) stays in the tab order by design.

Lists, Settings Pages & Search

  • Inventories of named things are grouped lists, not tables. Connections, sessions, keys, integrations: use @stll/ui/list. A ListGroup has a header (title + ListGroupCount + one quiet line + an optional quiet trailing action) over one List surface of hairline-divided rows. Row anatomy: 32px ListItemMedia tile, a truncating title, one clamped secondary line, then trailing status or actions. Use a Table only when people compare values across columns (sorting, numbers, dates side by side).
  • Nothing scrolls sideways. Free text (descriptions, scope lists, notes) never sits in a whitespace-nowrap cell or row. Clamp it (line-clamp-1/-2 with min-w-0 on the column), and collapse a long list into a count that opens a popover ("15 permissions"). The trailing actions stay on screen at every width.
  • Status is a dot plus a label (ListItemStatus with a tone). The label carries the meaning; colour is never the only signal.
  • Anything people look up by name gets search. Use @stll/ui/search-field: an instant local filter, case- and accent-insensitive, and Escape clears it. While a query is active, hide setup and help content, show match counts in the group headers, and when nothing matches, show one line plus the next action.
  • Show things where people look for them. When an entity is managed elsewhere but people expect it on another page (integrations live in Tools, yet people look for them under Connections), list it there too as a read-mostly row that links to its one source of truth. Don't copy the management UI.
  • Write for a lawyer who has never heard of MCP. The first path names the tools people already use (Claude, ChatGPT) and asks for at most one thing to copy. Protocol names, server variants, CLI commands and developer tools (Cursor, Codex) live behind a folded "Developer access" section, never in the default view.
  • Setup steps have one source. Product copy, onboarding and copied agent instructions point to the same guide (one docs page, one shared component such as AssistantSetup); they never carry their own step lists that can drift.
  • One-time setup is rows with copy buttons, placed below the live state: server URLs, CLI commands, keys. No stacks of paragraphs and read-only inputs. Copy confirms in place (the icon turns into a check).
  • Pick the primitive whose anatomy fits; don't bend one. Squeezing FramePanel to p-1 so a heading fits inside it produced text flush against the border. If a primitive needs its padding overridden to work, it is the wrong one.

Empty States & Inline Help

  • An empty state earns one line, and that line is an action (the gesture that fills it), not a definition. Teach "Select text → right-click → Make field", not what a field is. Conceptual education lives in tooltips or docs, never as permanent panel prose.
  • Full-width buttons are reserved for a surface's single primary action. Structural or secondary adds use quiet ghost affordances (small +, text button).
  • Side-panel sections with zero items collapse to a single row (heading + count + add affordance) by default; they expand on demand or when they gain content.

Loading States

  • Skeleton loaders must match the real layout. The skeleton's dimensions, spacing, and column count should mirror the content it replaces so the page doesn't shift on load.
  • Inline spinners over full-page loaders. Scope loading indicators to the region that's actually waiting. A full-page spinner for a sidebar fetch is disorienting.
  • Render the destination's structure, not a logo. When a route loads, show its real shape (table chrome with header and columns, toolbar, section cards) with the values shimmering in. The centered glowing logo (DefaultPendingComponent) is the last-resort fallback, not the default. Give each route its own pendingComponent so the route you navigate to picks its own shape.
  • Shimmer only what you do not know yet. Chrome that needs no data (the logo, primary nav icons, rail toggles, fixed icons) renders for real in a skeleton, taken from the same definitions the real chrome uses; only data- or locale-dependent parts (labels, lists, the account, the page body) shimmer. A grey box where the stella logo belongs reads as broken.
  • Skeletons must be structurally drift-proof. Generate the skeleton from the same source as the real UI, never a hand-copied parallel tree. For tables, render the header, rows, and skeleton from one column model (@stll/ui table plus the shared TableSkeletonRows helper) so adding or reordering a column moves all three at once. Where the page chrome does not need the data, move the Suspense boundary inward (render the real chrome, suspend only the leaves). For scaffolds that cannot share a source, mirror the real layout faithfully.

Viewport & Responsive

  • Use min-h-dvh, not h-screen. h-screen and vh ignore the mobile browser chrome and cause scroll/overlap bugs. dvh adapts to the dynamic viewport; use svh for a length that must not move while the toolbar collapses. (enforced by no-layout-motion-classes/no-layout-motion-classes and stylelint)

Mobile

  • A surface fixed to a screen edge pads that edge with the --safe-area-* properties (@stll/ui lib/safe-area.ts); shared sheets, dialogs, toasts and the shell already do.
  • A new interactive primitive gets select-none and its own pressed state; never select-none on text people may copy.

Hit Areas

  • Stella targets a minimum 44×44 CSS-pixel touch area for interactive controls. If the visible element is smaller, extend its hit area without changing the visual size or colliding with adjacent controls.
  • Use native interactive elements and preserve keyboard activation, focus order, accessible names, and visible focus. A clickable div with ARIA is not a substitute for a button or link.
  • Respect reduced-motion preferences. Meaning and completion feedback must not depend on animation, hover, colour alone, or pointer precision.

Right-to-Left (RTL) & Bidirectional

Stella ships an Arabic (RTL) UI; every surface must mirror correctly and stay locale-aware. Test new UI in ar (not just en) before shipping — flipping documentElement.dir is not enough on its own.

  • Logical properties only. Use ps-/pe-, ms-/me-, start-/end-, border-s/-e, text-start/-end — never physical pl-/pr-, ml-/mr-, left-/right-, text-left/right, border-l/-r, rounded-l/-r. Physical directional classes do not flip under dir="rtl". Enforced by the no-physical-properties oxlint rule.
  • Mirror direction, not identity. Render directional glyphs (chevrons, arrows, back/forward, next/prev, breadcrumb separators, pagination, drawer toggles) through <DirectionalIcon icon={...} /> (@stll/ui), which centralizes the rtl:-scale-x-100 mirror. Do NOT mirror brand marks/logos, the media play triangle, checkmarks, or anything whose meaning is orientation-free. For disclosure chevrons that rotate-90 on expand, pass flip={!isExpanded} so the mirror applies only while collapsed — an always-on mirror composes with the rotation and points the expanded state the wrong way.
  • Format through the central locale. Route all numbers, dates, and relative times through useFormatter()/getFormatter() (or pass getFormattingLocale()); never a bare Intl.* / .toLocale*String() with the base lang or no locale — that drops the user's numbering-system preference (e.g. Eastern Arabic-Indic digits). Enforced by the no-raw-locale-format oxlint rule.
  • Isolate embedded Latin & numerals. Wrap Latin runs inside RTL text (case numbers, ECLI, citations, file names, emails) in <bdi> so bidi reordering doesn't scramble neighbouring punctuation. Use dir="auto" on free-text user inputs; keep tel/email/url and code fields LTR.
  • Panels mirror as a unit. A panel on the inline-end in LTR (right) docks to the inline-start in RTL (left); its internal rail + content keep their relative order automatically via flex + logical props — never pin with physical right-0/left-0.
  • Document surfaces stay LTR-based. The editor/document canvas (folio) keeps an LTR base direction even under an RTL UI; only the app chrome mirrors. Brand wordmark/logo is never mirrored or transliterated.
  • No untranslated LTR islands. Don't leave English strings (suggested prompts, labels, empty-state copy) inside an otherwise-Arabic surface; route them through i18n. A Latin island reads as "unfinished," not "global."

Look at the Render

Lint and tests prove nothing banned was written; they say nothing about whether the screen is good. Before calling UI done, capture it with bun run agent:drive (see the dev skill): snap <route> at the default desktop size and again with --viewport 390x844, then a run flow that switches to ar. Judge the screenshots, not the code: you wrote the code, so you read your intentions into it. A problem visible in a screenshot is real whatever the code says.

Make each pass on its own:

  1. One second. Glance and look away. You should have seen one thing: the content the screen exists for. "A page" means nothing leads.
  2. Squint. Text blurs into grey bars. The screen should reduce to a few masses of clearly different weight, not many equal ones.
  3. Edges. Count the distinct left (inline-start) edges of text blocks. Two or three, reused, is composed; more means things were placed one at a time.
  4. Space. Related things sit close, unrelated things far. Any empty region is either deliberate or a hole beside a short column or a removed element.
  5. Accent. Name where the accent appears. On the primary action and perhaps one other thing it means "here"; in ten places it means nothing.
  6. States. Empty, loading, error, long content and many items: each one shot, each one designed (see Empty States, Loading States).
  7. Collisions. Nothing overlaps text or a control, nothing is clipped, nothing scrolls sideways, at either width or direction.

Write the findings down the way a designer would tell a colleague: "the row's secondary line starts 8px left of its title", not "could be more polished". Fix in this order, because later items cannot rescue earlier ones: hierarchy (one dominant thing per region), composition (edges, spacing, holes), typography, colour, detail (states, focus, alignment), motion. Re-shoot and look again; stop after three rounds and say plainly what still falls short.

Anti-Patterns

Explicit list of patterns that AI agents tend to generate and that do not belong in a professional legal workspace. Each began as a reasonable idea and became a tell by being produced by default, without a reason.

Removing a tell is half the job. A screen stripped of ornament with nothing in its place reads as unfinished; fill the space with the content itself (larger, complete, specific), not with another ornament.

  • No emojis in the product UI. Use Lucide icons or semantic status colours instead.
  • No "AI aesthetic" chrome: purple/blue neon glows, pulsing gradient borders, holographic effects. Stella's AI features should feel like native tools, not a tech demo.
  • No centred hero sections in application views. Heroes belong on marketing pages; app screens use content-first layouts.
  • No generic three-equal-cards grids. If content genuinely comes in threes, fine; but don't default to grid-cols-3 as the go-to layout. Prefer asymmetric grids, two-column splits, or list views that match the data shape.
  • No filler copy. Never use "Lorem ipsum", "John Doe", "Acme Corp", "99.99%", or similar placeholder text in committed UI. Use realistic legal-domain examples (matter names, party names, deadlines) or leave the slot empty with a proper empty state.
  • No AI copywriting clichés: "Elevate", "Seamless", "Unleash", "Revolutionize", "Supercharge". Write plain, specific microcopy.
  • No generic copy shapes. No "not just X, it's Y"; no "Get started" or "Learn more" labels (name what the button does); no exclamation marks in success messages; no "Oops!" errors (say what failed and what to do next); English headings and labels use sentence case, not Title Case, and each translation follows its own locale's capitalization.
  • No decorative scroll indicators (bouncing chevrons, "Scroll to explore" text). If content scrolls, the scrollbar is sufficient affordance.
  • No custom cursors or pointer overrides beyond the standard pointer for interactive elements.
  • No restyling @stll/ui components through className. A component owns its spacing and typography (shadcn(no-restyle)): pick a variant or size, and add one to the component when the design needs a treatment none provides. Margin around it and gap on its parent stay yours.

Version History

  • 0f3714b Current 2026-10-19 03:37

    新增移动端浏览器行为、安全区域处理、主题色配置、滚动限制、动效规范及按钮按压反馈等细节要求。

  • e41bf51 2026-09-22 09:03

    新增 lint 规则以约束动画和视口类,强化滚动溢出及导入常量限制,并更新设计系统基线配置。

  • 9d34a6e 2026-08-28 06:22

    新增审查界面(Review chrome)组件规范,明确文档与源码编辑器的使用场景划分,统一审查相关的状态徽章、操作控件及差异文本渲染逻辑。

  • dd81665 2026-08-16 07:09
  • 85792bd 2026-07-24 16:12

Same Skill Collection

.agents/skills/click-around/SKILL.md
.agents/skills/conventions-ai/SKILL.md
.agents/skills/conventions-db/SKILL.md
.agents/skills/conventions-i18n/SKILL.md
.agents/skills/conventions-ingestion/SKILL.md
.agents/skills/conventions-mcp/SKILL.md
.agents/skills/conventions-perf/SKILL.md
.agents/skills/conventions-scale/SKILL.md
.agents/skills/conventions-security/SKILL.md
.agents/skills/conventions-use-effect/SKILL.md
.agents/skills/conventions-ux/SKILL.md
.agents/skills/dev/SKILL.md
.agents/skills/finish-pr/SKILL.md
.agents/skills/new-handler/SKILL.md
.agents/skills/open-pr/SKILL.md
.agents/skills/plan/SKILL.md
.agents/skills/product-deep-think/SKILL.md
.agents/skills/product-think/SKILL.md
.agents/skills/rabbit-round/SKILL.md
.agents/skills/regression-hunt/SKILL.md
.agents/skills/security-audit/SKILL.md
.agents/skills/update-deps/SKILL.md
.ai/local-skills/click-around/SKILL.md
.ai/local-skills/conventions-ai/SKILL.md
.ai/local-skills/conventions-db/SKILL.md
.ai/local-skills/conventions-i18n/SKILL.md
.ai/local-skills/conventions-ingestion/SKILL.md
.ai/local-skills/conventions-mcp/SKILL.md
.ai/local-skills/conventions-perf/SKILL.md
.ai/local-skills/conventions-scale/SKILL.md
.ai/local-skills/conventions-security/SKILL.md
.ai/local-skills/conventions-use-effect/SKILL.md
.ai/local-skills/dev/SKILL.md
.ai/local-skills/new-handler/SKILL.md
.ai/local-skills/open-pr/SKILL.md
.ai/local-skills/plan/SKILL.md
.ai/local-skills/product-deep-think/SKILL.md
.ai/local-skills/rabbit-round/SKILL.md
.ai/local-skills/security-audit/SKILL.md
.ai/local-skills/update-deps/SKILL.md
.claude/skills/conventions-ai/SKILL.md
.claude/skills/conventions-db/SKILL.md
.claude/skills/conventions-i18n/SKILL.md
.claude/skills/conventions-ingestion/SKILL.md
.claude/skills/conventions-mcp/SKILL.md
.claude/skills/conventions-perf/SKILL.md
.claude/skills/conventions-scale/SKILL.md
.claude/skills/conventions-security/SKILL.md
.claude/skills/conventions-use-effect/SKILL.md

Metadata

Files
0
Version
0f3714b
Hash
bc8c4f4f
Indexed
2026-07-24 16:12

홈 - 위키
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-10-19 21:26
浙ICP备14020137号-1