@godxjp/ui 28.8.0 → 28.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/START-HERE.md +193 -0
- package/agent/anti-ai-tells.json +158 -0
- package/agent/components/Accordion.json +60 -0
- package/agent/components/AccountChip.json +59 -0
- package/agent/components/Actions.json +78 -0
- package/agent/components/Activity.json +80 -0
- package/agent/components/Affix.json +78 -0
- package/agent/components/Alert.json +65 -0
- package/agent/components/AlertDialog.json +109 -0
- package/agent/components/AlertDialogRoot.json +52 -0
- package/agent/components/Anchor.json +119 -0
- package/agent/components/AppLauncher.json +95 -0
- package/agent/components/AppProvider.json +105 -0
- package/agent/components/AppSettingPicker.json +88 -0
- package/agent/components/AppSettingToggle.json +70 -0
- package/agent/components/AppShell.json +159 -0
- package/agent/components/AreaChart.json +105 -0
- package/agent/components/AspectRatio.json +38 -0
- package/agent/components/Attachments.json +76 -0
- package/agent/components/AuthAccountSummary.json +64 -0
- package/agent/components/AuthDivider.json +35 -0
- package/agent/components/AuthFooter.json +48 -0
- package/agent/components/AuthIdentity.json +42 -0
- package/agent/components/AuthShell.json +108 -0
- package/agent/components/AuthStack.json +21 -0
- package/agent/components/Avatar.json +89 -0
- package/agent/components/Badge.json +96 -0
- package/agent/components/Banner.json +48 -0
- package/agent/components/BarChart.json +108 -0
- package/agent/components/BranchScopePicker.json +89 -0
- package/agent/components/Breadcrumb.json +54 -0
- package/agent/components/Button.json +133 -0
- package/agent/components/Calendar.json +259 -0
- package/agent/components/Callout.json +46 -0
- package/agent/components/Card.json +112 -0
- package/agent/components/CardBar.json +49 -0
- package/agent/components/CardContent.json +51 -0
- package/agent/components/Carousel.json +50 -0
- package/agent/components/Cascader.json +209 -0
- package/agent/components/CenteredShell.json +66 -0
- package/agent/components/ChatBubble.json +100 -0
- package/agent/components/ChatBubbleList.json +64 -0
- package/agent/components/ChatComposer.json +160 -0
- package/agent/components/ChatSuggestion.json +86 -0
- package/agent/components/Checkbox.json +68 -0
- package/agent/components/CheckboxGroup.json +96 -0
- package/agent/components/CodeBlock.json +64 -0
- package/agent/components/Collapsible.json +74 -0
- package/agent/components/ColorPicker.json +87 -0
- package/agent/components/Command.json +168 -0
- package/agent/components/CommandPalette.json +84 -0
- package/agent/components/CompactBarTrend.json +101 -0
- package/agent/components/Conversations.json +82 -0
- package/agent/components/CredentialReveal.json +93 -0
- package/agent/components/DataState.json +79 -0
- package/agent/components/DataTable.json +268 -0
- package/agent/components/DatePicker.json +275 -0
- package/agent/components/Descriptions.json +67 -0
- package/agent/components/Dialog.json +78 -0
- package/agent/components/DraggablePanel.json +106 -0
- package/agent/components/DropdownMenu.json +102 -0
- package/agent/components/EmptyState.json +83 -0
- package/agent/components/ErrorSurface.json +128 -0
- package/agent/components/FeatureList.json +43 -0
- package/agent/components/Field.json +64 -0
- package/agent/components/FilterBar.json +99 -0
- package/agent/components/Flex.json +153 -0
- package/agent/components/FloatButton.json +91 -0
- package/agent/components/Form.json +87 -0
- package/agent/components/FormErrors.json +51 -0
- package/agent/components/FormField.json +137 -0
- package/agent/components/FormFieldArray.json +39 -0
- package/agent/components/FormFieldControl.json +129 -0
- package/agent/components/FormRoot.json +122 -0
- package/agent/components/Heading.json +61 -0
- package/agent/components/HoverCard.json +55 -0
- package/agent/components/Icon.json +60 -0
- package/agent/components/InfiniteQueryState.json +58 -0
- package/agent/components/Input.json +122 -0
- package/agent/components/InputOTP.json +106 -0
- package/agent/components/Label.json +43 -0
- package/agent/components/LegalDocumentShell.json +102 -0
- package/agent/components/Legend.json +42 -0
- package/agent/components/LineChart.json +103 -0
- package/agent/components/Link.json +41 -0
- package/agent/components/ListRow.json +92 -0
- package/agent/components/Logo.json +85 -0
- package/agent/components/Marquee.json +91 -0
- package/agent/components/Masonry.json +82 -0
- package/agent/components/MasterDetail.json +95 -0
- package/agent/components/MegaMenu.json +120 -0
- package/agent/components/MobileShell.json +73 -0
- package/agent/components/NavList.json +63 -0
- package/agent/components/NumberInput.json +158 -0
- package/agent/components/OrgSwitcher.json +89 -0
- package/agent/components/OverlayPortalProvider.json +42 -0
- package/agent/components/PageContainer.json +181 -0
- package/agent/components/Pagination.json +132 -0
- package/agent/components/Paragraph.json +40 -0
- package/agent/components/PasswordInput.json +79 -0
- package/agent/components/PasswordStrength.json +51 -0
- package/agent/components/PermissionMatrix.json +81 -0
- package/agent/components/PieChart.json +99 -0
- package/agent/components/Popover.json +110 -0
- package/agent/components/PrefetchLink.json +65 -0
- package/agent/components/Progress.json +79 -0
- package/agent/components/Prose.json +57 -0
- package/agent/components/QrCode.json +62 -0
- package/agent/components/Radio.json +98 -0
- package/agent/components/RadioGroup.json +91 -0
- package/agent/components/RangeTimeline.json +80 -0
- package/agent/components/Rating.json +92 -0
- package/agent/components/ResizablePanel.json +69 -0
- package/agent/components/ResponsiveGrid.json +77 -0
- package/agent/components/Reveal.json +70 -0
- package/agent/components/ScrollArea.json +104 -0
- package/agent/components/SearchInput.json +98 -0
- package/agent/components/Segmented.json +96 -0
- package/agent/components/Select.json +397 -0
- package/agent/components/Separator.json +86 -0
- package/agent/components/ServiceCatalogCta.json +46 -0
- package/agent/components/ServiceLauncherCard.json +86 -0
- package/agent/components/ServiceRolePanel.json +83 -0
- package/agent/components/Sheet.json +85 -0
- package/agent/components/Sidebar.json +118 -0
- package/agent/components/Skeleton.json +57 -0
- package/agent/components/SkeletonArticle.json +71 -0
- package/agent/components/SkeletonAvatar.json +50 -0
- package/agent/components/SkeletonButton.json +57 -0
- package/agent/components/SkeletonForm.json +52 -0
- package/agent/components/SkeletonImage.json +37 -0
- package/agent/components/SkeletonInput.json +51 -0
- package/agent/components/SkeletonNode.json +42 -0
- package/agent/components/SkeletonRows.json +49 -0
- package/agent/components/SkeletonTable.json +45 -0
- package/agent/components/Slider.json +160 -0
- package/agent/components/SplitPane.json +66 -0
- package/agent/components/StatCard.json +83 -0
- package/agent/components/Steps.json +95 -0
- package/agent/components/Swatch.json +41 -0
- package/agent/components/Switch.json +81 -0
- package/agent/components/Table.json +112 -0
- package/agent/components/Tabs.json +158 -0
- package/agent/components/TagInput.json +105 -0
- package/agent/components/Text.json +201 -0
- package/agent/components/Textarea.json +126 -0
- package/agent/components/ThoughtChain.json +76 -0
- package/agent/components/Thumbnail.json +70 -0
- package/agent/components/TimePicker.json +200 -0
- package/agent/components/TimeRangePicker.json +90 -0
- package/agent/components/Timeline.json +47 -0
- package/agent/components/TimelineGrid.json +92 -0
- package/agent/components/Title.json +67 -0
- package/agent/components/Toaster.json +42 -0
- package/agent/components/Toggle.json +90 -0
- package/agent/components/ToggleGroup.json +102 -0
- package/agent/components/Toolbar.json +120 -0
- package/agent/components/Tooltip.json +110 -0
- package/agent/components/Topbar.json +83 -0
- package/agent/components/TopbarItem.json +79 -0
- package/agent/components/Transfer.json +141 -0
- package/agent/components/Tree.json +185 -0
- package/agent/components/TreeSelect.json +232 -0
- package/agent/components/TwoFactorSetup.json +79 -0
- package/agent/components/Typography.json +42 -0
- package/agent/components/Upload.json +221 -0
- package/agent/components/UploadCropDialog.json +60 -0
- package/agent/components/VisuallyHidden.json +20 -0
- package/agent/components/Welcome.json +65 -0
- package/agent/components/formatDate.json +46 -0
- package/agent/components/inertiaUpload.json +32 -0
- package/agent/components/useZodForm.json +39 -0
- package/agent/components-index.json +884 -0
- package/agent/components.json +15507 -0
- package/agent/index.json +56 -0
- package/agent/llms.txt +32 -0
- package/agent/patterns/account-recovery-settings.json +19 -0
- package/agent/patterns/async-data-state.json +20 -0
- package/agent/patterns/auth-recovery-panels.json +29 -0
- package/agent/patterns/badge-coloring.json +14 -0
- package/agent/patterns/common-fixes.json +16 -0
- package/agent/patterns/confirm-destructive.json +11 -0
- package/agent/patterns/data-table-page.json +18 -0
- package/agent/patterns/deferred-loading.json +12 -0
- package/agent/patterns/error-pages.json +28 -0
- package/agent/patterns/inertia-detail-page.json +13 -0
- package/agent/patterns/inertia-list-page.json +15 -0
- package/agent/patterns/inertia-persistent-layout.json +14 -0
- package/agent/patterns/organization-memberships.json +19 -0
- package/agent/patterns/page-sections.json +18 -0
- package/agent/patterns/settings-page-responsive.json +18 -0
- package/agent/patterns/settings-section-rows.json +23 -0
- package/agent/patterns/signup-form.json +13 -0
- package/agent/patterns/topbar-account-chip.json +18 -0
- package/agent/patterns/transactional-email.json +22 -0
- package/agent/patterns-index.json +323 -0
- package/agent/patterns.json +342 -0
- package/agent/rules.json +237 -0
- package/agent/tokens.json +8422 -0
- package/agent/vocabulary.json +198 -0
- package/dist/components/data-entry/input.js +8 -1
- package/dist/components/layout/flex.d.ts +2 -2
- package/dist/components/layout/flex.js +2 -0
- package/dist/components/ui/tag-input.d.ts +10 -0
- package/dist/components/ui/tag-input.js +35 -2
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +23 -1
- package/dist/i18n/messages/ja.json +21 -1
- package/dist/i18n/messages/vi.json +21 -1
- package/dist/lib/variants.js +4 -1
- package/dist/props/components/data-entry.prop.d.ts +21 -2
- package/dist/props/components/layout.prop.d.ts +42 -0
- package/dist/props/registry.d.ts +9 -0
- package/dist/props/registry.js +6 -0
- package/dist/props/vocabulary/layout.prop.d.ts +1 -1
- package/dist/styles/base.css +47 -14
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/chart-layout.css +6 -6
- package/dist/styles/control.css +41 -6
- package/dist/styles/data-display-layout.css +21 -6
- package/dist/styles/density.css +2 -0
- package/dist/styles/dialog-layout.css +4 -1
- package/dist/styles/focus-ring.css +4 -1
- package/dist/styles/layout.css +30 -3
- package/dist/styles/navigation-layout.css +3 -1
- package/dist/styles/shell-layout.css +27 -21
- package/dist/styles/table-layout.css +50 -9
- package/dist/styles/text-layout.css +94 -23
- package/dist/tokens/components/activity.css +13 -4
- package/dist/tokens/components/attachments.css +1 -1
- package/dist/tokens/components/badge.css +1 -1
- package/dist/tokens/components/card.css +28 -7
- package/dist/tokens/components/chart.css +4 -1
- package/dist/tokens/components/chat-composer.css +4 -1
- package/dist/tokens/components/control.css +69 -30
- package/dist/tokens/components/conversations.css +4 -1
- package/dist/tokens/components/data-display.css +42 -15
- package/dist/tokens/components/data-entry.css +8 -2
- package/dist/tokens/components/descriptions.css +1 -1
- package/dist/tokens/components/feedback.css +8 -5
- package/dist/tokens/components/float-button.css +8 -2
- package/dist/tokens/components/legal-document.css +12 -3
- package/dist/tokens/components/logo.css +15 -6
- package/dist/tokens/components/mega-menu.css +14 -5
- package/dist/tokens/components/navigation.css +37 -13
- package/dist/tokens/components/segmented.css +9 -2
- package/dist/tokens/components/separator.css +4 -1
- package/dist/tokens/components/shell.css +96 -31
- package/dist/tokens/components/table.css +13 -6
- package/dist/tokens/components/thought-chain.css +4 -1
- package/dist/tokens/components/toggle.css +4 -1
- package/dist/tokens/components/tree.css +1 -1
- package/dist/tokens/components/upload.css +21 -9
- package/dist/tokens/foundation.css +24 -30
- package/dist/tokens/semantic/layout.css +19 -5
- package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
- package/docs/DESIGN-AUTHORITY.md +14 -0
- package/docs/DEVELOPMENT.md +81 -6
- package/docs/TOKENS.md +16 -1
- package/docs/data-entry/tag-input.tsx +37 -0
- package/docs/layout/flex.tsx +40 -0
- package/docs/roadmap/website-components.md +34 -0
- package/docs/showcase/case4-login.tsx +10 -2
- package/docs/showcase/case5-shift-calendar.tsx +1 -1
- package/docs/showcase/case6-agency-handy.tsx +6 -6
- package/docs/showcase/futurelastic-web.tsx +7 -9
- package/docs/showcase/marketing-page.tsx +61 -52
- package/docs/showcase/table-expandable-rows.tsx +4 -1
- package/docs/showcase/table-pagination.tsx +88 -18
- package/docs/showcase/theme-customization.tsx +25 -2
- package/package.json +8 -5
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# @godxjp/ui — for AI agents
|
|
2
|
+
|
|
3
|
+
You are about to write code against a design system you did not author. This file is the whole
|
|
4
|
+
contract. Read it before you write JSX.
|
|
5
|
+
|
|
6
|
+
**This catalog describes `@godxjp/ui` 28.10.0.** If the project you are editing has a different
|
|
7
|
+
version in its `package.json`, read the pinned catalog for THAT version instead
|
|
8
|
+
(`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
|
|
9
|
+
not exist yet; older, and it hides props that do. Neither failure announces itself.
|
|
10
|
+
|
|
11
|
+
Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Which lane are you in
|
|
16
|
+
|
|
17
|
+
## Where to read these files
|
|
18
|
+
|
|
19
|
+
Three places carry this catalog, and they are not equivalent:
|
|
20
|
+
|
|
21
|
+
| source | URL / path | use it when |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| docs site | `https://godx-jp.github.io/godxjp-ui/agent/…` | you are an assistant in a browser and can only fetch a URL |
|
|
24
|
+
| npm package | `node_modules/@godxjp/ui/agent/…` | the project is on disk — this copy is **version-locked to the installed package by construction** |
|
|
25
|
+
| raw GitHub | `https://raw.githubusercontent.com/godx-jp/godxjp-ui/v<version>/agent/…` | you need a version the other two cannot give you |
|
|
26
|
+
|
|
27
|
+
Prefer the package copy when a project is in front of you: it cannot drift from what is installed,
|
|
28
|
+
which is exactly the failure the version note above describes. The docs-site copy always describes
|
|
29
|
+
the LATEST release, so pair it with the raw `v<version>` URL if the project is pinned to an older
|
|
30
|
+
one.
|
|
31
|
+
|
|
32
|
+
**You can run a process** (Claude Code · Codex CLI · Cursor · any client with MCP)
|
|
33
|
+
→ Do not use these files. Run the MCP server; it is searchable, version-locked to the package on
|
|
34
|
+
disk, and costs far fewer tokens than fetching a 1 MB JSON.
|
|
35
|
+
|
|
36
|
+
```jsonc
|
|
37
|
+
// .mcp.json — or let `npx @godxjp/ui sync-rules` write it for you
|
|
38
|
+
{ "mcpServers": { "godx-ui": { "command": "npx", "args": ["@godxjp/ui-mcp@<installed version>"] } } }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then ask it: `search_components`, `get_component`, `get_tokens`, `get_rule`, `list_anti_ai_tells`.
|
|
42
|
+
|
|
43
|
+
**You cannot run a process** (ChatGPT web · Claude.ai · anything fetching URLs)
|
|
44
|
+
→ These files are for you. Fetch in this order:
|
|
45
|
+
|
|
46
|
+
0. `patterns-index.json` — 19 whole-task patterns as name + tagline + tags. **If your
|
|
47
|
+
task is a task** — "build a settings page", "confirm a destructive delete", "a list page with
|
|
48
|
+
filters" — start HERE, not at the components. Then fetch `patterns/<name>.json` for complete,
|
|
49
|
+
copy-paste-ready code. A component index answers "does X exist"; it cannot answer "build Y".
|
|
50
|
+
1. `components-index.json` — 45 KB, all 170 components as name + group +
|
|
51
|
+
tagline. Read this when you already know the SHAPE you need. Each entry may carry `absorbed`:
|
|
52
|
+
names that **do not exist** and map to it — `Combobox`, `Autocomplete`, `CountrySelect` and
|
|
53
|
+
`SearchSelect` are all `Select`. If you are about to hand-roll something, search this field
|
|
54
|
+
first; it exists because that is the mistake.
|
|
55
|
+
2. `components/<Name>.json` — one file per component (1 KB–33 KB, median 6 KB), carrying its props,
|
|
56
|
+
its `importPath`, and its examples. Fetch only the handful you picked in step 1.
|
|
57
|
+
3. `rules.json` — 47 cardinal rules. The ones about raw HTML and hardcoded colour are not
|
|
58
|
+
style advice.
|
|
59
|
+
4. `tokens.json` — 1684 design tokens. Only when you need a specific knob's name.
|
|
60
|
+
5. `anti-ai-tells.json` — 26 shapes that make generated UI look generated, each with the
|
|
61
|
+
fix. Read before you reach for a gradient hero or a wall of coloured chips.
|
|
62
|
+
|
|
63
|
+
**Do not fetch `components.json`.** It is 1.1 MB, and most URL fetchers truncate a
|
|
64
|
+
response that size and return the head without telling you. You get the first few entries, believe
|
|
65
|
+
you read the catalog, and answer the rest from memory — which is the failure this file exists to
|
|
66
|
+
prevent. The per-component files say the same thing without the cliff.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## The four rules that decide whether your output is usable
|
|
71
|
+
|
|
72
|
+
Everything else is detail. These four are why generated code gets rejected here.
|
|
73
|
+
|
|
74
|
+
### 1. Never a raw HTML control
|
|
75
|
+
|
|
76
|
+
No `<button>`, `<input>`, `<select>`, `<textarea>`, hand-rolled `<table>`. Use `Button`, `Input`,
|
|
77
|
+
`Select`, `Textarea`, `DataTable`. A raw control has no focus ring, no size ladder, no RTL, and no
|
|
78
|
+
dark mode — it *looks* fine in a screenshot and fails every gate this package ships.
|
|
79
|
+
|
|
80
|
+
### 2. Never a colour, size or spacing literal
|
|
81
|
+
|
|
82
|
+
Not `#6400D4`, not `color: rgb(...)`, not `p-[13px]`, not `height: 36px`. Reference the **role**:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<Alert tone="warning"> {/* not: style={{ borderColor: "#B45309" }} */}
|
|
86
|
+
<Button size="sm"> {/* not: className="h-8" */}
|
|
87
|
+
<CardContent> {/* not: <Card className="p-4"> */}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
In CSS, reference the token by NAME so it re-resolves per theme and per tenant:
|
|
91
|
+
|
|
92
|
+
```css
|
|
93
|
+
color: var(--code-block-token-keyword-color, hsl(var(--primary))); /* yes */
|
|
94
|
+
color: #6400D4; /* no */
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. Compose primitives fully
|
|
98
|
+
|
|
99
|
+
Padding comes from `CardContent`, not from `p-4` on a bare `Card`. An empty table state comes from
|
|
100
|
+
`DataTable`'s own empty, not from a `<div>` you wrote. If you find yourself styling a `<div>` to
|
|
101
|
+
look like a component, the component exists — search the index.
|
|
102
|
+
|
|
103
|
+
### 4. It is one control, not a family
|
|
104
|
+
|
|
105
|
+
There is no `Combobox`, `Autocomplete`, `CountrySelect`, `SearchSelect`. There is `Select`, with
|
|
106
|
+
`showSearch` and `loadOptions`. The i18n pickers are one `AppSettingPicker kind=…`. Components were
|
|
107
|
+
deleted for being duplicates; do not add another by hand-rolling.
|
|
108
|
+
|
|
109
|
+
This is not advice you have to remember — it is **data**. Every one of those names is in the
|
|
110
|
+
`absorbed` field of the component that replaced it, in `components-index.json`, and
|
|
111
|
+
`check:absorbed-names` fails the build if any of them ever becomes real. Search the name you were
|
|
112
|
+
about to invent.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Tokens: how to stay flexible
|
|
117
|
+
|
|
118
|
+
The common failure is treating tokens as a **closed menu of approved layouts**. They are not.
|
|
119
|
+
Tokens carry VALUES; composition, responsive behaviour and interaction are still yours.
|
|
120
|
+
|
|
121
|
+
What keeps a token-first page flexible is the **scoped override contract** — the same knob, settable
|
|
122
|
+
at three levels, each beating the one above:
|
|
123
|
+
|
|
124
|
+
| level | how | reaches |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| theme | `:root { --card-radius: 0; }` | every Card in the product |
|
|
127
|
+
| region / tenant | `[data-tenant="acme"] { --card-radius: 0; }` | one tenant, one section |
|
|
128
|
+
| instance | `<Card className="…">` or the documented prop | this one Card |
|
|
129
|
+
|
|
130
|
+
So: **build any layout you like out of primitives, and express every visual decision as a token or
|
|
131
|
+
a documented prop.** You keep full freedom of composition and lose none of the theming. The moment
|
|
132
|
+
you write a literal, that pixel stops following the theme and the tenant override silently skips it.
|
|
133
|
+
|
|
134
|
+
A knob you want to retheme globally is a **component token** (`--{component}-{part}-{property}`).
|
|
135
|
+
Most are declared `initial` with the real default at the call site — that is deliberate, so a
|
|
136
|
+
scoped override re-resolves instead of freezing at `:root`.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## A page that would pass review
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { PageContainer, Flex } from "@godxjp/ui/layout";
|
|
144
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent, DataTable } from "@godxjp/ui/data-display";
|
|
145
|
+
import { Button } from "@godxjp/ui/general";
|
|
146
|
+
import { Select } from "@godxjp/ui/data-entry";
|
|
147
|
+
import { Alert, AlertTitle, AlertDescription } from "@godxjp/ui/feedback";
|
|
148
|
+
|
|
149
|
+
export default function InvoicesPage() {
|
|
150
|
+
return (
|
|
151
|
+
<PageContainer title="請求一覧" subtitle="今月の未収を上から">
|
|
152
|
+
<Flex direction="col" gap="lg">
|
|
153
|
+
<Alert tone="warning">
|
|
154
|
+
<AlertTitle>お支払いが確認できていません</AlertTitle>
|
|
155
|
+
<AlertDescription>3 件の請求が期限を過ぎています。</AlertDescription>
|
|
156
|
+
</Alert>
|
|
157
|
+
|
|
158
|
+
<Card>
|
|
159
|
+
<CardHeader>
|
|
160
|
+
<CardTitle level={2}>未収</CardTitle>
|
|
161
|
+
<CardDescription>期限順。金額は税込。</CardDescription>
|
|
162
|
+
</CardHeader>
|
|
163
|
+
{/* `flush` because DataTable draws its own edges — a padded body double-insets it */}
|
|
164
|
+
<CardContent flush>
|
|
165
|
+
<DataTable data={rows} columns={columns} getRowId={(r) => r.id} striped />
|
|
166
|
+
</CardContent>
|
|
167
|
+
</Card>
|
|
168
|
+
|
|
169
|
+
<Flex direction="row" gap="sm" wrap>
|
|
170
|
+
<Select width="auto" options={statuses} defaultValue="unpaid" aria-label="状態" />
|
|
171
|
+
<Button variant="outline" size="sm">CSV</Button>
|
|
172
|
+
</Flex>
|
|
173
|
+
</Flex>
|
|
174
|
+
</PageContainer>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Note what is absent: no colour, no pixel, no raw control, no `<div className="flex gap-4">` where
|
|
180
|
+
`Flex` exists.
|
|
181
|
+
|
|
182
|
+
**Take the import path from the catalog, not from the group.** Every entry in
|
|
183
|
+
`components/<Name>.json` carries an `importPath`; it is the only one guaranteed to resolve. The
|
|
184
|
+
group is a docs heading and does not always match a subpath — `AppProvider` is in group
|
|
185
|
+
`providers`, and `@godxjp/ui/providers` does not exist (it is `@godxjp/ui/app`).
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Before you answer
|
|
190
|
+
|
|
191
|
+
Say which catalog version you read and which files you actually fetched. If you could not fetch
|
|
192
|
+
one, say so rather than filling the gap from memory — a prop invented from another library is the
|
|
193
|
+
single most common failure here, and it looks exactly like a correct answer.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"body": "The default LLM color palette — purple → blue → cyan radial /\nlinear gradient as hero background. Looks like every AI-generated\nSaaS landing page from 2023.",
|
|
4
|
+
"category": "visual",
|
|
5
|
+
"fix": "Use the framework's accent palette (`data-accent=\"blue\"` /\n\"violet\" / \"cyan\" / \"green\" / \"orange\" / \"rose\"). Solid surface\ncolors with semantic meaning. If you want depth, use a SINGLE\nsubtle gradient that supports brand (not decoration).",
|
|
6
|
+
"name": "Purple-blue gradient hero"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"body": "Frosted-glass cards stacked on a colorful blurry background.\nLooked novel in 2020 — now a tell that the designer reached for\ntrend instead of solving a problem.",
|
|
10
|
+
"category": "visual",
|
|
11
|
+
"fix": "Solid surface tiers (Card on background, Popover on Card,\nDialog on backdrop). The framework's elevation system already\nencodes 3 surface tiers — use them.",
|
|
12
|
+
"name": "Glassmorphism without purpose"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"body": "Random gradient blobs floating behind content with no narrative\npurpose. The \"creative space-filler\" AI pattern. Reads as\ndistracting noise.",
|
|
16
|
+
"category": "visual",
|
|
17
|
+
"fix": "If the page needs visual interest, use a REAL image (product\nphoto, founder photo, branded illustration). If you need\n\"breathing room\", use whitespace. Never use shapes as filler.",
|
|
18
|
+
"name": "Ambient blobs / floating shapes"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"body": "Every Card / Button / Input with `border-radius: 24px`. Reads\nas \"I picked one radius and applied it globally\". Premium design\nuses ROLES — small radius on inputs (4-6px), medium on cards\n(8-12px), pill on chips (full).",
|
|
22
|
+
"category": "visual",
|
|
23
|
+
"fix": "Use the framework's radius scale (`--radius-sm | -md | -lg | -full`).\nEach primitive defaults to the right role; only override when the\ndesign canon specifically calls for it.",
|
|
24
|
+
"name": "Oversized border-radius on everything"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"body": "Row of Tags / Badges each in a different color (red, orange,\nyellow, green, blue, purple) — usually navigation or filter\ncategories. Reads as chaos; eye can't anchor.",
|
|
28
|
+
"category": "visual",
|
|
29
|
+
"fix": "Pick ONE accent for the tag row. Use `appearance` (\"soft\" vs\n\"solid\" vs \"outline\") for variety within the same hue. Reserve\nnon-neutral colors (success / warning / destructive) for tags\nthat genuinely carry that meaning.",
|
|
30
|
+
"name": "Rainbow chip wall"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"body": "Full-bleed, fully-saturated brand color on buttons, banners and\nnotification bars (the classic loud Slack/Linear/Notion blue) — the\naccent SCREAMS instead of signalling. A bright primary CTA bar across\nthe whole width, vivid send buttons, neon success. It reads as a\nverbatim copy of a SaaS chrome, not a restrained product surface.",
|
|
34
|
+
"category": "visual",
|
|
35
|
+
"fix": "渋み (restraint): keep --primary chroma ≤ 0.18 — desaturate so the\naccent BLENDS with the warm-neutral spine and is used sparingly for\nthe ONE primary action. Don't paint a full-width bar in raw blue;\nprefer a quiet Alert (icon + text + a normal-width action). Never\nhard-code a vivid hex — read `bg-primary`/`text-primary` tokens, and\nlet a service retheme via --primary only.",
|
|
36
|
+
"name": "Oversaturated brand accent"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"body": "Homepage with a 4x2 grid of \"stat cards\" — each with an icon, a\nnumber, a sparkline, a delta. None of them relate to a real\nbusiness question; they were chosen because \"more cards = more\ndata\". Classic AI dashboard slop.",
|
|
40
|
+
"category": "layout",
|
|
41
|
+
"fix": "Show 1-2 hero metrics (the ones executives ASK about), then the\ntop action list (orders waiting, tasks due, alerts). If the user\nneeds more analytics, link to a dedicated Reports page.",
|
|
42
|
+
"name": "8-card stat dashboard"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"body": "Mobile screen rendered as a vertical strip with the same density\n+ same layout as desktop — just narrower. Cramped tap targets,\nhorizontal scrolling for overflow, no system bar awareness.",
|
|
46
|
+
"category": "layout",
|
|
47
|
+
"fix": "Mobile is its OWN design. Use full-width inputs (`block` Button),\nstacked layout, larger tap targets, Sheet/Drawer for secondary\ncontent, system-bar safe area. The framework's `useBreakpoint`\n+ Tailwind `sm:` variants give you the canvas.",
|
|
48
|
+
"name": "Phone-shaped website"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"body": "10+ tabs at the top of a screen, no priority. User has to read all\nof them to find the right one. AI default: \"more tabs = more\nfeatures = better\".",
|
|
52
|
+
"category": "layout",
|
|
53
|
+
"fix": "2-4 tabs max. If you have more categories, use a sidebar (Sidebar),\nor a Cascader / Tree picker. Tabs are for switching between PEERS\n(2-4 mutually exclusive views of the same data).",
|
|
54
|
+
"name": "Wall-of-tabs navigation"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"body": "5 onboarding steps where every screen has the same headline +\nillustration + 2 buttons layout. Reads as \"I copy-pasted the\ntemplate\" — and devalues the user's time at each step.",
|
|
58
|
+
"category": "layout",
|
|
59
|
+
"fix": "Each step has a distinct visual + interactive feel. Step 1 might\nbe a centered question, step 2 a side-by-side comparison, step 3\na multi-field form, step 4 a single yes/no card. Same palette +\ntype system for coherence; different composition for engagement.",
|
|
60
|
+
"name": "Identical clone screens"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"body": "A notification / \"enable X\" bar where the pieces are stacked\nVERTICALLY and mis-placed: the icon floats centered ABOVE the text\n(often a SECOND redundant icon on the far left too), the primary\naction is a FULL-WIDTH colored bar UNDER the text, and the dismiss ×\nsits centered at the BOTTOM on its own line. It's a hand-rolled\nbanner that ignores the framework's Alert anatomy.",
|
|
64
|
+
"category": "layout",
|
|
65
|
+
"fix": "Use <Alert> and respect its fixed anatomy — ONE leading tone icon\nat the inline-start (top-aligned, auto by `tone`, never two), the\ntext body, an <Alert.Actions> with a NORMAL-WIDTH Button in the\ntrailing-right column, and `onDismiss` to render the × in the\nTOP-RIGHT corner. It is ONE horizontal row, never a vertical stack;\nnever hand-roll the ✕ or a full-bleed action bar.",
|
|
66
|
+
"name": "Stacked notification banner (misplaced alert controls)"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"body": "Emoji sprinkled through the product surface — ✅ / 🎉 / 🔥 in chat\nmessages, status lines, toasts, buttons, empty states or success\nbanners (\"All tests green 🎉\", \"done ✅\"). It reads as casual\nconsumer-app slop, breaks on Windows/Linux, and pollutes the\naccessible name. Celebrating with confetti/🎉 is the same tell.",
|
|
70
|
+
"category": "copy",
|
|
71
|
+
"fix": "No emoji anywhere in product UI. State the fact quietly in\ni18n-keyed copy (\"承認しました\" / \"All checks passed\"). Use a Lucide\nicon (1.5px) for an affordance and a semantic Badge `tone`\n(success/warning/destructive) for status — color + label carry the\nmeaning, not a glyph. Country names come from `Intl.DisplayNames`,\nnever emoji flags.",
|
|
72
|
+
"name": "Emoji in product UI"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"body": "\"Elevate your potential\", \"unlock seamless productivity\",\n\"transform your workflow\", \"next-generation experience\". Reads as\nnothing because it MEANS nothing.",
|
|
76
|
+
"category": "copy",
|
|
77
|
+
"fix": "Write what the feature DOES, specifically. \"Sync 1,000 rows in 2\nseconds\" beats \"Lightning-fast performance\". \"Replaces 3 manual\nsteps\" beats \"Streamline your workflow\".",
|
|
78
|
+
"name": "Filler corporate phrases"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"body": "Acme, NovaCore, Flowbit, Quantix, VeloPay, Lumen, Apex — the\ngo-to AI brand names that scream \"I couldn't think of one\".",
|
|
82
|
+
"category": "copy",
|
|
83
|
+
"fix": "Use believable real-sounding names: 株式会社ABC商事, Tanaka\nTrading, Yokohama Coffee Roasters, Mountain View Bakery. Or use\nyour actual project's brand if known.",
|
|
84
|
+
"name": "Generic brand placeholders"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"body": "\"Get started\", \"Begin your journey\", \"No items yet\" — without\nsaying WHAT to do or WHY there's nothing.",
|
|
88
|
+
"category": "copy",
|
|
89
|
+
"fix": "Be specific + actionable: \"まだ注文がありません。商品を追加して\n最初の注文を作成しましょう。\" + a clear next-action Button.\nEmpty states are TEACHING MOMENTS — use them to onboard.",
|
|
90
|
+
"name": "Vague empty-state copy"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"body": "\"Sorry, something went wrong\" / \"An error has occurred\" — no\ninformation about WHAT, no recovery action.",
|
|
94
|
+
"category": "copy",
|
|
95
|
+
"fix": "Specific + actionable: \"メールアドレスの形式が正しくありません\n(例: name@example.com)\". For server errors: \"通信エラー\n(再試行 ボタン)\". Never apologise if you can't say what failed\nor what to do.",
|
|
96
|
+
"name": "Apologetic / passive-voice error messages"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"body": "Action buttons that only appear on hover (table row actions\nhidden until mouseover). Breaks on mobile (no hover), inaccessible\n(keyboard users can't discover).",
|
|
100
|
+
"category": "interaction",
|
|
101
|
+
"fix": "Show actions inline or in a kebab menu (DropdownMenu) that's\nALWAYS visible. If you must hide on desktop for density, ensure\nthe same actions are reachable via keyboard (Tab to row, Enter to\nexpand a row-actions DropdownMenu).",
|
|
102
|
+
"name": "Hover-only affordances"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"body": "Hero carousel that rotates every 3 seconds. Users haven't\nfinished reading slide 1; now slide 2 is gone. Accessibility\nnightmare (cognitive load, motion-sensitive).",
|
|
106
|
+
"category": "interaction",
|
|
107
|
+
"fix": "Carousel ONLY rotates on explicit user action (arrow click,\ndot click, swipe). It has no autoplay prop and never rotates on its\nown — that is the default and there is nothing to turn off.",
|
|
108
|
+
"name": "Auto-advancing carousel"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"body": "Cards / list items reorderable by long-press anywhere — no\nvisual indicator that they ARE draggable. Users discover it by\naccident or never.",
|
|
112
|
+
"category": "interaction",
|
|
113
|
+
"fix": "Show a drag handle icon (`<GripVertical>`) on the left of the\nrow. Users see it, understand \"this row is draggable\", reach for\nit deliberately.",
|
|
114
|
+
"name": "Drag-without-handle"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"body": "`outline: none` on focus to \"look cleaner\". Keyboard users\ncan't see where they are; total navigation failure.",
|
|
118
|
+
"category": "interaction",
|
|
119
|
+
"fix": "Use `:focus-visible` (Radix primitives do automatically) so the\nring shows on keyboard focus, hides on mouse-click. Don't strip.",
|
|
120
|
+
"name": "Disappearing focus ring"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"body": "Empty state / About page with a photo of a \"diverse team in an\nopen office laughing at a laptop\". Reads as 2010 corporate stock.\nNo relationship to your product.",
|
|
124
|
+
"category": "imagery",
|
|
125
|
+
"fix": "Real photos of YOUR team / users (with consent), product\nscreenshots, branded illustrations. Avatar's INITIALS fallback is\nbetter than a generic stock person.",
|
|
126
|
+
"name": "Stock photo of generic smiling team"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"body": "Empty state with a chrome / pastel 3D icon (coin, key, shield)\nfloating in the center. Looks like every NFT marketplace from\n2021.",
|
|
130
|
+
"category": "imagery",
|
|
131
|
+
"fix": "Simple lucide-react line icon (`<Inbox size={48} />`) +\ndescriptive title. Or a flat illustration matching the brand\npalette. Skip the 3D entirely unless your brand IS 3D.",
|
|
132
|
+
"name": "Floating 3D crypto icon"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"body": "Page sections with a colorful gradient mesh (\"Stripe-style\")\nbehind text. Looks \"premium\" until you realize every AI design\nuses it. Often hurts text contrast.",
|
|
136
|
+
"category": "imagery",
|
|
137
|
+
"fix": "Solid background (`--background`). If you need depth, use a\nsubtle 1px border + `--card` background tint. Reserve high-effort\nbackgrounds for pages where they matter (marketing hero, product\nshowcase) — not every internal screen.",
|
|
138
|
+
"name": "Decorative gradient mesh background"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"body": "Settings page with 40 form fields in a single scroll. User\nloses their place, can't find the field they came for.",
|
|
142
|
+
"category": "structure",
|
|
143
|
+
"fix": "Section the form with `<Typography.Title size={5}>` subheaders\n+ `<Separator />`. Group by concern (基本情報 / 公開範囲 / 通知 /\nセキュリティ). If 40 fields is still too many, split into Tabs\nor a Sidebar-driven multi-page settings flow.",
|
|
144
|
+
"name": "Settings as one long form"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"body": "Click a Button → Dialog opens → click \"Edit\" → another Dialog\nopens → click \"Confirm\" → AlertDialog opens. Triple stack;\nuser loses context.",
|
|
148
|
+
"category": "structure",
|
|
149
|
+
"fix": "Use Sheet for the FIRST level (side panel), Dialog for the\nconfirm. Or, redesign the flow so the edit is INLINE in the\nfirst Dialog (no second Dialog needed). AlertDialog for confirm\nis correct — but ONE deep, not three.",
|
|
150
|
+
"name": "Modal-in-modal-in-modal"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"body": "Page-level spinner while data loads. User stares at an empty\nshell with a centered spinner. Layout shifts when content\narrives.",
|
|
154
|
+
"category": "structure",
|
|
155
|
+
"fix": "Use Skeleton placeholders matching the eventual content shape.\nRender `<Skeleton className=\"h-9 w-full\" />` in place of each control\n— inside the FormField that will hold it, so the labels and grid stay\nput. Height and fill are this screen's measurements; never add a\n`rounded-*`, because Skeleton already carries the radius token. Layout stays stable, perceived speed improves.",
|
|
156
|
+
"name": "Spinner-only loading state"
|
|
157
|
+
}
|
|
158
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from \"@godxjp/ui/data-display\";\n\n<Accordion type=\"single\" collapsible>\n <AccordionItem value=\"ship\">\n <AccordionTrigger>配送について</AccordionTrigger>\n <AccordionContent>3〜5営業日でお届けします。</AccordionContent>\n </AccordionItem>\n</Accordion>",
|
|
3
|
+
"group": "data-display",
|
|
4
|
+
"importPath": "@godxjp/ui/data-display",
|
|
5
|
+
"name": "Accordion",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "single = one open at a time; multiple = independent.",
|
|
9
|
+
"name": "type",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "\"single\" | \"multiple\""
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "When type=single, allow closing the open item.",
|
|
15
|
+
"name": "collapsible",
|
|
16
|
+
"type": "boolean"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "Controlled open item(s).",
|
|
20
|
+
"name": "value",
|
|
21
|
+
"type": "string | string[]"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "Uncontrolled initial open item(s).",
|
|
25
|
+
"name": "defaultValue",
|
|
26
|
+
"type": "string | string[]"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"description": "Open-state callback.",
|
|
30
|
+
"name": "onValueChange",
|
|
31
|
+
"type": "(value: string | string[]) => void"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"related": [
|
|
35
|
+
"Collapsible (single open/close region, no item set)",
|
|
36
|
+
"Tabs (mutually-exclusive views, always one visible)"
|
|
37
|
+
],
|
|
38
|
+
"rules": [
|
|
39
|
+
3,
|
|
40
|
+
6
|
|
41
|
+
],
|
|
42
|
+
"storyPath": "data-display/Accordion.stories.tsx",
|
|
43
|
+
"subParts": [
|
|
44
|
+
"AccordionContent",
|
|
45
|
+
"AccordionItem",
|
|
46
|
+
"AccordionTrigger"
|
|
47
|
+
],
|
|
48
|
+
"tagline": "Radix accordion — vertically stacked, collapsible sections. Compose Accordion > AccordionItem > AccordionTrigger + AccordionContent.",
|
|
49
|
+
"usage": [
|
|
50
|
+
"DO compose the full set: <Accordion type=\"single\" collapsible><AccordionItem value=\"a\"><AccordionTrigger/><AccordionContent/></AccordionItem></Accordion>.",
|
|
51
|
+
"DO give each AccordionItem a unique `value`.",
|
|
52
|
+
"DON'T use it for primary navigation — that's Sidebar/Tabs. Accordion is for collapsible content/FAQ."
|
|
53
|
+
],
|
|
54
|
+
"useCases": [
|
|
55
|
+
"FAQ lists",
|
|
56
|
+
"Grouped settings sections",
|
|
57
|
+
"Collapsible detail panels on a record page",
|
|
58
|
+
"Filter facet groups in a sidebar"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"example": "import { AccountChip } from \"@godxjp/ui/layout\";\n\n<PageContainer title=\"Bug report\" extra={<><Button variant=\"outline\">History</Button><AccountChip name={user.name} email={user.email} actionLabel={t(\"auth.signOut\")} onAction={signOut} /><Button>Send</Button></>}>…</PageContainer>",
|
|
3
|
+
"group": "layout",
|
|
4
|
+
"importPath": "@godxjp/ui/layout",
|
|
5
|
+
"name": "AccountChip",
|
|
6
|
+
"props": [
|
|
7
|
+
{
|
|
8
|
+
"description": "Display name; its first character is the avatar fallback.",
|
|
9
|
+
"name": "name",
|
|
10
|
+
"required": true,
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Shown as the chip's title (hover / assistive tooltip).",
|
|
15
|
+
"name": "email",
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"description": "Optional avatar image URL.",
|
|
20
|
+
"name": "avatarSrc",
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"description": "Overrides the first-character fallback.",
|
|
25
|
+
"name": "avatarFallback",
|
|
26
|
+
"type": "ReactNode"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"description": "Accessible name of the action button, e.g. a localized sign-out label.",
|
|
30
|
+
"name": "actionLabel",
|
|
31
|
+
"type": "ReactNode"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"description": "Action handler; omit it to render no button.",
|
|
35
|
+
"name": "onAction",
|
|
36
|
+
"type": "() => void"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"description": "Disables the action button.",
|
|
40
|
+
"name": "disabled",
|
|
41
|
+
"type": "boolean"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"description": "Optional structural class override.",
|
|
45
|
+
"name": "className",
|
|
46
|
+
"type": "string"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"rules": [
|
|
50
|
+
40
|
|
51
|
+
],
|
|
52
|
+
"storyPath": "layout/AccountChip.stories.tsx",
|
|
53
|
+
"tagline": "Signed-in user for a PageContainer extra slot, Topbar or footer row: avatar, name and one ghost action at the control tier height — never a hand-rolled rounded-full border div.",
|
|
54
|
+
"usage": [
|
|
55
|
+
"Put it in PageContainer `extra` beside Buttons and a Select width=\"auto\": every control in that row shares --control-height.",
|
|
56
|
+
"Pass a localized actionLabel with onAction; the component owns no route, session or permission behaviour.",
|
|
57
|
+
"DO compose from the package (it is Avatar + Text + Button in a Flex row); DON'T hand-roll <div className=\"inline-flex rounded-full border py-0.5\"> — it drifts from the control height and the audit flags it (no-hand-rolled-surface)."
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"docPath": "general/actions.tsx",
|
|
3
|
+
"example": "import { Actions, ActionsCopy, ActionsFeedback } from \"@godxjp/ui/general\";\nimport { RefreshCw, Share2 } from \"lucide-react\";\n\n<Actions\n label=\"回答の操作\"\n items={[\n { key: \"retry\", label: \"やり直す\", icon: <RefreshCw />, onItemClick: () => regenerate() },\n {\n key: \"more\",\n label: \"その他\",\n subItems: [{ key: \"share\", label: \"共有\", icon: <Share2 /> }],\n },\n { key: \"copy\", actionRender: <ActionsCopy text={answer} /> },\n { key: \"feedback\", actionRender: <ActionsFeedback value={vote} onChange={setVote} /> },\n ]}\n onClick={({ key }) => run(key)}\n/>",
|
|
4
|
+
"group": "general",
|
|
5
|
+
"importPath": "@godxjp/ui/general",
|
|
6
|
+
"name": "Actions",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"description": "The actions: { key, label?, icon?, onItemClick?, danger?, subItems?, actionRender? }. `label` is the accessible name AND the tooltip. `subItems` folds the action into a menu; `actionRender` replaces it entirely.",
|
|
10
|
+
"name": "items",
|
|
11
|
+
"required": true,
|
|
12
|
+
"type": "ActionsItemsProp[]"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"description": "Fires for any action WITHOUT its own onItemClick — a per-item handler wins and this does not also fire, exactly as in Ant Design X. A sub-item reports keyPath [subKey, parentKey].",
|
|
16
|
+
"name": "onClick",
|
|
17
|
+
"type": "(info: { item, key, keyPath, domEvent }) => void"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"defaultValue": "\"borderless\"",
|
|
21
|
+
"description": "Chrome of the STRIP, not the intent of the buttons (that is `danger` per item).",
|
|
22
|
+
"name": "variant",
|
|
23
|
+
"type": "\"borderless\" | \"filled\" | \"outlined\""
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"description": "The strip fades in on mount. Zeroed under prefers-reduced-motion.",
|
|
27
|
+
"name": "fadeIn",
|
|
28
|
+
"type": "boolean"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"description": "The same fade, arriving along the LOGICAL inline axis (so it mirrors under dir=rtl).",
|
|
32
|
+
"name": "fadeInLeft",
|
|
33
|
+
"type": "boolean"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"description": "Accessible name of the toolbar (a plain string). Localized default otherwise.",
|
|
37
|
+
"name": "label",
|
|
38
|
+
"type": "string"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"description": "DOM id of the strip.",
|
|
42
|
+
"name": "id",
|
|
43
|
+
"type": "string"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"related": [
|
|
47
|
+
"Toolbar / FilterBar — the list-page filter strip. Actions is the per-message action cluster, not a page-level control bar.",
|
|
48
|
+
"DropdownMenu — what `subItems` renders; compose it directly when the menu is not one action in a strip.",
|
|
49
|
+
"ChatBubble — the message the strip belongs to.",
|
|
50
|
+
"CredentialReveal — a copy affordance for a SECRET field; ActionsCopy copies message text."
|
|
51
|
+
],
|
|
52
|
+
"rules": [
|
|
53
|
+
2,
|
|
54
|
+
6,
|
|
55
|
+
23,
|
|
56
|
+
44,
|
|
57
|
+
45
|
|
58
|
+
],
|
|
59
|
+
"storyPath": "general/Actions.stories.tsx",
|
|
60
|
+
"subParts": [
|
|
61
|
+
"ActionsItem",
|
|
62
|
+
"ActionsCopy",
|
|
63
|
+
"ActionsFeedback"
|
|
64
|
+
],
|
|
65
|
+
"tagline": "The strip of actions under an assistant message (Ant Design X Actions): copy, retry, like, and a menu for the rest — a WAI-ARIA toolbar with ONE tab stop, where Ant X's own strip is <div onClick> with no role and no accessible name.",
|
|
66
|
+
"usage": [
|
|
67
|
+
"DO give every action a `label`. It becomes the accessible name and the tooltip; without one the key is used, which is better than nameless but worse than a sentence.",
|
|
68
|
+
"DO use `subItems` once the strip passes about five actions — it folds them behind one trigger instead of widening the row under every message.",
|
|
69
|
+
"DO reach for ActionsCopy and ActionsFeedback instead of hand-rolling copy and thumbs: ActionsCopy announces the copy through a live region (a tick alone is invisible to a screen reader), and ActionsFeedback keeps BOTH buttons on screen with aria-pressed rather than hiding the one you did not pick.",
|
|
70
|
+
"DON'T put a form control in the strip. It is a toolbar of buttons with one tab stop; a field inside would be unreachable by Tab.",
|
|
71
|
+
"DON'T expect `dropdownProps`, `triggerSubMenuAction`, `styles` or `classNames` from Ant Design X — they are not ported; the strip is retuned through the --actions-* tokens."
|
|
72
|
+
],
|
|
73
|
+
"useCases": [
|
|
74
|
+
"Under an assistant answer: copy, regenerate, like/dislike, and a menu with share and report.",
|
|
75
|
+
"Under a streaming answer: an ActionsItem with status=\"running\" while the audio plays back, error when it fails.",
|
|
76
|
+
"In a message hover strip inside ChatBubbleList."
|
|
77
|
+
]
|
|
78
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"absorbed": [
|
|
3
|
+
"Spin",
|
|
4
|
+
"Spinner",
|
|
5
|
+
"Loading",
|
|
6
|
+
"LoadingIndicator"
|
|
7
|
+
],
|
|
8
|
+
"example": "import { Activity } from \"@godxjp/ui/general\";\n\n// a channel typing indicator — no live region by default (it flickers with every socket event)\n<Activity label={t(\"channel.typing\", { name: \"佐藤\" })} />\n\n// an ambient sync mark in a header\n<Activity variant=\"bar\" tone=\"info\" label={t(\"sync.running\")} />\n\n// a reconnect notice that must actually be heard, announced once and politely\n<Activity announce=\"polite\" tone=\"warning\" label={t(\"realtime.reconnecting\")} />",
|
|
9
|
+
"group": "general",
|
|
10
|
+
"importPath": "@godxjp/ui/general",
|
|
11
|
+
"name": "Activity",
|
|
12
|
+
"props": [
|
|
13
|
+
{
|
|
14
|
+
"defaultValue": "\"dots\"",
|
|
15
|
+
"description": "The mark. `dots` = three dots rising in sequence, the ellipsis convention (someone is typing). `pulse` = a single breathing mark (live / recording). `bar` = an indeterminate sweep (syncing / streaming).",
|
|
16
|
+
"name": "variant",
|
|
17
|
+
"type": "\"dots\" | \"pulse\" | \"bar\""
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"defaultValue": "\"sm\"",
|
|
21
|
+
"description": "Size step. Default `sm` — an ambient mark is never the loudest thing on screen. Scales the mark AND the label together (the mark is em-based off `--activity-font-size-*`).",
|
|
22
|
+
"name": "size",
|
|
23
|
+
"type": "\"xs\" | \"sm\" | \"md\" | \"lg\""
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"defaultValue": "\"muted\"",
|
|
27
|
+
"description": "Semantic colour intent for the mark and the label.",
|
|
28
|
+
"name": "tone",
|
|
29
|
+
"type": "\"default\" | \"muted\" | \"primary\" | \"success\" | \"warning\" | \"destructive\" | \"info\" | \"inherit\""
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"description": "Localized description of WHAT is happening ('佐藤さんが入力しています…', 'Hưng đang nhập…'). Rendered as visible Text beside the mark when `children` are absent; when `children` ARE present it becomes an sr-only description instead. Consumer-owned copy — the library never invents it, and 'N people are typing' must be pluralized with the consumer's Intl.PluralRules.",
|
|
33
|
+
"name": "label",
|
|
34
|
+
"type": "ReactNode"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"description": "Richer visible content in place of `label` (a name in a <strong>, a Badge…). The mark stays aria-hidden; pass `label` alongside for the sr-only description.",
|
|
38
|
+
"name": "children",
|
|
39
|
+
"type": "ReactNode"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"defaultValue": "false",
|
|
43
|
+
"description": "Announce the label to assistive technology. Default `false` and then NO live region is emitted at all — the DELIBERATE default, because an ambient indicator that fires a live region on every socket event is a screen-reader flood. `'polite'` wraps ONLY the label in one aria-live='polite' aria-atomic='true' region; the mark stays outside it and aria-hidden.",
|
|
44
|
+
"name": "announce",
|
|
45
|
+
"type": "false | \"polite\""
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"related": [
|
|
49
|
+
"Reveal — the one-shot ENTRANCE counterpart. Reveal runs once on mount; Activity runs forever. Both live in styles/motion.css and both drop their animation under prefers-reduced-motion.",
|
|
50
|
+
"Skeleton — content is LOADING (aria-busy + a shaped placeholder). Use Skeleton when the content itself has not arrived; use Activity when content is present and something is happening elsewhere.",
|
|
51
|
+
"Button (loading) — THIS action is in flight, on a control. Not an ambient state.",
|
|
52
|
+
"Progress — a DETERMINATE amount is done. Activity's `bar` variant is the indeterminate case, where no percentage exists.",
|
|
53
|
+
"DataState — antd `Spin`'s wrapper form (`<Spin spinning>{children}</Spin>`) over a query: skeleton → prerequisite → empty → error, with cause-aware retry. This is what to reach for when the REGION is loading.",
|
|
54
|
+
"antd `Spin` — no such component here, by a ruling recorded twice in docs/roadmap/parity-backlog.md and docs/roadmap/parity-audit-data-display-feedback.md §2.20. Its four jobs are Activity / Skeleton / DataState / Button `loading`. Its `delay` (flicker guard) is tracked separately against Button as `loadingDelay`; its `fullscreen` is a Dialog/app-shell concern, not an indicator one."
|
|
55
|
+
],
|
|
56
|
+
"rules": [],
|
|
57
|
+
"storyPath": "general/Activity.stories.tsx",
|
|
58
|
+
"tagline": "The official AMBIENT-motion primitive and the standalone indeterminate indicator (antd `Spin`) — a continuous, unbounded 'something is happening right now, elsewhere' mark (someone typing, a sync running, a response streaming, a recording live). Loading a REGION is Skeleton; an in-flight ACTION is Button loading; a known percentage is Progress.",
|
|
59
|
+
"usage": [
|
|
60
|
+
"DO read this first if you came looking for a Spin/Spinner (gh#830). antd's `Spin` is ONE component with a `spinning` boolean covering four different situations; here those are four components, because the accessible semantics of each are genuinely different: (1) something is happening ELSEWHERE, indefinitely → Activity, no live region by default; (2) the CONTENT of this region is loading → Skeleton (aria-busy + aria-live, shaped placeholders) or DataState (the whole skeleton → prerequisite → empty → error lifecycle); (3) THIS action is in flight → Button `loading` (aria-busy + activation blocked on the control itself); (4) a percentage is known → Progress. Picking by shape ('I want the round one') is how a persistent indicator ends up telling a screen reader the page is busy forever.",
|
|
61
|
+
"DO use `variant='bar'` for the indeterminate case — that IS this library's indeterminate indicator, and the mark being a sweeping bar rather than a rotating circle is a system-level decision, not a gap. antd's `Spin percent='auto'` (a synthesized percentage that never reaches 100) is deliberately NOT ported: a fabricated number over an unknown wait is a determinate-looking lie, and Progress is there for when the number is real.",
|
|
62
|
+
"DO use <Activity> INSTEAD of hand-rolling `@keyframes typing-bounce` in a consumer app CSS. That re-derives interval/amplitude/stagger the DS owns as tokens (`--duration-loop`, `--activity-interval`, `--activity-stagger-step`, `--activity-mark-offset`) and needs its own prefers-reduced-motion guard — the guard consumers forget.",
|
|
63
|
+
"DO NOT reuse Skeleton for an ambient indicator. Skeleton hard-codes `aria-busy='true'` + `aria-live='polite'` because it means CONTENT IS LOADING; a persistent typing indicator built on it tells every screen reader the region is busy for as long as anyone is typing, and re-announces. Activity emits neither by default.",
|
|
64
|
+
"DO NOT reuse Button `loading`. That is a spinner bound to an in-flight action, on a control. Activity means something is happening indefinitely, ELSEWHERE.",
|
|
65
|
+
"DO leave `announce` at its default `false` for a flickering affordance (typing, presence). Opt into `announce='polite'` only when the change genuinely must be heard (a reconnect banner, a recording that just started) — and never on a value that changes on every socket event.",
|
|
66
|
+
"DO always pass a localized `label` (or `children`) when the indicator carries meaning: the mark alone announces nothing and communicates nothing under reduced motion. Route the string through your app's t()/Intl.PluralRules — the library ships no copy for it.",
|
|
67
|
+
"DO rely on the built-in reduced-motion behaviour — under `prefers-reduced-motion: reduce` the loop is dropped and each mark falls back to a DESIGNED resting state (three solid dots / a solid pulse mark / a bar segment parked at the reading-start), never to nothing, with no layout shift (WCAG 2.2 SC 2.3.3 and SC 2.2.2).",
|
|
68
|
+
"DO retune the ambient feel from a service theme, not per call site: `--activity-interval` (rhythm), `--activity-stagger-step` (dot offset), `--activity-mark-size` / `--activity-mark-offset` (mark and travel), `--activity-mark-rest-alpha`, `--activity-pulse-mark-size`, `--activity-gap`, `--activity-font-size-{xs,sm,md,lg}`, `--activity-bar-{width,height,radius,segment-width,track-alpha}`, `--activity-color`.",
|
|
69
|
+
"DO reserve the indicator's row height in the surrounding layout (it sits above a composer): Activity itself never changes size, but the row appearing and disappearing is the consumer's layout to keep stable.",
|
|
70
|
+
"DO NOT add `role='status'` yourself — that implies an unconditional polite live region, which is exactly what the `announce` default exists to keep off. Activity is not focusable and is not a tab stop."
|
|
71
|
+
],
|
|
72
|
+
"useCases": [
|
|
73
|
+
"A chat channel's typing affordance under the composer: `<Activity label={t('channel.typing', { name })} />` — appears and disappears on socket events without ever announcing.",
|
|
74
|
+
"A live-sync pulse in a page header: `<Activity variant='bar' tone='info' label='同期中…' />`.",
|
|
75
|
+
"A streaming assistant response: `<Activity variant='dots' label='生成しています…' />` under the partial answer.",
|
|
76
|
+
"A recording / live mark: `<Activity variant='pulse' tone='destructive' label='録画中' />`.",
|
|
77
|
+
"A live-updating dashboard tile: `<Activity variant='pulse' size='xs' label='リアルタイム更新中' />` beside the metric.",
|
|
78
|
+
"A reconnect notice that MUST be heard once: `<Activity announce='polite' tone='warning' label='接続を再試行しています…' />`."
|
|
79
|
+
]
|
|
80
|
+
}
|