diff --git a/.gitignore b/.gitignore index bef9e080c4..b4f2917904 100644 --- a/.gitignore +++ b/.gitignore @@ -63,8 +63,6 @@ dogfood-output/ # MCP configuration .mcp.json -# ...except the agent plugin's own MCP server map, which is part of the published plugin -!libs/ui/agent-plugin/.mcp.json .schaltwerk/ # Frontend demo test pages diff --git a/libs/ui/agent-plugin/hooks/hooks.json b/libs/ui/agent-plugin/.claude-plugin/hooks.json similarity index 100% rename from libs/ui/agent-plugin/hooks/hooks.json rename to libs/ui/agent-plugin/.claude-plugin/hooks.json diff --git a/libs/ui/agent-plugin/.claude-plugin/plugin.json b/libs/ui/agent-plugin/.claude-plugin/plugin.json index 46035dd931..d49ff033eb 100644 --- a/libs/ui/agent-plugin/.claude-plugin/plugin.json +++ b/libs/ui/agent-plugin/.claude-plugin/plugin.json @@ -1,15 +1,38 @@ { "name": "techsio-ui-kit-ai", - "version": "0.1.0", - "description": "Techsio UI Kit AI — skills and subagents for developing the @techsio/ui-kit design system (libs/ui). Same content as .codex-plugin/plugin.json; this manifest makes the plugin installable in Claude Code / GitHub Copilot too.", + "version": "0.2.0", + "description": "Techsio UI Kit AI — skills for developing and consuming the @techsio/ui-kit design system (libs/ui): component usage with UX/UI guidance, tokens, Storybook, Figma sync and the quality gate.", "author": { "name": "Lukáš Chylík", "email": "lukaschylik93@gmail.com" }, "homepage": "https://github.com/TechsioCZ/new-engine/tree/master/libs/ui/agent-plugin", "repository": "https://github.com/TechsioCZ/new-engine", - "keywords": ["techsio", "ui-kit", "design-system", "codex", "claude-code", "agent-skills"], "license": "MIT", - "hooks": "hooks/hooks.json", - "mcpServers": ".mcp.json" + "keywords": [ + "techsio", + "ui-kit", + "design-system", + "ux-guidelines", + "agent-plugins", + "agent-skills", + "codex", + "claude-code" + ], + "hooks": "./.claude-plugin/hooks.json", + "mcpServers": { + "context7": { + "type": "http", + "url": "https://mcp.context7.com/mcp" + }, + "figma": { + "type": "http", + "url": "https://mcp.figma.com/mcp" + }, + "chrome-devtools": { + "type": "stdio", + "command": "npx", + "args": ["chrome-devtools-mcp@latest"] + } + } } diff --git a/libs/ui/agent-plugin/.codex-plugin/plugin.json b/libs/ui/agent-plugin/.codex-plugin/plugin.json deleted file mode 100644 index f69c15908e..0000000000 --- a/libs/ui/agent-plugin/.codex-plugin/plugin.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "techsio-ui-kit-ai", - "version": "0.1.0", - "description": "Techsio UI Kit AI — skills and subagents for developing the @techsio/ui-kit design system (libs/ui). Codex-first; the skill format is shared with Claude Code.", - "author": { - "name": "Lukáš Chylík", - "email": "lukaschylik93@gmail.com" - }, - "homepage": "https://github.com/TechsioCZ/new-engine/tree/master/libs/ui/agent-plugin", - "repository": "https://github.com/TechsioCZ/new-engine", - "keywords": ["techsio", "ui-kit", "design-system", "codex", "claude-code", "agent-skills"], - "license": "MIT", - "skills": "./skills/", - "hooks": "./hooks/hooks.json", - "mcpServers": "./.mcp.json" -} diff --git a/libs/ui/agent-plugin/AGENTS.md b/libs/ui/agent-plugin/AGENTS.md index 6bef8da813..52704d3a5a 100644 --- a/libs/ui/agent-plugin/AGENTS.md +++ b/libs/ui/agent-plugin/AGENTS.md @@ -1,8 +1,8 @@ # Techsio UI Kit AI — plugin guidance -This folder is the **techsio-ui-kit-ai** agent plugin for developing `@techsio/ui-kit` -(`libs/ui`). Canonical library rules live in `libs/ui/AGENTS.md` — read that first; this file -only routes. +This folder is the **techsio-ui-kit-ai** plugin — an [Agent Plugins 1.0.0](https://agent-plugins.org) +package for developing `@techsio/ui-kit` (`libs/ui`). Canonical library rules live in +`libs/ui/AGENTS.md` — read that first; this file only routes. ## Routing @@ -15,9 +15,24 @@ only routes. | Figma Code Connect / variable sync | `$ui-figma-sync` skill → `ui-figma-connector` agent | | Quality gate before commit/push | `$ui-validate` skill → `ui-qa-validator` agent (mandatory last) | | Publish readiness / semver | `$ui-release-check` skill | -| Consuming the kit from an app | `$ui-component-usage` skill → bundled `-usage` skills | +| Consuming the kit from an app | `$ui-component-usage` skill → `ux-guidelines` → bundled `-usage` skills | +| UX copy, dates/numbers, alignment, states, CRUD/toast placement, "which component?" | bundled `ux-guidelines` skill | | Architecture / patterns / nesting policy / design review | `ui-design-system-expert` agent | +Agents are Codex subagents in `com.openai/agents/` (client-specific, not part of the portable +contract). + +## Package layout (Agent Plugins 1.0.0) + +- `plugin.json`, `mcp.json`, `skills/` — the portable contract. Edit these. +- `com.openai/` — Codex extension directory (hooks, subagents), referenced from + `plugin.json` → `extensions["com.openai"]`. +- `.claude-plugin/` — GENERATED Claude Code shim; never edit, run + `node scripts/build-client-manifests.mjs`. +- `skills/-usage`, `ux-guidelines`, … — GENERATED from `libs/ui/skills/`; never edit here, + run `node scripts/sync-skills.mjs`. +- `node scripts/validate-plugin.mjs` must pass before committing plugin changes. + ## Ground rules (summary — full list in `libs/ui/AGENTS.md`) - React 19: `ref` prop, no `forwardRef`; `type` not `interface`; named exports; no barrels. diff --git a/libs/ui/agent-plugin/README.md b/libs/ui/agent-plugin/README.md index 0cb2f3c1f2..2d251f9ced 100644 --- a/libs/ui/agent-plugin/README.md +++ b/libs/ui/agent-plugin/README.md @@ -1,27 +1,47 @@ -# techsio-ui-kit-ai v0.1.0 +# techsio-ui-kit-ai v0.2.0 -Agent plugin for the **@techsio/ui-kit** design system — built for **OpenAI Codex** (CLI, -IDE extension, and the Codex app), compatible with **Claude Code** (skills use the shared -[Agent Skills](https://agentskills.io) standard; a `.claude-plugin/plugin.json` manifest is -included). +Agent plugin for the **@techsio/ui-kit** design system, packaged to the +**[Agent Plugins 1.0.0](https://agent-plugins.org)** standard +([spec](https://github.com/agentplugins/agent-plugins-spec/blob/main/spec/1.0.0.md)) — one +portable directory of [Agent Skills](https://agentskills.io) and MCP servers. Works in +**OpenAI Codex** (reads the portable manifest natively), **Claude Code** (through a generated +legacy manifest) and any other conformant client. The plugin is **self-contained and published separately** from the `new-engine` monorepo. It currently lives at `libs/ui/agent-plugin` only as its authoring location — deep skills are bundled in (see *Syncing bundled skills*), so the folder can be moved to its own repo or marketplace as-is. -Inspired by the Neuron FE AI plugin, scoped to design-system development and consumption — -no spec-driven workflow. - ## What's included | Type | Count | Purpose | | --- | --- | --- | | Workflow skills | 8 | `$ui-*` entry points: scaffold, tokens, stories, theming, Figma sync, validation, release, usage routing | -| Bundled deep skills | 60 | Synced 1:1 from `libs/ui/skills/`: per-component `*-usage` guides, `component-authoring`, `tailwind-token-authoring`, `storybook-authoring`, `zag-compound-components`, … | -| Subagents (Codex TOML) | 6 | design-system expert, component-dev orchestrator, token/story/figma specialists, QA gate | -| Hooks | 2 | Real git `pre-push` gate (auto-installed in the ui-kit source repo only) + a `--no-verify` guard | -| MCP servers | 3 | context7 (docs), figma (design context + Code Connect), chrome-devtools (browser) | +| Bundled deep skills | 74 | Synced from `libs/ui/skills/`: 59 per-component `*-usage` guides (each with a **UX/UI guidelines** section), `ux-guidelines`, `component-authoring`, `tailwind-token-authoring`, `storybook-authoring`, `zag-compound-components`, … | +| MCP servers | 3 | context7 (docs), figma (design context + Code Connect), chrome-devtools (browser) — `mcp.json` | +| Codex extension (`com.openai/`) | 6 agents + 2 hooks | Subagents (design-system expert, component-dev orchestrator, token/story/figma specialists, QA gate); real git `pre-push` gate installer + `--no-verify` guard | + +## UX/UI guidelines + +`ux-guidelines` is the house rulebook every screen follows, distilled from the Storybook page +compositions (`Pages/*`) and the kit's components: + +- **UX writing** — sentence case; verb + object buttons (`New product`, `Create product`, + `Save changes`, `Delete product`, `Discard changes`); toasts as ` `; + errors say what, why and how to fix; one status vocabulary mapped to Badge variants. +- **Formatting** — dates and numbers only through `Intl` with the app locale (never raw ISO, + never `"$" + n.toFixed(2)`); pass `locale` explicitly because kit defaults differ. +- **Alignment** — money/quantities/percentages end-aligned with tabular figures; text, IDs + and dates start-aligned; missing values as `—`. +- **States** — when to disable (only with a visible reason), read-only vs disabled, loading + (`isLoading` vs Skeleton), validation, empty, dirty. +- **Where things live** — page actions top-right in the page header, form/dialog actions + bottom-right with the primary last, danger zone separated; one `` at bottom-end; + which feedback surface (inline, FormErrorSummary, toast, alertdialog) for which event. +- **Component selection** — intent → component, with the look-alike to avoid. + +Every `-usage` skill applies these rules in its own **UX/UI guidelines** section: +when to use it, what to use instead, do, don't, copy and states. > **Note on slash commands:** Codex deprecated `~/.codex/prompts` custom prompts in favor of > skills. Skills ARE the slash commands now — invoke them explicitly with `$skill-name` (or @@ -42,7 +62,8 @@ no spec-driven workflow. ## Subagents -Codex subagents are TOML files in `agents/`. Copy (or symlink) them into `~/.codex/agents/` +Codex subagents are TOML files in `com.openai/agents/` (Codex's extension directory — agents +are not a portable Agent Plugins component type). Copy (or symlink) them into `~/.codex/agents/` (personal) or a repo's `.codex/agents/` (project-scoped — the project must be trusted in Codex). Hooks additionally require `features.hooks` enabled in Codex `config.toml`. @@ -95,8 +116,33 @@ claude plugin marketplace add claude plugin install techsio-ui-kit-ai ``` -Skills and hooks work as-is via `.claude-plugin/plugin.json`. The TOML subagents are -Codex-specific — Claude Code users get equivalent behavior through the skills. +Claude Code does not read the portable root `plugin.json` yet, so the plugin ships a +**generated** `.claude-plugin/plugin.json` (metadata from `plugin.json`, MCP servers inlined +from `mcp.json` with `streamable-http` → `http`) and `.claude-plugin/hooks.json` (a copy of +`com.openai/hooks.json`). Never edit them by hand — change the portable files and run +`node scripts/build-client-manifests.mjs`. The TOML subagents are Codex-specific — Claude Code +users get equivalent behavior through the skills. + +### Any other Agent Plugins client + +Point the client at the plugin directory. It discovers `plugin.json`, `skills/*/SKILL.md` and +`mcp.json`, and ignores `com.openai/` and `.claude-plugin/`. + +## Agent Plugins 1.0.0 conformance + +| Spec | How this package complies | +| --- | --- | +| §5 manifest | root `plugin.json`, `$schema` `https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`, closed field set, client data only under `extensions` | +| §6–7.1 skills | fixed location `skills//SKILL.md`; frontmatter limited to Agent Skills keys — library metadata (`type`, `library_version`, `component_version`, `requires`, `sources`, …) lives under `metadata` as strings | +| §7.2 MCP | root `mcp.json` with the matching `$schema`, explicit `type` per server (`streamable-http`, `stdio`) | +| §8 extensions | Codex data in `extensions["com.openai"]` + the `com.openai/` directory (hooks, subagents) | +| §4.1 containment | no path or symlink resolves outside the plugin root | + +Check it locally (also verifies the generated files are current): + +```sh +node scripts/validate-plugin.mjs # or: pnpm validate +``` ## Syncing bundled skills @@ -104,17 +150,23 @@ Codex-specific — Claude Code users get equivalent behavior through the skills. skills. Before each plugin release, refresh the bundle from a checkout of `new-engine`: ```sh -node scripts/sync-skills.mjs # or: pnpm sync-skills +node scripts/sync-skills.mjs # or: pnpm sync-skills +node scripts/sync-skills.mjs --check # fail if the bundle is stale ``` -The script copies every skill (except `_artifacts`) into `skills/` and fails on a name -collision with the 8 authored workflow skills. Never edit bundled skills inside the plugin — -edit them in `libs/ui/skills/` and re-sync. +The script copies every skill (except `_artifacts`) into `skills/`, removes skills deleted +from the source, and fails on a name collision with the 8 authored workflow skills. The +source keeps the TanStack Intent authoring format (`requires` / `sources` lists at top +level); on the way into the bundle each `SKILL.md` frontmatter is rewritten to the strict +Agent Skills form (every non-spec key moved under `metadata`, lists joined with spaces). +The body and all other files are copied byte-for-byte. Never edit bundled skills inside the +plugin — edit them in `libs/ui/skills/` and re-sync; the repo's pre-commit +`scripts/check-skill-sync.mjs` rejects a stale bundle. ## The pre-push quality gate Pushes carrying `libs/ui` changes are blocked until `$ui-validate` has passed for the ref being -pushed. Enforcement is a real **git `pre-push` hook** (`hooks/pre-push`), installed into the +pushed. Enforcement is a real **git `pre-push` hook** (`scripts/git-hooks/pre-push`), installed into the repo by a `SessionStart` hook (`scripts/install-git-hook.mjs`). **Scoped to the ui-kit source repo.** The plugin may be installed globally, so sessions start @@ -165,17 +217,26 @@ slot, collisions are resolved so existing hooks keep running — never won by fo ``` techsio-ui-kit-ai/ -├── .codex-plugin/plugin.json # Codex manifest -├── .claude-plugin/plugin.json # Claude Code manifest (same plugin) -├── package.json # npm packaging (@techsio/ui-kit-ai) -├── AGENTS.md # routing table -├── .mcp.json # context7, figma, chrome-devtools -├── agents/*.toml # 6 Codex subagents -├── skills/ # 8 authored workflow skills + 60 bundled deep skills -├── hooks/ +├── plugin.json # Agent Plugins 1.0.0 manifest (portable, canonical) +├── mcp.json # MCP servers: context7, figma, chrome-devtools (portable) +├── skills/ # 8 authored workflow skills + 74 bundled deep skills (portable) +├── com.openai/ # Codex extension directory (extensions["com.openai"]) │ ├── hooks.json # SessionStart installer + --no-verify guard -│ └── pre-push # the real gate (git hands it the exact refs/SHAs) -└── scripts/ # install-git-hook.mjs, pre-push-validate-gate.mjs, sync-skills.mjs +│ └── agents/*.toml # 6 Codex subagents +├── .claude-plugin/ # GENERATED Claude Code shim — do not edit +│ ├── plugin.json # metadata + inlined MCP servers +│ └── hooks.json # copy of com.openai/hooks.json +├── scripts/ +│ ├── git-hooks/pre-push # the real gate (git hands it the exact refs/SHAs) +│ ├── install-git-hook.mjs # SessionStart installer +│ ├── pre-push-validate-gate.mjs +│ ├── sync-skills.mjs # libs/ui/skills → skills/ (portable frontmatter) +│ ├── build-client-manifests.mjs # plugin.json + mcp.json → .claude-plugin/ +│ ├── validate-plugin.mjs # Agent Plugins 1.0.0 + Agent Skills conformance +│ └── lib/ # shared helpers +├── AGENTS.md # routing table +├── package.json # npm packaging (@techsio/ui-kit-ai) +└── LICENSE ``` ## License diff --git a/libs/ui/agent-plugin/agents/ui-component-dev.toml b/libs/ui/agent-plugin/com.openai/agents/ui-component-dev.toml similarity index 95% rename from libs/ui/agent-plugin/agents/ui-component-dev.toml rename to libs/ui/agent-plugin/com.openai/agents/ui-component-dev.toml index 623c0e9944..ae051311fd 100644 --- a/libs/ui/agent-plugin/agents/ui-component-dev.toml +++ b/libs/ui/agent-plugin/com.openai/agents/ui-component-dev.toml @@ -1,5 +1,5 @@ # Codex subagent — orchestrator for @techsio/ui-kit component development. -# Install: symlink/copy into /.codex/agents/ (project, trusted) or ~/.codex/agents/ (personal). +# Install: symlink/copy com.openai/agents/*.toml into /.codex/agents/ (project, trusted) or ~/.codex/agents/ (personal). name = "ui-component-dev" description = "Builds and refactors @techsio/ui-kit components in libs/ui end-to-end: component source, tv() variants, token CSS, stories, exports. Delegates token CSS to ui-token-stylist, stories to ui-storybook-writer, Figma mapping to ui-figma-connector, and always finishes with ui-qa-validator." diff --git a/libs/ui/agent-plugin/agents/ui-design-system-expert.toml b/libs/ui/agent-plugin/com.openai/agents/ui-design-system-expert.toml similarity index 100% rename from libs/ui/agent-plugin/agents/ui-design-system-expert.toml rename to libs/ui/agent-plugin/com.openai/agents/ui-design-system-expert.toml diff --git a/libs/ui/agent-plugin/agents/ui-figma-connector.toml b/libs/ui/agent-plugin/com.openai/agents/ui-figma-connector.toml similarity index 100% rename from libs/ui/agent-plugin/agents/ui-figma-connector.toml rename to libs/ui/agent-plugin/com.openai/agents/ui-figma-connector.toml diff --git a/libs/ui/agent-plugin/agents/ui-qa-validator.toml b/libs/ui/agent-plugin/com.openai/agents/ui-qa-validator.toml similarity index 100% rename from libs/ui/agent-plugin/agents/ui-qa-validator.toml rename to libs/ui/agent-plugin/com.openai/agents/ui-qa-validator.toml diff --git a/libs/ui/agent-plugin/agents/ui-storybook-writer.toml b/libs/ui/agent-plugin/com.openai/agents/ui-storybook-writer.toml similarity index 100% rename from libs/ui/agent-plugin/agents/ui-storybook-writer.toml rename to libs/ui/agent-plugin/com.openai/agents/ui-storybook-writer.toml diff --git a/libs/ui/agent-plugin/agents/ui-token-stylist.toml b/libs/ui/agent-plugin/com.openai/agents/ui-token-stylist.toml similarity index 100% rename from libs/ui/agent-plugin/agents/ui-token-stylist.toml rename to libs/ui/agent-plugin/com.openai/agents/ui-token-stylist.toml diff --git a/libs/ui/agent-plugin/com.openai/hooks.json b/libs/ui/agent-plugin/com.openai/hooks.json new file mode 100644 index 0000000000..c5463983c6 --- /dev/null +++ b/libs/ui/agent-plugin/com.openai/hooks.json @@ -0,0 +1,25 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/scripts/install-git-hook.mjs\"" + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Bash|shell|local_shell", + "hooks": [ + { + "type": "command", + "command": "node \"${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/scripts/pre-push-validate-gate.mjs\"" + } + ] + } + ] + } +} diff --git a/libs/ui/agent-plugin/.mcp.json b/libs/ui/agent-plugin/mcp.json similarity index 67% rename from libs/ui/agent-plugin/.mcp.json rename to libs/ui/agent-plugin/mcp.json index 4336d5462a..8a52e25199 100644 --- a/libs/ui/agent-plugin/.mcp.json +++ b/libs/ui/agent-plugin/mcp.json @@ -1,11 +1,12 @@ { + "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", "mcpServers": { "context7": { - "type": "http", + "type": "streamable-http", "url": "https://mcp.context7.com/mcp" }, "figma": { - "type": "http", + "type": "streamable-http", "url": "https://mcp.figma.com/mcp" }, "chrome-devtools": { diff --git a/libs/ui/agent-plugin/package.json b/libs/ui/agent-plugin/package.json index 5a08d5c67e..f3f7b01f8f 100644 --- a/libs/ui/agent-plugin/package.json +++ b/libs/ui/agent-plugin/package.json @@ -1,7 +1,7 @@ { "name": "@techsio/ui-kit-ai", - "version": "0.1.0", - "description": "Techsio UI Kit AI — agent plugin (skills + Codex subagents) for developing and consuming the @techsio/ui-kit design system.", + "version": "0.2.0", + "description": "Techsio UI Kit AI — Agent Plugins 1.0.0 package (skills + MCP) for developing and consuming the @techsio/ui-kit design system.", "author": { "name": "Lukáš Chylík", "email": "lukaschylik93@gmail.com" @@ -11,23 +11,33 @@ "url": "git+https://github.com/TechsioCZ/new-engine.git", "directory": "libs/ui/agent-plugin" }, - "keywords": ["techsio", "ui-kit", "design-system", "codex", "claude-code", "agent-skills", "plugin"], + "keywords": [ + "techsio", + "ui-kit", + "design-system", + "agent-plugins", + "codex", + "claude-code", + "agent-skills", + "plugin" + ], "license": "MIT", "private": true, "scripts": { - "sync-skills": "node scripts/sync-skills.mjs" + "sync-skills": "node scripts/sync-skills.mjs", + "build:manifests": "node scripts/build-client-manifests.mjs", + "validate": "node scripts/validate-plugin.mjs" }, "files": [ - ".codex-plugin", + "plugin.json", + "mcp.json", + "skills", + "com.openai", ".claude-plugin", - ".mcp.json", + "scripts", "AGENTS.md", "LICENSE", - "README.md", - "agents", - "hooks", - "scripts", - "skills" + "README.md" ], - "_publishing": "Distributed as a Codex/Claude plugin via a marketplace (git source), not via npm. To publish to npm instead, drop `private` and add `publishConfig.access`." + "_publishing": "Distributed as an Agent Plugins directory via a marketplace (git source), not via npm. To publish to npm instead, drop `private` and add `publishConfig.access`." } diff --git a/libs/ui/agent-plugin/plugin.json b/libs/ui/agent-plugin/plugin.json new file mode 100644 index 0000000000..8597c1bef9 --- /dev/null +++ b/libs/ui/agent-plugin/plugin.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "techsio-ui-kit-ai", + "version": "0.2.0", + "description": "Techsio UI Kit AI — skills for developing and consuming the @techsio/ui-kit design system (libs/ui): component usage with UX/UI guidance, tokens, Storybook, Figma sync and the quality gate.", + "author": { + "name": "Lukáš Chylík", + "email": "lukaschylik93@gmail.com" + }, + "homepage": "https://github.com/TechsioCZ/new-engine/tree/master/libs/ui/agent-plugin", + "repository": "https://github.com/TechsioCZ/new-engine", + "license": "MIT", + "keywords": [ + "techsio", + "ui-kit", + "design-system", + "ux-guidelines", + "agent-plugins", + "agent-skills", + "codex", + "claude-code" + ], + "extensions": { + "com.openai": { + "hooks": "./com.openai/hooks.json", + "interface": { + "displayName": "Techsio UI Kit AI", + "shortDescription": "Build and consume the @techsio/ui-kit design system.", + "longDescription": "Workflow skills ($ui-*) for scaffolding components, tokens, stories, theming, Figma sync, validation and release, plus per-component usage skills with UX/UI guidance (when to use, when not, copy, states, formatting, feedback placement).", + "developerName": "Techsio", + "category": "Coding", + "websiteURL": "https://github.com/TechsioCZ/new-engine/tree/master/libs/ui/agent-plugin" + } + } + } +} diff --git a/libs/ui/agent-plugin/scripts/build-client-manifests.mjs b/libs/ui/agent-plugin/scripts/build-client-manifests.mjs new file mode 100644 index 0000000000..dc8f0dd898 --- /dev/null +++ b/libs/ui/agent-plugin/scripts/build-client-manifests.mjs @@ -0,0 +1,130 @@ +#!/usr/bin/env node +/** + * Generates the legacy Claude Code manifest from the portable Agent Plugins files. + * + * node scripts/build-client-manifests.mjs # write .claude-plugin/* + * node scripts/build-client-manifests.mjs --check # fail if they are stale + * + * `plugin.json` + `mcp.json` + `skills/` at the plugin root are the single source of truth + * (Agent Plugins 1.0.0). Codex reads them natively and takes its client-specific data from + * `extensions["com.openai"]` and the `com.openai/` extension directory. + * + * Claude Code does not read root `plugin.json` yet — it still loads `.claude-plugin/plugin.json` + * and `.mcp.json`. This script derives that manifest so the two can never drift: + * + * - metadata is copied from `plugin.json`; + * - MCP servers are inlined from `mcp.json`, mapped to Claude Code's config shape + * (`streamable-http` → `http`, `${PLUGIN_ROOT}` → `${CLAUDE_PLUGIN_ROOT}`, …); + * - hooks point at `.claude-plugin/hooks.json`, a copy of `com.openai/hooks.json` (both clients + * run the same hook format, and the commands resolve `${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}`). + * + * JSON has no comments, so the "do not edit" notice lives in README.md; `--check` catches edits. + * + * Portable clients ignore `.claude-plugin/` entirely (it is neither a component location nor an + * extension directory), so the shim never overrides the portable manifest. + */ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs" +import { dirname, join, resolve } from "node:path" +import { fileURLToPath } from "node:url" + +const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), "..") +const check = process.argv.includes("--check") +const readJson = (path) => + JSON.parse(readFileSync(join(pluginDir, path), "utf8")) + +const manifest = readJson("plugin.json") +const mcp = readJson("mcp.json") +const codex = manifest.extensions?.["com.openai"] ?? {} + +// Literal placeholder names, not template strings — `$` + `{…}` are joined on purpose. +const placeholder = (name) => `$${"{"}${name}}` +const toClaudeVars = (value) => + value + .replaceAll(placeholder("PLUGIN_ROOT"), placeholder("CLAUDE_PLUGIN_ROOT")) + .replaceAll(placeholder("PLUGIN_DATA"), placeholder("CLAUDE_PLUGIN_DATA")) + +const mapServer = (server) => { + if (server.type === "streamable-http" || server.type === "sse") { + return { + type: server.type === "sse" ? "sse" : "http", + url: server.url, + ...(server.headers ? { headers: server.headers } : {}), + } + } + const command = server.command.startsWith("./") + ? `\${CLAUDE_PLUGIN_ROOT}/${server.command.slice(2)}` + : server.command + return { + type: "stdio", + command, + ...(server.args ? { args: server.args.map(toClaudeVars) } : {}), + ...(server.env + ? { + env: Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, toClaudeVars(v)]) + ), + } + : {}), + ...(server.cwd + ? { + cwd: toClaudeVars( + server.cwd.startsWith("./") + ? `\${PLUGIN_ROOT}/${server.cwd.slice(2)}` + : server.cwd + ), + } + : {}), + } +} + +const pick = (keys) => + Object.fromEntries( + keys.filter((k) => k in manifest).map((k) => [k, manifest[k]]) + ) + +const files = new Map() +files.set(".claude-plugin/plugin.json", { + ...pick([ + "name", + "version", + "description", + "author", + "homepage", + "repository", + "license", + "keywords", + ]), + ...(codex.hooks ? { hooks: "./.claude-plugin/hooks.json" } : {}), + mcpServers: Object.fromEntries( + Object.entries(mcp.mcpServers).map(([name, s]) => [name, mapServer(s)]) + ), +}) +if (codex.hooks) { + files.set(".claude-plugin/hooks.json", readJson(codex.hooks)) +} + +const stale = [] +for (const [path, content] of files) { + const text = `${JSON.stringify(content, null, 2)}\n` + const target = join(pluginDir, path) + if (check) { + // Compare parsed JSON, so reformatting the file (biome) never reads as stale. + const current = existsSync(target) ? readFileSync(target, "utf8") : "null" + if (JSON.stringify(JSON.parse(current)) !== JSON.stringify(content)) { + stale.push(path) + } + continue + } + mkdirSync(dirname(target), { recursive: true }) + writeFileSync(target, text) +} + +if (check && stale.length) { + console.error( + `Stale client manifests: ${stale.join(", ")}\nRun: node libs/ui/agent-plugin/scripts/build-client-manifests.mjs` + ) + process.exit(1) +} +console.log( + check ? "Client manifests in sync." : `Wrote ${[...files.keys()].join(", ")}` +) diff --git a/libs/ui/agent-plugin/hooks/pre-push b/libs/ui/agent-plugin/scripts/git-hooks/pre-push similarity index 100% rename from libs/ui/agent-plugin/hooks/pre-push rename to libs/ui/agent-plugin/scripts/git-hooks/pre-push diff --git a/libs/ui/agent-plugin/scripts/install-git-hook.mjs b/libs/ui/agent-plugin/scripts/install-git-hook.mjs index f6d8df9335..46b98af045 100644 --- a/libs/ui/agent-plugin/scripts/install-git-hook.mjs +++ b/libs/ui/agent-plugin/scripts/install-git-hook.mjs @@ -37,7 +37,7 @@ import { fileURLToPath } from "node:url"; import { isUiKitSourceRepo } from "./lib/is-ui-kit-source-repo.mjs"; const MARKER = "git pre-push hook — the REAL ui-kit quality gate"; -const source = join(dirname(dirname(fileURLToPath(import.meta.url))), "hooks", "pre-push"); +const source = join(dirname(fileURLToPath(import.meta.url)), "git-hooks", "pre-push"); const git = (...args) => execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(); diff --git a/libs/ui/agent-plugin/scripts/lib/skill-frontmatter.mjs b/libs/ui/agent-plugin/scripts/lib/skill-frontmatter.mjs new file mode 100644 index 0000000000..c1b8e5f00d --- /dev/null +++ b/libs/ui/agent-plugin/scripts/lib/skill-frontmatter.mjs @@ -0,0 +1,266 @@ +/** + * SKILL.md frontmatter helpers shared by sync-skills.mjs, validate-plugin.mjs and the repo's + * scripts/check-skill-sync.mjs. + * + * Two formats meet here: + * + * - The AUTHORING format in libs/ui/skills/ (TanStack Intent flavoured): Agent Skills keys, the + * Intent scalars under `metadata` (`type`, `library`, `library_version`, `component`, + * `component_version`, …), and the Intent list keys `requires` / `sources` at top level. + * - The PORTABLE format bundled into the plugin: strictly the Agent Skills spec + * (https://agentskills.io/specification) — top-level keys limited to name, description, license, + * compatibility, metadata and allowed-tools, and `metadata` a string→string map. Agent Plugins + * 1.0.0 §7.1 requires clients to SKIP a non-conforming skill, so top-level lists cannot ship. + * + * `toPortableSkill()` converts the first into the second deterministically: every non-spec + * top-level key moves under `metadata`, lists are joined with single spaces (the same convention + * Agent Skills uses for `allowed-tools`). The body is copied byte-for-byte. + * + * The parser covers the YAML subset these files use — `key: scalar`, `key: >`/`|` block scalars, + * `key:` + ` - item` lists and `key:` + ` sub: scalar` maps — and throws on anything else, so an + * unexpected construct fails loudly instead of being rewritten wrongly. + */ + +/** + * Workflow skills written for the plugin itself. They exist only in the bundle; every other + * bundled skill is generated from libs/ui/skills/ (which also has `ui-kit-*` skills, so a name + * prefix cannot tell the two apart). + */ +export const AUTHORED_SKILLS = new Set([ + "ui-new-component", + "ui-tokens", + "ui-story", + "ui-validate", + "ui-theme-brand", + "ui-figma-sync", + "ui-release-check", + "ui-component-usage", +]) + +export const SPEC_KEYS = [ + "name", + "description", + "license", + "compatibility", + "metadata", + "allowed-tools", +] +export const SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/ + +const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n|$)/ + +/** Split a SKILL.md into its frontmatter lines and body. */ +export function splitSkill(source) { + const match = source.match(FRONTMATTER_RE) + if (!match) { + throw new Error("missing YAML frontmatter (file must start with ---)") + } + return { lines: match[1].split(/\r?\n/), body: source.slice(match[0].length) } +} + +// A quoted scalar followed by an optional YAML comment: `"value" # note`. +const DOUBLE_QUOTED = /^("(?:[^"\\]|\\.)*")\s*(?:#.*)?$/ +const SINGLE_QUOTED = /^'((?:[^']|'')*)'\s*(?:#.*)?$/ +// In a plain scalar a comment starts at whitespace + `#`. +const PLAIN_COMMENT = /\s+#.*$/ + +const unquote = (raw) => { + const value = raw.trim() + const double = DOUBLE_QUOTED.exec(value) + if (double) { + return JSON.parse(double[1]) + } + const single = SINGLE_QUOTED.exec(value) + if (single) { + return single[1].replace(/''/g, "'") + } + return value.replace(PLAIN_COMMENT, "") +} + +/** + * Parse frontmatter lines into ordered entries: + * { key, kind: "scalar" | "block" | "list" | "map", raw: string[], value } + * `raw` keeps the original lines so spec keys can be re-emitted untouched. + */ +// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: a single-pass line parser; splitting it would scatter the grammar +export function parseFrontmatter(lines) { + const entries = [] + let current = null + for (const line of lines) { + if (line.trim() === "" && current?.kind !== "block") { + continue + } + const top = line.match(/^([A-Za-z0-9_-]+):(?:\s(.*))?$/) + if (top) { + const [, key, rest = ""] = top + const trimmed = rest.trim() + if (trimmed === "") { + current = { key, kind: "pending", raw: [line], value: undefined } + } else if (/^[>|][-+]?$/.test(trimmed)) { + current = { key, kind: "block", raw: [line], value: "" } + } else { + current = { key, kind: "scalar", raw: [line], value: unquote(trimmed) } + } + entries.push(current) + continue + } + if (!(current && /^\s/.test(line))) { + throw new Error(`unsupported frontmatter line: ${JSON.stringify(line)}`) + } + current.raw.push(line) + if (current.kind === "block") { + current.value += `${current.value ? " " : ""}${line.trim()}` + continue + } + const item = line.match(/^\s+-\s+(.*)$/) + const pair = line.match(/^\s+([A-Za-z0-9_.-]+):\s*(.*)$/) + if (item && (current.kind === "pending" || current.kind === "list")) { + current.kind = "list" + current.value = [...(current.value ?? []), unquote(item[1])] + } else if (pair && (current.kind === "pending" || current.kind === "map")) { + current.kind = "map" + current.value = { ...(current.value ?? {}), [pair[1]]: unquote(pair[2]) } + } else { + throw new Error( + `unsupported frontmatter construct under "${current.key}": ${JSON.stringify(line)}` + ) + } + } + for (const entry of entries) { + if (entry.kind === "pending") { + entry.kind = "scalar" + entry.value = "" + } + if (entry.kind === "block") { + entry.value = entry.value.replace(/\s+/g, " ").trim() + } + } + return entries +} + +const quote = (value) => JSON.stringify(String(value)) + +/** + * Convert an authoring-format SKILL.md into a strictly Agent Skills–conformant one. + * Idempotent: a file that is already portable comes back unchanged. + */ +// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one linear conversion pass, kept together for readability +export function toPortableSkill(source) { + const { lines, body } = splitSkill(source) + const entries = parseFrontmatter(lines) + const metadata = new Map() + const out = [] + + for (const entry of entries) { + if (entry.key === "metadata") { + if (entry.kind !== "map") { + throw new Error("metadata must be a mapping") + } + for (const [k, v] of Object.entries(entry.value)) { + metadata.set(k, v) + } + } + } + for (const entry of entries) { + if (entry.key === "metadata") { + continue + } + if (SPEC_KEYS.includes(entry.key)) { + out.push(...entry.raw) + continue + } + if (metadata.has(entry.key)) { + throw new Error( + `"${entry.key}" is set both at top level and under metadata` + ) + } + if (entry.kind === "list") { + metadata.set(entry.key, entry.value.join(" ")) + } else if (entry.kind === "scalar" || entry.kind === "block") { + metadata.set(entry.key, entry.value) + } else { + throw new Error(`cannot move nested mapping "${entry.key}" into metadata`) + } + } + if (metadata.size > 0) { + out.push("metadata:") + for (const [k, v] of metadata) { + out.push(` ${k}: ${quote(v)}`) + } + } + return `---\n${out.join("\n")}\n---\n${body}` +} + +/** Validate a SKILL.md against the Agent Skills spec. Returns a list of problems. */ +// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: a flat list of independent spec checks +export function validatePortableSkill(source, dirName) { + const problems = [] + let entries + try { + entries = parseFrontmatter(splitSkill(source).lines) + } catch (error) { + return [error.message] + } + const byKey = new Map(entries.map((e) => [e.key, e])) + for (const entry of entries) { + if (!SPEC_KEYS.includes(entry.key)) { + problems.push( + `non-spec top-level key "${entry.key}" (move it under metadata)` + ) + } + } + const name = byKey.get("name")?.value + if (typeof name !== "string" || name === "") { + problems.push("missing name") + } else { + if (name.length > 64) { + problems.push("name exceeds 64 characters") + } + if (!SKILL_NAME_RE.test(name)) { + problems.push( + `name "${name}" must be lowercase letters, digits and single hyphens` + ) + } + if (name !== dirName) { + problems.push(`name "${name}" does not match its directory "${dirName}"`) + } + } + const description = byKey.get("description")?.value + if (typeof description !== "string" || description === "") { + problems.push("missing description") + } else if (description.length > 1024) { + problems.push(`description exceeds 1024 characters (${description.length})`) + } + const compatibility = byKey.get("compatibility")?.value + if ( + compatibility !== undefined && + (typeof compatibility !== "string" || compatibility.length > 500) + ) { + problems.push("compatibility must be a string of at most 500 characters") + } + const metadata = byKey.get("metadata") + if (metadata && metadata.kind !== "map") { + problems.push("metadata must be a string→string mapping") + } + for (const key of ["license", "allowed-tools"]) { + const entry = byKey.get(key) + if (entry && entry.kind !== "scalar" && entry.kind !== "block") { + problems.push(`${key} must be a string`) + } + } + return problems +} + +/** Read one metadata (or legacy top-level) scalar from an authoring-format SKILL.md. */ +export function readSkillField(source, key) { + const entries = parseFrontmatter(splitSkill(source).lines) + const metadata = + entries.find((e) => e.key === "metadata" && e.kind === "map")?.value ?? {} + if (key in metadata) { + return metadata[key] + } + const top = entries.find((e) => e.key === key) + return top && (top.kind === "scalar" || top.kind === "block") + ? top.value + : undefined +} diff --git a/libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs b/libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs index b2cb3d076a..7466c5157e 100644 --- a/libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs +++ b/libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs @@ -2,7 +2,7 @@ /** * PreToolUse hook — a NARROW companion to the real gate. * - * The enforcement lives in the git `pre-push` hook (`hooks/pre-push`, installed by + * The enforcement lives in the git `pre-push` hook (`scripts/git-hooks/pre-push`, installed by * `scripts/install-git-hook.mjs`), which git calls with the exact refs and SHAs it is about to * upload. This file deliberately does NOT try to work out which refs a push will send — an * earlier version did and was bypassable eleven different ways. diff --git a/libs/ui/agent-plugin/scripts/sync-skills.mjs b/libs/ui/agent-plugin/scripts/sync-skills.mjs index a5931610a2..c8e84f5599 100644 --- a/libs/ui/agent-plugin/scripts/sync-skills.mjs +++ b/libs/ui/agent-plugin/scripts/sync-skills.mjs @@ -6,50 +6,116 @@ * libs/ui/skills/ stays the single source of truth — run this before every * plugin release: * - * node scripts/sync-skills.mjs + * node scripts/sync-skills.mjs # write the bundle + * node scripts/sync-skills.mjs --check # fail if the bundle is stale (CI / pre-commit) * * Copies every skill directory except `_artifacts`, refuses to overwrite the - * plugin's own authored workflow skills on a name collision. + * plugin's own authored workflow skills on a name collision. Each SKILL.md is + * converted to the portable Agent Skills frontmatter on the way + * (see lib/skill-frontmatter.mjs); every other file is copied byte-for-byte. */ -import { cpSync, existsSync, readdirSync, rmSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const srcDir = resolve(pluginDir, "../skills"); // libs/ui/skills -const destDir = resolve(pluginDir, "skills"); - -const AUTHORED = new Set([ - "ui-new-component", - "ui-tokens", - "ui-story", - "ui-validate", - "ui-theme-brand", - "ui-figma-sync", - "ui-release-check", - "ui-component-usage", -]); +import { + cpSync, + existsSync, + readdirSync, + readFileSync, + rmSync, + statSync, + writeFileSync, +} from "node:fs" +import { dirname, join, relative, resolve } from "node:path" +import { fileURLToPath } from "node:url" +import { AUTHORED_SKILLS, toPortableSkill } from "./lib/skill-frontmatter.mjs" + +const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), "..") +const srcDir = resolve(pluginDir, "../skills") // libs/ui/skills +const destDir = resolve(pluginDir, "skills") +const check = process.argv.includes("--check") if (!existsSync(srcDir)) { - console.error(`Source skills directory not found: ${srcDir}`); - console.error("Run this script from a checkout of the new-engine repo (plugin at libs/ui/agent-plugin)."); - process.exit(1); + console.error(`Source skills directory not found: ${srcDir}`) + console.error( + "Run this script from a checkout of the new-engine repo (plugin at libs/ui/agent-plugin)." + ) + process.exit(1) +} + +const listFiles = (dir) => + readdirSync(dir).flatMap((name) => { + const path = join(dir, name) + return statSync(path).isDirectory() ? listFiles(path) : [path] + }) + +const expectedContent = (file) => { + const raw = readFileSync(file) + // Only a skill's top-level SKILL.md is rewritten; nested files ship verbatim. + return relative(srcDir, file).split(/[\\/]/).length === 2 && + file.endsWith("SKILL.md") + ? Buffer.from(toPortableSkill(raw.toString("utf8"))) + : raw } const entries = readdirSync(srcDir, { withFileTypes: true }) .filter((e) => e.isDirectory() && e.name !== "_artifacts") - .map((e) => e.name); + .map((e) => e.name) -let copied = 0; +const stale = [] for (const name of entries) { - if (AUTHORED.has(name)) { - console.error(`COLLISION: repo skill "${name}" clashes with an authored plugin skill — rename one.`); - process.exit(1); + if (AUTHORED_SKILLS.has(name)) { + console.error( + `COLLISION: repo skill "${name}" clashes with an authored plugin skill — rename one.` + ) + process.exit(1) + } + const from = resolve(srcDir, name) + const to = resolve(destDir, name) + if (check) { + const want = listFiles(from) + .map((f) => relative(from, f)) + .sort() + const have = existsSync(to) + ? listFiles(to) + .map((f) => relative(to, f)) + .sort() + : [] + if (want.join("\n") !== have.join("\n")) { + stale.push(name) + continue + } + if ( + want.some( + (f) => !expectedContent(join(from, f)).equals(readFileSync(join(to, f))) + ) + ) { + stale.push(name) + } + continue } - const dest = resolve(destDir, name); - rmSync(dest, { recursive: true, force: true }); - cpSync(resolve(srcDir, name), dest, { recursive: true }); - copied++; + rmSync(to, { recursive: true, force: true }) + cpSync(from, to, { recursive: true }) + writeFileSync(join(to, "SKILL.md"), expectedContent(join(from, "SKILL.md"))) } -console.log(`Bundled ${copied} skills from ${srcDir} into ${destDir}`); +// A skill deleted from the source must disappear from the bundle too. +for (const name of readdirSync(destDir)) { + if (AUTHORED_SKILLS.has(name) || entries.includes(name)) { + continue + } + if (check) { + stale.push(`${name} (removed from source)`) + } else { + rmSync(resolve(destDir, name), { recursive: true, force: true }) + } +} + +if (check) { + if (stale.length) { + console.error( + `Bundled skills are stale: ${stale.join(", ")}\nRun: node libs/ui/agent-plugin/scripts/sync-skills.mjs` + ) + process.exit(1) + } + console.log(`Bundle in sync (${entries.length} skills).`) +} else { + console.log(`Bundled ${entries.length} skills from ${srcDir} into ${destDir}`) +} diff --git a/libs/ui/agent-plugin/scripts/validate-plugin.mjs b/libs/ui/agent-plugin/scripts/validate-plugin.mjs new file mode 100644 index 0000000000..3aee3344a1 --- /dev/null +++ b/libs/ui/agent-plugin/scripts/validate-plugin.mjs @@ -0,0 +1,357 @@ +#!/usr/bin/env node +/** + * Validates this plugin against Agent Plugins 1.0.0 (https://agent-plugins.org, spec/1.0.0.md) + * and every bundled skill against the Agent Skills spec (https://agentskills.io/specification). + * + * node scripts/validate-plugin.mjs + * + * Checks, in the order a conformant client loads the package: + * §4.1 every file stays inside the plugin root (no symlink escapes) + * §5 plugin.json: closed manifest, required $schema + name, name constraints, field types + * §6-7 skills//SKILL.md discovery + Agent Skills frontmatter conformance + * §7.2 mcp.json: closed config, matching $schema version, one closed variant per server + * §8 top-level directories are component locations, reverse-domain extension directories, + * or plain support files — and extensions[] keys are reverse-domain namespaces + * plus the generated artifacts: the Claude Code shim and (in the monorepo) the skill bundle. + */ +import { execFileSync } from "node:child_process" +import { + existsSync, + lstatSync, + readdirSync, + readFileSync, + realpathSync, +} from "node:fs" +import { dirname, join, relative, resolve, sep } from "node:path" +import { fileURLToPath } from "node:url" +import { validatePortableSkill } from "./lib/skill-frontmatter.mjs" + +const pluginDir = resolve(dirname(fileURLToPath(import.meta.url)), "..") +const SPEC = "1.0.0" +const PLUGIN_SCHEMA = `https://agent-plugins.org/schemas/${SPEC}/plugin.schema.json` +const MCP_SCHEMA = `https://agent-plugins.org/schemas/${SPEC}/mcp.schema.json` +const NAME_RE = /^(?!.*(?:--|\.\.))[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/ +const NAMESPACE_RE = /^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/ +const MANIFEST_FIELDS = [ + "$schema", + "name", + "version", + "description", + "author", + "homepage", + "repository", + "license", + "keywords", + "extensions", +] + +const errors = [] +const fail = (where, message) => errors.push(`${where}: ${message}`) +const isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v) +const readJson = (path) => { + try { + return JSON.parse(readFileSync(join(pluginDir, path), "utf8")) + } catch (error) { + fail(path, `not valid JSON (${error.message})`) + return + } +} + +// §4.1 — containment. +const root = realpathSync(pluginDir) +const walk = (dir) => { + for (const name of readdirSync(dir)) { + const path = join(dir, name) + if (name === "node_modules" || name === ".git") { + continue + } + const real = realpathSync(path) + if (real !== root && !real.startsWith(root + sep)) { + fail(relative(pluginDir, path), "resolves outside the plugin root") + continue + } + if (lstatSync(path).isDirectory()) { + walk(path) + } + } +} +walk(pluginDir) + +// §5 — manifest. +const manifest = readJson("plugin.json") +if (isObject(manifest)) { + for (const key of Object.keys(manifest)) { + if (!MANIFEST_FIELDS.includes(key)) { + fail( + "plugin.json", + `unknown top-level field "${key}" (client data belongs under extensions)` + ) + } + } + if (manifest.$schema !== PLUGIN_SCHEMA) { + fail("plugin.json", `$schema must be ${PLUGIN_SCHEMA}`) + } + if ( + typeof manifest.name !== "string" || + manifest.name.length > 64 || + !NAME_RE.test(manifest.name) + ) { + fail( + "plugin.json", + "name must be 1-64 chars of a-z 0-9 - . , alphanumeric at both ends, no -- or .." + ) + } + for (const key of [ + "version", + "description", + "homepage", + "repository", + "license", + ]) { + if (key in manifest && typeof manifest[key] !== "string") { + fail("plugin.json", `${key} must be a string`) + } + } + if ("author" in manifest) { + if (isObject(manifest.author)) { + for (const [k, v] of Object.entries(manifest.author)) { + if (!["name", "email", "url"].includes(k) || typeof v !== "string") { + fail("plugin.json", `author.${k} is not allowed or not a string`) + } + } + } else { + fail("plugin.json", "author must be an object") + } + } + if ( + "keywords" in manifest && + !( + Array.isArray(manifest.keywords) && + manifest.keywords.every((k) => typeof k === "string") + ) + ) { + fail("plugin.json", "keywords must be an array of strings") + } + if ("extensions" in manifest) { + if (isObject(manifest.extensions)) { + for (const [ns, value] of Object.entries(manifest.extensions)) { + if (!NAMESPACE_RE.test(ns)) { + fail( + "plugin.json", + `extensions key "${ns}" is not a reverse-domain namespace` + ) + } + if (!isObject(value)) { + fail("plugin.json", `extensions["${ns}"] must be an object`) + } + } + } else { + fail("plugin.json", "extensions must be an object") + } + } + // Codex (com.openai) resolves its extension paths against the root and requires "./". + const codexHooks = manifest.extensions?.["com.openai"]?.hooks + if ( + codexHooks !== undefined && + (typeof codexHooks !== "string" || + !codexHooks.startsWith("./com.openai/") || + !existsSync(join(pluginDir, codexHooks))) + ) { + fail( + "plugin.json", + `extensions["com.openai"].hooks must be an existing ./com.openai/ path` + ) + } +} else if (manifest !== undefined) { + fail("plugin.json", "must contain a JSON object") +} + +// §6–7.1 — skills. +const skillsDir = join(pluginDir, "skills") +let skillCount = 0 +if (existsSync(skillsDir)) { + for (const name of readdirSync(skillsDir)) { + const file = join(skillsDir, name, "SKILL.md") + if (!lstatSync(join(skillsDir, name)).isDirectory()) { + continue + } + if (!existsSync(file)) { + fail(`skills/${name}`, "directory has no SKILL.md") + continue + } + skillCount += 1 + for (const problem of validatePortableSkill( + readFileSync(file, "utf8"), + name + )) { + fail(`skills/${name}/SKILL.md`, problem) + } + } +} + +// §7.2 — MCP configuration. +if (existsSync(join(pluginDir, "mcp.json"))) { + const mcp = readJson("mcp.json") + if (isObject(mcp)) { + for (const key of Object.keys(mcp)) { + if (!["$schema", "mcpServers"].includes(key)) { + fail("mcp.json", `unknown top-level field "${key}"`) + } + } + if (mcp.$schema !== MCP_SCHEMA) { + fail( + "mcp.json", + `$schema must be ${MCP_SCHEMA} (same version as plugin.json)` + ) + } + if (!isObject(mcp.mcpServers)) { + fail("mcp.json", "mcpServers must be an object") + } + for (const [id, server] of Object.entries(mcp.mcpServers ?? {})) { + const where = `mcp.json#${id}` + const allowed = { + stdio: ["type", "command", "args", "env", "cwd"], + "streamable-http": ["type", "url", "headers"], + sse: ["type", "url", "headers"], + }[server?.type] + if (!allowed) { + fail( + where, + `type must be stdio, streamable-http or sse (got ${JSON.stringify(server?.type)})` + ) + continue + } + for (const key of Object.keys(server)) { + if (!allowed.includes(key)) { + fail( + where, + `field "${key}" does not belong to the ${server.type} variant` + ) + } + } + if (server.type === "stdio") { + if ( + typeof server.command !== "string" || + !server.command || + /\s/.test(server.command) + ) { + fail(where, "command must be one executable token") + } + if ( + server.command?.startsWith("../") || + server.command?.includes("${") + ) { + fail( + where, + "command must be a bare name or ./ path without placeholders" + ) + } + if ( + server.env && + ("PLUGIN_ROOT" in server.env || "PLUGIN_DATA" in server.env) + ) { + fail(where, "env must not set PLUGIN_ROOT / PLUGIN_DATA") + } + if ( + server.cwd !== undefined && + !/^(?:\.\/|\$\{PLUGIN_ROOT\}(?:\/|$)|\$\{PLUGIN_DATA\}(?:\/|$))/.test( + server.cwd + ) + ) { + fail( + where, + "cwd must start with ./, $PLUGIN_ROOT or $PLUGIN_DATA (placeholder form)" + ) + } + } else { + let url + try { + url = new URL(server.url) + } catch { + fail(where, "url must be an absolute URL") + } + if (url) { + const loopback = + url.hostname === "localhost" || + url.hostname === "127.0.0.1" || + url.hostname === "[::1]" + if ( + url.protocol !== "https:" && + !(url.protocol === "http:" && loopback) + ) { + fail(where, "non-loopback endpoints must use https") + } + if (url.username || url.password || url.hash) { + fail(where, "url must not contain user info or a fragment") + } + } + } + } + } else if (mcp !== undefined) { + fail("mcp.json", "must contain a JSON object") + } +} + +// §8 — top-level layout. +const SUPPORT_DIRS = new Set(["scripts", ".claude-plugin"]) +for (const name of readdirSync(pluginDir)) { + if ( + !lstatSync(join(pluginDir, name)).isDirectory() || + name === "skills" || + name === "node_modules" + ) { + continue + } + if (NAMESPACE_RE.test(name)) { + if (!(name in (manifest?.extensions ?? {}))) { + fail( + name, + "extension directory has no matching extensions entry in plugin.json" + ) + } + } else if (!SUPPORT_DIRS.has(name)) { + fail( + name, + "unexpected top-level directory (client files belong in a reverse-domain extension directory)" + ) + } +} +for (const legacy of [".codex-plugin", ".mcp.json", "hooks", "agents"]) { + if (existsSync(join(pluginDir, legacy))) { + fail( + legacy, + "legacy location — the portable files are plugin.json, mcp.json, skills/ and com.openai/" + ) + } +} + +// Generated artifacts must be current. +const runCheck = (script) => { + try { + execFileSync( + process.execPath, + [join(pluginDir, "scripts", script), "--check"], + { stdio: "pipe" } + ) + } catch (error) { + fail( + `scripts/${script}`, + (error.stderr?.toString() || error.message).trim() + ) + } +} +runCheck("build-client-manifests.mjs") +if (existsSync(resolve(pluginDir, "../skills"))) { + runCheck("sync-skills.mjs") +} + +if (errors.length) { + console.error(`✖ Agent Plugins ${SPEC} validation failed:\n`) + for (const error of errors) { + console.error(` • ${error}`) + } + process.exit(1) +} +console.log( + `✔ ${manifest.name}@${manifest.version} conforms to Agent Plugins ${SPEC} (${skillCount} skills, ${Object.keys(readJson("mcp.json")?.mcpServers ?? {}).length} MCP servers).` +) diff --git a/libs/ui/agent-plugin/skills/accordion-usage/SKILL.md b/libs/ui/agent-plugin/skills/accordion-usage/SKILL.md index 748b95cd01..3f2c8aec76 100644 --- a/libs/ui/agent-plugin/skills/accordion-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/accordion-usage/SKILL.md @@ -1,23 +1,16 @@ --- -component_version: "1.0.0" name: accordion-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Accordion for collapsible sections using the Zag.js-backed compound anatomy, supported variant, shadow, size, value, multiple, and collapsible props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/accordion.tsx" - - "libs/ui/src/tokens/components/molecules/_accordion.css" - - "libs/ui/stories/molecules/accordion.stories.tsx" - - "libs/ui/src/molecules/accordion.figma.ts" - - "https://zagjs.com/components/react/accordion" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/accordion.tsx libs/ui/src/tokens/components/molecules/_accordion.css libs/ui/stories/molecules/accordion.stories.tsx libs/ui/src/molecules/accordion.figma.ts https://zagjs.com/components/react/accordion" --- # @techsio/ui-kit Accordion Usage @@ -25,6 +18,45 @@ sources: Use Accordion for related collapsible content sections. Do not build disclosure behavior with local state and native buttons when this component fits. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Accordion`. + +**Use it when** + +- Optional or secondary content the user may want to expand: FAQs, shipping details, advanced settings. +- Long pages where users scan headings and open only what they need. +- Mobile layouts that stack content which sits side by side on desktop (with care — see Don't). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Peer views of the same object the user switches between | Tabs | +| Ordered steps that must be completed in sequence | Steps | +| Navigation links in a tree | VerticalNavigation | +| Content everyone needs to read (prices, legal terms, errors) | keep it visible, no disclosure | + +**Do** + +- Write triggers as short noun phrases or questions that predict the content (`Shipping and returns`, `Can I change my order?`). +- Open the first item by default when it answers the most common question; otherwise start collapsed. +- Allow multiple open items for reference content; single-open only when items are mutually exclusive steps. +- Keep the chevron/indicator from the component — it is the only affordance that the heading is interactive. + +**Don't** + +- Hide required form fields or validation errors inside a collapsed item. +- Nest accordions inside accordions. +- Put primary actions only inside a collapsed panel. +- Use an accordion for one item — show the content or use a Link to a detail page. + +**Copy and states** + +- Trigger text in sentence case, no trailing colon or punctuation. +- If a collapsed section contains an error, open it on submit and focus the field (see ux-guidelines/states). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/action-icon-usage/SKILL.md b/libs/ui/agent-plugin/skills/action-icon-usage/SKILL.md index ffe1e44315..8533b60d27 100644 --- a/libs/ui/agent-plugin/skills/action-icon-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/action-icon-usage/SKILL.md @@ -4,10 +4,12 @@ description: > Use when an app needs the @techsio/ui-kit ActionIcon atom — an icon-only, square action button (toolbar/inline actions). Covers variant, size, disabled state, accessible labelling, and token-first styling. -type: core -library: "@techsio/ui-kit" -component: ActionIcon -component_version: "1.0.0" +metadata: + type: "core" + library: "@techsio/ui-kit" + component: "ActionIcon" + component_version: "1.0.0" + requires: "ux-guidelines" --- # ActionIcon usage @@ -15,12 +17,44 @@ component_version: "1.0.0" `ActionIcon` is an icon-only, square action button for compact/inline actions where a full `Button` with a text label would be too heavy (toolbars, table row actions, input adornments). -## When to use +## UX/UI guidelines -- A single icon communicates the action (close, edit, delete, expand). -- Space is constrained and a labelled `Button` does not fit. +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `ActionIcon`. -Otherwise prefer `Button` (with `icon` props) so the action keeps a visible text label. +**Use it when** + +- A single, universally understood icon communicates the action: close, edit, delete, more, expand, copy. +- Space is constrained: table row actions, toolbars, input adornments, card corners. +- The action repeats many times on the screen, so a text label would be noise. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| The action is the main thing on the surface, or the icon is ambiguous | Button with `icon` and a text label | +| Navigation to another page | Link / LinkButton | +| Several actions on one row | one ActionIcon that opens a Menu (`More actions`) | +| Decorative icon next to text | Icon | + +**Do** + +- Always pass `aria-label` as a verb + object: `Delete product`, `Close notification`, `Copy link`. +- Pair it with a Tooltip carrying the same text on pointer devices. +- Keep the destructive one last in a group and use the danger tone. +- Keep hit area ≥ 24×24 CSS px (the kit sizes do); prefer `md` for touch surfaces. + +**Don't** + +- Use icon-only actions for the page's primary action. +- Invent icons for abstract actions (`Sync`, `Reconcile`) — use a labelled Button. +- Put two different actions with similar icons side by side (edit pencil vs rename pencil). +- Disable an ActionIcon without explaining why elsewhere — tooltips don't show on disabled controls. + +**Copy and states** + +- The `aria-label` and Tooltip text follow button-label rules from ux-guidelines/ux-writing. +- Destructive icon actions still confirm or offer Undo (ux-guidelines/feedback-and-actions). ## Accessibility diff --git a/libs/ui/agent-plugin/skills/app-token-initialization/SKILL.md b/libs/ui/agent-plugin/skills/app-token-initialization/SKILL.md index 78115bb71c..db1a2aa866 100644 --- a/libs/ui/agent-plugin/skills/app-token-initialization/SKILL.md +++ b/libs/ui/agent-plugin/skills/app-token-initialization/SKILL.md @@ -4,19 +4,12 @@ description: > Use when adopting @techsio/ui-kit in a new or existing apps/* project and setting up app semantic, typography, spacing, layout, radius, state, and minimal component token override files after a focused design-token interview. -type: lifecycle -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - tailwind-token-authoring -sources: - - "libs/ui/skills/_artifacts/consumer_app_usage_rules.md" - - "libs/ui/src/tokens/index.css" - - "libs/ui/src/tokens/_semantic.css" - - "libs/ui/src/tokens/_layout.css" - - "libs/ui/src/tokens/_spacing.css" - - "libs/ui/src/tokens/components/components.css" - - "https://github.com/TechsioCZ/new-engine/issues/72" +metadata: + type: "lifecycle" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "tailwind-token-authoring" + sources: "libs/ui/skills/_artifacts/consumer_app_usage_rules.md libs/ui/src/tokens/index.css libs/ui/src/tokens/_semantic.css libs/ui/src/tokens/_layout.css libs/ui/src/tokens/_spacing.css libs/ui/src/tokens/components/components.css https://github.com/TechsioCZ/new-engine/issues/72" --- # @techsio/ui-kit App Token Initialization diff --git a/libs/ui/agent-plugin/skills/app-token-overrides/SKILL.md b/libs/ui/agent-plugin/skills/app-token-overrides/SKILL.md index 66bb6ab89b..9b9526f98c 100644 --- a/libs/ui/agent-plugin/skills/app-token-overrides/SKILL.md +++ b/libs/ui/agent-plugin/skills/app-token-overrides/SKILL.md @@ -5,18 +5,12 @@ description: > typography, spacing, layout, radius, or component CSS token overrides while avoiding redundant token chains, duplicated JSX className styling, and permanent local API-gap workarounds. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - tailwind-token-authoring -sources: - - "libs/ui/skills/_artifacts/consumer_app_usage_rules.md" - - "libs/ui/src/tokens/_semantic.css" - - "libs/ui/src/tokens/components/atoms/_button.css" - - "libs/ui/src/tokens/components/molecules/_dialog.css" - - "libs/ui/src/tokens/components/components.css" - - "https://github.com/TechsioCZ/new-engine/issues/72" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "tailwind-token-authoring" + sources: "libs/ui/skills/_artifacts/consumer_app_usage_rules.md libs/ui/src/tokens/_semantic.css libs/ui/src/tokens/components/atoms/_button.css libs/ui/src/tokens/components/molecules/_dialog.css libs/ui/src/tokens/components/components.css https://github.com/TechsioCZ/new-engine/issues/72" --- # @techsio/ui-kit App Token Overrides diff --git a/libs/ui/agent-plugin/skills/app-ui-kit-audit/SKILL.md b/libs/ui/agent-plugin/skills/app-ui-kit-audit/SKILL.md index 1a4d4cbd09..2862366a47 100644 --- a/libs/ui/agent-plugin/skills/app-ui-kit-audit/SKILL.md +++ b/libs/ui/agent-plugin/skills/app-ui-kit-audit/SKILL.md @@ -5,18 +5,12 @@ description: > existing component usage, native HTML replacement, nonexistent props, duplicate className styling, token-first violations, framework adapter mistakes, unnecessary wrappers, and UI-kit API gaps. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/skills/_artifacts/consumer_app_usage_rules.md" - - "libs/ui/package.json" - - "libs/ui/src/atoms" - - "libs/ui/src/molecules" - - "libs/ui/src/tokens/components" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides" + sources: "libs/ui/skills/_artifacts/consumer_app_usage_rules.md libs/ui/package.json libs/ui/src/atoms libs/ui/src/molecules libs/ui/src/tokens/components" --- # @techsio/ui-kit App Adoption Audit diff --git a/libs/ui/agent-plugin/skills/badge-usage/SKILL.md b/libs/ui/agent-plugin/skills/badge-usage/SKILL.md index 0bb775739e..313b384dc6 100644 --- a/libs/ui/agent-plugin/skills/badge-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/badge-usage/SKILL.md @@ -1,21 +1,16 @@ --- -component_version: "1.0.0" name: badge-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Badge for compact status, category, discount, or metadata labels without duplicating token-backed color and spacing classes in JSX. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/badge.tsx" - - "libs/ui/src/tokens/components/atoms/_badge.css" - - "libs/ui/stories/atoms/badge.stories.tsx" - - "libs/ui/src/atoms/badge.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/badge.tsx libs/ui/src/tokens/components/atoms/_badge.css libs/ui/stories/atoms/badge.stories.tsx libs/ui/src/atoms/badge.figma.ts" --- # @techsio/ui-kit Badge Usage @@ -23,6 +18,45 @@ sources: Use Badge for short non-interactive labels. It is not a button, link, alert, or long message container. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Badge`. + +**Use it when** + +- Short, non-interactive labels of state or category: `Draft`, `Paid`, `New`, `−20 %`. +- Counts next to navigation items or tabs (unread, pending). +- Product flags on cards (discount, new, out of stock). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A sentence explaining something | StatusText | +| Something the user can click or remove | Button / ActionIcon (removable filter chips live in the filter UI) | +| A page-level alert | inline StatusText section at the top of the content | +| Confirming an action | Toast | + +**Do** + +- Use one word, two at most; map each state to one variant app-wide (see the status table in ux-guidelines/ux-writing). +- Pair colour with the word — the text carries the meaning, colour only supports it. +- Place status badges next to the title they describe (page header meta, first table column or a Status column). +- Use `discount` only for price reductions, `danger` only for failed/blocked states. + +**Don't** + +- Mix synonyms (`Live`, `Active`, `Enabled`) for one state. +- Use a badge as a button or link. +- Stack more than two badges on one item — prioritise. +- Show a `0` count badge; hide it until there is something to count. + +**Copy and states** + +- Sentence case, no punctuation; counts formatted with `Intl.NumberFormat` (`1,204`), capped (`99+`) in navigation. +- Give count badges context for screen readers (`3 unread messages`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/breadcrumb-usage/SKILL.md b/libs/ui/agent-plugin/skills/breadcrumb-usage/SKILL.md index 2baf32b4e7..d56e2cd58f 100644 --- a/libs/ui/agent-plugin/skills/breadcrumb-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/breadcrumb-usage/SKILL.md @@ -1,28 +1,59 @@ --- -component_version: "1.0.0" name: breadcrumb-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Breadcrumb for page hierarchy navigation with Link/NextLink adapters, current page state, separators, ellipsis, icons, size, and underline variant. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/molecules/breadcrumb.tsx" - - "libs/ui/src/tokens/components/molecules/_breadcrumb.css" - - "libs/ui/stories/molecules/breadcrumb.stories.tsx" - - "libs/ui/src/molecules/breadcrumb.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/breadcrumb.tsx libs/ui/src/tokens/components/molecules/_breadcrumb.css libs/ui/stories/molecules/breadcrumb.stories.tsx libs/ui/src/molecules/breadcrumb.figma.ts" --- # @techsio/ui-kit Breadcrumb Usage Use Breadcrumb for location hierarchy, not for primary navigation tabs. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Breadcrumb`. + +**Use it when** + +- Pages three or more levels deep in a hierarchy (catalog → category → product; admin → section → record). +- Record editors and detail pages, so users can return to the list they came from. +- Storefront category and product pages, where it also helps SEO. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Switching peer views | Tabs | +| Progress through a task | Steps | +| Primary site navigation | Header / Sidebar + VerticalNavigation | +| Browser-history back | a `Back to list` Button in the page header | + +**Do** + +- Start at the root users recognise (`Home` or the section), end with the current page as plain text (not a link). +- Mirror the information architecture, not the click history. +- Truncate the middle on small screens, never the current page. +- Use the same labels as the navigation and page titles. + +**Don't** + +- Link the current page to itself. +- Use breadcrumbs on top-level pages where they only contain `Home`. +- Replace the page title with the last breadcrumb — keep both. + +**Copy and states** + +- Labels are page names in sentence case; separators come from the component, never typed characters. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/button-usage/SKILL.md b/libs/ui/agent-plugin/skills/button-usage/SKILL.md index c6e583cfdb..74f6a06338 100644 --- a/libs/ui/agent-plugin/skills/button-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/button-usage/SKILL.md @@ -4,19 +4,14 @@ description: > Use after component-usage-ux when an app needs @techsio/ui-kit Button for actions, including correct variant, theme, size, loading state, icon props, and token-first styling rules. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -component: Button -component_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/button.tsx" - - "libs/ui/src/tokens/components/atoms/_button.css" - - "libs/ui/stories/atoms/button.stories.tsx" - - "libs/ui/src/atoms/button.figma.ts" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + component: "Button" + component_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/button.tsx libs/ui/src/tokens/components/atoms/_button.css libs/ui/stories/atoms/button.stories.tsx libs/ui/src/atoms/button.figma.ts" --- # @techsio/ui-kit Button Usage @@ -24,6 +19,48 @@ sources: Use Button for in-place actions. Use `LinkButton` for navigation that should look like a button. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Button`. + +**Use it when** + +- Any action that changes state on the current page: save, create, delete, open a dialog, apply filters. +- Form submission and dialog/drawer footers. +- Page-level commands in the page header (`New product`). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Navigate to a URL, styled as a button | LinkButton | +| Inline navigation in text | Link | +| Compact repeated icon action | ActionIcon | +| More than three related commands | Menu behind one Button | +| Toggle a setting on/off | Switch | + +**Do** + +- One `variant="primary"` per surface; secondary actions use `secondary` / `outlined` / `borderless`. +- Order groups `[Cancel] [Primary]` — primary last, at the end of the container (ux-guidelines/feedback-and-actions). +- Use `variant="danger"` only for destructive actions and keep them away from Save. +- Show progress with `isLoading` + `loadingText` (`Saving…`) on the button that started the action. +- Add an `icon` only when it speeds recognition (`+` for New, trash for Delete); keep the text. + +**Don't** + +- Use `OK`, `Yes`, `Submit`, `Confirm` or `Click here` as labels. +- Put two primary buttons side by side. +- Disable a submit button when the user can't see why (ux-guidelines/states#disabled). +- Fake a link with `onClick={() => router.push()}` — use LinkButton. +- Change the label to a result (`Saved!`); confirm with a Toast instead. + +**Copy and states** + +- Verb + object in sentence case: `New product`, `Create product`, `Save changes`, `Delete product`, `Discard changes` (full table in ux-guidelines/ux-writing#button-labels). +- `Cancel` leaves a form without saving; `Close` closes something with no pending changes. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/carousel-usage/SKILL.md b/libs/ui/agent-plugin/skills/carousel-usage/SKILL.md index dc8798cf77..43225cac6f 100644 --- a/libs/ui/agent-plugin/skills/carousel-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/carousel-usage/SKILL.md @@ -1,24 +1,16 @@ --- -component_version: "1.0.0" name: carousel-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Carousel for Zag.js-backed slides, images, controls, indicators, autoplay, sizing, aspect ratio, object fit, and framework image adapters. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/carousel.tsx" - - "libs/ui/src/tokens/components/molecules/_carousel.css" - - "libs/ui/stories/molecules/carousel.stories.tsx" - - "libs/ui/src/molecules/carousel.figma.ts" - - "https://zagjs.com/components/react/carousel" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/carousel.tsx libs/ui/src/tokens/components/molecules/_carousel.css libs/ui/stories/molecules/carousel.stories.tsx libs/ui/src/molecules/carousel.figma.ts https://zagjs.com/components/react/carousel" --- # @techsio/ui-kit Carousel Usage @@ -26,6 +18,42 @@ sources: Use Carousel for slide-based browsing. Use Gallery for product image galleries with thumbnails. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Carousel`. + +**Use it when** + +- Horizontally browsable sets of equal items: related products, banners, testimonials. +- Content where only part needs to be visible and the rest is optional. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Product images with thumbnails and zoom | Gallery | +| Content every user must see (key offer, required info) | a static layout — most users never swipe | +| Navigating between steps | Steps | +| A long list of results | a grid with Pagination | + +**Do** + +- Show a partial next item or visible controls so users know there is more. +- Provide previous/next controls and indicators with accessible labels; keep swipe as an addition, not the only way. +- Stop any auto-rotation on hover, focus and with `prefers-reduced-motion`; offer pause. +- Keep slides the same height to avoid layout shift. + +**Don't** + +- Auto-rotate promotional banners faster than users can read (or at all, if the content has actions). +- Put critical CTAs only on slide 3+. +- Nest carousels or put a carousel inside a horizontally scrolling area. + +**Copy and states** + +- Controls labelled `Previous slide` / `Next slide`; indicators `Go to slide 2 of 5`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/cascade-select-usage/SKILL.md b/libs/ui/agent-plugin/skills/cascade-select-usage/SKILL.md index 1f1c09719f..fb80188289 100644 --- a/libs/ui/agent-plugin/skills/cascade-select-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/cascade-select-usage/SKILL.md @@ -1,23 +1,17 @@ --- -component_version: "1.0.1" name: cascade-select-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit CascadeSelect for choosing a value from hierarchical data with Zag.js path values, compound parts, parent or multiple selection, validation, and keyboard navigation. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/cascade-select.tsx" - - "libs/ui/src/tokens/components/molecules/_cascade-select.css" - - "libs/ui/stories/molecules/cascade-select.stories.tsx" - - "https://zagjs.com/components/react/cascade-select" +metadata: + component_version: "1.0.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/cascade-select.tsx libs/ui/src/tokens/components/molecules/_cascade-select.css libs/ui/stories/molecules/cascade-select.stories.tsx https://zagjs.com/components/react/cascade-select" --- # @techsio/ui-kit CascadeSelect Usage @@ -25,6 +19,41 @@ sources: Use CascadeSelect for form choices whose options have parent/child levels. Use Select for a flat known list and TreeView for hierarchical navigation. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `CascadeSelect`. + +**Use it when** + +- A form value that lives inside a hierarchy: category → subcategory, region → city. +- Hierarchies deep or wide enough that one flat Select would be unreadable. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A flat list of known options | Select | +| A flat list the user needs to search | Combobox | +| Navigating categories (links) | VerticalNavigation | +| Selecting nodes in a tree widget with arrow keys | TreeView | + +**Do** + +- Show the full path of the selected value in the trigger (`Clothing › Shirts › Linen`). +- Let users select parent levels only when a parent is a valid value; otherwise only leaves are selectable. +- Keep level labels short; order siblings meaningfully (alphabetical or by popularity), consistently. + +**Don't** + +- Use it for two levels with a handful of items — two Selects or a grouped Select read faster. +- Mix navigation links into a form control. + +**Copy and states** + +- Label is the noun of the final value (`Category`), placeholder `Select a category`. +- Path separator comes from the component; don't type `>` or `/`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/chart-usage/SKILL.md b/libs/ui/agent-plugin/skills/chart-usage/SKILL.md index 389ac55356..d655e3cc45 100644 --- a/libs/ui/agent-plugin/skills/chart-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/chart-usage/SKILL.md @@ -1,5 +1,4 @@ --- -component_version: "1.0.0" name: chart-usage description: > Use after component-usage-ux when an app needs the @techsio/ui-kit Chart — @@ -8,17 +7,13 @@ description: > area, bar, horizontal bar, scatter, pie and donut charts from the same data/x/y/series channels, with token-driven series colors, legend, tooltip, value formatting and selection callbacks. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/chart.tsx" - - "libs/ui/src/tokens/components/molecules/_chart.css" - - "libs/ui/stories/molecules/chart.stories.tsx" - - "https://tanstack.com/charts/v0/docs/overview" # supported upstream: 0.6.x +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/chart.tsx libs/ui/src/tokens/components/molecules/_chart.css libs/ui/stories/molecules/chart.stories.tsx https://tanstack.com/charts/v0/docs/overview" --- # @techsio/ui-kit Chart Usage @@ -29,6 +24,41 @@ channels and pick a `type`; it picks the right marks and scales, wires the tooltip and legend, and paints every series from the `--color-chart-series-*` design tokens in fixed order. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Chart`. + +**Use it when** + +- Trends over time (line/area), comparisons between categories (bar), composition of a small whole (≤ 5 parts). +- Dashboards and reports where the shape of the data matters more than exact values. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Exact values the user looks up or copies | Table / DataTable | +| A single KPI | a stat tile with the formatted number | +| More than 5 slices of a whole | a bar chart, not a pie/donut | + +**Do** + +- Pick the chart type by the question (ux-guidelines/component-selection); keep series colours from `--color-chart-series-*` in fixed order. +- Format axis ticks and tooltips with the app locale (`Intl.NumberFormat`, `Intl.DateTimeFormat`) and label units. +- Provide a text summary or a table alternative for screen readers. +- Show loading (Skeleton), empty (`No orders in this period`) and error (`Try again`) states instead of empty axes. + +**Don't** + +- Rely on colour alone to distinguish series — keep the legend and labels. +- Use 3D, gradients or heavy grid lines. +- Animate the entrance when the user prefers reduced motion. + +**Copy and states** + +- Title says the insight or metric and period: `Revenue, last 30 days`; axis labels include the unit. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/checkbox-usage/SKILL.md b/libs/ui/agent-plugin/skills/checkbox-usage/SKILL.md index 2a7f3b27a2..ab626d1234 100644 --- a/libs/ui/agent-plugin/skills/checkbox-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/checkbox-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.0" name: checkbox-usage description: > Use after component-usage-ux when an app needs the low-level @techsio/ui-kit Checkbox atom, including invalid and indeterminate states, while preferring form molecules for labeled form rows. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/checkbox.tsx" - - "libs/ui/src/molecules/form-checkbox.tsx" - - "libs/ui/src/tokens/components/atoms/_checkbox.css" - - "libs/ui/stories/atoms/checkbox.stories.tsx" - - "libs/ui/src/atoms/checkbox.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/checkbox.tsx libs/ui/src/molecules/form-checkbox.tsx libs/ui/src/tokens/components/atoms/_checkbox.css libs/ui/stories/atoms/checkbox.stories.tsx libs/ui/src/atoms/checkbox.figma.ts" --- # @techsio/ui-kit Checkbox Usage @@ -24,6 +18,39 @@ sources: Use `Checkbox` for the bare control. Use `FormCheckbox` when the UI includes a label, helper text, validation text, or a form-field layout. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Checkbox`. + +**Use it when** + +- Bare selection controls inside tables and lists (row selection, select all). +- Custom compositions where the label/help structure is provided by the surrounding component. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A checkbox with its own label, help or error | FormCheckbox | +| A setting that applies immediately | Switch | +| One exclusive option | RadioGroup | + +**Do** + +- Give bare checkboxes an accessible name (`Select Linen shirt`, `Select all products on this page`). +- Use the indeterminate state for a partially selected group. +- Make the whole row or label clickable where the pattern allows. + +**Don't** + +- Use a checkbox to trigger an immediate action (that is a Switch or Button). +- Reverse meaning with negative labels (`Don't send emails`). + +**Copy and states** + +- Positive, parallel statements; no trailing punctuation for short labels. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/color-select-usage/SKILL.md b/libs/ui/agent-plugin/skills/color-select-usage/SKILL.md index 2e46914929..eb8496c5bd 100644 --- a/libs/ui/agent-plugin/skills/color-select-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/color-select-usage/SKILL.md @@ -1,20 +1,16 @@ --- -component_version: "1.0.0" name: color-select-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit ColorSelect for choosing color swatches with supported layout, size, radius, disabled state, selected state, labels, counts, and single/multiple roles. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/color-select.tsx" - - "libs/ui/src/tokens/components/molecules/_color-select.css" - - "libs/ui/stories/molecules/color-select.stories.tsx" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/color-select.tsx libs/ui/src/tokens/components/molecules/_color-select.css libs/ui/stories/molecules/color-select.stories.tsx" --- # @techsio/ui-kit ColorSelect Usage @@ -22,6 +18,37 @@ sources: Use ColorSelect for product color filters or swatch choices. It is not a general Select replacement. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `ColorSelect`. + +**Use it when** + +- Choosing a product colour/variant by swatch, or filtering a catalog by colour. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Choosing any other value | Select / RadioGroup | +| Brand theme colour in admin settings | a dedicated color input, not a swatch filter | + +**Do** + +- Give every swatch an accessible name with the colour's product name (`Navy`), not the hex value. +- Show the selected colour's name next to the control. +- Mark unavailable colours as unavailable (not just faded) and explain on the product page. + +**Don't** + +- Rely on colour alone — the name must be visible or available on focus. +- Use more than ~12 swatches without grouping or a More option. + +**Copy and states** + +- Label `Colour` (or `Color`, per locale); selected value shown as `Colour: Navy`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/combobox-usage/SKILL.md b/libs/ui/agent-plugin/skills/combobox-usage/SKILL.md index b043f6f883..528d7aec77 100644 --- a/libs/ui/agent-plugin/skills/combobox-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/combobox-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.3.0" name: combobox-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Combobox for searchable selection with Zag.js collection behavior, controlled value/input, multiple mode, validation status, clear trigger, and token styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/combobox.tsx" - - "libs/ui/src/tokens/components/molecules/_combobox.css" - - "libs/ui/stories/molecules/combobox.stories.tsx" - - "libs/ui/src/molecules/combobox.figma.ts" - - "https://zagjs.com/components/react/combobox" +metadata: + component_version: "1.3.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/combobox.tsx libs/ui/src/tokens/components/molecules/_combobox.css libs/ui/stories/molecules/combobox.stories.tsx libs/ui/src/molecules/combobox.figma.ts https://zagjs.com/components/react/combobox" --- # @techsio/ui-kit Combobox Usage @@ -24,6 +18,41 @@ sources: Use Combobox for searchable select-like input. Use Select when search/filtering is not needed. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Combobox`. + +**Use it when** + +- Choosing one or more values from a long list where typing is the fastest way to find it (countries, customers, products, tags). +- Lists loaded asynchronously as the user types. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Fewer than ~10 known options | Select (or RadioGroup for 2–5 visible options) | +| Search that navigates to pages/products | SearchSuggestions / SearchForm | +| Running app commands | Command | +| Hierarchical values | CascadeSelect | + +**Do** + +- Show a helpful empty state (`No customers match “nor”`) and, when allowed, a create option (`Create “Nora”`). +- Debounce remote search and show loading inside the listbox, not a page spinner. +- Keep selected values visible (tags for multiple) and removable by keyboard. +- Highlight the matched part of each option. + +**Don't** + +- Allow free text when the value must come from the list — validate or restrict. +- Open the list with hundreds of items before the user types; show recent or popular items instead. + +**Copy and states** + +- Placeholder is an example or instruction (`Search customers…`), never the label. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/command-usage/SKILL.md b/libs/ui/agent-plugin/skills/command-usage/SKILL.md index 8dc44736ec..82fdd03117 100644 --- a/libs/ui/agent-plugin/skills/command-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/command-usage/SKILL.md @@ -1,25 +1,16 @@ --- -component_version: "1.0.1" name: command-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Command for a searchable action list or a Dialog-based command palette with compound parts, grouped results, disabled actions, and optional shortcut hints. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/command.tsx" - - "libs/ui/src/internal/molecules/command.context.ts" - - "libs/ui/src/tokens/components/molecules/_command.css" - - "libs/ui/stories/molecules/command.stories.tsx" - - "libs/ui/src/molecules/dialog.tsx" - - "libs/ui/test/command.spec.ts" - - "https://zagjs.com/components/react/combobox" +metadata: + component_version: "1.0.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/command.tsx libs/ui/src/internal/molecules/command.context.ts libs/ui/src/tokens/components/molecules/_command.css libs/ui/stories/molecules/command.stories.tsx libs/ui/src/molecules/dialog.tsx libs/ui/test/command.spec.ts https://zagjs.com/components/react/combobox" --- # @techsio/ui-kit Command Usage @@ -27,6 +18,40 @@ sources: Use Command for searching and activating application actions. Use Combobox for a form value, Menu for a short menu without search, and Dialog for the modal shell. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Command`. + +**Use it when** + +- A keyboard-first palette for searching and running application actions and navigation (⌘K). +- Power-user workflows in admin tools where many commands exist. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Choosing a form value | Combobox | +| A short list of actions for one object | Menu | +| Catalog search for shoppers | SearchSuggestions | + +**Do** + +- Group results (Navigation, Actions, Recent) with clear headings; put the most likely result first. +- Show each command's shortcut with Hotkeys. +- Name commands as verbs + objects (`Create product`, `Go to orders`). +- Confirm destructive commands the same way the UI does (alertdialog or Undo). + +**Don't** + +- Make the palette the only way to reach a feature. +- Execute on highlight; execute on Enter/click only. + +**Copy and states** + +- Placeholder `Type a command or search…`; empty state `No results for “exprt”`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/component-authoring/SKILL.md b/libs/ui/agent-plugin/skills/component-authoring/SKILL.md index 12e0c5a7db..62a802d09b 100644 --- a/libs/ui/agent-plugin/skills/component-authoring/SKILL.md +++ b/libs/ui/agent-plugin/skills/component-authoring/SKILL.md @@ -5,21 +5,12 @@ description: > including atoms, molecules, organisms, tv() variants, React 19 ref props, component token CSS, Storybook stories, package subpath exports, and Figma handoff reminders. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - tailwind-token-authoring - - storybook-authoring -sources: - - "libs/ui/AGENTS.md" - - "libs/ui/README.md" - - "libs/ui/src/atoms/button.tsx" - - "libs/ui/src/molecules/carousel.tsx" - - "libs/ui/src/organisms/header.tsx" - - "libs/ui/src/tokens/components/components.css" - - "libs/ui/stories/atoms/button.stories.tsx" - - "https://github.com/TechsioCZ/new-engine/issues/295" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "tailwind-token-authoring storybook-authoring" + sources: "libs/ui/AGENTS.md libs/ui/README.md libs/ui/src/atoms/button.tsx libs/ui/src/molecules/carousel.tsx libs/ui/src/organisms/header.tsx libs/ui/src/tokens/components/components.css libs/ui/stories/atoms/button.stories.tsx https://github.com/TechsioCZ/new-engine/issues/295" --- # @techsio/ui-kit Component Authoring diff --git a/libs/ui/agent-plugin/skills/component-consistency-validation/SKILL.md b/libs/ui/agent-plugin/skills/component-consistency-validation/SKILL.md index 361dbb0cc1..4e3844b8e5 100644 --- a/libs/ui/agent-plugin/skills/component-consistency-validation/SKILL.md +++ b/libs/ui/agent-plugin/skills/component-consistency-validation/SKILL.md @@ -5,18 +5,11 @@ description: > stories, Figma Code Connect files, package exports, or validation scripts. Checks prop/variant/token/story/Figma consistency and recommends focused commands before maintainer handoff. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -sources: - - "libs/ui/package.json" - - "libs/ui/project.json" - - "libs/ui/.storybook/main.ts" - - "libs/ui/scripts/validate-token-usage.js" - - "libs/ui/scripts/validate-token-definitions.js" - - "libs/ui/scripts/run-component-tests.mjs" - - "libs/ui/figma.config.json" - - "libs/ui/skills/_artifacts/domain_map.yaml" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + sources: "libs/ui/package.json libs/ui/project.json libs/ui/.storybook/main.ts libs/ui/scripts/validate-token-usage.js libs/ui/scripts/validate-token-definitions.js libs/ui/scripts/run-component-tests.mjs libs/ui/figma.config.json libs/ui/skills/_artifacts/domain_map.yaml" --- # @techsio/ui-kit Component Consistency Validation diff --git a/libs/ui/agent-plugin/skills/component-usage-ux/SKILL.md b/libs/ui/agent-plugin/skills/component-usage-ux/SKILL.md index 4b83765a09..7179988f35 100644 --- a/libs/ui/agent-plugin/skills/component-usage-ux/SKILL.md +++ b/libs/ui/agent-plugin/skills/component-usage-ux/SKILL.md @@ -4,24 +4,27 @@ description: > Use as the apps/* UI-kit usage orchestrator. Chooses which @techsio/ui-kit component and per-component *-usage skill to load before selecting props, variants, themes, sizes, slots, framework adapters, and token overrides. -type: composition -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - app-token-overrides -sources: - - "libs/ui/skills/_artifacts/consumer_app_usage_rules.md" - - "libs/ui/src/atoms" - - "libs/ui/src/molecules" - - "libs/ui/src/organisms" - - "libs/ui/package.json" - - "https://zagjs.com/components/react/combobox" +metadata: + type: "composition" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "app-token-overrides" + sources: "libs/ui/skills/_artifacts/consumer_app_usage_rules.md libs/ui/src/atoms libs/ui/src/molecules libs/ui/src/organisms libs/ui/package.json https://zagjs.com/components/react/combobox" --- # @techsio/ui-kit Component Usage UX Use this in apps. It is a router, not a complete component manual. +Load order for any UI work: + +```text +component-usage-ux -> which component fits the intent +ux-guidelines -> house UX rules: copy, dates/numbers, alignment, states, + where actions and feedback live, component selection +-usage -> the component API, plus its "UX/UI guidelines" section +``` + ## Setup Find the component and load its usage skill: @@ -54,9 +57,13 @@ Searchable action panel -> command-usage (plus dialog-usage for a modal shell) ### Start from UX intent +The full intent → component table (with the look-alikes to avoid) is +`ux-guidelines/references/component-selection.md`. + ```text Destructive action -> Button danger, maybe Dialog confirmation CRUD success/error -> Toast or StatusText depending persistence and context +Where the CRUD buttons go -> ux-guidelines (page actions top-right, form actions bottom-right) Hierarchical form choice -> CascadeSelect Hierarchical page/category navigation -> VerticalNavigation Tree widget selection -> TreeView diff --git a/libs/ui/agent-plugin/skills/data-table-usage/SKILL.md b/libs/ui/agent-plugin/skills/data-table-usage/SKILL.md index 2a16ac4238..a3d9dddf52 100644 --- a/libs/ui/agent-plugin/skills/data-table-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/data-table-usage/SKILL.md @@ -1,5 +1,4 @@ --- -component_version: "1.2.0" name: data-table-usage description: > Use after component-usage-ux when an app needs the @techsio/ui-kit DataTable — @@ -8,17 +7,13 @@ description: > column filters, global search, row selection, column visibility/pinning/reorder, row reorder, tree/expanding rows, inline edit, colSpan/rowSpan, virtualization / infinite scroll and pagination — every feature behind a flag with a callback. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides - - table-usage -sources: - - "libs/ui/src/organisms/data-table.tsx" - - "libs/ui/src/organisms/data-table.helpers.ts" - - "libs/ui/stories/organisms/data-table.stories.tsx" +metadata: + component_version: "1.2.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides table-usage ux-guidelines" + sources: "libs/ui/src/organisms/data-table.tsx libs/ui/src/organisms/data-table.helpers.ts libs/ui/stories/organisms/data-table.stories.tsx" --- # @techsio/ui-kit DataTable Usage @@ -29,6 +24,47 @@ renders into the presentational `Table` organism, so it inherits every when you only need static markup; reach for `DataTable` when you need sorting/filtering/selection/pagination and friends. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `DataTable`. + +**Use it when** + +- Collections users search, sort, filter, select, edit inline and page through — the default for admin lists. +- Any list that is the main content of a page (orders, products, customers, pages). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A small static table (spec sheet, invoice lines) | Table | +| Browsing products visually | a ProductCard grid | +| Showing a trend | Chart | +| Editing a whole record with validation across fields | the drawer/full form, not inline edit | + +**Do** + +- Align numeric columns (money, counts, percentages) with `meta: { align: "end" }` and render them with `tabular-nums`; text and dates stay start-aligned (ux-guidelines/formatting#alignment-in-tables). +- Format cells with the app's `Intl` formatters; render missing values as `—`. +- Row click = read (detail drawer); edit and delete live in `rowActions`, delete last with `tone: "danger"`. +- Show bulk actions only while rows are selected; confirm destructive bulk actions with the count. +- Design all three empty states (first use, no results, load error). `translations.emptyTitle` / `emptyDescription` hold one message, so pick it from the app's state on each render, or use `renderEmpty` to render a different state (with `Clear filters` / `Try again`) per case — never let a failed load read as `No records`. +- Choose `size` per page: `sm` for scanning, `md` when rows are edited inline. + +**Don't** + +- Build a separate filter bar when column filters (`enableColumnFilters` + `meta.type`) fit. +- Blank the table while refreshing — keep rows and show progress in the toolbar. +- Center numbers or right-align text. +- Put more than one primary action in the toolbar; the page's primary lives in the page header. + +**Copy and states** + +- Column headers are short nouns in sentence case, with units when shared (`Price (€)`). +- Inline edit success → toast ` saved`; errors stay on the cell editor. +- Search placeholder `Search …`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/date-picker-usage/SKILL.md b/libs/ui/agent-plugin/skills/date-picker-usage/SKILL.md index 434cc3bf76..4c02ff9b2b 100644 --- a/libs/ui/agent-plugin/skills/date-picker-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/date-picker-usage/SKILL.md @@ -1,25 +1,17 @@ --- -component_version: "1.1.1" name: date-picker-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit DatePicker for locale-aware segmented date entry, calendar selection, optional transactional time entry, single or range typed values, and canonical form serialization through one integrated field. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/date-picker.tsx" - - "libs/ui/src/tokens/components/molecules/_date-picker.css" - - "libs/ui/stories/molecules/date-picker.stories.tsx" - - "https://zagjs.com/components/react/date-input" - - "https://zagjs.com/components/react/date-picker" - - "https://react-aria.adobe.com/internationalized/date/" +metadata: + component_version: "1.1.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/date-picker.tsx libs/ui/src/tokens/components/molecules/_date-picker.css libs/ui/stories/molecules/date-picker.stories.tsx https://zagjs.com/components/react/date-input https://zagjs.com/components/react/date-picker https://react-aria.adobe.com/internationalized/date/" --- # @techsio/ui-kit DatePicker Usage @@ -29,6 +21,42 @@ it from a calendar. Set `granularity` to `hour`, `minute`, or `second` when the same field must also collect a time. Use separate domain controls when date and time have independent lifecycles. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `DatePicker`. + +**Use it when** + +- Entering a specific date or date-time: delivery date, publish schedule, birth date, report period. +- When users benefit from both typing (segments) and picking (calendar). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Showing a date (read-only) | formatted text via `Intl.DateTimeFormat` | +| Relative choices (`Last 7 days`, `This month`) | Select or RadioGroup of presets, optionally with a custom range | +| Time-of-day only with an independent lifecycle | a separate time control | + +**Do** + +- Pass the app `locale` explicitly — the component defaults to `en-US`, NumericInput to `cs-CZ` (ux-guidelines/formatting#locale-defaults-in-the-kit). +- Set `min`/`max` and unavailable dates so impossible choices can't be made, and say why in help text (`Delivery from 2 working days`). +- Use `granularity` for date-time instead of two unrelated fields when they form one value. +- Show the time zone when it matters (scheduled publishing, cut-offs). + +**Don't** + +- Add a hand-written format hint that disagrees with the locale's segment order. +- Use a calendar for birth dates far in the past without allowing typing. +- Store or display ISO strings to users. + +**Copy and states** + +- Label names the date's meaning (`Delivery date`, `Publish at`), not `Date`. +- Validation says the allowed range in formatted dates: `Choose a date between 4 Sept 2026 and 30 Sept 2026.` + ## Setup Date-only selection uses `CalendarDate | null` and commits a calendar choice diff --git a/libs/ui/agent-plugin/skills/dialog-usage/SKILL.md b/libs/ui/agent-plugin/skills/dialog-usage/SKILL.md index 0b2ced1c1c..62927ea346 100644 --- a/libs/ui/agent-plugin/skills/dialog-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/dialog-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.1" name: dialog-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Dialog for modal dialogs, alert dialogs, drawers, actions, focus management, placement, size, and close behavior backed by Zag.js. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/dialog.tsx" - - "libs/ui/src/tokens/components/molecules/_dialog.css" - - "libs/ui/stories/molecules/dialog.stories.tsx" - - "libs/ui/src/molecules/dialog.figma.ts" - - "https://zagjs.com/components/react/dialog" +metadata: + component_version: "1.0.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/dialog.tsx libs/ui/src/tokens/components/molecules/_dialog.css libs/ui/stories/molecules/dialog.stories.tsx libs/ui/src/molecules/dialog.figma.ts https://zagjs.com/components/react/dialog" --- # @techsio/ui-kit Dialog Usage @@ -24,6 +18,46 @@ sources: Use Dialog for focused overlays and confirmations. Use Popover for lightweight anchored content and Tooltip for short supplemental help. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Dialog`. + +**Use it when** + +- A focused task or decision that must be finished or cancelled before continuing. +- Irreversible confirmations with `role="alertdialog"`. +- Create / edit / read of a record over its list with `placement="right"` (drawer pattern from the CRUD reference). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Lightweight anchored content (filters, small forms next to their trigger) | Popover | +| A hint on hover/focus | Tooltip | +| Confirming a finished action | Toast | +| Persistent navigation or edge panels | Sidebar / Drawer | +| A long, multi-section record | a full page (record editor) | + +**Do** + +- Title = the action or record (`New product`, `Edit product`); destructive title = question (`Delete this product?`). +- Put actions in the `actions` footer: `[Cancel] [Primary]`, bottom-right, one primary. +- In an alertdialog the destructive button replaces the primary and repeats the verb + object (`Delete product`); the description names the record and consequences. +- Ask before closing a dirty form (`Discard unsaved changes?`). +- Size to the content: `sm` for confirmations, `md` for forms; right drawer for record CRUD. + +**Don't** + +- Open a dialog from a dialog; replace the content or use steps inside one dialog. +- Use a dialog for success messages or marketing interruptions on load. +- Put the only close affordance inside scrolled content — the close button and `Cancel` stay visible. +- Use `Yes` / `No` buttons. + +**Copy and states** + +- Buttons follow ux-guidelines/ux-writing#button-labels; descriptions are one or two sentences about consequences. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/drawer-usage/SKILL.md b/libs/ui/agent-plugin/skills/drawer-usage/SKILL.md index d735641a3a..fcb0800685 100644 --- a/libs/ui/agent-plugin/skills/drawer-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/drawer-usage/SKILL.md @@ -1,24 +1,16 @@ --- -component_version: "1.0.0" name: drawer-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Drawer for transient edge panels, modal or non-modal behavior, snap points, swipe gestures, multiple triggers, custom portals, controlled state, and stacks. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/drawer.tsx" - - "libs/ui/src/internal/molecules/drawer.context.tsx" - - "libs/ui/src/internal/molecules/drawer.styles.ts" - - "libs/ui/src/tokens/components/molecules/_drawer.css" - - "libs/ui/stories/molecules/drawer.stories.tsx" - - "https://zagjs.com/components/react/drawer" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/drawer.tsx libs/ui/src/internal/molecules/drawer.context.tsx libs/ui/src/internal/molecules/drawer.styles.ts libs/ui/src/tokens/components/molecules/_drawer.css libs/ui/stories/molecules/drawer.stories.tsx https://zagjs.com/components/react/drawer" --- # @techsio/ui-kit Drawer Usage @@ -27,6 +19,40 @@ Use Drawer for transient panels that enter from a viewport or container edge. Use Sidebar for persistent application navigation, Dialog for centered focused flows, and Popover for anchored contextual content. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Drawer`. + +**Use it when** + +- Transient panels that slide from an edge: cart, mobile filters, mobile navigation, quick views. +- Content that relates to the page behind it and should keep that context visible. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Persistent desktop navigation | Sidebar | +| Record create/edit/read in admin | Dialog placement="right" (the CRUD reference pattern) | +| Focused blocking decision | Dialog | +| Anchored small content | Popover | + +**Do** + +- Open from the edge that matches the content's origin: navigation start, cart/filters/details end. +- Keep a visible title and close button; footer actions bottom-right like dialogs. +- Return focus to the trigger on close; preserve page scroll. + +**Don't** + +- Stack drawers. +- Use a drawer for long multi-step flows — use a page with Steps. + +**Copy and states** + +- Title names the content (`Cart (3)`, `Filters`); filter drawers end with `[Reset] [Show 24 results]`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/facet-filter-panel-usage/SKILL.md b/libs/ui/agent-plugin/skills/facet-filter-panel-usage/SKILL.md index 806c7b64ae..6341ba9d38 100644 --- a/libs/ui/agent-plugin/skills/facet-filter-panel-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/facet-filter-panel-usage/SKILL.md @@ -1,20 +1,13 @@ --- -component_version: "1.0.1" name: facet-filter-panel-usage description: Use for controlled catalog facet presentation with generic option and range groups, active filters, overflow, and an inline panel or Dialog drawer. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - accordion-usage - - dialog-usage - - form-checkbox-usage - - slider-usage -sources: - - "libs/ui/src/templates/facet-filter-panel.tsx" - - "libs/ui/src/tokens/components/templates/_facet-filter-panel.css" - - "libs/ui/stories/templates/facet-filter-panel.stories.tsx" +metadata: + component_version: "1.0.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux accordion-usage dialog-usage form-checkbox-usage slider-usage ux-guidelines" + sources: "libs/ui/src/templates/facet-filter-panel.tsx libs/ui/src/tokens/components/templates/_facet-filter-panel.css libs/ui/stories/templates/facet-filter-panel.stories.tsx" --- # FacetFilterPanel Usage @@ -62,6 +55,38 @@ import { FacetFilterPanel } from "@techsio/ui-kit/templates/facet-filter-panel" /> ``` +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `FacetFilterPanel`. + +**Use it when** + +- Catalog filtering by facets (category, brand, price, colour, size) on listing pages. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Filtering an admin table | DataTable column filters | +| A single sort/filter choice | Select in the listing toolbar | + +**Do** + +- Desktop: left rail next to results; below `lg`: the same panel in a start-side drawer (Storefront category story). +- Show counts per facet value and hide or disable zero-result values consistently. +- Show active filters as removable chips above results, with `Clear all`. +- Apply instantly on desktop; in the mobile drawer end with `[Reset] [Show 24 results]`. + +**Don't** + +- Reset scroll or other filters when one facet changes. +- Put price as free-text inputs only — pair a Slider with NumericInputs. + +**Copy and states** + +- Facet titles are nouns (`Brand`, `Price`); counts formatted with the app locale. + ## Contract Option groups contain generic `value`, `label`, optional `count`, and diff --git a/libs/ui/agent-plugin/skills/figma-sync-handoff/SKILL.md b/libs/ui/agent-plugin/skills/figma-sync-handoff/SKILL.md index 4037a61c38..2ab809ea59 100644 --- a/libs/ui/agent-plugin/skills/figma-sync-handoff/SKILL.md +++ b/libs/ui/agent-plugin/skills/figma-sync-handoff/SKILL.md @@ -5,15 +5,11 @@ description: > Code Connect follow-up. Prepares a reminder and points to the component-to-figma skill; never edits Figma or runs Figma MCP tools automatically. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -sources: - - "libs/ui/AGENTS.md" - - "libs/ui/figma.config.json" - - "libs/ui/src/atoms/button.figma.ts" - - "libs/ui/src/molecules/dialog.figma.ts" - - ".agents/skills/component-to-figma/SKILL.md" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + sources: "libs/ui/AGENTS.md libs/ui/figma.config.json libs/ui/src/atoms/button.figma.ts libs/ui/src/molecules/dialog.figma.ts .agents/skills/component-to-figma/SKILL.md" --- # @techsio/ui-kit Figma Sync Handoff diff --git a/libs/ui/agent-plugin/skills/file-upload-usage/SKILL.md b/libs/ui/agent-plugin/skills/file-upload-usage/SKILL.md index 4814576231..a54b72fa64 100644 --- a/libs/ui/agent-plugin/skills/file-upload-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/file-upload-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.0" name: file-upload-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit FileUpload to select, validate, preview, and manage local File objects through the Zag.js compound API, including accepted/rejected files and native form behavior. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/file-upload.tsx" - - "libs/ui/src/tokens/components/molecules/_file-upload.css" - - "libs/ui/stories/molecules/file-upload.stories.tsx" - - "https://zagjs.com/components/react/file-upload" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/file-upload.tsx libs/ui/src/tokens/components/molecules/_file-upload.css libs/ui/stories/molecules/file-upload.stories.tsx https://zagjs.com/components/react/file-upload" --- # @techsio/ui-kit FileUpload Usage @@ -25,6 +19,39 @@ FileUpload manages local browser `File` objects for selection, validation, preview, and removal; it does not upload files to a server. Network transport, progress, cancellation, retry, and server errors belong to the consuming app. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `FileUpload`. + +**Use it when** + +- Attaching files to a record: product images, documents, imports. +- Drag-and-drop with a visible click-to-browse alternative. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Picking from existing media | a media library dialog | +| Pasting a URL | FormInput | + +**Do** + +- State accepted types and max size before selection (`PNG or JPG, up to 5 MB`). +- Show each file with name, size (formatted), progress, and a remove action with an accessible label. +- Show rejected files with the reason and how to fix it (`photo.heic isn't supported. Use PNG or JPG.`). +- Upload progress, retry and server errors belong to the app — surface them per file, not only as a toast. + +**Don't** + +- Make drag-and-drop the only way to add files. +- Clear already accepted files when one file is rejected. + +**Copy and states** + +- Dropzone text: `Drag files here or browse`; button label `Choose files`. + ## Setup Render the required hidden input and compose accepted and rejected collections diff --git a/libs/ui/agent-plugin/skills/footer-usage/SKILL.md b/libs/ui/agent-plugin/skills/footer-usage/SKILL.md index 7a7978c7e5..e194f434c1 100644 --- a/libs/ui/agent-plugin/skills/footer-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/footer-usage/SKILL.md @@ -1,22 +1,17 @@ --- -component_version: "1.0.0" name: footer-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Footer for site footer composition with container, section, title, list, link, text, divider, bottom area, size, layout, direction, section flow, and framework link adapters. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/organisms/footer.tsx" - - "libs/ui/src/tokens/components/organisms/_footer.css" - - "libs/ui/stories/organisms/footer.stories.tsx" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/organisms/footer.tsx libs/ui/src/tokens/components/organisms/_footer.css libs/ui/stories/organisms/footer.stories.tsx" --- # @techsio/ui-kit Footer Usage @@ -24,6 +19,37 @@ sources: Use Footer for global footer layout. Do not build app footers from raw divs and anchors when this organism fits. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Footer`. + +**Use it when** + +- The global site footer: secondary navigation, legal links, contact, newsletter, payment/shipping marks. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Primary navigation | Header | +| Actions for the current page | page content / page header | + +**Do** + +- Group links under short headings; keep the same footer on every storefront page. +- Include legal links (terms, privacy, cookies) and contact details users look for. +- Stack groups into accordions or a single column on mobile without hiding legal links. + +**Don't** + +- Put important actions only in the footer. +- Duplicate the whole header navigation. + +**Copy and states** + +- Headings are nouns (`Customer care`, `Company`); `© 2026 Company name` generated from the current year. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/form-checkbox-usage/SKILL.md b/libs/ui/agent-plugin/skills/form-checkbox-usage/SKILL.md index a4becb5c04..ea4823e749 100644 --- a/libs/ui/agent-plugin/skills/form-checkbox-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/form-checkbox-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.1.1" name: form-checkbox-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit FormCheckbox for labeled checkbox fields with Zag.js checked/indeterminate state, help text, validation status, required, disabled, and read-only props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/form-checkbox.tsx" - - "libs/ui/src/tokens/components/atoms/_checkbox.css" - - "libs/ui/stories/molecules/form-checkbox.stories.tsx" - - "libs/ui/src/molecules/form-checkbox.figma.ts" - - "https://zagjs.com/components/react/checkbox" +metadata: + component_version: "1.1.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/form-checkbox.tsx libs/ui/src/tokens/components/atoms/_checkbox.css libs/ui/stories/molecules/form-checkbox.stories.tsx libs/ui/src/molecules/form-checkbox.figma.ts https://zagjs.com/components/react/checkbox" --- # @techsio/ui-kit FormCheckbox Usage @@ -24,6 +18,40 @@ sources: Use FormCheckbox for checkbox fields with label/help/error structure. Use the Checkbox atom only for bare table/list controls. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `FormCheckbox`. + +**Use it when** + +- Opt-ins, agreements and independent yes/no choices inside a form that is saved with a submit button. +- Several independent options from a short list (`Email`, `SMS`). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A setting that applies immediately | Switch | +| Exactly one of several options | RadioGroup | +| Bare row selection in a table | Checkbox | + +**Do** + +- Write the label as a positive statement of what checking does (`Send me order updates by email`). +- Put consequences or legal detail in `helpText`, links inline in the label when required (terms). +- Show required agreement errors on the checkbox itself (`Accept the terms to continue.`). + +**Don't** + +- Pre-check marketing consent. +- Use negative labels (`Don't send me emails`). +- Use a checkbox that saves on change without a submit — that's a Switch. + +**Copy and states** + +- Sentence case, no trailing period for single-line labels; full sentences in help text end with a period. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/form-error-summary-usage/SKILL.md b/libs/ui/agent-plugin/skills/form-error-summary-usage/SKILL.md index 441533967a..bac843e6c0 100644 --- a/libs/ui/agent-plugin/skills/form-error-summary-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/form-error-summary-usage/SKILL.md @@ -1,20 +1,16 @@ --- -component_version: "1.0.1" name: form-error-summary-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit FormErrorSummary for a focusable error summary that links to invalid fields, without depending on a specific form library. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/form-error-summary.tsx" - - "libs/ui/src/tokens/components/molecules/_form-error-summary.css" - - "libs/ui/stories/molecules/form-error-summary.stories.tsx" +metadata: + component_version: "1.0.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/form-error-summary.tsx libs/ui/src/tokens/components/molecules/_form-error-summary.css libs/ui/stories/molecules/form-error-summary.stories.tsx" --- # @techsio/ui-kit FormErrorSummary Usage @@ -22,6 +18,40 @@ sources: Use FormErrorSummary to list validation errors with links that move focus to the invalid fields. It is presentational and form-library-agnostic. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `FormErrorSummary`. + +**Use it when** + +- After a failed submit with two or more errors, above the form (or at the top of a long page). +- Server-side validation results that map to several fields. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A single field error | the field's inline error only; focus that field | +| A non-field failure (network, permission) | StatusText above the actions or an error Toast with `Try again` | +| Live validation while typing | inline field errors | + +**Do** + +- Move focus to the summary after submit; each item links to and focuses its field. +- Word items the same as the inline messages, prefixed by the field label where needed. +- Keep inline errors too — the summary is an index, not a replacement. +- Remove the summary when the errors are fixed or on the next successful submit. + +**Don't** + +- Show the summary before the user tries to submit. +- Put it in a toast or a dialog. + +**Copy and states** + +- Title: `There are 3 problems with this form` (pluralised); items: `Enter an SKU with at least 4 characters`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/form-input-usage/SKILL.md b/libs/ui/agent-plugin/skills/form-input-usage/SKILL.md index 1ccba3a082..307abd6622 100644 --- a/libs/ui/agent-plugin/skills/form-input-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/form-input-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.1.0" name: form-input-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit FormInput for labeled single-line fields with Label, Input, StatusText, validation status, help text, required, disabled, and size props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - input-usage - - app-token-overrides -sources: - - "libs/ui/src/molecules/form-input.tsx" - - "libs/ui/src/atoms/input.tsx" - - "libs/ui/stories/molecules/form-input.stories.tsx" - - "libs/ui/src/molecules/form-input.figma.ts" +metadata: + component_version: "1.1.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux input-usage app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/form-input.tsx libs/ui/src/atoms/input.tsx libs/ui/stories/molecules/form-input.stories.tsx libs/ui/src/molecules/form-input.figma.ts" --- # @techsio/ui-kit FormInput Usage @@ -24,6 +18,45 @@ sources: Use FormInput for labeled text-like inputs. Prefer this over manually composing Label, Input, and StatusText in apps. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `FormInput`. + +**Use it when** + +- Every labelled single-line text field in app forms: name, email, SKU, URL, search inside a form. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Multi-line text | FormTextarea | +| Numbers, prices, quantities | FormNumericInput | +| Phone numbers | PhoneInput | +| Dates | DatePicker | +| Choosing from options | Select / Combobox / RadioGroup | +| Global/site search | SearchForm | + +**Do** + +- Label = noun, no colon (`Email`); placeholder only as an example (`e.g. TS-0042`). +- Use `helpText` for format rules and consequences; it stays visible. +- Validate on blur/submit; show the error through `validateStatus="error"` with a message saying how to fix it. +- Set the right `type`, `autoComplete` and `inputMode` so browsers and mobile keyboards help. +- Size the field to the expected value length within the form grid. + +**Don't** + +- Use the placeholder as the label. +- Disable fields to show read-only data — render text or use read-only (ux-guidelines/states#read-only-vs-disabled). +- Block paste or password managers. + +**Copy and states** + +- Error messages: what's wrong + how to fix (`Enter an email like name@example.com.`), never `Invalid input`. +- Mark required fields with the component's `required`, not a typed asterisk. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/form-numeric-input-usage/SKILL.md b/libs/ui/agent-plugin/skills/form-numeric-input-usage/SKILL.md index e8b81e21e9..cb731334cd 100644 --- a/libs/ui/agent-plugin/skills/form-numeric-input-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/form-numeric-input-usage/SKILL.md @@ -1,23 +1,16 @@ --- -component_version: "1.1.0" name: form-numeric-input-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit FormNumericInput for labeled numeric fields using NumericInput compound children, validation status, help text, and number-specific constraints. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - numeric-input-usage - - app-token-overrides -sources: - - "libs/ui/src/molecules/form-numeric-input.tsx" - - "libs/ui/src/atoms/numeric-input.tsx" - - "libs/ui/stories/molecules/form-numeric-input.stories.tsx" - - "libs/ui/src/molecules/form-numeric-input.figma.ts" - - "https://zagjs.com/components/react/number-input" +metadata: + component_version: "1.1.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux numeric-input-usage app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/form-numeric-input.tsx libs/ui/src/atoms/numeric-input.tsx libs/ui/stories/molecules/form-numeric-input.stories.tsx libs/ui/src/molecules/form-numeric-input.figma.ts https://zagjs.com/components/react/number-input" --- # @techsio/ui-kit FormNumericInput Usage @@ -25,6 +18,39 @@ sources: Use FormNumericInput for labeled quantities, limits, prices, or measurements. It requires NumericInput compound children. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `FormNumericInput`. + +**Use it when** + +- Labelled exact numbers: price, quantity, stock, discount %, weight, dimensions. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Approximate value where feel matters (volume, price range filter) | Slider (optionally paired) | +| Identifiers that look numeric (postal code, card, order #) | FormInput with `inputMode="numeric"` | +| Unlabelled compact stepper (cart line) | NumericInput | + +**Do** + +- Pass the app `locale` and `formatOptions` (currency, percent, unit) so display matches the rest of the app. +- Set `min`, `max` and step; show the limits in help text when not obvious (`Up to 99 per order`). +- Put the unit in the label (`Price (€)`, `Weight (kg)`) when not formatted into the value. +- Keep increment/decrement triggers for small integer adjustments; omit them for prices. + +**Don't** + +- Use `` or FormInput for numbers. +- Silently clamp a typed value without telling the user — show the limit. + +**Copy and states** + +- Validation: `Enter a quantity between 1 and 99.` with formatted numbers. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/form-textarea-usage/SKILL.md b/libs/ui/agent-plugin/skills/form-textarea-usage/SKILL.md index f8d238429d..2820d937b5 100644 --- a/libs/ui/agent-plugin/skills/form-textarea-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/form-textarea-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.1.0" name: form-textarea-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit FormTextarea for labeled multi-line text fields with Textarea, Label, StatusText, validation status, help text, size, resize, required, and disabled props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - textarea-usage - - app-token-overrides -sources: - - "libs/ui/src/molecules/form-textarea.tsx" - - "libs/ui/src/atoms/textarea.tsx" - - "libs/ui/stories/molecules/form-textarea.stories.tsx" - - "libs/ui/src/molecules/form-textarea.figma.ts" +metadata: + component_version: "1.1.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux textarea-usage app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/form-textarea.tsx libs/ui/src/atoms/textarea.tsx libs/ui/stories/molecules/form-textarea.stories.tsx libs/ui/src/molecules/form-textarea.figma.ts" --- # @techsio/ui-kit FormTextarea Usage @@ -24,6 +18,37 @@ sources: Use FormTextarea for labeled long text fields such as notes, comments, and descriptions. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `FormTextarea`. + +**Use it when** + +- Labelled free text longer than one line: notes, descriptions, comments, messages. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Single-line text | FormInput | +| Rich formatted content | a rich-text editor | + +**Do** + +- Size the initial height to the expected length (3–5 lines for notes). +- Show a character counter only when there is a limit, and before the limit is reached. +- Explain where the text will appear (`Shown to the customer on the invoice.`). + +**Don't** + +- Use a textarea for structured data (addresses, lists) that deserves separate fields. +- Truncate saved text silently. + +**Copy and states** + +- Counter format `120 / 500`; over-limit error `Shorten the note to 500 characters.` + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/framework-consumer-integration/SKILL.md b/libs/ui/agent-plugin/skills/framework-consumer-integration/SKILL.md index 7f6e1ddf3e..8811248d23 100644 --- a/libs/ui/agent-plugin/skills/framework-consumer-integration/SKILL.md +++ b/libs/ui/agent-plugin/skills/framework-consumer-integration/SKILL.md @@ -4,20 +4,13 @@ description: > Use when consuming @techsio/ui-kit in framework apps, especially Next.js 16+ with React Compiler, NextLink, NextImage, token CSS imports, polymorphic `as` props, adapter prop forwarding, and wrapper-avoidance checks. -type: framework -framework: react -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux -sources: - - "libs/ui/skills/_artifacts/consumer_app_usage_rules.md" - - "libs/ui/package.json" - - "libs/ui/src/atoms/link.tsx" - - "libs/ui/src/atoms/link-button.tsx" - - "libs/ui/src/atoms/image.tsx" - - "libs/ui/src/molecules/breadcrumb.tsx" - - "libs/ui/src/molecules/pagination.tsx" +metadata: + type: "framework" + framework: "react" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux" + sources: "libs/ui/skills/_artifacts/consumer_app_usage_rules.md libs/ui/package.json libs/ui/src/atoms/link.tsx libs/ui/src/atoms/link-button.tsx libs/ui/src/atoms/image.tsx libs/ui/src/molecules/breadcrumb.tsx libs/ui/src/molecules/pagination.tsx" --- This skill builds on `component-usage-ux`. Read it first for component diff --git a/libs/ui/agent-plugin/skills/gallery-usage/SKILL.md b/libs/ui/agent-plugin/skills/gallery-usage/SKILL.md index 94a8acdacb..765246b1f1 100644 --- a/libs/ui/agent-plugin/skills/gallery-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/gallery-usage/SKILL.md @@ -1,23 +1,17 @@ --- -component_version: "1.0.0" name: gallery-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Gallery for product or media image galleries with Carousel integration, thumbnails, controlled page, orientation, thumbnail image adapters, thumbnail aria labels, empty state, and NextImage support. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - carousel-usage - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/organisms/gallery.tsx" - - "libs/ui/src/tokens/components/organisms/_gallery.css" - - "libs/ui/stories/organisms/gallery.stories.tsx" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux carousel-usage framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/organisms/gallery.tsx libs/ui/src/tokens/components/organisms/_gallery.css libs/ui/stories/organisms/gallery.stories.tsx" --- # @techsio/ui-kit Gallery Usage @@ -25,6 +19,39 @@ sources: Use Gallery for product or media galleries with thumbnails. Use Carousel directly when thumbnails are not part of the UX. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Gallery`. + +**Use it when** + +- Product detail images with thumbnails, zoom/fullscreen and variant switching. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Banners or related items without thumbnails | Carousel | +| Single image | Image | +| Media management in admin | a media library grid | + +**Do** + +- Keep a fixed aspect ratio so the page doesn't jump between images. +- Give every image meaningful alt text (product, colour, angle); thumbnails can share the main alt. +- Switch the gallery when the user changes the colour variant. +- Support keyboard and swipe; show position (`2 / 6`). + +**Don't** + +- Auto-rotate product images. +- Hide the only view of a variant behind the 10th thumbnail. + +**Copy and states** + +- Alt text: `Linen shirt in navy, front`; controls `Previous image`, `Next image`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/header-usage/SKILL.md b/libs/ui/agent-plugin/skills/header-usage/SKILL.md index 3423ec69ed..c85a67bcf9 100644 --- a/libs/ui/agent-plugin/skills/header-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/header-usage/SKILL.md @@ -1,22 +1,17 @@ --- -component_version: "1.0.0" name: header-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Header for responsive site header composition with desktop/mobile sections, containers, nav, nav items, actions, hamburger, active state, size, direction, and token styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/organisms/header.tsx" - - "libs/ui/src/tokens/components/organisms/_header.css" - - "libs/ui/stories/organisms/header.stories.tsx" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/organisms/header.tsx libs/ui/src/tokens/components/organisms/_header.css libs/ui/stories/organisms/header.stories.tsx" --- # @techsio/ui-kit Header Usage @@ -24,6 +19,37 @@ sources: Use Header for global page header/navigation layout. Compose it with Link or LinkButton for actual navigation items. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Header`. + +**Use it when** + +- The global top bar: logo, primary navigation, search, account, cart. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| In-app admin navigation with many destinations | Sidebar + VerticalNavigation | +| Page-level title and actions | the page header inside content (title + top-right actions) | + +**Do** + +- Keep the same order across pages: logo start, navigation, search, account/cart end. +- Show cart count and account state; collapse navigation to a Drawer/Sidebar below `lg`. +- Make it sticky only if it stays compact; ensure it never covers focused content (scroll-padding). + +**Don't** + +- Put page-specific actions in the global header. +- Hide search behind an icon on desktop storefronts where search is a primary task. + +**Copy and states** + +- Navigation labels are short nouns; icon buttons have labels (`Cart, 3 items`, `Account`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/hotkeys-usage/SKILL.md b/libs/ui/agent-plugin/skills/hotkeys-usage/SKILL.md index 96410f2ed5..5dcb131246 100644 --- a/libs/ui/agent-plugin/skills/hotkeys-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/hotkeys-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.1" name: hotkeys-usage description: > Use after component-usage-ux when an app needs shortcut hints or explicit-store Zag keyboard registrations with @techsio/ui-kit Hotkeys. Covers compound keycaps, current callbacks, store ownership, scopes and optional registry readback. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/hotkeys.ts" - - "libs/ui/src/atoms/hotkeys.tsx" - - "libs/ui/src/tokens/components/atoms/_hotkeys.css" - - "libs/ui/stories/atoms/hotkeys.stories.tsx" - - "libs/ui/test/hotkeys.spec.ts" +metadata: + component_version: "1.0.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/hotkeys.ts libs/ui/src/atoms/hotkeys.tsx libs/ui/src/tokens/components/atoms/_hotkeys.css libs/ui/stories/atoms/hotkeys.stories.tsx libs/ui/test/hotkeys.spec.ts" --- # @techsio/ui-kit Hotkeys Usage @@ -24,6 +18,37 @@ sources: Use Hotkeys for shortcut hints and the separate hooks for native Zag registrations. A hint never installs a handler or requires a store. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Hotkeys`. + +**Use it when** + +- Showing a keyboard shortcut next to the command it triggers (menus, tooltips, Command palette). +- Registering app shortcuts for frequent actions in power-user tools. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Explaining how to do something | help text or a Tour | + +**Do** + +- Show platform-appropriate keys (⌘ on macOS, Ctrl elsewhere). +- Reserve shortcuts for frequent actions; list them in one discoverable place (Command palette or help). +- Every shortcut action must also be reachable by pointer. + +**Don't** + +- Override browser or assistive-technology shortcuts. +- Trigger shortcuts while the user types in inputs (unless the shortcut is meant for fields, like ⌘Enter). + +**Copy and states** + +- Order modifiers consistently (`⌘ Shift K`); the command name next to it follows button-label rules. + ## Shortcut presentation ```tsx diff --git a/libs/ui/agent-plugin/skills/icon-usage/SKILL.md b/libs/ui/agent-plugin/skills/icon-usage/SKILL.md index 49a3d8150d..dbac37b3a0 100644 --- a/libs/ui/agent-plugin/skills/icon-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/icon-usage/SKILL.md @@ -1,20 +1,15 @@ --- -component_version: "1.0.0" name: icon-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Icon tokens or Iconify classes with the library's supported size and semantic color props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/icon.tsx" - - "libs/ui/src/tokens/components/atoms/_icon.css" - - "libs/ui/stories/atoms/icon.stories.tsx" - - "libs/ui/src/atoms/icon.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/icon.tsx libs/ui/src/tokens/components/atoms/_icon.css libs/ui/stories/atoms/icon.stories.tsx libs/ui/src/atoms/icon.figma.ts" --- # @techsio/ui-kit Icon Usage @@ -22,6 +17,39 @@ sources: Use Icon for decorative or component-adjacent icons. The Icon atom renders `aria-hidden`, so it must not be the only accessible content for an action. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Icon`. + +**Use it when** + +- Decorative icons that support adjacent text (status, list items, headings). +- Icons inside components that render them for you (Button `icon`, StatusText). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A clickable icon | ActionIcon (with `aria-label`) or Button with `icon` | +| An icon that carries meaning without text | Icon + visually hidden text, or text instead | + +**Do** + +- Use one icon set and consistent size per context (the kit's icon tokens). +- Use the same icon for the same meaning everywhere (trash = delete, pencil = edit). +- Keep icons decorative — the text must carry the meaning. + +**Don't** + +- Use emoji as icons. +- Attach `onClick` to an Icon. +- Use colour-only icons to convey status. + +**Copy and states** + +- No text inside icons; meaning lives in the adjacent label. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/image-usage/SKILL.md b/libs/ui/agent-plugin/skills/image-usage/SKILL.md index 5476aefe70..260537a4b8 100644 --- a/libs/ui/agent-plugin/skills/image-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/image-usage/SKILL.md @@ -1,21 +1,15 @@ --- -component_version: "1.0.0" name: image-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Image or a framework image adapter such as NextImage through the Image atom's as prop. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/atoms/image.tsx" - - "libs/ui/src/tokens/components/atoms/_image.css" - - "libs/ui/stories/atoms/image.stories.tsx" - - "libs/ui/src/atoms/image.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/image.tsx libs/ui/src/tokens/components/atoms/_image.css libs/ui/stories/atoms/image.stories.tsx libs/ui/src/atoms/image.figma.ts" --- # @techsio/ui-kit Image Usage @@ -24,6 +18,38 @@ Use Image when the UI-kit or a molecule needs a framework-agnostic image slot. In Next apps, prefer NextImage through `as` when the app needs framework image optimization. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Image`. + +**Use it when** + +- Framework-agnostic image slots in kit molecules/templates; `as={NextImage}` in Next apps. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Several product images | Gallery | +| Decorative symbols | Icon | + +**Do** + +- Always reserve space (width/height or aspect ratio) to avoid layout shift. +- Write alt text for meaningful images; `alt=""` for decorative ones. +- Lazy-load below-the-fold images; prioritise the hero/LCP image. +- Provide a neutral placeholder for missing images, not a broken icon. + +**Don't** + +- Put text that matters inside images. +- Stretch images — use cover/contain deliberately. + +**Copy and states** + +- Alt text describes content and purpose (`Linen shirt in navy`), not `image` or file names. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/input-usage/SKILL.md b/libs/ui/agent-plugin/skills/input-usage/SKILL.md index e996c718a8..a826826ba6 100644 --- a/libs/ui/agent-plugin/skills/input-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/input-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.0" name: input-usage description: > Use after component-usage-ux when an app needs the low-level @techsio/ui-kit Input atom, including valid size, variant, embedded button spacing, and token-first validation styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/input.tsx" - - "libs/ui/src/molecules/form-input.tsx" - - "libs/ui/src/tokens/components/atoms/_input.css" - - "libs/ui/stories/atoms/input.stories.tsx" - - "libs/ui/src/atoms/input.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/input.tsx libs/ui/src/molecules/form-input.tsx libs/ui/src/tokens/components/atoms/_input.css libs/ui/stories/atoms/input.stories.tsx libs/ui/src/atoms/input.figma.ts" --- # @techsio/ui-kit Input Usage @@ -24,6 +18,38 @@ sources: Use Input for a bare text-like control. Use `FormInput` when the UI includes a label, helper text, validation text, or full form-field spacing. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Input`. + +**Use it when** + +- Bare text controls inside compositions that supply their own label: table filters, inline edit cells, toolbars. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Any labelled form field | FormInput | +| Numbers | NumericInput | +| Search with submit/clear | SearchForm | + +**Do** + +- Give bare inputs an accessible name (`aria-label` or an associated Label). +- Use the kit sizes that match the surrounding control size (DataTable `size`). +- Set `type`, `autoComplete` and `inputMode` correctly. + +**Don't** + +- Compose Label + Input + StatusText by hand in app forms. +- Use placeholder text as the only label. + +**Copy and states** + +- Placeholders are examples or `Search …` in filters. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/intent-skill-maintenance/SKILL.md b/libs/ui/agent-plugin/skills/intent-skill-maintenance/SKILL.md index d6107ced0f..b69b5b694f 100644 --- a/libs/ui/agent-plugin/skills/intent-skill-maintenance/SKILL.md +++ b/libs/ui/agent-plugin/skills/intent-skill-maintenance/SKILL.md @@ -5,16 +5,11 @@ description: > usage patterns, Figma handoff practices, validation commands, or generated skills change and the TanStack Intent skill artifacts or SKILL.md files need review or regeneration. -type: lifecycle -library: "@techsio/ui-kit" -library_version: "0.3.2" -sources: - - "libs/ui/skills/_artifacts/domain_map.yaml" - - "libs/ui/skills/_artifacts/skill_spec.md" - - "libs/ui/skills/_artifacts/skill_tree.yaml" - - "package:@tanstack/intent@0.0.41/meta/domain-discovery" - - "package:@tanstack/intent@0.0.41/meta/tree-generator" - - "package:@tanstack/intent@0.0.41/meta/generate-skill" +metadata: + type: "lifecycle" + library: "@techsio/ui-kit" + library_version: "0.3.2" + sources: "libs/ui/skills/_artifacts/domain_map.yaml libs/ui/skills/_artifacts/skill_spec.md libs/ui/skills/_artifacts/skill_tree.yaml package:@tanstack/intent@0.0.41/meta/domain-discovery package:@tanstack/intent@0.0.41/meta/tree-generator package:@tanstack/intent@0.0.41/meta/generate-skill" --- # @techsio/ui-kit Intent Skill Maintenance diff --git a/libs/ui/agent-plugin/skills/label-usage/SKILL.md b/libs/ui/agent-plugin/skills/label-usage/SKILL.md index 10a763e71d..8920e8ce78 100644 --- a/libs/ui/agent-plugin/skills/label-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/label-usage/SKILL.md @@ -1,19 +1,15 @@ --- -component_version: "1.0.0" name: label-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Label for form-control labels with valid size, disabled, required, and htmlFor usage. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/label.tsx" - - "libs/ui/src/tokens/components/atoms/_label.css" - - "libs/ui/src/atoms/label.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/label.tsx libs/ui/src/tokens/components/atoms/_label.css libs/ui/src/atoms/label.figma.ts" --- # @techsio/ui-kit Label Usage @@ -21,6 +17,37 @@ sources: Use Label for standalone form-control labels. Prefer form molecules when the whole field structure is available. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Label`. + +**Use it when** + +- Standalone labels for controls that are not wrapped by a Form* molecule. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A complete field | FormInput / FormTextarea / FormNumericInput / FormCheckbox | +| Section headings | a heading element | + +**Do** + +- Connect every label to its control (`htmlFor`). +- Keep labels short nouns; move explanations to help text. +- Keep labels visible — no placeholder-only fields. + +**Don't** + +- End labels with a colon. +- Write labels as instructions (`Please enter your email`). + +**Copy and states** + +- Sentence case, no punctuation; units in parentheses (`Weight (kg)`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/link-button-usage/SKILL.md b/libs/ui/agent-plugin/skills/link-button-usage/SKILL.md index a81a43e0a9..0228fe912b 100644 --- a/libs/ui/agent-plugin/skills/link-button-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/link-button-usage/SKILL.md @@ -1,23 +1,16 @@ --- -component_version: "1.0.0" name: link-button-usage description: > Use after component-usage-ux when navigation should look like a Button using @techsio/ui-kit LinkButton, including framework link adapters, href, disabled state, icons, and Button-compatible variants/themes/sizes. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/atoms/link-button.tsx" - - "libs/ui/src/atoms/link.tsx" - - "libs/ui/src/atoms/button.tsx" - - "libs/ui/src/tokens/components/atoms/_button.css" - - "libs/ui/src/atoms/link-button.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/link-button.tsx libs/ui/src/atoms/link.tsx libs/ui/src/atoms/button.tsx libs/ui/src/tokens/components/atoms/_button.css libs/ui/src/atoms/link-button.figma.ts" --- # @techsio/ui-kit LinkButton Usage @@ -25,6 +18,37 @@ sources: Use LinkButton for navigation that needs Button styling. Use Button for actions that stay in the current page state. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `LinkButton`. + +**Use it when** + +- Navigation that must look like a button: `Go to checkout`, `View order`, CTA on marketing pages, `Back to list` when it is a real URL. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| An action that stays on the page | Button | +| A link inside text or lists | Link | + +**Do** + +- Label with the destination or intent (`View order`, `Continue shopping`). +- Follow Button hierarchy: one primary per surface. +- Use the framework link adapter so client routing and prefetch work. + +**Don't** + +- Use LinkButton to submit forms or trigger mutations. +- Open new tabs without saying so (`opens in a new tab`). + +**Copy and states** + +- No `Click here` / `Learn more` without context; `Read the returns policy` instead. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/link-usage/SKILL.md b/libs/ui/agent-plugin/skills/link-usage/SKILL.md index bccb6d9097..8903c791c5 100644 --- a/libs/ui/agent-plugin/skills/link-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/link-usage/SKILL.md @@ -1,20 +1,16 @@ --- -component_version: "1.0.0" name: link-usage description: > Use after component-usage-ux when an app needs the low-level @techsio/ui-kit Link atom for inline or unstyled navigation, including external links and framework adapter usage. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration -sources: - - "libs/ui/src/atoms/link.tsx" - - "libs/ui/src/atoms/link-button.tsx" - - "libs/ui/src/atoms/link.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration ux-guidelines" + sources: "libs/ui/src/atoms/link.tsx libs/ui/src/atoms/link-button.tsx libs/ui/src/atoms/link.figma.ts" --- # @techsio/ui-kit Link Usage @@ -22,6 +18,37 @@ sources: Use Link for inline navigation or when a molecule needs a framework-agnostic link. Use LinkButton when the link should look like a button. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Link`. + +**Use it when** + +- Navigation inside text, lists, tables (record names), breadcrumbs and footers. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Navigation styled as a button | LinkButton | +| An action | Button | + +**Do** + +- Make link text describe the destination; in tables, link the record name, not a separate `View` column. +- Mark external links and links that download files. +- Keep underline or another non-colour cue for links inside text. + +**Don't** + +- Use links for actions (`Delete`). +- Make whole cards clickable while also nesting other links inside. + +**Copy and states** + +- Destination-first, sentence case; no `here`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/menu-usage/SKILL.md b/libs/ui/agent-plugin/skills/menu-usage/SKILL.md index 0c7d621c3d..59841b2fa4 100644 --- a/libs/ui/agent-plugin/skills/menu-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/menu-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.1.0" name: menu-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Menu for action, radio, checkbox, separator, or submenu items using the Zag.js menu wrapper, Button trigger, icons, and supported open/highlight/select props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/menu.tsx" - - "libs/ui/src/tokens/components/molecules/_menu.css" - - "libs/ui/stories/molecules/menu.stories.tsx" - - "libs/ui/src/molecules/menu.figma.ts" - - "https://zagjs.com/components/react/menu" +metadata: + component_version: "1.1.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/menu.tsx libs/ui/src/tokens/components/molecules/_menu.css libs/ui/stories/molecules/menu.stories.tsx libs/ui/src/molecules/menu.figma.ts https://zagjs.com/components/react/menu" --- # @techsio/ui-kit Menu Usage @@ -24,6 +18,40 @@ sources: Use Menu for contextual command lists. Use Select for choosing a value in a form, Tabs for switching panels, and Popover for custom content. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Menu`. + +**Use it when** + +- A list of commands for one object or context: row `More actions`, account menu, overflow of page actions. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Choosing a value in a form | Select | +| Switching views | Tabs | +| Custom content (forms, pickers) | Popover | +| Searching many commands | Command | + +**Do** + +- Order items by frequency; group with separators; destructive items last and in danger tone. +- Label items as verbs (`Duplicate`, `Archive`, `Delete…`); `…` when the item opens a dialog asking for more input. +- Show shortcuts with Hotkeys when they exist. +- Disable an unavailable item only if the reason is obvious; otherwise hide it. + +**Don't** + +- Put navigation and commands in one flat list without grouping. +- Nest menus more than one level. + +**Copy and states** + +- Trigger for overflow: ActionIcon with `aria-label="More actions"`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/numeric-input-usage/SKILL.md b/libs/ui/agent-plugin/skills/numeric-input-usage/SKILL.md index 08ec47293a..cc4b9f749f 100644 --- a/libs/ui/agent-plugin/skills/numeric-input-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/numeric-input-usage/SKILL.md @@ -1,24 +1,17 @@ --- -component_version: "1.0.1" name: numeric-input-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit NumericInput for accessible number entry with Zag.js spinbutton behavior, compound parts, numeric public values, locale formatting, min/max/step, and token-first styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/atoms/numeric-input.tsx" - - "libs/ui/src/tokens/components/atoms/_numeric-input.css" - - "libs/ui/stories/atoms/numeric-input.stories.tsx" - - "libs/ui/src/atoms/numeric-input.figma.ts" - - "https://zagjs.com/components/react/number-input" +metadata: + component_version: "1.0.1" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/numeric-input.tsx libs/ui/src/tokens/components/atoms/_numeric-input.css libs/ui/stories/atoms/numeric-input.stories.tsx libs/ui/src/atoms/numeric-input.figma.ts https://zagjs.com/components/react/number-input" --- # @techsio/ui-kit NumericInput Usage @@ -27,6 +20,39 @@ Use NumericInput for quantities, percentages, currency-like values, or bounded numbers where keyboard, wheel, increment/decrement, and validation behavior matter. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `NumericInput`. + +**Use it when** + +- Bare numeric controls in compact compositions: cart quantity, inline table editors, stepper fields. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A labelled numeric field | FormNumericInput | +| Approximate values | Slider | +| Numeric identifiers | Input | + +**Do** + +- Pass the app `locale` — the component defaults to `cs-CZ` (ux-guidelines/formatting#locale-defaults-in-the-kit). +- Use `formatOptions` for currency/percent/unit so the displayed value matches the rest of the UI. +- Set `min`/`max`/`step`; at the limit, disable only the trigger that would exceed it. +- Right-align numeric editors inside numeric table columns. + +**Don't** + +- Use `type="number"` inputs. +- Clamp silently on blur without feedback. + +**Copy and states** + +- Trigger labels `Increase quantity` / `Decrease quantity`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/pagination-usage/SKILL.md b/libs/ui/agent-plugin/skills/pagination-usage/SKILL.md index 1ea7e6a729..9dd6d2fc08 100644 --- a/libs/ui/agent-plugin/skills/pagination-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/pagination-usage/SKILL.md @@ -1,23 +1,16 @@ --- -component_version: "1.0.0" name: pagination-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Pagination for link-based paginated navigation with Zag.js pagination, getPageUrl, LinkButton, NextLink adapters, compact mode, variants, and sizes. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/molecules/pagination.tsx" - - "libs/ui/src/tokens/components/molecules/_pagination.css" - - "libs/ui/stories/molecules/pagination.stories.tsx" - - "libs/ui/src/molecules/pagination.figma.ts" - - "https://zagjs.com/components/react/pagination" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/pagination.tsx libs/ui/src/tokens/components/molecules/_pagination.css libs/ui/stories/molecules/pagination.stories.tsx libs/ui/src/molecules/pagination.figma.ts https://zagjs.com/components/react/pagination" --- # @techsio/ui-kit Pagination Usage @@ -25,6 +18,39 @@ sources: Use Pagination for page-based navigation. Do not use it for infinite scroll or stepper/wizard progress. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Pagination`. + +**Use it when** + +- Page-based navigation of results where position matters and users return to it (admin tables, search results). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Steps of a task | Steps | +| Browsing a small set | show everything | +| Feed-like storefront browsing | a `Load more` Button with the count (`Show 24 more`) | + +**Do** + +- Show the range and total (`1–25 of 1,204`), formatted with the app locale. +- Keep page size options few (25, 50, 100) and remember the choice. +- Reflect the page in the URL so back/refresh keep the position. +- Scroll to the top of the list (not the page) after changing pages. + +**Don't** + +- Reset filters when the page changes. +- Show page numbers beyond what fits — use ellipsis. + +**Copy and states** + +- Controls `Previous page`, `Next page`, `Page 3`; current page marked with `aria-current`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/phone-input-usage/SKILL.md b/libs/ui/agent-plugin/skills/phone-input-usage/SKILL.md index baf7751cfc..c6d880724e 100644 --- a/libs/ui/agent-plugin/skills/phone-input-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/phone-input-usage/SKILL.md @@ -1,22 +1,17 @@ --- -component_version: "1.0.0" name: phone-input-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit PhoneInput for international phone entry with country selection, libphonenumber details, hidden E.164 form value, native validation, validation status, and compound country picker slots. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - select-usage - - app-token-overrides -sources: - - "libs/ui/src/molecules/phone-input.tsx" - - "libs/ui/src/tokens/components/molecules/_phone-input.css" - - "libs/ui/stories/molecules/phone-input.stories.tsx" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux select-usage app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/phone-input.tsx libs/ui/src/tokens/components/molecules/_phone-input.css libs/ui/stories/molecules/phone-input.stories.tsx" --- # @techsio/ui-kit PhoneInput Usage @@ -24,6 +19,36 @@ sources: Use PhoneInput for telephone fields. Do not compose country Select and Input manually. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `PhoneInput`. + +**Use it when** + +- Any telephone field, with country code selection and formatting. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Other numeric identifiers | FormInput | + +**Do** + +- Default the country from the store/locale; allow changing it. +- Validate on blur with a message showing the expected format. +- Set `autoComplete="tel"`. + +**Don't** + +- Compose a country Select and an Input by hand. +- Reject valid numbers because of spaces or dashes — normalise instead. + +**Copy and states** + +- Label `Phone`; help text says why you need it (`For delivery updates only.`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/popover-usage/SKILL.md b/libs/ui/agent-plugin/skills/popover-usage/SKILL.md index ae8c18d267..a44336aa85 100644 --- a/libs/ui/agent-plugin/skills/popover-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/popover-usage/SKILL.md @@ -1,23 +1,16 @@ --- -component_version: "1.0.0" name: popover-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Popover for anchored non-blocking content using Zag.js compound parts, placement, portalling, close behavior, arrow, title, description, and close trigger. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/popover.tsx" - - "libs/ui/src/tokens/components/molecules/_popover.css" - - "libs/ui/stories/molecules/popover.stories.tsx" - - "libs/ui/src/molecules/popover.figma.ts" - - "https://zagjs.com/components/react/popover" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/popover.tsx libs/ui/src/tokens/components/molecules/_popover.css libs/ui/stories/molecules/popover.stories.tsx libs/ui/src/molecules/popover.figma.ts https://zagjs.com/components/react/popover" --- # @techsio/ui-kit Popover Usage @@ -25,6 +18,39 @@ sources: Use Popover for anchored, dismissible content. Use Dialog for modal flows and Tooltip for tiny supplemental text. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Popover`. + +**Use it when** + +- Small interactive content anchored to its trigger: filter panels, colour pickers, quick edit of one value, share options. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A decision that must block the page | Dialog | +| A read-only hint | Tooltip | +| A list of commands | Menu | +| Large forms | Dialog / drawer | + +**Do** + +- Keep content short; if it scrolls, it should be a dialog. +- End filter popovers with `[Reset] [Apply]` bottom-right, or apply instantly — not both. +- Return focus to the trigger on close; close on Escape and outside click. + +**Don't** + +- Put required information only in a popover. +- Open popovers on hover. + +**Copy and states** + +- Trigger text says what opens (`Filters`, `Share`); count active filters in the trigger (`Filters (2)`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/product-card-usage/SKILL.md b/libs/ui/agent-plugin/skills/product-card-usage/SKILL.md index b45800d03a..1d98699cb9 100644 --- a/libs/ui/agent-plugin/skills/product-card-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/product-card-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.0" name: product-card-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit ProductCard for product summaries with image adapter, name, price, stock, badges, rating, actions, and card button variants. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - framework-consumer-integration - - app-token-overrides -sources: - - "libs/ui/src/molecules/product-card.tsx" - - "libs/ui/src/tokens/components/molecules/_product-card.css" - - "libs/ui/stories/molecules/product-card.stories.tsx" - - "libs/ui/src/molecules/product-card.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux framework-consumer-integration app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/product-card.tsx libs/ui/src/tokens/components/molecules/_product-card.css libs/ui/stories/molecules/product-card.stories.tsx libs/ui/src/molecules/product-card.figma.ts" --- # @techsio/ui-kit ProductCard Usage @@ -24,6 +18,38 @@ sources: Use ProductCard for product listing and recommendation surfaces. It is a compound component; do not rebuild product card structure with generic divs. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `ProductCard`. + +**Use it when** + +- Products in listings, search results, recommendations and carousels. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Product detail page | the detail layout with Gallery | +| Admin product rows | DataTable | + +**Do** + +- Show, in this order: image, name, key variant info, price (formatted, with the previous price when discounted), availability, one action. +- Make the name the link to the product; keep one primary action (`Add to cart`). +- Confirm `Add to cart` with a toast (`Added to cart`) and update the cart count. +- Keep card heights consistent in a grid; clamp long names to two lines with the full name available. + +**Don't** + +- Stack more than two badges. +- Hide the price until hover. + +**Copy and states** + +- Price via `Intl.NumberFormat` currency; discounts as `−20 %` badge plus struck-through old price. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/radio-card-usage/SKILL.md b/libs/ui/agent-plugin/skills/radio-card-usage/SKILL.md index b8c435bc6b..4deef534d4 100644 --- a/libs/ui/agent-plugin/skills/radio-card-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/radio-card-usage/SKILL.md @@ -1,23 +1,16 @@ --- -component_version: "1.0.0" name: radio-card-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit RadioCard for prominent single-choice cards with Zag.js radio behavior, label, item, hidden input, control, text, description, addon, indicator, and status text. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/radio-card.tsx" - - "libs/ui/src/tokens/components/molecules/_radio-card.css" - - "libs/ui/stories/molecules/radio-card.stories.tsx" - - "libs/ui/src/molecules/radio-card.figma.ts" - - "https://zagjs.com/components/react/radio-group" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/radio-card.tsx libs/ui/src/tokens/components/molecules/_radio-card.css libs/ui/stories/molecules/radio-card.stories.tsx libs/ui/src/molecules/radio-card.figma.ts https://zagjs.com/components/react/radio-group" --- # @techsio/ui-kit RadioCard Usage @@ -25,6 +18,37 @@ sources: Use RadioCard for prominent exclusive choices with title, description, or addon content. Use RadioGroup for simple text options. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `RadioCard`. + +**Use it when** + +- 2–4 exclusive choices that need a description, price or icon: shipping method, plan, payment method. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Simple short text options | RadioGroup | +| Many options | Select | +| Several can be chosen | FormCheckbox group | + +**Do** + +- Put the decisive information first (name, price, delivery time) and align prices at the end. +- Preselect the recommended/cheapest option when a default is safe; say why in the description. +- Keep all cards the same structure and height. + +**Don't** + +- Put links or buttons inside a card — the card is the control. + +**Copy and states** + +- Title is the option name; description one line; price formatted with the app locale (`Free` for zero). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/radio-group-usage/SKILL.md b/libs/ui/agent-plugin/skills/radio-group-usage/SKILL.md index a2a26c3dc8..63c928363e 100644 --- a/libs/ui/agent-plugin/skills/radio-group-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/radio-group-usage/SKILL.md @@ -1,24 +1,17 @@ --- -component_version: "1.0.0" name: radio-group-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit RadioGroup for simple exclusive choices with Zag.js radio behavior, label, item group, hidden inputs, controls, text, descriptions, validation status, variants, and sizes. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/radio-group.tsx" - - "libs/ui/src/tokens/components/molecules/_radio-group.css" - - "libs/ui/stories/molecules/radio-group.stories.tsx" - - "libs/ui/src/molecules/radio-group.figma.ts" - - "https://zagjs.com/components/react/radio-group" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/radio-group.tsx libs/ui/src/tokens/components/molecules/_radio-group.css libs/ui/stories/molecules/radio-group.stories.tsx libs/ui/src/molecules/radio-group.figma.ts https://zagjs.com/components/react/radio-group" --- # @techsio/ui-kit RadioGroup Usage @@ -26,6 +19,39 @@ sources: Use RadioGroup for simple exclusive text choices. Use RadioCard for larger card-like choices. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `RadioGroup`. + +**Use it when** + +- One choice from 2–5 short options that should all be visible. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Options need descriptions/prices | RadioCard | +| 6+ options | Select | +| On/off | Switch or FormCheckbox | +| Several choices | FormCheckbox group | + +**Do** + +- Order options logically (frequency, size, alphabet) and consistently across the app. +- Preselect a safe default when one exists. +- Group label (legend) states the question (`Delivery speed`). + +**Don't** + +- Use a single radio button. +- Trigger actions on change without a submit, unless it's a view switch. + +**Copy and states** + +- Option labels parallel in grammar and short; no trailing punctuation. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/rating-usage/SKILL.md b/libs/ui/agent-plugin/skills/rating-usage/SKILL.md index adbe086101..2d56a1852d 100644 --- a/libs/ui/agent-plugin/skills/rating-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/rating-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.0" name: rating-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Rating for accessible rating capture or display using the Zag.js rating-group wrapper, supported value/count/allowHalf/readOnly/disabled props, and token styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/rating.tsx" - - "libs/ui/src/tokens/components/atoms/_rating.css" - - "libs/ui/stories/atoms/rating.stories.tsx" - - "libs/ui/src/atoms/rating.figma.ts" - - "https://zagjs.com/components/react/rating-group" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/rating.tsx libs/ui/src/tokens/components/atoms/_rating.css libs/ui/stories/atoms/rating.stories.tsx libs/ui/src/atoms/rating.figma.ts https://zagjs.com/components/react/rating-group" --- # @techsio/ui-kit Rating Usage @@ -24,6 +18,36 @@ sources: Use Rating for product reviews, satisfaction scores, and read-only rating display. Do not build custom star maps. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Rating`. + +**Use it when** + +- Showing an average rating with count; collecting a rating in reviews or satisfaction surveys. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A progress or score that isn't a rating | text or a meter | + +**Do** + +- Always show the numeric value and count next to read-only stars (`4.6 (128 reviews)`). +- Allow half values only in display, whole values in input unless the product needs halves. +- Label interactive ratings (`Rate this product`). + +**Don't** + +- Show stars with no reviews — show `No reviews yet`. +- Use colour-only stars without accessible text. + +**Copy and states** + +- Numbers formatted with the app locale (`4,6` in cs-CZ). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/search-form-usage/SKILL.md b/libs/ui/agent-plugin/skills/search-form-usage/SKILL.md index 9424a05222..683dd0bf11 100644 --- a/libs/ui/agent-plugin/skills/search-form-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/search-form-usage/SKILL.md @@ -1,22 +1,17 @@ --- -component_version: "1.0.0" name: search-form-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit SearchForm for search landmarks, controlled or uncontrolled search text, label, control, input, submit button, clear button, icon props, and token-backed field layout. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/search-form.tsx" - - "libs/ui/src/tokens/components/molecules/_search-form.css" - - "libs/ui/stories/molecules/search-form.stories.tsx" - - "libs/ui/src/molecules/search-form.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/search-form.tsx libs/ui/src/tokens/components/molecules/_search-form.css libs/ui/stories/molecules/search-form.stories.tsx libs/ui/src/molecules/search-form.figma.ts" --- # @techsio/ui-kit SearchForm Usage @@ -24,6 +19,38 @@ sources: Use SearchForm for search input and submit/clear actions. It wraps a semantic `` and `
`. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `SearchForm`. + +**Use it when** + +- Site or catalog search input with submit and clear. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Search with live suggestions that navigate | SearchSuggestions | +| Filtering a table | DataTable global filter | +| Picking a form value | Combobox | + +**Do** + +- Keep the query visible on the results page and in the URL. +- Offer clear (`Clear search`) and submit on Enter. +- Results page states the query and count (`24 results for “linen”`) and a helpful no-results state. + +**Don't** + +- Search on every keystroke against the full catalog without debounce. +- Hide the search field behind an icon on desktop storefronts. + +**Copy and states** + +- Placeholder `Search products…`; button label `Search`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/search-suggestions-usage/SKILL.md b/libs/ui/agent-plugin/skills/search-suggestions-usage/SKILL.md index 747e2a60e6..48833df618 100644 --- a/libs/ui/agent-plugin/skills/search-suggestions-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/search-suggestions-usage/SKILL.md @@ -1,17 +1,13 @@ --- -component_version: "1.0.0" name: search-suggestions-usage description: Use for grouped storefront search navigation with rich results and an all-results link, composed from the UI-kit Combobox. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - combobox-usage -sources: - - "libs/ui/src/templates/search-suggestions.tsx" - - "libs/ui/src/molecules/combobox.tsx" - - "libs/ui/stories/templates/search-suggestions.stories.tsx" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux combobox-usage ux-guidelines" + sources: "libs/ui/src/templates/search-suggestions.tsx libs/ui/src/molecules/combobox.tsx libs/ui/stories/templates/search-suggestions.stories.tsx" --- # SearchSuggestions Usage @@ -34,6 +30,37 @@ import { SearchSuggestions } from "@techsio/ui-kit/templates/search-suggestions" /> ``` +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `SearchSuggestions`. + +**Use it when** + +- Header/catalog search that suggests products, categories, brands and articles as the user types and navigates to them. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Selecting a value in a form | Combobox | +| App commands | Command | + +**Do** + +- Group suggestions by type with headings; show product image and price in product rows. +- Keep Enter on the typed query going to the full results page. +- Show recent/popular searches on focus before typing. + +**Don't** + +- Auto-navigate on highlight. +- Show more than ~8 suggestions per group. + +**Copy and states** + +- `See all 124 results for “linen”` as the last item. + ## Contract Shared props are derived from ComboboxProps. Supply `groups`, each with a unique diff --git a/libs/ui/agent-plugin/skills/select-usage/SKILL.md b/libs/ui/agent-plugin/skills/select-usage/SKILL.md index a298b2912a..002862531e 100644 --- a/libs/ui/agent-plugin/skills/select-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/select-usage/SKILL.md @@ -1,24 +1,17 @@ --- -component_version: "1.1.0" name: select-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Select for non-search selection with Zag.js collection behavior, hidden form select, trigger, value text, clear trigger, item groups, item indicators, validation status, size, and multiple mode. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/select.tsx" - - "libs/ui/src/tokens/components/molecules/_select.css" - - "libs/ui/stories/molecules/select.stories.tsx" - - "libs/ui/src/molecules/select.figma.ts" - - "https://zagjs.com/components/react/select" +metadata: + component_version: "1.1.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/select.tsx libs/ui/src/tokens/components/molecules/_select.css libs/ui/stories/molecules/select.stories.tsx libs/ui/src/molecules/select.figma.ts https://zagjs.com/components/react/select" --- # @techsio/ui-kit Select Usage @@ -26,6 +19,40 @@ sources: Use Select for choosing from known options. Use Combobox when the user needs search/filter text input. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Select`. + +**Use it when** + +- One (or several) values from a known list of about 6–15 options: status, category, country with few entries, sort order. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| 2–5 options that fit on screen | RadioGroup | +| Long list needing search | Combobox | +| Hierarchical values | CascadeSelect | +| Commands | Menu | +| Colour swatches | ColorSelect | + +**Do** + +- Label with the noun; placeholder `Select a ` only when there is no sensible default. +- Order options predictably (logical order, else alphabetical); keep `Other` last. +- Show disabled options only when the reason is visible (e.g. `Out of stock`). + +**Don't** + +- Use a Select for yes/no. +- Trigger navigation or actions on change. + +**Copy and states** + +- Options in sentence case, parallel grammar; the same option names as the status vocabulary. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/sidebar-usage/SKILL.md b/libs/ui/agent-plugin/skills/sidebar-usage/SKILL.md index bbbb0261ca..99661d091a 100644 --- a/libs/ui/agent-plugin/skills/sidebar-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/sidebar-usage/SKILL.md @@ -3,22 +3,14 @@ name: sidebar-usage description: >- Use after component-usage-ux when an app needs responsive start/end navigation, icon collapse, two-pane navigation, or a sticky-header shell. -type: core -component: Sidebar -component_version: "1.0.0" -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - drawer-usage - - app-token-overrides -sources: - - libs/ui/src/organisms/sidebar.tsx - - libs/ui/src/internal/organisms/sidebar.context.tsx - - libs/ui/src/internal/organisms/sidebar.focus.ts - - libs/ui/src/internal/organisms/sidebar.styles.ts - - libs/ui/src/tokens/components/organisms/_sidebar.css - - libs/ui/stories/organisms/sidebar.stories.tsx +metadata: + type: "core" + component: "Sidebar" + component_version: "1.0.0" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux drawer-usage app-token-overrides ux-guidelines" + sources: "libs/ui/src/organisms/sidebar.tsx libs/ui/src/internal/organisms/sidebar.context.tsx libs/ui/src/internal/organisms/sidebar.focus.ts libs/ui/src/internal/organisms/sidebar.styles.ts libs/ui/src/tokens/components/organisms/_sidebar.css libs/ui/stories/organisms/sidebar.stories.tsx" --- # Sidebar Usage @@ -27,6 +19,39 @@ Use `Sidebar` for application navigation that must change between persistent desktop panels and modal mobile navigation. Its sides are logical: `start` and `end` follow `dir`, so consumers do not need separate RTL layouts. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Sidebar`. + +**Use it when** + +- Application navigation that is persistent on desktop and becomes a modal panel on mobile (the admin shell). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Page-local sections | Tabs | +| A transient panel (cart, filters) | Drawer | +| Global storefront nav | Header | + +**Do** + +- Put primary navigation in the start sidebar; the leading panel is always navigation (Pages/Layouts). +- Highlight the current item and keep it visible after navigation. +- Collapse to icons only when users know the destinations; keep labels in tooltips. +- Only the content column scrolls; the sidebar scrolls independently when long. + +**Don't** + +- Mix sidebar, tabs and bottom navigation at the same hierarchy level. +- Put page actions (Save, New) in the sidebar. + +**Copy and states** + +- Destinations are nouns (`Orders`, `Products`), not verbs. + ## Import ```tsx diff --git a/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md b/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md index a27ad37d7d..b72fcbd7a1 100644 --- a/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md @@ -1,27 +1,54 @@ --- -component_version: "1.0.0" name: skeleton-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Skeleton for loading placeholders using Root, Circle, Text, and Rectangle compound parts with token-backed variants, sizes, and animation speeds. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/skeleton.tsx" - - "libs/ui/src/tokens/components/atoms/_skeleton.css" - - "libs/ui/stories/atoms/skeleton.stories.tsx" - - "libs/ui/src/atoms/skeleton-rectangle.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/skeleton.tsx libs/ui/src/tokens/components/atoms/_skeleton.css libs/ui/stories/atoms/skeleton.stories.tsx libs/ui/src/atoms/skeleton-rectangle.figma.ts" --- # @techsio/ui-kit Skeleton Usage Use Skeleton for loading placeholders that preserve the final layout shape. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Skeleton`. + +**Use it when** + +- Content that takes longer than ~300 ms to load, where the final layout is known (lists, cards, detail pages, charts). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A button action in progress | Button `isLoading` + `loadingText` | +| Very short waits | nothing | +| Unknown layout / long job | progress text or a loading toast | + +**Do** + +- Mirror the real layout: same number of rows/columns, same heights and widths. +- Keep headers, navigation and actions real while their content loads. +- Replace the skeleton in place without layout shift. + +**Don't** + +- Show skeletons for errors or empty results. +- Animate aggressively; respect reduced motion. + +**Copy and states** + +- Announce loading once for assistive tech (`Loading orders`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/slider-usage/SKILL.md b/libs/ui/agent-plugin/skills/slider-usage/SKILL.md index e6f4d8955d..cc7bcf28fb 100644 --- a/libs/ui/agent-plugin/skills/slider-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/slider-usage/SKILL.md @@ -1,22 +1,17 @@ --- -component_version: "1.0.0" name: slider-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Slider for single or range numeric adjustment with Zag.js slider behavior, label, markers, value text, orientation, min/max/step, validation status, and token styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/slider.tsx" - - "libs/ui/src/tokens/components/molecules/_slider.css" - - "libs/ui/stories/molecules/slider.stories.tsx" - - "https://zagjs.com/components/react/slider" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/slider.tsx libs/ui/src/tokens/components/molecules/_slider.css libs/ui/stories/molecules/slider.stories.tsx https://zagjs.com/components/react/slider" --- # @techsio/ui-kit Slider Usage @@ -24,6 +19,36 @@ sources: Use Slider for bounded numeric adjustment. Use NumericInput when exact typed entry is required. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Slider`. + +**Use it when** + +- Approximate values in a range where visual feedback matters: price range filters, volume, zoom. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Exact values | NumericInput / FormNumericInput | +| Few discrete options | RadioGroup / Select | + +**Do** + +- Show the current value(s) formatted (`€20 – €80`), and pair price ranges with numeric inputs. +- Use sensible steps; label min and max. +- Apply filter changes on release, not on every pixel. + +**Don't** + +- Use a slider for values the user must set exactly (quantity, price in admin). + +**Copy and states** + +- Thumb labels `Minimum price`, `Maximum price`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/status-text-usage/SKILL.md b/libs/ui/agent-plugin/skills/status-text-usage/SKILL.md index 0edf58a286..d235514cbb 100644 --- a/libs/ui/agent-plugin/skills/status-text-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/status-text-usage/SKILL.md @@ -1,21 +1,16 @@ --- -component_version: "1.0.0" name: status-text-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit StatusText for inline validation, success, warning, or default status messages with optional token icons and size/alignment props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/status-text.tsx" - - "libs/ui/src/tokens/components/atoms/_status-text.css" - - "libs/ui/stories/atoms/status-text.stories.tsx" - - "libs/ui/src/atoms/status-text.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/status-text.tsx libs/ui/src/tokens/components/atoms/_status-text.css libs/ui/stories/atoms/status-text.stories.tsx libs/ui/src/atoms/status-text.figma.ts" --- # @techsio/ui-kit StatusText Usage @@ -23,6 +18,39 @@ sources: Use StatusText for short inline messages that describe a nearby field, section, or operation result. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `StatusText`. + +**Use it when** + +- Short inline messages tied to a nearby field, section or operation: validation hints, section-level warnings, `Last saved 2 minutes ago`. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Transient confirmation of an action | Toast | +| List of form errors after submit | FormErrorSummary | +| Short state label | Badge | +| Blocking decision | Dialog | + +**Do** + +- Place it directly under/next to what it describes. +- Use `status` by meaning: `error` blocks, `warning` informs of a consequence, `success` confirms, `default` neutral. +- Keep the icon — colour must not be the only signal. + +**Don't** + +- Stack many status texts; summarise. +- Use it for long instructions — use help text or docs. + +**Copy and states** + +- One sentence: what + how to fix for errors (ux-guidelines/ux-writing#messages). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/steps-usage/SKILL.md b/libs/ui/agent-plugin/skills/steps-usage/SKILL.md index 78ca7b89a6..418e3c3fb7 100644 --- a/libs/ui/agent-plugin/skills/steps-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/steps-usage/SKILL.md @@ -1,24 +1,17 @@ --- -component_version: "1.0.0" name: steps-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Steps for multi-step workflows using the Zag.js steps machine, compound list/items, panels, progress, navigation triggers, linear flow, controlled step, and variants. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/steps.tsx" - - "libs/ui/src/tokens/components/molecules/_steps.css" - - "libs/ui/stories/molecules/steps.stories.tsx" - - "libs/ui/src/molecules/steps.figma.ts" - - "https://zagjs.com/components/react/steps" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/steps.tsx libs/ui/src/tokens/components/molecules/_steps.css libs/ui/stories/molecules/steps.stories.tsx libs/ui/src/molecules/steps.figma.ts https://zagjs.com/components/react/steps" --- # @techsio/ui-kit Steps Usage @@ -26,6 +19,39 @@ sources: Use Steps for wizard/progress workflows. Use Tabs for peer panels, and Pagination for page navigation. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Steps`. + +**Use it when** + +- Ordered multi-step tasks: checkout, onboarding, import wizards (3–7 steps). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Peer views | Tabs | +| Pages of results | Pagination | +| Explaining existing UI | Tour | + +**Do** + +- Name steps by what the user does (`Shipping`, `Payment`, `Review`). +- Actions at the bottom: `Back` at the start, `Continue` at the end; the last step's button is the real action (`Place order`). +- Validate each step before continuing; let users go back without losing data. +- Allow jumping back to completed steps. + +**Don't** + +- Use more than ~7 steps; split the flow. +- Hide the global navigation choice to leave (focused layouts still offer a way out). + +**Copy and states** + +- Step titles are nouns; progress text `Step 2 of 4` for assistive tech. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/storybook-authoring/SKILL.md b/libs/ui/agent-plugin/skills/storybook-authoring/SKILL.md index 31b14dc7e4..30b26eda72 100644 --- a/libs/ui/agent-plugin/skills/storybook-authoring/SKILL.md +++ b/libs/ui/agent-plugin/skills/storybook-authoring/SKILL.md @@ -5,18 +5,11 @@ description: > libs/ui, including Playground stories, argTypes controls, VariantContainer and VariantGroup matrices, fn() handlers, token-based story layout classes, and UI-kit components instead of native HTML. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -sources: - - "libs/ui/AGENTS.md" - - "libs/ui/.storybook/main.ts" - - "libs/ui/.storybook/preview.ts" - - "libs/ui/stories/atoms/button.stories.tsx" - - "libs/ui/stories/molecules/dialog.stories.tsx" - - "libs/ui/stories/molecules/tree-view.stories.tsx" - - "libs/ui/local/storybook-practice.md" - - "libs/ui/local/storybook-migration.md" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + sources: "libs/ui/AGENTS.md libs/ui/.storybook/main.ts libs/ui/.storybook/preview.ts libs/ui/stories/atoms/button.stories.tsx libs/ui/stories/molecules/dialog.stories.tsx libs/ui/stories/molecules/tree-view.stories.tsx libs/ui/local/storybook-practice.md libs/ui/local/storybook-migration.md" --- # @techsio/ui-kit Storybook Authoring diff --git a/libs/ui/agent-plugin/skills/switch-usage/SKILL.md b/libs/ui/agent-plugin/skills/switch-usage/SKILL.md index f32cb7d778..487eeced25 100644 --- a/libs/ui/agent-plugin/skills/switch-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/switch-usage/SKILL.md @@ -1,23 +1,17 @@ --- -component_version: "1.0.0" name: switch-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Switch for boolean settings using Zag.js switch behavior, hidden input, label children, checked/defaultChecked, validation status, help text, required, disabled, and read-only state. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/switch.tsx" - - "libs/ui/src/tokens/components/molecules/_switch.css" - - "libs/ui/stories/molecules/switch.stories.tsx" - - "libs/ui/src/molecules/switch.figma.ts" - - "https://zagjs.com/components/react/switch" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/switch.tsx libs/ui/src/tokens/components/molecules/_switch.css libs/ui/stories/molecules/switch.stories.tsx libs/ui/src/molecules/switch.figma.ts https://zagjs.com/components/react/switch" --- # @techsio/ui-kit Switch Usage @@ -25,6 +19,38 @@ sources: Use Switch for immediate boolean settings. Use Checkbox for terms, multi-select lists, or non-immediate boolean form choices. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Switch`. + +**Use it when** + +- Settings that take effect immediately (and confirm with a toast): notifications, feature toggles, visibility. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A choice saved with a form's submit | FormCheckbox | +| Agreements and consent | FormCheckbox | +| More than two states | RadioGroup / Select | + +**Do** + +- Label the setting, not the state (`Email notifications`), and describe the effect in `helpText`. +- Apply on change; on failure revert the switch and show an error toast with `Try again`. +- Keep switches and explicitly-saved fields in separate groups (Settings story rule). + +**Don't** + +- Put switches in a form that also needs `Save changes` — users can't tell what is already saved. +- Use `On/Off` text inside the label. + +**Copy and states** + +- Confirmation toast: `Email notifications turned on`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/table-usage/SKILL.md b/libs/ui/agent-plugin/skills/table-usage/SKILL.md index 84eaa66248..b62031eaea 100644 --- a/libs/ui/agent-plugin/skills/table-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/table-usage/SKILL.md @@ -1,21 +1,17 @@ --- -component_version: "1.2.0" name: table-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Table for semantic tabular data with caption, header, body, footer, rows, column headers, numeric cells, selected rows, variants, interactive rows, sticky header/first column, column borders, and size props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/organisms/table.tsx" - - "libs/ui/src/tokens/components/organisms/_table.css" - - "libs/ui/stories/organisms/table.stories.tsx" +metadata: + component_version: "1.2.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/organisms/table.tsx libs/ui/src/tokens/components/organisms/_table.css libs/ui/stories/organisms/table.stories.tsx" --- # @techsio/ui-kit Table Usage @@ -23,6 +19,39 @@ sources: Use Table for semantic tabular data. Do not use div grids for data tables when table semantics are needed. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Table`. + +**Use it when** + +- Static or lightly interactive tabular data: invoice lines, order items, spec sheets, comparison tables. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Sorting, filtering, selection, paging, inline edit | DataTable | +| Layout of non-tabular content | grid/flex layout | +| Trends | Chart | + +**Do** + +- Use `numeric` on number/money cells and their headers so they end-align; add `tabular-nums` (ux-guidelines/formatting#alignment-in-tables). +- Keep precision equal within a column; totals row repeats alignment with a top border. +- Render missing values as `—`; format dates/numbers with `Intl`. +- Add a caption or heading that names the data. + +**Don't** + +- Use `$` + `toFixed(2)` or raw ISO dates in cells. +- Center numeric columns or mix alignments between header and cells. + +**Copy and states** + +- Column headers short nouns with units (`Qty`, `Unit price (€)`). + ## Setup ```tsx @@ -32,7 +61,7 @@ table semantics are needed. OrderTotal - #1001129 EUR + #1001{money.format(129)} ``` diff --git a/libs/ui/agent-plugin/skills/tabs-usage/SKILL.md b/libs/ui/agent-plugin/skills/tabs-usage/SKILL.md index 18edeb2123..5c8df41787 100644 --- a/libs/ui/agent-plugin/skills/tabs-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/tabs-usage/SKILL.md @@ -1,24 +1,17 @@ --- -component_version: "1.0.0" name: tabs-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Tabs for switching peer content panels with Zag.js tabs behavior, compound list, triggers, content, indicator, orientation, activation mode, variants, fitted, and justify props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/tabs.tsx" - - "libs/ui/src/tokens/components/molecules/_tabs.css" - - "libs/ui/stories/molecules/tabs.stories.tsx" - - "libs/ui/src/molecules/tabs.figma.ts" - - "https://zagjs.com/components/react/tabs" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/tabs.tsx libs/ui/src/tokens/components/molecules/_tabs.css libs/ui/stories/molecules/tabs.stories.tsx libs/ui/src/molecules/tabs.figma.ts https://zagjs.com/components/react/tabs" --- # @techsio/ui-kit Tabs Usage @@ -26,6 +19,39 @@ sources: Use Tabs for peer panels in the same page context. Use Steps for ordered workflow progress and Breadcrumb for hierarchy. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Tabs`. + +**Use it when** + +- Peer views of the same object or page: record sections (Content, SEO, History), settings groups (vertical). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Sequential steps | Steps | +| Optional expandable content | Accordion | +| Navigation between pages | links / Sidebar | +| Two views of the same data (table/grid) | a segmented control or icon toggle | + +**Do** + +- Keep labels short (1–2 words) and the first tab the content users came for. +- Preserve the active tab in the URL for linkable settings/records. +- Show errors in a tab's label (count badge) when the invalid field is in a hidden tab. + +**Don't** + +- Nest tabs inside tabs. +- Use tabs when users must compare content across tabs. + +**Copy and states** + +- Nouns in sentence case; counts in parentheses or a Badge (`Orders 12`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/tailwind-token-authoring/SKILL.md b/libs/ui/agent-plugin/skills/tailwind-token-authoring/SKILL.md index e024487523..30a81eea1b 100644 --- a/libs/ui/agent-plugin/skills/tailwind-token-authoring/SKILL.md +++ b/libs/ui/agent-plugin/skills/tailwind-token-authoring/SKILL.md @@ -5,22 +5,11 @@ description: > @theme static, semantic aliases, two-layer component tokens, component CSS imports, Tailwind token utility class mapping, app override compatibility, and token validator commands. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -sources: - - "libs/ui/AGENTS.md" - - "libs/ui/token-contribution.md" - - "libs/ui/src/tokens/index.css" - - "libs/ui/src/tokens/_semantic.css" - - "libs/ui/src/tokens/_layout.css" - - "libs/ui/src/tokens/_spacing.css" - - "libs/ui/src/tokens/components/atoms/_button.css" - - "libs/ui/src/tokens/components/components.css" - - "libs/ui/scripts/validate-token-usage.js" - - "libs/ui/scripts/validate-token-definitions.js" - - "https://github.com/TechsioCZ/new-engine/issues/72" - - "https://github.com/TechsioCZ/new-engine/issues/329" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + sources: "libs/ui/AGENTS.md libs/ui/token-contribution.md libs/ui/src/tokens/index.css libs/ui/src/tokens/_semantic.css libs/ui/src/tokens/_layout.css libs/ui/src/tokens/_spacing.css libs/ui/src/tokens/components/atoms/_button.css libs/ui/src/tokens/components/components.css libs/ui/scripts/validate-token-usage.js libs/ui/scripts/validate-token-definitions.js https://github.com/TechsioCZ/new-engine/issues/72 https://github.com/TechsioCZ/new-engine/issues/329" --- # @techsio/ui-kit Tailwind Token Authoring diff --git a/libs/ui/agent-plugin/skills/textarea-usage/SKILL.md b/libs/ui/agent-plugin/skills/textarea-usage/SKILL.md index 08c023f1d4..1d56b8ea1e 100644 --- a/libs/ui/agent-plugin/skills/textarea-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/textarea-usage/SKILL.md @@ -1,21 +1,16 @@ --- -component_version: "1.0.0" name: textarea-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Textarea for multi-line text entry with valid variant, size, resize, readonly styling, and token-first validation. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/textarea.tsx" - - "libs/ui/src/tokens/components/atoms/_textarea.css" - - "libs/ui/stories/atoms/textarea.stories.tsx" - - "libs/ui/src/atoms/textarea.figma.ts" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/textarea.tsx libs/ui/src/tokens/components/atoms/_textarea.css libs/ui/stories/atoms/textarea.stories.tsx libs/ui/src/atoms/textarea.figma.ts" --- # @techsio/ui-kit Textarea Usage @@ -23,6 +18,35 @@ sources: Use Textarea for multi-line text entry. Use form molecules when a complete label/help/error field abstraction exists for the current app context. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Textarea`. + +**Use it when** + +- Bare multi-line text controls in compositions that supply their own label (inline comment boxes, table editors). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A labelled field | FormTextarea | +| Single-line text | Input | + +**Do** + +- Give it an accessible name. +- Auto-grow or size to the expected content; keep a sensible max height. + +**Don't** + +- Use placeholder as the label. + +**Copy and states** + +- Placeholder as an example (`Add a note for the courier…`). + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/toast-usage/SKILL.md b/libs/ui/agent-plugin/skills/toast-usage/SKILL.md index 5fea8ee0d2..6779eba9cc 100644 --- a/libs/ui/agent-plugin/skills/toast-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/toast-usage/SKILL.md @@ -1,23 +1,17 @@ --- -component_version: "1.0.0" name: toast-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Toast for transient CRUD feedback using the global Zag.js toaster store, Toaster portal, useToast, toast types, title, description, close trigger, and token styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/molecules/toast.tsx" - - "libs/ui/src/tokens/components/molecules/_toast.css" - - "libs/ui/stories/molecules/toast.stories.tsx" - - "libs/ui/src/molecules/toast.figma.ts" - - "https://zagjs.com/components/react/toast" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/toast.tsx libs/ui/src/tokens/components/molecules/_toast.css libs/ui/stories/molecules/toast.stories.tsx libs/ui/src/molecules/toast.figma.ts https://zagjs.com/components/react/toast" --- # @techsio/ui-kit Toast Usage @@ -25,6 +19,44 @@ sources: Use Toast for transient feedback after actions. Use StatusText for inline field messages and Dialog for blocking decisions. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Toast`. + +**Use it when** + +- Confirming a finished mutation when the UI doesn't show it clearly (drawer closed, row updated, background save). +- Results of async/background work; `Undo` for reversible destructive actions. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Field or form validation | inline errors + FormErrorSummary | +| Irreversible decisions | Dialog role="alertdialog" | +| Persistent page conditions | inline StatusText at the top of the content | +| Information to read or copy later | inline content | + +**Do** + +- Mount one `` in the app root; keep the kit placement bottom-end (bottom-right) — ux-guidelines/feedback-and-actions#toasts. +- Title ` ` (`Product created`); optional one-sentence description; at most one action. +- Durations: success/info ~5 s, warning ~8 s; errors with an action and `loading` stay until resolved. +- Update a `loading` toast into success/error instead of stacking a second toast. +- One toast per user action; bulk actions get one summary toast. + +**Don't** + +- Use toasts for validation errors or as the only copy of an error. +- Put links to the only place something can be found inside a toast. +- Show a toast on page load or for things the user didn't do. + +**Copy and states** + +- Never `Success!`, `Done` or `Saved` alone — name the object (`Settings saved`). +- Errors: `Product not saved` + reason + `Try again`. + ## Setup ```tsx @@ -43,12 +75,12 @@ function SaveButton() { onClick={() => toaster.create({ type: "success", - title: "Saved", - description: "Changes were saved.", + title: "Product saved", + description: "Linen shirt", }) } > - Save + Save changes ) } @@ -92,7 +124,7 @@ Correct: ```tsx const toaster = useToast() -return +return ``` Source: libs/ui/src/molecules/toast.tsx @@ -124,7 +156,7 @@ Wrong: Correct: ```tsx - + ``` Source: libs/ui/src/tokens/components/molecules/_toast.css diff --git a/libs/ui/agent-plugin/skills/tooltip-usage/SKILL.md b/libs/ui/agent-plugin/skills/tooltip-usage/SKILL.md index 7af390d553..9fc3f2df48 100644 --- a/libs/ui/agent-plugin/skills/tooltip-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/tooltip-usage/SKILL.md @@ -1,22 +1,16 @@ --- -component_version: "1.0.0" name: tooltip-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Tooltip for supplemental hover/focus help using the Zag.js tooltip wrapper, supported timing, placement, interaction, controlled state, and token styling props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - app-token-overrides -sources: - - "libs/ui/src/atoms/tooltip.tsx" - - "libs/ui/src/tokens/components/atoms/_tooltip.css" - - "libs/ui/stories/atoms/tooltip.stories.tsx" - - "libs/ui/src/atoms/tooltip.figma.ts" - - "https://zagjs.com/components/react/tooltip" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux app-token-overrides ux-guidelines" + sources: "libs/ui/src/atoms/tooltip.tsx libs/ui/src/tokens/components/atoms/_tooltip.css libs/ui/stories/atoms/tooltip.stories.tsx libs/ui/src/atoms/tooltip.figma.ts https://zagjs.com/components/react/tooltip" --- # @techsio/ui-kit Tooltip Usage @@ -24,6 +18,39 @@ sources: Use Tooltip for short supplemental information on hover/focus. Do not use it for required instructions, blocking errors, or primary content. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Tooltip`. + +**Use it when** + +- Short, non-essential hints on hover and focus: icon-button names, truncated text, keyboard shortcuts. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Required information or instructions | visible help text | +| Interactive content | Popover | +| Errors | inline StatusText / field error | + +**Do** + +- Keep to a few words; repeat the ActionIcon's `aria-label`. +- Attach to focusable triggers so keyboard users get it too. +- Use for the full value of truncated cells. + +**Don't** + +- Put tooltips on disabled buttons (they don't receive hover/focus) — explain next to the control. +- Put links or buttons inside a tooltip. +- Use tooltips on touch-only surfaces as the only explanation. + +**Copy and states** + +- No trailing period for fragments; sentence case. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/tour-usage/SKILL.md b/libs/ui/agent-plugin/skills/tour-usage/SKILL.md index 74705d6623..0390c0715f 100644 --- a/libs/ui/agent-plugin/skills/tour-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/tour-usage/SKILL.md @@ -1,25 +1,16 @@ --- -component_version: "1.0.2" name: tour-usage description: > Use after component-usage-ux for guided walkthroughs with @techsio/ui-kit Tour: tooltip/dialog/floating steps, target resolution, interactive wait effects, imperative control, shared action buttons, cleanup and app-owned onboarding policy. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/tour.tsx" - - "libs/ui/src/tokens/components/molecules/_tour.css" - - "libs/ui/stories/molecules/tour.stories.tsx" - - "libs/ui/test/tour.spec.ts" - - "libs/ui/src/molecules/tour.figma.ts" - - "libs/ui/docs/tour-figma-handoff.md" - - "https://zagjs.com/components/react/tour" +metadata: + component_version: "1.0.2" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/tour.tsx libs/ui/src/tokens/components/molecules/_tour.css libs/ui/stories/molecules/tour.stories.tsx libs/ui/test/tour.spec.ts libs/ui/src/molecules/tour.figma.ts libs/ui/docs/tour-figma-handoff.md https://zagjs.com/components/react/tour" --- # @techsio/ui-kit Tour Usage @@ -28,6 +19,39 @@ Use Tour to explain an existing interface. Use Steps for a wizard/progress workflow, Tooltip for a short contextual hint, and Dialog for an independent modal task. Tour owns one Zag machine, not nested Tooltip/Dialog machines. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Tour`. + +**Use it when** + +- Introducing new or changed UI in place, once, with 3–5 steps that point at real elements. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Doing a task step by step | Steps | +| One contextual hint | Tooltip | +| An independent modal task | Dialog | + +**Do** + +- Start only on user request or after a meaningful event (new feature), never on every visit. +- Each step: short title, one sentence, `Back` / `Next`, final `Done`; always allow `Skip tour`. +- Point at elements that exist and are visible; scroll them into view. +- Remember completion per user. + +**Don't** + +- Block critical tasks with a tour. +- Explain what the label already says. + +**Copy and states** + +- Progress `2 of 4`; final button `Done`. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/tree-view-usage/SKILL.md b/libs/ui/agent-plugin/skills/tree-view-usage/SKILL.md index e4537ea01e..e06f824189 100644 --- a/libs/ui/agent-plugin/skills/tree-view-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/tree-view-usage/SKILL.md @@ -1,24 +1,17 @@ --- -component_version: "1.0.0" name: tree-view-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit TreeView for hierarchical data with Zag.js tree-view behavior, node collection, branch and item parts, selection behavior, icons, indentation, expansion, selection, and keyboard/typeahead support. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-usage-ux - - zag-compound-components - - app-token-overrides -sources: - - "libs/ui/src/molecules/tree-view.tsx" - - "libs/ui/src/tokens/components/molecules/_tree-view.css" - - "libs/ui/stories/molecules/tree-view.stories.tsx" - - "libs/ui/src/molecules/tree-view.figma.ts" - - "https://zagjs.com/components/react/tree-view" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux zag-compound-components app-token-overrides ux-guidelines" + sources: "libs/ui/src/molecules/tree-view.tsx libs/ui/src/tokens/components/molecules/_tree-view.css libs/ui/stories/molecules/tree-view.stories.tsx libs/ui/src/molecules/tree-view.figma.ts https://zagjs.com/components/react/tree-view" --- # @techsio/ui-kit TreeView Usage @@ -26,6 +19,38 @@ sources: Use TreeView for hierarchical navigation or selection. Do not flatten trees into custom nested lists when keyboard navigation/selection matters. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `TreeView`. + +**Use it when** + +- Selecting or managing items in a hierarchy with keyboard tree semantics: folders, category trees in admin, file browsers. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Site/category navigation links | VerticalNavigation | +| A hierarchical form value | CascadeSelect | +| Flat lists | Table / list | + +**Do** + +- Show counts or state per node where helpful; keep labels short. +- Persist expanded state across navigation. +- Offer search/filter for large trees. + +**Don't** + +- Put complex forms inside nodes. +- Nest deeper than ~5 levels without breadcrumbs or search. + +**Copy and states** + +- Node labels are names; actions on nodes follow Menu rules. + ## Setup ```tsx diff --git a/libs/ui/agent-plugin/skills/ui-component-usage/SKILL.md b/libs/ui/agent-plugin/skills/ui-component-usage/SKILL.md index 61f874bb4e..f65b36a75a 100644 --- a/libs/ui/agent-plugin/skills/ui-component-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-component-usage/SKILL.md @@ -5,7 +5,7 @@ description: > UX rules). Use when implementing app UI with the kit rather than developing the kit itself. Invoke explicitly with $ui-component-usage . metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- @@ -17,17 +17,23 @@ component types from the installed package. ## Order of reading -1. `component-usage-ux` skill — global usage/UX rules (always first) -2. `-usage` skill — the specific component -3. If styling/overrides are needed: `app-token-overrides` skill +1. `component-usage-ux` skill — which component fits the intent (always first) +2. `ux-guidelines` skill — house UX/UI rules: UX writing, dates/numbers, numeric + alignment, disabled/loading/empty states, where CRUD actions and toasts go, + component selection +3. `-usage` skill — the specific component; its **UX/UI guidelines** + section says when to use it, what to use instead, do/don't and copy +4. If styling/overrides are needed: `app-token-overrides` skill (app-side tokens; never patch the kit from an app) ## Available per-component guides -accordion, badge, breadcrumb, button, carousel, checkbox, color-select, combobox, dialog, -footer, form-checkbox, form-input, form-numeric-input, form-textarea, gallery, header, icon, -image, input, label, link, link-button, menu, numeric-input, pagination, phone-input, -popover, product-card, radio-card, radio-group, rating, search-form, select, skeleton, +accordion, action-icon, badge, breadcrumb, button, carousel, cascade-select, chart, +checkbox, color-select, combobox, command, data-table, date-picker, dialog, drawer, +facet-filter-panel, file-upload, footer, form-checkbox, form-error-summary, form-input, +form-numeric-input, form-textarea, gallery, header, hotkeys, icon, image, input, label, +link, link-button, menu, numeric-input, pagination, phone-input, popover, product-card, +radio-card, radio-group, rating, search-form, search-suggestions, select, sidebar, skeleton, slider, status-text, steps, switch, table, tabs, textarea, toast, tooltip, tour, tree-view, vertical-navigation — each bundled as the `-usage` skill. @@ -45,7 +51,8 @@ Token CSS in the app entry: `@techsio/ui-kit/tokens` (tokens only) or ## Rules of thumb -- Never rebuild something the kit ships — a data grid is the kit `Table`, not ``. +- Never rebuild something the kit ships — a data grid is the kit `DataTable`/`Table`, not `
`. +- Before shipping a screen, run the `ux-guidelines` review checklist. - Style via kit variants + semantic token classes; no arbitrary Tailwind values. - New requirement the kit can't express → file it as kit work ($ui-new-component), don't fork the component into the app. diff --git a/libs/ui/agent-plugin/skills/ui-figma-sync/SKILL.md b/libs/ui/agent-plugin/skills/ui-figma-sync/SKILL.md index ca8603d5f0..e1afe3664c 100644 --- a/libs/ui/agent-plugin/skills/ui-figma-sync/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-figma-sync/SKILL.md @@ -5,7 +5,7 @@ description: > variable naming/aliasing, and the token export pipeline. Use after a component's public API or visuals changed. Invoke explicitly with $ui-figma-sync. metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- diff --git a/libs/ui/agent-plugin/skills/ui-kit-publish-readiness/SKILL.md b/libs/ui/agent-plugin/skills/ui-kit-publish-readiness/SKILL.md index a3154b0489..252f269634 100644 --- a/libs/ui/agent-plugin/skills/ui-kit-publish-readiness/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-kit-publish-readiness/SKILL.md @@ -4,19 +4,12 @@ description: > Use before releasing or packaging @techsio/ui-kit. Checks RSLib build, package subpath exports, publint, token validators, Storybook build/a11y, component screenshots, files array, and semantic-release CI constraints. -type: lifecycle -library: "@techsio/ui-kit" -library_version: "0.3.2" -requires: - - component-consistency-validation -sources: - - "libs/ui/package.json" - - "libs/ui/project.json" - - "libs/ui/rslib.config.ts" - - "libs/ui/release.config.mjs" - - "libs/ui/scripts/storybook-a11y.sh" - - "libs/ui/scripts/storybook-a11y-summary.mjs" - - "libs/ui/scripts/run-component-tests.mjs" +metadata: + type: "lifecycle" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-consistency-validation" + sources: "libs/ui/package.json libs/ui/project.json libs/ui/rslib.config.ts libs/ui/release.config.mjs libs/ui/scripts/storybook-a11y.sh libs/ui/scripts/storybook-a11y-summary.mjs libs/ui/scripts/run-component-tests.mjs" --- # @techsio/ui-kit Publish Readiness Checklist diff --git a/libs/ui/agent-plugin/skills/ui-kit-workflow-orchestrator/SKILL.md b/libs/ui/agent-plugin/skills/ui-kit-workflow-orchestrator/SKILL.md index da7aee250f..fcd91fc17d 100644 --- a/libs/ui/agent-plugin/skills/ui-kit-workflow-orchestrator/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-kit-workflow-orchestrator/SKILL.md @@ -5,14 +5,11 @@ description: > consumer. Routes component authoring, app usage, token work, Storybook, validation, framework adapters, Figma handoff, and Intent skill maintenance without crossing library/app scope accidentally. -type: lifecycle -library: "@techsio/ui-kit" -library_version: "0.3.2" -sources: - - "libs/ui/AGENTS.md" - - "libs/ui/skills/_artifacts/domain_map.yaml" - - "libs/ui/skills/_artifacts/skill_spec.md" - - "libs/ui/skills/_artifacts/skill_tree.yaml" +metadata: + type: "lifecycle" + library: "@techsio/ui-kit" + library_version: "0.3.2" + sources: "libs/ui/AGENTS.md libs/ui/skills/_artifacts/domain_map.yaml libs/ui/skills/_artifacts/skill_spec.md libs/ui/skills/_artifacts/skill_tree.yaml" --- # @techsio/ui-kit Workflow Orchestrator diff --git a/libs/ui/agent-plugin/skills/ui-new-component/SKILL.md b/libs/ui/agent-plugin/skills/ui-new-component/SKILL.md index f978e4fe54..9b238e2dc6 100644 --- a/libs/ui/agent-plugin/skills/ui-new-component/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-new-component/SKILL.md @@ -5,7 +5,7 @@ description: > atom, molecule, organism, or template — walks through component source with tv(), token CSS, story, and Figma follow-up. Invoke explicitly with $ui-new-component . metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- diff --git a/libs/ui/agent-plugin/skills/ui-release-check/SKILL.md b/libs/ui/agent-plugin/skills/ui-release-check/SKILL.md index 5d072f403f..ffd30a6bcc 100644 --- a/libs/ui/agent-plugin/skills/ui-release-check/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-release-check/SKILL.md @@ -5,7 +5,7 @@ description: > semantic-release commit types, build output, and breaking-change review. Invoke explicitly with $ui-release-check. metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- diff --git a/libs/ui/agent-plugin/skills/ui-story/SKILL.md b/libs/ui/agent-plugin/skills/ui-story/SKILL.md index 642d0070c6..09c6c39eb6 100644 --- a/libs/ui/agent-plugin/skills/ui-story/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-story/SKILL.md @@ -5,7 +5,7 @@ description: > autodocs). Use when a component is added or its API changes. Invoke explicitly with $ui-story . metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- diff --git a/libs/ui/agent-plugin/skills/ui-theme-brand/SKILL.md b/libs/ui/agent-plugin/skills/ui-theme-brand/SKILL.md index 7678e2b465..348f754cfd 100644 --- a/libs/ui/agent-plugin/skills/ui-theme-brand/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-theme-brand/SKILL.md @@ -5,7 +5,7 @@ description: > variables pipeline or fully in code ("vibe" a theme). Use for brand colors, data-theme switching, or theme overrides. Invoke explicitly with $ui-theme-brand. metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- diff --git a/libs/ui/agent-plugin/skills/ui-tokens/SKILL.md b/libs/ui/agent-plugin/skills/ui-tokens/SKILL.md index f3d3a34074..65229b45ee 100644 --- a/libs/ui/agent-plugin/skills/ui-tokens/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-tokens/SKILL.md @@ -5,7 +5,7 @@ description: > the token cascade, shared form-control/icon-button utilities, and run token validation. Use for any change under libs/ui/src/tokens/. Invoke explicitly with $ui-tokens. metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- diff --git a/libs/ui/agent-plugin/skills/ui-validate/SKILL.md b/libs/ui/agent-plugin/skills/ui-validate/SKILL.md index 991a35099c..4fb6f6693c 100644 --- a/libs/ui/agent-plugin/skills/ui-validate/SKILL.md +++ b/libs/ui/agent-plugin/skills/ui-validate/SKILL.md @@ -5,7 +5,7 @@ description: > biome on changed files, tailwind lint, visual tests, consistency checklist. Use as the last step of any libs/ui change. Invoke explicitly with $ui-validate. metadata: - plugin: techsio-ui-kit-ai + plugin: "techsio-ui-kit-ai" library: "@techsio/ui-kit" --- diff --git a/libs/ui/agent-plugin/skills/ux-guidelines/SKILL.md b/libs/ui/agent-plugin/skills/ux-guidelines/SKILL.md new file mode 100644 index 0000000000..d4451d0ead --- /dev/null +++ b/libs/ui/agent-plugin/skills/ux-guidelines/SKILL.md @@ -0,0 +1,160 @@ +--- +name: ux-guidelines +description: > + Use when designing, building or reviewing any screen with @techsio/ui-kit — + the house UX/UI rules shared by every component: UX writing (button labels, + save/edit/delete copy, toast and error messages), date and number formatting, + numeric alignment in tables, disabled/read-only/loading/empty states, where + CRUD actions and feedback appear (top-right vs bottom-right, toast placement), + and which component to pick instead of another. +metadata: + type: "composition" + library: "@techsio/ui-kit" + library_version: "0.3.2" + requires: "component-usage-ux" + sources: "libs/ui/stories/pages/crud-workflow.stories.tsx libs/ui/stories/pages/cms-record-editor.stories.tsx libs/ui/stories/pages/cms-content-list.stories.tsx libs/ui/stories/pages/settings.stories.tsx libs/ui/stories/pages/system-states.stories.tsx libs/ui/stories/pages/layouts.stories.tsx libs/ui/stories/pages/checkout.stories.tsx libs/ui/src/molecules/toast.tsx libs/ui/src/molecules/dialog.tsx libs/ui/src/organisms/table.tsx libs/ui/src/organisms/data-table.tsx libs/ui/skills/_artifacts/consumer_app_usage_rules.md" +--- + +# @techsio/ui-kit UX Guidelines + +One rulebook for how screens built from the kit *behave and speak*. Component +skills (`-usage`) say how to call a component; this skill says how +the result must feel, read and behave, so every page in every app is +consistent. Each component skill ends with a **UX/UI guidelines** section that +applies these rules to that component. + +Load order: `component-usage-ux` (pick the component) → this skill (the house +rules) → `-usage` (the API). + +## The rules in one screen + +| Topic | Rule | Details | +| --- | --- | --- | +| Voice | Sentence case everywhere; say what happens, in the user's words | [ux-writing](references/ux-writing.md) | +| Buttons | Verb (+ object): `Create product`, `Save changes`, `Delete product` — never `OK`, `Yes`, `Submit` | [ux-writing](references/ux-writing.md#button-labels) | +| Messages | ` `: `Product created`; errors say what, why, and how to fix | [ux-writing](references/ux-writing.md#messages) | +| Dates | `Intl.DateTimeFormat` with the app locale; never raw ISO; relative only for recent activity | [formatting](references/formatting.md#dates-and-times) | +| Numbers | `Intl.NumberFormat` with the app locale and ISO currency; never `"$" + n.toFixed(2)` | [formatting](references/formatting.md#numbers-currency-units) | +| Alignment | Numbers, money, quantities, percentages right-aligned (`align: "end"` / `numeric`) with tabular figures; text and dates start-aligned | [formatting](references/formatting.md#alignment-in-tables) | +| Missing value | `—` (em dash), not `0`, `N/A`, `null` or an empty cell | [formatting](references/formatting.md#empty-and-missing-values) | +| Disabled | Only when the reason is visible or obvious; otherwise keep enabled and validate | [states](references/states.md#disabled) | +| Read-only | Values the user may see but not change are read-only, not disabled | [states](references/states.md#read-only-vs-disabled) | +| Loading | `isLoading` + `loadingText` on the button that started it; Skeleton for content | [states](references/states.md#loading) | +| Page actions | Top-right of the page header; one primary per page | [feedback-and-actions](references/feedback-and-actions.md#where-actions-live) | +| Form actions | Bottom-right, after the last field: `[Cancel] [Primary]`, primary last | [feedback-and-actions](references/feedback-and-actions.md#where-actions-live) | +| Destructive | Separate from Save; confirm irreversible deletes in an `alertdialog` that names the record | [feedback-and-actions](references/feedback-and-actions.md#destructive-actions) | +| Toasts | One ``, bottom-end (bottom-right), for confirming mutations; never for validation | [feedback-and-actions](references/feedback-and-actions.md#choosing-the-feedback-surface) | +| Component choice | Pick by intent; each component has a documented "use instead" | [component-selection](references/component-selection.md) | + +## Core Patterns + +### CRUD loop (reference: Pages/Patterns/CRUD workflow) + +```text +List page PageHeader: title + [New ] top-right (the only primary) + DataTable: search, filters, sort, pagination; row click = read +Create Dialog placement="right" (drawer), form, footer [Cancel] [Create ] +Read row click → right drawer with DetailList + [Edit ] +Update light DataTable inline edit (one or two fields) → toast " saved" +Update full same drawer as create, title "Edit ", footer [Cancel] [Save changes] +Delete row action (danger, last) → alertdialog "Delete this ?" + naming the record, footer [Cancel] [Delete ] → toast " deleted" +``` + +### Full-page record editor (reference: Pages/CMS/Record editor) + +```text +PageHeader actions top-right: [Back to list] [Preview] [Save] (header stays visible) +Save disabled until the form is dirty; Badge "Unsaved changes" next to the status +Leaving while dirty → alertdialog [Keep editing] [Discard changes] +Delete lives in a separate section at the bottom — never beside Save +Save → toast " saved" (user stays on the page) +``` + +### Feedback surface decision + +```text +Field is invalid → inline error on the field (validateStatus + helpText) +Submit failed with 2+ errors → FormErrorSummary above the form + inline errors +Mutation succeeded, UI barely changes → Toast success +Background job finished / failed → Toast (error toast with a retry action stays until dismissed) +Irreversible or high-cost decision → Dialog role="alertdialog" +Persistent page condition → inline StatusText / SectionCard at the top of the content +Whole page missing/forbidden/broken → system state screen (keep the navigation) +``` + +## Common Mistakes + +### HIGH Toast as validation + +Wrong: + +```tsx +toaster.create({ type: "error", title: "Please fill in the SKU" }) +``` + +Correct: + +```tsx + +``` + +Toasts disappear and are announced out of context; field problems belong on the +field (and in `FormErrorSummary` after a failed submit). + +### HIGH Hand-formatted numbers and dates + +Wrong: + +```tsx +${product.price.toFixed(2)} + +``` + +Correct: + +```tsx +const money = new Intl.NumberFormat(locale, { style: "currency", currency: "EUR" }) +const day = new Intl.DateTimeFormat(locale, { dateStyle: "medium" }) + +{money.format(product.price)} + +``` + +### HIGH Vague action labels + +Wrong: `OK`, `Yes`, `Confirm`, `Submit`, `Delete` in a dialog that deletes a product. + +Correct: `Create product`, `Save changes`, `Delete product`, `Discard changes`. + +The button must still make sense when read alone by a screen reader. + +### MEDIUM Disabled with no reason + +Wrong: a greyed-out `Publish` button and nothing that says why. + +Correct: keep `Publish` enabled and show what is missing on click, or keep it +disabled *and* show the reason next to it ("Add a title to publish"). + +### MEDIUM Two primaries or scattered actions + +Wrong: `New product` top-right *and* a primary `Import` beside it; `Save` in the +header of one settings section and at the bottom of the next. + +Correct: one primary per surface; page actions top-right, form actions +bottom-right, consistently across the app. + +## Review checklist + +```text +[ ] Every action label is a verb (+ object) in sentence case +[ ] Every mutation has visible feedback (UI change or toast); no toast for validation +[ ] Dates/numbers go through Intl with the app locale; no raw ISO, no toFixed + symbol +[ ] Numeric columns are end-aligned with tabular figures; header aligned with its column +[ ] Missing values render as — +[ ] Disabled controls have a visible or obvious reason; read-only data is read-only +[ ] Page primary action top-right; form actions bottom-right, primary last +[ ] Destructive actions are separated and confirmed (or undoable) +[ ] Empty, loading and error states exist for every list and async section +[ ] The component matches its intent (see component-selection) +``` diff --git a/libs/ui/agent-plugin/skills/ux-guidelines/references/component-selection.md b/libs/ui/agent-plugin/skills/ux-guidelines/references/component-selection.md new file mode 100644 index 0000000000..1bb21b6b31 --- /dev/null +++ b/libs/ui/agent-plugin/skills/ux-guidelines/references/component-selection.md @@ -0,0 +1,84 @@ +# Component selection + +Pick by the user's intent, not by how the component looks. Every row links a +need to the kit component and names the look-alike you should *not* use. +Each `-usage` skill repeats its own rows in its UX/UI section. + +## Actions and navigation + +| Intent | Use | Not | +| --- | --- | --- | +| Do something on this page (save, delete, open a dialog) | `Button` | `Link`, `LinkButton` | +| Go to another URL, styled as a button | `LinkButton` | `Button` with `onClick={() => router.push()}` | +| Go to another URL inside text or a list | `Link` | `LinkButton`, `Button` | +| Compact icon action in toolbars, rows, inputs | `ActionIcon` (with `aria-label`) | `Button` with an icon and no text, `Icon` with `onClick` | +| Several commands on one object | `Menu` | a row of five buttons, `Select` | +| Search and run app-wide commands (⌘K) | `Command` | `Combobox`, `SearchSuggestions` | +| Show a shortcut | `Hotkeys` | hand-written `` | +| Where am I in the hierarchy | `Breadcrumb` | `Tabs`, `Steps` | +| App/site navigation, persistent on desktop | `Sidebar` + `VerticalNavigation` | `TreeView`, `Accordion` of links | +| Hierarchical selection with arrow keys (files, categories to pick) | `TreeView` | `VerticalNavigation` | +| Global top bar / footer | `Header` / `Footer` | custom flex divs | +| Move between pages of results | `Pagination` | `Steps`, infinite scroll in admin tables | + +## Overlays + +| Intent | Use | Not | +| --- | --- | --- | +| Focused task or decision that blocks the page | `Dialog` (center) | `Popover` | +| Irreversible confirmation | `Dialog role="alertdialog"` | `window.confirm`, `Popover` | +| Create/edit/read a record over a list | `Dialog placement="right"` (drawer) | a new page for a 5-field form | +| Transient edge panel (cart, filters on mobile, nav on mobile) | `Drawer` / `Sidebar` mobile mode | `Dialog` center | +| Small anchored content the user interacts with (filters, color, mini form) | `Popover` | `Dialog`, `Tooltip` | +| Short non-essential hint on hover/focus | `Tooltip` | `Popover`, `title` attribute | +| Teach an existing UI step by step | `Tour` | chained `Tooltip`s, `Dialog`s | + +## Feedback and status + +| Intent | Use | Not | +| --- | --- | --- | +| Confirm a finished action | `Toast` | `Dialog`, `alert()` | +| Message tied to a field or section | `StatusText` (or the Form* `helpText`/`validateStatus`) | `Toast` | +| List every error after submit | `FormErrorSummary` | a toast per error | +| Short label of a state or category | `Badge` | `Button`, `StatusText` | +| Content is loading | `Skeleton` | spinner in place of the layout | +| Button action in progress | `Button isLoading loadingText` | `Skeleton`, disabling the whole form | +| Rating value | `Rating` | star icons | + +## Form inputs + +| Intent | Use | Not | +| --- | --- | --- | +| Short text with label/help/error | `FormInput` | `Label` + `Input` + `StatusText` by hand | +| Long text | `FormTextarea` | `FormInput` | +| Exact number, price, quantity | `FormNumericInput` | `FormInput type="number"`, `Slider` | +| Approximate value in a range, visual feedback | `Slider` | `NumericInput` | +| Phone number | `PhoneInput` | `Select` + `Input` | +| Date / date-time | `DatePicker` | three `Select`s, `Input type="date"` in apps | +| One of 2–5 visible options | `RadioGroup` | `Select` | +| One of 2–4 options that need description/price | `RadioCard` | `RadioGroup` with long labels | +| One of many known options (6+) | `Select` | `RadioGroup` | +| One of many options, needs typing to find | `Combobox` | `Select` with 200 items | +| Option inside a hierarchy | `CascadeSelect` | nested `Select`s | +| Colour swatch choice | `ColorSelect` | `RadioGroup` of coloured dots | +| Agree / opt in / pick several | `FormCheckbox` (`Checkbox` in tables) | `Switch` | +| Setting that takes effect immediately | `Switch` | `Checkbox` | +| Attach files | `FileUpload` | bare `` | +| Site/catalog search that navigates | `SearchForm` / `SearchSuggestions` | `Combobox` | + +## Structure and data + +| Intent | Use | Not | +| --- | --- | --- | +| Peer views of the same object | `Tabs` | `Accordion`, `Steps` | +| Ordered multi-step task | `Steps` | `Tabs` | +| Optional/secondary content the user may expand; FAQs | `Accordion` | `Tabs` for sequential reading | +| Static tabular data | `Table` | div grids | +| Data the user sorts, filters, selects, edits, pages | `DataTable` | `Table` + hand-written sorting | +| Trends and comparisons | `Chart` (with a table alternative) | a table of 200 numbers | +| Product image with thumbnails / zoom | `Gallery` | `Carousel` | +| Horizontally browsable slides (banners, related products) | `Carousel` | `Gallery` | +| Product in a listing | `ProductCard` | custom card divs | +| Catalog facets | `FacetFilterPanel` | hand-built accordions of checkboxes | +| Images with fixed ratio / framework optimisation | `Image` (`as={NextImage}`) | `` | +| Decorative or adjacent icon | `Icon` | emoji, inline SVG | diff --git a/libs/ui/agent-plugin/skills/ux-guidelines/references/feedback-and-actions.md b/libs/ui/agent-plugin/skills/ux-guidelines/references/feedback-and-actions.md new file mode 100644 index 0000000000..dcb2895f6a --- /dev/null +++ b/libs/ui/agent-plugin/skills/ux-guidelines/references/feedback-and-actions.md @@ -0,0 +1,110 @@ +# Actions and feedback: where things live + +The same kind of action must appear in the same place on every screen, so +users stop searching. Positions below are for LTR; the kit uses logical +properties (`start`/`end`), so RTL mirrors automatically. + +## Where actions live + +**Rule:** an action sits at the *end* (right) of the container it acts on. +It goes at the **top** when it acts on the whole page or collection and must +be reachable without scrolling; it goes at the **bottom** when it *completes* +something the user fills or reads top-to-bottom. + +| Surface | Actions | Position | Order (start → end) | +| --- | --- | --- | --- | +| List page (collection) | `New ` (the page's only primary), `Import`, `Export` | top-right, in `PageHeader` actions | secondary/outlined … primary last | +| Full-page record editor | `Back to list`, `Preview`, `Save` / `Publish` | top-right, in the page header — kept visible (sticky) while the form scrolls | borderless back … primary last | +| Dialog, drawer (`Dialog placement="right"`) | `Cancel`, `Create ` / `Save changes` | bottom-right footer (`actions` prop, already `justify-end`) | `[Cancel] [Primary]` | +| Confirmation (`role="alertdialog"`) | `Cancel`, `Delete ` | bottom-right footer | `[Cancel] [Destructive]` | +| Settings section / inline form card | `Save changes` for that section | bottom-right of the section, after its last field | `[Reset]` … `[Save changes]` | +| Wizard / checkout step | `Back`, `Continue` (last step: the real action) | bottom: `Back` start, primary end | `[Back] ……… [Continue]` | +| Popover / filter panel | `Reset`, `Apply` | bottom-right of the panel | `[Reset] [Apply]` | +| Table rows | `Open`/`Edit`, `Duplicate`, `Delete` | end of the row (DataTable actions column), overflow in a Menu | destructive last, `tone: "danger"` | +| Bulk selection | actions for the selected rows | table toolbar, top, only while a selection exists | destructive last | +| Card (product, record) | one primary action | bottom of the card | — | +| Danger zone (delete record/workspace) | `Delete ` | its own section at the **bottom of the page**, never beside Save | — | + +Hard rules: + +- **One primary button per surface.** Everything else is `secondary`, + `outlined` or `borderless`. +- **Primary is last** (right-most) in a button group; `Cancel` sits before it. + Don't reorder by platform. +- **Destructive replaces the primary** in a confirmation; never show `Save` + and `Delete` as neighbours. +- **Don't split one form's actions** between the header and the footer. A + short form (dialog, drawer, settings section) → bottom; a long full-page + editor → sticky header. +- On small screens footers stay at the bottom (full-width buttons are fine); + the page header's actions collapse into a Menu before they wrap. + +## Choosing the feedback surface + +| What happened | Surface | Why | +| --- | --- | --- | +| A field is invalid | Inline on the field (`validateStatus`, `helpText`) | the fix happens there | +| Submit failed, 2+ errors | `FormErrorSummary` above the form + inline | one place to start, links to each field | +| Mutation succeeded and the change is visible in place | nothing, or a toast | don't congratulate every keystroke | +| Mutation succeeded, change not obvious (drawer closed, row moved, background save) | Toast `success` | the list alone doesn't prove the write | +| Reversible destructive action done (archive, remove) | Toast `success` with `Undo` | faster than a confirmation | +| Irreversible or expensive action requested | `Dialog role="alertdialog"` before acting | the user must decide | +| Background/async job result | Toast (`loading` → `success`/`error`) | the user may be elsewhere | +| Recoverable failure of an async action | Toast `error` with `Try again`, stays until dismissed | needs action, must not vanish | +| Persistent condition of this page (read-only mode, sync paused, trial ends) | inline StatusText/section at the top of the content | must stay visible while relevant | +| Whole page unavailable (404, 403, 500, offline) | system state screen inside the app shell | see Pages/System/States | +| Contextual hint about a control | Tooltip (icon buttons) or help text (fields) | on demand, next to it | + +Never use a toast for: validation errors, information the user must act on +later, content they need to copy, or the only copy of an error message. + +## Toasts + +- **One `` per app**, mounted in the root layout. The kit's store + places toasts **bottom-end (bottom-right)** with a 16 px gap and 24 px + offset. Don't create other placements per page. +- **Why bottom-right:** page commands and the user menu live top-right — + a top toast would cover the next action; form and dialog actions end + bottom-right, so the confirmation appears where the user's attention already + is, without covering content being read top-left. +- Mind sticky bottom bars (cookie bars, mobile checkout bars): offset the + toaster so toasts never cover a primary action. +- **Duration:** `success`/`info` 5 s (the kit default in stories); `warning` + 8 s; `error` with an action and `loading` stay until resolved or dismissed. + Never auto-dismiss something the user must read or act on. +- **Content:** title = ` ` (see ux-writing); + optional one-sentence description; at most one action (`Undo`, `View`, + `Try again`). +- **Rate:** one toast per user action. Bulk operations produce one summary + toast (`12 products archived`), not twelve. +- Toasts are announced politely and never steal focus; don't put the only way + to reach something inside a toast. + +## Destructive actions + +| Action | Pattern | +| --- | --- | +| Reversible (archive, remove from a collection, soft delete) | act immediately → toast with `Undo` | +| Irreversible, one record | `alertdialog` naming the record and consequences → `[Cancel] [Delete ]` | +| Irreversible, high impact (workspace, all data, billing) | `alertdialog` + type the name to confirm; lives in a danger-zone section | +| Bulk irreversible | `alertdialog` with the count: `Delete 12 products?` | + +- Destructive buttons use `variant="danger"`; row actions use `tone: "danger"` + and sit last. +- The confirmation's description says what will be lost and what will not + (`Orders that include it are kept.`). +- After deletion, return the user to a stable place (the list, with focus on + the neighbour row) and confirm with a toast. + +## Create / read / update / delete surfaces + +| Operation | Default surface | Use a full page instead when | +| --- | --- | --- | +| Create | right drawer (`Dialog placement="right"`) over the list | the record is long, has media or several sections | +| Read | row click → right drawer with details | the record has its own sub-lists/tabs (order detail, customer) | +| Update (1–2 fields) | DataTable inline edit | never — inline edit is the light path | +| Update (full) | the same drawer as create | same as create | +| Delete | row action → `alertdialog` (or `Undo` toast if reversible) | — | + +Create and update share **one form component** — a record has one shape. +Opening a record must not lose the list's scroll position, search or filters. diff --git a/libs/ui/agent-plugin/skills/ux-guidelines/references/formatting.md b/libs/ui/agent-plugin/skills/ux-guidelines/references/formatting.md new file mode 100644 index 0000000000..1150a5edc4 --- /dev/null +++ b/libs/ui/agent-plugin/skills/ux-guidelines/references/formatting.md @@ -0,0 +1,108 @@ +# Dates, numbers and alignment + +Format every value through `Intl` with **one app locale** (from the i18n layer +or the request), never by string concatenation. Create formatters once per +locale (module scope or `useMemo`) and reuse them. + +```ts +// apps//src/lib/format.ts — one place per app +export const formatters = (locale: string, currency = "EUR") => ({ + money: new Intl.NumberFormat(locale, { style: "currency", currency }), + count: new Intl.NumberFormat(locale), + percent: new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 1 }), + compact: new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: 1 }), + date: new Intl.DateTimeFormat(locale, { dateStyle: "medium" }), + dateTime: new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }), + time: new Intl.DateTimeFormat(locale, { timeStyle: "short" }), + relative: new Intl.RelativeTimeFormat(locale, { numeric: "auto" }), +}) +``` + +## Locale defaults in the kit + +Kit components format with their own defaults when no locale is passed, and the +defaults differ: `NumericInput` defaults to `cs-CZ`, `DatePicker` to `en-US`. +**Always pass the app locale explicitly** (`locale={locale}`) to every +locale-aware component (`NumericInput`, `FormNumericInput`, `DatePicker`, +`PhoneInput`, `Chart` value formatters) so one screen never mixes `1 234,50` +with `1,234.50`. + +## Dates and times + +| Context | Format | en-GB | cs-CZ | +| --- | --- | --- | --- | +| Tables, lists, detail values | `dateStyle: "medium"` | `4 Sept 2026` | `4. 9. 2026` | +| With time (audit, orders) | `dateStyle: "medium", timeStyle: "short"` | `4 Sept 2026, 14:05` | `4. 9. 2026 14:05` | +| Headlines, emails, invoices | `dateStyle: "long"` | `4 September 2026` | `4. září 2026` | +| Activity feeds, comments, "last seen" (< 7 days) | `RelativeTimeFormat` | `2 hours ago`, `yesterday` | `před 2 hodinami` | +| Ranges | `formatRange` | `4 – 12 Sept 2026` | `04.09.2026 – 12.09.2026` | + +- **Never render ISO strings** (`2026-09-04`, `2026-08-11 10:03`) or + `Date.toString()` to users. ISO is for data, URLs and `
``` diff --git a/libs/ui/skills/tabs-usage/SKILL.md b/libs/ui/skills/tabs-usage/SKILL.md index 18edeb2123..c571aff17b 100644 --- a/libs/ui/skills/tabs-usage/SKILL.md +++ b/libs/ui/skills/tabs-usage/SKILL.md @@ -1,18 +1,20 @@ --- -component_version: "1.0.0" name: tabs-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Tabs for switching peer content panels with Zag.js tabs behavior, compound list, triggers, content, indicator, orientation, activation mode, variants, fitted, and justify props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" requires: - component-usage-ux - zag-compound-components - app-token-overrides + - ux-guidelines sources: - "libs/ui/src/molecules/tabs.tsx" - "libs/ui/src/tokens/components/molecules/_tabs.css" @@ -26,6 +28,39 @@ sources: Use Tabs for peer panels in the same page context. Use Steps for ordered workflow progress and Breadcrumb for hierarchy. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Tabs`. + +**Use it when** + +- Peer views of the same object or page: record sections (Content, SEO, History), settings groups (vertical). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Sequential steps | Steps | +| Optional expandable content | Accordion | +| Navigation between pages | links / Sidebar | +| Two views of the same data (table/grid) | a segmented control or icon toggle | + +**Do** + +- Keep labels short (1–2 words) and the first tab the content users came for. +- Preserve the active tab in the URL for linkable settings/records. +- Show errors in a tab's label (count badge) when the invalid field is in a hidden tab. + +**Don't** + +- Nest tabs inside tabs. +- Use tabs when users must compare content across tabs. + +**Copy and states** + +- Nouns in sentence case; counts in parentheses or a Badge (`Orders 12`). + ## Setup ```tsx diff --git a/libs/ui/skills/tailwind-token-authoring/SKILL.md b/libs/ui/skills/tailwind-token-authoring/SKILL.md index e024487523..f79b2c6c1d 100644 --- a/libs/ui/skills/tailwind-token-authoring/SKILL.md +++ b/libs/ui/skills/tailwind-token-authoring/SKILL.md @@ -5,9 +5,10 @@ description: > @theme static, semantic aliases, two-layer component tokens, component CSS imports, Tailwind token utility class mapping, app override compatibility, and token validator commands. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" sources: - "libs/ui/AGENTS.md" - "libs/ui/token-contribution.md" diff --git a/libs/ui/skills/textarea-usage/SKILL.md b/libs/ui/skills/textarea-usage/SKILL.md index 08c023f1d4..241c63eb95 100644 --- a/libs/ui/skills/textarea-usage/SKILL.md +++ b/libs/ui/skills/textarea-usage/SKILL.md @@ -1,16 +1,18 @@ --- -component_version: "1.0.0" name: textarea-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Textarea for multi-line text entry with valid variant, size, resize, readonly styling, and token-first validation. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" requires: - component-usage-ux - app-token-overrides + - ux-guidelines sources: - "libs/ui/src/atoms/textarea.tsx" - "libs/ui/src/tokens/components/atoms/_textarea.css" @@ -23,6 +25,35 @@ sources: Use Textarea for multi-line text entry. Use form molecules when a complete label/help/error field abstraction exists for the current app context. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Textarea`. + +**Use it when** + +- Bare multi-line text controls in compositions that supply their own label (inline comment boxes, table editors). + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| A labelled field | FormTextarea | +| Single-line text | Input | + +**Do** + +- Give it an accessible name. +- Auto-grow or size to the expected content; keep a sensible max height. + +**Don't** + +- Use placeholder as the label. + +**Copy and states** + +- Placeholder as an example (`Add a note for the courier…`). + ## Setup ```tsx diff --git a/libs/ui/skills/toast-usage/SKILL.md b/libs/ui/skills/toast-usage/SKILL.md index 5fea8ee0d2..059d206a15 100644 --- a/libs/ui/skills/toast-usage/SKILL.md +++ b/libs/ui/skills/toast-usage/SKILL.md @@ -1,17 +1,19 @@ --- -component_version: "1.0.0" name: toast-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Toast for transient CRUD feedback using the global Zag.js toaster store, Toaster portal, useToast, toast types, title, description, close trigger, and token styling. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" requires: - component-usage-ux - app-token-overrides + - ux-guidelines sources: - "libs/ui/src/molecules/toast.tsx" - "libs/ui/src/tokens/components/molecules/_toast.css" @@ -25,6 +27,44 @@ sources: Use Toast for transient feedback after actions. Use StatusText for inline field messages and Dialog for blocking decisions. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Toast`. + +**Use it when** + +- Confirming a finished mutation when the UI doesn't show it clearly (drawer closed, row updated, background save). +- Results of async/background work; `Undo` for reversible destructive actions. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Field or form validation | inline errors + FormErrorSummary | +| Irreversible decisions | Dialog role="alertdialog" | +| Persistent page conditions | inline StatusText at the top of the content | +| Information to read or copy later | inline content | + +**Do** + +- Mount one `` in the app root; keep the kit placement bottom-end (bottom-right) — ux-guidelines/feedback-and-actions#toasts. +- Title ` ` (`Product created`); optional one-sentence description; at most one action. +- Durations: success/info ~5 s, warning ~8 s; errors with an action and `loading` stay until resolved. +- Update a `loading` toast into success/error instead of stacking a second toast. +- One toast per user action; bulk actions get one summary toast. + +**Don't** + +- Use toasts for validation errors or as the only copy of an error. +- Put links to the only place something can be found inside a toast. +- Show a toast on page load or for things the user didn't do. + +**Copy and states** + +- Never `Success!`, `Done` or `Saved` alone — name the object (`Settings saved`). +- Errors: `Product not saved` + reason + `Try again`. + ## Setup ```tsx @@ -43,12 +83,12 @@ function SaveButton() { onClick={() => toaster.create({ type: "success", - title: "Saved", - description: "Changes were saved.", + title: "Product saved", + description: "Linen shirt", }) } > - Save + Save changes ) } @@ -92,7 +132,7 @@ Correct: ```tsx const toaster = useToast() -return +return ``` Source: libs/ui/src/molecules/toast.tsx @@ -124,7 +164,7 @@ Wrong: Correct: ```tsx - + ``` Source: libs/ui/src/tokens/components/molecules/_toast.css diff --git a/libs/ui/skills/tooltip-usage/SKILL.md b/libs/ui/skills/tooltip-usage/SKILL.md index 7af390d553..0b093cd89f 100644 --- a/libs/ui/skills/tooltip-usage/SKILL.md +++ b/libs/ui/skills/tooltip-usage/SKILL.md @@ -1,16 +1,18 @@ --- -component_version: "1.0.0" name: tooltip-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit Tooltip for supplemental hover/focus help using the Zag.js tooltip wrapper, supported timing, placement, interaction, controlled state, and token styling props. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" requires: - component-usage-ux - app-token-overrides + - ux-guidelines sources: - "libs/ui/src/atoms/tooltip.tsx" - "libs/ui/src/tokens/components/atoms/_tooltip.css" @@ -24,6 +26,39 @@ sources: Use Tooltip for short supplemental information on hover/focus. Do not use it for required instructions, blocking errors, or primary content. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Tooltip`. + +**Use it when** + +- Short, non-essential hints on hover and focus: icon-button names, truncated text, keyboard shortcuts. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Required information or instructions | visible help text | +| Interactive content | Popover | +| Errors | inline StatusText / field error | + +**Do** + +- Keep to a few words; repeat the ActionIcon's `aria-label`. +- Attach to focusable triggers so keyboard users get it too. +- Use for the full value of truncated cells. + +**Don't** + +- Put tooltips on disabled buttons (they don't receive hover/focus) — explain next to the control. +- Put links or buttons inside a tooltip. +- Use tooltips on touch-only surfaces as the only explanation. + +**Copy and states** + +- No trailing period for fragments; sentence case. + ## Setup ```tsx diff --git a/libs/ui/skills/tour-usage/SKILL.md b/libs/ui/skills/tour-usage/SKILL.md index 74705d6623..12080027c1 100644 --- a/libs/ui/skills/tour-usage/SKILL.md +++ b/libs/ui/skills/tour-usage/SKILL.md @@ -1,17 +1,19 @@ --- -component_version: "1.0.2" name: tour-usage description: > Use after component-usage-ux for guided walkthroughs with @techsio/ui-kit Tour: tooltip/dialog/floating steps, target resolution, interactive wait effects, imperative control, shared action buttons, cleanup and app-owned onboarding policy. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + component_version: "1.0.2" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" requires: - component-usage-ux - zag-compound-components - app-token-overrides + - ux-guidelines sources: - "libs/ui/src/molecules/tour.tsx" - "libs/ui/src/tokens/components/molecules/_tour.css" @@ -28,6 +30,39 @@ Use Tour to explain an existing interface. Use Steps for a wizard/progress workflow, Tooltip for a short contextual hint, and Dialog for an independent modal task. Tour owns one Zag machine, not nested Tooltip/Dialog machines. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `Tour`. + +**Use it when** + +- Introducing new or changed UI in place, once, with 3–5 steps that point at real elements. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Doing a task step by step | Steps | +| One contextual hint | Tooltip | +| An independent modal task | Dialog | + +**Do** + +- Start only on user request or after a meaningful event (new feature), never on every visit. +- Each step: short title, one sentence, `Back` / `Next`, final `Done`; always allow `Skip tour`. +- Point at elements that exist and are visible; scroll them into view. +- Remember completion per user. + +**Don't** + +- Block critical tasks with a tour. +- Explain what the label already says. + +**Copy and states** + +- Progress `2 of 4`; final button `Done`. + ## Setup ```tsx diff --git a/libs/ui/skills/tree-view-usage/SKILL.md b/libs/ui/skills/tree-view-usage/SKILL.md index e4537ea01e..9e5383c91d 100644 --- a/libs/ui/skills/tree-view-usage/SKILL.md +++ b/libs/ui/skills/tree-view-usage/SKILL.md @@ -1,18 +1,20 @@ --- -component_version: "1.0.0" name: tree-view-usage description: > Use after component-usage-ux when an app needs @techsio/ui-kit TreeView for hierarchical data with Zag.js tree-view behavior, node collection, branch and item parts, selection behavior, icons, indentation, expansion, selection, and keyboard/typeahead support. -type: core -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + component_version: "1.0.0" + type: "core" + library: "@techsio/ui-kit" + library_version: "0.3.2" requires: - component-usage-ux - zag-compound-components - app-token-overrides + - ux-guidelines sources: - "libs/ui/src/molecules/tree-view.tsx" - "libs/ui/src/tokens/components/molecules/_tree-view.css" @@ -26,6 +28,38 @@ sources: Use TreeView for hierarchical navigation or selection. Do not flatten trees into custom nested lists when keyboard navigation/selection matters. +## UX/UI guidelines + +House rules come from the `ux-guidelines` skill (writing, formatting, states, +where actions and feedback live). This section applies them to `TreeView`. + +**Use it when** + +- Selecting or managing items in a hierarchy with keyboard tree semantics: folders, category trees in admin, file browsers. + +**Use something else when** + +| Need | Use instead | +| --- | --- | +| Site/category navigation links | VerticalNavigation | +| A hierarchical form value | CascadeSelect | +| Flat lists | Table / list | + +**Do** + +- Show counts or state per node where helpful; keep labels short. +- Persist expanded state across navigation. +- Offer search/filter for large trees. + +**Don't** + +- Put complex forms inside nodes. +- Nest deeper than ~5 levels without breadcrumbs or search. + +**Copy and states** + +- Node labels are names; actions on nodes follow Menu rules. + ## Setup ```tsx diff --git a/libs/ui/skills/ui-kit-publish-readiness/SKILL.md b/libs/ui/skills/ui-kit-publish-readiness/SKILL.md index a3154b0489..c3c982cb50 100644 --- a/libs/ui/skills/ui-kit-publish-readiness/SKILL.md +++ b/libs/ui/skills/ui-kit-publish-readiness/SKILL.md @@ -4,9 +4,10 @@ description: > Use before releasing or packaging @techsio/ui-kit. Checks RSLib build, package subpath exports, publint, token validators, Storybook build/a11y, component screenshots, files array, and semantic-release CI constraints. -type: lifecycle -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + type: "lifecycle" + library: "@techsio/ui-kit" + library_version: "0.3.2" requires: - component-consistency-validation sources: diff --git a/libs/ui/skills/ui-kit-workflow-orchestrator/SKILL.md b/libs/ui/skills/ui-kit-workflow-orchestrator/SKILL.md index da7aee250f..da459aa56d 100644 --- a/libs/ui/skills/ui-kit-workflow-orchestrator/SKILL.md +++ b/libs/ui/skills/ui-kit-workflow-orchestrator/SKILL.md @@ -5,9 +5,10 @@ description: > consumer. Routes component authoring, app usage, token work, Storybook, validation, framework adapters, Figma handoff, and Intent skill maintenance without crossing library/app scope accidentally. -type: lifecycle -library: "@techsio/ui-kit" -library_version: "0.3.2" +metadata: + type: "lifecycle" + library: "@techsio/ui-kit" + library_version: "0.3.2" sources: - "libs/ui/AGENTS.md" - "libs/ui/skills/_artifacts/domain_map.yaml" diff --git a/libs/ui/skills/ux-guidelines/SKILL.md b/libs/ui/skills/ux-guidelines/SKILL.md new file mode 100644 index 0000000000..63a6149ad5 --- /dev/null +++ b/libs/ui/skills/ux-guidelines/SKILL.md @@ -0,0 +1,173 @@ +--- +name: ux-guidelines +description: > + Use when designing, building or reviewing any screen with @techsio/ui-kit — + the house UX/UI rules shared by every component: UX writing (button labels, + save/edit/delete copy, toast and error messages), date and number formatting, + numeric alignment in tables, disabled/read-only/loading/empty states, where + CRUD actions and feedback appear (top-right vs bottom-right, toast placement), + and which component to pick instead of another. +metadata: + type: "composition" + library: "@techsio/ui-kit" + library_version: "0.3.2" +requires: + - component-usage-ux +sources: + - "libs/ui/stories/pages/crud-workflow.stories.tsx" + - "libs/ui/stories/pages/cms-record-editor.stories.tsx" + - "libs/ui/stories/pages/cms-content-list.stories.tsx" + - "libs/ui/stories/pages/settings.stories.tsx" + - "libs/ui/stories/pages/system-states.stories.tsx" + - "libs/ui/stories/pages/layouts.stories.tsx" + - "libs/ui/stories/pages/checkout.stories.tsx" + - "libs/ui/src/molecules/toast.tsx" + - "libs/ui/src/molecules/dialog.tsx" + - "libs/ui/src/organisms/table.tsx" + - "libs/ui/src/organisms/data-table.tsx" + - "libs/ui/skills/_artifacts/consumer_app_usage_rules.md" +--- + +# @techsio/ui-kit UX Guidelines + +One rulebook for how screens built from the kit *behave and speak*. Component +skills (`-usage`) say how to call a component; this skill says how +the result must feel, read and behave, so every page in every app is +consistent. Each component skill ends with a **UX/UI guidelines** section that +applies these rules to that component. + +Load order: `component-usage-ux` (pick the component) → this skill (the house +rules) → `-usage` (the API). + +## The rules in one screen + +| Topic | Rule | Details | +| --- | --- | --- | +| Voice | Sentence case everywhere; say what happens, in the user's words | [ux-writing](references/ux-writing.md) | +| Buttons | Verb (+ object): `Create product`, `Save changes`, `Delete product` — never `OK`, `Yes`, `Submit` | [ux-writing](references/ux-writing.md#button-labels) | +| Messages | ` `: `Product created`; errors say what, why, and how to fix | [ux-writing](references/ux-writing.md#messages) | +| Dates | `Intl.DateTimeFormat` with the app locale; never raw ISO; relative only for recent activity | [formatting](references/formatting.md#dates-and-times) | +| Numbers | `Intl.NumberFormat` with the app locale and ISO currency; never `"$" + n.toFixed(2)` | [formatting](references/formatting.md#numbers-currency-units) | +| Alignment | Numbers, money, quantities, percentages right-aligned (`align: "end"` / `numeric`) with tabular figures; text and dates start-aligned | [formatting](references/formatting.md#alignment-in-tables) | +| Missing value | `—` (em dash), not `0`, `N/A`, `null` or an empty cell | [formatting](references/formatting.md#empty-and-missing-values) | +| Disabled | Only when the reason is visible or obvious; otherwise keep enabled and validate | [states](references/states.md#disabled) | +| Read-only | Values the user may see but not change are read-only, not disabled | [states](references/states.md#read-only-vs-disabled) | +| Loading | `isLoading` + `loadingText` on the button that started it; Skeleton for content | [states](references/states.md#loading) | +| Page actions | Top-right of the page header; one primary per page | [feedback-and-actions](references/feedback-and-actions.md#where-actions-live) | +| Form actions | Bottom-right, after the last field: `[Cancel] [Primary]`, primary last | [feedback-and-actions](references/feedback-and-actions.md#where-actions-live) | +| Destructive | Separate from Save; confirm irreversible deletes in an `alertdialog` that names the record | [feedback-and-actions](references/feedback-and-actions.md#destructive-actions) | +| Toasts | One ``, bottom-end (bottom-right), for confirming mutations; never for validation | [feedback-and-actions](references/feedback-and-actions.md#choosing-the-feedback-surface) | +| Component choice | Pick by intent; each component has a documented "use instead" | [component-selection](references/component-selection.md) | + +## Core Patterns + +### CRUD loop (reference: Pages/Patterns/CRUD workflow) + +```text +List page PageHeader: title + [New ] top-right (the only primary) + DataTable: search, filters, sort, pagination; row click = read +Create Dialog placement="right" (drawer), form, footer [Cancel] [Create ] +Read row click → right drawer with DetailList + [Edit ] +Update light DataTable inline edit (one or two fields) → toast " saved" +Update full same drawer as create, title "Edit ", footer [Cancel] [Save changes] +Delete row action (danger, last) → alertdialog "Delete this ?" + naming the record, footer [Cancel] [Delete ] → toast " deleted" +``` + +### Full-page record editor (reference: Pages/CMS/Record editor) + +```text +PageHeader actions top-right: [Back to list] [Preview] [Save] (header stays visible) +Save disabled until the form is dirty; Badge "Unsaved changes" next to the status +Leaving while dirty → alertdialog [Keep editing] [Discard changes] +Delete lives in a separate section at the bottom — never beside Save +Save → toast " saved" (user stays on the page) +``` + +### Feedback surface decision + +```text +Field is invalid → inline error on the field (validateStatus + helpText) +Submit failed with 2+ errors → FormErrorSummary above the form + inline errors +Mutation succeeded, UI barely changes → Toast success +Background job finished / failed → Toast (error toast with a retry action stays until dismissed) +Irreversible or high-cost decision → Dialog role="alertdialog" +Persistent page condition → inline StatusText / SectionCard at the top of the content +Whole page missing/forbidden/broken → system state screen (keep the navigation) +``` + +## Common Mistakes + +### HIGH Toast as validation + +Wrong: + +```tsx +toaster.create({ type: "error", title: "Please fill in the SKU" }) +``` + +Correct: + +```tsx + +``` + +Toasts disappear and are announced out of context; field problems belong on the +field (and in `FormErrorSummary` after a failed submit). + +### HIGH Hand-formatted numbers and dates + +Wrong: + +```tsx +${product.price.toFixed(2)} + +``` + +Correct: + +```tsx +const money = new Intl.NumberFormat(locale, { style: "currency", currency: "EUR" }) +const day = new Intl.DateTimeFormat(locale, { dateStyle: "medium" }) + +{money.format(product.price)} + +``` + +### HIGH Vague action labels + +Wrong: `OK`, `Yes`, `Confirm`, `Submit`, `Delete` in a dialog that deletes a product. + +Correct: `Create product`, `Save changes`, `Delete product`, `Discard changes`. + +The button must still make sense when read alone by a screen reader. + +### MEDIUM Disabled with no reason + +Wrong: a greyed-out `Publish` button and nothing that says why. + +Correct: keep `Publish` enabled and show what is missing on click, or keep it +disabled *and* show the reason next to it ("Add a title to publish"). + +### MEDIUM Two primaries or scattered actions + +Wrong: `New product` top-right *and* a primary `Import` beside it; `Save` in the +header of one settings section and at the bottom of the next. + +Correct: one primary per surface; page actions top-right, form actions +bottom-right, consistently across the app. + +## Review checklist + +```text +[ ] Every action label is a verb (+ object) in sentence case +[ ] Every mutation has visible feedback (UI change or toast); no toast for validation +[ ] Dates/numbers go through Intl with the app locale; no raw ISO, no toFixed + symbol +[ ] Numeric columns are end-aligned with tabular figures; header aligned with its column +[ ] Missing values render as — +[ ] Disabled controls have a visible or obvious reason; read-only data is read-only +[ ] Page primary action top-right; form actions bottom-right, primary last +[ ] Destructive actions are separated and confirmed (or undoable) +[ ] Empty, loading and error states exist for every list and async section +[ ] The component matches its intent (see component-selection) +``` diff --git a/libs/ui/skills/ux-guidelines/references/component-selection.md b/libs/ui/skills/ux-guidelines/references/component-selection.md new file mode 100644 index 0000000000..1bb21b6b31 --- /dev/null +++ b/libs/ui/skills/ux-guidelines/references/component-selection.md @@ -0,0 +1,84 @@ +# Component selection + +Pick by the user's intent, not by how the component looks. Every row links a +need to the kit component and names the look-alike you should *not* use. +Each `-usage` skill repeats its own rows in its UX/UI section. + +## Actions and navigation + +| Intent | Use | Not | +| --- | --- | --- | +| Do something on this page (save, delete, open a dialog) | `Button` | `Link`, `LinkButton` | +| Go to another URL, styled as a button | `LinkButton` | `Button` with `onClick={() => router.push()}` | +| Go to another URL inside text or a list | `Link` | `LinkButton`, `Button` | +| Compact icon action in toolbars, rows, inputs | `ActionIcon` (with `aria-label`) | `Button` with an icon and no text, `Icon` with `onClick` | +| Several commands on one object | `Menu` | a row of five buttons, `Select` | +| Search and run app-wide commands (⌘K) | `Command` | `Combobox`, `SearchSuggestions` | +| Show a shortcut | `Hotkeys` | hand-written `` | +| Where am I in the hierarchy | `Breadcrumb` | `Tabs`, `Steps` | +| App/site navigation, persistent on desktop | `Sidebar` + `VerticalNavigation` | `TreeView`, `Accordion` of links | +| Hierarchical selection with arrow keys (files, categories to pick) | `TreeView` | `VerticalNavigation` | +| Global top bar / footer | `Header` / `Footer` | custom flex divs | +| Move between pages of results | `Pagination` | `Steps`, infinite scroll in admin tables | + +## Overlays + +| Intent | Use | Not | +| --- | --- | --- | +| Focused task or decision that blocks the page | `Dialog` (center) | `Popover` | +| Irreversible confirmation | `Dialog role="alertdialog"` | `window.confirm`, `Popover` | +| Create/edit/read a record over a list | `Dialog placement="right"` (drawer) | a new page for a 5-field form | +| Transient edge panel (cart, filters on mobile, nav on mobile) | `Drawer` / `Sidebar` mobile mode | `Dialog` center | +| Small anchored content the user interacts with (filters, color, mini form) | `Popover` | `Dialog`, `Tooltip` | +| Short non-essential hint on hover/focus | `Tooltip` | `Popover`, `title` attribute | +| Teach an existing UI step by step | `Tour` | chained `Tooltip`s, `Dialog`s | + +## Feedback and status + +| Intent | Use | Not | +| --- | --- | --- | +| Confirm a finished action | `Toast` | `Dialog`, `alert()` | +| Message tied to a field or section | `StatusText` (or the Form* `helpText`/`validateStatus`) | `Toast` | +| List every error after submit | `FormErrorSummary` | a toast per error | +| Short label of a state or category | `Badge` | `Button`, `StatusText` | +| Content is loading | `Skeleton` | spinner in place of the layout | +| Button action in progress | `Button isLoading loadingText` | `Skeleton`, disabling the whole form | +| Rating value | `Rating` | star icons | + +## Form inputs + +| Intent | Use | Not | +| --- | --- | --- | +| Short text with label/help/error | `FormInput` | `Label` + `Input` + `StatusText` by hand | +| Long text | `FormTextarea` | `FormInput` | +| Exact number, price, quantity | `FormNumericInput` | `FormInput type="number"`, `Slider` | +| Approximate value in a range, visual feedback | `Slider` | `NumericInput` | +| Phone number | `PhoneInput` | `Select` + `Input` | +| Date / date-time | `DatePicker` | three `Select`s, `Input type="date"` in apps | +| One of 2–5 visible options | `RadioGroup` | `Select` | +| One of 2–4 options that need description/price | `RadioCard` | `RadioGroup` with long labels | +| One of many known options (6+) | `Select` | `RadioGroup` | +| One of many options, needs typing to find | `Combobox` | `Select` with 200 items | +| Option inside a hierarchy | `CascadeSelect` | nested `Select`s | +| Colour swatch choice | `ColorSelect` | `RadioGroup` of coloured dots | +| Agree / opt in / pick several | `FormCheckbox` (`Checkbox` in tables) | `Switch` | +| Setting that takes effect immediately | `Switch` | `Checkbox` | +| Attach files | `FileUpload` | bare `` | +| Site/catalog search that navigates | `SearchForm` / `SearchSuggestions` | `Combobox` | + +## Structure and data + +| Intent | Use | Not | +| --- | --- | --- | +| Peer views of the same object | `Tabs` | `Accordion`, `Steps` | +| Ordered multi-step task | `Steps` | `Tabs` | +| Optional/secondary content the user may expand; FAQs | `Accordion` | `Tabs` for sequential reading | +| Static tabular data | `Table` | div grids | +| Data the user sorts, filters, selects, edits, pages | `DataTable` | `Table` + hand-written sorting | +| Trends and comparisons | `Chart` (with a table alternative) | a table of 200 numbers | +| Product image with thumbnails / zoom | `Gallery` | `Carousel` | +| Horizontally browsable slides (banners, related products) | `Carousel` | `Gallery` | +| Product in a listing | `ProductCard` | custom card divs | +| Catalog facets | `FacetFilterPanel` | hand-built accordions of checkboxes | +| Images with fixed ratio / framework optimisation | `Image` (`as={NextImage}`) | `` | +| Decorative or adjacent icon | `Icon` | emoji, inline SVG | diff --git a/libs/ui/skills/ux-guidelines/references/feedback-and-actions.md b/libs/ui/skills/ux-guidelines/references/feedback-and-actions.md new file mode 100644 index 0000000000..dcb2895f6a --- /dev/null +++ b/libs/ui/skills/ux-guidelines/references/feedback-and-actions.md @@ -0,0 +1,110 @@ +# Actions and feedback: where things live + +The same kind of action must appear in the same place on every screen, so +users stop searching. Positions below are for LTR; the kit uses logical +properties (`start`/`end`), so RTL mirrors automatically. + +## Where actions live + +**Rule:** an action sits at the *end* (right) of the container it acts on. +It goes at the **top** when it acts on the whole page or collection and must +be reachable without scrolling; it goes at the **bottom** when it *completes* +something the user fills or reads top-to-bottom. + +| Surface | Actions | Position | Order (start → end) | +| --- | --- | --- | --- | +| List page (collection) | `New ` (the page's only primary), `Import`, `Export` | top-right, in `PageHeader` actions | secondary/outlined … primary last | +| Full-page record editor | `Back to list`, `Preview`, `Save` / `Publish` | top-right, in the page header — kept visible (sticky) while the form scrolls | borderless back … primary last | +| Dialog, drawer (`Dialog placement="right"`) | `Cancel`, `Create ` / `Save changes` | bottom-right footer (`actions` prop, already `justify-end`) | `[Cancel] [Primary]` | +| Confirmation (`role="alertdialog"`) | `Cancel`, `Delete ` | bottom-right footer | `[Cancel] [Destructive]` | +| Settings section / inline form card | `Save changes` for that section | bottom-right of the section, after its last field | `[Reset]` … `[Save changes]` | +| Wizard / checkout step | `Back`, `Continue` (last step: the real action) | bottom: `Back` start, primary end | `[Back] ……… [Continue]` | +| Popover / filter panel | `Reset`, `Apply` | bottom-right of the panel | `[Reset] [Apply]` | +| Table rows | `Open`/`Edit`, `Duplicate`, `Delete` | end of the row (DataTable actions column), overflow in a Menu | destructive last, `tone: "danger"` | +| Bulk selection | actions for the selected rows | table toolbar, top, only while a selection exists | destructive last | +| Card (product, record) | one primary action | bottom of the card | — | +| Danger zone (delete record/workspace) | `Delete ` | its own section at the **bottom of the page**, never beside Save | — | + +Hard rules: + +- **One primary button per surface.** Everything else is `secondary`, + `outlined` or `borderless`. +- **Primary is last** (right-most) in a button group; `Cancel` sits before it. + Don't reorder by platform. +- **Destructive replaces the primary** in a confirmation; never show `Save` + and `Delete` as neighbours. +- **Don't split one form's actions** between the header and the footer. A + short form (dialog, drawer, settings section) → bottom; a long full-page + editor → sticky header. +- On small screens footers stay at the bottom (full-width buttons are fine); + the page header's actions collapse into a Menu before they wrap. + +## Choosing the feedback surface + +| What happened | Surface | Why | +| --- | --- | --- | +| A field is invalid | Inline on the field (`validateStatus`, `helpText`) | the fix happens there | +| Submit failed, 2+ errors | `FormErrorSummary` above the form + inline | one place to start, links to each field | +| Mutation succeeded and the change is visible in place | nothing, or a toast | don't congratulate every keystroke | +| Mutation succeeded, change not obvious (drawer closed, row moved, background save) | Toast `success` | the list alone doesn't prove the write | +| Reversible destructive action done (archive, remove) | Toast `success` with `Undo` | faster than a confirmation | +| Irreversible or expensive action requested | `Dialog role="alertdialog"` before acting | the user must decide | +| Background/async job result | Toast (`loading` → `success`/`error`) | the user may be elsewhere | +| Recoverable failure of an async action | Toast `error` with `Try again`, stays until dismissed | needs action, must not vanish | +| Persistent condition of this page (read-only mode, sync paused, trial ends) | inline StatusText/section at the top of the content | must stay visible while relevant | +| Whole page unavailable (404, 403, 500, offline) | system state screen inside the app shell | see Pages/System/States | +| Contextual hint about a control | Tooltip (icon buttons) or help text (fields) | on demand, next to it | + +Never use a toast for: validation errors, information the user must act on +later, content they need to copy, or the only copy of an error message. + +## Toasts + +- **One `` per app**, mounted in the root layout. The kit's store + places toasts **bottom-end (bottom-right)** with a 16 px gap and 24 px + offset. Don't create other placements per page. +- **Why bottom-right:** page commands and the user menu live top-right — + a top toast would cover the next action; form and dialog actions end + bottom-right, so the confirmation appears where the user's attention already + is, without covering content being read top-left. +- Mind sticky bottom bars (cookie bars, mobile checkout bars): offset the + toaster so toasts never cover a primary action. +- **Duration:** `success`/`info` 5 s (the kit default in stories); `warning` + 8 s; `error` with an action and `loading` stay until resolved or dismissed. + Never auto-dismiss something the user must read or act on. +- **Content:** title = ` ` (see ux-writing); + optional one-sentence description; at most one action (`Undo`, `View`, + `Try again`). +- **Rate:** one toast per user action. Bulk operations produce one summary + toast (`12 products archived`), not twelve. +- Toasts are announced politely and never steal focus; don't put the only way + to reach something inside a toast. + +## Destructive actions + +| Action | Pattern | +| --- | --- | +| Reversible (archive, remove from a collection, soft delete) | act immediately → toast with `Undo` | +| Irreversible, one record | `alertdialog` naming the record and consequences → `[Cancel] [Delete ]` | +| Irreversible, high impact (workspace, all data, billing) | `alertdialog` + type the name to confirm; lives in a danger-zone section | +| Bulk irreversible | `alertdialog` with the count: `Delete 12 products?` | + +- Destructive buttons use `variant="danger"`; row actions use `tone: "danger"` + and sit last. +- The confirmation's description says what will be lost and what will not + (`Orders that include it are kept.`). +- After deletion, return the user to a stable place (the list, with focus on + the neighbour row) and confirm with a toast. + +## Create / read / update / delete surfaces + +| Operation | Default surface | Use a full page instead when | +| --- | --- | --- | +| Create | right drawer (`Dialog placement="right"`) over the list | the record is long, has media or several sections | +| Read | row click → right drawer with details | the record has its own sub-lists/tabs (order detail, customer) | +| Update (1–2 fields) | DataTable inline edit | never — inline edit is the light path | +| Update (full) | the same drawer as create | same as create | +| Delete | row action → `alertdialog` (or `Undo` toast if reversible) | — | + +Create and update share **one form component** — a record has one shape. +Opening a record must not lose the list's scroll position, search or filters. diff --git a/libs/ui/skills/ux-guidelines/references/formatting.md b/libs/ui/skills/ux-guidelines/references/formatting.md new file mode 100644 index 0000000000..1150a5edc4 --- /dev/null +++ b/libs/ui/skills/ux-guidelines/references/formatting.md @@ -0,0 +1,108 @@ +# Dates, numbers and alignment + +Format every value through `Intl` with **one app locale** (from the i18n layer +or the request), never by string concatenation. Create formatters once per +locale (module scope or `useMemo`) and reuse them. + +```ts +// apps//src/lib/format.ts — one place per app +export const formatters = (locale: string, currency = "EUR") => ({ + money: new Intl.NumberFormat(locale, { style: "currency", currency }), + count: new Intl.NumberFormat(locale), + percent: new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 1 }), + compact: new Intl.NumberFormat(locale, { notation: "compact", maximumFractionDigits: 1 }), + date: new Intl.DateTimeFormat(locale, { dateStyle: "medium" }), + dateTime: new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }), + time: new Intl.DateTimeFormat(locale, { timeStyle: "short" }), + relative: new Intl.RelativeTimeFormat(locale, { numeric: "auto" }), +}) +``` + +## Locale defaults in the kit + +Kit components format with their own defaults when no locale is passed, and the +defaults differ: `NumericInput` defaults to `cs-CZ`, `DatePicker` to `en-US`. +**Always pass the app locale explicitly** (`locale={locale}`) to every +locale-aware component (`NumericInput`, `FormNumericInput`, `DatePicker`, +`PhoneInput`, `Chart` value formatters) so one screen never mixes `1 234,50` +with `1,234.50`. + +## Dates and times + +| Context | Format | en-GB | cs-CZ | +| --- | --- | --- | --- | +| Tables, lists, detail values | `dateStyle: "medium"` | `4 Sept 2026` | `4. 9. 2026` | +| With time (audit, orders) | `dateStyle: "medium", timeStyle: "short"` | `4 Sept 2026, 14:05` | `4. 9. 2026 14:05` | +| Headlines, emails, invoices | `dateStyle: "long"` | `4 September 2026` | `4. září 2026` | +| Activity feeds, comments, "last seen" (< 7 days) | `RelativeTimeFormat` | `2 hours ago`, `yesterday` | `před 2 hodinami` | +| Ranges | `formatRange` | `4 – 12 Sept 2026` | `04.09.2026 – 12.09.2026` | + +- **Never render ISO strings** (`2026-09-04`, `2026-08-11 10:03`) or + `Date.toString()` to users. ISO is for data, URLs and `