@cahyo-dimas/freeday 3.12.0 → 3.12.1

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,33 @@ 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.12.1] - 2026-10-27
13
+
14
+ **Modal dan drawer kembali beranimasi di bawah Safari 17.4, termasuk di iPhone 7 pemiliknya.**
15
+
16
+ ### Fixed
17
+ - **Empat shorthand `transition` dipecah jadi longhand** (`#097`), di `modal.css` dan `drawer.css`.
18
+ Shorthand yang membawa satu keyword yang tak dikenal mesin **tidak sah saat parse**, jadi SELURUH
19
+ deklarasinya dibuang -- dan `allow-discrete` tak dikenal di bawah Safari 17.4, yang ada di bawah
20
+ lantai dukungan kit. Akibatnya modal dan drawer buka-tutup tanpa animasi apa pun. Dipecah begini,
21
+ yang hilang di bawah lantai hanya `transition-behavior`, dan paruh opacity/transform-nya bertahan.
22
+ `overlay` dan `display` tetap terdaftar di `transition-property`, tempat nama yang tak cocok tetap
23
+ sah secara sintaks dan sekadar tak pernah cocok, jadi tak ada lagi yang ikut jatuh. Keluarnya
24
+ tetap seketika di sana sebab `display` memang tak dapat ditransisikan di bawah lantai -- sama
25
+ seperti sebelumnya, jadi pemecahan ini tidak mengambil apa pun.
26
+
27
+ Ekuivalensinya di atas lantai **diukur, bukan dinalar**: `transition-property`,
28
+ `transition-timing-function` dan `transition-behavior` identik byte-per-byte sebelum dan sesudah,
29
+ dan satu-satunya beda adalah satu nilai `transition-duration` yang terserialisasi sebagai satu
30
+ nilai alih-alih tiga. Bahwa satu nilai itu tetap berlaku untuk SETIAP properti juga diukur: 280ms
31
+ masing-masing, dan easing tetap per-indeks.
32
+
33
+ Harganya empat deklarasi yang harus tetap sejalan, dan itu kini dijaga uji: `baseline.test.mjs`
34
+ menolak `allow-discrete` di dalam shorthand `transition` mana pun, dan menolak
35
+ `transition-behavior` yang berdiri tanpa ketiga longhand lainnya. Regresi yang paling mungkin
36
+ adalah seseorang merapikan empat longhand kembali menjadi satu shorthand, yang terbaca lebih
37
+ bersih dan diam-diam mengambil animasinya dari setiap peramban di bawah Safari 17.4.
38
+
12
39
  ## [3.12.0] - 2026-10-27
13
40
 
14
41
  **Sebuah form kini dapat menuntut kontrol kit terisi.** `data-fdy-validate` mewirekan Constraint
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.12.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.12.0)
8
+ [![Release](https://img.shields.io/badge/release-v3.12.1-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.12.1)
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.12.0-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.12.0)
8
+ [![Release](https://img.shields.io/badge/release-v3.12.1-0078d4?style=flat-square)](https://github.com/cahyo-dimas/freeday-ui-kit/tree/v3.12.1)
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`.
@@ -2472,10 +2472,10 @@ a { color: var(--color-primary-strong); }
2472
2472
  /* display is gated behind [open] so the native <dialog> stays hidden until opened.
2473
2473
  * An unconditional display here would beat the UA `dialog:not([open]){display:none}`
2474
2474
  * rule (author origin wins over user-agent) and leave the drawer visible on load. */
2475
- .fdy-drawer{position:fixed;inset:0 auto 0 0;margin:0;padding:0;height:100vh;max-height:none;width:min(20rem,82vw);flex-direction:column;border:0;background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-4),var(--surface-inset);overflow:hidden;transform:translateX(-100%);transition:transform var(--dur-slow) var(--ease-standard),overlay var(--dur-slow) allow-discrete,display var(--dur-slow) allow-discrete;}
2475
+ .fdy-drawer{position:fixed;inset:0 auto 0 0;margin:0;padding:0;height:100vh;max-height:none;width:min(20rem,82vw);flex-direction:column;border:0;background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-4),var(--surface-inset);overflow:hidden;transform:translateX(-100%);transition-property:transform,overlay,display;transition-duration:var(--dur-slow);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
2476
2476
  .fdy-drawer[open]{display:flex;transform:none;}
2477
2477
  .fdy-drawer--right{inset:0 0 0 auto;transform:translateX(100%);}
2478
- .fdy-drawer::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity var(--dur-slow) var(--ease-standard),overlay var(--dur-slow) allow-discrete,display var(--dur-slow) allow-discrete;}
2478
+ .fdy-drawer::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition-property:opacity,overlay,display;transition-duration:var(--dur-slow);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
2479
2479
  .fdy-drawer__header{flex:none;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
2480
2480
  .fdy-drawer__title{font-family:var(--font-display);font-weight:var(--weight-semibold);font-size:var(--text-lg);color:var(--color-text);margin:0 auto 0 0;}
2481
2481
  .fdy-drawer__close{border:0;background:transparent;color:var(--color-text-muted);font-size:var(--text-xl);line-height:1;cursor:pointer;padding:var(--space-1);border-radius:var(--radius-sm);}
@@ -2778,12 +2778,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2778
2778
  .fdy-btn-split__toggle svg{display:block;width:1rem;height:1rem;}
2779
2779
 
2780
2780
  /* Freeday. Modal (native <dialog>) */
2781
- .fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-lift-hover),var(--surface-inset);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);transition:opacity var(--dur-base) var(--ease-spring),transform var(--dur-base) var(--ease-spring),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
2781
+ .fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-lift-hover),var(--surface-inset);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);/* Longhands, not the `transition` shorthand. A shorthand carrying one keyword the engine does not know is INVALID AT PARSE, so the whole declaration is thrown away -- and `allow-discrete` is unknown below Safari 17.4, which is under this kit's floor. The result was a modal and a drawer that opened and closed with no animation at all on an iPhone 7. Split this way only `transition-behavior` is dropped there, and the opacity/transform half survives. The lists align BY INDEX and reproduce the shorthand exactly, including `ease` for the discrete pair and `normal` for the continuous one -- verified by comparing computed styles before and after (#097). */transition-property:opacity,transform,overlay,display;transition-duration:var(--dur-base);transition-timing-function:var(--ease-spring),var(--ease-spring),ease,ease;transition-behavior:normal,normal,allow-discrete,allow-discrete;}
2782
2782
  .fdy-modal--sm{width:min(24rem,calc(100vw - var(--space-8)));}
2783
2783
  .fdy-modal--md{width:min(32rem,calc(100vw - var(--space-8)));}
2784
2784
  .fdy-modal--lg{width:min(48rem,calc(100vw - var(--space-8)));}
2785
2785
  .fdy-modal--wide{width:min(60rem,calc(100vw - var(--space-8)));}
2786
- .fdy-modal::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity var(--dur-base) var(--ease-standard),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
2786
+ .fdy-modal::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition-property:opacity,overlay,display;transition-duration:var(--dur-base);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
2787
2787
  .fdy-modal__header{flex:none;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
2788
2788
  .fdy-modal__title{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text);margin:0 auto 0 0;}
2789
2789
  .fdy-modal__close{border:0;background:transparent;color:var(--color-text-muted);font-size:var(--text-xl);line-height:1;cursor:pointer;padding:var(--space-1);border-radius:var(--radius-sm);}
package/dist/freeday.css CHANGED
@@ -1295,10 +1295,10 @@ a { color: var(--color-primary-strong); }
1295
1295
  /* display is gated behind [open] so the native <dialog> stays hidden until opened.
1296
1296
  * An unconditional display here would beat the UA `dialog:not([open]){display:none}`
1297
1297
  * rule (author origin wins over user-agent) and leave the drawer visible on load. */
1298
- .fdy-drawer{position:fixed;inset:0 auto 0 0;margin:0;padding:0;height:100vh;max-height:none;width:min(20rem,82vw);flex-direction:column;border:0;background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-4),var(--surface-inset);overflow:hidden;transform:translateX(-100%);transition:transform var(--dur-slow) var(--ease-standard),overlay var(--dur-slow) allow-discrete,display var(--dur-slow) allow-discrete;}
1298
+ .fdy-drawer{position:fixed;inset:0 auto 0 0;margin:0;padding:0;height:100vh;max-height:none;width:min(20rem,82vw);flex-direction:column;border:0;background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-4),var(--surface-inset);overflow:hidden;transform:translateX(-100%);transition-property:transform,overlay,display;transition-duration:var(--dur-slow);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
1299
1299
  .fdy-drawer[open]{display:flex;transform:none;}
1300
1300
  .fdy-drawer--right{inset:0 0 0 auto;transform:translateX(100%);}
1301
- .fdy-drawer::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity var(--dur-slow) var(--ease-standard),overlay var(--dur-slow) allow-discrete,display var(--dur-slow) allow-discrete;}
1301
+ .fdy-drawer::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition-property:opacity,overlay,display;transition-duration:var(--dur-slow);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
1302
1302
  .fdy-drawer__header{flex:none;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
1303
1303
  .fdy-drawer__title{font-family:var(--font-display);font-weight:var(--weight-semibold);font-size:var(--text-lg);color:var(--color-text);margin:0 auto 0 0;}
1304
1304
  .fdy-drawer__close{border:0;background:transparent;color:var(--color-text-muted);font-size:var(--text-xl);line-height:1;cursor:pointer;padding:var(--space-1);border-radius:var(--radius-sm);}
@@ -1601,12 +1601,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
1601
1601
  .fdy-btn-split__toggle svg{display:block;width:1rem;height:1rem;}
1602
1602
 
1603
1603
  /* Freeday. Modal (native <dialog>) */
1604
- .fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-lift-hover),var(--surface-inset);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);transition:opacity var(--dur-base) var(--ease-spring),transform var(--dur-base) var(--ease-spring),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
1604
+ .fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-lift-hover),var(--surface-inset);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);/* Longhands, not the `transition` shorthand. A shorthand carrying one keyword the engine does not know is INVALID AT PARSE, so the whole declaration is thrown away -- and `allow-discrete` is unknown below Safari 17.4, which is under this kit's floor. The result was a modal and a drawer that opened and closed with no animation at all on an iPhone 7. Split this way only `transition-behavior` is dropped there, and the opacity/transform half survives. The lists align BY INDEX and reproduce the shorthand exactly, including `ease` for the discrete pair and `normal` for the continuous one -- verified by comparing computed styles before and after (#097). */transition-property:opacity,transform,overlay,display;transition-duration:var(--dur-base);transition-timing-function:var(--ease-spring),var(--ease-spring),ease,ease;transition-behavior:normal,normal,allow-discrete,allow-discrete;}
1605
1605
  .fdy-modal--sm{width:min(24rem,calc(100vw - var(--space-8)));}
1606
1606
  .fdy-modal--md{width:min(32rem,calc(100vw - var(--space-8)));}
1607
1607
  .fdy-modal--lg{width:min(48rem,calc(100vw - var(--space-8)));}
1608
1608
  .fdy-modal--wide{width:min(60rem,calc(100vw - var(--space-8)));}
1609
- .fdy-modal::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity var(--dur-base) var(--ease-standard),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
1609
+ .fdy-modal::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition-property:opacity,overlay,display;transition-duration:var(--dur-base);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
1610
1610
  .fdy-modal__header{flex:none;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
1611
1611
  .fdy-modal__title{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text);margin:0 auto 0 0;}
1612
1612
  .fdy-modal__close{border:0;background:transparent;color:var(--color-text-muted);font-size:var(--text-xl);line-height:1;cursor:pointer;padding:var(--space-1);border-radius:var(--radius-sm);}
@@ -198,7 +198,7 @@ live docs also have a copy button per component.
198
198
  ```bash
199
199
  npm i @cahyo-dimas/freeday
200
200
  ```
201
- Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.12.0"` (public npm package). `dist/` is
201
+ Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.12.1"` (public npm package). `dist/` is
202
202
  committed and published → no build step; `npm ci` runs without auth.
203
203
 
204
204
  ### 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.12.0",
3
+ "version": "3.12.1",
4
4
  "description": "Freeday: token-driven, framework-agnostic UI KIT (design source-of-truth).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -96,7 +96,7 @@
96
96
  "scripts": {
97
97
  "build": "node tokens/build.mjs",
98
98
  "test": "node --test",
99
- "test:browser": "node --test --test-concurrency=3 browser/color-mix.mjs 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/cfl-dialog-only.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/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/tabs-vertical.mjs browser/menu-align.mjs browser/cfl-narrow.mjs browser/form-reveal.mjs browser/confirm-search-copy.mjs browser/daterange-settled.mjs browser/display-parts.mjs browser/table-empty.mjs browser/table-footer-text.mjs browser/month-mode.mjs browser/monthpicker.mjs browser/page-columns.mjs browser/table-hover.mjs browser/typed-popover.mjs browser/filter-parity.mjs browser/filter-wrap.mjs browser/filter-stack.mjs browser/timepicker.mjs browser/form-kit-required.mjs browser/harness-contract.mjs",
99
+ "test:browser": "node --test --test-concurrency=3 browser/color-mix.mjs 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/cfl-dialog-only.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/bottom-nav.mjs browser/picker-states.mjs browser/tabs-strip.mjs browser/tabs-vertical.mjs browser/menu-align.mjs browser/cfl-narrow.mjs browser/form-reveal.mjs browser/confirm-search-copy.mjs browser/daterange-settled.mjs browser/display-parts.mjs browser/table-empty.mjs browser/table-footer-text.mjs browser/month-mode.mjs browser/monthpicker.mjs browser/page-columns.mjs browser/table-hover.mjs browser/typed-popover.mjs browser/filter-parity.mjs browser/filter-wrap.mjs browser/filter-stack.mjs browser/timepicker.mjs browser/form-kit-required.mjs browser/picker-in-modal.mjs browser/harness-contract.mjs",
100
100
  "prepack": "node tokens/build.mjs",
101
101
  "version": "node tokens/build.mjs && git add dist",
102
102
  "typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit",
@@ -4,10 +4,10 @@
4
4
  /* display is gated behind [open] so the native <dialog> stays hidden until opened.
5
5
  * An unconditional display here would beat the UA `dialog:not([open]){display:none}`
6
6
  * rule (author origin wins over user-agent) and leave the drawer visible on load. */
7
- .fdy-drawer{position:fixed;inset:0 auto 0 0;margin:0;padding:0;height:100vh;max-height:none;width:min(20rem,82vw);flex-direction:column;border:0;background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-4),var(--surface-inset);overflow:hidden;transform:translateX(-100%);transition:transform var(--dur-slow) var(--ease-standard),overlay var(--dur-slow) allow-discrete,display var(--dur-slow) allow-discrete;}
7
+ .fdy-drawer{position:fixed;inset:0 auto 0 0;margin:0;padding:0;height:100vh;max-height:none;width:min(20rem,82vw);flex-direction:column;border:0;background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-4),var(--surface-inset);overflow:hidden;transform:translateX(-100%);transition-property:transform,overlay,display;transition-duration:var(--dur-slow);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
8
8
  .fdy-drawer[open]{display:flex;transform:none;}
9
9
  .fdy-drawer--right{inset:0 0 0 auto;transform:translateX(100%);}
10
- .fdy-drawer::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity var(--dur-slow) var(--ease-standard),overlay var(--dur-slow) allow-discrete,display var(--dur-slow) allow-discrete;}
10
+ .fdy-drawer::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition-property:opacity,overlay,display;transition-duration:var(--dur-slow);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
11
11
  .fdy-drawer__header{flex:none;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
12
12
  .fdy-drawer__title{font-family:var(--font-display);font-weight:var(--weight-semibold);font-size:var(--text-lg);color:var(--color-text);margin:0 auto 0 0;}
13
13
  .fdy-drawer__close{border:0;background:transparent;color:var(--color-text-muted);font-size:var(--text-xl);line-height:1;cursor:pointer;padding:var(--space-1);border-radius:var(--radius-sm);}
@@ -1,10 +1,10 @@
1
1
  /* Freeday. Modal (native <dialog>) */
2
- .fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-lift-hover),var(--surface-inset);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);transition:opacity var(--dur-base) var(--ease-spring),transform var(--dur-base) var(--ease-spring),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
2
+ .fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface-raised);backdrop-filter:var(--surface-filter);-webkit-backdrop-filter:var(--surface-filter);color:var(--color-text);box-shadow:var(--shadow-lift-hover),var(--surface-inset);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);/* Longhands, not the `transition` shorthand. A shorthand carrying one keyword the engine does not know is INVALID AT PARSE, so the whole declaration is thrown away -- and `allow-discrete` is unknown below Safari 17.4, which is under this kit's floor. The result was a modal and a drawer that opened and closed with no animation at all on an iPhone 7. Split this way only `transition-behavior` is dropped there, and the opacity/transform half survives. The lists align BY INDEX and reproduce the shorthand exactly, including `ease` for the discrete pair and `normal` for the continuous one -- verified by comparing computed styles before and after (#097). */transition-property:opacity,transform,overlay,display;transition-duration:var(--dur-base);transition-timing-function:var(--ease-spring),var(--ease-spring),ease,ease;transition-behavior:normal,normal,allow-discrete,allow-discrete;}
3
3
  .fdy-modal--sm{width:min(24rem,calc(100vw - var(--space-8)));}
4
4
  .fdy-modal--md{width:min(32rem,calc(100vw - var(--space-8)));}
5
5
  .fdy-modal--lg{width:min(48rem,calc(100vw - var(--space-8)));}
6
6
  .fdy-modal--wide{width:min(60rem,calc(100vw - var(--space-8)));}
7
- .fdy-modal::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity var(--dur-base) var(--ease-standard),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
7
+ .fdy-modal::backdrop{background:rgba(10,12,25,.5);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition-property:opacity,overlay,display;transition-duration:var(--dur-base);transition-timing-function:var(--ease-standard),ease,ease;transition-behavior:normal,allow-discrete,allow-discrete;}
8
8
  .fdy-modal__header{flex:none;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
9
9
  .fdy-modal__title{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text);margin:0 auto 0 0;}
10
10
  .fdy-modal__close{border:0;background:transparent;color:var(--color-text-muted);font-size:var(--text-xl);line-height:1;cursor:pointer;padding:var(--space-1);border-radius:var(--radius-sm);}