@cahyo-dimas/freeday 1.53.0 → 2.0.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 (145) hide show
  1. package/CHANGELOG.md +657 -535
  2. package/COMPONENTS.md +153 -135
  3. package/README.id.md +46 -45
  4. package/README.md +46 -43
  5. package/USAGE.md +39 -35
  6. package/adapters/blazor/FdyAppShell.razor +50 -0
  7. package/adapters/blazor/FdyAppShell.razor.cs +114 -0
  8. package/adapters/blazor/FdyCfl.razor.cs +1 -1
  9. package/adapters/blazor/FdyChart.razor +1 -1
  10. package/adapters/blazor/FdyChart.razor.cs +3 -3
  11. package/adapters/blazor/FdyChartSeries.cs +1 -1
  12. package/adapters/blazor/FdyCombo.razor.cs +2 -2
  13. package/adapters/blazor/FdyModal.razor +1 -1
  14. package/adapters/blazor/FdyTable.razor.cs +7 -7
  15. package/adapters/blazor/FdyTableFilter.razor +1 -1
  16. package/adapters/blazor/FdyTableFooter.razor +1 -1
  17. package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
  18. package/adapters/blazor/Freeday.Blazor.csproj +1 -1
  19. package/adapters/blazor/FreedayComponentBase.cs +1 -1
  20. package/adapters/blazor/TableModel.cs +3 -3
  21. package/adapters/blazor/TableTypes.cs +3 -3
  22. package/adapters/blazor/freeday-blazor.js +5 -5
  23. package/adapters/core/app-shell.d.ts +30 -0
  24. package/adapters/core/app-shell.js +134 -0
  25. package/adapters/core/table-model.d.ts +2 -2
  26. package/adapters/core/table-model.js +8 -8
  27. package/adapters/react/components/FdyAppShell.tsx +159 -0
  28. package/adapters/react/components/FdyAutocomplete.tsx +1 -1
  29. package/adapters/react/components/FdyCascade.tsx +1 -1
  30. package/adapters/react/components/FdyCfl.tsx +11 -11
  31. package/adapters/react/components/FdyDateRange.tsx +2 -2
  32. package/adapters/react/components/FdyDatepicker.tsx +5 -5
  33. package/adapters/react/components/FdyDrawer.tsx +1 -1
  34. package/adapters/react/components/FdyModal.tsx +2 -2
  35. package/adapters/react/components/FdyTable.tsx +7 -7
  36. package/adapters/react/components/FdyTableFilter.tsx +1 -1
  37. package/adapters/react/components/FdyTableFooter.tsx +5 -5
  38. package/adapters/react/index.d.ts +2 -1
  39. package/adapters/react/index.js +1 -0
  40. package/adapters/react/useFreeday.js +3 -3
  41. package/adapters/vue/components/FdyAppShell.vue +160 -0
  42. package/adapters/vue/components/FdyAutocomplete.vue +1 -1
  43. package/adapters/vue/components/FdyCascade.vue +1 -1
  44. package/adapters/vue/components/FdyCfl.vue +8 -8
  45. package/adapters/vue/components/FdyChart.vue +2 -2
  46. package/adapters/vue/components/FdyCombo.vue +1 -1
  47. package/adapters/vue/components/FdyDateRange.vue +2 -2
  48. package/adapters/vue/components/FdyDatepicker.vue +5 -5
  49. package/adapters/vue/components/FdyDrawer.vue +3 -3
  50. package/adapters/vue/components/FdyModal.vue +3 -3
  51. package/adapters/vue/components/FdyTable.vue +7 -7
  52. package/adapters/vue/components/FdyTableFilter.vue +1 -1
  53. package/adapters/vue/components/FdyTableFooter.vue +5 -5
  54. package/adapters/vue/index.d.ts +3 -2
  55. package/adapters/vue/index.js +1 -0
  56. package/adapters/vue/useFreeday.js +3 -3
  57. package/dist/asset.d.ts +3 -3
  58. package/dist/freeday-app-shell.js +49 -9
  59. package/dist/freeday-autocomplete.js +2 -2
  60. package/dist/freeday-breakpoint.js +1 -1
  61. package/dist/freeday-carousel.js +5 -5
  62. package/dist/freeday-cascade.js +7 -7
  63. package/dist/freeday-cfl.js +9 -9
  64. package/dist/freeday-chart.js +11 -11
  65. package/dist/freeday-chip.js +3 -3
  66. package/dist/freeday-datepicker.js +11 -7
  67. package/dist/freeday-datetime.js +5 -5
  68. package/dist/freeday-drawer.js +1 -1
  69. package/dist/freeday-form.js +14 -14
  70. package/dist/freeday-mask.js +6 -6
  71. package/dist/freeday-menu.js +1 -1
  72. package/dist/freeday-number.js +3 -3
  73. package/dist/freeday-popover.js +3 -3
  74. package/dist/freeday-rating.js +2 -2
  75. package/dist/freeday-select.js +5 -5
  76. package/dist/freeday-slider.js +2 -2
  77. package/dist/freeday-stepper.js +7 -7
  78. package/dist/freeday-table.js +15 -15
  79. package/dist/freeday-tabs.js +1 -1
  80. package/dist/freeday-timepicker.js +2 -2
  81. package/dist/freeday-toast.js +8 -8
  82. package/dist/freeday-tree.js +3 -3
  83. package/dist/freeday-upload.js +25 -25
  84. package/dist/freeday.bundle.css +176 -176
  85. package/dist/freeday.css +170 -170
  86. package/dist/freeday.d.ts +2 -2
  87. package/dist/freeday.js +202 -158
  88. package/dist/freeday.tokens.css +5 -5
  89. package/docs/agent-onboarding.md +42 -42
  90. package/docs/getting-started.md +35 -35
  91. package/docs/integrations.md +18 -18
  92. package/docs/reference-screen.html +14 -14
  93. package/package.json +5 -4
  94. package/src/base.css +6 -6
  95. package/src/components/accordion.css +4 -4
  96. package/src/components/alert.css +1 -1
  97. package/src/components/app-shell.css +10 -10
  98. package/src/components/appbar.css +3 -3
  99. package/src/components/autocomplete.css +1 -1
  100. package/src/components/avatar.css +3 -3
  101. package/src/components/badge.css +5 -5
  102. package/src/components/breadcrumb.css +1 -1
  103. package/src/components/breakpoints.css +4 -4
  104. package/src/components/button.css +13 -13
  105. package/src/components/card.css +8 -8
  106. package/src/components/carousel.css +2 -2
  107. package/src/components/cascade.css +2 -2
  108. package/src/components/cfl.css +4 -4
  109. package/src/components/chart.css +3 -3
  110. package/src/components/chip.css +2 -2
  111. package/src/components/combo.css +3 -3
  112. package/src/components/composition.css +7 -7
  113. package/src/components/datepicker.css +6 -6
  114. package/src/components/datetimepicker.css +1 -1
  115. package/src/components/description-list.css +1 -1
  116. package/src/components/divider.css +1 -1
  117. package/src/components/drawer.css +1 -1
  118. package/src/components/file-upload.css +5 -5
  119. package/src/components/filterbar.css +7 -7
  120. package/src/components/form-grid.css +2 -2
  121. package/src/components/input-group.css +6 -6
  122. package/src/components/input.css +6 -6
  123. package/src/components/kbd.css +1 -1
  124. package/src/components/list.css +6 -6
  125. package/src/components/menu.css +3 -3
  126. package/src/components/modal.css +1 -1
  127. package/src/components/pagination.css +1 -1
  128. package/src/components/progress.css +1 -1
  129. package/src/components/rating.css +2 -2
  130. package/src/components/selection.css +4 -4
  131. package/src/components/skeleton.css +1 -1
  132. package/src/components/slider.css +1 -1
  133. package/src/components/spinner.css +1 -1
  134. package/src/components/states.css +1 -1
  135. package/src/components/stepper.css +2 -2
  136. package/src/components/table.css +15 -15
  137. package/src/components/tabs.css +3 -3
  138. package/src/components/timeline.css +1 -1
  139. package/src/components/timepicker.css +1 -1
  140. package/src/components/toast.css +3 -3
  141. package/src/components/tooltip.css +1 -1
  142. package/src/components/tree.css +2 -2
  143. package/tokens/breakpoints.d.ts +2 -2
  144. package/tokens/breakpoints.mjs +3 -3
  145. package/tokens/tokens.json +1 -1
@@ -1,4 +1,4 @@
1
- /* Freeday tokens GENERATED by tokens/build.mjs. Do not edit by hand. */
1
+ /* Freeday tokens. GENERATED by tokens/build.mjs. Do not edit by hand. */
2
2
  :root {
3
3
  --azure-50: #eff4ff;
4
4
  --azure-100: #dbe6fe;
@@ -178,7 +178,7 @@
178
178
  --focus-ring-width: 2px;
179
179
  }
180
180
  /* The SYSTEM default stays root-scoped: it is a statement about the document, and dropping :root
181
- * here would match every element that is not itself [data-theme="light"] which would re-darken
181
+ * here would match every element that is not itself [data-theme="light"], which would re-darken
182
182
  * the children of a light panel, since they carry no attribute of their own. */
183
183
  @media (prefers-color-scheme: dark) {
184
184
  :root:not([data-theme="light"]) {
@@ -240,7 +240,7 @@
240
240
  }
241
241
  /* The two EXPLICIT opt-ins are deliberately NOT scoped to :root, for the same reason as density
242
242
  * below: these are inheriting custom properties, so data-theme on any ancestor re-themes just that
243
- * subtree a dark brand panel beside a light form is an ordinary layout, and it should not require
243
+ * subtree, a dark brand panel beside a light form is an ordinary layout, and it should not require
244
244
  * re-colouring each component by hand. The root still matches, so data-theme on <html> is unchanged.
245
245
  * Both keep the same specificity (0,1,0) as :root and come after it, so they still win there; and
246
246
  * --light after --dark means a light island inside a dark region wins in turn. */
@@ -356,7 +356,7 @@
356
356
  --chart-tick: var(--slate-400);
357
357
  --focus-ring: var(--azure-600);
358
358
  }
359
- /* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit
359
+ /* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit,
360
360
  * putting the attribute on any ancestor (a route wrapper, one section) densifies just that
361
361
  * subtree, which is how density is actually decided: per screen, not per app. The root still
362
362
  * matches, so setting data-density on the html element keeps working exactly as before. */
@@ -368,7 +368,7 @@
368
368
  --control-h: 2rem;
369
369
  }
370
370
  /* ...and the way back out. Inheritance only runs downhill: once the root is compact every subtree is
371
- * compact, and before this rule existed a data-density="comfortable" wrapper matched nothing so the
371
+ * compact, and before this rule existed a data-density="comfortable" wrapper matched nothing, so the
372
372
  * comment above was true in one direction only. An app that is dense overall and carries one region
373
373
  * of shared chrome (a header, a toolbar, a footer shared with a sibling product) had to restate the
374
374
  * defaults by hand, which is exactly the local copy that goes stale when the kit retunes them. */
@@ -5,7 +5,7 @@ Copilot…). No model has Freeday in its training data, so an agent that is mere
5
5
  will invent class names or silently fall back to Bootstrap/Tailwind conventions. This file is the
6
6
  fix: paste the block below into the consuming project's agent instruction file, once.
7
7
 
8
- > Working on **the kit itself**, not a consuming app? That's [`../CLAUDE.md`](../CLAUDE.md) — this
8
+ > Working on **the kit itself**, not a consuming app? That's [`../CLAUDE.md`](../CLAUDE.md). This
9
9
  > file is about *using* the published package.
10
10
 
11
11
  ---
@@ -23,7 +23,7 @@ markup) with **typed components for Vue, React and Blazor** layered on top. Most
23
23
  markup + classes; ten interactive components also ship a typed wrapper, and in those three stacks
24
24
  the wrapper is the correct way to use them.
25
25
 
26
- **0. First decide which entry point this project uses. This is not an optimisation get it wrong
26
+ **0. First decide which entry point this project uses. This is not an optimisation. Get it wrong
27
27
  and the code looks correct and fails later.**
28
28
 
29
29
  | This project's stack | Import the ten components from | Binding |
@@ -31,38 +31,38 @@ and the code looks correct and fails later.**
31
31
  | Vue 3 | `@cahyo-dimas/freeday/vue` | `v-model` |
32
32
  | React 18/19 | `@cahyo-dimas/freeday/react` | `value` + `onChange` |
33
33
  | Blazor (net8.0) | `@using Freeday.Blazor` (RCL) | `@bind-Value` |
34
- | Static HTML, Svelte, server-rendered templates… | no wrapper raw markup + the enhancer script | `fdy-*` DOM events |
34
+ | Static HTML, Svelte, server-rendered templates… | no wrapper: raw markup + the enhancer script | `fdy-*` DOM events |
35
35
 
36
36
  The ten: **FdyCombo · FdyDatepicker · FdyDateRange · FdyAutocomplete · FdyCascade · FdyCfl ·
37
37
  FdyChart · FdyTable · FdyModal · FdyDrawer**. In Vue/React/Blazor, **never hand-write the raw
38
- markup + enhancer for these ten.** The raw path *appears* to work the enhancer auto-initialises
39
- once on `DOMContentLoaded` and the first render is correct then fails quietly: DOM your framework
38
+ markup + enhancer for these eleven.** The raw path *appears* to work: the enhancer auto-initialises
39
+ once on `DOMContentLoaded` and the first render is correct. Then it fails quietly: DOM your framework
40
40
  renders later is never hydrated, and the widget's state lives in the DOM instead of in your
41
41
  framework's state.
42
42
 
43
43
  Everything else is the same in every stack: plain `fdy-*` markup (button, card, badge, alert,
44
44
  table markup, layout…). For the *other* interactive components (chips, stepper, input mask, file
45
45
  upload, tree, tabs, menu, rating, slider, form validation, carousel, timepicker) there is no
46
- wrapper use the raw markup and hydrate it:
46
+ wrapper, use the raw markup and hydrate it:
47
47
 
48
- - **Vue / React** `useFreeday(rootRef)` from the same import path, plus `import '@cahyo-dimas/freeday'`
49
- once at app entry to register the enhancers. (The ten typed components do **not** need this: they
48
+ - **Vue / React:** `useFreeday(rootRef)` from the same import path, plus `import '@cahyo-dimas/freeday'`
49
+ once at app entry to register the enhancers. (The eleven typed components do **not** need this: they
50
50
  are native Vue/React implementations of the same markup, not wrappers over the enhancer.)
51
- - **Blazor** `FreedayBlazor.initAll` interop. (Here the typed components *are* thin wrappers over
51
+ - **Blazor:** `FreedayBlazor.initAll` interop. (Here the typed components *are* thin wrappers over
52
52
  the enhancers, so the enhancer script is always required.)
53
53
 
54
54
  **Before writing or editing any markup/CSS, read these (they ship inside the package):**
55
- - `node_modules/@cahyo-dimas/freeday/COMPONENTS.md` every class that exists, with minimal markup
55
+ - `node_modules/@cahyo-dimas/freeday/COMPONENTS.md` lists every class that exists, with minimal markup
56
56
  skeletons, enhancer hooks and the a11y contract per component. **The class list is closed:
57
- if a class is not in that file, it does not exist do not invent one.**
58
- - `node_modules/@cahyo-dimas/freeday/USAGE.md` the doctrine: which token/role/shadow to use when.
59
- - `node_modules/@cahyo-dimas/freeday/docs/reference-screen.html` one complete screen, assembled
57
+ if a class is not in that file, it does not exist. Do not invent one.**
58
+ - `node_modules/@cahyo-dimas/freeday/USAGE.md` is the doctrine: which token/role/shadow to use when.
59
+ - `node_modules/@cahyo-dimas/freeday/docs/reference-screen.html` is one complete screen, assembled
60
60
  the intended way. Copy this structure for a new screen.
61
61
 
62
62
  **Non-negotiables:**
63
63
  1. No raw hex or px in app CSS. Use tokens: `var(--color-primary)`, `var(--space-4)` (4px scale),
64
64
  `var(--radius-md)`, `var(--shadow-1)`, `var(--dur-2)`.
65
- 2. Components only touch semantic tokens (`--color-*`) never the primitive ramp (`--azure-600`).
65
+ 2. Components only touch semantic tokens (`--color-*`), never the primitive ramp (`--azure-600`).
66
66
  3. `.fdy-btn` is already the primary action (there is no `--primary`). One per screen; everything
67
67
  else is `--ghost` or `--text`.
68
68
  4. Three title roles only: `.fdy-title-page` (one `<h1>`) / `.fdy-title-section` / `.fdy-title-card`.
@@ -72,40 +72,40 @@ wrapper — use the raw markup and hydrate it:
72
72
  Icon-only buttons need `aria-label`. Status is never colour-only.
73
73
  7. Interactive components need their enhancer script loaded (see the table in COMPONENTS.md); in an
74
74
  SPA, re-hydrate dynamic DOM with `useFreeday` / `FreedayBlazor.initAll`. This applies to the
75
- components **without** a typed wrapper for the ten in step 0, use the wrapper instead.
75
+ components **without** a typed wrapper. For the eleven in step 0, use the wrapper instead.
76
76
  8. Freeday owns components + tokens, **not layout**. Grids/stacks/one-off gaps come from our own
77
- layout layer build its theme on `var(--space-N)` so both systems stay in step.
77
+ layout layer. Build its theme on `var(--space-N)` so both systems stay in step.
78
78
  ```
79
79
 
80
80
  Adjust the paths if the package lives somewhere else (a workspace, a vendored copy, `wwwroot/` for
81
- Blazor). Then verify the agent can actually read those files an agent that can't open
81
+ Blazor). Then verify the agent can actually read those files. An agent that can't open
82
82
  `node_modules` will keep guessing.
83
83
 
84
84
  ## 2. What ships in the package
85
85
 
86
86
  | File | What it answers |
87
87
  |---|---|
88
- | `COMPONENTS.md` | The complete class surface what exists, its modifiers, minimal markup, a11y. |
89
- | `USAGE.md` | The doctrine which token/role/shadow/emphasis to use when. |
88
+ | `COMPONENTS.md` | The complete class surface: what exists, its modifiers, minimal markup, a11y. |
89
+ | `USAGE.md` | The doctrine: which token/role/shadow/emphasis to use when. |
90
90
  | `docs/getting-started.md` | Install + import + theme, per stack (Static HTML · Vue · React · Blazor). |
91
91
  | `docs/integrations.md` | How to bridge third-party libraries (validation, charts, dates, i18n…). |
92
92
  | `docs/reference-screen.html` | A full screen assembled from the shell down. Open it in a browser. |
93
93
  | `docs/agent-onboarding.md` | This file. |
94
- | `CHANGELOG.md` | **What changed between the version this project had and the one it has now** read it after every upgrade; each entry says what broke, what is new, and why. |
95
- | `dist/` | Built CSS + enhancers. **`freeday.bundle.css` = tokens + components** (what `@cahyo-dimas/freeday/css` resolves to); `freeday.css` is components **only**, `freeday.tokens.css` tokens only linking `freeday.css` alone leaves every `var(--…)` unresolved. Plus `freeday-*.js` and the `.d.ts` files. |
94
+ | `CHANGELOG.md` | **What changed between the version this project had and the one it has now.** Read it after every upgrade; each entry says what broke, what is new, and why. |
95
+ | `dist/` | Built CSS + enhancers. **`freeday.bundle.css` = tokens + components** (what `@cahyo-dimas/freeday/css` resolves to); `freeday.css` is components **only**, `freeday.tokens.css` tokens only, so linking `freeday.css` alone leaves every `var(--…)` unresolved. Plus `freeday-*.js` and the `.d.ts` files. |
96
96
  | `src/components/*.css` | The authoritative source for every class, when a doc is ambiguous. |
97
- | `tokens/tokens.json` | Every token in W3C DTCG format machine-readable. |
97
+ | `tokens/tokens.json` | Every token in W3C DTCG format, machine-readable. |
98
98
  | `adapters/vue` · `adapters/react` · `adapters/blazor` | Typed wrappers, 10 components each. |
99
99
 
100
100
  The live docs (with an interactive playground) are at
101
- <https://cahyo-dimas.github.io/freeday-ui-kit/>, and the repo including three complete example
102
- apps under `examples/` (Vue, React, Blazor) that are **not** in the npm tarball is at
101
+ <https://cahyo-dimas.github.io/freeday-ui-kit/>, and the repo (including three complete example
102
+ apps under `examples/` (Vue, React, Blazor) that are **not** in the npm tarball) is at
103
103
  <https://github.com/cahyo-dimas/freeday-ui-kit>.
104
104
 
105
105
  ### After an upgrade, read the changelog first
106
106
 
107
107
  `npm i @cahyo-dimas/freeday@latest` does not tell you what you gained. Read
108
- `node_modules/@cahyo-dimas/freeday/CHANGELOG.md` down to the version this project was on before
108
+ `node_modules/@cahyo-dimas/freeday/CHANGELOG.md` down to the version this project was on before,
109
109
  it is written for exactly this moment, and it is the difference between adopting a new affordance
110
110
  and re-implementing it locally.
111
111
 
@@ -130,31 +130,31 @@ Recent additions most likely to replace something an app hand-rolled (all detail
130
130
 
131
131
  The order matters; skipping to components is what produces flat, identical-card screens.
132
132
 
133
- 0. **Pick the screen shape first.** Which archetype is this dashboard, master-detail, kanban,
133
+ 0. **Pick the screen shape first.** Which archetype is this: dashboard, master-detail, kanban,
134
134
  wizard, POS…? The repo's
135
135
  [`reference/README.md`](https://github.com/cahyo-dimas/freeday-ui-kit/blob/main/reference/README.md)
136
136
  maps 15 archetypes to the exact primitives that compose each one, and says plainly which shapes
137
137
  the kit has **no** component for (kanban columns, calendar month grid, chat bubbles, canvas) so
138
- you build the frame instead of inventing a class. Not in the npm package read it on GitHub.
139
- 1. **Shell** is `.fdy-app` already in place (usually once, in the app layout)? If not, copy it
138
+ you build the frame instead of inventing a class. Not in the npm package, so read it on GitHub.
139
+ 1. **Shell:** is `.fdy-app` already in place (usually once, in the app layout)? If not, copy it
140
140
  from `docs/getting-started.md` §The app shell.
141
- 2. **Theme** `data-theme="light|dark"` + `data-density="comfortable|compact"`, normally on
141
+ 2. **Theme:** `data-theme="light|dark"` + `data-density="comfortable|compact"`, normally on
142
142
  `<html>`, set once at the root. Use `compact` for table-heavy back-office screens. Both attributes
143
143
  also work on **any ancestor**: `<section data-theme="dark">` inverts that region and every
144
144
  component inside it follows, so never hand-colour an inverted panel.
145
- 3. **Fonts** the package ships **no** `@font-face`. Load Sora / IBM Plex Sans / JetBrains Mono
145
+ 3. **Fonts:** the package ships **no** `@font-face`. Load Sora / IBM Plex Sans / JetBrains Mono
146
146
  yourself, or override `--font-display`/`--font-body`/`--font-mono`. Skipping this reads as
147
147
  "unfinished design", not "missing dependency".
148
- 4. **Page frame** `.fdy-page` + `.fdy-page__header` (eyebrow + `.fdy-title-page` + desc on the
148
+ 4. **Page frame:** `.fdy-page` + `.fdy-page__header` (eyebrow + `.fdy-title-page` + desc on the
149
149
  left, **one** primary action on the right).
150
- 5. **Sections** one `.fdy-page-section` per region, each with a `.fdy-title-section`.
151
- 6. **Components** from `COMPONENTS.md`, inside the sections.
152
- 7. **Verify** the checklist in §5.
150
+ 5. **Sections:** one `.fdy-page-section` per region, each with a `.fdy-title-section`.
151
+ 6. **Components:** from `COMPONENTS.md`, inside the sections.
152
+ 7. **Verify:** the checklist in §5.
153
153
 
154
154
  ## 4. Migrating an existing UI to Freeday
155
155
 
156
156
  Migration is a **class-and-structure swap**, not a rewrite. Keep the app's DOM semantics; replace
157
- the styling layer. Rough equivalents always confirm the target class in `COMPONENTS.md`, and note
157
+ the styling layer. Rough equivalents, but always confirm the target class in `COMPONENTS.md`, and note
158
158
  that Freeday deliberately has **no** layout/spacing utilities, so grid/flex/margin classes stay with
159
159
  your own layout layer:
160
160
 
@@ -174,19 +174,19 @@ your own layout layer:
174
174
  | `table table-striped` / `MudTable` | `fdy-table` in `fdy-table-wrap`; interactive → `fdy-datatable` |
175
175
  | `badge bg-success` / `MudChip` (status) | `fdy-badge fdy-badge--success` |
176
176
  | `alert alert-danger` | `fdy-alert fdy-alert--danger` + `role="alert"` |
177
- | `modal` / `MudDialog` | `<dialog class="fdy-modal">` (native drop the JS backdrop plumbing) |
177
+ | `modal` / `MudDialog` | `<dialog class="fdy-modal">` (native, so drop the JS backdrop plumbing) |
178
178
  | `offcanvas` / `MudDrawer` | `<dialog class="fdy-drawer">` + `data-fdy-drawer` |
179
179
  | `nav nav-tabs` | `fdy-tabs` + `data-fdy-tabs` |
180
180
  | `breadcrumb` / `pagination` | `fdy-breadcrumb` / `fdy-pagination` (same `<nav><ol>` structure) |
181
181
  | `spinner-border` / `progress` | `fdy-spinner` / `fdy-progress` + `__bar` |
182
- | `toast` container + JS | `Freeday.toast({…})` imperative, no markup to author |
182
+ | `toast` container + JS | `Freeday.toast({…})`, imperative, no markup to author |
183
183
  | `text-muted` | `fdy-text-muted` |
184
184
  | `d-none` / `visually-hidden` | `fdy-hidden` / `fdy-visually-hidden` |
185
- | `container` / `row` / `col-*` / `mb-3` / `gap-2` | **stays yours** Freeday ships no layout utilities |
185
+ | `container` / `row` / `col-*` / `mb-3` / `gap-2` | **stays yours**: Freeday ships no layout utilities |
186
186
 
187
187
  Order of work that avoids a half-migrated mess:
188
188
 
189
- 1. Load Freeday's CSS and **turn off the old framework's reset/preflight** `base.css` is the
189
+ 1. Load Freeday's CSS and **turn off the old framework's reset/preflight**, because `base.css` is the
190
190
  reset now. Two resets fighting is the usual source of "everything looks slightly off".
191
191
  2. Shell + theme attributes first, so tokens resolve everywhere.
192
192
  3. Then screen by screen: page frame → sections → controls. Convert a whole screen at a time;
@@ -194,15 +194,15 @@ Order of work that avoids a half-migrated mess:
194
194
  4. Delete the old framework's CSS only when no screen references it, then grep for leftover class
195
195
  prefixes.
196
196
  5. Replace hand-rolled modal/drawer/dropdown JS with the native-`<dialog>` components and the
197
- enhancers that is usually where the most code disappears.
197
+ enhancers, which is usually where the most code disappears.
198
198
 
199
199
  ## 5. Verification checklist (before claiming a screen is done)
200
200
 
201
201
  - Every `fdy-*` class used appears in `COMPONENTS.md`. Grep the diff for `fdy-` and check.
202
202
  - No raw hex/rgb/px in the diff's CSS. Grep for `#` and `px`.
203
203
  - Exactly one `.fdy-btn` without a variant modifier on the screen; one `.fdy-title-page`.
204
- - Toggle `data-theme="dark"` on `<html>` nothing becomes unreadable, no hard-coded white/black.
205
- - Toggle `data-density="compact"` the layout still holds.
204
+ - Toggle `data-theme="dark"` on `<html>`: nothing becomes unreadable, no hard-coded white/black.
205
+ - Toggle `data-density="compact"`: the layout still holds.
206
206
  - Keyboard: Tab reaches every control, focus is always visible, Esc closes overlays.
207
207
  - Form errors carry `aria-invalid` + a linked message; icon-only buttons have `aria-label`.
208
208
  - The interactive components on the screen have their enhancer loaded, and SPA-rendered DOM is
@@ -15,43 +15,43 @@ A step-by-step guide to adopting Freeday in **your new project**. Pick your stac
15
15
  Freeday = **CSS** (semantic tokens + `fdy-*` classes) + **zero-dependency JS enhancers** (optional).
16
16
 
17
17
  1. **Static vs interactive.** Static components (button, card, badge, plain input, layout) need
18
- only the **`fdy-*` classes** no JS. Interactive components (select/combo, cascade, date/time
18
+ only the **`fdy-*` classes**, no JS. Interactive components (select/combo, cascade, date/time
19
19
  picker, table, dropzone, form validation, input mask, chip) need the **JS enhancers**.
20
- 2. **The enhancer is the source of truth *on the raw path*.** You don't re-implement components;
20
+ 2. **The enhancer is the source of truth, *on the raw path*.** You don't re-implement components;
21
21
  the enhancer owns the widget's DOM. You **listen for `fdy-*` events** (all bubbling
22
22
  `CustomEvent`s, data in `event.detail`) → store them in your framework state. Event/API contract
23
23
  table: [`integrations.md` §Event & API contract](integrations.md).
24
- **On Vue, React or Blazor this is not the path to take for ten of the components** `FdyCombo`,
24
+ **On Vue, React or Blazor this is not the path to take for eleven of the components.** `FdyCombo`,
25
25
  `FdyDatepicker`, `FdyDateRange`, `FdyAutocomplete`, `FdyCascade`, `FdyCfl`, `FdyChart`,
26
26
  `FdyTable`, `FdyModal`, `FdyDrawer` ship typed wrappers that own the state properly (Vue and
27
27
  React re-implement the interaction natively; Blazor wraps the enhancer over interop). Use them;
28
28
  the raw path is for the components without a wrapper, and for stacks without an adapter.
29
29
  3. **Hydrate dynamic DOM.** Enhancers auto-init once on `DOMContentLoaded`. DOM an SPA renders
30
30
  **after** that must be re-hydrated: `window.Freeday<X>.initAll(el)` (idempotent, safe to repeat).
31
- Each framework's adapter wraps this you don't call it manually.
31
+ Each framework's adapter wraps this, so you don't call it manually.
32
32
  4. **Theme via `data-*`.** `data-theme="light|dark"` (all semantic tokens switch) +
33
33
  `data-density="comfortable|compact"` (control height, for data-dense screens). Normally on
34
34
  `<html>`; change at runtime with `document.documentElement.dataset.theme = 'dark'`. Both also
35
- work on **any ancestor** these are inheriting custom properties, so `<section data-theme="dark">`
35
+ work on **any ancestor**, because these are inheriting custom properties, so `<section data-theme="dark">`
36
36
  inverts just that region and every component inside it follows. See [`USAGE.md`](../USAGE.md) §5b.
37
37
  5. **3-tier token rule.** Components only touch **Tier 2/3** (`var(--color-primary)`,
38
38
  `var(--space-4)`, `var(--radius-md)`…). **Never** write raw hex/px.
39
39
  6. **Scope: components + tokens, deliberately *not* layout.** Freeday ships components and tokens;
40
40
  the only layout helpers are `.fdy-hidden` / `.fdy-visually-hidden`. Stacks, grids, gaps and sizing
41
- come from **your** layout layer pair Freeday with a utility framework (Tailwind, UnoCSS…) run
41
+ come from **your** layout layer. Pair Freeday with a utility framework (Tailwind, UnoCSS…) run
42
42
  **utilities-only, preflight OFF** (Freeday's `base.css` is your reset). Two consequences worth
43
43
  knowing up front:
44
- - **base.css is a *light* reset** it does not strip `ul`/`ol`/`p` margins. With preflight off, a
44
+ - **base.css is a *light* reset.** It does not strip `ul`/`ol`/`p` margins. With preflight off, a
45
45
  semantic `<ul>` keeps native bullets + a 40px indent; add **`.fdy-list-reset`** (or use a Freeday
46
46
  list component) on such lists.
47
47
  - **The spacing scale is public.** `--space-0`…`--space-24`, `--radius-*`, `--dur-*` etc. are real
48
- custom properties in `dist/freeday.tokens.css` **define your utility theme in terms of them**
48
+ custom properties in `dist/freeday.tokens.css`, so **define your utility theme in terms of them**
49
49
  (`spacing: { 4: 'var(--space-4)' }`) so both systems stay in step. `data-density="compact"` steps
50
50
  `--control-h` **and** the mid-range spacing scale (`--space-3`…`--space-6`), so Freeday components
51
- densify and if your utility theme is built on `var(--space-N)`, density reaches your utilities too.
52
- 7. **Load the fonts the package does not.** The type tokens *name* **Sora** (display), **IBM Plex
51
+ densify, and if your utility theme is built on `var(--space-N)`, density reaches your utilities too.
52
+ 7. **Load the fonts yourself. The package does not.** The type tokens *name* **Sora** (display), **IBM Plex
53
53
  Sans** (body) and **JetBrains Mono** (data), but Freeday bundles no `@font-face` and no font files.
54
- Load them yourself, or the kit renders in the system fallback which reads as "unfinished design",
54
+ Load them yourself, or the kit renders in the system fallback, which reads as "unfinished design",
55
55
  not "missing dependency". One line with [Fontsource](https://fontsource.org):
56
56
  ```css
57
57
  @import '@fontsource/sora/600.css'; @import '@fontsource/sora/700.css';
@@ -59,23 +59,23 @@ Freeday = **CSS** (semantic tokens + `fdy-*` classes) + **zero-dependency JS enh
59
59
  ```
60
60
  (Or a `<link>` to your own self-hosted copies, or override `--font-display`/`--font-body`/`--font-mono`
61
61
  to faces you already ship. If you keep a system-sans fallback, consider softening
62
- `--tracking-tighter` on headings it's tuned for Sora's proportions.)
62
+ `--tracking-tighter` on headings, since it's tuned for Sora's proportions.)
63
63
  8. **Start from the shell, then compose.** Every application goes inside **`.fdy-app`** (see below);
64
- inside it, assemble screens from the composition primitives `.fdy-page`, `.fdy-page__header`,
65
- `.fdy-page-section`, `.fdy-toolbar`, `.fdy-stats`/`.fdy-stat` and the type roles (`.fdy-title-page`
64
+ inside it, assemble screens from the composition primitives (`.fdy-page`, `.fdy-page__header`,
65
+ `.fdy-page-section`, `.fdy-toolbar`, `.fdy-stats`/`.fdy-stat`) and the type roles (`.fdy-title-page`
66
66
  / `-section` / `-card`), not by re-using `.fdy-card__title` for everything. **Which token/role/shadow
67
- to use when lives in [`USAGE.md`](../USAGE.md)** read it once; it's what makes screens cohere.
67
+ to use when lives in [`USAGE.md`](../USAGE.md)**. Read it once; it's what makes screens cohere.
68
68
 
69
69
  ---
70
70
 
71
71
  ## The app shell (start here)
72
72
 
73
- Every Freeday application goes inside **`.fdy-app`** the frame that holds a top bar, a sidebar, and
73
+ Every Freeday application goes inside **`.fdy-app`**, the frame that holds a top bar, a sidebar, and
74
74
  the scrolling content. Don't hand-roll one from flexbox; the responsive sidebar + backdrop are built in.
75
75
  The nav toggle is one line: toggle `.fdy-app--nav-open` (mobile drawer) / `.fdy-app--nav-collapsed`
76
76
  (desktop) on the `.fdy-app` element from the `__navtoggle` button's click.
77
77
 
78
- The nesting is not free-form `.fdy-app` is a flex **row** of `[sidebar | content]`, and `__content`
78
+ The nesting is not free-form: `.fdy-app` is a flex **row** of `[sidebar | content]`, and `__content`
79
79
  is the column that holds the topbar and the main area (it gives the sticky topbar a tall containing
80
80
  block to travel in). The brand belongs in the **sidebar**, sized to match the topbar's height:
81
81
 
@@ -109,11 +109,11 @@ block to travel in). The brand belongs in the **sidebar**, sized to match the to
109
109
  </div>
110
110
  ```
111
111
 
112
- `.fdy-app__main` already carries the page padding (`--space-8`, `--space-5` on mobile) don't wrap
112
+ `.fdy-app__main` already carries the page padding (`--space-8`, `--space-5` on mobile), so don't wrap
113
113
  your screen in another padded box. The toggle's two states split at **720px**: above it,
114
114
  `.fdy-app--nav-collapsed` collapses the sidebar to zero width; at or below it,
115
115
  `.fdy-app--nav-open` slides the sidebar in as an off-canvas drawer over the backdrop. A complete,
116
- working version of all of this including the toggle script is
116
+ working version of all of this, including the toggle script, is
117
117
  [`reference-screen.html`](reference-screen.html).
118
118
 
119
119
  Then compose the screen inside `__main` with `.fdy-page` / `.fdy-page__header` / `.fdy-page-section`
@@ -125,10 +125,10 @@ token to use where.
125
125
 
126
126
  ## Static HTML (no build)
127
127
 
128
- Good for plain `.html` pages / templates no bundler, no npm.
128
+ Good for plain `.html` pages / templates, with no bundler and no npm.
129
129
 
130
130
  ### 1. Get the dist files into your project
131
- `dist/` is committed, so there's no build step. Easiest way use npm once just to download, then
131
+ `dist/` is committed, so there's no build step. Easiest way: use npm once just to download, then
132
132
  copy the files (vendor them):
133
133
  ```bash
134
134
  npm i @cahyo-dimas/freeday
@@ -184,7 +184,7 @@ live docs also have a copy button per component.
184
184
  ```bash
185
185
  npm i @cahyo-dimas/freeday
186
186
  ```
187
- Lands in `package.json` as `"@cahyo-dimas/freeday": "^1.34.0"` (public npm package). `dist/` is
187
+ Lands in `package.json` as `"@cahyo-dimas/freeday": "^2.0.0"` (public npm package). `dist/` is
188
188
  committed and published → no build step; `npm ci` runs without auth.
189
189
 
190
190
  ### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
@@ -229,7 +229,7 @@ const onDate = (e: Event) => { form.dueDate = (e as CustomEvent<FdyDatepicke
229
229
  ```
230
230
 
231
231
  **Gotcha:** if TypeScript complains about `import '@cahyo-dimas/freeday/css'`, make sure `env.d.ts`
232
- has `/// <reference types="vite/client" />`. For **Nuxt/SSR**, enhancers are client-only wrap them
232
+ has `/// <reference types="vite/client" />`. For **Nuxt/SSR**, enhancers are client-only, so wrap them
233
233
  in `onMounted`/`<ClientOnly>`.
234
234
 
235
235
  Full working example: [`examples/vue-faktur/`](../examples/vue-faktur/).
@@ -296,13 +296,13 @@ export function Panel() {
296
296
  }
297
297
  ```
298
298
 
299
- **Gotcha:** because the enhancer owns the widget DOM, don't double-control it from React store the
299
+ **Gotcha:** because the enhancer owns the widget DOM, don't double-control it from React. Store the
300
300
  value from `event.detail` in state/ref; don't set the DOM `value` back. `StrictMode` mounts twice in
301
301
  dev; `useFreeday` is idempotent, so it's safe.
302
302
 
303
303
  ### 5. Alternative: typed controlled components (`FdyCombo` · `FdyDatepicker` · `FdyDateRange` · `FdyAutocomplete` · `FdyCascade` · `FdyCfl` · `FdyChart`)
304
304
  For fields you'd normally write as a native `<select>`/`<input type="date">`,
305
- `@cahyo-dimas/freeday/react` also exports typed **controlled** components plain `value`/`onChange`,
305
+ `@cahyo-dimas/freeday/react` also exports typed **controlled** components with plain `value`/`onChange`,
306
306
  no manual event bubbling (parity with the Vue `v-model` components above):
307
307
  ```tsx
308
308
  import { FdyCombo } from '@cahyo-dimas/freeday/react';
@@ -320,7 +320,7 @@ function StatusField({ value, onChange }: { value: Status; onChange: (v: Status)
320
320
  }
321
321
  ```
322
322
  `FdyDatepicker`, `FdyCfl` (async choose-from-list), and `FdyChart` share the same shape (typed
323
- `value`/`onChange`, or `series`/`values` for `FdyChart`) see [`integrations.md`](integrations.md)
323
+ `value`/`onChange`, or `series`/`values` for `FdyChart`). See [`integrations.md`](integrations.md)
324
324
  and `examples/react-faktur/src/App.tsx` for the full patterns. **Vite works with no extra config**
325
325
  (esbuild transpiles the `.tsx` source directly); **Next.js** consumers may need
326
326
  `transpilePackages: ['@cahyo-dimas/freeday']` in `next.config.js`.
@@ -331,9 +331,9 @@ Full working example: [`examples/react-faktur/`](../examples/react-faktur/).
331
331
 
332
332
  ## Blazor (WASM)
333
333
 
334
- Blazor doesn't use npm Freeday is served as **static files** in `wwwroot/`.
334
+ Blazor doesn't use npm. Freeday is served as **static files** in `wwwroot/`.
335
335
 
336
- > **Prefer the native components?** Jump to [§4 the `Freeday.Blazor` RCL](#4-recommended-native-typed-components-freedayblazor-rcl):
336
+ > **Prefer the native components?** Jump to [§4, the `Freeday.Blazor` RCL](#4-recommended-native-typed-components-freedayblazor-rcl):
337
337
  > typed `<FdyX>` with `@bind`, no manual JS interop. Steps 1–2 (assets + scripts) still apply; step 3
338
338
  > below (the raw enhancer + event bridge) is the underlying mechanism and the fallback for markup the
339
339
  > RCL doesn't cover.
@@ -365,7 +365,7 @@ Load `freeday.js` then `freeday-blazor.js` **before** `blazor.webassembly.js`:
365
365
  <script src="_framework/blazor.webassembly.js"></script>
366
366
  </body>
367
367
  ```
368
- > Use the global IIFE (`window.FreedayBlazor`), **not** an ES module so it passes strict-MIME on static hosts.
368
+ > Use the global IIFE (`window.FreedayBlazor`), **not** an ES module, so it passes strict-MIME on static hosts.
369
369
 
370
370
  ### 3. Hydrate + bridge events in code-behind (`.razor.cs`)
371
371
  In `OnAfterRenderAsync(firstRender)`: `initAll`, then `on(...)` per event → `[JSInvokable]` methods.
@@ -402,7 +402,7 @@ public partial class Panel : ComponentBase, IAsyncDisposable
402
402
  }
403
403
  ```
404
404
  ```razor
405
- @* Panel.razor @ref on the subtree container, fdy-* classes + data-fdy-* hooks in the markup *@
405
+ @* Panel.razor: @ref on the subtree container, fdy-* classes + data-fdy-* hooks in the markup *@
406
406
  <div @ref="_root">
407
407
  <button class="fdy-btn fdy-btn--primary" type="button">Save</button>
408
408
  <div data-fdy-cascade></div>
@@ -415,7 +415,7 @@ to flip the theme. Event DTOs are deserialized case-insensitively by Blazor.
415
415
  ### 4. Recommended: native typed components (`Freeday.Blazor` RCL)
416
416
 
417
417
  Instead of hand-writing `fdy-*` markup + the interop above, reference the **Razor Class Library** and
418
- use typed `<FdyX>` components with `@bind` the Blazor equivalent of the Vue `v-model` / React
418
+ use typed `<FdyX>` components with `@bind`, the Blazor equivalent of the Vue `v-model` / React
419
419
  `value`/`onChange` adapters. Place the Freeday repo near your solution and add a project reference:
420
420
  ```xml
421
421
  <!-- YourApp.csproj -->
@@ -428,7 +428,7 @@ use typed `<FdyX>` components with `@bind` — the Blazor equivalent of the Vue
428
428
  Load `freeday.js` + `freeday-blazor.js` exactly as in step 2 (the components still hydrate over the
429
429
  kit's CSS/enhancers), then bind:
430
430
  ```razor
431
- @* Invoice.razor no @ref, no manual JS interop, no [JSInvokable] *@
431
+ @* Invoice.razor: no @ref, no manual JS interop, no [JSInvokable] *@
432
432
  <FdyCombo TValue="string" @bind-Value="_status" Options="_statusOptions" AriaLabelledby="lbl-status" />
433
433
  <FdyDatepicker @bind-Value="_dueDate" Label="Due date" />
434
434
  <FdyTable TRow="Invoice" Columns="_cols" Rows="_rows" RowKey="@(i => i.Code)"
@@ -444,7 +444,7 @@ or controlled `Sort`/`Filters`/`Page` for a server-paged table; `RowActivatable`
444
444
  `select`-type control also takes `Disabled`/`Readonly`/`Invalid`. The RCL targets **net8.0** and is
445
445
  consumed as source (`<ProjectReference>`); `.NET bin/obj` never ships in the npm tarball.
446
446
 
447
- Full working example (all ten): [`examples/blazor-faktur/`](../examples/blazor-faktur/)
447
+ Full working example: [`examples/blazor-faktur/`](../examples/blazor-faktur/),
448
448
  `Pages/ComponentsDemo.razor`.
449
449
 
450
450
  ---
@@ -452,8 +452,8 @@ Full working example (all ten): [`examples/blazor-faktur/`](../examples/blazor-f
452
452
  ## Verify (every stack)
453
453
 
454
454
  Run the project → check two things:
455
- 1. **CSS connected** buttons/cards are styled (not plain HTML).
456
- 2. **Enhancers connected** interactive components come alive (e.g. datepicker/combo open on
455
+ 1. **CSS connected:** buttons/cards are styled (not plain HTML).
456
+ 2. **Enhancers connected:** interactive components come alive (e.g. datepicker/combo open on
457
457
  click), and `event.detail` reaches your state.
458
458
 
459
459
  If the visuals are plain → the CSS didn't load. If visuals are fine but widgets are dead → the
@@ -3,7 +3,7 @@
3
3
  Freeday is a **dependency-free foundation**: tokens + accessible markup + vanilla enhancers. This
4
4
  document maps each area to the ecosystem library you'd normally install in a real project, **when**
5
5
  Freeday's built-ins are enough, and **how to bridge**. The goal: when you start a new project, open
6
- this one file no more hunting around.
6
+ this one file, so there's no hunting around.
7
7
 
8
8
  > Golden rule: **Freeday owns the look (tokens + markup + a11y); the library owns the engine
9
9
  > (heavy logic).** Don't duplicate. Connect through the 3 mechanisms below.
@@ -14,11 +14,11 @@ this one file — no more hunting around.
14
14
 
15
15
  Every integration comes down to one of these:
16
16
 
17
- 1. **`fdy-*` events** listen to enhancer output, forward it to framework/library state.
17
+ 1. **`fdy-*` events:** listen to enhancer output, forward it to framework/library state.
18
18
  All events are bubbling `CustomEvent`s; the data is in `event.detail`.
19
- 2. **Init hook `window.Freeday<X>.initAll(el)`** call it again after DOM is rendered
19
+ 2. **Init hook `window.Freeday<X>.initAll(el)`:** call it again after DOM is rendered
20
20
  dynamically (Vue `onMounted`, React `useEffect`, Blazor `OnAfterRenderAsync`). Idempotent.
21
- 3. **Color tokens** match a library's colors to the active theme:
21
+ 3. **Color tokens:** match a library's colors to the active theme:
22
22
  ```js
23
23
  // Read a live semantic token so a 3rd-party lib matches the current theme
24
24
  const token = (name) =>
@@ -79,7 +79,7 @@ library only.
79
79
  | Virtual scroll | 🔌 | — | **TanStack Virtual** · Vue: **vue-virtual-scroller** | — |
80
80
 
81
81
  > **Monospace data cells:** add `.fdy-mono` to any identifier / code / IP / timestamp cell (or an
82
- > inline `<span>`) to render it in the data font with tabular figures. It is alignment-neutral
82
+ > inline `<span>`) to render it in the data font with tabular figures. It is alignment-neutral,
83
83
  > unlike `.fdy-table__num`, which is also right-aligned. `FdyTable` applies it automatically to any
84
84
  > column with `mono: true`.
85
85
 
@@ -115,7 +115,7 @@ library only.
115
115
 
116
116
  | Area | Freeday enough? | When the built-in is enough | If you need more | Bridge |
117
117
  |---|---|---|---|---|
118
- | Icons | ➕ (inline SVG) | A small built-in set | A full set: **Lucide** (best fit Freeday uses this stroke style), **Heroicons**, **Tabler**, **Phosphor** | Drop the SVG into a slot: `[data-fdy-icon]`, `.fdy-input-group__addon--icon`, `.fdy-combo__icon`. |
118
+ | Icons | ➕ (inline SVG) | A small built-in set | A full set: **Lucide** (best fit, since Freeday uses this stroke style), **Heroicons**, **Tabler**, **Phosphor** | Drop the SVG into a slot: `[data-fdy-icon]`, `.fdy-input-group__addon--icon`, `.fdy-combo__icon`. |
119
119
  | Drag & drop / sortable | 🔌 | — | **SortableJS** (vanilla) · React: **dnd-kit** · Vue: **vuedraggable** | Lay it out with Freeday markup, DnD from the library. |
120
120
  | Command palette | 🔌 | — | **cmdk** (React) · **kbar** | Style with tokens + `.fdy-kbd`. |
121
121
  | Routing | 🔌 | — | Vue Router · React Router · Blazor Router | After a route change → `window.FreedayTable.initAll()`, etc. |
@@ -124,8 +124,8 @@ library only.
124
124
 
125
125
  ## Per-framework binding
126
126
 
127
- > **Recommended path:** use the ready-made adapters the `useFreeday` composable (Vue), the
128
- > `useFreeday` hook (React), or `window.FreedayBlazor` (Blazor) which wrap hydration + event
127
+ > **Recommended path:** use the ready-made adapters: the `useFreeday` composable (Vue), the
128
+ > `useFreeday` hook (React), or `window.FreedayBlazor` (Blazor). They wrap hydration + event
129
129
  > bridging. Full steps per stack are in **[`getting-started.md`](getting-started.md)**. The raw
130
130
  > `initAll()` pattern below is the underlying mechanism (and a fallback if you don't use an adapter).
131
131
 
@@ -154,7 +154,7 @@ function Panel(): JSX.Element {
154
154
  ```
155
155
  > **Controlled alternative (parity with Vue's `v-model`):** `@cahyo-dimas/freeday/react` also exports
156
156
  > the typed components `FdyCombo` / `FdyDatepicker` / `FdyDateRange` / `FdyAutocomplete` /
157
- > `FdyCascade` / `FdyCfl` / `FdyChart` / `FdyTable` / `FdyModal` / `FdyDrawer` plain
157
+ > `FdyCascade` / `FdyCfl` / `FdyChart` / `FdyTable` / `FdyModal` / `FdyDrawer`, with plain
158
158
  > `value`/`onChange` (props in, events out), no `data-fdy-*` + manual event listener:
159
159
  > ```tsx
160
160
  > import { FdyCombo } from '@cahyo-dimas/freeday/react';
@@ -168,22 +168,22 @@ function Panel(): JSX.Element {
168
168
  > `row-detail`/`renderRowDetail` + controlled `expandedKeys`); `FdyModal`/`FdyDrawer` take `open` +
169
169
  > `onClose`.
170
170
  >
171
- > **Responsive list screens drive the page from outside the table.** `FdyTable`'s pager renders
171
+ > **Responsive list screens: drive the page from outside the table.** `FdyTable`'s pager renders
172
172
  > inside `.fdy-datatable`, so a screen that hides the table below `md` and shows a card list loses it.
173
173
  > Pass **`pageIndex`** (+ `update:pageIndex` / `onPageIndexChange` / `PageIndexChanged`) to own the
174
174
  > client-side index while the table still does filter/sort/paginate: render **one** pager outside for
175
175
  > both breakpoints, feed the card list from the **`process`** event (`{ rows, total }`), and build the
176
176
  > pager's page window with `pageWindow` from `@cahyo-dimas/freeday/table-model` so it is literally the
177
- > same computation the table uses. One page index can then span several tables which is what a
177
+ > same computation the table uses. One page index can then span several tables, which is what a
178
178
  > grouped list needs. The same set is available
179
- > in `@cahyo-dimas/freeday/vue` via `v-model` / props both adapters are fully symmetric. See
179
+ > in `@cahyo-dimas/freeday/vue` via `v-model` / props, and both adapters are fully symmetric. See
180
180
  > [`getting-started.md` §React](getting-started.md#react-vite) and
181
181
  > `examples/react-faktur/src/App.tsx`. **Vite** transpiles the `.tsx` source with no extra config;
182
182
  > **Next.js** may need `transpilePackages: ['@cahyo-dimas/freeday']`.
183
183
 
184
184
  **Blazor**
185
185
  ```csharp
186
- // Component.razor.cs call the enhancer after Blazor renders the markup.
186
+ // Component.razor.cs: call the enhancer after Blazor renders the markup.
187
187
  protected override async Task OnAfterRenderAsync(bool firstRender)
188
188
  {
189
189
  if (firstRender)
@@ -200,7 +200,7 @@ document.addEventListener('fdy-form-invalid', (e) =>
200
200
  > **Native components (parity with Vue's `v-model` / React's `value`/`onChange`):** the
201
201
  > **`Freeday.Blazor`** RCL (net8.0, `adapters/blazor/`) ships the typed components `FdyCombo<TValue>` /
202
202
  > `FdyDatepicker` / `FdyDateRange` / `FdyAutocomplete` / `FdyCascade` / `FdyCfl<TRow>` / `FdyChart` /
203
- > `FdyTable<TRow>` / `FdyModal` / `FdyDrawer` `@bind`, no `@ref` / manual `initAll` / `[JSInvokable]`:
203
+ > `FdyTable<TRow>` / `FdyModal` / `FdyDrawer` with `@bind`, no `@ref` / manual `initAll` / `[JSInvokable]`:
204
204
  > ```razor
205
205
  > @using Freeday.Blazor
206
206
  > <FdyCombo TValue="string" @bind-Value="_status" Options="_statusOptions" AriaLabelledby="lbl-status" />
@@ -297,7 +297,7 @@ computePosition(trigger, tip, { placement: 'top', middleware: [offset(8), flip()
297
297
  ```
298
298
 
299
299
  ### 6. Large async select → `freeday-cfl`
300
- For thousands of server-side rows, don't force a `<select>` use choose-from-list:
300
+ For thousands of server-side rows, don't force a `<select>`. Use choose-from-list:
301
301
  ```js
302
302
  window.FreedayCfl.init(el, {
303
303
  // Server owns paging/search; Freeday owns the accessible dialog UI.
@@ -311,13 +311,13 @@ el.addEventListener('fdy-cfl-select', (e) => store.setCustomer(e.detail.row));
311
311
  ## SAP B1 note
312
312
 
313
313
  Freeday is purely for the **web** (companion apps, .NET web add-ons, portals). A **UI API
314
- (SAPBouiCOM)** add-on is WinForms/COM a different world where Freeday doesn't apply. For
314
+ (SAPBouiCOM)** add-on is WinForms/COM, a different world where Freeday doesn't apply. For
315
315
  web-based add-ons (Service Layer + .NET), Freeday + enhancers over JS interop work normally.
316
316
 
317
317
  ---
318
318
 
319
319
  *Library recommendations reflect what's common and stable as of 2026; choose based on your
320
- project's license and bundle-size budget. Freeday locks you into none of them everything is
320
+ project's license and bundle-size budget. Freeday locks you into none of them. Everything is
321
321
  optional and replaceable.*
322
322
 
323
323
  ## Building a control the kit does not ship
@@ -329,7 +329,7 @@ import { usePopover } from '@cahyo-dimas/freeday/vue'; // or '/react'
329
329
  usePopover(panelRef, triggerRef, open); // panel needs popover="manual"
330
330
  ```
331
331
 
332
- This is what every kit dropdown uses to escape an ancestor's overflow clip `.fdy-card` is
332
+ This is what every kit dropdown uses to escape an ancestor's overflow clip, because `.fdy-card` is
333
333
  `overflow:hidden`, so a panel positioned inside one is otherwise cut at the card's edge. It is
334
334
  exported so a control you build behaves like one the kit ships, and improves when the kit's
335
335
  positioning does.