diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
index 439d801..e265012 100644
--- a/.github/workflows/checks.yml
+++ b/.github/workflows/checks.yml
@@ -24,4 +24,7 @@ jobs:
if: always()
with:
name: browser-screenshots
- path: docs/*.png
+ path: |
+ docs/*.png
+ docs/create-request/*.png
+ docs/create-request/*results.json
diff --git a/README.md b/README.md
index 3291719..74b0d80 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Support Desk Workbench
-A support queue for triaging requests, recording internal notes and keeping work between visits. Built with React, TypeScript and Vite; an optional local Node.js server adds SQLite snapshots.
+A support queue for creating and triaging requests, recording internal notes and keeping work between visits. Built with React, TypeScript and Vite; an optional local Node.js server adds SQLite snapshots. The create-request flow uses scoped Tailwind CSS utilities without Preflight.
[Live demo](https://flankero-dev.github.io/support-desk-workbench/) · [Checks](https://github.com/flankero-dev/support-desk-workbench/actions/workflows/checks.yml) · [Engineering decisions](docs/decisions.md)
@@ -10,6 +10,8 @@ The demo uses fictional customers and browser storage. It opens directly into a
## Review it in two minutes
+Choose **New request** in the queue. Submit an empty form to see field errors, then enter a title, customer, category and description. New requests start as Open, with Normal priority and no assignee. Success appears only after browser storage accepts the write. Current filters and sorting stay intact; the confirmation explains if they hide the new request and offers **View request**.
+
1. Search for **Maya** and open the checkout request. Change its status, add a note, then close it with **Escape**.
2. Reload. The URL restores the search; the edited request and note remain saved.
3. Clear the search, select **Open** and **Urgent**, name the filters and choose **Save view**. Clear the filters and reapply that view.
@@ -67,6 +69,8 @@ npm run test:browser
Tests use Node's built-in runner. Browser checks use the production build and cover filtering, pagination, saved views, editing, import/export, reset, keyboard focus, storage conflicts and server snapshots. CI runs these checks; the Pages deployment also checks the build before publishing. Browser screenshots are available as CI artifacts.
+Create-request design and review: [design specification](docs/create-request-design.md), [QA evidence and limitations](docs/create-request-qa.md). No Figma file or Figma handoff was produced. Additional browser checks cover field validation, ID collisions, record/UTF-8 limits, failed persistence, multi-tab conflicts, responsive layout and axe accessibility scans. To reproduce the optional pre-change CSS comparison, build the base revision separately and set `BASELINE_DIST=/path/to/base/dist` when running `node tests/create-request-review.mjs`.
+
## Boundaries
- The public demo stores requests in this browser and profile. Export before clearing site data or switching browsers.
diff --git a/docs/create-request-design.md b/docs/create-request-design.md
new file mode 100644
index 0000000..fc01c58
--- /dev/null
+++ b/docs/create-request-design.md
@@ -0,0 +1,64 @@
+# Create request — design specification
+
+## Design source and scope
+
+This is a specification of the implemented scenario, derived from the existing Support Desk Workbench interface. **No Figma document was created or opened, and no Figma handoff is claimed.** No connected Figma editing/workspace capability was available. The screenshots below are actual browser captures of the production build, not a substitute presented as a Figma file.
+
+One action is added to the queue heading: **New request**. It opens a native modal dialog. The existing queue, detail view, design language and storage model remain in use.
+
+## Geometry and tokens
+
+| Element | Specification |
+| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| Dialog | Existing white surface, 1 px `#d3ded4` border, 10 px radius; width `calc(100% - 32px)`, maximum 600 px, maximum height 90 vh; vertical scrolling when needed |
+| Backdrop | Existing `#142a2355` with 2 px blur |
+| Inner padding | 20 px below 640 px viewport width; 24 px at/above 640 px |
+| Grid | One column below 640 px; two equal columns at/above 640 px; 16 px gap; title/description span both columns |
+| Heading | Existing font stack; 23 px, weight 600, line-height 1.3, tracking -0.4 px |
+| Text | 14 px / 21 px, `#23332d`; secondary `#58695e`; labels 13 px, `#637369` |
+| Controls | Minimum 44 px high; existing font, 6 px radius, 8 px vertical / 10 px horizontal padding; textarea minimum 112 px, vertically resizable |
+| Control boundary | `#82948a`, approximately 3.21:1 against white; scoped to this dialog |
+| Invalid | `#9b4335` border and 13 px error text; persistent label, `aria-invalid`, linked error description |
+| Primary action | Existing `.primary`: `#174f42` background with white text; existing hover/focus treatment |
+| Focus | 3 px outline, 3 px offset: `#276752` normally, `#9b4335` on invalid fields; focused elements scroll into view |
+| Actions | 12 px gap; 44 px minimum buttons. Mobile actions remain within the dialog's scrollable content |
+| Footer | 24 px top margin, 16 px top padding, existing divider color; browser-only persistence notice |
+
+Tailwind's spacing scale uses rem, while this application's root is 14 px. Intentional pixel values avoid accidentally turning a 44 px target into 38.5 px. The responsive boundary is verified immediately below/at 640 px.
+
+## Fields
+
+| Label | Data field | Default | Validation |
+| ------------- | ------------- | --------------------------------------- | -------------------------------------------------------------------------- |
+| Title * | `title` | Empty | Non-whitespace; trimmed, 1–200 characters |
+| Customer * | `customer` | Empty | Non-whitespace; trimmed, 1–200 characters |
+| Category * | `category` | Empty; suggestions from current tickets | Non-whitespace; trimmed, 1–200 characters; new categories allowed |
+| Priority | `priority` | Normal | Existing priority enum |
+| Assignee | `assignee` | Unassigned | Existing workspace options, including imported names |
+| Description * | `description` | Empty | Non-whitespace; trimmed, 1–4000 characters; internal line breaks preserved |
+
+ID, status Open, timestamps and the initial history entry are generated, rather than additional user input. The whole candidate snapshot must still pass the existing importer, 500-record limit and 2 million UTF-8-byte limit.
+
+## States and behavior
+
+- **Default:** title receives initial focus; labels and required marks stay visible. Cancel, Close or Escape discard this unsubmitted draft. Reopening starts clean.
+- **Invalid:** submit shows specific messages and focuses the first invalid field. Errors update as that field is corrected. Enter in a text field submits; Enter in description inserts a newline.
+- **Persistence error:** the form stays open and retains all values. Failed storage, duplicate ID, capacity and another-tab conflicts produce actionable text. No success message or in-memory insert is committed for a rejected creation.
+- **Success:** focus moves to the confirmation heading. The title is the primary 23 px semibold heading in the summary card; the confirmation label is 16 px. The full, selectable ID follows customer/priority as labelled 12 px secondary text. Long titles and IDs wrap without truncation. It offers View request or Back to queue. Existing filters and sort are not silently cleared; a hidden result is explained. A matching result's page is selected using its actual filtered/sorted index.
+- **Loading/disabled:** no artificial loading delay or disabled-submit state is introduced. Creation is a synchronous browser-storage operation; there is no creation request to a server. Native controls and existing unrelated disabled buttons retain their behavior.
+- Tab/Shift+Tab wrap within the modal; native modality keeps the background inert. Closing restores the New request trigger; View request opens the existing detail dialog.
+
+## CSS integration
+
+The Vite Tailwind plugin compiles `tw:`-prefixed utilities used only by App and CreateRequestDialog. Theme and utilities are imported separately; **Preflight is omitted**. Utilities are unlayered so existing unlayered element rules do not automatically outrank them. One scoped control-border rule matches the legacy `input:not([type=checkbox])` specificity. The invalid attribute utility is more specific. No global font, heading, input or button reset is added.
+
+## Browser comparison
+
+
+
+
+
+
+
+
+Measured widths, column counts, target heights and overflow checks are recorded in [browser results](create-request/browser-results.json). On small screens, the 90 vh dialog intentionally scrolls vertically; screenshots of invalid fields can therefore show the focused field instead of the header. This is not a claim that the full form fits without scrolling.
diff --git a/docs/create-request-qa.md b/docs/create-request-qa.md
new file mode 100644
index 0000000..04e369e
--- /dev/null
+++ b/docs/create-request-qa.md
@@ -0,0 +1,100 @@
+# Create request — QA and review
+
+Date: 16 October 2026. Base: main `82614209f1d50933fe938ee871b41e488f1f1f8d` (tree `4c92e2835805b48adcc12f3868528015c0bcbd40`).
+
+## Recommendation
+
+**READY for owner review, conditional on green GitHub Checks for the final PR head.** No merge or public deployment was performed. This is a bounded feature, not an architecture migration.
+
+## Verified evidence
+
+| Check | Status | Evidence |
+| ---------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| TypeScript strict, browser and server configurations | VERIFIED | `npm run build` |
+| ESLint and formatting | VERIFIED | `npm run lint`, `npm run format:check` |
+| Existing and new unit/integration tests | VERIFIED | 48 tests, including 7 creation-domain tests and a creation/SQLite/revision round trip |
+| Existing browser regression suite | VERIFIED | `tests/browser-smoke.mjs` against the actual production build |
+| Creation browser suite | VERIFIED | 8 scenario groups in `tests/create-request.browser.mjs`; [results](create-request/browser-results.json) |
+| Independent production review pass | VERIFIED | Separate black-box assertions in `tests/create-request-review.mjs`; [results](create-request/review-results.json) |
+| Empty/whitespace fields, enum and length boundaries | VERIFIED | Domain tests and browser invalid/first-error focus checks |
+| ID collision | VERIFIED | Domain collision and browser stubbed UUID; original record/copy unchanged |
+| 499 → 500 and rejection of request 501 | VERIFIED | Domain boundary and browser capacity error |
+| 2 MB UTF-8 limit | VERIFIED | Domain multibyte growth and browser rejection with all description text retained |
+| Persistence, quota failure and retry | VERIFIED | Reload retains creation/history; failed write changes neither saved copy nor queue; retry inserts once |
+| Two tabs and storage-event timing | VERIFIED | Peer copy protected, stale draft retained; immediate read-check also catches a change without a delivered event |
+| API revision conflict and SQLite restart | VERIFIED | Existing PUT snapshot route accepts creation; old ETag returns 412, duplicate document returns 400; revision/data survive restart |
+| Public/static-host behavior | VERIFIED | Production assets served under a non-loopback test origin; creation + reload make zero API requests |
+| Desktop/mobile layout | VERIFIED | 320, 390, 639, 640, 768, 1024, 1440 px; dialog/page horizontal overflow absent; new controls at least 44 px high |
+| Keyboard and focus | VERIFIED | Initial title focus, Tab/Shift+Tab containment, first invalid focus, Enter/newline, Escape, cancel/reopen, success heading and return focus |
+| Accessibility scan | VERIFIED within scan scope | Five default/invalid/success desktop/mobile axe scans, no violations. Some native-control contrast checks marked incomplete by axe; separate computed-color checks verify text >=4.5:1 and control boundaries >=3:1 |
+| Existing CSS cascade | VERIFIED | Computed typography/colors/padding/borders for 8 unchanged interface selectors equal the separately built base revision |
+| Literal hostile text | VERIFIED | HTML-like title remains text, no injected image or handler execution |
+| Corrupt-data recovery | VERIFIED | Creation works from the existing sample fallback; resulting saved snapshot is valid and reloadable |
+
+The additional review pass uses the interface and production assets rather than asserting React state or Tailwind class strings. It was performed as a separate adversarial review by the implementation agent; it is **not an external human review**. Automated checks are supplemented by visual inspection of actual desktop/mobile captures.
+
+## Regressions exercised
+
+The unchanged browser smoke suite covers URL filters and sort, pagination, cross-page selection/bulk updates, ticket editing, notes/history, focus when an edited ticket leaves the filter, saved-view creation/apply/reload/duplicates/removal/two-tab and corrupt-storage behavior, JSON export/import/file restore, reset, long imported names, an assignee named All, large histories, size-rejected edits, local server snapshot refresh/save/load, stale revisions, unavailable Vite API and static-host controls. Creation tests additionally round-trip a created record, preserve a saved view and explain a filtered-out new request.
+
+## Limited UX polish review (16 October 2026)
+
+Only success-screen hierarchy and invalid-field focus color changed. The request title is now 23 px/600, ahead of the full labelled 12 px identifier. UUID generation, persistence, schema, import/export and request navigation are unchanged. Invalid keyboard focus uses the same red as the error border while retaining the existing solid 3 px outline and 3 px offset; valid controls retain green focus.
+
+- **VERIFIED:** all 48 unit/integration tests, strict TypeScript, ESLint, formatting, production build and all three production-browser scripts rerun successfully.
+- **VERIFIED:** separate black-box polish assertions at 320, 390, 639, 640 and 1440 px. A 200-character unbroken title and a long Ukrainian title wrap without horizontal overflow; the entire ID remains rendered, the saved record survives reload unchanged, and keyboard Tab/Enter opens the same existing detail record. Escape/reopen starts a clean form.
+- **VERIFIED:** computed invalid border/outline colors match, outline remains 3 px with 3 px offset, valid focus remains green. Seven axe scans have zero violations; minimum measured text contrast is 4.86:1. Eight existing interface selectors still match the main-branch baseline.
+- **VERIFIED:** desktop/mobile screenshots inspected, including long titles and focused errors. Existing regression coverage listed below passed again, including multi-tab, limits, import/export and API revision conflicts.
+- **UNVERIFIED:** external human review, human screen-reader sessions, Safari/Firefox and physical mobile devices. This separate review pass was performed by the implementation agent, not an independent person.
+
+[Desktop success](create-request/desktop-success.png) · [Mobile success](create-request/mobile-success.png) · [Desktop keyboard error](create-request/polish-1440-invalid.png) · [Mobile keyboard error](create-request/polish-390-invalid.png) · [Long title desktop](create-request/polish-1440-success.png) · [Long title mobile](create-request/polish-390-success.png). Machine-readable measurements are in `review-results.json` under `polish`.
+
+GitHub Actions must also pass for the new remote HEAD; the PR description records that exact SHA and run links after completion. No merge or deployment is authorized by this review.
+
+## Findings corrected during review
+
+1. Initial browser focus selected Close instead of Title; title is now focused after showModal.
+2. Inline errors were changing the input's accessible name; each field now has a stable labelled-by span and a separately described error.
+3. Native Tab boundary behavior was inconsistent with the intended contained sequence; explicit boundary wrapping covers both form and success states.
+4. Tailwind rem sizing would shrink controls in this 14 px-root application; intentional pixel values retain 44 px targets and existing spacing.
+5. Existing low-contrast input borders were unsuitable for the new controls; a scoped >=3:1 boundary was added without resetting the rest of the app.
+
+A failing draft test compared JSON string key order after canonical import, and a focus test selected a hidden saved-views dialog instead of the open dialog. These test defects were corrected; they are not represented as product fixes.
+
+## Limitations and remaining verification
+
+| Item | Status | Practical implication |
+| ------------------------------------------------------------ | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Real Figma file/handoff | UNVERIFIED / unavailable | [Exact design specification](create-request-design.md) and screenshots are provided; no Figma experience is claimed |
+| Safari, Firefox, physical mobile browsers, virtual keyboard | UNVERIFIED | Local browser verification uses Chromium 153; CI uses Playwright's installed Chromium |
+| NVDA/VoiceOver or another human screen-reader session | UNVERIFIED | Semantics, linked errors, focus and axe checked; no certified accessibility claim |
+| Browser quota/private mode/power loss | ENVIRONMENT DEPENDENT | Quota failures are simulated; storage durability and limits remain browser-specific |
+| Truly simultaneous multi-tab writes between read and setItem | ENVIRONMENT DEPENDENT | Existing localStorage optimistic guard is retained; it is not a transactional cross-tab compare-and-swap. Local API ETags/SQLite provide transactional revision checks |
+| GitHub Actions on final head | ENVIRONMENT DEPENDENT until completed | Consult the PR Checks tab; local results do not substitute for a successful remote run |
+| Next.js example | NOT STARTED | Requires a separate scenario and approval |
+
+No unresolved FAILED product check is being hidden. UNVERIFIED items above remain outside the demonstrated scope; do not describe this as “ALL PASSED.”
+
+## Architecture decisions
+
+- `appendRequest` is a pure typed domain operation. It trims user input, appends a history event, checks collisions and validates the candidate using the existing import/export schema. The original records are not mutated.
+- Existing `setTickets` gains an opt-in persistence requirement. Creation checks the latest saved string and writes before committing React state, so failure keeps the form and queue unchanged. Existing edit/import in-tab fallback behavior is preserved.
+- UUID-based IDs avoid depending on numeric seed IDs or imported ID formats. A detected collision rejects the operation rather than overwriting data.
+- A synchronous local write needs neither an invented network request nor a spinner. The optional server still receives the full snapshot only when the user explicitly saves it through Server snapshot, with the existing ETag contract.
+- Native dialog and small local form state keep this feature independent of authentication, global state libraries or new routing. Tailwind is confined to the new flow.
+
+## Technologies honestly demonstrated
+
+React component/state/event handling; strict TypeScript domain types; Tailwind/Vite integration and cascade control; responsive forms; native dialog focus behavior; accessible labels/validation; localStorage failure handling; Playwright and axe browser checks; Node/SQLite snapshot integration and optimistic revisions; GitHub CI configuration. **Not demonstrated by this change:** Figma handoff, Next.js, Redux, commercial experience, production authentication or cloud server persistence.
+
+## Interview questions
+
+1. Why does creation require persistence before updating React state, while existing edits allow an in-tab fallback?
+2. How do validation, duplicate IDs and UTF-8 snapshot limits differ, and why validate the entire candidate snapshot?
+3. Why omit Tailwind Preflight and use prefixed utilities? How do CSS layers and selector specificity affect legacy styles?
+4. What should happen when the created ticket is hidden by current filters or appears on another page?
+5. What protection does localStorage's optimistic guard provide, and how is it weaker than ETag/SQLite revision checking?
+
+
+
+
diff --git a/docs/create-request/before-desktop.png b/docs/create-request/before-desktop.png
new file mode 100644
index 0000000..4e20dfd
Binary files /dev/null and b/docs/create-request/before-desktop.png differ
diff --git a/docs/create-request/before-mobile.png b/docs/create-request/before-mobile.png
new file mode 100644
index 0000000..de13789
Binary files /dev/null and b/docs/create-request/before-mobile.png differ
diff --git a/docs/create-request/browser-results.json b/docs/create-request/browser-results.json
new file mode 100644
index 0000000..a73eba0
--- /dev/null
+++ b/docs/create-request/browser-results.json
@@ -0,0 +1,159 @@
+{
+ "results": [
+ {
+ "name": "keyboard, negative validation, successful save, Enter and focus restoration",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "current filters, saved view and sort survive creation; reset and JSON round-trip",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "quota error preserves all form fields and memory, then retry creates only once",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "another tab's creation is protected and unsaved draft survives conflict",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "immediate storage recheck works even without a delivered storage event",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "UUID collision never overwrites the existing record",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "500-record cap preserves the existing workspace and draft",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "desktop/mobile layout, minimum targets and modal keyboard containment",
+ "status": "VERIFIED"
+ }
+ ],
+ "measurements": [
+ {
+ "viewport": 320,
+ "width": 288,
+ "overflow": false,
+ "pageOverflow": false,
+ "fieldHeights": [
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44
+ ],
+ "columns": "246px"
+ },
+ {
+ "viewport": 390,
+ "width": 358,
+ "overflow": false,
+ "pageOverflow": false,
+ "fieldHeights": [
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44
+ ],
+ "columns": "316px"
+ },
+ {
+ "viewport": 639,
+ "width": 600,
+ "overflow": false,
+ "pageOverflow": false,
+ "fieldHeights": [
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44
+ ],
+ "columns": "558px"
+ },
+ {
+ "viewport": 640,
+ "width": 600,
+ "overflow": false,
+ "pageOverflow": false,
+ "fieldHeights": [
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44
+ ],
+ "columns": "267px 267px"
+ },
+ {
+ "viewport": 768,
+ "width": 600,
+ "overflow": false,
+ "pageOverflow": false,
+ "fieldHeights": [
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44
+ ],
+ "columns": "267px 267px"
+ },
+ {
+ "viewport": 1024,
+ "width": 600,
+ "overflow": false,
+ "pageOverflow": false,
+ "fieldHeights": [
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44
+ ],
+ "columns": "267px 267px"
+ },
+ {
+ "viewport": 1440,
+ "width": 600,
+ "overflow": false,
+ "pageOverflow": false,
+ "fieldHeights": [
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44,
+ 44
+ ],
+ "columns": "267px 267px"
+ }
+ ],
+ "errors": [],
+ "browser": "153.0.8010.0"
+}
\ No newline at end of file
diff --git a/docs/create-request/desktop-default.png b/docs/create-request/desktop-default.png
new file mode 100644
index 0000000..0d58238
Binary files /dev/null and b/docs/create-request/desktop-default.png differ
diff --git a/docs/create-request/desktop-invalid.png b/docs/create-request/desktop-invalid.png
new file mode 100644
index 0000000..49bddb3
Binary files /dev/null and b/docs/create-request/desktop-invalid.png differ
diff --git a/docs/create-request/desktop-success.png b/docs/create-request/desktop-success.png
new file mode 100644
index 0000000..ba6d94f
Binary files /dev/null and b/docs/create-request/desktop-success.png differ
diff --git a/docs/create-request/mobile-default.png b/docs/create-request/mobile-default.png
new file mode 100644
index 0000000..184a010
Binary files /dev/null and b/docs/create-request/mobile-default.png differ
diff --git a/docs/create-request/mobile-invalid.png b/docs/create-request/mobile-invalid.png
new file mode 100644
index 0000000..095ef54
Binary files /dev/null and b/docs/create-request/mobile-invalid.png differ
diff --git a/docs/create-request/mobile-success.png b/docs/create-request/mobile-success.png
new file mode 100644
index 0000000..f28df47
Binary files /dev/null and b/docs/create-request/mobile-success.png differ
diff --git a/docs/create-request/polish-1440-invalid.png b/docs/create-request/polish-1440-invalid.png
new file mode 100644
index 0000000..eb930f7
Binary files /dev/null and b/docs/create-request/polish-1440-invalid.png differ
diff --git a/docs/create-request/polish-1440-success.png b/docs/create-request/polish-1440-success.png
new file mode 100644
index 0000000..11ec933
Binary files /dev/null and b/docs/create-request/polish-1440-success.png differ
diff --git a/docs/create-request/polish-390-invalid.png b/docs/create-request/polish-390-invalid.png
new file mode 100644
index 0000000..29a6b90
Binary files /dev/null and b/docs/create-request/polish-390-invalid.png differ
diff --git a/docs/create-request/polish-390-success.png b/docs/create-request/polish-390-success.png
new file mode 100644
index 0000000..86bb740
Binary files /dev/null and b/docs/create-request/polish-390-success.png differ
diff --git a/docs/create-request/queue-desktop.png b/docs/create-request/queue-desktop.png
new file mode 100644
index 0000000..9d1c6be
Binary files /dev/null and b/docs/create-request/queue-desktop.png differ
diff --git a/docs/create-request/review-results.json b/docs/create-request/review-results.json
new file mode 100644
index 0000000..937d502
--- /dev/null
+++ b/docs/create-request/review-results.json
@@ -0,0 +1,1025 @@
+{
+ "browser": "153.0.8010.0",
+ "accessibility": [
+ {
+ "state": "desktop default",
+ "violations": [],
+ "incomplete": [
+ "color-contrast"
+ ]
+ },
+ {
+ "state": "desktop invalid",
+ "violations": [],
+ "incomplete": [
+ "color-contrast"
+ ]
+ },
+ {
+ "state": "desktop success",
+ "violations": [],
+ "incomplete": [
+ "color-contrast"
+ ]
+ },
+ {
+ "state": "mobile default",
+ "violations": [],
+ "incomplete": []
+ },
+ {
+ "state": "mobile invalid",
+ "violations": [],
+ "incomplete": [
+ "color-contrast"
+ ]
+ },
+ {
+ "state": "polish 390 long-title success",
+ "violations": [],
+ "incomplete": [
+ "color-contrast"
+ ]
+ },
+ {
+ "state": "polish 1440 long-title success",
+ "violations": [],
+ "incomplete": [
+ "color-contrast"
+ ]
+ }
+ ],
+ "contrast": [
+ {
+ "state": "desktop default",
+ "minimumText": 5.015754885495116,
+ "minimumControlBorder": 3.205068171626354,
+ "measured": [
+ {
+ "tag": "H2",
+ "text": "New request",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "P",
+ "text": "Add a request to this browser’s workspace. Fields marked * are require",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Close ×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "Title *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-title-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Customer *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-customer-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Category *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-category-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Priority",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-priority-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Assignee",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-assignee-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Description *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "TEXTAREA",
+ "text": "create-description-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "0/4000 characters",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "P",
+ "text": "Status: Open · Browser only",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Cancel",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Create request",
+ "textRatio": 9.40230837163695
+ }
+ ]
+ },
+ {
+ "state": "desktop invalid",
+ "minimumText": 5.015754885495116,
+ "minimumControlBorder": 3.205068171626354,
+ "measured": [
+ {
+ "tag": "H2",
+ "text": "New request",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "P",
+ "text": "Add a request to this browser’s workspace. Fields marked * are require",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Close ×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "Title *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-title-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a title.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Customer *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-customer-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a customer.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Category *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-category-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a category.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Priority",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-priority-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Assignee",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-assignee-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Description *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "TEXTAREA",
+ "text": "create-description-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a description.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "0/4000 characters",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "P",
+ "text": "Status: Open · Browser only",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Cancel",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Create request",
+ "textRatio": 9.40230837163695
+ }
+ ]
+ },
+ {
+ "state": "desktop success",
+ "minimumText": 4.859231016510143,
+ "minimumControlBorder": null,
+ "measured": [
+ {
+ "tag": "H2",
+ "text": "Request created",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "P",
+ "text": "Saved in this browser. New requests start as Open.",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "H3",
+ "text": "",
+ "textRatio": 12.322613650434448
+ },
+ {
+ "tag": "P",
+ "text": "Customer · Normal",
+ "textRatio": 5.422357696790662
+ },
+ {
+ "tag": "P",
+ "text": "Request ID: REQ-8fd88776-3978-4ca8-a184-4d496c7c0ce6",
+ "textRatio": 5.422357696790662
+ },
+ {
+ "tag": "SPAN",
+ "text": "REQ-8fd88776-3978-4ca8-a184-4d496c7c0ce6",
+ "textRatio": 4.859231016510143
+ },
+ {
+ "tag": "P",
+ "text": "Your queue is on the page containing this request, with the current so",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Back to queue",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "BUTTON",
+ "text": "View request",
+ "textRatio": 9.40230837163695
+ }
+ ]
+ },
+ {
+ "state": "mobile default",
+ "minimumText": 5.015754885495116,
+ "minimumControlBorder": 3.205068171626354,
+ "measured": [
+ {
+ "tag": "H2",
+ "text": "New request",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "P",
+ "text": "Add a request to this browser’s workspace. Fields marked * are require",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Close ×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "Title *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-title-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Customer *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-customer-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Category *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-category-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Priority",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-priority-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Assignee",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-assignee-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Description *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "TEXTAREA",
+ "text": "create-description-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "0/4000 characters",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "P",
+ "text": "Status: Open · Browser only",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Cancel",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Create request",
+ "textRatio": 9.40230837163695
+ }
+ ]
+ },
+ {
+ "state": "mobile invalid",
+ "minimumText": 5.015754885495116,
+ "minimumControlBorder": 3.205068171626354,
+ "measured": [
+ {
+ "tag": "H2",
+ "text": "New request",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "P",
+ "text": "Add a request to this browser’s workspace. Fields marked * are require",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Close ×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "×",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "SPAN",
+ "text": "Title *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-title-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a title.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Customer *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-customer-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a customer.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Category *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "INPUT",
+ "text": "create-category-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a category.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Priority",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-priority-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Assignee",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "SELECT",
+ "text": "create-assignee-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 3.205068171626354
+ },
+ {
+ "tag": "SPAN",
+ "text": "Description *",
+ "textRatio": 5.015754885495116
+ },
+ {
+ "tag": "TEXTAREA",
+ "text": "create-description-label",
+ "textRatio": 13.266890495436922,
+ "borderRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "Enter a description.",
+ "textRatio": 6.465551692567027
+ },
+ {
+ "tag": "SPAN",
+ "text": "0/4000 characters",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "P",
+ "text": "Status: Open · Browser only",
+ "textRatio": 5.837870749755676
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Cancel",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Create request",
+ "textRatio": 9.40230837163695
+ }
+ ]
+ },
+ {
+ "state": "polish 390 long-title success",
+ "minimumText": 4.859231016510143,
+ "minimumControlBorder": null,
+ "measured": [
+ {
+ "tag": "H2",
+ "text": "Request created",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "P",
+ "text": "Saved in this browser. New requests start as Open.",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "H3",
+ "text": "Довга назва заявки Довга назва заявки Довга назва заявки Довга назва з",
+ "textRatio": 12.322613650434448
+ },
+ {
+ "tag": "P",
+ "text": "Customer · Normal",
+ "textRatio": 5.422357696790662
+ },
+ {
+ "tag": "P",
+ "text": "Request ID: REQ-c1653e91-da2c-485d-8a76-5ac4e9fce56d",
+ "textRatio": 5.422357696790662
+ },
+ {
+ "tag": "SPAN",
+ "text": "REQ-c1653e91-da2c-485d-8a76-5ac4e9fce56d",
+ "textRatio": 4.859231016510143
+ },
+ {
+ "tag": "P",
+ "text": "Your queue is on the page containing this request, with the current so",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Back to queue",
+ "textRatio": 9.573371520139666
+ },
+ {
+ "tag": "BUTTON",
+ "text": "View request",
+ "textRatio": 9.40230837163695
+ }
+ ]
+ },
+ {
+ "state": "polish 1440 long-title success",
+ "minimumText": 4.859231016510143,
+ "minimumControlBorder": null,
+ "measured": [
+ {
+ "tag": "H2",
+ "text": "Request created",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "P",
+ "text": "Saved in this browser. New requests start as Open.",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "H3",
+ "text": "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
+ "textRatio": 12.322613650434448
+ },
+ {
+ "tag": "P",
+ "text": "Customer · Normal",
+ "textRatio": 5.422357696790662
+ },
+ {
+ "tag": "P",
+ "text": "Request ID: REQ-a8cda6b3-4620-47a5-9095-7696c438860e",
+ "textRatio": 5.422357696790662
+ },
+ {
+ "tag": "SPAN",
+ "text": "REQ-a8cda6b3-4620-47a5-9095-7696c438860e",
+ "textRatio": 4.859231016510143
+ },
+ {
+ "tag": "P",
+ "text": "Your queue is on the page containing this request, with the current so",
+ "textRatio": 13.266890495436922
+ },
+ {
+ "tag": "BUTTON",
+ "text": "Back to queue",
+ "textRatio": 10.56097848234087
+ },
+ {
+ "tag": "BUTTON",
+ "text": "View request",
+ "textRatio": 9.40230837163695
+ }
+ ]
+ }
+ ],
+ "baselineCSS": {
+ "status": "VERIFIED",
+ "before": {
+ "h1": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "32px",
+ "fontWeight": "600",
+ "lineHeight": "38.4px",
+ "color": "rgb(35, 51, 45)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(35, 51, 45)",
+ "padding": "0px"
+ },
+ ".topbar": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "400",
+ "lineHeight": "19.5px",
+ "color": "rgb(83, 104, 90)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(83, 104, 90) rgb(83, 104, 90) rgb(226, 231, 225)",
+ "padding": "0px"
+ },
+ ".metrics strong": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "29px",
+ "fontWeight": "500",
+ "lineHeight": "40.6px",
+ "color": "rgb(35, 51, 45)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(35, 51, 45)",
+ "padding": "0px"
+ },
+ ".request-title": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "14px",
+ "fontWeight": "500",
+ "lineHeight": "21px",
+ "color": "rgb(43, 66, 53)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(43, 66, 53)",
+ "padding": "0px"
+ },
+ "thead": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "12px",
+ "fontWeight": "400",
+ "lineHeight": "18px",
+ "color": "rgb(100, 112, 92)",
+ "backgroundColor": "rgb(248, 250, 246)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(100, 112, 92)",
+ "padding": "0px"
+ },
+ ".status": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "400",
+ "lineHeight": "19.5px",
+ "color": "rgb(78, 102, 50)",
+ "backgroundColor": "rgb(244, 247, 237)",
+ "borderRadius": "4px",
+ "borderColor": "rgb(227, 233, 213)",
+ "padding": "3px 7px"
+ },
+ ".sort-label select": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "400",
+ "lineHeight": "normal",
+ "color": "rgb(35, 51, 45)",
+ "backgroundColor": "rgb(255, 255, 255)",
+ "borderRadius": "6px",
+ "borderColor": "rgb(229, 233, 227)",
+ "padding": "5px 23px 5px 7px"
+ },
+ ".pagination button": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "500",
+ "lineHeight": "19.5px",
+ "color": "rgb(52, 66, 59)",
+ "backgroundColor": "rgb(255, 255, 255)",
+ "borderRadius": "6px",
+ "borderColor": "rgb(213, 221, 215)",
+ "padding": "5px 10px"
+ }
+ },
+ "after": {
+ "h1": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "32px",
+ "fontWeight": "600",
+ "lineHeight": "38.4px",
+ "color": "rgb(35, 51, 45)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(35, 51, 45)",
+ "padding": "0px"
+ },
+ ".topbar": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "400",
+ "lineHeight": "19.5px",
+ "color": "rgb(83, 104, 90)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(83, 104, 90) rgb(83, 104, 90) rgb(226, 231, 225)",
+ "padding": "0px"
+ },
+ ".metrics strong": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "29px",
+ "fontWeight": "500",
+ "lineHeight": "40.6px",
+ "color": "rgb(35, 51, 45)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(35, 51, 45)",
+ "padding": "0px"
+ },
+ ".request-title": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "14px",
+ "fontWeight": "500",
+ "lineHeight": "21px",
+ "color": "rgb(43, 66, 53)",
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(43, 66, 53)",
+ "padding": "0px"
+ },
+ "thead": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "12px",
+ "fontWeight": "400",
+ "lineHeight": "18px",
+ "color": "rgb(100, 112, 92)",
+ "backgroundColor": "rgb(248, 250, 246)",
+ "borderRadius": "0px",
+ "borderColor": "rgb(100, 112, 92)",
+ "padding": "0px"
+ },
+ ".status": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "400",
+ "lineHeight": "19.5px",
+ "color": "rgb(78, 102, 50)",
+ "backgroundColor": "rgb(244, 247, 237)",
+ "borderRadius": "4px",
+ "borderColor": "rgb(227, 233, 213)",
+ "padding": "3px 7px"
+ },
+ ".sort-label select": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "400",
+ "lineHeight": "normal",
+ "color": "rgb(35, 51, 45)",
+ "backgroundColor": "rgb(255, 255, 255)",
+ "borderRadius": "6px",
+ "borderColor": "rgb(229, 233, 227)",
+ "padding": "5px 23px 5px 7px"
+ },
+ ".pagination button": {
+ "fontFamily": "\"DM Sans\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
+ "fontSize": "13px",
+ "fontWeight": "500",
+ "lineHeight": "19.5px",
+ "color": "rgb(52, 66, 59)",
+ "backgroundColor": "rgb(255, 255, 255)",
+ "borderRadius": "6px",
+ "borderColor": "rgb(213, 221, 215)",
+ "padding": "5px 10px"
+ }
+ }
+ },
+ "checks": [
+ {
+ "name": "Enter first error and literal hostile text",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "browser UTF-8 2MB rejection preserves draft and saved copy",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "creation from existing corrupt-data recovery fallback",
+ "status": "VERIFIED"
+ },
+ {
+ "name": "static-host creation and reload with zero API requests",
+ "status": "VERIFIED"
+ }
+ ],
+ "polish": [
+ {
+ "width": 320,
+ "status": "VERIFIED",
+ "focusStyle": {
+ "active": true,
+ "visible": true,
+ "border": "rgb(155, 67, 53)",
+ "outline": "rgb(155, 67, 53)",
+ "width": "3px",
+ "offset": "3px",
+ "style": "solid"
+ },
+ "hierarchy": {
+ "titleSize": 23,
+ "idSize": 12,
+ "weight": 600,
+ "titleBeforeId": true,
+ "overflow": false,
+ "titleOverflow": false
+ },
+ "fullIdPreserved": true,
+ "reload": true,
+ "keyboardViewRequest": true
+ },
+ {
+ "width": 390,
+ "status": "VERIFIED",
+ "focusStyle": {
+ "active": true,
+ "visible": true,
+ "border": "rgb(155, 67, 53)",
+ "outline": "rgb(155, 67, 53)",
+ "width": "3px",
+ "offset": "3px",
+ "style": "solid"
+ },
+ "hierarchy": {
+ "titleSize": 23,
+ "idSize": 12,
+ "weight": 600,
+ "titleBeforeId": true,
+ "overflow": false,
+ "titleOverflow": false
+ },
+ "fullIdPreserved": true,
+ "reload": true,
+ "keyboardViewRequest": true
+ },
+ {
+ "width": 639,
+ "status": "VERIFIED",
+ "focusStyle": {
+ "active": true,
+ "visible": true,
+ "border": "rgb(155, 67, 53)",
+ "outline": "rgb(155, 67, 53)",
+ "width": "3px",
+ "offset": "3px",
+ "style": "solid"
+ },
+ "hierarchy": {
+ "titleSize": 23,
+ "idSize": 12,
+ "weight": 600,
+ "titleBeforeId": true,
+ "overflow": false,
+ "titleOverflow": false
+ },
+ "fullIdPreserved": true,
+ "reload": true,
+ "keyboardViewRequest": true
+ },
+ {
+ "width": 640,
+ "status": "VERIFIED",
+ "focusStyle": {
+ "active": true,
+ "visible": true,
+ "border": "rgb(155, 67, 53)",
+ "outline": "rgb(155, 67, 53)",
+ "width": "3px",
+ "offset": "3px",
+ "style": "solid"
+ },
+ "hierarchy": {
+ "titleSize": 23,
+ "idSize": 12,
+ "weight": 600,
+ "titleBeforeId": true,
+ "overflow": false,
+ "titleOverflow": false
+ },
+ "fullIdPreserved": true,
+ "reload": true,
+ "keyboardViewRequest": true
+ },
+ {
+ "width": 1440,
+ "status": "VERIFIED",
+ "focusStyle": {
+ "active": true,
+ "visible": true,
+ "border": "rgb(155, 67, 53)",
+ "outline": "rgb(155, 67, 53)",
+ "width": "3px",
+ "offset": "3px",
+ "style": "solid"
+ },
+ "hierarchy": {
+ "titleSize": 23,
+ "idSize": 12,
+ "weight": 600,
+ "titleBeforeId": true,
+ "overflow": false,
+ "titleOverflow": false
+ },
+ "fullIdPreserved": true,
+ "reload": true,
+ "keyboardViewRequest": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/create-request/save-error.png b/docs/create-request/save-error.png
new file mode 100644
index 0000000..efdecc8
Binary files /dev/null and b/docs/create-request/save-error.png differ
diff --git a/docs/create-request/tab-conflict.png b/docs/create-request/tab-conflict.png
new file mode 100644
index 0000000..70eb825
Binary files /dev/null and b/docs/create-request/tab-conflict.png differ
diff --git a/package-lock.json b/package-lock.json
index 03c825c..96ef66b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,9 @@
"react-dom": "^19.2.6"
},
"devDependencies": {
+ "@axe-core/playwright": "^4.11.1",
"@eslint/js": "^10.0.1",
+ "@tailwindcss/vite": "^4.3.0",
"@types/node": "^24.12.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
@@ -23,11 +25,25 @@
"globals": "^17.6.0",
"playwright": "^1.58.2",
"prettier": "3.9.10",
+ "tailwindcss": "^4.3.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.59.2",
"vite": "^8.0.12"
}
},
+ "node_modules/@axe-core/playwright": {
+ "version": "4.11.1",
+ "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.1.tgz",
+ "integrity": "sha512-mKEfoUIB1MkVTht0BGZFXtSAEKXMJoDkyV5YZ9jbBmZCcWDz71tegNsdTkIN8zc/yMi5Gm2kx7Z5YQ9PfWNAWw==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "axe-core": "~4.11.1"
+ },
+ "peerDependencies": {
+ "playwright-core": ">= 1.0.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
@@ -839,6 +855,278 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@tailwindcss/node": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz",
+ "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "^5.21.0",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.32.0",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.3.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz",
+ "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.3.0",
+ "@tailwindcss/oxide-darwin-arm64": "4.3.0",
+ "@tailwindcss/oxide-darwin-x64": "4.3.0",
+ "@tailwindcss/oxide-freebsd-x64": "4.3.0",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.3.0",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.3.0",
+ "@tailwindcss/oxide-linux-x64-musl": "4.3.0",
+ "@tailwindcss/oxide-wasm32-wasi": "4.3.0",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.3.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz",
+ "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz",
+ "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz",
+ "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz",
+ "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz",
+ "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz",
+ "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz",
+ "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz",
+ "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz",
+ "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz",
+ "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.10.0",
+ "@emnapi/runtime": "^1.10.0",
+ "@emnapi/wasi-threads": "^1.2.1",
+ "@napi-rs/wasm-runtime": "^1.1.4",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
+ "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz",
+ "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
+ "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.3.0",
+ "@tailwindcss/oxide": "4.3.0",
+ "tailwindcss": "4.3.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7 || ^8"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
@@ -1210,6 +1498,16 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/axe-core": {
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz",
+ "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
@@ -1372,6 +1670,20 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/enhanced-resolve": {
+ "version": "5.26.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz",
+ "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -1718,6 +2030,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/hermes-estree": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
@@ -1785,6 +2104,16 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -2150,6 +2479,16 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
"node_modules/minimatch": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
@@ -2515,6 +2854,27 @@
"node": ">=0.10.0"
}
},
+ "node_modules/tailwindcss": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz",
+ "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
"node_modules/tinyglobby": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
diff --git a/package.json b/package.json
index dfa31a8..6cfc93c 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"lint": "eslint .",
"preview": "vite preview",
"test": "node --test tests/*.test.mjs",
- "test:browser": "node tests/browser-smoke.mjs",
+ "test:browser": "node tests/browser-smoke.mjs && node tests/create-request.browser.mjs && node tests/create-request-review.mjs",
"start": "node server/start.ts",
"format": "prettier --write src tests index.html README.md \"*.json\" \"*.js\" server \"*.ts\" \"docs/*.md\" .github/workflows",
"format:check": "prettier --check src tests index.html README.md \"*.json\" \"*.js\" server \"*.ts\" \"docs/*.md\" .github/workflows"
@@ -19,7 +19,9 @@
"react-dom": "^19.2.6"
},
"devDependencies": {
+ "@axe-core/playwright": "^4.11.1",
"@eslint/js": "^10.0.1",
+ "@tailwindcss/vite": "^4.3.0",
"@types/node": "^24.12.3",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
@@ -30,6 +32,7 @@
"globals": "^17.6.0",
"playwright": "^1.58.2",
"prettier": "3.9.10",
+ "tailwindcss": "^4.3.0",
"typescript": "~6.0.2",
"typescript-eslint": "^8.59.2",
"vite": "^8.0.12"
diff --git a/src/App.tsx b/src/App.tsx
index b9641d8..17e40a0 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -18,7 +18,10 @@ import { useWorkspace } from "./useWorkspace.ts";
import { TicketDialog } from "./components/TicketDialog.tsx";
import { ImportDialog } from "./components/ImportDialog.tsx";
import { ServerSnapshotDialog } from "./components/ServerSnapshotDialog.tsx";
+import { CreateRequestDialog } from "./components/CreateRequestDialog.tsx";
+import { appendRequest } from "./domain/createRequest.ts";
import "./styles.css";
+import "./create-request.css";
const pageSize = 8;
export default function App() {
@@ -31,6 +34,8 @@ export default function App() {
const [selected, setSelected] = useState
Requests {results.length}
-