@cahyo-dimas/freeday 3.0.0 → 3.1.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.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,80 @@ benar. Perubahan seperti itu ditulis di bawah `### Changed: BREAKING (types)`
9
9
  lama → tipe baru, dan cara menyempitkannya — bukan di bawah `### Added`, betapapun aditifnya dari
10
10
  sisi kit.
11
11
 
12
+ ## [3.1.0] - 2026-08-27
13
+
14
+ **Dua cacat lebar yang dilaporkan dari layar sungguhan, dan keduanya tak kelihatan di DOM.** Markup-nya
15
+ benar, class-nya benar, kotaknya cuma berhenti lebih awal. Rilis ini juga membuat satu kalimat
16
+ `COMPONENTS.md` jadi benar setelah lama tidak.
17
+
18
+ ### Fixed
19
+ - **Kontrol di dalam `.fdy-field` kini selebar field-nya** (`improvement-notes` #051, dari
20
+ `IDU_EMATE_APPL_WEB` dan `IDU_EMATE_ACCT_WEB`). `.fdy-input-group`, `.fdy-combo`,
21
+ `.fdy-autocomplete` dan `.fdy-cascade` masing-masing di-cap `22rem` — cap untuk kontrol yang
22
+ berdiri sendiri, tanpa field yang bisa memberinya lebar — dan cap itu hanya dilepas **di satu
23
+ container**, `.fdy-filterbar`, yang menyebut dua dari empatnya. Akibatnya baris `--full` di
24
+ `.fdy-form-grid` memegang combo 352px di bawah label 600px, sementara `.fdy-input`
25
+ di sebelahnya (yang tak pernah punya cap) mengisi penuh: satu form terlihat tak sejajar dengan
26
+ dirinya sendiri. Yang paling mahal justru versi yang dilaporkan pengguna tanpa diminta — *"jarak
27
+ search ke filter status agak jauh, ya?"* — pada toolbar dengan field `26rem`: kontrolnya 352px,
28
+ jadi **64px ruang mati ada DI DALAM field**, dan `gap:var(--space-3)` yang ditulis penulisnya
29
+ terbaca 76px di layar. Tak ada satu pun langkah debug yang wajar bisa melihatnya: gap-nya benar,
30
+ flex-nya benar.
31
+ Perbaikannya satu aturan di `input.css`, bukan selector ketiga di samping filterbar, dan bentuknya
32
+ **sudah ada di kit sejak #017** — `.fdy-field>.fdy-datepicker` melepas cap picker dengan komentar
33
+ "a picker fills the field *like every other control*". Kalimat itu tidak benar untuk empat kontrol
34
+ di atas, dan sekarang benar. Cap `22rem` tetap ada **di `.fdy-field`**, jadi field yang tak
35
+ dilebarkan siapa pun tak berubah sedikit pun.
36
+ Ikut terangkat, dan tak ada di laporan mana pun: `.fdy-autocomplete` dan `.fdy-cascade` di field
37
+ `--w-2xl` (25rem) berhenti 48px lebih pendek **di dalam filterbar sendiri** — container yang kit
38
+ kira sudah beres. Aturan lama di `filterbar.css` dihapus, bukan dibiarkan di samping yang baru:
39
+ dua aturan yang mengatakan hal sama persis adalah cara filterbar jadi tahu sesuatu yang tak
40
+ diketahui `input.css`.
41
+ - **`.fdy-pagination` akhirnya benar-benar dikirim** (#050 §2). `COMPONENTS.md` menyuruh konsumen
42
+ menulis `<nav class="fdy-pagination" data-fdy-table-pagination>`, dan class blok itu **nol match di
43
+ seluruh paket** — bukan di enhancer, bukan di tiga footer typed. Tak ada yang tampak berbeda,
44
+ karena blok itu memang tak punya rule (`NEXT-UP` #9), tapi selector konsumen atau asersi e2e pada
45
+ blok itu **lulus terhadap markup tulisan tangan dan gagal terhadap keluaran kit sendiri**, padahal
46
+ kedua jalur seharusnya bisa saling menggantikan. Enhancer kini menyetelnya sekali saat init;
47
+ `FdyTableFooter` Vue/React/Blazor merendernya.
48
+
49
+ ### Added
50
+ - **`.fdy-stats--inline`** — strip KPI yang **memeluk angkanya** alih-alih membentang, untuk yang
51
+ duduk di samping judul halaman (#050 §1). Ini jawaban atas cacat yang tak bisa diperbaiki dengan
52
+ dokumentasi saja: `.fdy-stat` membawa `container-type:inline-size` (itu yang memberi `__value`
53
+ tipe fluidnya), dan **inline-size containment berarti tile tak menyumbang lebar intrinsik sama
54
+ sekali**. Jadi floor `11rem` bawaan bukan pilihan rasa — ia satu-satunya yang memberi track itu
55
+ ukuran — dan konsumen yang me-retrack grid ke konten mendapat tiga track nol: tiga label saling
56
+ menimpa (`COMPAMEMBERSTA…`) dan halaman meluber ke samping. Terukur dari header
57
+ `IDU_EMATE_ACCT_WEB`: strip 40px, seluruhnya gap. `justify-items` lebih tajam lagi — apa pun
58
+ selain `stretch` mengecilkan tile ke sumbangan nol itu **walau track-nya sudah benar lebarnya**.
59
+ `--inline` melepas container-nya, jadi tile-nya berukuran sesuai kontennya sendiri dan `__value`
60
+ memakai `--text-3xl` datar: track yang seukuran kontennya tak pernah jadi track sempit yang
61
+ membuat tipe fluid itu ada. Konsumen tak perlu lagi menebak floor sendiri.
62
+
63
+ ### Docs
64
+ - **`COMPONENTS.md` §Stats akhirnya menyebut containment-nya.** `container-type`, `cqw`,
65
+ `inline-size` dan `contain` nol match di seluruh berkas itu sebelum rilis ini, padahal detail
66
+ implementasi tipe `__value` itu bocor langsung ke kontrak layout siapa pun yang me-retrack grid-nya.
67
+ Kini tertulis sebagai batasan: override `grid-template-columns` wajib punya floor eksplisit, dan
68
+ `justify-items` harus tetap `stretch`.
69
+ - **§Field menyebut cap-nya.** `22rem` dan `max-width` juga nol match di `COMPONENTS.md` sebelum ini,
70
+ jadi cap maupun pengecualian filterbar-nya hanya bisa ditemukan dengan mengukur form yang sudah
71
+ dirender. Aturannya sekarang satu kalimat: **beri ukuran pada field, jangan pada kontrolnya.**
72
+
73
+ ### Guarded
74
+ - `test/css.test.mjs`: bentuk CSS-nya — keempat kontrol dilepas di `.fdy-field`, `.fdy-field`
75
+ mempertahankan cap-nya sendiri, aturan filterbar lama tak boleh kembali, dan `--inline` harus
76
+ keluar dari container maupun clamp `cqw`-nya.
77
+ - `browser/field-width.mjs` (6 test, Chrome sungguhan): hasilnya — keduanya adalah **outcome layout**
78
+ yang tak terlihat di stylesheet. Termasuk asersi bahwa gap yang terlihat pembaca sama dengan gap
79
+ yang ditulis penulisnya, dan tile `--inline` diukur terhadap referensi yang berukuran-konten
80
+ secara konstruksi, bukan sekadar "bukan nol": tanpa itu, satu track yang masih memegang floor
81
+ `11rem` lolos karena strip-nya tetap masuk akal.
82
+ - `test/docs.test.mjs`: berkas mana pun yang merender `__element` sebuah blok harus menyebut bloknya.
83
+ Dicari **secara struktur**, bukan dari daftar empat berkas yang salah — renderer pager kelima
84
+ adalah yang tak akan diingat siapa pun.
85
+
12
86
  ## [3.0.0] - 2026-08-26
13
87
 
14
88
  **Kenapa MAJOR padahal rilis ini seluruhnya aditif.** Tak ada satu pun perubahan di bawah yang
package/COMPONENTS.md CHANGED
@@ -227,7 +227,22 @@ carry the doctrine in markup.
227
227
  sits half a label-height low in it, so labelled fields belong in `.fdy-filterbar`
228
228
  (`align-items:flex-end`). In a toolbar, hide the label with `.fdy-visually-hidden`.
229
229
  - `.fdy-stats` is a KPI grid of `.fdy-stat` (`__label` `__value` `__meta`). Deliberately **not**
230
- cards. `.fdy-stats--boxed` for one shared surface with dividers.
230
+ cards. `.fdy-stats--boxed` for one shared surface with dividers; `.fdy-stats--inline` for a strip
231
+ that **hugs its numbers** instead of spanning the row, which is what a page header wants.
232
+ - **A `.fdy-stat` in the default grid is size-contained, and that constrains what you may override.**
233
+ It carries `container-type:inline-size` — that is what gives `__value` its fluid type — and
234
+ inline-size containment means the tile contributes **no intrinsic width**. Two consequences, and
235
+ re-tracking the grid hits both at once:
236
+ - Content-based track sizing cannot work: `auto`, `max-content`, `min-content` and `minmax(0, …)`
237
+ all resolve to a **zero** track. The default `minmax(11rem, 1fr)` floor is not a style choice,
238
+ it is the only thing giving these tracks a size, so **any `grid-template-columns` override needs
239
+ an explicit floor of its own**.
240
+ - `justify-items` must stay `stretch`. A non-stretched grid item is sized to its (zero)
241
+ contribution, so `start` collapses the tile to nothing **even when the track is correctly wide**.
242
+
243
+ Use `--inline` rather than working around either: it drops the container, so its tiles size to
244
+ their own content, and its `__value` keeps the flat `--text-3xl`. A hugging track is never the
245
+ narrow track the fluid type exists for.
231
246
 
232
247
  ```html
233
248
  <div class="fdy-page">
@@ -409,6 +424,13 @@ native control, otherwise a `<div>` + explicitly associated label.
409
424
 
410
425
  - `.fdy-field` (+ `--full` inside `.fdy-form-grid`; widths `--w-sm` `--w-lg` `--w-xl` `--w-2xl`
411
426
  `--w-grow` inside `.fdy-filterbar`)
427
+ - **The field owns the width; the control in it follows.** `.fdy-field` caps at `22rem` so a lone
428
+ field does not run the width of the page. `.fdy-form-grid` and `.fdy-filterbar` lift that cap, and
429
+ so does any width you state yourself — and in every one of those cases the control (`.fdy-input`,
430
+ `.fdy-input-group`, `.fdy-combo`, `.fdy-autocomplete`, `.fdy-cascade`, and the pickers) spans the
431
+ field. So **size the field, never the control**: styling the control instead leaves the field
432
+ wider than what is in it, and that dead space is invisible in the DOM — it reads on screen as a
433
+ gap you did not write.
412
434
  - `.fdy-label` (+`--required`) · `.fdy-input` (+`--error`) · `.fdy-textarea` · `.fdy-help` (+`--error`)
413
435
 
414
436
  **Grouped controls are a `<fieldset>`, not a new block.** Put `.fdy-field` on the fieldset and
@@ -1216,7 +1238,9 @@ and breaks the moment the class changes.
1216
1238
  ## Pagination — `.fdy-pagination`
1217
1239
  The block class on the `<nav>` is a **structural hook only**. It carries no rule of its own; the
1218
1240
  `__list` / `__link` / `__ellipsis` elements do all the styling, and the data table targets
1219
- `data-fdy-table-pagination`. Keep it on the wrapper anyway, for consistency with the rest of the kit.
1241
+ `data-fdy-table-pagination`. Keep it on the wrapper anyway, for consistency with the rest of the kit
1242
+ — and the kit keeps it too: the enhancer sets it on the element it finds, and all three typed
1243
+ footers render it, so a selector or an e2e assertion on the block matches either path.
1220
1244
 
1221
1245
  `<nav class="fdy-pagination" aria-label="Pagination">` → `<ul class="fdy-pagination__list">`;
1222
1246
  each item is a `.fdy-pagination__link` (`<button>` when navigable, `<span aria-current="page">`
package/README.id.md CHANGED
@@ -5,7 +5,7 @@
5
5
  > **Lebih banyak _free day_ buat dev. UI kit-nya sudah siap pakai.**
6
6
 
7
7
  [![Live docs](https://img.shields.io/badge/docs-live-2050d8?style=flat-square)](https://cahyo-dimas.github.io/freeday-ui-kit/)
8
- [![Release](https://img.shields.io/badge/release-v3.0.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.0.0)
8
+ [![Release](https://img.shields.io/badge/release-v3.1.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.1.0)
9
9
 
10
10
  UI KIT yang token-driven & framework-agnostic. Satu sumber kebenaran untuk warna, tipografi,
11
11
  spasi, dan komponen. Blueprint: `docs/superpowers/specs/2026-07-21-freeday-ui-kit-design.md`.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  > **More free days for devs. The UI kit is ready to use.**
6
6
 
7
7
  [![Live docs](https://img.shields.io/badge/docs-live-2050d8?style=flat-square)](https://cahyo-dimas.github.io/freeday-ui-kit/)
8
- [![Release](https://img.shields.io/badge/release-v3.0.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.0.0)
8
+ [![Release](https://img.shields.io/badge/release-v3.1.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.1.0)
9
9
 
10
10
  A token-driven, framework-agnostic UI kit. One source of truth for color, typography,
11
11
  spacing, and components. Blueprint: `docs/superpowers/specs/2026-07-21-freeday-ui-kit-design.md`.
@@ -24,7 +24,7 @@
24
24
 
25
25
  @if (HasPager)
26
26
  {
27
- <nav aria-label="Pagination">
27
+ <nav class="fdy-pagination" aria-label="Pagination">
28
28
  <ul class="fdy-pagination__list">
29
29
  <li>
30
30
  <button type="button" class="fdy-pagination__link" aria-label="Previous page"
@@ -75,7 +75,7 @@ export function FdyTableFooter(props: FdyTableFooterProps): JSX.Element | null {
75
75
  )}
76
76
 
77
77
  {hasPager && (
78
- <nav aria-label="Pagination">
78
+ <nav className="fdy-pagination" aria-label="Pagination">
79
79
  <ul className="fdy-pagination__list">
80
80
  <li>
81
81
  <button type="button" className="fdy-pagination__link" aria-label="Previous page" disabled={currentPage1 === 1} onClick={(): void => goTo(currentPage1 - 1)}>‹</button>
@@ -90,7 +90,7 @@ function onSize(value: string): void {
90
90
  />
91
91
  </div>
92
92
 
93
- <nav v-if="hasPager" aria-label="Pagination">
93
+ <nav v-if="hasPager" class="fdy-pagination" aria-label="Pagination">
94
94
  <ul class="fdy-pagination__list">
95
95
  <li>
96
96
  <button
@@ -84,6 +84,13 @@
84
84
  var countEl = root.querySelector('[data-fdy-table-count]');
85
85
  var infoEl = root.querySelector('[data-fdy-table-info]');
86
86
  var pagerEl = root.querySelector('[data-fdy-table-pagination]');
87
+ /* COMPONENTS.md documents the wrapper as <nav class="fdy-pagination" data-fdy-table-pagination>
88
+ and nothing in the kit ever wrote that class, here or in the three typed footers (#050 §2).
89
+ It carries no rule (NEXT-UP #9), so nothing rendered differently, but a consumer selector or
90
+ an e2e assertion on the block passed against hand-written markup and failed against the kit's
91
+ own output, and the raw and typed paths are supposed to be interchangeable. Set once at init,
92
+ not per render: the wrapper outlives every pager we build into it. */
93
+ if (pagerEl) pagerEl.classList.add('fdy-pagination');
87
94
  var pageSizeEl = root.querySelector('[data-fdy-table-page-size]');
88
95
  var selectAll = root.querySelector('[data-fdy-select-all]');
89
96
  var pageSize = parseInt(root.getAttribute('data-page-size'), 10) || 0; // 0 = no pagination
@@ -1242,7 +1242,16 @@ a { color: var(--color-primary); }
1242
1242
 
1243
1243
  /* The caption sits on a solid surface, not on the scrim. A scrim is semi-transparent over unknown
1244
1244
  * content, so text on it has no computable contrast ratio and no gate could ever cover it. On
1245
- * --color-surface it is the same pair the contrast suite already checks. */
1245
+ * --color-surface it is the same pair the contrast suite already checks.
1246
+ *
1247
+ * THIS IS ALSO WHY THIS PANEL DOES NOT JOIN THE GLASS AXIS, and the omission is a decision rather
1248
+ * than an oversight. Every other raised panel reads --color-surface-raised and frosts under
1249
+ * [data-style="glass"]; this one stays opaque. The glass gate asserts its ink against the panel
1250
+ * composited over the PAGE GROUND (--color-surface-2), which is what sits behind a card or a modal.
1251
+ * Behind this one is the scrim, over arbitrary content — the very case that has no computable
1252
+ * ratio. A frosted busy panel would be the one surface in the kit whose caption nothing could
1253
+ * verify, while it is up precisely because the app is doing something the reader must not
1254
+ * interrupt. Left opaque on purpose; do not "fix" it for consistency. */
1246
1255
  .fdy-busy__panel{display:flex;flex-direction:column;align-items:center;gap:var(--space-4);padding:var(--space-6) var(--space-8);border-radius:var(--radius-lg);background:var(--color-surface);box-shadow:var(--shadow-lift);max-width:min(20rem,calc(100vw - var(--space-8)));}
1247
1256
  .fdy-busy__mark{display:flex;align-items:center;justify-content:center;min-height:2.5rem;}
1248
1257
  .fdy-busy__caption{margin:0;font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text);text-align:center;}
@@ -1670,9 +1679,22 @@ a { color: var(--color-primary); }
1670
1679
  grid track, `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
1671
1680
  own content. The unconditional rule above stays as the fallback wherever @container is missing. */
1672
1681
  @supports (container-type:inline-size){
1673
- .fdy-stats>.fdy-stat{container-type:inline-size;}
1674
- .fdy-stats>.fdy-stat .fdy-stat__value{font-size:clamp(var(--text-xl),11cqw,var(--text-3xl));}
1675
- }
1682
+ .fdy-stats:not(.fdy-stats--inline)>.fdy-stat{container-type:inline-size;}
1683
+ .fdy-stats:not(.fdy-stats--inline)>.fdy-stat .fdy-stat__value{font-size:clamp(var(--text-xl),11cqw,var(--text-3xl));}
1684
+ }
1685
+ /* The price of that container, and the reason --inline opts out of it (#050). `container-type`
1686
+ applies inline-size CONTAINMENT: the tile's min-content and max-content contributions are both
1687
+ ZERO. So the 11rem floor above is not a taste choice, it is the only thing giving these tracks a
1688
+ size, and re-tracking the grid on content (`auto`, `max-content`, `minmax(0,…)`) resolves every
1689
+ track to 0. `justify-items` is the sharper edge: anything but the default `stretch` sizes the
1690
+ tile to that zero contribution even when the track is correctly wide, and three labels paint on
1691
+ top of each other. Measured from IDU_EMATE_ACCT_WEB's page header, where a hugging strip came out
1692
+ 40px wide, all of it gap.
1693
+ --inline is the shape that header wanted: a strip that hugs its numbers and sits beside a page
1694
+ title, instead of a full-width band whose 11rem floor collapses to one column at 390px. It cannot
1695
+ have both, so it drops the container and keeps the flat --text-3xl the base rule already sets;
1696
+ a track sized to its own content is never the narrow track the clamp exists for. */
1697
+ .fdy-stats--inline{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:minmax(0,max-content);}
1676
1698
  .fdy-stats--boxed{background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);gap:0;}
1677
1699
  .fdy-stats--boxed .fdy-stat{padding:var(--space-5);border-right:var(--bw) solid var(--color-border-muted);}
1678
1700
  .fdy-stats--boxed .fdy-stat:last-child{border-right:0;}
@@ -1878,8 +1900,9 @@ a { color: var(--color-primary); }
1878
1900
  /* Fields are direct children. Drop the standalone field's 22rem cap; default to the
1879
1901
  * medium column width with no unbounded growth (that's what caused the raggedness). */
1880
1902
  .fdy-filterbar>.fdy-field{max-width:none;flex:0 0 auto;width:12rem;}
1881
- /* Controls fill their field's column (drop the standalone input-group/combo caps). */
1882
- .fdy-filterbar>.fdy-field .fdy-input-group,.fdy-filterbar>.fdy-field .fdy-combo{max-width:none;width:100%;}
1903
+ /* Controls fill their field's column: released in input.css for every field, not just this one.
1904
+ The rule used to live here, naming the input group and the combo only, which is why an
1905
+ .fdy-autocomplete or an .fdy-cascade in a --w-2xl field still stopped 48px short of it (#051). */
1883
1906
  /* Column-width rhythm, pick one per field so bars across the app line up. */
1884
1907
  .fdy-filterbar>.fdy-field--w-sm{width:9rem;}
1885
1908
  .fdy-filterbar>.fdy-field--w-lg{width:15rem;}
@@ -1956,6 +1979,15 @@ a { color: var(--color-primary); }
1956
1979
 
1957
1980
  /* Freeday. Field & Input */
1958
1981
  .fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
1982
+ /* Inside a labelled field, a control fills the field (#051). The 22rem cap those controls carry is
1983
+ for one standing on its own, with no field to take a width from; #017 already released the date
1984
+ and time pickers here for exactly this reason, and its comment claims a picker then fills its
1985
+ field "like every other control". That was not true of these four: a `--full` row in a
1986
+ .fdy-form-grid held a 352px combo under a 600px label, and a 26rem toolbar search field hid 64px
1987
+ of dead space INSIDE itself, so an author who wrote `gap:var(--space-3)` measured 76px on screen.
1988
+ The field's own max-width above is what governs, and it still caps a field nobody has widened, so
1989
+ this changes nothing wherever the cap was already doing its job. */
1990
+ .fdy-field>.fdy-input-group,.fdy-field>.fdy-combo,.fdy-field>.fdy-autocomplete,.fdy-field>.fdy-cascade{max-width:none;}
1959
1991
  /* A grouped field is legitimately a <fieldset> (it needs a <legend>), e.g. a radio/checkbox group.
1960
1992
  Neutralise the UA groove border / padding / min-inline-size so it lays out identically to the
1961
1993
  <div> form (min-inline-size:min-content would otherwise block the filterbar column widths). */
package/dist/freeday.css CHANGED
@@ -364,7 +364,16 @@ a { color: var(--color-primary); }
364
364
 
365
365
  /* The caption sits on a solid surface, not on the scrim. A scrim is semi-transparent over unknown
366
366
  * content, so text on it has no computable contrast ratio and no gate could ever cover it. On
367
- * --color-surface it is the same pair the contrast suite already checks. */
367
+ * --color-surface it is the same pair the contrast suite already checks.
368
+ *
369
+ * THIS IS ALSO WHY THIS PANEL DOES NOT JOIN THE GLASS AXIS, and the omission is a decision rather
370
+ * than an oversight. Every other raised panel reads --color-surface-raised and frosts under
371
+ * [data-style="glass"]; this one stays opaque. The glass gate asserts its ink against the panel
372
+ * composited over the PAGE GROUND (--color-surface-2), which is what sits behind a card or a modal.
373
+ * Behind this one is the scrim, over arbitrary content — the very case that has no computable
374
+ * ratio. A frosted busy panel would be the one surface in the kit whose caption nothing could
375
+ * verify, while it is up precisely because the app is doing something the reader must not
376
+ * interrupt. Left opaque on purpose; do not "fix" it for consistency. */
368
377
  .fdy-busy__panel{display:flex;flex-direction:column;align-items:center;gap:var(--space-4);padding:var(--space-6) var(--space-8);border-radius:var(--radius-lg);background:var(--color-surface);box-shadow:var(--shadow-lift);max-width:min(20rem,calc(100vw - var(--space-8)));}
369
378
  .fdy-busy__mark{display:flex;align-items:center;justify-content:center;min-height:2.5rem;}
370
379
  .fdy-busy__caption{margin:0;font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text);text-align:center;}
@@ -792,9 +801,22 @@ a { color: var(--color-primary); }
792
801
  grid track, `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
793
802
  own content. The unconditional rule above stays as the fallback wherever @container is missing. */
794
803
  @supports (container-type:inline-size){
795
- .fdy-stats>.fdy-stat{container-type:inline-size;}
796
- .fdy-stats>.fdy-stat .fdy-stat__value{font-size:clamp(var(--text-xl),11cqw,var(--text-3xl));}
804
+ .fdy-stats:not(.fdy-stats--inline)>.fdy-stat{container-type:inline-size;}
805
+ .fdy-stats:not(.fdy-stats--inline)>.fdy-stat .fdy-stat__value{font-size:clamp(var(--text-xl),11cqw,var(--text-3xl));}
797
806
  }
807
+ /* The price of that container, and the reason --inline opts out of it (#050). `container-type`
808
+ applies inline-size CONTAINMENT: the tile's min-content and max-content contributions are both
809
+ ZERO. So the 11rem floor above is not a taste choice, it is the only thing giving these tracks a
810
+ size, and re-tracking the grid on content (`auto`, `max-content`, `minmax(0,…)`) resolves every
811
+ track to 0. `justify-items` is the sharper edge: anything but the default `stretch` sizes the
812
+ tile to that zero contribution even when the track is correctly wide, and three labels paint on
813
+ top of each other. Measured from IDU_EMATE_ACCT_WEB's page header, where a hugging strip came out
814
+ 40px wide, all of it gap.
815
+ --inline is the shape that header wanted: a strip that hugs its numbers and sits beside a page
816
+ title, instead of a full-width band whose 11rem floor collapses to one column at 390px. It cannot
817
+ have both, so it drops the container and keeps the flat --text-3xl the base rule already sets;
818
+ a track sized to its own content is never the narrow track the clamp exists for. */
819
+ .fdy-stats--inline{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:minmax(0,max-content);}
798
820
  .fdy-stats--boxed{background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);gap:0;}
799
821
  .fdy-stats--boxed .fdy-stat{padding:var(--space-5);border-right:var(--bw) solid var(--color-border-muted);}
800
822
  .fdy-stats--boxed .fdy-stat:last-child{border-right:0;}
@@ -1000,8 +1022,9 @@ a { color: var(--color-primary); }
1000
1022
  /* Fields are direct children. Drop the standalone field's 22rem cap; default to the
1001
1023
  * medium column width with no unbounded growth (that's what caused the raggedness). */
1002
1024
  .fdy-filterbar>.fdy-field{max-width:none;flex:0 0 auto;width:12rem;}
1003
- /* Controls fill their field's column (drop the standalone input-group/combo caps). */
1004
- .fdy-filterbar>.fdy-field .fdy-input-group,.fdy-filterbar>.fdy-field .fdy-combo{max-width:none;width:100%;}
1025
+ /* Controls fill their field's column: released in input.css for every field, not just this one.
1026
+ The rule used to live here, naming the input group and the combo only, which is why an
1027
+ .fdy-autocomplete or an .fdy-cascade in a --w-2xl field still stopped 48px short of it (#051). */
1005
1028
  /* Column-width rhythm, pick one per field so bars across the app line up. */
1006
1029
  .fdy-filterbar>.fdy-field--w-sm{width:9rem;}
1007
1030
  .fdy-filterbar>.fdy-field--w-lg{width:15rem;}
@@ -1078,6 +1101,15 @@ a { color: var(--color-primary); }
1078
1101
 
1079
1102
  /* Freeday. Field & Input */
1080
1103
  .fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
1104
+ /* Inside a labelled field, a control fills the field (#051). The 22rem cap those controls carry is
1105
+ for one standing on its own, with no field to take a width from; #017 already released the date
1106
+ and time pickers here for exactly this reason, and its comment claims a picker then fills its
1107
+ field "like every other control". That was not true of these four: a `--full` row in a
1108
+ .fdy-form-grid held a 352px combo under a 600px label, and a 26rem toolbar search field hid 64px
1109
+ of dead space INSIDE itself, so an author who wrote `gap:var(--space-3)` measured 76px on screen.
1110
+ The field's own max-width above is what governs, and it still caps a field nobody has widened, so
1111
+ this changes nothing wherever the cap was already doing its job. */
1112
+ .fdy-field>.fdy-input-group,.fdy-field>.fdy-combo,.fdy-field>.fdy-autocomplete,.fdy-field>.fdy-cascade{max-width:none;}
1081
1113
  /* A grouped field is legitimately a <fieldset> (it needs a <legend>), e.g. a radio/checkbox group.
1082
1114
  Neutralise the UA groove border / padding / min-inline-size so it lays out identically to the
1083
1115
  <div> form (min-inline-size:min-content would otherwise block the filterbar column widths). */
package/dist/freeday.js CHANGED
@@ -3905,6 +3905,13 @@
3905
3905
  var countEl = root.querySelector('[data-fdy-table-count]');
3906
3906
  var infoEl = root.querySelector('[data-fdy-table-info]');
3907
3907
  var pagerEl = root.querySelector('[data-fdy-table-pagination]');
3908
+ /* COMPONENTS.md documents the wrapper as <nav class="fdy-pagination" data-fdy-table-pagination>
3909
+ and nothing in the kit ever wrote that class, here or in the three typed footers (#050 §2).
3910
+ It carries no rule (NEXT-UP #9), so nothing rendered differently, but a consumer selector or
3911
+ an e2e assertion on the block passed against hand-written markup and failed against the kit's
3912
+ own output, and the raw and typed paths are supposed to be interchangeable. Set once at init,
3913
+ not per render: the wrapper outlives every pager we build into it. */
3914
+ if (pagerEl) pagerEl.classList.add('fdy-pagination');
3908
3915
  var pageSizeEl = root.querySelector('[data-fdy-table-page-size]');
3909
3916
  var selectAll = root.querySelector('[data-fdy-select-all]');
3910
3917
  var pageSize = parseInt(root.getAttribute('data-page-size'), 10) || 0; // 0 = no pagination
@@ -191,7 +191,7 @@ live docs also have a copy button per component.
191
191
  ```bash
192
192
  npm i @cahyo-dimas/freeday
193
193
  ```
194
- Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.0.0"` (public npm package). `dist/` is
194
+ Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.1.0"` (public npm package). `dist/` is
195
195
  committed and published → no build step; `npm ci` runs without auth.
196
196
 
197
197
  ### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cahyo-dimas/freeday",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Freeday: token-driven, framework-agnostic UI KIT (design source-of-truth).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -92,7 +92,7 @@
92
92
  "scripts": {
93
93
  "build": "node tokens/build.mjs",
94
94
  "test": "node --test",
95
- "test:browser": "node --test --test-concurrency=3 browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/cfl-multi.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/picker-states.mjs browser/harness-contract.mjs",
95
+ "test:browser": "node --test --test-concurrency=3 browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/field-width.mjs browser/cfl-multi.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/picker-states.mjs browser/harness-contract.mjs",
96
96
  "prepack": "node tokens/build.mjs",
97
97
  "version": "node tokens/build.mjs && git add dist",
98
98
  "typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit",
@@ -21,7 +21,16 @@
21
21
 
22
22
  /* The caption sits on a solid surface, not on the scrim. A scrim is semi-transparent over unknown
23
23
  * content, so text on it has no computable contrast ratio and no gate could ever cover it. On
24
- * --color-surface it is the same pair the contrast suite already checks. */
24
+ * --color-surface it is the same pair the contrast suite already checks.
25
+ *
26
+ * THIS IS ALSO WHY THIS PANEL DOES NOT JOIN THE GLASS AXIS, and the omission is a decision rather
27
+ * than an oversight. Every other raised panel reads --color-surface-raised and frosts under
28
+ * [data-style="glass"]; this one stays opaque. The glass gate asserts its ink against the panel
29
+ * composited over the PAGE GROUND (--color-surface-2), which is what sits behind a card or a modal.
30
+ * Behind this one is the scrim, over arbitrary content — the very case that has no computable
31
+ * ratio. A frosted busy panel would be the one surface in the kit whose caption nothing could
32
+ * verify, while it is up precisely because the app is doing something the reader must not
33
+ * interrupt. Left opaque on purpose; do not "fix" it for consistency. */
25
34
  .fdy-busy__panel{display:flex;flex-direction:column;align-items:center;gap:var(--space-4);padding:var(--space-6) var(--space-8);border-radius:var(--radius-lg);background:var(--color-surface);box-shadow:var(--shadow-lift);max-width:min(20rem,calc(100vw - var(--space-8)));}
26
35
  .fdy-busy__mark{display:flex;align-items:center;justify-content:center;min-height:2.5rem;}
27
36
  .fdy-busy__caption{margin:0;font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text);text-align:center;}
@@ -60,9 +60,22 @@
60
60
  grid track, `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
61
61
  own content. The unconditional rule above stays as the fallback wherever @container is missing. */
62
62
  @supports (container-type:inline-size){
63
- .fdy-stats>.fdy-stat{container-type:inline-size;}
64
- .fdy-stats>.fdy-stat .fdy-stat__value{font-size:clamp(var(--text-xl),11cqw,var(--text-3xl));}
63
+ .fdy-stats:not(.fdy-stats--inline)>.fdy-stat{container-type:inline-size;}
64
+ .fdy-stats:not(.fdy-stats--inline)>.fdy-stat .fdy-stat__value{font-size:clamp(var(--text-xl),11cqw,var(--text-3xl));}
65
65
  }
66
+ /* The price of that container, and the reason --inline opts out of it (#050). `container-type`
67
+ applies inline-size CONTAINMENT: the tile's min-content and max-content contributions are both
68
+ ZERO. So the 11rem floor above is not a taste choice, it is the only thing giving these tracks a
69
+ size, and re-tracking the grid on content (`auto`, `max-content`, `minmax(0,…)`) resolves every
70
+ track to 0. `justify-items` is the sharper edge: anything but the default `stretch` sizes the
71
+ tile to that zero contribution even when the track is correctly wide, and three labels paint on
72
+ top of each other. Measured from IDU_EMATE_ACCT_WEB's page header, where a hugging strip came out
73
+ 40px wide, all of it gap.
74
+ --inline is the shape that header wanted: a strip that hugs its numbers and sits beside a page
75
+ title, instead of a full-width band whose 11rem floor collapses to one column at 390px. It cannot
76
+ have both, so it drops the container and keeps the flat --text-3xl the base rule already sets;
77
+ a track sized to its own content is never the narrow track the clamp exists for. */
78
+ .fdy-stats--inline{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:minmax(0,max-content);}
66
79
  .fdy-stats--boxed{background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);gap:0;}
67
80
  .fdy-stats--boxed .fdy-stat{padding:var(--space-5);border-right:var(--bw) solid var(--color-border-muted);}
68
81
  .fdy-stats--boxed .fdy-stat:last-child{border-right:0;}
@@ -13,8 +13,9 @@
13
13
  /* Fields are direct children. Drop the standalone field's 22rem cap; default to the
14
14
  * medium column width with no unbounded growth (that's what caused the raggedness). */
15
15
  .fdy-filterbar>.fdy-field{max-width:none;flex:0 0 auto;width:12rem;}
16
- /* Controls fill their field's column (drop the standalone input-group/combo caps). */
17
- .fdy-filterbar>.fdy-field .fdy-input-group,.fdy-filterbar>.fdy-field .fdy-combo{max-width:none;width:100%;}
16
+ /* Controls fill their field's column: released in input.css for every field, not just this one.
17
+ The rule used to live here, naming the input group and the combo only, which is why an
18
+ .fdy-autocomplete or an .fdy-cascade in a --w-2xl field still stopped 48px short of it (#051). */
18
19
  /* Column-width rhythm, pick one per field so bars across the app line up. */
19
20
  .fdy-filterbar>.fdy-field--w-sm{width:9rem;}
20
21
  .fdy-filterbar>.fdy-field--w-lg{width:15rem;}
@@ -1,5 +1,14 @@
1
1
  /* Freeday. Field & Input */
2
2
  .fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
3
+ /* Inside a labelled field, a control fills the field (#051). The 22rem cap those controls carry is
4
+ for one standing on its own, with no field to take a width from; #017 already released the date
5
+ and time pickers here for exactly this reason, and its comment claims a picker then fills its
6
+ field "like every other control". That was not true of these four: a `--full` row in a
7
+ .fdy-form-grid held a 352px combo under a 600px label, and a 26rem toolbar search field hid 64px
8
+ of dead space INSIDE itself, so an author who wrote `gap:var(--space-3)` measured 76px on screen.
9
+ The field's own max-width above is what governs, and it still caps a field nobody has widened, so
10
+ this changes nothing wherever the cap was already doing its job. */
11
+ .fdy-field>.fdy-input-group,.fdy-field>.fdy-combo,.fdy-field>.fdy-autocomplete,.fdy-field>.fdy-cascade{max-width:none;}
3
12
  /* A grouped field is legitimately a <fieldset> (it needs a <legend>), e.g. a radio/checkbox group.
4
13
  Neutralise the UA groove border / padding / min-inline-size so it lays out identically to the
5
14
  <div> form (min-inline-size:min-content would otherwise block the filterbar column widths). */