# ran — full documentation corpus # https://ran.chaxus.com • auto-generated at build time ================================================================ # https://ran.chaxus.com/design/guide ================================================================ # Ran Design A one-page checklist for anyone, person or coding agent, building the interface of a web product. Use it before you start, while you build, and before you hand a change back. It assumes the product already has tokens and components; it is about using them well. **MUST** marks a defect a user can observe, and nothing else is a MUST. **SHOULD** is a starting point, not a limit: depart from it whenever the work is better for it, and say why. Items marked **(checked)** are enforced by a test in this repository; the table at the end says which one. Everything else needs your eyes in a real browser. ranui's [implementation adapter](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/adapters/DESIGN.md) maps these items to ranui's tokens, stacking tiers and component specifics. ## Before you start Answer three questions before you change what a page shows; for a page or region you are building or restructuring, write the answers down: 1. What does the reader need to see on arrival? 2. What else must be visible for that to make sense? 3. What do they do next? A page has one primary model and one primary action per region. Choose the layout from the reader's question: compare → table, recognise and open → list, one object's state → sectioned detail, stages → step flow, what happened → timeline, continuous text → document. Put identity and status first, the primary action in the title region, and feedback beside the action that caused it. Count how hard each choice is, not how many clicks it takes: several steps that each need no thought beat one crowded screen where every option does. For a new page or region, compare two or three directions side by side in one place (a toggle, a canvas) before committing to one, and vary what is actually undecided: the layout, the interaction, the copy or the look. Name the surface's mode, because it decides how much expression the page can spend. A tool (dashboards, settings, editors) and a document (docs, articles, changelogs) spend it on precision and restraint, in patterns the reader already knows; a page that persuades (a landing or pricing page) can commit to a bold point of view, carried by one signature idea it will be remembered for rather than several effects competing for the same attention. The mode comes from the surface, not the product: a tool's landing page persuades, its documentation reads. ## The five-second test From Steve Krug's _Don't Make Me Think_: show the first screen to someone for five seconds, hide it, and ask what the page is, who it is for and what they would do next. If they cannot answer, the hierarchy has failed, whatever the details look like. - The page name is a single, visible heading on the first screen at 320px and at desktop width. **(checked)** - One thing leads. Squint: the screen should reduce to two or three masses of clearly different size. - The primary action is a real button or link on the first screen, named for what it does. ## The trunk test Also from Krug: drop a reader on any page, as if they had been carried there in a car boot. They MUST be able to answer, without scrolling: - **Which site is this?** A site identity that links home. **(checked)** - **Which page am I on?** One page heading, matching the link they followed. **(checked: one heading)** - **What are the main sections?** The primary navigation, in the header or one tap away in a drawer. **(checked)** - **Where am I in them?** The current section and page are marked, not only by colour. **(checked: `aria-current`)** - **How can I search?** Search is visible or one tap away where the site has it. **(checked)** ## Visual language - Emphasis is a budget: colour, weight, badges and elevation are rare, so they mean something. - Reach for spacing, alignment and weight before a border, fill or shadow. Every remaining boundary has a job you can name. - Spacing comes from a 4px scale, and a smaller gap means a closer relationship: 4px inside one control, 8px between related controls, 16px between groups, 24–32px between regions. Hidden content leaves no gap. **(checked: off-scale spacing)** - Colours come from tokens and work in both themes; state is never shown by colour alone. **(checked: raw colours and light-only fallbacks)** - MUST: text meets WCAG AA contrast (4.5:1, large text 3:1) on the real composition, in both themes. The first paint already follows the reader's theme; nothing flashes light before going dark. - Nested corners use **inner radius = outer radius − inset**. When that leaves 2px or less, give the inner shape its own radius, or remove the inner box. ![A card with radius 16, an inset panel 8px inside it with radius 8, and a button 4px inside that with radius 4, their corner arcs sharing one centre; beside it, an inner panel that keeps radius 16 makes the band swell from 8px on the edges to about 11px at the corner](./assets/radius-nesting.svg) - Name the alignment spines, and put headings, rows, actions and empty states on them. Follow the left edges of the text blocks down the page: two or three positions, reused on purpose. ![Three vertical alignment spines through a page: navigation, content and nested form labels, with headings, rows, actions and the empty state all sitting on their declared spine](./assets/alignment-spines.svg) - Use few type sizes, far apart. Body text keeps a measure of about 45–75 characters and a leading of at least 1.5; text a reader needs is never below 12px. **(checked)** - A heading has more space above it than below, so it binds to what it introduces. **(checked)** Headings wrap with `text-wrap: balance`, paragraphs with `text-wrap: pretty`. - The surfaces the browser draws still carry the design: text selection, the caret, focus rings, scrollbars and the numerals in tables (`tabular-nums`). Theme them from the tokens. - Mixed scripts: put the Latin face first and the CJK or Arabic fallback after it, mark each block's `lang`, and give Chinese, Japanese or Korean running text more leading than Latin (about 1.7–1.8). A Latin serif has no Han glyphs; pair it with a CJK serif. - Decoration is a decision, not a default. When an eyebrow label, a gradient, a glow or a card around everything appears, check that it does a job on this page; remove it if it only fills space. ## Layout - MUST: no horizontal page scroll at 320px; wide tables and code scroll inside their own box. **(checked)** - MUST: nothing shifts on load, on a status change, or when a late component registers. Reserve the box for anything that loads or appears later. **(checked: shift on load, Chromium)** - MUST: text, headings, values and navigation are readable on first paint, without script and without animation. **(checked on the home page)** - A component's box contains everything it paints. Rotated, fanned or offset parts stay inside it. - MUST: text and values are never clipped. If something must truncate, the full value stays reachable (a tooltip, an expanded row, the detail page). - Actions stay reachable without hover, and at every width. Anything clickable looks clickable at rest: touch has no hover to reveal it. ## Interaction - MUST: every action works with keyboard, touch and pointer. Focus is always visible, follows reading order, and returns to the trigger when an overlay closes. **(checked: visible focus on the first tab stops of key pages)** - MUST: touch and pointer targets are at least 24×24px, or spaced so a 24px circle around each does not touch its neighbours (WCAG 2.5.8). Links inside running text are exempt. **(checked)** - MUST: input is never lost. Drafts, focus, caret and reading position survive updates, failures and animation. Do not replace an input someone is typing in. - Accept input the way people type it (spaces in a phone number, a pasted value with a trailing space) rather than rejecting it, and ask only for what the task needs. - Validation sits on the field; one activation sends once; success appears only after it is confirmed; a failure keeps every entry and says how to recover. - Each async result is tied to the request that produced it, so a late answer never overwrites a newer one, and a background update never moves focus. - A modal is for a short confirmation or one answer the task cannot continue without, not a place to navigate; transient context belongs in a popover, peer views of one object in tabs, and a second task on its own page. - A modal dialog keeps focus inside it until it closes. Enter never submits while an input method is still composing text. - Selection looks different from hover. A link navigates; a button changes state. ## Motion ![Three position-over-time curves: entering decelerates over 250–400ms and settles on the target without passing it; leaving accelerates over 150–200ms; a third curve, marked as one to avoid, overshoots the target and comes back](./assets/motion-curves.svg) - Feedback (hover, focus, press) is 0–100ms. Entering is 250–400ms and decelerates without overshoot; leaving is 150–200ms and accelerates. Theme switches are not animated. - Controls and content settle without bounce or elastic easing; a page that persuades may spend one expressive moment. - Prefer `transform` and `opacity`; neighbours make room by transform (FLIP). Never animate layout (`height`, `padding`, `max-height`, `top`, `left`), which reflows the page every frame, and never use `transition: all`. - MUST: under reduced motion nothing travels or keeps running; at most a short fade. **(checked)** - Motion is interruptible and reverses cleanly. Watch it as a recording, not a still. ## Words From Krug again: remove half the words, then half of what is left. - Make things self-evident before making them self-explanatory: name them the way readers already do, and follow conventions unless the new way is clearly better. A clever name costs a moment of thought every time it is read. - Name actions for what they do ("Send invite", not "Submit"). No welcome text or instructions that restate the layout. - Keep what adds a decision, constraint or consequence: accessible names, field labels, units, errors and risk disclosures. - Errors say what happened and what to do next, beside the thing that failed. - Don't fill space with invented content: sections, stats, icons or copy nobody asked for. An area that looks empty is a layout problem, not a content one. - Say the product's own thing. Generic marketing words (empower, seamless, next-generation) and dashes used for rhythm read as copy nobody wrote. - Sentence case. Try long text, large numbers and every locale the product ships. ## Verifying a change - Open the change in a real browser, in both themes, at 320px and at desktop width, and walk it with the keyboard. Exercise the states it touched: hover, focus, disabled, loading, empty, error. - Judge from screenshots, not from the code you wrote: you will read your intentions into the code. - Run the five-second and trunk tests on any page whose structure changed. - Report what you observed, what you only inferred and what you could not check. Do not call your own review acceptance. - Watching three people try the task finds the worst problems faster than any review. Fix the worst first, with the smallest change that removes it. - Rank findings so the reader fixes the right thing first: **P0** blocks use (lost input, unreadable text, an unreachable action), **P1** breaks a MUST, **P2** departs from a SHOULD without a reason, **P3** is polish. - Write each fix as values, not adjectives: "48px to `clamp(40px, 6vw, 72px)`", not "bigger". ## What the checks cover | Item | Check | | -------------------------------------------------------- | ----------------------------------------------------- | | Raw colours, light-only fallbacks, off-scale spacing | `verify:design` (static, configured packages) | | Measure, leading, text size, heading spacing | `packages/docs/test/browser/design-checklist.spec.ts` | | No horizontal scroll; navigation reachable; still motion | `packages/docs/test/browser/site-layout.spec.ts` | | Five-second heading, trunk test, targets, focus, shift | `packages/docs/test/browser/design-checklist.spec.ts` | | Readable without script or entrance motion | `packages/docs/test/browser/layout-audit.spec.ts` | ================================================================ # https://ran.chaxus.com/cn/agents/ ================================================================ --- title: Agents --- # Agents 一个 ran 插件提供三个 skill:design 指导产品设计,ranui 指导组件使用,ranuts 指导工具选择。 [design](../design/) · [ranui](../ranui/) · [ranuts](../ranuts/) 从 GitHub 安装插件。 ## Codex ```sh codex plugin marketplace add chaxus/ran codex plugin add ran@ran ``` ## Claude Code ```sh claude plugin marketplace add chaxus/ran claude plugin install ran@ran ``` 新建会话后,Codex 使用 `$design`、`$ranui` 或 `$ranuts`;Claude Code 使用 `/ran:design`、`/ran:ranui` 或 `/ran:ranuts`。 想试用本地克隆的版本,就在仓库根目录执行市场命令,把 `chaxus/ran` 换成 `.`。 [plugins/ran](https://github.com/chaxus/ran/tree/main/plugins/ran) ================================================================ # https://ran.chaxus.com/cn/design/ ================================================================ --- title: Ran Design --- # Ran Design 一页设计检查清单,用于构建产品界面,不依赖特定组件库。 开工前、开发中、交付前都可以对照。它包含 Steve Krug 的五秒测试和 trunk test,并标出了由浏览器测试自动检查的条目。 - [设计检查清单](/cn/src/design/guide) [ranui](../ranui/) · [ranuts](../ranuts/) · [Agents](../agents/) ================================================================ # https://ran.chaxus.com/cn/ranui/api ================================================================ --- title: ranui 元素 API description: 'ranui 的全部自定义元素 —— 41 个元素的属性、属性值、事件、插槽与 ::part() 名称,均从源码提取。' --- # ranui 元素 API(自动生成) 由 `pnpm -F ranui doc:api` 从组件源码自动生成,因此不会与实际发布的代码脱节:逐个元素 列出属性(attribute)、带类型的属性值(property)、事件(含 `detail` 结构与派发选项)、 插槽与 `::part()` 名称。描述直接提取自源码 JSDoc,因此保持英文。 每个元素暴露的 CSS 变量见 [style-tokens-public.md](https://github.com/chaxus/ran/blob/main/packages/ranui/docs/style-tokens-public.md);如何在其中取舍见 [设计系统](/cn/src/ranui/design-system/)与[设计规范](/cn/src/ranui/design-guides/)。 单个元素的用法说明在侧边栏各自的页面里,这里是一次性列全的完整接口。 每个事件都标注了它的派发选项:`bubbles`(冒泡)、`composed`(可穿过 Shadow 边界)、 `cancelable`(`preventDefault()` 可否决)。**`element-only`** 表示三者皆无——在祖先节点上 做事件委托永远收不到它,请把监听绑在元素本身上。 **共 41 个自定义元素。** ## `` 源码: `components/attachments/index.ts` - **属性(attribute)**: `sheet: string` - **属性值(property)**: - `accept: string` — Comma-separated types or extensions, in the form `` takes. - `attachments: readonly Attachment[]` — The staged files, in the order they arrived. - `files: File[]` — Just the files, for building a request body. - `maxCount: number` — Most files that may be staged at once; unlimited when unset. - `maxSize: number` — Largest file accepted, in bytes. - `sheet: string` - **事件**: - `attachmentrejected` · bubbles, composed · detail `{ file, reason }` - `attachmentschange` · bubbles, composed · detail `{ attachments }` - **插槽**: — - **Part**: `attachment`, `icon`, `list`, `name`, `remove`, `size`, `thumb` ## `` 源码: `components/button/index.ts` - **属性(attribute)**: `aria-label`, `disabled: boolean | string`, `effect: boolean`, `icon: string`, `iconSize: string`, `sheet: string`, `type: string` - **属性值(property)**: - `disabled: boolean | string` — Blocks clicks and keyboard activation and removes the button from the tab order. Off by default; `disabled="false"` counts as enabled. - `effect: boolean` — Whether the click ripple is drawn. On by default; opt out with `effect="false"`. - `icon: string` — Name of an `` drawn before the label, for example `'search'`. No icon when empty. - `iconSize: string` — Icon size passed to the inner ``. Defaults to about 1.15× the button's font size; set it only when the icon needs a fixed size. - `sheet: string` - `type: string` — Visual variant: `''` (default) | `'primary'` (monochrome) | `'warning'` | `'text'`. Drives the `:host([type=...])` styles. - **事件**: — - **插槽**: `默认插槽` - **Part**: `button`, `content` ## `` 源码: `components/card/index.ts` - **属性(attribute)**: `description: string`, `heading: string`, `hoverable: boolean`, `sheet: string` - **属性值(property)**: - `description: string` — Secondary text shown under the heading. Empty by default; the header is hidden when the heading, description and `extra` slot are all empty. - `heading: string` — Heading text. - `hoverable: boolean` — Interactive card (Geist): hover darkens the border one step. Purely presentational — gate it to cards that are actually clickable. - `sheet: string` - **事件**: — - **插槽**: `默认插槽`, `extra(具名)`, `footer(具名)` - **Part**: `body`, `card`, `description`, `extra`, `footer`, `header`, `title` ## `` 源码: `components/checkbox/index.ts` - **属性(attribute)**: `checked: boolean`, `disabled: boolean`, `required: boolean`, `sheet: string`, `value: string` - **属性值(property)**: `checked: boolean`, `disabled: boolean`, `required: boolean`, `sheet: string`, `validationMessage: string`, `validity: ValidityState | undefined`, `value: string` - **事件**: - `change` · element-only · detail `{ checked }` - **插槽**: `默认插槽` - **Part**: `checkbox`, `inner`, `input`, `label`, `wrapper` ## `` 源码: `components/colorpicker/index.ts` - **属性(attribute)**: `alpha-label`, `disabled: boolean`, `hue-label`, `label: string`, `sheet: string`, `value: string` - **属性值(property)**: - `alphaLabel: string` — Accessible name of the opacity slider. - `disabled: boolean` — Blocks opening the picker and emitting `change`, and removes the swatch from the tab order. Off by default; `disabled="false"` counts as enabled. - `hueLabel: string` — Accessible name of the hue slider. - `label: string` — Accessible name of the swatch that opens the picker. Defaults to `'Choose color'`. - `sheet: string` - `value: string` — Selected colour as `#rrggbb` (or `#rgb`), `rgb(...)` or `rgba(...)`; picking a colour writes a hex value, or `rgba(...)` when opacity is below 1. Unparseable values are ignored. Empty until set; the swatch starts white. - **事件**: - `change` · bubbles, composed - **插槽**: — - **Part**: `block`, `swatch` ## `` 源码: `components/popover/content/index.ts` - **属性(attribute)**: — - **属性值(property)**: — - **事件**: - `change` · element-only - **插槽**: `默认插槽` - **Part**: — ## `` 源码: `components/conversation/index.ts` - **属性(attribute)**: `empty: string`, `follow: boolean`, `loading-older`, `older: string`, `sheet: string` - **属性值(property)**: - `empty: string` — Text shown while the projection has produced no rows. - `follow: boolean` — Whether new content is followed until the reader scrolls away from the floor. - `loadingOlder: boolean` — Whether a page is in flight; the affordance stays visible and goes inert. - `older: string` — Label for the paging affordance above the first row. Empty hides it. - `pinned: boolean` — Whether the view is currently following new content. - `sheet: string` - **事件**: - `olderrequest` · bubbles, composed - `pinnedchange` · bubbles, composed · detail `{ pinned }` - **插槽**: `footer(具名)` - **Part**: `conversation`, `empty`, `footer`, `list`, `older` ## `` 源码: `components/disclosure-row/index.ts` - **属性(attribute)**: `busy: boolean`, `expandable: boolean`, `heading: string`, `name: string`, `open: boolean`, `sheet: string`, `summary: string`, `tone: string` - **属性值(property)**: - `busy: boolean` — Whether the work this row stands for is still running. - `expandable: boolean` — Whether the row has a body worth opening. - `heading: string` — The fixed-width left half of the line. - `name: string` — Groups rows so that opening one closes the rest. - `open: boolean` — Whether the body is shown. - `sheet: string` - `summary: string` — The truncating right half. Empty drops the separator with it. - `tone: string` — `error` colours the summary; anything else is the ordinary tone. - **事件**: - `disclosurebeforetoggle` · bubbles, composed, cancelable · detail `{ open }` - `disclosuretoggle` · bubbles, composed · detail `{ open }` - **插槽**: `默认插槽`, `leading(具名)` - **Part**: `body`, `disclosure`, `leading`, `row`, `separator`, `summary`, `title` ## `` 源码: `components/dropdown/index.ts` - **属性(attribute)**: `arrow: string`, `sheet: string`, `transit: string` - **属性值(property)**: `arrow: string`, `sheet: string`, `transit: string` - **事件**: — - **插槽**: `默认插槽` - **Part**: `dropdown` ## `` 源码: `components/select/dropdown-item/index.ts` - **属性(attribute)**: `active: string`, `selected: boolean`, `sheet: string`, `title: string`, `value: string` - **属性值(property)**: - `active: string` — The transient cursor that pointer hover and arrow keys move; r-select sets it. It shares the hover fill and carries no selection meaning. - `selected: boolean` — Marks the committed choice with a check mark. Unlike `active` (the transient cursor), it stays put while the cursor moves. - `sheet: string` - `title: string` - `value: string` - **事件**: — - **插槽**: `默认插槽` - **Part**: `content`, `item` ## `` 源码: `components/fan-gallery/index.ts` - **属性(attribute)**: `sheet: string`, `spread: string` - **属性值(property)**: - `sheet: string` - `spread: string` — Angle in degrees between the first and last child once open. Defaults to `24`; `0` lines them up. - **事件**: — - **插槽**: `默认插槽` - **Part**: `stage` ## `` 源码: `components/glass/index.ts` - **属性(attribute)**: `backdrop: string`, `bevel: string`, `blur`, `dispersion: string`, `displace: string`, `interactive: boolean`, `material: string`, `radius: string`, `rim: boolean`, `saturate: string`, `specular: string`, `tint: string`, `tone: string` - **属性值(property)**: - `backdrop: string` — An `` or `