I haven't looked at any code. The state table below comes from your description (happy path with ten rows only), so "Built?" is no wherever you said only the happy path exists. It is unknown for things a happy-path build may still cover by accident, like hover and focus.
State table: Invoices table
| State | Applies? | What the user sees | What they can do | Copy | Built? |
|---|---|---|---|---|---|
| Initial / idle | Yes (search box, before any query) | Full list with empty search | Type, filter | Placeholder: "Search by invoice number or customer" | Yes |
| First load | Yes | Skeleton rows matching the 10-row layout, with a ~200 ms delay before showing | Wait; filters and search disabled or queued | None, or a visually hidden "Loading invoices" | No |
| Refreshing (search, filter or page change with data shown) | Yes | Old rows dimmed, with a thin progress bar. Height stays fixed | Keep typing; cancel by changing the query | None | No |
| Loading more / page change | Yes | Pager shows a busy state on the clicked control; rows are replaced or dimmed | Click another page | None | No |
| Empty: first use | Yes | Illustration and message, no filters or pager | Primary action (create an invoice, or none if invoices are system-generated) | "No invoices yet. They'll appear here after your first billing cycle." | No |
| Empty: no results | Yes | Message that echoes the search and filters in use | "Clear search", "Clear filters" | "No invoices match "acme" with Status: Overdue." | No |
| Empty: no access | Yes | See permission denied | No | ||
| Success: 1, a few, many | Yes | 1 row, a partial last page, thousands of total results | Normal use | Correct plural: "1 invoice", "2,431 invoices" | Partly (only 10 rows) |
| Partial | Yes (list loaded but a row's PDF link or customer name failed) | Row shown with a "—" or inline warning | Retry that part | "Customer name unavailable" | No |
| Stale | Yes (a new invoice or payment status arrives elsewhere) | "Last updated" time, with an optional "Refresh" prompt | Refresh, keeping search, filters and page | "Updated 12 min ago. Refresh" | No |
| Recoverable error (list fetch fails or times out) | Yes | Error panel in the table body. Search and filter values are kept; old rows stay if present | Retry | "We couldn't load your invoices. Check your connection and try again." | No |
| Error on a later page or filter change | Yes | Previous rows kept, with an inline banner | Retry; go back | "Couldn't load page 3. Try again." | No |
| Validation error | Partly (date-range filter with from > to, amount min > max, search too long) | Inline message beside the field. The invalid value stays | Fix the value | "End date must be after start date." | No |
| Permission denied | Yes (401, 403) | 401 sends the user to sign in. 403 shows a "not allowed" panel | Sign in; request access | "You don't have access to invoices for this account. Ask an admin to grant access." | No |
| Not found | Partly (page beyond the last after a filter change, or a stale deep link) | Reset to page 1 with a notice | Go back to page 1 | "That page no longer exists. Showing page 1." | No |
| Offline | Yes | Banner. Cached rows stay, with a stale mark | Retry on reconnect; PDF button disabled | "You're offline. Showing the last loaded invoices." | No |
| Rate limited / maintenance (429, 503) | Yes | Panel with a retry-after time | Wait and retry | "Too many requests. Try again in 30 seconds." | No |
| Unexpected failure | Yes | Generic fallback panel with a reference ID | Retry, go back, contact support | "Something went wrong on our side. Reference: abc123." | No |
| Conflict | Mostly no (read-only list). Applies if the PDF is regenerated or the invoice is voided while the user is viewing it | Notice | Refresh | "This invoice was updated. Refresh to see the latest." | No |
| Default / hover / focus / pressed | Yes | Visible focus ring on search, filters, pager, row buttons | Keyboard use | n/a | Unknown |
| Selected / current | Yes (current page, active filters) | aria-current="page" on the page number. Filter chips for active filters |
Remove a filter chip | "Status: Overdue ✕" | Unknown |
| Disabled | Yes (Prev on page 1, Next on the last page, PDF when unavailable) | Greyed control with a tooltip or adjacent text saying why | Read the reason | "PDF not ready yet", "Available once the invoice is issued" | No |
| In progress: Download PDF | Yes | Per-row spinner, with only that row's button disabled | Continue using the rest of the table | "Preparing PDF…" | No |
| Success feedback: download | Yes | Browser download, plus a toast if it is slow or opens in a new tab | Dismiss | "Invoice INV-1042 downloaded." (announced via a polite live region) | No |
| Download failure | Yes | Row-level error, and a toast | Retry on that row | "Couldn't download INV-1042. Try again." | No |
| Destructive | No (read-only, nothing to delete) | n/a | n/a | n/a | n/a |
| Zero, one, many | Yes | See Success | Partly | ||
| Very long text | Yes (customer name, invoice reference) | Truncated with ellipsis and a tooltip or title for the full text. No horizontal page overflow | Hover or focus for the full text | n/a | No |
| Missing fields | Yes (no due date, null amount, no customer) | "—" in the cell, never "null" or "undefined" | No | ||
| Unusual data | Yes | Currency formatting by locale, negative amounts for credit notes, large totals, time zones, non-Latin names, RTL | "–$120.00 (credit)" | Unknown | |
| Different sizes | Yes | On narrow screens, a card layout or a horizontal scroll with a sticky first column. Zoomed text still works | Unknown | ||
| Returning with saved state | Yes | Search, filters and page persisted in the URL | Share and restore; clear all | "Clear all filters" | No |
| Concurrency | Yes | Race: a slower earlier response overwrites a newer one. Handle with abort or request IDs | No | ||
| Interruption (session expiry mid-use) | Yes | Re-auth prompt that keeps the query | Sign in again | "Your session expired. Sign in to continue." | No |
| Motion and display prefs | Yes | Skeleton shimmer respects reduced motion. Dark mode and high contrast contrast ratios | Unknown |
Prioritised list of what is missing
P0: blocks the task or shows wrong data
- Error states (list fetch, page change, 401/403, 429/503, unexpected). Right now a failed request probably leaves a blank or stuck table. Add one table-body error component with plain-language copy, retry, and a reference code. Keep the old rows and the user's search and filter values.
- Empty states, split by cause. "No invoices yet" and "No results for these filters" need different messages. The no-results state needs "Clear search" and "Clear filters".
- Download PDF failure and in-progress states. Disable only the clicked row's button, show a spinner, handle failure with a retry, and handle a missing or not-yet-generated PDF with a disabled button and an explanation. This is the one action on the page that matters.
- Request race conditions. Debounce search (~300 ms), cancel in-flight requests when the query changes, and ignore stale responses. Otherwise the results shown can fail to match the search box.
- Pagination boundaries. Disable Prev on the first page and Next on the last. Reset to page 1 when the search or filter changes. Handle a page that no longer exists after the data shrinks.
P1: significant UX and trust
6. Loading states. A skeleton on first load and a dimmed "refreshing" state on later loads. Keep the table height stable, and delay the indicator by ~200 ms so fast responses don't flash it.
7. Offline detection. Show a banner, keep the cached rows, and disable PDF downloads with a reason.
8. Filter validation. Inline errors for an inverted date range or amount range, with the entered values kept.
9. Disabled controls with reasons. No unexplained greyed-out buttons.
10. Content extremes. Truncate long names with a full-text alternative, show "—" for null fields, handle credit notes and negative amounts, and format currency and dates by locale and time zone.
11. Persist the query in the URL. Search, filters, and page should survive reload and be shareable, with a visible "Clear all".
P2: polish and robustness
12. Partial failures (a row's data or PDF link missing) with per-row retry.
13. Stale-data indicator ("Updated 12 min ago" and a Refresh action that keeps the user's place).
14. Session expiry mid-use that keeps the query and returns the user to it after sign-in.
15. Accessibility: a visible focus ring, aria-current on the current page, and a live region announcing result counts ("24 invoices found") and download success. Give each row's button a unique name ("Download PDF for INV-1042"), not just "Download PDF".
16. Responsive layout, reduced motion, dark mode and high contrast.
How to verify
Force each state on purpose, using DevTools throttling and offline mode plus a mock API. Return an empty array, 401, 403, 429, 500 and a timeout, a 100-character customer name, a null due date, and a response that arrives out of order. For each one, check the visual result, the keyboard path out, and what a screen reader announces.
If you can share the component code, I can mark the "Built?" column from the code instead of from your description.
Real run in an isolated folder with only this skill installed. Only the Skill and Read tools were enabled, so no command was run and no file was written. The scenario is fictional and the model had no access to the code. The build status in the table comes from the description, not from code.
它做什麼
大多數介面 bug 都出在沒人設計的狀態裡。這個 Skill 讓你一次只處理一個單元(一個清單、表單、表格、上傳或對話框),找出是什麼在改變它,再過五組狀態:資料生命週期(初始、載入、更新、載入更多、各種不同的空狀態、部分成功、過期)、錯誤與無法使用(可復原、驗證、無權限、找不到、離線、被限流、意外失敗、衝突)、互動狀態(懸停、聚焦、選取、停用、唯讀、進行中、成功、破壞性操作、拖曳)、內容極端情況(零個、一個與很多個、很長或缺失的文字、損壞的媒體、不尋常的資料)以及脈絡(首次使用、已儲存狀態、角色、並行、被打斷、偏好設定)。對每一種適用的狀態,它會問:使用者看到什麼、下一步能做什麼、怎麼離開這個狀態,以及具體的文字。
還涵蓋
怎麼寫狀態文案、測試時怎麼逐個強制出現每種狀態,以及一份參考檔,裡面有逐狀態的表格與針對清單、表單、按鈕、搜尋、上傳、對話框、圖表、通知與驗證的清單。輸出是一張狀態表,外加依優先順序排序的缺口清單。
適合什麼場景
發布前檢視一個介面,以及弄清「正常流程能跑通,其他全崩」的原因。
低風險:純指令檔,沒有腳本,不連網、不寫檔。它檢視的是你描述或貼上的內容;沒看到程式碼時,會把狀態標為「未知」而不是亂猜,試用中就是這樣。它不能替你強制觸發這些狀態,所以驗證步驟要你自己去跑。它為錯誤與空狀態建議的文案只是起點,需要結合你產品的語氣與當地語言品質來審閱。AIBars 原創(MIT)。已用一個虛構的發票表格試用過一次。