@foldjs/svelte 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,25 +1,47 @@
1
- # fold.js
1
+ # fold.js: foldable phone support for React, Vue, Svelte and the web
2
2
 
3
- **Make any web app foldable-ready.** Hinge-aware layouts for foldable phones — in React, Vue, Svelte, Solid, Angular… or plain HTML.
3
+ [![npm](https://img.shields.io/npm/v/foldjs?color=5b4bff)](https://www.npmjs.com/package/foldjs)
4
+ [![gzip size](https://img.shields.io/bundlejs/size/foldjs?label=gzip)](https://bundlejs.com/?q=foldjs)
5
+ [![license](https://img.shields.io/npm/l/foldjs)](LICENSE)
6
+ [![demo](https://img.shields.io/badge/demo-live-5b4bff)](https://christophe77.github.io/foldjs/)
4
7
 
5
- - 🪟 **Two-pane layouts** that put each pane on its own side of the hinge
6
- - 📐 **Posture detection**: `flat`, `book`, `tabletop`, `folded`
7
- - 🧪 **Built-in foldable simulator** — no device, no DevTools setup
8
- - 🧩 **Works everywhere**: framework-agnostic core, thin adapters, Web Components, CSS-only layer
9
- - 🪶 **~2 kB** min+gzip core, zero dependencies, SSR-safe, TypeScript
8
+ **fold.js is an open-source JavaScript library that makes web apps work properly on foldable phones** such as the Samsung Galaxy Z Fold, Google Pixel Fold, Honor Magic V or Microsoft Surface Duo. It detects the hinge and the device posture (flat, book, tabletop), and gives you two-pane layouts that never put content under the hinge. It works with React, Vue, Svelte, Solid, Angular, Astro or plain HTML, weighs about 2 kB, and ships with a built-in foldable simulator so you can build without owning a device.
10
9
 
11
- Built on the web standards shipping in Chromium: [CSS Viewport Segments](https://developer.mozilla.org/en-US/docs/Web/API/Viewport_segments_API) and the [Device Posture API](https://developer.mozilla.org/en-US/docs/Web/API/Device_Posture_API).
10
+ ### [▶ Try the live demo](https://christophe77.github.io/foldjs/) (works on desktop: the simulator is built in) · [See an existing site fixed in one line](https://christophe77.github.io/foldjs/legacy.html)
12
11
 
13
- ---
12
+ ```bash
13
+ npx foldjs audit https://your-site.com # how foldable-ready is your site? Score out of 100 + HTML report
14
+ ```
15
+
16
+ ![fold.js demo switching a web app between flat, book and tabletop postures on a simulated foldable phone](https://raw.githubusercontent.com/christophe77/foldjs/main/docs/demo.gif)
17
+
18
+ ## Why fold.js?
19
+
20
+ Most web apps treat a foldable phone as one big rectangle. Once the device is folded or spanned, text and buttons land under the hinge, and the half-folded "tabletop" posture is ignored. Chromium now exposes the right web standards ([CSS Viewport Segments](https://developer.mozilla.org/en-US/docs/Web/API/Viewport_segments_API) and the [Device Posture API](https://developer.mozilla.org/en-US/docs/Web/API/Device_Posture_API)), but using them directly means pixel math, event wiring, SSR guards and no way to test without hardware. Microsoft's earlier dual-screen libraries (`react-foldable`, `react-native-dualscreen`) are archived.
21
+
22
+ fold.js wraps all of that in one small, framework-agnostic API.
23
+
24
+ ## Features
25
+
26
+ - 🪟 **Two-pane layouts**: each pane on its own side of the hinge, stacked (or one hidden) on regular phones
27
+ - 📐 **Posture detection**: `flat`, `book` (vertical hinge), `tabletop` (horizontal hinge), `folded`
28
+ - 📏 **Hinge geometry**: exact position and size of the hinge and of each screen segment, in JS and as CSS variables
29
+ - 🧪 **Built-in foldable simulator**: switch postures from a floating panel or with Alt+Shift+F, on any desktop browser, with live outlines of everything under the hinge
30
+ - 🔍 **Audit CLI**: `npx foldjs audit <url>` scores any site out of 100 on Galaxy Z Fold and Pixel Fold, with an HTML report, a README badge and a CI mode
31
+ - 🪄 **foldjs/auto**: one script tag retrofits an existing mobile site (modals, tab bars, videos, text columns), no rewrite needed
32
+ - 🧩 **Every framework**: React, Vue, Svelte, Solid adapters, Web Components for everything else, and a CSS-only layer
33
+ - 🪶 **Tiny and safe**: about 2 kB min+gzip, zero dependencies, SSR-safe (Next.js, Nuxt, SvelteKit, Astro), TypeScript types included
14
34
 
15
35
  ## Install
16
36
 
17
37
  ```bash
18
- npm i foldjs # core, CSS, Web Components, simulator
38
+ npm i foldjs # core, CSS layer, Web Components, simulator
19
39
  npm i @foldjs/react # or @foldjs/vue, @foldjs/svelte, @foldjs/solid
20
40
  ```
21
41
 
22
- ## React
42
+ ## Quick start
43
+
44
+ ### React (and Next.js)
23
45
 
24
46
  ```tsx
25
47
  import { TwoPane, FoldWhen, FoldDevtools, useFold } from "@foldjs/react";
@@ -37,13 +59,15 @@ export function Inbox() {
37
59
  <FoldWhen posture="tabletop">
38
60
  <PlayerControls />
39
61
  </FoldWhen>
40
- {import.meta.env.DEV && <FoldDevtools initial="book" />}
62
+ {process.env.NODE_ENV === "development" && <FoldDevtools initial="book" />}
41
63
  </>
42
64
  );
43
65
  }
44
66
  ```
45
67
 
46
- ## Vue
68
+ `@foldjs/react` is marked `"use client"`, so it works in the Next.js App Router out of the box.
69
+
70
+ ### Vue (and Nuxt)
47
71
 
48
72
  ```vue
49
73
  <script setup>
@@ -59,7 +83,7 @@ const fold = useFold(); // fold.value.posture
59
83
  </template>
60
84
  ```
61
85
 
62
- ## Svelte
86
+ ### Svelte (and SvelteKit)
63
87
 
64
88
  ```svelte
65
89
  <script>
@@ -75,7 +99,7 @@ const fold = useFold(); // fold.value.posture
75
99
  <div use:foldWhen={"tabletop"}>Tabletop only</div>
76
100
  ```
77
101
 
78
- ## Solid
102
+ ### Solid
79
103
 
80
104
  ```tsx
81
105
  import { createFold, twoPane } from "@foldjs/solid";
@@ -88,7 +112,7 @@ const fold = createFold(); // fold().posture
88
112
  </div>
89
113
  ```
90
114
 
91
- ## Web Components (Angular, Lit, Astro, htmx, plain HTML…)
115
+ ### Web Components (Angular, Lit, Astro, htmx, plain HTML)
92
116
 
93
117
  ```html
94
118
  <script type="module">import "foldjs/elements";</script>
@@ -101,7 +125,7 @@ const fold = createFold(); // fold().posture
101
125
  <fold-when posture="tabletop book">Only on a split screen</fold-when>
102
126
  ```
103
127
 
104
- ## CSS only
128
+ ### CSS only
105
129
 
106
130
  ```js
107
131
  import "foldjs/css";
@@ -117,23 +141,86 @@ init(); // optional: enables the simulator and non-native browsers
117
141
  <div class="fold-only-tabletop">Tabletop only</div>
118
142
  ```
119
143
 
120
- Variables available on `<html>`: `--fold-hinge-x|y|width|height`, `--fold-segment-0-width|height`, `--fold-segment-1-width|height`.
121
- Attributes: `data-fold-posture`, `data-fold-spanning`.
144
+ CSS variables on `<html>`: `--fold-hinge-x|y|width|height`, `--fold-segment-0-width|height`, `--fold-segment-1-width|height`.
145
+ Attributes on `<html>`: `data-fold-posture`, `data-fold-spanning`.
122
146
 
123
- ## The simulator
147
+ ## Test foldable layouts without a device
124
148
 
125
149
  ```js
126
150
  import { mountDevtools } from "foldjs/devtools";
127
151
  mountDevtools({ initial: "book", position: "bottom-right", hingeSize: 24 });
128
152
  ```
129
153
 
130
- Switch between **Device / Flat / Book / Tabletop** from the floating panel, or press **Alt + Shift + F**.
131
- Or drive it from code (tests, demos, Storybook): `simulate({ posture: "tabletop" })`, `simulate(null)`.
154
+ A floating panel lets you switch between **Device / Flat / Book / Tabletop** and draws the simulated hinge on screen. Press **Alt + Shift + F** to cycle postures. In tests, demos or Storybook, drive it from code: `simulate({ posture: "tabletop" })`, then `simulate(null)` to go back to the real device.
155
+
156
+ The panel also shows a live **foldable score** and outlines every element under the hinge: red for split buttons and form fields, orange for text, modals, fixed elements and videos, yellow for images. Click the score to toggle the outlines and log each issue, with a suggested fix, to the console. Pass `highlight: false` to turn it off.
157
+
158
+ ## Audit any website: `npx foldjs audit`
159
+
160
+ ```bash
161
+ npx foldjs audit https://your-site.com
162
+ ```
163
+
164
+ ```
165
+ 64 Samsung Galaxy Z Fold · book 2 critical, 6 warnings
166
+ 94 Samsung Galaxy Z Fold · tabletop 0 critical, 2 warnings
167
+ 67 Google Pixel Fold · book 3 critical, 5 warnings
168
+ 88 Google Pixel Fold · tabletop 2 critical, 0 warnings
169
+
170
+ Top issues
171
+ ● Form field is split by the hinge #searchInput
172
+ ● Button is split by the hinge header > button.cta
173
+
174
+ Foldable score: 78/100
175
+ Report: ./foldjs-report/report.html
176
+ ```
177
+
178
+ It opens the page in headless Chrome at the real inner-screen sizes of the Samsung Galaxy Z Fold and Google Pixel Fold, in book and tabletop postures, and lists every element under the hinge. The HTML report shows annotated screenshots and a fix for each issue, plus a badge for your README: [![foldable-ready](https://img.shields.io/badge/foldable--ready-100%2F100-brightgreen)](https://github.com/christophe77/foldjs)
179
+
180
+ | Option | Default | |
181
+ |---|---|---|
182
+ | `--device` | `all` | `galaxy-z-fold`, `pixel-fold` or `all` |
183
+ | `--posture` | `all` | `book`, `tabletop` or `all` |
184
+ | `--out` | `foldjs-report` | Report folder (`report.html` + `report.json`) |
185
+ | `--min-score` | | Exit with code 1 below this score, for CI |
186
+ | `--json` | | Print the result as JSON |
187
+ | `--browser` | auto | Chrome, Edge or Chromium path (or `CHROME_PATH`). No browser is downloaded. |
188
+
189
+ In CI (GitHub Actions runners ship with Chrome):
190
+
191
+ ```yaml
192
+ - run: npx foldjs audit http://localhost:3000 --min-score 90
193
+ ```
194
+
195
+ The audit checks the first screen of the page. It runs `findHingeCollisions()` from the core, so you can also call it yourself in a browser or in Playwright tests.
196
+
197
+ ## Retrofit an existing site: `foldjs/auto`
198
+
199
+ One line, no build step, no rewrite:
200
+
201
+ ```html
202
+ <script src="https://unpkg.com/foldjs/dist/auto.global.js"></script>
203
+ ```
204
+
205
+ Or from your bundle: `import { autoFix } from "foldjs/auto"; const undo = autoFix();`
206
+
207
+ On a foldable (and in the simulator), it applies safe, reversible fixes as inline styles, and removes them as soon as the device is flat again:
208
+
209
+ | Fix | Automatic | Option |
210
+ |---|---|---|
211
+ | Open dialogs and modals move to a single screen (right screen in book, top half in tabletop) | ✅ | `modals` |
212
+ | Fixed tab bars and toolbars get an empty slot under the hinge, so no item is split | ✅ | `bars` |
213
+ | Videos and iframes stay on one screen (top half in tabletop) | ✅ | `media` |
214
+ | Text blocks flow into two columns around the hinge | add `data-fold-columns` | `columns` |
215
+ | Any element is pushed off the hinge (titles, call-to-action buttons) | add `data-fold-avoid` | `avoid` |
216
+ | An element and its children are never touched | add `data-fold-ignore` | |
217
+
218
+ Turn a fix off with `autoFix({ bars: false })` or `<script src="…/auto.global.js" data-bars="false">`. [See it on a typical mobile site](https://christophe77.github.io/foldjs/legacy.html): the foldable score goes from 70 to 100 in book posture.
132
219
 
133
220
  ## Core API
134
221
 
135
222
  ```ts
136
- import { getFold, subscribe, simulate, twoPaneLayout, bindTwoPane } from "foldjs";
223
+ import { getFold, subscribe, simulate, twoPaneLayout, bindTwoPane, findHingeCollisions, foldScore } from "foldjs";
137
224
 
138
225
  getFold();
139
226
  // {
@@ -148,28 +235,63 @@ getFold();
148
235
  // }
149
236
 
150
237
  const off = subscribe((state) => console.log(state.posture));
238
+
239
+ // What sits under the hinge right now (real or simulated)?
240
+ const issues = findHingeCollisions(); // [{ severity, kind, message, suggestion, selector, rect, element }, …]
241
+ foldScore(issues); // 0 to 100
151
242
  ```
152
243
 
153
- Writing an adapter for another framework? `subscribe` + `getFold` are all you need; `twoPaneLayout(state)` returns the styles every built-in adapter uses.
244
+ Writing an adapter for another framework? `subscribe` and `getFold` are all you need, and `twoPaneLayout(state)` returns the styles every built-in adapter uses.
245
+
246
+ ## FAQ
247
+
248
+ ### How do I check if my website works on foldable phones?
249
+ Run `npx foldjs audit https://your-site.com`. It tests the page on the Samsung Galaxy Z Fold and Google Pixel Fold in book and tabletop postures, gives a score out of 100 and an HTML report with screenshots of every element under the hinge. While developing, the fold.js simulator shows the same issues live.
250
+
251
+ ### Can I make an existing mobile site foldable-friendly without rewriting it?
252
+ Yes. Add `<script src="https://unpkg.com/foldjs/dist/auto.global.js"></script>`. It moves modals, tab bars and videos off the hinge automatically, and you can mark text blocks with `data-fold-columns` or elements with `data-fold-avoid` for more. For new screens, `<TwoPane>` gives the best result.
253
+
254
+ ### How do I detect a foldable phone in JavaScript?
255
+ Call `getFold()` from `foldjs` (or `useFold()` in React and Vue). `isSpanned` tells you whether the viewport is split by a hinge, `posture` tells you how the device is held, and `hinge` gives the hinge rectangle in CSS pixels. Under the hood fold.js reads `window.viewport.segments` and `navigator.devicePosture`, with a fallback for older Edge builds.
256
+
257
+ ### How do I keep content away from the hinge in CSS?
258
+ Use the `fold-two-pane` class from `foldjs/css`, or the `--fold-hinge-*` and `--fold-segment-*` variables. On supporting browsers the CSS layer also uses the native `env(viewport-segment-*)` values and the `horizontal-viewport-segments` / `vertical-viewport-segments` media queries.
259
+
260
+ ### What is the difference between book and tabletop posture?
261
+ **Book** means the hinge is vertical and the two screens are side by side, like an open book: ideal for list and detail. **Tabletop** means the hinge is horizontal, like a half-open laptop: ideal for a video or camera on top and controls at the bottom.
262
+
263
+ ### Can I build for foldables without owning one?
264
+ Yes. The built-in simulator (`<FoldDevtools />`, `<FoldDevtools>` in Vue, or `mountDevtools()` anywhere) fakes the segments and posture on any desktop browser. Chrome DevTools device emulation (Galaxy Z Fold, Surface Duo) also works with fold.js.
265
+
266
+ ### Which devices and browsers are supported?
267
+ Chrome and Edge on Android foldables (Samsung Galaxy Z Fold, Google Pixel Fold, Honor, Oppo, Vivo, Surface Duo) support the native APIs. On every other browser, including Samsung Internet, Firefox and Safari on iOS, fold.js falls back to a normal single-screen layout and the simulator still works.
268
+
269
+ ### Does fold.js work with server-side rendering?
270
+ Yes. On the server the state is a flat, unsplit screen, and it updates on the client after hydration. It works with Next.js, Nuxt, SvelteKit, Remix and Astro.
271
+
272
+ ### Is fold.js a replacement for react-foldable or react-native-dualscreen?
273
+ For the web, yes. Those Microsoft packages are archived and rely on the deprecated `spanning` media query and `getWindowSegments()`. fold.js uses the current standards and supports more frameworks. It does not target React Native.
154
274
 
155
275
  ## Browser support
156
276
 
157
- | | Native | With `foldjs` |
277
+ | | Native APIs | With fold.js |
158
278
  |---|---|---|
159
- | Chrome / Edge on Android foldables | ✅ Viewport Segments + Device Posture | ✅ |
160
- | Samsung Internet, other browsers | — | ✅ Single-screen fallback, simulator |
161
- | Desktop | — | ✅ Simulator |
279
+ | Chrome / Edge on Android foldables | ✅ Viewport Segments + Device Posture | ✅ Full support |
280
+ | Samsung Internet, Firefox, Safari | ❌ | ✅ Single-screen fallback, simulator |
281
+ | Desktop browsers | ❌ | ✅ Simulator |
162
282
 
163
- ## Repository
283
+ ## Packages
164
284
 
165
- ```
166
- packages/core foldjs core, CSS, Web Components, simulator
167
- packages/react @foldjs/react
168
- packages/vue @foldjs/vue
169
- packages/svelte @foldjs/svelte
170
- packages/solid @foldjs/solid
171
- apps/demo demo site (Vite + React)
172
- ```
285
+ | Package | Description |
286
+ |---|---|
287
+ | [`foldjs`](https://www.npmjs.com/package/foldjs) | Core API, CSS layer (`foldjs/css`), Web Components (`foldjs/elements`), simulator (`foldjs/devtools`), retrofit script (`foldjs/auto`) |
288
+ | [`@foldjs/react`](https://www.npmjs.com/package/@foldjs/react) | `useFold`, `<TwoPane>`, `<FoldWhen>`, `<FoldDevtools>` |
289
+ | [`@foldjs/vue`](https://www.npmjs.com/package/@foldjs/vue) | `useFold`, `<TwoPane>`, `<FoldWhen>`, `<FoldDevtools>` |
290
+ | [`@foldjs/svelte`](https://www.npmjs.com/package/@foldjs/svelte) | `fold` store, `use:twoPane`, `use:foldWhen` |
291
+ | [`@foldjs/solid`](https://www.npmjs.com/package/@foldjs/solid) | `createFold`, `use:twoPane` |
292
+ | [`@foldjs/cli`](https://www.npmjs.com/package/@foldjs/cli) | `foldjs audit` command (run it with `npx foldjs audit`) |
293
+
294
+ ## Contributing
173
295
 
174
296
  ```bash
175
297
  npm install
@@ -178,6 +300,8 @@ npm test
178
300
  npm run build
179
301
  ```
180
302
 
303
+ Issues and pull requests are welcome, especially reports from people testing on real foldable devices.
304
+
181
305
  ## License
182
306
 
183
307
  MIT
package/dist/index.d.cts CHANGED
@@ -15,7 +15,7 @@ declare function twoPane(node: HTMLElement, options?: TwoPaneOptions): {
15
15
  update(next?: TwoPaneOptions): void;
16
16
  destroy: () => void;
17
17
  };
18
- /** <div use:foldWhen={"tabletop"}>…</div> — hidden unless the posture matches. */
18
+ /** <div use:foldWhen={"tabletop"}>…</div>: hidden unless the posture matches. */
19
19
  declare function foldWhen(node: HTMLElement, posture: Posture | Posture[]): {
20
20
  update(next: Posture | Posture[]): void;
21
21
  destroy: () => void;
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ declare function twoPane(node: HTMLElement, options?: TwoPaneOptions): {
15
15
  update(next?: TwoPaneOptions): void;
16
16
  destroy: () => void;
17
17
  };
18
- /** <div use:foldWhen={"tabletop"}>…</div> — hidden unless the posture matches. */
18
+ /** <div use:foldWhen={"tabletop"}>…</div>: hidden unless the posture matches. */
19
19
  declare function foldWhen(node: HTMLElement, posture: Posture | Posture[]): {
20
20
  update(next: Posture | Posture[]): void;
21
21
  destroy: () => void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@foldjs/svelte",
3
- "version": "0.1.0",
4
- "description": "Svelte bindings for Fold.js: a `fold` store and `use:twoPane` / `use:foldWhen` actions.",
3
+ "version": "0.2.0",
4
+ "description": "Svelte store and actions for foldable phones: $fold, use:twoPane, use:foldWhen and a built-in foldable simulator. Works with SvelteKit.",
5
5
  "license": "MIT",
6
6
  "author": "christophe77",
7
7
  "repository": {
@@ -9,7 +9,7 @@
9
9
  "url": "git+https://github.com/christophe77/foldjs.git",
10
10
  "directory": "packages/svelte"
11
11
  },
12
- "homepage": "https://github.com/christophe77/foldjs#readme",
12
+ "homepage": "https://christophe77.github.io/foldjs/",
13
13
  "bugs": "https://github.com/christophe77/foldjs/issues",
14
14
  "type": "module",
15
15
  "main": "./dist/index.cjs",
@@ -28,14 +28,30 @@
28
28
  ],
29
29
  "sideEffects": false,
30
30
  "keywords": [
31
- "foldable",
32
31
  "svelte",
32
+ "sveltejs",
33
+ "sveltekit",
34
+ "svelte-action",
35
+ "store",
36
+ "foldable",
37
+ "foldable-phone",
38
+ "foldables",
33
39
  "dual-screen",
34
40
  "hinge",
41
+ "viewport-segments",
42
+ "device-posture",
43
+ "galaxy-fold",
44
+ "pixel-fold",
45
+ "surface-duo",
46
+ "two-pane",
47
+ "responsive",
48
+ "responsive-design",
49
+ "layout",
50
+ "tabletop-mode",
35
51
  "foldjs"
36
52
  ],
37
53
  "peerDependencies": {
38
- "foldjs": "^0.1.0",
54
+ "foldjs": "^0.2.0",
39
55
  "svelte": ">=4"
40
56
  },
41
57
  "scripts": {