@flood-md/agent 0.2.12 → 0.2.13

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.
Files changed (57) hide show
  1. package/DESIGN.md +10 -1
  2. package/README.md +9 -4
  3. package/SKILL.md +16 -5
  4. package/dist/catalog.json +742 -214
  5. package/dist/materials/component-alert-dialog.md +1 -0
  6. package/dist/materials/component-app-shell.md +1 -1
  7. package/dist/materials/component-bar-chart.md +52 -0
  8. package/dist/materials/component-bar-list.md +41 -0
  9. package/dist/materials/component-box-plot.md +46 -0
  10. package/dist/materials/component-bullet-chart.md +43 -0
  11. package/dist/materials/component-code-snippet.md +3 -1
  12. package/dist/materials/component-collection-toolbar.md +2 -0
  13. package/dist/materials/component-command-palette.md +1 -0
  14. package/dist/materials/component-copy-button.md +2 -0
  15. package/dist/materials/component-dialog.md +2 -0
  16. package/dist/materials/component-donut-chart.md +46 -0
  17. package/dist/materials/component-drawer.md +2 -0
  18. package/dist/materials/component-dumbbell-chart.md +46 -0
  19. package/dist/materials/component-heatmap.md +45 -0
  20. package/dist/materials/component-histogram.md +46 -0
  21. package/dist/materials/component-image-preview.md +1 -0
  22. package/dist/materials/component-line-chart.md +50 -0
  23. package/dist/materials/component-menu.md +2 -0
  24. package/dist/materials/component-range-chart.md +46 -0
  25. package/dist/materials/component-scatter-chart.md +48 -0
  26. package/dist/materials/component-sheet.md +1 -0
  27. package/dist/materials/component-table.md +1 -1
  28. package/dist/materials/component-waterfall-chart.md +47 -0
  29. package/dist/materials/guide-agent-knowledge.md +5 -5
  30. package/dist/materials/guide-agents.md +35 -2
  31. package/dist/materials/guide-charts.md +34 -0
  32. package/dist/materials/guide-foundations.md +2 -2
  33. package/dist/materials/guide-installation.md +6 -6
  34. package/dist/materials/guide-introduction.md +2 -1
  35. package/dist/materials/guide-motion.md +12 -0
  36. package/dist/materials/guide-skills.md +10 -0
  37. package/dist/materials/recipe-collection-views.md +3 -0
  38. package/dist/materials/recipe-navigation-workspace.md +3 -0
  39. package/dist/materials/recipe-record-management.md +92 -58
  40. package/knowledge.json +16 -0
  41. package/lib/inspect.d.ts +14 -2
  42. package/lib/inspect.mjs +63 -9
  43. package/manifest.json +11 -1
  44. package/package.json +1 -1
  45. package/references/component-families.md +10 -0
  46. package/references/flood-composition/SKILL.md +7 -0
  47. package/references/flood-data-display/SKILL.md +85 -0
  48. package/references/flood-forms/SKILL.md +5 -0
  49. package/references/flood-overlays/SKILL.md +5 -0
  50. package/references/flood-records/SKILL.md +13 -4
  51. package/references/flood-screen-composition/SKILL.md +48 -0
  52. package/references/flood-verification/SKILL.md +32 -0
  53. package/references/flood-visual-review/SKILL.md +59 -0
  54. package/references/rendered-review.md +49 -3
  55. package/references/routing.md +2 -1
  56. package/rules.json +34 -2
  57. package/workflows.json +22 -0
@@ -0,0 +1,48 @@
1
+ # ScatterChart
2
+
3
+ Paired numeric observations for inspecting how two measures vary together.
4
+
5
+ Connect Flood once using [the installation guide](https://flood.md/docs/installation). Then import the components you use; no per-component add command is needed.
6
+
7
+ ## Example
8
+
9
+ ```tsx
10
+ "use client";
11
+
12
+ import { FloodRoot, ScatterChart } from "@flood-md/react";
13
+ import "@flood-md/react/styles.css";
14
+
15
+ export function Example() {
16
+ return (
17
+ <FloodRoot>
18
+ <ScatterChart label="Response time by traffic" xLabel="Requests / sec" yLabel="Response time (ms)" points={[
19
+ {id:"run-1", label:"Run 1", x:12, y:110},
20
+ {id:"run-2", label:"Run 2", x:52, y:162},
21
+ {id:"run-3", label:"Run 3", x:104, y:301}
22
+ ]} />
23
+ </FloodRoot>
24
+ );
25
+ }
26
+ ```
27
+
28
+ ## Design notes
29
+
30
+ - Give each point a stable ID and finite x and y values. Label both axes with their units.
31
+ - Use matching xDomain and yDomain to compare separate plots. Use formatX and formatY for localized values.
32
+ - Inspect the nearest point with the pointer or traverse observations in supplied order with the keyboard.
33
+
34
+ ## API reference
35
+
36
+ | Prop | Values | Default |
37
+ | --- | --- | --- |
38
+ | `label` | `string` | `Required` |
39
+ | `xLabel` | `string` | `Required` |
40
+ | `yLabel` | `string` | `Required` |
41
+ | `points` | `readonly ScatterChartPoint[]` | `Required` |
42
+ | `xDomain` | `readonly [number, number]` | `—` |
43
+ | `yDomain` | `readonly [number, number]` | `—` |
44
+ | `formatX` | `(value: number) => string` | `—` |
45
+ | `formatY` | `(value: number) => string` | `—` |
46
+ | `emptyLabel` | `string` | `—` |
47
+ | `navigationLabel` | `string` | `—` |
48
+ | `Native HTML props` | `Omit<HTMLAttributes<HTMLDivElement>, "children" \| "role" \| "aria-label">` | `Forwarded` |
@@ -44,6 +44,7 @@ export function Example() {
44
44
  | `closeLabel` | `string` | `—` |
45
45
  | `closeOnOutsideClick` | `boolean` | `—` |
46
46
  | `initialFocusRef` | `RefObject<HTMLElement \| null>` | `—` |
47
+ | `getFallbackFocus` | `() => HTMLElement \| null` | `—` |
47
48
  | `size` | `"sm" \| "md" \| "lg"` | `"md"` |
48
49
  | `footer` | `ReactNode` | `—` |
49
50
  | `Native HTML props` | `Omit<Omit<HTMLAttributes<HTMLDialogElement>, "children" \| "title" \| "role">, "side">` | `Forwarded` |
@@ -28,7 +28,7 @@ export function Example() {
28
28
 
29
29
  - Caption is required and remains the accessible table name with hideCaption. Supply table sections and correct native header/cell associations.
30
30
  - Native table props, className/style and HTMLTableElement ref target the actual table. Set style.minWidth when horizontal overflow is preferable to compressed columns.
31
- - Flood ScrollArea owns horizontal scrolling and keyboard access. scrollLabel can name its viewport independently; dir and hidden apply to the containing scroll area too.
31
+ - Flood ScrollArea owns horizontal scrolling and keyboard access. Set maxViewportHeight for bounded vertical scrolling with stable clearance for the overlay rail. scrollLabel names the viewport independently.
32
32
  - Use data-numeric on headers/cells for end alignment and tabular figures; data-align=start/center/end gives logical alignment. No automatic sorting, selection, pagination or data access.
33
33
  - Striped and compact/comfortable density are opt-in presentation. Available in 0.2.4.
34
34
 
@@ -0,0 +1,47 @@
1
+ # WaterfallChart
2
+
3
+ Signed changes and checkpoints along a running total.
4
+
5
+ Connect Flood once using [the installation guide](https://flood.md/docs/installation). Then import the components you use; no per-component add command is needed.
6
+
7
+ ## Example
8
+
9
+ ```tsx
10
+ "use client";
11
+
12
+ import { FloodRoot, WaterfallChart } from "@flood-md/react";
13
+ import "@flood-md/react/styles.css";
14
+
15
+ export function Example() {
16
+ return (
17
+ <FloodRoot>
18
+ <WaterfallChart label="Net revenue (USD)" steps={[
19
+ {id:"gross", label:"Gross", value:32000, kind:"total"},
20
+ {id:"refunds", label:"Refunds", value:-4500},
21
+ {id:"fees", label:"Fees", value:-2500},
22
+ {id:"net", label:"Net", value:25000, kind:"total"}
23
+ ]} />
24
+ </FloodRoot>
25
+ );
26
+ }
27
+ ```
28
+
29
+ ## Design notes
30
+
31
+ - A change adds its signed value to the running total. A total is an absolute checkpoint that replaces the running total.
32
+ - Use the same domain for charts you want to compare. It must contain zero and every intermediate total. Use formatValue to add units.
33
+ - Hover, tap or use the keyboard to inspect exact changes and running totals. Decreases use a dashed outline.
34
+
35
+ ## API reference
36
+
37
+ | Prop | Values | Default |
38
+ | --- | --- | --- |
39
+ | `label` | `string` | `Required` |
40
+ | `steps` | `readonly WaterfallChartStep[]` | `Required` |
41
+ | `domain` | `readonly [number, number]` | `—` |
42
+ | `formatValue` | `(value: number) => string` | `—` |
43
+ | `changeLabel` | `string` | `—` |
44
+ | `totalLabel` | `string` | `—` |
45
+ | `emptyLabel` | `string` | `—` |
46
+ | `navigationLabel` | `string` | `—` |
47
+ | `Native HTML props` | `Omit<HTMLAttributes<HTMLDivElement>, "children" \| "role" \| "aria-label">` | `Forwarded` |
@@ -7,11 +7,11 @@ The same package sources build the website, CLI results and agent instructions.
7
7
  ## Discover, then read
8
8
 
9
9
  ```sh
10
- npx @flood-md/cli@0.2.12 info --json
11
- npx @flood-md/cli@0.2.12 catalog --kind workflow --group foundations
12
- npx @flood-md/cli@0.2.12 catalog --kind component --search table
13
- npx @flood-md/cli@0.2.12 read recipe:table-bulk-actions
14
- npx @flood-md/cli@0.2.12 read component:data-table
10
+ npx @flood-md/cli@0.2.13 info --json
11
+ npx @flood-md/cli@0.2.13 catalog --kind workflow --group foundations
12
+ npx @flood-md/cli@0.2.13 catalog --kind component --search table
13
+ npx @flood-md/cli@0.2.13 read recipe:table-bulk-actions
14
+ npx @flood-md/cli@0.2.13 read component:data-table
15
15
  ```
16
16
 
17
17
  These commands return JSON and do not install dependencies, edit files, execute
@@ -1,5 +1,9 @@
1
1
  # Using Flood with an agent
2
2
 
3
+ Flood pairs a React design system with a workflow for agents building and
4
+ reviewing interfaces. Each release connects component contracts, runnable
5
+ recipes and focused skills through the same catalog.
6
+
3
7
  Start with the host's instructions, package.json, flood.json and installed
4
8
  flood/DESIGN.md. Use matching package/CLI versions and declarations. A newer
5
9
  website example does not add APIs to an older installation.
@@ -82,8 +86,37 @@ component pages. Before completion use verification. For a color-only change,
82
86
  load customization and the relevant token roles; unrelated guides stay unloaded.
83
87
 
84
88
  Clients supporting the skill-folder convention can discover it. Others can read
85
- it explicitly or link it from existing instructions. The installer preserves
86
- AGENTS.md; it does not configure clients or guarantee automatic discovery.
89
+ it explicitly. For a client that reads project instructions such as `AGENTS.md`,
90
+ add a short trigger to the existing instructions at the scope where Flood is used:
91
+
92
+ ```markdown
93
+ For UI work in this project, read `.agents/skills/flood-ui/SKILL.md` first.
94
+ Follow its routing for the screen or component you are changing. Check the
95
+ installed component contract. For visible changes, use the visual review workflow
96
+ to exercise the affected states and inspect screenshots from the running app.
97
+ ```
98
+
99
+ Use the `installedEntry` path in `flood/manifest.json` if it differs. The
100
+ installer preserves `AGENTS.md`; add this trigger yourself if it suits your client.
101
+
102
+ ## Review the running interface
103
+
104
+ When composing a new flow, start from a nearby recipe, check the installed
105
+ component contracts and apply the workflow for the task. For example, use the
106
+ [record management recipe](recipes/record-management.md) to see how a collection
107
+ preserves filters, scroll and focus around a detail Drawer and edit form.
108
+
109
+ For a visual issue, exercise the actual state and inspect the captured evidence.
110
+ If it repeats, check the smallest component or layout that can enforce the fix.
111
+ Add a browser assertion when behavior can be measured. Use the relevant workflow
112
+ or a runnable example for decisions that depend on the task, such as information
113
+ hierarchy or which details should stay visible. Use clean runs to test whether
114
+ agents discover and apply new guidance on another task.
115
+
116
+ For a full-window AppShell route, use
117
+ [`assertWorkspaceScroll`](../packages/agent/references/rendered-review.md#require-workspace-scroll-in-browser-tests)
118
+ in the browser suite. Exercise both the populated and filtered states so
119
+ document scrolling fails the test on either state.
87
120
 
88
121
  The rules index is data, not an executable policy engine. It cannot make an agent
89
122
  obey guidance or certify accessibility. Host checks and Flood's repository
@@ -0,0 +1,34 @@
1
+ # Charts
2
+
3
+ [Try the three latency charts](https://flood.md/docs/charts#latency-example)
4
+ with one shared service filter, or pick a chart below and copy its React example.
5
+ For setup in React or Next.js, see [Installation](installation.md).
6
+
7
+ ## Trends and comparisons
8
+
9
+ - [LineChart](https://flood.md/components/line-chart) shows how a measure changes over time. Compare this week with last week on the same scale.
10
+ - [Sparkline](https://flood.md/components/sparkline) shows a small trend beside a value in a table or summary.
11
+ - [BarChart](https://flood.md/components/bar-chart) compares categories. Group series to compare actual and planned values, or use percent stacks to compare their shares.
12
+ - [BarList](https://flood.md/components/bar-list) ranks named values, such as traffic sources, where the labels and counts matter as much as the bars.
13
+ - [BulletChart](https://flood.md/components/bullet-chart) compares a single value with a target and optional reference bands.
14
+
15
+ ## Distributions and relationships
16
+
17
+ - [Histogram](https://flood.md/components/histogram) shows how often values fall into intervals. Use it to see whether requests cluster near 100 ms or have a long tail.
18
+ - [BoxPlot](https://flood.md/components/box-plot) compares the median, quartiles and endpoints across groups, such as latency by service.
19
+ - [ScatterChart](https://flood.md/components/scatter-chart) shows the relationship between two measurements, such as throughput and response time.
20
+ - [Heatmap](https://flood.md/components/heatmap) shows patterns across two dimensions, such as activity by day and hour.
21
+ - [RangeChart](https://flood.md/components/range-chart) shows an interval for each item, such as a forecast range.
22
+
23
+ ## Parts of a whole and change
24
+
25
+ - [DonutChart](https://flood.md/components/donut-chart) shows a few shares of one whole, such as spending by category.
26
+ - [WaterfallChart](https://flood.md/components/waterfall-chart) explains how individual increases and decreases add up to a final value.
27
+ - [DumbbellChart](https://flood.md/components/dumbbell-chart) compares two values for each category, such as before and after a rollout.
28
+
29
+ ## Inspecting values
30
+
31
+ Give each chart a name that includes its measure. Keep units consistent across
32
+ axis labels and value formatting. Hover or focus interactive plots to inspect
33
+ values, then use the arrow keys to move through the data. Where readers need to
34
+ scan or copy many exact values, provide the data in a table alongside the chart.
@@ -1,6 +1,6 @@
1
- # Foundations tokens · 0.2.12 candidate
1
+ # Foundations tokens · 0.2.13
2
2
 
3
- This guide describes the **0.2.12 candidate** foundation exports. Keep Flood packages at
3
+ This guide describes the **0.2.13** foundation exports. Keep Flood packages at
4
4
  matching exact versions. Existing 0.1.1 installs use their earlier token contract.
5
5
 
6
6
  Foundations separates fixed reference values (`ref`) from semantic roles (`sys`).
@@ -1,6 +1,6 @@
1
- # Installation · 0.2.12
1
+ # Installation · 0.2.13
2
2
 
3
- The commands below target version 0.2.12. Verify registry availability before installation; for local source evaluation, see the tarball instructions at the end of this guide.
3
+ The commands below target version 0.2.13. Verify registry availability before installation; for local source evaluation, see the tarball instructions at the end of this guide.
4
4
 
5
5
  Use Node.js 22.12 or newer and an existing npm-based React/Next.js project.
6
6
  React 18.3/19 is supported; React 19 and Next.js App Router are integration targets.
@@ -15,8 +15,8 @@ repeat installation when you start using another component.
15
15
  ## Connect with the CLI
16
16
 
17
17
  ```sh
18
- npx @flood-md/cli@0.2.12 init --dry-run
19
- npx @flood-md/cli@0.2.12 init
18
+ npx @flood-md/cli@0.2.13 init --dry-run
19
+ npx @flood-md/cli@0.2.13 init
20
20
  ```
21
21
 
22
22
  The installer adds exact matching tokens, React and agent packages. It creates
@@ -40,7 +40,7 @@ No Tailwind configuration or font CDN is needed. The foundations profile is dark
40
40
  only; legacy theme behavior remains available outside `FloodRoot`.
41
41
 
42
42
  ```sh
43
- npx @flood-md/cli@0.2.12 doctor
43
+ npx @flood-md/cli@0.2.13 doctor
44
44
  ```
45
45
 
46
46
  Doctor checks package versions and guide presence, not stylesheet imports or
@@ -50,7 +50,7 @@ reference `flood/DESIGN.md` from its existing instructions.
50
50
  ## Manual package installation
51
51
 
52
52
  ```sh
53
- npm install --save-exact @flood-md/tokens@0.2.12 @flood-md/react@0.2.12 @flood-md/agent@0.2.12
53
+ npm install --save-exact @flood-md/tokens@0.2.13 @flood-md/react@0.2.13 @flood-md/agent@0.2.13
54
54
  ```
55
55
 
56
56
  Import the stylesheet and use `FloodRoot` as above. Manual installation does not
@@ -16,6 +16,7 @@ them without a separate content service.
16
16
  | [Project UI](project-ui.md) | Optional semantic @/ui/button imports in your application |
17
17
  | [Layout](layout.md) | Intrinsic actions, responsive forms, choice sizing, gaps and hierarchy |
18
18
  | [Components](components.md) | Available controls, props and form composition |
19
+ | [Charts](charts.md) | Choose a chart by the question it answers and try the live latency example |
19
20
  | [Base component inventory](base-components.md) | Available primitives and controls in the base release |
20
21
  | [Customization](theming.md) | Scoped dark foundations overrides and legacy theme boundaries |
21
22
  | [Foundations](foundations.md) | Typed semantic tokens and neutral dark profile for 0.2.11 |
@@ -30,7 +31,7 @@ them without a separate content service.
30
31
 
31
32
  ## Versions
32
33
 
33
- This branch documents **version 0.2.11**; source availability does
34
+ This branch documents **version 0.2.13**; source availability does
34
35
  not establish npm publication. Use matching exact versions of the Flood packages. Documentation on `main` can describe later work; the
35
36
  [release tags](https://github.com/tillwithered/flood.md/releases) preserve the
36
37
  source and docs for each release. Installed `flood/DESIGN.md` takes precedence
@@ -54,6 +54,18 @@ behavior of their children. State updates and focus take effect immediately.
54
54
  | `CodeSnippet`, `Card`, `DataList`, `DataListItem`, `FileItem`, `FileList`, `AvatarGroup` | Embedded actions and avatars own motion. Reading order and record layout remain stable. |
55
55
  | `Badge`, `DescriptionList`, `Table`, `Image`, `List`, `Figure`, `Metric`, `Timeline`, `ColorSwatch`, `FileMeta` | Static content. Changes to data, labels and supporting media render immediately. |
56
56
  | `Sparkline` | Hover briefly strengthens the latest point and area. Data changes render immediately. |
57
+ | `LineChart` | Crosshair, markers and local line emphasis track the pointer continuously. The readout shows the nearest recorded sample and lifts clear of the marker near the trailing edge. On exit the readout and marks fade in place from their last pointer position. |
58
+ | `BarList` | Uses Meter's value transition and the same fast highlight as chart inspection. Labels and values update immediately. |
59
+ | `BarChart` | Uses the fast motion role to emphasize the inspected group while its neighbors dim. The readout updates with the active category and column geometry stays fixed. |
60
+ | `DonutChart` | Uses the fast motion role for the inspected segment's outline. Category colors, fills and geometry stay fixed, and the center readout updates immediately. |
61
+ | `Heatmap` | Moves the value tooltip immediately to the inspected cell and keeps the matrix fixed. Visual gutters share the adjacent cells' hit areas. |
62
+ | `ScatterChart` | Highlights the inspected observation and presents its coordinates. Points keep their positions. |
63
+ | `RangeChart` | Highlights the inspected interval while its exact bounds update immediately. |
64
+ | `BulletChart` | Uses the Meter transition for the actual value. Target marks and exact values update immediately. |
65
+ | `WaterfallChart` | Highlights the inspected step and shows the change and running balance. |
66
+ | `Histogram` | Shows exact bin bounds and count as inspection moves. Bin geometry stays fixed. |
67
+ | `BoxPlot` | Highlights the inspected distribution and anchors its summary to the median. The tooltip prefers the right side and flips when space runs out. |
68
+ | `DumbbellChart` | Highlights the inspected comparison without moving its marks. The readout updates immediately. |
57
69
  | `Avatar` | Loaded image eases in within a fixed frame. Fallback text is immediately available. |
58
70
  | `DataTable` | Selected row paint, chevron rotation and a short height transition for row details. Sorting, pagination and column resize update geometry immediately. |
59
71
  | `Tooltip`, `Popover`, `Menu`, `ContextMenu`, `HoverCard`, `CommandPalette`, `Dialog`, `AlertDialog`, `Drawer`, `Sheet`, `ImagePreview` | Anchored entrances follow placement. Modal and edge panels have their own timing. Sheet uses the bottom Drawer entrance. Controls, focus, close and positioning retain their native behavior. |
@@ -13,11 +13,19 @@ The hierarchy workflow covers task/group/field roles, common control tracks and
13
13
  relationship spacing. It complements editorial decisions and the spacing workflow;
14
14
  it does not prescribe one screen template.
15
15
 
16
+ The screen composition workflow helps choose page structure and reading order
17
+ for landing pages, workspaces, forms and documentation. Review it by running
18
+ the user's find, compare or act task in the rendered screen.
19
+
16
20
  The records workflow covers list/detail information selection, stable scan
17
21
  positions and the distinction between category, lifecycle state, priority and
18
22
  time. It includes examples where those attributes should remain visible, rather
19
23
  than a universal rule to hide metadata or limit badge counts.
20
24
 
25
+ Visual review exercises the running UI, captures the affected states and checks
26
+ the screenshots before handoff. Use it for new UI, screenshot feedback and
27
+ visible regressions. Keep browser evidence with the findings or the fix.
28
+
21
29
  Use [Agent knowledge](agent-knowledge.md) to discover these workflows by semantic
22
30
  group and read the installed version. The catalog separates prerequisites from
23
31
  optional related components; grouping is not a request to load every skill.
@@ -40,6 +48,7 @@ Layout and task recipes assembled from existing primitives.
40
48
  | --- | --- | --- |
41
49
  | flood-composition | Build or review Flood UI from existing library primitives before adding composites. Use for component, website, playground and host-interface work, especially when a layout fix risks replacing a reusable element with custom markup. | [Read skill](/agent/references/flood-composition/SKILL.md) |
42
50
  | flood-interface-content | Write or review Flood interface content with editorial decisions about copy, labels, hierarchy and states. Use for visible UI text and reference adaptation; not typography styling, campaigns or rewriting user documents. | [Read skill](/agent/references/flood-interface-content/SKILL.md) |
51
+ | flood-screen-composition | Choose the layout, reading order and first useful action for a Flood screen. Use for new screens or when an existing page is hard to scan, compare or act on. | [Read skill](/agent/references/flood-screen-composition/SKILL.md) |
43
52
  | flood-hierarchy | Establish visual hierarchy, shared form tracks and vertical rhythm in Flood screens, forms and overlays. Use when titles, groups and labels compete or a composed interface feels flat; not a fixed page template. | [Read skill](/agent/references/flood-hierarchy/SKILL.md) |
44
53
  | flood-spacing | Diagnose Flood spacing, density, nested radii and alignment from rendered geometry. Use for large gaps, crowded rows or shifting compositions. | [Read skill](/agent/references/flood-spacing/SKILL.md) |
45
54
  | flood-records | Compose record lists and detail views with a clear metadata hierarchy. Use for crowded status/category/priority/date rows, drifting scan positions or deciding between a list and a comparison table. | [Read skill](/agent/references/flood-records/SKILL.md) |
@@ -74,6 +83,7 @@ Accessibility, evidence and regression checks.
74
83
  | flood-interface-content | Write or review Flood interface content with editorial decisions about copy, labels, hierarchy and states. Use for visible UI text and reference adaptation; not typography styling, campaigns or rewriting user documents. | [Read skill](/agent/references/flood-interface-content/SKILL.md) |
75
84
  | flood-accessibility | Review Flood interface names, keyboard flow, focus, perception and reflow in the actual host. Use for interaction or accessibility acceptance. | [Read skill](/agent/references/flood-accessibility/SKILL.md) |
76
85
  | flood-verification | Verify Flood component or composed-interface changes against applicable contracts and actual evidence. Use for stabilization, regression or completion review. | [Read skill](/agent/references/flood-verification/SKILL.md) |
86
+ | flood-visual-review | Review rendered Flood UI with Playwright or equivalent browser tooling. Use for visual bugs, screenshot feedback, new or changed UI, and visual regressions across interaction states. | [Read skill](/agent/references/flood-visual-review/SKILL.md) |
77
87
 
78
88
  ## Motion
79
89
 
@@ -239,6 +239,9 @@ defines reset scope and supplies canClear for any state held by quickFilters.
239
239
  At most two task-important quick properties remain inline; additional properties
240
240
  belong inside Filters. Summaries outside the panel name the property and preserve
241
241
  any-match meaning. Container width, rather than viewport size, governs reflow.
242
+ When search and filters share a wide row with a status or view selector, use
243
+ `align="end"` on CollectionToolbar. Check that the final control reaches the
244
+ row's trailing content edge and that the row wraps with enlarged text.
242
245
  The Projects table places its Columns control in CollectionToolbar's `actions`
243
246
  slot via DataTable's `toolbar` render function. It shares the tool row with
244
247
  search and filters, and the commands wrap as a group in a narrow container.
@@ -62,6 +62,9 @@ embedded shell an explicit block size. Choose `scroll="page"` for natural
62
62
  document scrolling. Place a persistent top bar and footer in the shell slots,
63
63
  and PageHeader inside main. The shell has a skip target and owns the scroll
64
64
  boundary; the host owns routes and persisted choices.
65
+ For a full-window workspace with a padded content edge, use
66
+ `scrollContentInset="none"` when that padding already clears the overlay rail.
67
+ Verify that the document width stays fixed as its collection grows and shrinks.
65
68
 
66
69
  For a single Sidebar, opt into `navigationDrawer` with a width threshold in CSS
67
70
  pixels and a `renderContent(close)` function. AppShell observes its own width.
@@ -2,22 +2,22 @@
2
2
 
3
3
  [Open the working composition](/playground/records).
4
4
 
5
- Search requests, narrow the list by status, open one request, and edit it in a
6
- Drawer. Saving applies the accepted changes and returns to the same list.
5
+ Search requests, narrow the table by status, open one request, and edit it in a
6
+ Drawer. Saving updates the request and returns to the same table.
7
7
  The example uses in-memory fixtures that reset on reload.
8
8
 
9
9
  ## Choose where details open
10
10
 
11
11
  Use a Drawer for a bounded inspection or edit that starts and ends in the same
12
12
  list. Use a destination page for details that need a stable URL, substantial
13
- room, or several independent tasks. This example uses a named Open button
14
- because opening its Drawer is an action on the current screen.
13
+ room, or several independent tasks.
15
14
 
16
15
  When users compare several attributes, DataTable can use a `primaryAction`
17
16
  in the identity column. Pass the Drawer trigger handlers and ref to that
18
17
  action. Check the row background, title and description, checkbox and any
19
18
  secondary menu independently. Use `toolbar` and `inset="roomy"` for a heading
20
- and filters that share the table surface.
19
+ and filters that share the table surface. Use the request title as the visible
20
+ action and keep its ID beside it for disambiguation.
21
21
 
22
22
  ```tsx
23
23
  <Drawer title="Review workspace access" size="lg"
@@ -52,6 +52,10 @@ reading area. Compose the body from Section for separately named content,
52
52
  DescriptionList for facts and Fieldset for related controls. Check wrapping and
53
53
  scrolling with the longest title and a real footer action before selecting a size.
54
54
  Use the group heading size inside a detail Drawer to keep the record name prominent.
55
+ Keep the request facts in one DescriptionList and use a compact, captioned Table
56
+ for related records that have comparable columns, such as activity dates and events.
57
+ Give that table its own heading when it identifies a distinct section. The Drawer
58
+ body owns vertical scrolling and the footer keeps the next action within reach.
55
59
 
56
60
  When the detail contains line items and a total, use the Table footer to mark
57
61
  the summary and keep its amount aligned with the line-item amounts. A row header
@@ -69,13 +73,14 @@ labels the total, and the Table footer supplies the separator and emphasis.
69
73
  ## Keep browsing state and edit state separate
70
74
 
71
75
  Keep search, filter, accepted records and the open record ID in the host.
72
- Keep the list and its ScrollArea mounted while the Drawer is open. Reset the
76
+ Keep the DataTable and its ScrollArea mounted while the Drawer is open. Reset the
73
77
  scroll position only when the user changes the query or filter.
74
78
 
75
79
  Entering edit mode copies the selected record. Cancel edit returns to details
76
80
  without changing accepted data. Saving replaces the record by stable ID.
77
- Recompute matches using the existing search and filter. If the updated record
78
- leaves the results, explain why and preserve the query. For a remote save, close
81
+ Recompute matches using the existing search and filter. Use a floating ToastRegion
82
+ for the save result so it cannot move the table. If the updated record leaves
83
+ the results, explain why and preserve the query. For a remote save, close
79
84
  on accepted data and retain the draft with actionable feedback on failure.
80
85
 
81
86
  ## Restore focus to a useful place
@@ -83,8 +88,9 @@ on accepted data and retain the draft with actionable feedback on failure.
83
88
  Forward the Drawer trigger props from the single overlay to the row action
84
89
  that opens it. Focus Edit request when details open, Subject when editing starts,
85
90
  and Edit request after Cancel edit. On close, return focus to the same row
86
- action without scrolling. If the row left the results, focus Search requests
87
- without clearing it.
91
+ action without scrolling. Drawer resolves `getFallbackFocus` after closing
92
+ if the row left the results. The recipe returns Search requests and keeps
93
+ the current query.
88
94
 
89
95
  Keep subject and ID together in the list. Status earns a stable trailing slot
90
96
  because users filter and compare by it. Put owner and the longer summary in the
@@ -105,9 +111,10 @@ Drawer's conditional detail content.
105
111
 
106
112
  import {useEffect, useId, useLayoutEffect, useRef, useState, type FormEvent} from "react";
107
113
  import {
108
- Alert, Button, DataList, DataListItem, DescriptionList, Drawer, EmptyState,
109
- Field, Grid, Heading, Inline, Input, ScrollArea, SearchInput, Select,
110
- Stack, StatusIndicator, Surface,
114
+ Button, DataTable, DescriptionList, Drawer, EmptyState,
115
+ Field, Grid, Heading, Inline, Input, SearchInput, Section, Select,
116
+ Stack, StatusIndicator, Surface, Table, ToastRegion,
117
+ type DataTableColumn, type ToastItem,
111
118
  } from "@flood-md/react";
112
119
 
113
120
  const statuses = {
@@ -125,6 +132,24 @@ type RequestRecord = {
125
132
  summary: string;
126
133
  };
127
134
 
135
+ const requestColumns: DataTableColumn[] = [
136
+ {id: "subject", label: "Subject", sortable: true},
137
+ {id: "status", label: "Status", sortable: true},
138
+ ];
139
+
140
+ const activityByRequest: Record<string, readonly {date: string; activity: string}[]> = {
141
+ "REQ-105": [
142
+ {date: "2026-11-10", activity: "Access issue reported"},
143
+ {date: "2026-11-11", activity: "Identity verified"},
144
+ {date: "2026-11-12", activity: "Recovery link sent"},
145
+ ],
146
+ "REQ-117": [
147
+ {date: "2026-11-11", activity: "Permission review requested"},
148
+ {date: "2026-11-12", activity: "Regional roles exported"},
149
+ ],
150
+ };
151
+ const activityDate = new Intl.DateTimeFormat("en", {month: "short", day: "numeric", timeZone: "UTC"});
152
+
128
153
  const fixtures: RequestRecord[] = [
129
154
  {id: "REQ-104", title: "Update billing address", owner: "Mina Chen", status: "new", summary: "Use the new address on future invoices."},
130
155
  {id: "REQ-105", title: "Restore workspace access", owner: "Alex Morgan", status: "active", summary: "A teammate cannot sign in after changing email."},
@@ -157,25 +182,20 @@ export function RecordManagement() {
157
182
  const [status, setStatus] = useState<Status | "all">("all");
158
183
  const [recordId, setRecordId] = useState<string | null>(null);
159
184
  const [draft, setDraft] = useState<RequestRecord | null>(null);
160
- const [notice, setNotice] = useState("");
185
+ const [notice, setNotice] = useState<ToastItem | null>(null);
161
186
  const search = useRef<HTMLInputElement>(null);
162
- const list = useRef<HTMLDivElement>(null);
187
+ const table = useRef<HTMLDivElement>(null);
163
188
  const subject = useRef<HTMLInputElement>(null);
164
189
  const edit = useRef<HTMLButtonElement>(null);
165
- const opener = useRef<HTMLButtonElement>(null);
166
- const restore = useRef(false);
167
190
  const returnToEdit = useRef(false);
168
191
  const selected = records.find(record => record.id === recordId);
192
+ const activity = selected ? activityByRequest[selected.id] : undefined;
169
193
  const results = records.filter(record => matchesRequest(record, query, status));
170
194
  const editing = draft !== null;
171
195
  const open = selected !== undefined;
172
196
 
173
197
  useBrowserLayoutEffect(() => {
174
- if (!open && restore.current) {
175
- restore.current = false;
176
- const target = opener.current?.isConnected ? opener.current : search.current;
177
- target?.focus({preventScroll: true});
178
- } else if (editing) {
198
+ if (editing) {
179
199
  subject.current?.focus({preventScroll: true});
180
200
  } else if (open && returnToEdit.current) {
181
201
  returnToEdit.current = false;
@@ -186,12 +206,11 @@ export function RecordManagement() {
186
206
  function changeQuery(nextQuery: string, nextStatus: Status | "all") {
187
207
  setQuery(nextQuery);
188
208
  setStatus(nextStatus);
189
- setNotice("");
190
- if (list.current) list.current.scrollTop = 0;
209
+ setNotice(null);
210
+ table.current?.querySelector('[role="region"][aria-label="Requests"]')?.scrollTo({top: 0});
191
211
  }
192
212
 
193
213
  function closeRecord() {
194
- restore.current = true;
195
214
  returnToEdit.current = false;
196
215
  setDraft(null);
197
216
  setRecordId(null);
@@ -208,9 +227,12 @@ export function RecordManagement() {
208
227
  }
209
228
  const updated = {...draft, title, owner: draft.owner.trim()};
210
229
  setRecords(current => current.map(record => record.id === updated.id ? updated : record));
211
- setNotice(matchesRequest(updated, query, status)
212
- ? `${updated.id} updated.`
213
- : `${updated.id} updated and no longer matches the current search or filter.`);
230
+ setNotice({
231
+ id: updated.id,
232
+ title: `${updated.id} updated`,
233
+ description: matchesRequest(updated, query, status) ? undefined : "It no longer matches your search or filter.",
234
+ status: "success",
235
+ });
214
236
  closeRecord();
215
237
  }
216
238
 
@@ -245,35 +267,37 @@ export function RecordManagement() {
245
267
  open={open}
246
268
  onOpenChange={open => { if (!open) closeRecord(); }}
247
269
  initialFocusRef={edit}
248
- trigger={({ref, onClick, "aria-expanded": expanded, ...trigger}) =>
249
- <Surface elevation="raised" padding="4">
250
- <ScrollArea ref={list} role="region" aria-label="Request results" style={{maxBlockSize: "22rem"}}>
251
- {results.length > 0
252
- ? <DataList aria-label="Requests" className="request-results">
253
- {results.map(record => <DataListItem
254
- key={record.id}
255
- title={record.title}
256
- description={record.id}
257
- metadata={<StatusIndicator {...statuses[record.status]} size="sm" />}
258
- actions={<Button {...trigger} aria-expanded={Boolean(expanded && record.id === recordId)}
259
- aria-label={`Open ${record.id} ${record.title}`}
260
- variant="subtle" size="sm" onClick={event => {
261
- opener.current = event.currentTarget;
262
- ref(event.currentTarget);
263
- setRecordId(record.id);
264
- onClick?.(event);
265
- }}>Open</Button>}
266
- />)}
267
- </DataList>
268
- : <EmptyState title="No matching requests" actions={<Button variant="subtle" onClick={() => {
269
- search.current?.focus({preventScroll: true});
270
- changeQuery("", "all");
271
- }}>Clear filters</Button>} />}
272
- </ScrollArea>
270
+ getFallbackFocus={() => search.current}
271
+ trigger={({ref, onClick, ...trigger}) =>
272
+ <Surface elevation="raised" padding="0" className="request-table-frame">
273
+ <DataTable ref={table} caption="Requests" hideCaption inset="roomy" minWidth="min-content"
274
+ maxViewportHeight="26rem" stickyHeader pageSize={fixtures.length}
275
+ columns={requestColumns} rows={results.map(record => ({
276
+ id: record.id,
277
+ cells: {status: <StatusIndicator {...statuses[record.status]} size="sm" />},
278
+ sortValues: {status: statuses[record.status].label},
279
+ primaryAction: {
280
+ column: "subject", title: record.title, description: record.id,
281
+ buttonProps: {
282
+ ...trigger,
283
+ "aria-expanded": open && record.id === recordId,
284
+ "aria-label": `${record.title}, ${record.id}`,
285
+ onClick: event => {
286
+ ref(event.currentTarget);
287
+ setRecordId(record.id);
288
+ onClick?.(event);
289
+ },
290
+ },
291
+ },
292
+ }))}
293
+ emptyContent={<EmptyState title="No matching requests" actions={<Button variant="subtle" onClick={() => {
294
+ search.current?.focus({preventScroll: true});
295
+ changeQuery("", "all");
296
+ }}>Clear filters</Button>} />} />
273
297
  </Surface>}
274
298
  footer={draft
275
- ? <Inline gap="2"><Button type="submit" form={formId}>Save changes</Button>
276
- <Button variant="subtle" onClick={cancelEdit}>Cancel edit</Button></Inline>
299
+ ? <Inline gap="2"><Button variant="subtle" onClick={cancelEdit}>Cancel edit</Button>
300
+ <Button type="submit" form={formId}>Save changes</Button></Inline>
277
301
  : <Button ref={edit} onClick={() => { if (selected) setDraft({...selected}); }}>Edit request</Button>}
278
302
  >
279
303
  {draft
@@ -297,13 +321,23 @@ export function RecordManagement() {
297
321
  </Field>
298
322
  </Stack>
299
323
  </form>
300
- : selected && <DescriptionList items={[
324
+ : selected && <Stack gap="8"><DescriptionList items={[
301
325
  {id: "request-id", term: "Request ID", description: selected.id},
302
326
  {id: "owner", term: "Owner", description: selected.owner || "Unassigned"},
303
327
  {id: "status", term: "Status", description: <StatusIndicator {...statuses[selected.status]} />},
304
- ]} />}
328
+ ]} />
329
+ {activity && <Section title="Activity" headingLevel={3} headingSize="group">
330
+ <Table caption="Request activity" hideCaption density="compact">
331
+ <thead><tr><th scope="col">Date</th><th scope="col">Event</th></tr></thead>
332
+ <tbody>{activity.map(item => <tr key={item.date}>
333
+ <td><time dateTime={item.date}>{activityDate.format(new Date(item.date))}</time></td>
334
+ <td>{item.activity}</td>
335
+ </tr>)}</tbody>
336
+ </Table>
337
+ </Section>}
338
+ </Stack>}
305
339
  </Drawer>
306
- {notice && <Alert tone="success" announcement="polite">{notice}</Alert>}
340
+ <ToastRegion items={notice ? [notice] : []} onDismiss={() => setNotice(null)} />
307
341
  </Stack>
308
342
  </section>;
309
343
  }