@marianmeres/stuic 3.170.0 → 3.172.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/AGENTS.md +10 -8
  2. package/API.md +90 -0
  3. package/README.md +1 -1
  4. package/dist/components/AssetsPreview/AssetsPreview.fixture.svelte +1 -1
  5. package/dist/components/Calendar/Calendar.svelte +799 -0
  6. package/dist/components/Calendar/Calendar.svelte.d.ts +135 -0
  7. package/dist/components/Calendar/README.md +294 -0
  8. package/dist/components/Calendar/calendar-i18n-sk.d.ts +20 -0
  9. package/dist/components/Calendar/calendar-i18n-sk.js +46 -0
  10. package/dist/components/Calendar/calendar-i18n.d.ts +61 -0
  11. package/dist/components/Calendar/calendar-i18n.js +73 -0
  12. package/dist/components/Calendar/index.css +307 -0
  13. package/dist/components/Calendar/index.d.ts +5 -0
  14. package/dist/components/Calendar/index.js +5 -0
  15. package/dist/components/Calendar/iso-date.d.ts +107 -0
  16. package/dist/components/Calendar/iso-date.js +247 -0
  17. package/dist/components/CommandMenu/CommandMenu.fixture.svelte +1 -1
  18. package/dist/components/ContextMenu/ContextMenu.svelte +1 -1
  19. package/dist/components/ContextMenu/ContextMenu.svelte.d.ts +1 -1
  20. package/dist/components/ContextMenu/README.md +1 -1
  21. package/dist/components/DropdownMenu/DropdownMenu.svelte +57 -3
  22. package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +4 -2
  23. package/dist/components/DropdownMenu/README.md +1 -0
  24. package/dist/components/Input/FieldDate.svelte +349 -0
  25. package/dist/components/Input/FieldDate.svelte.d.ts +79 -0
  26. package/dist/components/Input/FieldDateRange.svelte +373 -0
  27. package/dist/components/Input/FieldDateRange.svelte.d.ts +90 -0
  28. package/dist/components/Input/README.md +149 -16
  29. package/dist/components/Input/_internal/FieldDateShell.svelte +327 -0
  30. package/dist/components/Input/_internal/FieldDateShell.svelte.d.ts +66 -0
  31. package/dist/components/Input/index.css +119 -0
  32. package/dist/components/Input/index.d.ts +2 -0
  33. package/dist/components/Input/index.js +2 -0
  34. package/dist/components/ModalDialog/ModalDialog.fixture.svelte +1 -1
  35. package/dist/components/SlidingPanels/SlidingPanels.fixture.svelte +1 -1
  36. package/dist/icons/index.d.ts +1 -0
  37. package/dist/icons/index.js +1 -0
  38. package/dist/index.css +1 -0
  39. package/dist/index.d.ts +1 -0
  40. package/dist/index.js +1 -0
  41. package/docs/_archive/README.md +10 -0
  42. package/docs/{component-testing → _archive/component-testing}/00-overview-and-roadmap.md +8 -6
  43. package/docs/{component-testing → _archive/component-testing}/01-framework-setup.md +4 -2
  44. package/docs/{component-testing → _archive/component-testing}/03-component-coverage-roadmap.md +3 -1
  45. package/docs/{component-testing → _archive/component-testing}/04-hard-cases-and-e2e.md +5 -3
  46. package/docs/{component-testing → _archive/component-testing}/05-ci.md +2 -0
  47. package/docs/{component-testing → _archive/component-testing}/PROGRESS.md +3 -1
  48. package/docs/_archive/component-testing/README.md +28 -0
  49. package/docs/{upgrading.md → _archive/upgrading.md} +2 -0
  50. package/docs/architecture.md +19 -11
  51. package/docs/domains/actions.md +4 -3
  52. package/docs/domains/components.md +22 -19
  53. package/docs/domains/utils.md +2 -1
  54. package/docs/maybe-todo.md +6 -6
  55. package/docs/tasks.md +19 -9
  56. package/docs/{component-testing/02-test-conventions.md → testing-components.md} +30 -31
  57. package/docs/testing.md +3 -3
  58. package/package.json +2 -1
  59. package/docs/component-testing/README.md +0 -38
@@ -7,6 +7,8 @@ Planning artifact; no code was changed.
7
7
 
8
8
  # Framework Setup
9
9
 
10
+ > **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
11
+
10
12
  > This is the infrastructure dimension: the one-time work that turns "no DOM, no `$effect`"
11
13
  > into a working browser test harness. The single most important takeaway: **a Vitest
12
14
  > `projects` split routed by filename** (`*.test.ts` → fast node, `*.svelte.test.ts` → real
@@ -18,7 +20,7 @@ Planning artifact; no code was changed.
18
20
 
19
21
  - **Package manager:** pnpm. **Svelte:** 5.56.2. **SvelteKit:** 2.63.0 (adapter-auto). **Vitest:** 3.2.6.
20
22
  - **Test config:** none dedicated. Vitest is configured only implicitly through
21
- [`vite.config.ts`](../../vite.config.ts) (`plugins: [tailwindcss(), sveltekit()]`), and the
23
+ [`vite.config.ts`](../../../vite.config.ts) (`plugins: [tailwindcss(), sveltekit()]`), and the
22
24
  script is `"test": "vitest --dir src/"`.
23
25
  - **What runs today:** 9 suites / ~59 tests, all **node environment, pure logic** — validation
24
26
  helpers, stack classes (`NotificationsStack`, `AlertConfirmPromptStack`), `tr`, `replace-map`,
@@ -70,7 +72,7 @@ do **not** need `@testing-library/svelte` or `@testing-library/jest-dom` — `vi
70
72
 
71
73
  ## Step 3 — The `projects` config
72
74
 
73
- Add a `test` block to [`vite.config.ts`](../../vite.config.ts) (keep the file; just extend it).
75
+ Add a `test` block to [`vite.config.ts`](../../../vite.config.ts) (keep the file; just extend it).
74
76
  Verified against the live Vitest 4 docs (`provider: playwright()` is an imported **function**, not
75
77
  the old `'playwright'` string; `instances` is required):
76
78
 
@@ -7,6 +7,8 @@ Planning artifact; no code was changed.
7
7
 
8
8
  # Component Coverage Roadmap
9
9
 
10
+ > **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
11
+
10
12
  > The library has **74 components** (≈105 `.svelte` files incl. sub-components). Roughly **26 are
11
13
  > "easy"** (deterministic prop→DOM, no portals/traps), **23 "medium"** (actions, focus jumps, layout
12
14
  > reads), **30 "hard/E2E-only"** (portals, focus traps, anchor positioning, drag, Milkdown). The plan:
@@ -82,7 +84,7 @@ E2E. [04](./04-hard-cases-and-e2e.md) draws that line.
82
84
  - **One component per commit**, message like `test(Button): browser-mode coverage`.
83
85
  - Each commit: write `ComponentName.svelte.test.ts`, run `pnpm test` (both projects green), tick the
84
86
  row in [`PROGRESS.md`](./PROGRESS.md), commit.
85
- - Don't chase coverage %. Stop at the behavior contracts in [02](./02-test-conventions.md)'s checklist.
87
+ - Don't chase coverage %. Stop at the behavior contracts in [02](../../testing-components.md)'s checklist.
86
88
 
87
89
  ## Open questions / decisions needed
88
90
 
@@ -6,6 +6,8 @@ Claims verified against src/lib at commit cc9958b. Planning artifact; no code wa
6
6
 
7
7
  # Hard Cases & E2E Strategy
8
8
 
9
+ > **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
10
+
9
11
  > ~30 of the 74 components are "hard". But "hard" splits two ways: **most are hard for _jsdom_ yet
10
12
  > perfectly testable in Vitest browser mode** (focus traps, anchor positioning, ResizeObserver — all
11
13
  > work in a real Chromium); only **drag-heavy and Milkdown-class** components genuinely need a
@@ -32,7 +34,7 @@ candidates, both verified to exist:
32
34
 
33
35
  ### Candidate A (recommended) — anchor-position viewport clamping
34
36
 
35
- - **Code:** [`src/lib/utils/anchor-position.ts`](../../src/lib/utils/anchor-position.ts), consumed by
37
+ - **Code:** [`src/lib/utils/anchor-position.ts`](../../../src/lib/utils/anchor-position.ts), consumed by
36
38
  `DropdownMenu/DropdownMenu.svelte` (and others).
37
39
  - **Why:** this is precisely what regressed in `9d8c974` _"clamp anchor-positioned annotations to
38
40
  viewport on all paths"_ and `8c52afe`. A test here has immediate, proven value and prevents
@@ -45,7 +47,7 @@ candidates, both verified to exist:
45
47
 
46
48
  ### Candidate B (alternative) — focus trap
47
49
 
48
- - **Code:** [`src/lib/actions/focus-trap.ts`](../../src/lib/actions/focus-trap.ts), used by
50
+ - **Code:** [`src/lib/actions/focus-trap.ts`](../../../src/lib/actions/focus-trap.ts), used by
49
51
  `ModalDialog`, `Backdrop`, `Drawer`.
50
52
  - **Why:** Tab/Shift-Tab cycling and `returnFocus`-on-teardown are core a11y contracts that node
51
53
  cannot exercise at all.
@@ -59,7 +61,7 @@ candidates, both verified to exist:
59
61
  ## Portals & focus traps (browser-mode, later sprint)
60
62
 
61
63
  `Modal`, `ModalDialog`, `Backdrop`, `Drawer`, `AlertConfirmPrompt` — all use
62
- [`focus-trap.ts`](../../src/lib/actions/focus-trap.ts), scroll-lock, and an Escape-key stack. These
64
+ [`focus-trap.ts`](../../../src/lib/actions/focus-trap.ts), scroll-lock, and an Escape-key stack. These
63
65
  are testable in browser mode (open → focus trapped → Escape closes → backdrop click closes →
64
66
  `returnFocus`). The stack/queue _logic_ is already unit-tested (`AlertConfirmPromptStack`,
65
67
  `NotificationsStack`); browser tests add the DOM-interaction layer. Higher effort, real value —
@@ -6,6 +6,8 @@ Claims verified against the repo at commit cc9958b. Planning artifact; no code w
6
6
 
7
7
  # CI — GitHub Actions
8
8
 
9
+ > **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
10
+
9
11
  > The repo is on GitHub (`github.com/marianmeres/stuic`) and publishes to npm, so CI pays off: it
10
12
  > runs the tests automatically on every push/PR in a clean machine, catching regressions **before**
11
13
  > a broken release reaches npm. The plan: **one ~30-line workflow** that installs Chromium and runs
@@ -1,5 +1,7 @@
1
1
  # Implementation Progress — Component Testing
2
2
 
3
+ > **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
4
+
3
5
  Living tracker for acting on [`00-overview-and-roadmap.md`](./00-overview-and-roadmap.md).
4
6
  A fresh conversation should read this file first, then the relevant `NN-*.md` section.
5
7
 
@@ -17,7 +19,7 @@ Branch: `feat/component-testing`
17
19
  | --- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------ | --------- |
18
20
  | 1 | Upgrade vitest 3→4; confirm 9 existing node suites still green | [01](./01-framework-setup.md) Step 1 | ✅ | `71e47e2` |
19
21
  | 2 | Browser harness: add deps, `projects` config split, `playwright install chromium`, fix test scripts, Separator smoke test | [01](./01-framework-setup.md) Steps 2–5 | ✅ | `980b323` |
20
- | 3 | Reconcile [`docs/testing.md`](../testing.md) — add the browser-behavior layer | [02](./02-test-conventions.md) | ✅ | `977c431` |
22
+ | 3 | Reconcile [`docs/testing.md`](../../testing.md) — add the browser-behavior layer | [02](../../testing-components.md) | ✅ | `977c431` |
21
23
  | 4 | **Button** — flagship; establish assertion patterns | [03](./03-component-coverage-roadmap.md) #1 | ✅ | `9485e97` |
22
24
  | 5 | **Pill** — intent/active/dismissible event | [03](./03-component-coverage-roadmap.md) #2 | ✅ | `2992faf` |
23
25
  | 6 | **Switch** — checked binding, toggle, disabled | [03](./03-component-coverage-roadmap.md) #3 | ✅ | `6aa1771` |
@@ -0,0 +1,28 @@
1
+ # Component Testing — archived plan
2
+
3
+ > **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
4
+
5
+ > **Archived.** This was the planning set that introduced real-browser component tests to STUIC
6
+ > (Vitest 4 Browser Mode + `vitest-browser-svelte` + Playwright/Chromium), produced 2026-06-08
7
+ > against the codebase at commit `cc9958b`. The plan was executed: the harness, the project split,
8
+ > CI, and the component suite all shipped, and `PROGRESS.md` is closed out apart from two
9
+ > deliberately deferred items.
10
+ >
11
+ > **For how to write a component test today, read
12
+ > [`docs/testing-components.md`](../../testing-components.md)** — the conventions doc was promoted
13
+ > out of this set and is the live reference. Everything below is provenance: why the stack was
14
+ > chosen, what the tiers were, and how the sprint ran.
15
+
16
+ ## Documents
17
+
18
+ | # | Doc | Scope | Headline |
19
+ | --- | ---------------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------- |
20
+ | 00 | [overview-and-roadmap](./00-overview-and-roadmap.md) | synthesis + roadmap | The stack is the right default; the vitest 3→4 upgrade was the gating prerequisite. |
21
+ | 01 | [framework-setup](./01-framework-setup.md) | infra | Upgrade vitest 4, add a `projects` split (node `server` + browser `client`), route by filename. |
22
+ | 03 | [component-coverage-roadmap](./03-component-coverage-roadmap.md) | inventory + tiers | 74 components split easy / medium / hard; easy tier first, one commit each. |
23
+ | 04 | [hard-cases-and-e2e](./04-hard-cases-and-e2e.md) | hard cases | Most "hard" components are fine in browser mode; only drag/Milkdown need standalone E2E. |
24
+ | 05 | [ci](./05-ci.md) | CI | One ~30-line GitHub Actions workflow that installs Chromium and runs `pnpm test`. |
25
+ | | [PROGRESS.md](./PROGRESS.md) | tracker | Execution log — what was built, in which commit. |
26
+
27
+ Doc 02 (test conventions) is **not** here; it lives at
28
+ [`docs/testing-components.md`](../../testing-components.md).
@@ -1,5 +1,7 @@
1
1
  # Upgrading STUIC
2
2
 
3
+ > **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](./README.md) for why.
4
+
3
5
  Notes for coding agents (and humans) maintaining a project that consumes `@marianmeres/stuic`. This doc describes the deltas introduced on top of **v3.66.1** — grouped by what a consumer actually cares about, not by commit.
4
6
 
5
7
  ## TL;DR
@@ -36,19 +36,24 @@ Layer 4: Internal Vars (--_bg, --_text, --_border)
36
36
 
37
37
  ```
38
38
  src/lib/
39
- ├── components/ # 57 component directories
39
+ ├── components/ # 75 component directories
40
40
  │ └── {Name}/
41
41
  │ ├── {Name}.svelte # Main component
42
42
  │ ├── index.ts # Exports
43
43
  │ ├── index.css # CSS tokens (if styled)
44
44
  │ └── README.md # Documentation
45
45
  │
46
- ├── actions/ # 15 Svelte actions
46
+ ├── actions/ # 16 Svelte actions
47
47
  │ ├── *.svelte.ts # Reactive actions
48
48
  │ ├── *.ts # Traditional actions
49
49
  │ └── index.ts # Barrel export
50
50
  │
51
- ├── utils/ # 45 utility modules
51
+ ├── attachments/ # {@attach} DOM helpers (preferred over new actions)
52
+ │ ├── auto-height.ts
53
+ │ ├── long-press.ts
54
+ │ └── index.ts # Barrel export
55
+ │
56
+ ├── utils/ # 55 utility modules (48 re-exported from the barrel)
52
57
  │ ├── *.svelte.ts # Reactive utilities
53
58
  │ ├── *.ts # Pure functions
54
59
  │ └── index.ts # Barrel export
@@ -58,6 +63,8 @@ src/lib/
58
63
  ├── css/ # CSS-only presets (classes + tokens, no JS)
59
64
  │ └── frame.css # Ratio-locked frame (letterbox)
60
65
  │
66
+ ├── types.ts # Shared public types (DataAttributes, TranslateFn, …)
67
+ ├── mcp.ts # MCP tool definitions (@marianmeres/mcp-server discovery)
61
68
  ├── index.css # CENTRALIZED CSS imports
62
69
  └── index.ts # Main barrel export
63
70
  ```
@@ -87,7 +94,7 @@ src/lib/
87
94
  **DO NOT** use `import './index.css'` inside component `.svelte` files.
88
95
 
89
96
  **Exception — subpath-export components.** A component whose peer dependencies are
90
- declared _optional_ (`MarkdownEditor`, `CommentInput`) is kept off the root barrel and
97
+ declared _optional_ (`MarkdownEditor`, `CommentInput`, `TrendChart`) is kept off the root barrel and
91
98
  shipped behind its own subpath export, so those peers never enter the entry graph of
92
99
  consumers who don't use it. Such a component imports its own `index.css` locally and is
93
100
  **not** `@import`-ed into `src/lib/index.css` — centralizing it would ship the styles to
@@ -145,7 +152,7 @@ Props → Component → Data Attributes → CSS Selectors
145
152
  | ------------------------------------------ | --------------------------------------------------------------------------- |
146
153
  | `src/lib/index.css` | CSS entry point (import this) |
147
154
  | `src/lib/index.ts` | JS entry point (barrel export) |
148
- | `@marianmeres/design-tokens/css/stone.css` | Default theme (42 themes available) |
155
+ | `@marianmeres/design-tokens/css/stone.css` | Default theme (54 themes available) |
149
156
  | `src/lib/utils/tw-merge.ts` | Tailwind class merging |
150
157
  | `src/lib/utils/design-tokens.ts` | Theme types (`ThemeSchema`, `ColorPair`, etc.) and CSS generation functions |
151
158
 
@@ -173,14 +180,15 @@ Theme CSS files are provided by the `@marianmeres/design-tokens` package (a regu
173
180
  "./phone-validation": Phone validation helpers
174
181
  "./markdown-editor": MarkdownEditor — optional peer deps, local CSS
175
182
  "./comment-input": CommentInput — optional peer deps, local CSS
183
+ "./trend-chart": TrendChart — optional peer deps, local CSS
176
184
  ```
177
185
 
178
- The last two are deliberately **off** the main entry: they reach `@milkdown/*` and
179
- `@codemirror/*`, which are optional peers. A consumer that has not installed those
180
- peers gets a hard bundler error (rolldown reports every named import from the
181
- unresolved stub as `MISSING_EXPORT`) if the root barrel can reach them — even though
182
- the editor backends sit behind `await import()`, because the specifiers are static
183
- literals that a bundler must still resolve at build time.
186
+ The last three are deliberately **off** the main entry: they reach `@milkdown/*`,
187
+ `@codemirror/*` and `@marianmeres/trend-chart`, which are optional peers. A consumer
188
+ that has not installed those peers gets a hard bundler error (rolldown reports every
189
+ named import from the unresolved stub as `MISSING_EXPORT`) if the root barrel can reach
190
+ them — even though the editor backends sit behind `await import()`, because the
191
+ specifiers are static literals that a bundler must still resolve at build time.
184
192
 
185
193
  ---
186
194
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 15 Svelte actions (directives) for reusable DOM behavior.
5
+ 16 Svelte actions (directives) for reusable DOM behavior.
6
6
 
7
7
  ---
8
8
 
@@ -18,6 +18,7 @@
18
18
  | `fileDropzone` | Drag-and-drop file handling | `file-dropzone.svelte.ts` |
19
19
  | `highlightDragover` | Visual feedback on drag-over | `highlight-dragover.svelte.ts` |
20
20
  | `resizableWidth` | Draggable width resizing | `resizable-width.svelte.ts` |
21
+ | `draggable` | Pointer-delta drag handle (consumer owns the movement) | `draggable.svelte.ts` |
21
22
  | `trim` | Auto-trim whitespace from input | `trim.svelte.ts` |
22
23
  | `typeahead` | Advanced autocomplete behavior | `typeahead.svelte.ts` |
23
24
  | `onSubmitValidityCheck` | Form submit validation | `on-submit-validity-check.svelte.ts` |
@@ -120,8 +121,8 @@ Per the HTML spec, `<input type="hidden">` is _barred from constraint
120
121
  validation_ — `validity.valueMissing` stays `false` regardless of the
121
122
  `required` attribute, and native browser submit blocking is skipped. Several
122
123
  STUIC field components (`FieldPhoneNumber`, `FieldCountry`, `FieldObject`,
123
- `FieldAssets`, `FieldInputLocalized`, `FieldKeyValues`, `FieldLikeButton`)
124
- use a hidden input to participate in `FormData`, so they each enforce
124
+ `FieldAssets`, `FieldInputLocalized`, `FieldKeyValues`, `FieldLikeButton`,
125
+ `FieldDate`, `FieldDateRange`) use a hidden input to participate in `FormData`, so they each enforce
125
126
  `required` themselves inside their `customValidator`:
126
127
 
127
128
  ```ts
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 74 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
5
+ 75 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
6
6
 
7
7
  ## Component Categories
8
8
 
@@ -58,22 +58,24 @@
58
58
 
59
59
  ### Form
60
60
 
61
- | Component | Purpose |
62
- | --------------------------------------------- | --------------------------------------------------------------------------- |
63
- | Input (FieldInput, FieldSelect, etc.) | Form fields |
64
- | FieldMoney | Money input storing integer minor units (e.g. cents) |
65
- | FieldPhoneNumber | International phone input with country picker |
66
- | FieldObject | Dual-mode JSON object editor (pretty-print/raw) |
67
- | CronInput | Cron expression editor with presets and validation |
68
- | Fieldset | Field grouping with legend |
69
- | FieldKeyValues | Key-value pair editor |
70
- | FieldsBuilder | Field-definition list editor ("what properties does a thing have?") |
71
- | FieldAssets | File/asset management |
72
- | LoginForm, LoginFormModal | Standalone login form with optional modal variant |
73
- | RegisterForm | Standalone registration form with declarative extra fields |
74
- | LoginOrRegisterForm, LoginOrRegisterFormModal | Composite login/register/verify form (3 modes, shared social-logins) |
75
- | EmailVerifyForm | Post-registration email-verify form (OtpInput + resend cooldown) |
76
- | OtpInput | Generic N-slot one-time-code input (numeric/alphanumeric, paste-distribute) |
61
+ | Component | Purpose |
62
+ | --------------------------------------------- | ------------------------------------------------------------------------------------------ |
63
+ | Input (FieldInput, FieldSelect, etc.) | Form fields |
64
+ | FieldMoney | Money input storing integer minor units (e.g. cents) |
65
+ | Calendar | Month grid for single-date / range picking: keyboard grid, min/max, dropdown caption, i18n |
66
+ | FieldDate, FieldDateRange | Date / date-range fields around Calendar: trigger + dialog or embedded; ISO hidden inputs |
67
+ | FieldPhoneNumber | International phone input with country picker |
68
+ | FieldObject | Dual-mode JSON object editor (pretty-print/raw) |
69
+ | CronInput | Cron expression editor with presets and validation |
70
+ | Fieldset | Field grouping with legend |
71
+ | FieldKeyValues | Key-value pair editor |
72
+ | FieldsBuilder | Field-definition list editor ("what properties does a thing have?") |
73
+ | FieldAssets | File/asset management |
74
+ | LoginForm, LoginFormModal | Standalone login form with optional modal variant |
75
+ | RegisterForm | Standalone registration form with declarative extra fields |
76
+ | LoginOrRegisterForm, LoginOrRegisterFormModal | Composite login/register/verify form (3 modes, shared social-logins) |
77
+ | EmailVerifyForm | Post-registration email-verify form (OtpInput + resend cooldown) |
78
+ | OtpInput | Generic N-slot one-time-code input (numeric/alphanumeric, paste-distribute) |
77
79
 
78
80
  ### Display
79
81
 
@@ -150,7 +152,7 @@ Use `validate={false}` to bypass stuic's validation entirely.
150
152
 
151
153
  > **Why default-on?** Hidden-input field components (`FieldPhoneNumber`,
152
154
  > `FieldCountry`, `FieldObject`, `FieldAssets`, `FieldInputLocalized`,
153
- > `FieldKeyValues`, `FieldLikeButton`) _must_ be default-on because hidden
155
+ > `FieldKeyValues`, `FieldLikeButton`, `FieldDate`, `FieldDateRange`) _must_ be default-on because hidden
154
156
  > inputs are excluded from native browser constraint validation — without the
155
157
  > stuic action enforcing `required` in a `customValidator`, the attribute is a
156
158
  > silent no-op. Plain-input field components were harmonized to the same
@@ -161,7 +163,8 @@ Use `validate={false}` to bypass stuic's validation entirely.
161
163
  Available on `FieldInput`, `FieldMoney`, `FieldTextarea`, `FieldCheckbox`,
162
164
  `FieldSelect`, `FieldFile`, `FieldObject`, `FieldAssets`, `FieldInputLocalized`,
163
165
  `FieldKeyValues`, `FieldPhoneNumber`, `FieldCountry`, `FieldLikeButton`,
164
- `FieldRadios`, `FieldSwitch`, `FieldOptions`, and `Switch`:
166
+ `FieldRadios`, `FieldSwitch`, `FieldOptions`, `FieldDate`, `FieldDateRange`, and
167
+ `Switch`:
165
168
 
166
169
  | Method | Returns | Purpose |
167
170
  | ----------------------- | ------------------------------- | ------------------------------------------------------------- |
@@ -2,7 +2,8 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 45 utility modules for common tasks. Organized by category.
5
+ 55 utility modules for common tasks (48 re-exported from the package barrel; the rest
6
+ are library-internal). Organized by category.
6
7
 
7
8
  ---
8
9
 
@@ -24,12 +24,12 @@ Checked first, to avoid false positives:
24
24
 
25
25
  ## Tier 1 — staples nearly every comparable library ships
26
26
 
27
- 1. **Date picker / Calendar** — the single biggest gap. No calendar grid, no date-range
28
- picker anywhere in the lib (only native `type="date"` via `FieldInput`).
29
- `@marianmeres/calendar-utils` already does the date math, so this is mostly UI work.
30
- A `FieldDate` / `FieldDateRange` would slot naturally next to `FieldMoney` /
31
- `FieldPhoneNumber`. Most work of anything on this list, but also the absence
32
- consumers will actually notice.
27
+ 1. ~~**Date picker / Calendar**~~ — ✅ shipped (see `src/lib/components/Calendar/` and
28
+ `FieldDate` / `FieldDateRange` in `Input/`): `Calendar` is the month grid (single /
29
+ range, APG keyboard grid, min/max/blackouts, dropdown caption, multiple months, week
30
+ numbers, `renderDay`, Intl names + `t` texts with Slovak bundled), built on
31
+ `@marianmeres/calendar-utils`; the fields wrap it as trigger + dialog or embedded, with
32
+ ISO `YYYY-MM-DD` hidden inputs and the usual validate API.
33
33
  2. ~~**Breadcrumbs**~~ — ✅ shipped (see `src/lib/components/Breadcrumbs/`):
34
34
  APG nav/ol trail, collapsible long trails, schema.org `BreadcrumbList` JSON-LD
35
35
  helpers (`breadcrumbsJsonLd` / `breadcrumbsJsonLdScript` + inline `jsonLd` prop).
package/docs/tasks.md CHANGED
@@ -149,7 +149,9 @@ For consumers creating their own theme outside the library.
149
149
  ```ts
150
150
  import type { ThemeSchema } from "@marianmeres/stuic";
151
151
  import { generateThemeCss } from "@marianmeres/stuic";
152
- import stone from "@marianmeres/stuic/themes/stone";
152
+ // Built-in theme schemas live in design-tokens, not in stuic. stuic depends on it,
153
+ // but add it to your own package.json if you import from it directly.
154
+ import { stone } from "@marianmeres/design-tokens/themes";
153
155
  import { writeFileSync } from "node:fs";
154
156
 
155
157
  const myTheme: ThemeSchema = {
@@ -166,7 +168,9 @@ const myTheme: ThemeSchema = {
166
168
  dark: stone.dark,
167
169
  };
168
170
 
169
- writeFileSync("src/theme.css", generateThemeCss(myTheme));
171
+ // generateThemeCss(schema, prefix, options?) — the "stuic-" prefix is what the
172
+ // library's own CSS reads (--stuic-color-primary, …), so it is not optional.
173
+ writeFileSync("src/theme.css", generateThemeCss(myTheme, "stuic-"));
170
174
  ```
171
175
 
172
176
  ### Checklist
@@ -179,20 +183,26 @@ writeFileSync("src/theme.css", generateThemeCss(myTheme));
179
183
 
180
184
  ## Test Changes
181
185
 
186
+ The repo uses **pnpm**.
187
+
182
188
  ### Steps
183
189
 
184
- 1. Run `npm run build` - Check for errors
185
- 2. Run `npm run check` - TypeScript validation
186
- 3. Run `npm run dev` - Start dev server
187
- 4. Test in browser (http://localhost:8886)
188
- 5. Test light mode
189
- 6. Test dark mode (add `class="dark"` to `<html>`)
190
- 7. Test that `--stuic-color-primary` changes cascade
190
+ 1. Run `pnpm run build` - Check for errors
191
+ 2. Run `pnpm run check` - TypeScript validation (`svelte-check`)
192
+ 3. Run `pnpm run lint` - ESLint + Prettier
193
+ 4. Run `pnpm test` - Node + browser suites (see [Testing](./testing.md))
194
+ 5. Run `pnpm run dev` - Start dev server
195
+ 6. Test in browser (http://localhost:8886)
196
+ 7. Test light mode
197
+ 8. Test dark mode (add `class="dark"` to `<html>`)
198
+ 9. Test that `--stuic-color-primary` changes cascade
191
199
 
192
200
  ### Checklist
193
201
 
194
202
  - [ ] No build errors
195
203
  - [ ] No TypeScript errors
204
+ - [ ] Lint clean
205
+ - [ ] Tests green
196
206
  - [ ] Component renders correctly
197
207
  - [ ] Light mode works
198
208
  - [ ] Dark mode works
@@ -1,45 +1,30 @@
1
- <!--
2
- GENERATED ANALYSIS — @marianmeres/stuic real-browser component testing
3
- Produced 2026-06-08 by multi-agent research → adversarial verify → synthesize.
4
- Claims verified against the codebase at commit cc9958b and the live
5
- vitest-browser-svelte docs. Planning artifact; no code was changed.
6
- -->
1
+ # Component Test Conventions
7
2
 
8
- # Test Conventions
3
+ How to write a STUIC browser component test, and — just as important — **what is worth asserting**.
9
4
 
10
- > How to write a STUIC browser component test, and — just as important — **what is worth
11
- > asserting**. The headline: test _behavior the build can't see_ (events fire, bindings update,
5
+ > The headline: test _behavior the build can't see_ (events fire, bindings update,
12
6
  > aria/disabled/active states, computed layout), not "does it render" (already gated by
13
7
  > `svelte-check` + `publint` + the build). Use `render()` → locators → `expect.element`. In Svelte 5
14
8
  > events are props, so you assert them with spies; snippet children come from `createRawSnippet`.
15
9
 
16
- ## Reconciling with `docs/testing.md`
10
+ Companion to [`testing.md`](./testing.md), which covers the suite as a whole (both layers, what we
11
+ test and what we deliberately don't). This document is the how-to for the browser layer only.
17
12
 
18
- [`docs/testing.md`](../testing.md) currently states the library **deliberately does not** test full
19
- component rendering ("50+ components × prop combinations = slow suite with tiny yield... Rendering is
20
- already gated by svelte-check + publint + the build") and treats interactive/visual behavior as
21
- out of scope.
13
+ ## The line this layer does and doesn't cross
22
14
 
23
- That reasoning was **correct for what it described and is not actually reversed here** — it just
24
- predates a capability we didn't have:
15
+ "Does it render / compile / export" is still low-yield and still covered by `svelte-check` +
16
+ `publint` + the build. We do **not** write tests for that.
25
17
 
26
- - "Does it render / compile / export" → still low-yield, still covered by `svelte-check` + `publint` +
27
- build. We will **not** write tests for that.
28
- - "Does it _behave_" — click handlers, two-way `bind:`, `aria-*`/`disabled`/`active` state, focus
29
- traps, viewport-clamped anchor positioning (cf. the recent `9d8c974` annotation-clamp fix) — was
30
- **previously impossible** (node/server build, no DOM, no `$effect`). Browser mode makes it possible,
31
- and _this_ is the high-yield target.
32
-
33
- **Task in the roadmap:** update `docs/testing.md` to add this browser-test layer so the docs aren't
34
- self-contradictory — promote "interactive behavior" from ❌ to ✅-when-it's-a-real-contract, and point
35
- to this directory. (See [`PROGRESS.md`](./PROGRESS.md), sprint task.)
18
+ "Does it _behave_" — click handlers, two-way `bind:`, `aria-*`/`disabled`/`active` state, focus
19
+ traps, viewport-clamped anchor positioning — was impossible before browser mode (node/server build,
20
+ no DOM, no `$effect`). That is the high-yield target, and the whole reason this layer exists.
36
21
 
37
22
  ## File naming & location
38
23
 
39
24
  - One test per component, **co-located** next to the `.svelte` file (matches the existing co-located
40
25
  style, e.g. `Input/phone-validation.test.ts`).
41
26
  - Name it `ComponentName.svelte.test.ts` — the `.svelte.test.ts` suffix is what routes it into the
42
- browser `client` project (see [01](./01-framework-setup.md)). A plain `*.test.ts` next to a
27
+ browser `client` project (see [`_archive/component-testing/01-framework-setup.md`](./_archive/component-testing/01-framework-setup.md)). A plain `*.test.ts` next to a
43
28
  component stays in the fast node project (correct for extracted pure logic like `_internal/*.ts`).
44
29
 
45
30
  ## The canonical test
@@ -142,8 +127,22 @@ Rely on `expect.element` retries and awaited locator actions — **never fixed `
142
127
  reactivity resolves through the retry loop; only assertions on _external_ universal state living in a
143
128
  `*.svelte.ts` module may need `flushSync()` from `svelte`.
144
129
 
145
- ## Open questions / decisions needed
130
+ ## Shared helpers
131
+
132
+ There is deliberately **no shared test-util module**. The one recurring helper — a `text()` snippet
133
+ factory for `children` — is three lines, so each test file declares its own copy rather than
134
+ importing across the tree:
135
+
136
+ ```ts
137
+ const text = (s: string) =>
138
+ createRawSnippet(() => ({ render: () => `<span>${s}</span>` }));
139
+ ```
140
+
141
+ Revisit only if a genuinely non-trivial helper shows up.
142
+
143
+ ## Where this came from
146
144
 
147
- - **Shared test utilities** — agree on one home for the `text()` snippet helper and any future
148
- fixtures (suggest `src/lib/test-utils/` or `src/test-helpers.ts`), so it's not redefined per file.
149
- Decide when the second snippet-needing component lands.
145
+ The plan that produced this layer (roadmap, framework setup, coverage tiers, CI, and the completed
146
+ progress tracker) is archived under
147
+ [`_archive/component-testing/`](./_archive/component-testing/). It is history, not guidance — this
148
+ file is the live convention.
package/docs/testing.md CHANGED
@@ -16,7 +16,7 @@ Tests are for what those tools can't see. There are **two layers**, split by fil
16
16
  - **`*.test.ts` — node, fast.** Pure deterministic logic where a regression silently corrupts data.
17
17
  - **`*.svelte.test.ts` — real browser (Chromium).** Component _behavior_ the build can't see: events firing, two-way `bind:`, `aria`/`disabled`/`active` state, focus traps, computed layout/positioning.
18
18
 
19
- We still explicitly don't try to test everything. The browser layer targets **behavior contracts**, not "does it render" — see [`component-testing/`](./component-testing/) for the strategy, roadmap, and how-to.
19
+ We still explicitly don't try to test everything. The browser layer targets **behavior contracts**, not "does it render" — see [`testing-components.md`](./testing-components.md) for the how-to and the "what to assert" checklist.
20
20
 
21
21
  ## What we test
22
22
 
@@ -79,8 +79,8 @@ const t: TranslateFn = (k) => k;
79
79
  For **component** tests (`*.svelte.test.ts`), the patterns differ — `render()` from
80
80
  `vitest-browser-svelte`, locators, and the retry-able `expect.element`; events are props (assert with
81
81
  spies); snippet children come from `createRawSnippet`. See
82
- [`component-testing/02-test-conventions.md`](./component-testing/02-test-conventions.md) for the full
83
- how-to and the "what to assert" checklist.
82
+ [`testing-components.md`](./testing-components.md) for the full how-to and the "what to assert"
83
+ checklist.
84
84
 
85
85
  ## When in doubt
86
86
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.170.0",
3
+ "version": "3.172.0",
4
4
  "packageManager": "pnpm@11.5.0",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -183,6 +183,7 @@
183
183
  "vitest-browser-svelte": "^2.2.1"
184
184
  },
185
185
  "dependencies": {
186
+ "@marianmeres/calendar-utils": "^2.0.0",
186
187
  "@marianmeres/clog": "^3.21.0",
187
188
  "@marianmeres/countries": "^1.1.0",
188
189
  "@marianmeres/cron-parser": "^1.0.1",
@@ -1,38 +0,0 @@
1
- <!--
2
- GENERATED ANALYSIS — @marianmeres/stuic real-browser component testing
3
- Produced 2026-06-08 by multi-agent research → adversarial verify → synthesize.
4
- Claims verified against the codebase at commit cc9958b and the live Vitest 4 /
5
- vitest-browser-svelte docs. Planning artifact; no code was changed.
6
- -->
7
-
8
- # Component Testing — @marianmeres/stuic
9
-
10
- This directory holds the plan for introducing **real-browser component tests** to STUIC
11
- (Vitest 4 Browser Mode + `vitest-browser-svelte` + Playwright/Chromium). It was produced
12
- 2026-06-08 from a research pass over the codebase and the current Svelte/Vitest ecosystem.
13
- It is a **planning artifact — no code has been changed**; every claim is verified against the
14
- repo at commit `cc9958b` or against the live docs cited in each section.
15
-
16
- **Start here:** [`00-overview-and-roadmap.md`](./00-overview-and-roadmap.md). Then track and
17
- resume execution from [`PROGRESS.md`](./PROGRESS.md).
18
-
19
- ## Documents
20
-
21
- | # | Doc | Scope | Headline |
22
- | --- | ---------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------- |
23
- | 00 | [overview-and-roadmap](./00-overview-and-roadmap.md) | synthesis + roadmap | The stack is the right default; vitest 3→4 upgrade is the gating prerequisite. |
24
- | 01 | [framework-setup](./01-framework-setup.md) | infra | Upgrade vitest 4, add a `projects` split (node `server` + browser `client`), route by filename. |
25
- | 02 | [test-conventions](./02-test-conventions.md) | how-to | `render()` + locators + `expect.element`; events are props (spies); snippets via `createRawSnippet`. |
26
- | 03 | [component-coverage-roadmap](./03-component-coverage-roadmap.md) | what to cover | 74 components tiered; warm up on Button/Pill/Switch, one commit per component. |
27
- | 04 | [hard-cases-and-e2e](./04-hard-cases-and-e2e.md) | the hard 30 | Portals/focus-traps/anchor-positioning: one "hard proof" now; standalone Playwright E2E deferred. |
28
- | 05 | [ci](./05-ci.md) | automation | One ~30-line GitHub Actions workflow; install Chromium, run `pnpm test`. |
29
-
30
- ## How it was produced
31
-
32
- Five parallel research agents (test-infra audit, full component inventory, multistep-format
33
- extraction, two independent web-research angles on the stack) → synthesis → live-docs
34
- verification of the exact Vitest 4 config syntax → this plan.
35
-
36
- > Nothing here is decided beyond the four clarifying answers recorded in
37
- > [`PROGRESS.md`](./PROGRESS.md) → Decisions log. Each doc's "Open questions / decisions needed"
38
- > lists what still needs a call.