@wix/himalaya-cli 0.1618.0 → 0.1620.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/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/app-config.d.ts +13 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +2 -0
- package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/telemetry/wix-bi/src/oneapp.d.ts +3 -2
- package/dist/cli.mjs +1223 -67
- package/dist/preview-assets.generated.bin +0 -0
- package/guides/android-ux.md +66 -29
- package/guides/component-reference.json +5 -2
- package/guides/component-reference.md +3 -1
- package/guides/designing-himi-screens.md +43 -10
- package/guides/getting-started.md +2 -1
- package/guides/hhp.md +1 -1
- package/guides/himi-skill.md +2 -1
- package/guides/ios-ux.md +110 -31
- package/guides/mobile-ux.md +22 -5
- package/guides/skill.md +25 -1
- package/guides/tier-5-add-a-screen.md +2 -0
- package/package.json +2 -2
- package/templates/branded-lite/tier5-src/himi-branded-lite-shop/index.ts +27 -6
- package/templates/brandy/dev/index.ts +4 -3
- package/templates/retail-store/tier5-src/himi-branded-lite-shop/index.ts +27 -6
|
Binary file
|
package/guides/android-ux.md
CHANGED
|
@@ -1,39 +1,76 @@
|
|
|
1
1
|
# Designing a Himi App for Android
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Read this before you write `MOBILE-UX.md`, and again before you compose a screen. Each pattern
|
|
4
|
+
below is a decision, the Himi field that implements it, and the Android page it comes from. Read
|
|
5
|
+
it with `himi docs ios-ux` (https://apps.wix.com/_api/himi-serve/docs/ios-ux.md): iPhone and Android
|
|
6
|
+
render the **same** descriptor, so a decision you make here is made for iPhone too.
|
|
6
7
|
|
|
7
|
-
##
|
|
8
|
+
## What Android does for you, and what it cannot do
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
[adaptive layout guidance](https://developer.android.com/design/ui/mobile/guides/layout-and-content/adapt-layout)
|
|
12
|
-
is the reference.
|
|
13
|
-
- Make loading, empty, error, permission, offline, and destructive states actionable.
|
|
14
|
-
- Plan TalkBack, scalable text, contrast, reduced motion, RTL, and touch targets. Use Android's
|
|
15
|
-
[accessibility guidance](https://developer.android.com/design/ui/mobile/guides/foundations/accessibility)
|
|
16
|
-
for the current requirements.
|
|
10
|
+
A Himi screen is one descriptor that the Android shell renders with Jetpack Compose and
|
|
11
|
+
Material 3. Three things are native without any work from you, and you should not redraw them:
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
- **Chrome the shell draws:** the navigation bar at the bottom, the top app bar and its title,
|
|
14
|
+
system and predictive Back, the search bar, bottom sheets and dialogs.
|
|
15
|
+
- **Controls the renderer draws:** `Toggle` is a Material switch, `SegmentedControl` a
|
|
16
|
+
segmented button, `Picker`/`DatePicker`/`Menu`/`BottomSheet`/`AlertDialog` the Material ones.
|
|
17
|
+
- **Your type scale:** typography tokens are emitted in `sp` and follow the user's font scale.
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
One thing it **cannot** do: give Android a different layout from iPhone. Lists, cards and inset
|
|
20
|
+
sections draw the same on every renderer. Choose one structure and say so
|
|
21
|
+
in `MOBILE-UX.md`. Per-platform *look* — type face, corner radius, spacing — is the
|
|
22
|
+
`material` token overlay (see **Idiom overlay** in `himi docs ios-ux`), not a second screen.
|
|
23
|
+
The overlay may change type face, radius and spacing; it may not change a brand colour.
|
|
24
|
+
`himi render --platform android` changes the switch, the segmented button, and the type face.
|
|
25
|
+
A list or a grid looks the same in the iPhone PNG. That is one descriptor, not a failed render.
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Patterns
|
|
28
|
+
|
|
29
|
+
| Decision | Do this on Android | Himi mapping | Android source |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| Top-level destinations | Navigation bar of three to five destinations on a phone. | `navigation` tabs in `config.ts` (`himi docs app-config`); the shell draws the bar. | [Navigation bar](https://m3.material.io/components/navigation-bar/guidelines) |
|
|
32
|
+
| Screen title | Top app bar with the screen title. | Set the screen's `title`. Do not repeat it, or the app name, as Text in the body in any style. Today's Android shell leaves the top-bar title slot empty, so a body title would show twice on iPhone and still not as a bar title here. A group's name is the `Section` `header`. A render with no large heading at the top is correct when `title` is set. `himi render --platform android` draws the Material switch and segmented button. | [Top app bar](https://m3.material.io/components/top-app-bar/guidelines) |
|
|
33
|
+
| Back | System Back, including the predictive back gesture, closes transient UI before it leaves the screen. | The shell handles Back. Plan the order in `MOBILE-UX.md`: an open sheet, search or selection closes first. | [Predictive back](https://developer.android.com/design/ui/mobile/guides/patterns/predictive-back) |
|
|
34
|
+
| Completing a flow (Book, Pay, Confirm) | One filled button at the bottom of the content. | `stickyFooter` holding one `Button` with `style: "primary"` and `fullWidth: true`. The same footer serves iPhone. | [Buttons](https://m3.material.io/components/buttons/guidelines) |
|
|
35
|
+
| Creating, adding, editing, saving | An action in the top app bar. | `topBar.items: [{ id, placement: "trailing", label, icon, onTap, role: "confirm" }]`. It is a top-app-bar action here and a bar button on iPhone. | [Top app bar](https://m3.material.io/components/top-app-bar/guidelines) |
|
|
36
|
+
| Floating action button | Material's create affordance, but a shared screen also draws it on iPhone. | Prefer the `topBar.items` action. Use `Fab` only when the app is deliberately Android-first, and write down that iPhone shows it too. | [FAB](https://m3.material.io/components/floating-action-button/guidelines) |
|
|
37
|
+
| Collections people choose by photo (products, books, portfolio, projects) | A lazy grid of image tiles, or a carousel of covers; the tile is the tap target. | `Grid` `columns: 2` (`columnsMedium: 3`, `columnsExpanded: 4`); a gallery `columns: 3`, `spacing: 2`; a shelf a horizontal `LazyList` sized so the next cover peeks at 360dp. Each tile is an `Image` (`fillWidth`, fixed `aspectRatio`, `contentMode: "fill"`, `placeholder`, `contentDescription`) with the name and price under it and `modifiers.onTap` on the tile; no `Card` or button per tile. The same grid renders on iPhone. | [Lazy grids](https://developer.android.com/develop/ui/compose/lists#lazy-grids), [Carousel](https://m3.material.io/components/carousel/guidelines) |
|
|
38
|
+
| Lists and grouped content | List rows with supporting text; group related settings under a header. | `LazyList` of `ListItem` rows for a schedule or a service menu; `Section` (`header`, optional `inset: true`) for a group. Do not wrap each row in a `Card`. One featured object may be a single `Card`. Choices that may not fit the width, such as times, go in a `LazyList` with `horizontal: true`. The same structure renders on iPhone. | [Lists](https://m3.material.io/components/lists/guidelines) |
|
|
39
|
+
| On/off, one of a few, pick a date | Switch, segmented button, menu or dialog picker. | `Toggle` (`label`, `value`): the `label` is the row drawn beside the switch. `SegmentedControl` with `options: [{ id, label }]` — `label` is required, and `title` on an option fails native decode and drops the whole screen. `Picker`, `DatePicker`. Group the rows in a `Section`, not a `Card`. | [Switch](https://m3.material.io/components/switch/guidelines), [Segmented buttons](https://m3.material.io/components/segmented-buttons/guidelines) |
|
|
40
|
+
| A short task on top of a screen | A modal bottom sheet, dismissed by Back or the scrim. | `BottomSheet`. A new destination is a navigation instead. | [Bottom sheets](https://m3.material.io/components/bottom-sheets/guidelines) |
|
|
41
|
+
| Destructive or blocking decision | A dialog that names the action. | `AlertDialog`; confirm or offer undo. | [Dialogs](https://m3.material.io/components/dialogs/guidelines) |
|
|
42
|
+
| Success | A snackbar above the navigation bar. | `ui.snackbar` from the worker. | [Snackbar](https://m3.material.io/components/snackbar/guidelines) |
|
|
43
|
+
| Search | A search bar in the top app bar, or a dedicated search screen. | `topBar.searchable` plus the page's own `SearchField` (or the row that holds it) with `hiddenPath: "$env:topBarSearch"`. The standard app shell draws that field; only a host with a native search handler hides it for the bar. A dedicated screen when search starts a new task (`himi docs recipe-search`). | [Search](https://m3.material.io/components/search/guidelines) |
|
|
44
|
+
| Tablets, foldables, wide windows | List and detail side by side from the expanded width class (840dp and wider); a readable column for reading. | `SplitView`, `Grid` `columnsMedium`/`columnsExpanded`, `contentMaxWidth`. Render at 840px or wider and record the choice. | [Adaptive layouts](https://developer.android.com/design/ui/mobile/guides/layout-and-content/adapt-layout) |
|
|
45
|
+
| Type and shape | The Material type scale and shape scale, following the user's font size. | Typography tokens from the built-in ramp, no raw sizes; a `material` overlay in `tokens.json` `idiom` with `"$system"` (Roboto) and Material radii, turned on by `idiom: { policy: "follow-surface" }` in `config.ts`. | [Typography](https://m3.material.io/styles/typography/applying-type) |
|
|
46
|
+
| Touch targets | At least 48×48dp. | Use `Button`/`ListItem`; do not shrink a hit area. | [Accessibility](https://developer.android.com/design/ui/mobile/guides/foundations/accessibility) |
|
|
47
|
+
| Edge to edge | Content draws under the system bars; nothing important sits behind them. | The shell applies the insets; keep the last row clear of `stickyFooter`. | [Edge-to-edge](https://developer.android.com/design/ui/mobile/guides/layout-and-content/edge-to-edge) |
|
|
48
|
+
|
|
49
|
+
## Settings, copied as props
|
|
50
|
+
|
|
51
|
+
A settings screen is one `Section` (`inset: true`, `header`), not a stack of `Card`s, and it has no `title1` Text in the body. A `Toggle`'s `label` is the row the renderer draws beside the switch. A `ListItem` `title` with the same word paints it a second time. A row that drills in stays a `ListItem`.
|
|
28
52
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
## Plan
|
|
54
|
+
|
|
55
|
+
In `MOBILE-UX.md` → `## Platform decisions`, fill the Android column of each row from the table
|
|
56
|
+
above: navigation, title, back order, primary action, create/add, lists and grouped content, photo collections,
|
|
57
|
+
controls, sheets and dialogs, search, expanded width, and type/idiom. Give the official URL.
|
|
58
|
+
|
|
59
|
+
Also plan TalkBack labels on every icon and image, font scale, contrast in light and dark,
|
|
60
|
+
reduced motion, right-to-left, and permission, offline and error states.
|
|
61
|
+
|
|
62
|
+
## Audit
|
|
34
63
|
|
|
35
|
-
|
|
64
|
+
Render each primary screen with `--platform android` at a compact width (`--width 360`) and at
|
|
65
|
+
an expanded width (`--width 840`), in light and with `--scheme dark`. Check: the primary action
|
|
66
|
+
in the footer or the top bar, no floating button unless the plan chose it, no hand-drawn
|
|
67
|
+
controls, no heading and no app name added to stand in for the empty top-bar title, a schedule
|
|
68
|
+
made of `ListItem` rows rather than a `Card` per row, products, books and photos as image tiles
|
|
69
|
+
with every image filled and none stretched, a rail that shows part of its next item, a horizontal
|
|
70
|
+
choice row that still shows its last item, and an expanded layout that is either adaptive or recorded as an
|
|
71
|
+
intentional single column.
|
|
36
72
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
73
|
+
On an installed Android app, drive system Back from every transient UI, typed input and
|
|
74
|
+
keyboard submit, completion, TalkBack, enlarged text, dark theme, error recovery, and compact
|
|
75
|
+
plus expanded width. Record the result in the `MOBILE-UX.md` acceptance matrix; with no device,
|
|
76
|
+
record `himi ux record --status unavailable` with the reason.
|
|
@@ -789,8 +789,11 @@
|
|
|
789
789
|
"name": "Section",
|
|
790
790
|
"category": "Structure & disclosure",
|
|
791
791
|
"propsType": "SectionProps",
|
|
792
|
-
"bindings": [
|
|
793
|
-
|
|
792
|
+
"bindings": [
|
|
793
|
+
"header",
|
|
794
|
+
"footer"
|
|
795
|
+
],
|
|
796
|
+
"propsSource": "export interface SectionProps {\n /** Section caption — binding-aware (\"$service:\") on web, iOS and Android. */\n header?: string;\n /** Section caption — binding-aware (\"$service:\") on web, iOS and Android. */\n footer?: string;\n children?: Component[];\n inset?: boolean;\n}"
|
|
794
797
|
},
|
|
795
798
|
{
|
|
796
799
|
"name": "DisclosureGroup",
|
|
@@ -83,7 +83,7 @@ See also **[Modifiers](#modifiers)** (the `modifiers` block) and **[Actions](#ac
|
|
|
83
83
|
| `Popover` | Overlays & menus | `presentedPath` |
|
|
84
84
|
| `Chip` | Chips & selection | `selectedPath`, `tokenPath` |
|
|
85
85
|
| `ChipsInput` | Chips & selection | `valuesPath` |
|
|
86
|
-
| `Section` | Structure & disclosure |
|
|
86
|
+
| `Section` | Structure & disclosure | `header`, `footer` |
|
|
87
87
|
| `DisclosureGroup` | Structure & disclosure | `expandedPath` |
|
|
88
88
|
| `LabeledContent` | Structure & disclosure | — |
|
|
89
89
|
| `Label` | Structure & disclosure | — |
|
|
@@ -1565,7 +1565,9 @@ export interface ChipsInputProps {
|
|
|
1565
1565
|
|
|
1566
1566
|
```ts
|
|
1567
1567
|
export interface SectionProps {
|
|
1568
|
+
/** Section caption — binding-aware ("$service:") on web, iOS and Android. */
|
|
1568
1569
|
header?: string;
|
|
1570
|
+
/** Section caption — binding-aware ("$service:") on web, iOS and Android. */
|
|
1569
1571
|
footer?: string;
|
|
1570
1572
|
children?: Component[];
|
|
1571
1573
|
inset?: boolean;
|
|
@@ -18,6 +18,35 @@ prescriptive layer that sits **above** the wiring guide: `himi docs tier-5-add-a
|
|
|
18
18
|
> `.agents/skills/himalaya/examples/stocks-dashboard.descriptor.json` into the app — a
|
|
19
19
|
> fully-composed screen to study.
|
|
20
20
|
|
|
21
|
+
**A phone screen, before anything below.** Read `himi docs ios-ux` and `himi docs android-ux` too;
|
|
22
|
+
they decide what a phone screen looks like, and this guide decides how to compose it.
|
|
23
|
+
|
|
24
|
+
- **The title lives in the bar, not the body.** Set the screen's `title`; the shell draws it. Do
|
|
25
|
+
not open the body with the screen title, the app name or a greeting as `largeTitle`/`title1`
|
|
26
|
+
text. `himi render` does not draw the bar, so a PNG that starts with content is correct.
|
|
27
|
+
- **A collection is rows, not a card per item.** A schedule, a service menu, a session list or a
|
|
28
|
+
settings group is `ListItem` rows — in a root `LazyList`, or in one `Section` with `inset: true`
|
|
29
|
+
and a `header`. Time, price, coach or duration go in the row's `subtitle`/`trailing`; a small
|
|
30
|
+
action (Book, Full) goes in `trailing`. One featured object may be a single `Card`.
|
|
31
|
+
- **When people choose by the photo, the photo leads.** Products, books, a portfolio and projects
|
|
32
|
+
are image tiles, not rows with a small thumbnail: a `Grid` with `columns: 2` (`columnsMedium: 3`,
|
|
33
|
+
`columnsExpanded: 4`) for products and projects, `columns: 3` with `spacing: 2` and no captions
|
|
34
|
+
for a photo gallery, and a horizontal `LazyList` of covers for each shelf. A tile is one `Image`
|
|
35
|
+
(`fillWidth: true`, a fixed `aspectRatio`, `contentMode: "fill"`, `placeholder`,
|
|
36
|
+
`contentDescription`) with the name and price as text under it, and the whole tile is the tap
|
|
37
|
+
target (`modifiers.onTap`). No `Card` and no button on each tile. A collection without photos
|
|
38
|
+
stays rows.
|
|
39
|
+
- **Choices that may not fit go sideways.** Days and times are a `LazyList` with
|
|
40
|
+
`horizontal: true`, so the last chip stays reachable at 390 points. Size a rail of covers or
|
|
41
|
+
cards so part of the next one shows at 390 points (a cover about 110 points wide, a card about
|
|
42
|
+
160), or nothing tells the reader the rail scrolls.
|
|
43
|
+
- **A detail screen opens with its photos.** A product, a book or a project starts with its image
|
|
44
|
+
(a paged `Carousel` when there are several), edge to edge with a fixed height: the root `VStack`
|
|
45
|
+
has no padding, the image is its first child, and the name, price and options follow in a
|
|
46
|
+
`VStack` that carries the 16-point gutter.
|
|
47
|
+
- **The step that finishes the task sits in `stickyFooter`** — one primary `Button`, not a button
|
|
48
|
+
inside the last card.
|
|
49
|
+
|
|
21
50
|
---
|
|
22
51
|
|
|
23
52
|
## 1. The materials at a glance
|
|
@@ -58,7 +87,8 @@ Screen root gets consistent padding — `padding: { leading: 16, trailing: 16,
|
|
|
58
87
|
`surfacePrimary` root reads well. Tight, even spacing is most of what makes a screen look "designed."
|
|
59
88
|
|
|
60
89
|
### Typography — use the scale, don't invent
|
|
61
|
-
`title1` for the
|
|
90
|
+
`title1` for a heading inside the body that is not the screen title (a featured card, an empty
|
|
91
|
+
state, an onboarding page — the screen title itself is the screen's `title` field), `title3` for a sub-heading/stat, `body` for content, `caption`
|
|
62
92
|
for secondary/meta, `bodyEmphasis` when you need weight inside body copy. Do **not** introduce new
|
|
63
93
|
sizes or weights inline to "make it bigger" — that's a token change, deliberated once, not a per-screen
|
|
64
94
|
decision.
|
|
@@ -190,10 +220,10 @@ tiles; `Card` to group a related cluster onto a raised `surfaceSecondary`; `Sect
|
|
|
190
220
|
group; `Divider` for a hairline separation; `Spacer` to push apart within a row; `SplitView` for
|
|
191
221
|
responsive master/detail. *Reach for `Card` to cluster, not to wrap every single element.*
|
|
192
222
|
|
|
193
|
-
**Lists & collections** — `LazyList` for long/unbounded scrolls (virtualized); `Grid` for
|
|
194
|
-
(`columns:
|
|
223
|
+
**Lists & collections** — `LazyList` for long/unbounded scrolls (virtualized); `Grid` for photo tiles
|
|
224
|
+
(`columns: 2, columnsMedium: 3` for products, `columns: 3` for a photo gallery); `Table` for tabular rows; `Carousel`/`Pager` for swipeable panels;
|
|
195
225
|
`Timeline` for chronological events. Compose rows as `ListItem` / `LabeledContent` / `DisclosureGroup`
|
|
196
|
-
cells
|
|
226
|
+
cells in one `Section` with `inset: true` (or directly in a root `LazyList`) — never one `Card` per row.
|
|
197
227
|
> For a feed/list *screen*, put a vertical worker-bound `LazyList` directly in a plain root
|
|
198
228
|
> `VStack`, as the screen's only bound list: the root carries padding and a literal background at
|
|
199
229
|
> most, is leading-aligned, starts with a child that is always shown, and holds no `Spacer` or
|
|
@@ -233,7 +263,8 @@ long-form Ricos content rather than flattening it into text rows.*
|
|
|
233
263
|
**Inputs & forms** — field controls: `TextField` (with `keyboard`/`autoCapitalization`/`secure`),
|
|
234
264
|
`SearchField`, `Toggle`, `Stepper`, `Slider`, `SelectionControl` (checkbox/radio),
|
|
235
265
|
`Picker`/`WheelPicker`, `DatePicker`, `ColorPicker`, `OtpInput`, `ChipsInput`. *Reach for `Toggle` for
|
|
236
|
-
on/off, `SelectionControl` for pick-from-set — not a row of `Button`s.*
|
|
266
|
+
on/off, `SelectionControl` for pick-from-set — not a row of `Button`s.* A `Toggle`'s `label` is the
|
|
267
|
+
row drawn beside the switch; a `ListItem` `title` with the same word paints it a second time.
|
|
237
268
|
|
|
238
269
|
**Search** — use a dedicated search screen by default when search starts from a broader context
|
|
239
270
|
(such as Home or a catalog); keep search inline when it only filters the list already on screen.
|
|
@@ -293,7 +324,7 @@ Use a live `title` binding to announce “Adding to cart” while pending; `load
|
|
|
293
324
|
|
|
294
325
|
**Navigation** — `Tabs` / `SegmentedControl` to switch peer views; `Pager` + `PageControl` for
|
|
295
326
|
full-width swipe; `StepIndicator` for a wizard's progress; `CollapsingHeader` for a large-title-to-inline
|
|
296
|
-
scroll. *Reach for `SegmentedControl` for 2–4 in-content modes; `Tabs`/tab-bar for top-level sections.*
|
|
327
|
+
scroll. *Reach for `SegmentedControl` for 2–4 in-content modes; `Tabs`/tab-bar for top-level sections.* Each option is `{ id, label }`. `label` is required — `title` on an option fails native decode and drops the whole screen. `title` belongs on `Button` and `ListItem`.
|
|
297
328
|
> **`Pager` sizes each page to its natural content height** (it doesn't stretch to fill), and multi-line
|
|
298
329
|
> text inside a page **truncates** — so onboarding/carousel slides need *short* titles + an explicit
|
|
299
330
|
> `maxLines`, and you can't vertically center a slide with a `spacer()` inside the page (put the spacer
|
|
@@ -339,9 +370,10 @@ Concrete patterns lifted from real usage:
|
|
|
339
370
|
- **Row with a leading label and trailing detail** — `hstack({ spacing: 8, children: [ icon, text, spacer(), trailingText ] })`. The `spacer()` does the push; don't hand-pad.
|
|
340
371
|
- **Fill-width controls share a row** — put fill-width controls in **one** `HStack` and let them share it (weight/flex), rather than stacking `fillMaxWidth` elements. This is a real iOS↔Android parity requirement, not a preference — a full-width control alone in an HStack renders differently across platforms.
|
|
341
372
|
- **A screen that owns its top bar (search beside back)** — set `topBar: { leading: "none" }` and put the row in `stickyHeader`: `hstack([ iconButton({ icon: "arrow_left", accessibilityLabel: "Back", action: { actionId: "nav.back", presentation: "back" } }), withModifiers(textField({ placeholder: "Search", … }), { weight: 1 }) ])`. With **both** set, every shell draws no bar and no back control of its own (iOS hides the system bar, Android floats no chevron and reserves no band, web hides its bar on every look), so the row is the only top bar. `leading: "none"` without a `stickyHeader` keeps the bar's title and trailing actions but drops the back control (and the hamburger, for that screen). System back — hardware, gesture, edge swipe, browser — still pops. Any other `leading` value keeps today's chrome; demo: `gallery_search_topbar`, linked from the gallery's "Lists & data".
|
|
342
|
-
- **Cell list** — `
|
|
373
|
+
- **Cell list** — one `Section` with `inset: true` and a `header`, its children the `ListItem` cells; the cells own their own dividers and padding. A schedule, a service menu and a session list are this shape too, not a `Card` around each item.
|
|
374
|
+
- **Photo tile** — `vstack({ spacing: 6, alignment: "leading", children: [ image({ url, fillWidth: true, aspectRatio: 0.8, contentMode: "fill", placeholder: "surfaceSecondary", contentDescription }) + cornerRadius 8, text(name, "body"), text(price, "callout", { color: "textSecondary" }) ] })` with `modifiers.onTap` on the stack, inside `grid({ columns: 2, columnsMedium: 3, columnsExpanded: 4, spacing: 12 })`. A shelf is the same tile at a fixed `width` (about 110) in `lazyList({ horizontal: true, spacing: 12 })`.
|
|
343
375
|
- **Overlay** — `zstack({ alignment: "center", children: [ backdropImage(cornerRadius 12), withModifiers(label, { padding, background: { token: "accentPrimary" }, cornerRadius: 10 }) ] })`.
|
|
344
|
-
- **Responsive** — `grid({ columns: 1, columnsMedium: 2, spacing: 12 })` for 1-up on phone / 2-up on tablet; `splitView({ primary, detail, primaryMaxWidth: 280 })` for master/detail that stacks on a phone; set `contentMaxWidth` in tokens so wide surfaces keep a readable column instead of running edge-to-edge.
|
|
376
|
+
- **Responsive** — `grid({ columns: 1, columnsMedium: 2, spacing: 12 })` for wide cards, 1-up on phone / 2-up on tablet (photo tiles start at 2 on a phone); `splitView({ primary, detail, primaryMaxWidth: 280 })` for master/detail that stacks on a phone; set `contentMaxWidth` in tokens so wide surfaces keep a readable column instead of running edge-to-edge.
|
|
345
377
|
- **Centered separator, not a flanked divider** — the classic "—— or ——" needs each `divider()` to take equal remaining width, and the HStack row-share only kicks in for controls with an intrinsic fill. **A bare `divider()` inside an HStack is a column separator, not a flanking line**: iOS, macOS, Android, web and React Native all draw it as a thin vertical rule the height of the row that takes no share of the width, which is right for a stats row (`12 | 340 | 87`) and wrong for "—— or ——". For "—— or ——", use a **centered caption**: `hstack({ children: [spacer(), text("or continue with", "caption", { color: "textSecondary" }), spacer()] })`, optionally over a single full-width `divider()`.
|
|
346
378
|
|
|
347
379
|
> **Intrinsic-fill controls grab the row.** `Button` and `TextField` have an intrinsic FILL, so the next four idioms are about NOT letting that surprise you (they were the papercuts every off-repo app hit):
|
|
@@ -590,7 +622,7 @@ nicety. Don't animate everything: if every element moves, nothing reads as impor
|
|
|
590
622
|
A screen full of `id`s and no `accessibility_label`s is testable but unusable with VoiceOver/TalkBack.
|
|
591
623
|
It may be bound to worker state with `$service:<service>.<path>`; the web renderer keeps the
|
|
592
624
|
resulting `aria-label` current as that state changes.
|
|
593
|
-
- **Tap targets** stay comfortably large
|
|
625
|
+
- **Tap targets** stay comfortably large: at least 44pt on iOS and 48dp on Android — don't shrink `button`/`icon` hit areas below that.
|
|
594
626
|
- **Contrast** comes from the token roles — pair `textPrimary`/`textSecondary` with the right surface,
|
|
595
627
|
and provide the `highContrast` scheme value for load-bearing colors.
|
|
596
628
|
- **Reduced motion** — §6.
|
|
@@ -672,6 +704,7 @@ Run this punch list on the screen (it's also a good reviewer checklist):
|
|
|
672
704
|
- [ ] Verified in **both** light and dark (and highContrast for load-bearing color)?
|
|
673
705
|
- [ ] RTL-safe — leading/trailing alignment + logical insets throughout?
|
|
674
706
|
- [ ] Kit-clean — no hardcoded look a kit would override; `material`/gradient/background precedence respected?
|
|
707
|
+
- [ ] Platform-clean — the shell draws the tab bar, title and Back (none redrawn in the body); the primary action sits in `stickyFooter` or a trailing `topBar.items` entry, not a `Fab` an iPhone would also show; switches, segmented choices, pickers, sheets and alerts are the native components (`himi docs ios-ux`, `himi docs android-ux`)?
|
|
675
708
|
- [ ] Fast (§7b) — long or paged lists at the scroll root, stable row ids, `nav` on taps whose destination is fixed, short rails, fixed-size row images?
|
|
676
709
|
|
|
677
710
|
---
|
|
@@ -681,7 +714,7 @@ Run this punch list on the screen (it's also a good reviewer checklist):
|
|
|
681
714
|
The demo gallery's `layout()` is a compact model of the
|
|
682
715
|
principles:
|
|
683
716
|
|
|
684
|
-
- **Type scale, not sizes** — `title1` heading, `body` content, `caption` secondary, colored via
|
|
717
|
+
- **Type scale, not sizes** — `title1` for a heading inside a card, `body` content, `caption` secondary, colored via
|
|
685
718
|
`textPrimary`/`textSecondary`/`accentPrimary` tokens (never hex), `maxLines: 1` to truncate.
|
|
686
719
|
- **Card = cluster** — related copy grouped in `card([ vstack({ spacing: 4, alignment: "leading" }) ])`
|
|
687
720
|
on `surfaceSecondary`, not scattered.
|
|
@@ -33,7 +33,8 @@ and template-preview journey below. Write the reconstruction plan, `SPEC.md`,
|
|
|
33
33
|
same-site One App visitor binding, checks and hosted draft. Obtain the
|
|
34
34
|
site owner's agreement before creating or updating an OAuth client for optional
|
|
35
35
|
member flows; visitor reads and native email/password sign-in need no client.
|
|
36
|
-
Before composing content, read `himi docs
|
|
36
|
+
Before composing content, read `himi docs ios-ux`, `himi docs android-ux` and
|
|
37
|
+
`himi docs designing-himi-screens`, and apply the
|
|
37
38
|
migration guide's screen-content contract: readable detail sections, stable rail
|
|
38
39
|
and image sizing, grouped icon rows for More, native bottom clearance and distinct
|
|
39
40
|
loading/empty/error states. Put the relevant cases in `MOBILE-UX.md` and verify
|
package/guides/hhp.md
CHANGED
|
@@ -218,7 +218,7 @@ himi fidelity --content <dir> [--screen a,b] [--variant light,dark,rtl,tablet] [
|
|
|
218
218
|
For each screen and variant (`light`, `dark`, `rtl`, `tablet`), the command does four things:
|
|
219
219
|
|
|
220
220
|
1. **Captures the mockup.** It draws the page in Chromium at the variant's width. Dark mode is drawn by emulating `prefers-color-scheme`. RTL is drawn by setting `dir=rtl` on the page.
|
|
221
|
-
2. **Captures the web render** from the app's recorded data, as `himi render` does by default. Native frames come from Himi Player on the devices you pass: `--ios <udid>` and `--android <serial>`, plus `--ios-tablet` and `--android-tablet` for the tablet variant. Only those devices are touched. For each variant the command sets dark or light appearance and, for `rtl`, the app's right-to-left language (iOS `AppleLanguages`, Android per-app locale, API 33+). It opens `himi://app/<app>/screen/<screen>` and captures once two frames match. Every setting it changed is restored afterwards, also on Ctrl-C. A variant with no device is reported `skipped`, never passed. Without `--release <id>` or `--serve <port>` the Player shows the app's **live** release, not your working copy. `--serve <port>` serves the working copy on `127.0.0.1:<port>` as `himi run` does, so native and web render the same release. The Player reads a local origin only from a Debug build that compiled it in (see [View your package on a device](himi-player-implementation.md#view-your-package-on-a-device)): build it with `HIMALAYA_API_BASE_URL_INFO=http://127.0.0.1:<port>` on iOS and `-PhimalayaDevServeBaseUrl=http://10.0.2.2:<port>` on Android. An iOS device whose Player reads another origin is skipped with the rebuild to run. Android's origin cannot be read back from the installed app, so check that build yourself. A screen that declares `params` is skipped natively for now, because the Player's deep link does not carry them yet. On a fresh iOS simulator, open one `himi://` link by hand first and accept iOS's "Open in Himi Player?" prompt: the command cannot answer it, and until it is accepted every iOS capture shows the prompt.
|
|
221
|
+
2. **Captures the web render** from the app's recorded data, as `himi render` does by default. Native frames come from Himi Player on the devices you pass: `--ios <udid>` and `--android <serial>`, plus `--ios-tablet` and `--android-tablet` for the tablet variant. Only those devices are touched. For each variant the command sets dark or light appearance and, for `rtl`, the app's right-to-left language (iOS `AppleLanguages`, Android per-app locale, API 33+). It opens `himi://app/<app>/screen/<screen>` and captures once two frames match, the frame is not a blank or loading page, and, on Android, the Player is the app in front rather than the launcher. A screen that has not settled that way after 30 seconds is reported `skipped` as `capture_unsettled`, never judged. Every setting it changed is restored afterwards, also on Ctrl-C. A variant with no device is reported `skipped`, never passed. Without `--release <id>` or `--serve <port>` the Player shows the app's **live** release, not your working copy. `--serve <port>` serves the working copy on `127.0.0.1:<port>` as `himi run` does, so native and web render the same release. The Player reads a local origin only from a Debug build that compiled it in (see [View your package on a device](himi-player-implementation.md#view-your-package-on-a-device)): build it with `HIMALAYA_API_BASE_URL_INFO=http://127.0.0.1:<port>` on iOS and `-PhimalayaDevServeBaseUrl=http://10.0.2.2:<port>` on Android. An iOS device whose Player reads another origin is skipped with the rebuild to run. Android's origin cannot be read back from the installed app, so check that build yourself. A screen that declares `params` is skipped natively for now, because the Player's deep link does not carry them yet. On a fresh iOS simulator, open one `himi://` link by hand first and accept iOS's "Open in Himi Player?" prompt: the command cannot answer it, and until it is accepted every iOS capture shows the prompt.
|
|
222
222
|
3. **Scores mockup↔web** with ImageMagick SSIM, globally and per tile. The worst tile says where the screens differ most.
|
|
223
223
|
4. **Asks a fresh model** two questions, using the repo's fidelity judge prompt. The model never ran in the session that wrote the app.
|
|
224
224
|
- **Design:** does WEB look like the MOCKUP? This judges the port.
|
package/guides/himi-skill.md
CHANGED
|
@@ -56,7 +56,8 @@ and template-preview journey below. Write the reconstruction plan, `SPEC.md`,
|
|
|
56
56
|
same-site One App visitor binding, checks and hosted draft. Obtain the
|
|
57
57
|
site owner's agreement before creating or updating an OAuth client for optional
|
|
58
58
|
member flows; visitor reads and native email/password sign-in need no client.
|
|
59
|
-
Before composing content, read `himi docs
|
|
59
|
+
Before composing content, read `himi docs ios-ux`, `himi docs android-ux` and
|
|
60
|
+
`himi docs designing-himi-screens`, and apply the
|
|
60
61
|
migration guide's screen-content contract: readable detail sections, stable rail
|
|
61
62
|
and image sizing, grouped icon rows for More, native bottom clearance and distinct
|
|
62
63
|
loading/empty/error states. Put the relevant cases in `MOBILE-UX.md` and verify
|
package/guides/ios-ux.md
CHANGED
|
@@ -1,40 +1,119 @@
|
|
|
1
1
|
# Designing a Himi App for iOS
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Read this before you write `MOBILE-UX.md`, and again before you compose a screen. Each pattern
|
|
4
|
+
below is a decision, the Himi field that implements it, and the Apple page it comes from. Read
|
|
5
|
+
it with `himi docs android-ux` (https://apps.wix.com/_api/himi-serve/docs/android-ux.md): iPhone
|
|
6
|
+
and Android render the **same** descriptor, so a decision you make here is made for Android too.
|
|
7
|
+
|
|
8
|
+
## What iOS does for you, and what it cannot do
|
|
9
|
+
|
|
10
|
+
A Himi screen is one descriptor that the iOS shell renders with SwiftUI. Three things are
|
|
11
|
+
native on iPhone without any work from you, and you should not redraw them:
|
|
12
|
+
|
|
13
|
+
- **Chrome the shell draws:** the tab bar, the navigation bar and its title, Back and the edge
|
|
14
|
+
swipe, the search field in the bar, sheets and alerts.
|
|
15
|
+
- **Controls the renderer draws:** `Toggle` is a `UISwitch`, `SegmentedControl` is a segmented
|
|
16
|
+
control, `Picker`/`DatePicker`/`Menu`/`BottomSheet`/`AlertDialog` are the system ones.
|
|
17
|
+
- **Your type scale:** typography tokens scale with Dynamic Type.
|
|
18
|
+
|
|
19
|
+
One thing it **cannot** do: give iPhone a different layout from Android. Lists, cards and inset
|
|
20
|
+
sections draw the same on every renderer. Choose one structure and say so
|
|
21
|
+
in `MOBILE-UX.md`; do not plan "inset grouped on iPhone, full-bleed on Android", because no Himi
|
|
22
|
+
screen can deliver it. Per-platform *look* — type face, corner radius, spacing — is a token
|
|
23
|
+
overlay (see **Idiom overlay** below), not a second screen. `himi render --platform ios` changes
|
|
24
|
+
the switch, the segmented control, and the type face. A list or a grid looks the same in the
|
|
25
|
+
Android PNG. That is one descriptor, not a failed render.
|
|
26
|
+
|
|
27
|
+
## Patterns
|
|
28
|
+
|
|
29
|
+
| Decision | Do this on iOS | Himi mapping | Apple source |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| Top-level destinations | Tab bar of three to five destinations; the shell draws it. | `navigation` tabs in `config.ts` (`himi docs app-config`). Never a row of buttons at the bottom of a screen. | [Tab bars](https://developer.apple.com/design/human-interface-guidelines/tab-bars) |
|
|
32
|
+
| Screen title | Large title on a tab's root screen, collapsing as it scrolls; inline title on a pushed screen. | Set the screen's `title`; the shell makes a root screen's title large on its own. `topBar.titleDisplay: "large" \| "inline"` overrides it. Do not repeat the title, or the app name, as Text in the body in any style. A group's name is the `Section` `header`, not a heading above it. `himi render` does not draw that bar — pass `--platform ios` so the controls match iPhone, and judge the title by the `title` field. A PNG with no large heading at the top is correct when `title` is set. | [Navigation bars](https://developer.apple.com/design/human-interface-guidelines/navigation-bars) |
|
|
33
|
+
| Back | Back button plus edge swipe; the system owns both. | Push with navigation actions; the shell adds Back. Only `topBar.leading: "none"` with a `stickyHeader` replaces the bar, and system back still works. | [Navigation and search](https://developer.apple.com/design/human-interface-guidelines/navigation-and-search) |
|
|
34
|
+
| Completing a flow (Book, Pay, Confirm) | One prominent button at the bottom, above the home indicator. | `stickyFooter` holding one `Button` with `style: "primary"` and `fullWidth: true`, on a `surfacePrimary` background with padding. | [Buttons](https://developer.apple.com/design/human-interface-guidelines/buttons) |
|
|
35
|
+
| Creating, adding, editing, saving | A bar button in the navigation bar, trailing. | `topBar.items: [{ id, placement: "trailing", label, icon, onTap, role: "confirm" }]`. It is a bar button on iPhone and a top-app-bar action on Android. | [Toolbars](https://developer.apple.com/design/human-interface-guidelines/toolbars) |
|
|
36
|
+
| Floating action button | Not an iOS pattern. | Avoid `Fab` in a shared screen: it draws on iPhone too. Use the trailing `topBar.items` entry instead. | [Toolbars](https://developer.apple.com/design/human-interface-guidelines/toolbars) |
|
|
37
|
+
| Grouped content (settings, profile, booking details) | Inset grouped sections with a header and footer. | `Section` with `inset: true`, `header`, `footer`; rows are `ListItem` (`accessory: "chevron"` to drill in). The same sections render on Android. | [Lists and tables](https://developer.apple.com/design/human-interface-guidelines/lists-and-tables) |
|
|
38
|
+
| Collections people choose by photo (products, books, portfolio, projects) | A collection of image tiles; the tile is the tap target. | `Grid` `columns: 2` (`columnsMedium: 3`, `columnsExpanded: 4`); a gallery `columns: 3`, `spacing: 2`; a shelf of covers a horizontal `LazyList` sized so the next cover peeks at 390 points. Each tile is an `Image` (`fillWidth`, fixed `aspectRatio`, `contentMode: "fill"`, `placeholder`, `contentDescription`) with the name and price under it and `modifiers.onTap` on the tile; no `Card` or button per tile. A detail screen opens with its photos edge to edge. | [Collections](https://developer.apple.com/design/human-interface-guidelines/collections), [Images](https://developer.apple.com/design/human-interface-guidelines/images) |
|
|
39
|
+
| Long collections without photos (schedule, services, settings-like lists) | A plain scrolling list; the row is the tap target. | Root `LazyList` of `ListItem` rows; one primary action per row at most; `pagination` for more. A class and a service are `ListItem`s, not one `Card` each. One featured object may be a single `Card`. Choices that may not fit the width, such as times, go in a `LazyList` with `horizontal: true`, so the last choice is reachable at 390 points. | [Lists and tables](https://developer.apple.com/design/human-interface-guidelines/lists-and-tables) |
|
|
40
|
+
| On/off, one of a few, pick a date | The system switch, segmented control, picker. | `Toggle` (`label`, `value`): the `label` is the row drawn beside the switch. `SegmentedControl` with `options: [{ id, label }]` — `label` is required, and `title` on an option fails native decode and drops the whole screen. `Picker`, `DatePicker`. Never a row of buttons or a drawn switch. | [Toggles](https://developer.apple.com/design/human-interface-guidelines/toggles), [Segmented controls](https://developer.apple.com/design/human-interface-guidelines/segmented-controls) |
|
|
41
|
+
| A short task on top of a screen | A sheet; Cancel and Done in its bar. | `BottomSheet`. A new destination is a push instead. | [Sheets](https://developer.apple.com/design/human-interface-guidelines/sheets) |
|
|
42
|
+
| Destructive or blocking decision | An alert, with the destructive button styled as such. | `AlertDialog`, or a `ToolbarItem` with `role: "destructive"`. Confirm or offer undo. | [Alerts](https://developer.apple.com/design/human-interface-guidelines/alerts) |
|
|
43
|
+
| Success | Brief, non-blocking feedback. | `ui.snackbar` from the worker, not an alert. | [Feedback](https://developer.apple.com/design/human-interface-guidelines/feedback) |
|
|
44
|
+
| Search | A search field in the navigation bar. | `topBar.searchable` plus the page's own `SearchField` (or the row that holds it) with `hiddenPath: "$env:topBarSearch"`. The standard app shell draws that field; only a host with a native search handler hides it for the bar. A dedicated search screen when search starts a new task (`himi docs recipe-search`). | [Search fields](https://developer.apple.com/design/human-interface-guidelines/search-fields) |
|
|
45
|
+
| iPad and wide windows | Sidebar or split view for collections; a readable column for reading. | `SplitView`, `Grid` `columnsMedium`/`columnsExpanded`, `contentMaxWidth` in tokens. Render at 834px and record the choice. | [Split views](https://developer.apple.com/design/human-interface-guidelines/split-views) |
|
|
46
|
+
| Type | The system text styles, scaled by Dynamic Type. | Typography tokens from the built-in ramp: `largeTitle`, `title1`–`title3`, `headline`, `body`, `bodyEmphasis`, `callout`, `caption`, `footnote`. No raw sizes. | [Typography](https://developer.apple.com/design/human-interface-guidelines/typography) |
|
|
47
|
+
| Touch targets | At least 44×44 points. | Use `Button`/`ListItem`; do not shrink an icon's hit area below that. | [Accessibility](https://developer.apple.com/design/human-interface-guidelines/accessibility) |
|
|
48
|
+
|
|
49
|
+
## Settings, copied as props
|
|
50
|
+
|
|
51
|
+
A settings screen is one `Section`, not a stack of `Card`s, and it has no `title1` Text in the body. The screen `title` is the heading. A `Toggle`'s `label` is the row the renderer draws beside the switch. A `ListItem` `title` with the same word paints it a second time. A row that drills in stays a `ListItem` (`title`, `accessory: "chevron"`).
|
|
52
|
+
|
|
53
|
+
```jsonc
|
|
54
|
+
"type": "Section",
|
|
55
|
+
"props": { "inset": true, "header": "Preferences", "children": [ /* Toggle, then SegmentedControl */ ] }
|
|
56
|
+
|
|
57
|
+
"type": "SegmentedControl",
|
|
58
|
+
"props": {
|
|
59
|
+
"selectedId": "auto",
|
|
60
|
+
"options": [
|
|
61
|
+
{ "id": "light", "label": "Light" },
|
|
62
|
+
{ "id": "dark", "label": "Dark" },
|
|
63
|
+
{ "id": "auto", "label": "Automatic" }
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`Button` and `ListItem` take `title`. A segment takes `label`. Mixing those up is what drops the screen on device.
|
|
69
|
+
|
|
70
|
+
## Idiom overlay — the iOS look without a second screen
|
|
71
|
+
|
|
72
|
+
The brand stays the same on both phones. What may differ is the platform's dialect: the system
|
|
73
|
+
face, softer or sharper corners, spacing. That lives in `tokens.json`, as a named overlay the
|
|
74
|
+
iOS runtime merges over your base tokens:
|
|
75
|
+
|
|
76
|
+
```jsonc
|
|
77
|
+
// tokens.json — an overlay may only restyle tokens the base already declares
|
|
78
|
+
"idiom": {
|
|
79
|
+
"ios": { "typography": { "body": { "family": "$system", "size": 17, "weight": 400, "line": 1.3 } }, "radius": { "md": 10 } },
|
|
80
|
+
"material": { "typography": { "body": { "family": "$system", "size": 16, "weight": 400, "line": 1.5 } }, "radius": { "md": 12 } }
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// config.ts — declaring the block turns it on; each phone picks its own overlay
|
|
86
|
+
idiom: { policy: "follow-surface" },
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`"$system"` is SF Pro on an iPhone and Roboto on Android. Keep each typography entry complete
|
|
90
|
+
(`size`, `weight`, `line`), or the theme fails to decode on device. An overlay cannot change a
|
|
91
|
+
brand colour (`accentPrimary`, `accentSecondary`, `accentSurface`, `textOnAccent`); that is
|
|
92
|
+
deliberate, so one brand reads as one brand.
|
|
6
93
|
|
|
7
94
|
## Plan
|
|
8
95
|
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- Include dynamic type, VoiceOver, contrast, reduced motion, and right-to-left content from the
|
|
14
|
-
first screen map. Use Apple's [accessibility guidance](https://developer.apple.com/design/human-interface-guidelines/accessibility)
|
|
15
|
-
for the current requirements.
|
|
16
|
-
|
|
17
|
-
## Author
|
|
18
|
-
|
|
19
|
-
- Use Himi semantic components, logical leading/trailing alignment, accessibility labels, and
|
|
20
|
-
token-based typography and colour. Native renderers preserve the expected iOS behaviour.
|
|
21
|
-
- Preserve comfortably sized touch targets; Apple documents 44×44 points as the standard control
|
|
22
|
-
target.
|
|
23
|
-
- Prefer responsive projection for iPad and macOS width. Use `defineView(surface)` only where the
|
|
24
|
-
content or task differs—not merely because the canvas is larger.
|
|
25
|
-
- Use `@himalaya/app-info` only for a documented runtime behavioural fallback. It must not become
|
|
26
|
-
a layout switch.
|
|
96
|
+
In `MOBILE-UX.md` → `## Platform decisions`, write one row for each of: navigation, title,
|
|
97
|
+
back, primary action, create/add, lists and grouped content, photo collections, controls, sheets and alerts,
|
|
98
|
+
search, expanded width, and type/idiom. Fill the Himi mapping with the field from the table
|
|
99
|
+
above and the official source with its URL.
|
|
27
100
|
|
|
28
|
-
|
|
101
|
+
Also plan Dynamic Type, VoiceOver labels on every icon and image, contrast in light and dark,
|
|
102
|
+
reduced motion, and right-to-left, from the first screen map.
|
|
29
103
|
|
|
30
|
-
|
|
31
|
-
keyboard submit, completion, failure, Dynamic Type, VoiceOver, dark appearance, and reduced
|
|
32
|
-
motion. Record the result for every declared state in `MOBILE-UX.md`'s acceptance matrix.
|
|
33
|
-
- Compare every deliberate iOS difference with the `MOBILE-UX.md` decision record.
|
|
104
|
+
## Audit
|
|
34
105
|
|
|
35
|
-
|
|
106
|
+
Render each primary screen with `himi render <screen> --device iphone --platform ios --png <file>`
|
|
107
|
+
and again with `--scheme dark` and at `--device ipad --platform ios`. `--platform ios` draws the
|
|
108
|
+
iPhone switch and segmented control. The navigation bar is still the shell's, so a missing title
|
|
109
|
+
in the PNG is not a missing `title` field. Check: the screen's `title` is set, no heading and no
|
|
110
|
+
app name in the body, a schedule is `ListItem` rows rather than a `Card` per row, products,
|
|
111
|
+
books and photos are image tiles with every image filled and none stretched, a rail shows part of
|
|
112
|
+
its next item, a horizontal choice row still shows its last item, the primary action in the footer or the bar
|
|
113
|
+
(never floating), no hand-drawn switch or segmented control, and nothing hidden behind the home
|
|
114
|
+
indicator.
|
|
36
115
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
116
|
+
On an installed iOS app, drive back and edge swipe, typed input and keyboard submit,
|
|
117
|
+
completion, failure, Dynamic Type, VoiceOver and dark appearance. Record the result for every
|
|
118
|
+
declared state in the `MOBILE-UX.md` acceptance matrix; with no device, record
|
|
119
|
+
`himi ux record --status unavailable` with the reason.
|
package/guides/mobile-ux.md
CHANGED
|
@@ -18,7 +18,10 @@ the approved acceptance scenarios as the readiness bar for the private preview.
|
|
|
18
18
|
data sources, platform envelope, screen catalogue, and non-goals.
|
|
19
19
|
2. Write `MOBILE-UX.md`: task flows, every screen state, accessibility and localisation needs,
|
|
20
20
|
motion, and platform decisions.
|
|
21
|
-
3.
|
|
21
|
+
3. Read `himi docs ios-ux` (https://apps.wix.com/_api/himi-serve/docs/ios-ux.md) and
|
|
22
|
+
`himi docs android-ux` (https://apps.wix.com/_api/himi-serve/docs/android-ux.md) in full
|
|
23
|
+
**before** filling `## Platform decisions`, then apply them again while authoring and
|
|
24
|
+
reviewing. They map each Apple and Material pattern to the Himi field that builds it.
|
|
22
25
|
4. Build shared Himi screens from the contract. Keep the two documents with the app so the next
|
|
23
26
|
author can extend the experience without rediscovering its decisions.
|
|
24
27
|
|
|
@@ -87,6 +90,14 @@ perform the customer task, says what is unavailable, and cannot write customer d
|
|
|
87
90
|
## Platform decisions
|
|
88
91
|
| Shared intent | iOS behaviour | Android behaviour | Himi mapping | Official source |
|
|
89
92
|
| --- | --- | --- | --- | --- |
|
|
93
|
+
| Top-level navigation | | | | |
|
|
94
|
+
| Title and back | | | | |
|
|
95
|
+
| Primary action | | | | |
|
|
96
|
+
| Create / add | | | | |
|
|
97
|
+
| Lists and grouped content | | | | |
|
|
98
|
+
| Controls, sheets, alerts | | | | |
|
|
99
|
+
| Expanded width | | | | |
|
|
100
|
+
| Type and idiom | | | | |
|
|
90
101
|
|
|
91
102
|
## Implementation acceptance
|
|
92
103
|
| Flow or declared state | Trigger | Expected visible result | Primary CTA | Render / device evidence |
|
|
@@ -287,10 +298,16 @@ target file.
|
|
|
287
298
|
Use the least expensive option that preserves the task:
|
|
288
299
|
|
|
289
300
|
1. **One descriptor, responsive projection** — the normal case. Use semantic components, size
|
|
290
|
-
classes, grids, split views, and shell navigation.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
301
|
+
classes, grids, split views, and shell navigation. The shell and the native controls already
|
|
302
|
+
look like iOS on an iPhone and like Material on Android.
|
|
303
|
+
2. **Idiom overlay** — when iPhone and Android should differ in *look* (type face, radius,
|
|
304
|
+
spacing) but not in structure: a `tokens.json` `idiom` block with `ios` and `material`
|
|
305
|
+
overlays, turned on by `idiom: { policy: "follow-surface" }` in `config.ts`. It cannot change
|
|
306
|
+
brand colours. See `himi docs ios-ux` → Idiom overlay.
|
|
307
|
+
3. **One worker, surface-specific view** — use `defineView(surface)` when a phone, desktop, or
|
|
308
|
+
wrist answers a materially different question. Surfaces are form factors (`phone`, `tablet`,
|
|
309
|
+
`desktop`, `watch`, …), not operating systems: there is no iPhone-only or Android-only view.
|
|
310
|
+
4. **Tier-0 native screen** — use only when native APIs or interaction are the point of the screen.
|
|
294
311
|
|
|
295
312
|
Do not create per-node OS conditionals in a descriptor. The wire model stays platform-blind.
|
|
296
313
|
|
package/guides/skill.md
CHANGED
|
@@ -9,6 +9,24 @@ description: Guide a business from a mobile-app idea to a browser-reviewed priva
|
|
|
9
9
|
|
|
10
10
|
You are the customer's mobile-product partner and implementation agent. Take them from an idea to a working, **owner-private** web preview. Do not make the customer paste a long setup prompt or force them to understand Himalaya terminology.
|
|
11
11
|
|
|
12
|
+
## Required design-guide reading for coding agents
|
|
13
|
+
|
|
14
|
+
This page is long; if your tool shows only its start, read the rest from the saved output. Before
|
|
15
|
+
you write `MOBILE-UX.md`, and again before you compose the first screen, fetch each of these four
|
|
16
|
+
public guides by its own URL and read it in full (or `himi docs <id>` from the installed CLI):
|
|
17
|
+
|
|
18
|
+
- https://apps.wix.com/_api/himi-serve/docs/mobile-ux.md — the product and UX contract.
|
|
19
|
+
- https://apps.wix.com/_api/himi-serve/docs/ios-ux.md — Apple's patterns mapped to the Himi field that builds each one.
|
|
20
|
+
- https://apps.wix.com/_api/himi-serve/docs/android-ux.md — Material's patterns mapped the same way.
|
|
21
|
+
- https://apps.wix.com/_api/himi-serve/docs/designing-himi-screens.md — composing one screen from components and tokens.
|
|
22
|
+
|
|
23
|
+
A grep or summarized excerpt does not satisfy this. The iOS and Android guides are what make the
|
|
24
|
+
app native: the shell already draws the tab bar, title and Back on each platform, a shared `Fab`
|
|
25
|
+
also draws on iPhone, lists render the same on every renderer, and per-platform look comes from a
|
|
26
|
+
`tokens.json` `idiom` overlay, not from a second screen. Fill `## Platform decisions` from
|
|
27
|
+
them, with each official source as a URL. Do not add a customer approval gate just for reading
|
|
28
|
+
these public guides.
|
|
29
|
+
|
|
12
30
|
## One App migration route — before generic discovery
|
|
13
31
|
|
|
14
32
|
When the user invokes `himi one-app migrate`, or you are continuing that user's
|
|
@@ -27,7 +45,8 @@ and template-preview journey below. Write the reconstruction plan, `SPEC.md`,
|
|
|
27
45
|
same-site One App visitor binding, checks and hosted draft. Obtain the
|
|
28
46
|
site owner's agreement before creating or updating an OAuth client for optional
|
|
29
47
|
member flows; visitor reads and native email/password sign-in need no client.
|
|
30
|
-
Before composing content, read `himi docs
|
|
48
|
+
Before composing content, read `himi docs ios-ux`, `himi docs android-ux` and
|
|
49
|
+
`himi docs designing-himi-screens`, and apply the
|
|
31
50
|
migration guide's screen-content contract: readable detail sections, stable rail
|
|
32
51
|
and image sizing, grouped icon rows for More, native bottom clearance and distinct
|
|
33
52
|
loading/empty/error states. Put the relevant cases in `MOBILE-UX.md` and verify
|
|
@@ -70,6 +89,11 @@ These are agent instructions, not a claim that the CLI enforces a reading or ret
|
|
|
70
89
|
- **Write this skeleton into `MOBILE-UX.md`, then fill it in.** `himi validate --strict` and `himi ux audit --strict` read these exact headings and columns, so a plan written in your own structure fails the gate after approval, when rewriting it is most expensive. Keep both headings spelled as shown, give the acceptance table a `Primary CTA` column with one row per state and exactly one CTA per cell (`none` is an answer; a comma-, semicolon- or slash-separated list is not), and under `## Evidence` write the widths you actually rendered — one compact (at most 599px) and one wider (at least 600px); `himi render <screenId> --device iphone` renders 390px and `--device ipad` renders 834px. Your own sections (flows, states, accessibility, platform decisions) go around these. The full contract is `himi docs mobile-ux` (https://apps.wix.com/_api/himi-serve/docs/mobile-ux.md before the CLI is installed).
|
|
71
90
|
|
|
72
91
|
```markdown
|
|
92
|
+
## Platform decisions
|
|
93
|
+
| Shared intent | iOS behaviour | Android behaviour | Himi mapping | Official source |
|
|
94
|
+
| --- | --- | --- | --- | --- |
|
|
95
|
+
| Primary action | <from ios-ux> | <from android-ux> | <stickyFooter or topBar.items> | <developer.apple.com/design/… and m3.material.io/… URLs> |
|
|
96
|
+
|
|
73
97
|
## Implementation acceptance
|
|
74
98
|
| Flow or declared state | Trigger | Expected visible result | Primary CTA | Render / device evidence |
|
|
75
99
|
| --- | --- | --- | --- | --- |
|
|
@@ -159,6 +159,8 @@ one object either way.
|
|
|
159
159
|
|
|
160
160
|
So a text-input reducer is `setDraft: (s, p) => ({ ...s, draft: String(p?.value ?? "") })`, bound with `onChange: { actionId: "tier5.notes.setDraft" }`.
|
|
161
161
|
|
|
162
|
+
**Annotate that payload.** `defineService` gives an unannotated second parameter the type `never`, so `p.value` and `p.selectedId` fail `himi validate --strict` even when the read is the one in the table. Write the parameter's type: `setDraft: (s, p: { value?: unknown } | undefined) => ({ ...s, draft: String(p?.value ?? "") })`. A `SegmentedControl` handler names `selectedId` on that same type.
|
|
163
|
+
|
|
162
164
|
**The payload argument can be absent.** A host calls the reducer with one merged object when
|
|
163
165
|
`params`/`args` carry anything, and with **no argument at all** when both are empty — so the
|
|
164
166
|
second parameter is `undefined`, not `{}`. A device always injects `{ value }` for the controls in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/himalaya-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1620.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
|
|
6
6
|
"keywords": [
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"artifactId": "himalaya-cli"
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
|
-
"falconPackageHash": "
|
|
75
|
+
"falconPackageHash": "69fc7303ef0eb0de93d7760ffde1fb553ca5a667e515c46fac02efc3"
|
|
76
76
|
}
|