@synopackageland/ui 0.21.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +1 -0
- package/dist/cli.js +0 -0
- package/dist/components/app/app-layout/AppLayout.vue.d.ts +13 -2
- package/dist/components/composite/about-dialog/AboutDialog.vue.d.ts +1 -1
- package/dist/components/composite/adaptive-panel/AdaptivePanel.vue.d.ts +1 -1
- package/dist/components/composite/disclosure-row/DisclosureRow.vue.d.ts +39 -0
- package/dist/components/composite/record-panel/RecordPanel.vue.d.ts +1 -1
- package/dist/components/containers/split-pane/SplitPane.vue.d.ts +1 -1
- package/dist/components/feedback/validation-popup/ValidationPopup.vue.d.ts +1 -1
- package/dist/components/primitives/button/Button.vue.d.ts +7 -1
- package/dist/components/primitives/checkbox/CheckboxGroup.vue.d.ts +1 -1
- package/dist/components/primitives/dropdown-menu/types.d.ts +6 -0
- package/dist/components/primitives/pagination/Pagination.vue.d.ts +1 -1
- package/dist/components/primitives/popover/Popover.vue.d.ts +1 -1
- package/dist/components/primitives/progress/Progress.vue.d.ts +1 -1
- package/dist/components/primitives/tooltip/tooltipStyle.d.ts +1 -1
- package/dist/components/rich-text-editor/RichTextEditor.vue.d.ts +1 -1
- package/dist/feedback/StatusSlot.vue.d.ts +1 -1
- package/dist/grid-core/theme.d.ts +2 -1
- package/dist/grid.js +253 -251
- package/dist/index.d.ts +3 -0
- package/dist/index.js +14628 -14321
- package/dist/json/catalog.d.ts +2 -0
- package/dist/json/registry.d.ts +34 -0
- package/dist/json-ai-prompt.md +1 -1
- package/dist/json-catalog.schema.json +2 -1
- package/dist/json-preview/preview.js +9516 -9405
- package/dist/json.js +23 -23
- package/dist/lib/adaptiveLayout.d.ts +17 -0
- package/dist/lib/scroll.d.ts +31 -0
- package/dist/lib/typeScale.d.ts +20 -0
- package/dist/reka-runtime.json +1 -1
- package/dist/theme.css +1 -1
- package/dist/themes/dsm7-embedded-theme.css +1 -1
- package/dist/themes/dsm7-embedded.css +1 -1
- package/dist/themes/dsm7.css +1 -1
- package/docs/button-policy.md +19 -2
- package/docs/mobile-native.md +191 -34
- package/package.json +67 -59
package/docs/button-policy.md
CHANGED
|
@@ -29,6 +29,8 @@ are in [dsm-native-metrics.md](dsm-native-metrics.md).
|
|
|
29
29
|
| Section or card | `SettingsSection` / `Card` body, `#actions`, `#footer` | tool | `secondary`; a Save / Cancel row there goes in `FormActions` |
|
|
30
30
|
| Beside a field | `InputGroup` | tool | `secondary` (Open, Copy, Test) |
|
|
31
31
|
| Row of a list or grid | `ListItem` `#actions`, an `actions` column | tool | `IconButton` with a `label` |
|
|
32
|
+
| Short action inside a line of text | after the text it changes ("Source: Built-in microphone Change ›") | text | `text`, with `chevron` when it opens another view |
|
|
33
|
+
| A whole row that opens a page or picker | `DisclosureRow` | row | not a button variant: the row is the button |
|
|
32
34
|
| Navigation, help, external page | `Link` | text link | not a button |
|
|
33
35
|
|
|
34
36
|
## Rules
|
|
@@ -48,18 +50,33 @@ are in [dsm-native-metrics.md](dsm-native-metrics.md).
|
|
|
48
50
|
such as Sign in in a callout; at most one per section.
|
|
49
51
|
6. Keep the default size. `sm` and `lg` exist for dense rows and touch layouts;
|
|
50
52
|
neighbouring buttons share one size. On a phone every size is drawn one
|
|
51
|
-
step smaller than the field beside it (md 36, sm 28, lg
|
|
53
|
+
step smaller than the field beside it (md 36, sm 28, lg 40px;
|
|
52
54
|
`--ui-button-*`), with the same 44px tap box; an IconButton keeps its size.
|
|
53
55
|
7. A disabled button keeps its shape and place. Say why next to it.
|
|
54
56
|
8. An icon-only action is an `IconButton` with a `label`; it shows as its
|
|
55
57
|
tooltip and names the button for screen readers.
|
|
56
|
-
9. Labels are verbs, without a closing period
|
|
58
|
+
9. Labels are verbs, without a closing period or "..." (an action that opens
|
|
59
|
+
a dialog, a sheet or a picker says so with its title, not with dots):
|
|
60
|
+
Apply, Disable this connection, Change.
|
|
57
61
|
10. A label is plain text, with at most an icon before or after it:
|
|
58
62
|
`<Button><Mic :size="16" />Record</Button>`. The Button counts only an `svg`
|
|
59
63
|
or an element marked `data-icon` (`Icon` and `StatusDot` are) as the icon:
|
|
60
64
|
it gets the 8px gap and sits on the label's centre line. Do not wrap the
|
|
61
65
|
text in a `<span>`; a span is read as text, so it gets no gap of its own.
|
|
62
66
|
|
|
67
|
+
11. An inline action (a short verb that changes the value it follows: Change,
|
|
68
|
+
Edit, Retry) is `variant="text"`: primary-coloured text with no border or
|
|
69
|
+
background, a pressed state (it dims while held), grey while disabled, and
|
|
70
|
+
the 44px tap box without growing the line. Add `chevron` for a trailing ›
|
|
71
|
+
when it opens another view. Use it in a sentence or beside a value, never
|
|
72
|
+
in a footer or toolbar.
|
|
73
|
+
12. A row the whole of which is clickable (a setting that opens its own page
|
|
74
|
+
or picker: label, current value, ›) is a `DisclosureRow`, not a row with a
|
|
75
|
+
text button at its end. It is one button (or a link with `href`), at
|
|
76
|
+
least 44px high on a phone, with hover, pressed and focus states, and
|
|
77
|
+
Enter or Space opens it. Use a text button when only a word in a line acts;
|
|
78
|
+
use a DisclosureRow when the row is the action.
|
|
79
|
+
|
|
63
80
|
## What the components do
|
|
64
81
|
|
|
65
82
|
- A Button takes its shape from the closest footer: inside `Dialog`, `Sheet`
|
package/docs/mobile-native.md
CHANGED
|
@@ -55,31 +55,47 @@ control and grid sizes; it does not change the 16px field rule.
|
|
|
55
55
|
|
|
56
56
|
| Token | Desktop | Touch tablet | Phone | Used for |
|
|
57
57
|
|---|---|---|---|---|
|
|
58
|
-
| `--ui-font-size` | 13 | 13 | **
|
|
59
|
-
| `--ui-font-size-sm` | 12 | 12 | **
|
|
58
|
+
| `--ui-font-size` | 13 | 13 | **14** | primary text: list and record titles, field values, body copy, menu items |
|
|
59
|
+
| `--ui-font-size-sm` | 12 | 12 | **12** | secondary text: labels, meta, captions, helper text, badges |
|
|
60
60
|
| `--ui-font-size-xs` (new) | 12 | 12 | 12 | the smallest text anywhere |
|
|
61
|
-
| `--ui-line-height` | 20 | 20 | **
|
|
62
|
-
| `--ui-font-size-
|
|
61
|
+
| `--ui-line-height` | 20 | 20 | **20** | body line |
|
|
62
|
+
| `--ui-font-size-lg` | 14 | 14 | **15** | card and section titles |
|
|
63
|
+
| `--ui-font-size-title` | 16 | 16 | **16** | header and dialog titles |
|
|
63
64
|
| `--ui-grid-font-size` (new) | 13 | 13 | **14** | grid cell text (not yet read by the canvas grid; see below) |
|
|
64
65
|
| `--ui-font-size-field` (new) | 16 | 16 | 16 | every field on a coarse pointer, to stop iOS focus zoom |
|
|
65
66
|
| `--ui-control-md` | 28 | **44** | **44** | fields, selects, form and sheet buttons |
|
|
66
|
-
| `--ui-control-sm` | 24 | **32** | **
|
|
67
|
-
| `--ui-button-md` / `-sm` / `-lg` (new) | `--ui-control-*` | `--ui-control-*` | **36 / 28 /
|
|
67
|
+
| `--ui-control-sm` | 24 | **32** | **32** | inline and toolbar buttons (the tap box comes from `--ui-hit-min`) |
|
|
68
|
+
| `--ui-button-md` / `-sm` / `-lg` (new) | `--ui-control-*` | `--ui-control-*` | **36 / 28 / 40** | a Button and a SplitButton, one size smaller than the field beside it; the tap box stays 44. An IconButton keeps `--ui-control-*` |
|
|
68
69
|
| `--ui-hit-min` (new) | 0 | **44** | **44** | smallest tap box |
|
|
69
70
|
| `--ui-tap-gap` (new) | 0 | **8** | **8** | room between tap targets; `--ui-icon-button-gap` reads it on touch |
|
|
70
71
|
| `--ui-grid-row-height` / `-header-height` | 28 | **36** | **32** | canvas grid rows (docs/ux-grid.md) |
|
|
71
|
-
| `--ui-list-row-height`
|
|
72
|
-
| `--ui-menu-row-height` | 32 | **44** | **
|
|
73
|
-
| `--ui-page-padding-x` / `-y` | 16 (narrow layout 12) | 16 | **
|
|
74
|
-
| `--ui-section-gap` | 12 | 12 | **
|
|
75
|
-
| `--ui-card-padding` | 20 (Card default) | 20 | **
|
|
76
|
-
| `--ui-chrome-height` | 44 (web 48, narrow layout 44) | – | **
|
|
72
|
+
| `--ui-list-row-height` | 32 | **44** | **44** | list rows, a DisclosureRow |
|
|
73
|
+
| `--ui-menu-row-height` | 32 | **44** | **44** | menu, select and sheet list rows |
|
|
74
|
+
| `--ui-page-padding-x` / `-y` | 16 (narrow layout 12) | 16 | **12** | page gutter |
|
|
75
|
+
| `--ui-section-gap` | 12 | 12 | **12** | space between sections |
|
|
76
|
+
| `--ui-card-padding` | 20 (Card default) | 20 | **12** | card inset |
|
|
77
|
+
| `--ui-chrome-height` | 44 (web 48, narrow layout 44) | – | **44** | the phone bar (plus its 1px rule) |
|
|
78
|
+
| `--ui-tab-bar-height` | – | – | **48** | the tab bar's row, above `--ui-safe-bottom` |
|
|
79
|
+
| `--ui-section-tab-height` | 44 | 44 | **40** | the drawn height of a phone's tabs strip (its tap box stays 44) |
|
|
80
|
+
| `--ui-sticky-top-offset` | 0 | 0 | measured | what sticks at the top of a PageContent (see below) |
|
|
81
|
+
| `--ui-page-end-space` | 24 | 24 | 24, the safe area while the bars are hidden on a fill page | room after a page's last row |
|
|
77
82
|
| `--ui-radius-sheet` (new) | 12 | 12 | 12 | top corners of a bottom sheet |
|
|
78
83
|
| `--ui-safe-top/right/bottom/left` (new) | `env(safe-area-inset-*, 0px)` | same | same | notch and home indicator; non-zero only once the viewport meta has `viewport-fit=cover` |
|
|
79
84
|
| `--ui-pressed` (new) | 8% of the text colour | same | same | the pressed tint |
|
|
80
85
|
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
Phone density follows native apps: 14px body text, 12px insets and gaps,
|
|
87
|
+
44px rows and a 36px button (40 for `size="lg"`) inside a 44px tap box.
|
|
88
|
+
Components add no padding beyond the tokens, and the containers follow them
|
|
89
|
+
on a touch phone:
|
|
90
|
+
|
|
91
|
+
- A Card's header is one 44px row and its body `--ui-card-padding` on every
|
|
92
|
+
side, with no extra gap under the title.
|
|
93
|
+
- A `ListItem` row is 10px top and bottom; a description is 12px on a 16px
|
|
94
|
+
line, so a two-line row is 56px.
|
|
95
|
+
- A surface inside a card's body (a Card in a Card, a Surface) draws no
|
|
96
|
+
frame, shadow or inset of its own: cards never nest their padding.
|
|
97
|
+
- Fields stay 44px (the 16px field rule), and a desktop or DSM window keeps
|
|
98
|
+
its sizes.
|
|
83
99
|
|
|
84
100
|
Phone motion tokens (all read 0ms under reduced motion or `data-ui-motion="off"`):
|
|
85
101
|
|
|
@@ -208,7 +224,7 @@ alone on its line still needs 44px.
|
|
|
208
224
|
|
|
209
225
|
- **Hardcoded font sizes:** components that write a literal size, such as
|
|
210
226
|
`text-[13px]` in Checkbox, Switch, Radio, Input and the menu rows, don't
|
|
211
|
-
pick up the
|
|
227
|
+
pick up the 14px phone text until they read `--ui-font-size`. Their owners
|
|
212
228
|
(later changes) switch them over. Fields are already 16px on touch.
|
|
213
229
|
- **Canvas grid:** the grid draws text at fixed sizes from
|
|
214
230
|
`src/grid-core/theme.ts`. Reading `--ui-grid-font-size` needs a change in
|
|
@@ -312,7 +328,7 @@ attribute of the same name on the component overrides each one.
|
|
|
312
328
|
Use `integer` or `decimal` for numbers typed on a phone. They open the number
|
|
313
329
|
keyboard and skip the native number field's spinner and value parsing.
|
|
314
330
|
`type="number"` stays the native field. `Input` and `SearchField` read
|
|
315
|
-
`--ui-font-size` (
|
|
331
|
+
`--ui-font-size` (14px on a phone), and every field is 16px on a coarse
|
|
316
332
|
pointer (see Tokens and base guards). The clear button (`data-ui-hit`) and the password toggle get the
|
|
317
333
|
44px tap box.
|
|
318
334
|
|
|
@@ -414,7 +430,7 @@ Where it is used:
|
|
|
414
430
|
|---|---|---|
|
|
415
431
|
| `ListDetail` | the list fills the room; the detail is pushed over it, with Back in place of Close | the split view, unchanged |
|
|
416
432
|
| `SectionTabs`, five or more sections or `fill` (a settings page) | a grouped list (`group` on each item sets the blocks, 48px rows with a chevron) that pushes the tapped section; `v-model:pushed` | the strip |
|
|
417
|
-
| `SectionTabs`, four or fewer sections | the scrolling underline strip on a touch phone
|
|
433
|
+
| `SectionTabs`, four or fewer sections | the scrolling underline strip on a touch phone; a full-width SegmentControl with equal segments in a narrow window used with a mouse, or with `phone="segment"` | the strip |
|
|
418
434
|
| `SectionTabs` with `phone="strip"` | the scrolling strip, for tabs inside a pushed page or a sheet | the strip |
|
|
419
435
|
|
|
420
436
|
`phone` (`auto`, `list`, `segment`, `strip`) overrides the choice. A phone
|
|
@@ -450,23 +466,33 @@ Back, keyboard inset, scrim and motion. Neither has swipe code of its own.
|
|
|
450
466
|
## Phone shell
|
|
451
467
|
|
|
452
468
|
On a phone (`AppLayout` 640px or narrower) the layout draws one bar above
|
|
453
|
-
the page, `--ui-chrome-height` tall (
|
|
454
|
-
driven by a mouse):
|
|
469
|
+
the page, `--ui-chrome-height` tall (44px, plus a 1px rule):
|
|
455
470
|
|
|
456
471
|
- **Leading:** the menu button, or a Back button when `back` is set (it
|
|
457
472
|
emits `back`; the app pops its own route). While an `AppTabBar` More tab
|
|
458
473
|
opens the drawer, the menu button steps aside.
|
|
459
474
|
- **Title:** the `title` prop, else the title of the page's `PageHeader`
|
|
460
|
-
(the last one mounted in the layout's content column), else the brand.
|
|
475
|
+
(the last one mounted in the layout's content column), else the brand. 16px
|
|
461
476
|
semibold, one line, cut with an ellipsis; centred while nothing follows it,
|
|
462
477
|
start-aligned beside actions. A `PageHeader` whose title moves into the bar
|
|
463
478
|
keeps its heading for screen readers only, and takes no room when it has
|
|
464
479
|
nothing else to show (no description, meta or actions). There is no large
|
|
465
480
|
collapsing title.
|
|
466
481
|
- **Actions:** first the page's own actions, then `headerActions`
|
|
467
|
-
(DropdownMenu entries).
|
|
468
|
-
|
|
469
|
-
|
|
482
|
+
(DropdownMenu entries). A pick emits `headerAction` with the key.
|
|
483
|
+
- **One "…".** The bar never shows two overflow buttons. While the page's own
|
|
484
|
+
menu is in the bar (a `DropdownMenu` among the page actions, a sub-page's
|
|
485
|
+
"…"), the `headerActions` (an App shell's account entry too) join the end
|
|
486
|
+
of that menu after a separator; a pick of one still emits `headerAction`,
|
|
487
|
+
and the page's own entries still reach the page. Only without such a menu
|
|
488
|
+
do `headerActions` open from a "…" of their own. The same holds in a DSM
|
|
489
|
+
portal and on a sub-page.
|
|
490
|
+
- **No promoted button unless asked.** A `headerActions` entry is a 44px bar
|
|
491
|
+
button of its own (its icon, its label as the accessible name, or else its
|
|
492
|
+
label) only when it is marked `primary: true`, or when it is the only entry
|
|
493
|
+
and has an icon (an App shell's account), and only while the page puts no
|
|
494
|
+
actions or menu in the bar. An App that moves its menu into `headerActions`
|
|
495
|
+
keeps every entry in "…".
|
|
470
496
|
|
|
471
497
|
Every bar button is a 44px target.
|
|
472
498
|
|
|
@@ -503,18 +529,31 @@ them at the bottom of the screen). The root carries
|
|
|
503
529
|
`data-ui-layout-scroll="page"`. A DataGrid or another page that scrolls
|
|
504
530
|
itself keeps its layout.
|
|
505
531
|
|
|
506
|
-
### Native phone shell
|
|
532
|
+
### Native phone shell
|
|
507
533
|
|
|
508
534
|
A *touch phone* is an `AppLayout` 640px or narrower under a coarse pointer
|
|
509
535
|
(the root carries `data-ui-phone`). A narrow DSM window driven by a mouse is
|
|
510
536
|
the phone layout without these additions, and wider layouts are unchanged.
|
|
511
537
|
|
|
512
|
-
**Bar.** One
|
|
513
|
-
App's icon (`brand-icon`) on a
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
538
|
+
**Bar.** One thin 44px row (plus its 1px rule), like X or iOS: Back on a
|
|
539
|
+
sub-page, else the menu button, else the App's 24px icon (`brand-icon`) on a
|
|
540
|
+
root page; the 16px title, start-aligned beside anything else; at most two
|
|
541
|
+
trailing icons, the rest in "…". There is no brand row. Tabs for the page
|
|
542
|
+
(`SectionTabs` with four or fewer sections) are the scrolling underline strip
|
|
543
|
+
right under the bar, drawn 40px (`--ui-section-tab-height`) with a 44px tap
|
|
544
|
+
box; on a page that scrolls as one it sticks under the bar, and to the top
|
|
545
|
+
of the screen while the bars are hidden. `phone="segment"` keeps the
|
|
546
|
+
SegmentControl.
|
|
547
|
+
|
|
548
|
+
**Tab bar.** `AppTabBar` is icon only, like X: a 48px row
|
|
549
|
+
(`--ui-tab-bar-height`) plus the safe area, a 1px rule on top and no shadow.
|
|
550
|
+
Each tab is the bar's height and an equal share of its width (at least 44px
|
|
551
|
+
square) around a 24px icon. The current tab is the primary colour with a
|
|
552
|
+
heavier stroke. The label is the tab's accessible name; it shows in a bubble
|
|
553
|
+
above the tab on a long press (450ms; the tap that ends it does not switch
|
|
554
|
+
tabs) and on hover after 500ms. A count or a dot sits on the icon's corner,
|
|
555
|
+
and screen readers hear it after the label. A tab without an icon shows its
|
|
556
|
+
label instead.
|
|
518
557
|
|
|
519
558
|
**Page actions on a FAB.** By default (`AppLayout` `phone-actions="fab"`)
|
|
520
559
|
the page's actions (PageHeader `#actions`, a pushed page's
|
|
@@ -538,15 +577,22 @@ change: the same slot moves.
|
|
|
538
577
|
- Rare or destructive items belong in `headerActions` ("…" in the bar), not
|
|
539
578
|
in the page actions.
|
|
540
579
|
- `phone-actions` on `AppLayout`, and on a `PageHeader`, `AppToolbar` or
|
|
541
|
-
`PushTransition` for one page: `fab` (default), `bar` (icons in the bar
|
|
542
|
-
in 0.20.20) or `inline` (the page's own header row).
|
|
580
|
+
`PushTransition` for one page: `fab` (default), `bar` (icons in the bar) or `inline` (the page's own header row).
|
|
543
581
|
- `PageFab` picks the FAB explicitly: `<PageFab><Button variant="primary">…</Button></PageFab>`
|
|
544
582
|
inside the page's actions. On a touch phone its content is the FAB and the
|
|
545
583
|
page's other actions go to the bar; elsewhere it renders in place.
|
|
546
584
|
- A page whose primary action is in a footer bar (a `SettingsPage` footer, a
|
|
547
585
|
sticky `FormActions`) keeps its actions in the bar: the bottom already has a
|
|
548
586
|
primary. Content gets `--ui-fab-room` (88px) of bottom padding so the last
|
|
549
|
-
row is never under the FAB.
|
|
587
|
+
row is never under the FAB; there is no room while no FAB is drawn.
|
|
588
|
+
- A fill page (its content scrolls itself: `PageContent height="fill"`, or an
|
|
589
|
+
App shell that keeps the PageContent from scrolling; PageContent marks it
|
|
590
|
+
`data-ui-page-fills`) keeps that room only while the FAB is in view. While
|
|
591
|
+
the FAB is hidden on scroll or by the keyboard, the content's own scroller
|
|
592
|
+
takes the room down to the bars' edge, together with the room the hidden
|
|
593
|
+
bars give back, so no blank band is left under it. A page that scrolls as a
|
|
594
|
+
whole keeps the room at its end, so its length never changes under the
|
|
595
|
+
finger.
|
|
550
596
|
- A toast sits 8px above the FAB while the FAB is shown, and back at its own
|
|
551
597
|
place while the FAB is hidden on scroll or by the keyboard, and steps aside
|
|
552
598
|
(fades out) while the speed dial is open; it moves with the bars (`--ui-motion-bars`, at once with reduced motion). A page without a FAB,
|
|
@@ -573,6 +619,117 @@ than the bars' room never hides them.
|
|
|
573
619
|
footer stays at rest: it slides away only with the bars, so Reset / Apply
|
|
574
620
|
never appear or vanish under the finger while the user edits.
|
|
575
621
|
|
|
622
|
+
**Bottom accessory.** `AppLayout`'s `#accessory` slot is a bar across the
|
|
623
|
+
bottom of the page for something the App is running (a recording bar, a mini
|
|
624
|
+
player). Put the bar's content in it, with `v-if` while nothing runs (an
|
|
625
|
+
empty slot draws nothing):
|
|
626
|
+
|
|
627
|
+
```vue
|
|
628
|
+
<AppLayout>
|
|
629
|
+
…
|
|
630
|
+
<template #accessory><RecordingBar v-if="recording" /></template>
|
|
631
|
+
<template #tabbar><AppTabBar v-model="tab" :items="tabs" /></template>
|
|
632
|
+
</AppLayout>
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
- On a phone it docks right above the tab bar (the bottom-most bar without
|
|
636
|
+
one, over the home indicator's strip), with a 1px rule on top and the bar's
|
|
637
|
+
colour.
|
|
638
|
+
- It stays while the bars hide on scroll: it slides down into the tab bar's
|
|
639
|
+
place, in `--ui-motion-bars`, and comes back up with it. It hides while the
|
|
640
|
+
keyboard is up on a touch phone, as the tab bar does.
|
|
641
|
+
- The layout measures it (`--ui-bottom-accessory-height`, also on the App's
|
|
642
|
+
root) and lifts what must stay clear of it: the FAB sits 16px above it, a
|
|
643
|
+
toast above the FAB (or above the accessory without one), an AsidePanel's
|
|
644
|
+
phone button above it, and the page's end clears it. An App sets none of `--ui-tab-bar-height`, `--ui-fab-tab-bar` or
|
|
645
|
+
toast offsets for it.
|
|
646
|
+
- Elsewhere (a DSM window, a desktop) it is the column's bottom row, sticking
|
|
647
|
+
to the bottom while the document scrolls.
|
|
648
|
+
|
|
649
|
+
**Sticky rows in a padded page scroller.** An App shell that makes the
|
|
650
|
+
PageContent its page's scroller and pads its top (a page without a header)
|
|
651
|
+
would leave a stuck row (a group heading, a DataGrid's header row) that
|
|
652
|
+
padding below the scroller's top, with the rows scrolling under it showing
|
|
653
|
+
in the strip above. PageContent covers that strip
|
|
654
|
+
(`[data-ui-page-scroll-cover]`, in the page header's colour; the padding is
|
|
655
|
+
`--ui-page-scroll-pad`), so a stuck row reads as flush with the bar above it.
|
|
656
|
+
The content starts where it did, and a `position: sticky; top: 0` row needs
|
|
657
|
+
no offset of its own.
|
|
658
|
+
|
|
659
|
+
**What sticks at the top: `--ui-sticky-top-offset`.** PageContent sets
|
|
660
|
+
`--ui-sticky-top-offset` on its root: the height of what sticks at the top
|
|
661
|
+
of its scroller (the covered padding, a sticky `PageHeader` that keeps a row
|
|
662
|
+
of its own, and the phone's tabs strip). Every element in the page body gets
|
|
663
|
+
it as `scroll-margin-top`, so `scrollIntoView`, a `#hash` link and focus
|
|
664
|
+
land below the stuck rows. A row an App makes sticky under them uses
|
|
665
|
+
`top: var(--ui-sticky-top-offset)` instead of a hardcoded 44 or 96px.
|
|
666
|
+
|
|
667
|
+
**The end of a page: `--ui-page-end-space`.** The room after a page's last
|
|
668
|
+
row, 24px. While the bars are hidden on a fill page (`data-ui-page-fills`)
|
|
669
|
+
it is only `--ui-safe-bottom`, so the content's scroller runs to the bottom
|
|
670
|
+
of the screen. An App shell that pads its page end reads it:
|
|
671
|
+
`padding-bottom: var(--ui-page-end-space, var(--ui-space-6, 24px))`.
|
|
672
|
+
|
|
673
|
+
**Scroll helpers.** A phone page scrolls in a different element than a
|
|
674
|
+
desktop or DSM window. `nearestScroller(el)` returns the element that
|
|
675
|
+
scrolls `el` (an ancestor with `overflow-y` auto, scroll or overlay that has
|
|
676
|
+
room to scroll, else the document's scroller). `scrollIntoViewWithinScroller(el,
|
|
677
|
+
{ block, behavior, offset })` scrolls only that element (`block`: `nearest`,
|
|
678
|
+
the default, or `start`), honours `scroll-margin-top` / `-bottom` (so the
|
|
679
|
+
sticky offset above applies) and moves at once under reduced motion.
|
|
680
|
+
`scrollToStart(el)` is the `block: 'start'` case.
|
|
681
|
+
|
|
682
|
+
```ts
|
|
683
|
+
import { scrollToStart, scrollIntoViewWithinScroller } from '@synopackageland/ui'
|
|
684
|
+
scrollToStart(sectionHeading)
|
|
685
|
+
scrollIntoViewWithinScroller(invalidField, { offset: 8 })
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**Rows and inline actions.** A whole clickable row (a setting that opens its
|
|
689
|
+
own page or picker: label, current value, ›) is a `DisclosureRow`; a short
|
|
690
|
+
action inside a line of text ("Change" after the current source) is a
|
|
691
|
+
`Button variant="text"`, with `chevron` when it opens another view. See
|
|
692
|
+
docs/button-policy.md.
|
|
693
|
+
|
|
694
|
+
```vue
|
|
695
|
+
<DisclosureRow label="Language" value="English" @click="open('language')" />
|
|
696
|
+
<p>Source: Built-in microphone <Button variant="text" chevron @click="pick">Change</Button></p>
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**Zoom, large text and tap areas.** Density never costs reach or reading:
|
|
700
|
+
|
|
701
|
+
- Text sizes are the semantic tokens (`--ui-font-size`, `-sm`, `-xs`, `-lg`,
|
|
702
|
+
`-title`); components carry no literal sizes. Canvas and chart text reads
|
|
703
|
+
them too: the grid draws cells at `--ui-grid-font-size`, and an App's own
|
|
704
|
+
canvas or chart uses `readTypeScale(el)` (and `canvasFont(scale, 'small')`)
|
|
705
|
+
on the element that draws, again on a theme or layout change.
|
|
706
|
+
- Rows, footers, bars and the tab bar have a minimum height, never a fixed or
|
|
707
|
+
maximum one: two lines or larger text make them taller. A short action in a
|
|
708
|
+
row (a text button) keeps its label and never shrinks; the text beside it
|
|
709
|
+
wraps instead.
|
|
710
|
+
- Pinch zoom is never locked (no `maximum-scale`, no `user-scalable=no`). At
|
|
711
|
+
200% zoom (a 390px phone lays out at 195px) every page reflows: nothing
|
|
712
|
+
scrolls sideways and no text is cut.
|
|
713
|
+
- The drawn size may be small; the tap area is not. Every bar button, tab,
|
|
714
|
+
text button, DisclosureRow and icon button owns a 44px square around its
|
|
715
|
+
centre (`--ui-hit-min`), and the square stops at a neighbour instead of
|
|
716
|
+
covering it. A box held inside its component's end edge
|
|
717
|
+
(`--ui-hit-end-room`, a field's reveal button) grows towards its start.
|
|
718
|
+
- `pnpm check:text-zoom` checks the phone scenes at 390x844 and at 200% zoom
|
|
719
|
+
for all three: sideways overflow, cut text, and each tap square.
|
|
720
|
+
|
|
721
|
+
**Bar and tab sizes are tokens.** Set once on the App's root to adjust every
|
|
722
|
+
page; there is no per-platform style:
|
|
723
|
+
|
|
724
|
+
| Token | Phone | Sets |
|
|
725
|
+
|---|---|---|
|
|
726
|
+
| `--ui-chrome-height` | 44 | the top bar's row |
|
|
727
|
+
| `--ui-font-size-title` | 16 | the bar's title |
|
|
728
|
+
| `--ui-tab-bar-height` | 48 | the tab bar's row, above the safe area |
|
|
729
|
+
| `--ui-tab-bar-icon-size` | 24 | a tab's icon |
|
|
730
|
+
| `--ui-section-tab-height` | 40 | the drawn tabs strip under the bar |
|
|
731
|
+
| `--ui-hit-min` | 44 | the smallest tap area |
|
|
732
|
+
|
|
576
733
|
**Footer hints.** In any footer bar (`data-ui-footer-bar`: SettingsPage,
|
|
577
734
|
Dialog, Sheet, BottomSheet, Card, Panel, Wizard, AppActionBar) a hint or
|
|
578
735
|
status beside the buttons ("Enter a Custom ASR API key") takes a full-width
|
|
@@ -641,7 +798,7 @@ without sizes shows the label as plain text. An endless pager shows
|
|
|
641
798
|
unchanged.
|
|
642
799
|
|
|
643
800
|
**DataGrid and DataTable.** `mobile-view="auto"` (the default) shows a list
|
|
644
|
-
instead of the table. Each row is at least `--ui-list-row-height` (
|
|
801
|
+
instead of the table. Each row is at least `--ui-list-row-height` (44px on a
|
|
645
802
|
phone): the title column on the first line, up to two columns on the second
|
|
646
803
|
with a small gap between them, and trailing columns at the end. Columns place themselves with
|
|
647
804
|
`list`:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synopackageland/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Shared Vue 3 UI component library with a DSM 7 inspired application theme",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -60,6 +60,63 @@
|
|
|
60
60
|
"./themes/dsm7-embedded-reset.css": "./dist/themes/dsm7-embedded-reset.css",
|
|
61
61
|
"./themes/dsm7-embedded-theme.css": "./dist/themes/dsm7-embedded-theme.css"
|
|
62
62
|
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"check:docs": "node --test scripts/check-doc-versions.test.mjs && node scripts/check-doc-versions.mjs",
|
|
65
|
+
"dev": "vite build --watch",
|
|
66
|
+
"build": "vite build && vite build --mode json && vite build --mode rich-text && vite build --mode grid && vite build --mode cli && vite build --mode preview && vite build --mode embedded && node scripts/postbuild.mjs",
|
|
67
|
+
"lint": "eslint .",
|
|
68
|
+
"typecheck": "vue-tsc --noEmit -p tsconfig.json",
|
|
69
|
+
"test": "vitest run",
|
|
70
|
+
"test:watch": "vitest",
|
|
71
|
+
"test:changed": "echo \"test:changed skips the tests the changes since ${SYNOPKGLAND_GATE_BASE:-origin/master} do not reach (vitest --changed); pnpm check runs them all\" && vitest run --changed ${SYNOPKGLAND_GATE_BASE:-origin/master}",
|
|
72
|
+
"story": "histoire dev",
|
|
73
|
+
"story:build": "histoire build",
|
|
74
|
+
"check:host": "node scripts/dsm-host-shots.mjs",
|
|
75
|
+
"check:switch": "node scripts/check-switch.mjs",
|
|
76
|
+
"check:tabs": "node scripts/check-tabs.mjs",
|
|
77
|
+
"check:mobile": "node scripts/check-mobile-overflow.mjs",
|
|
78
|
+
"check:ui-patch": "node scripts/check-ui-patch.mjs",
|
|
79
|
+
"check:narrow": "node scripts/check-narrow-controls.mjs",
|
|
80
|
+
"check:drawer-form": "node scripts/check-drawer-form.mjs",
|
|
81
|
+
"check:responsive": "node scripts/check-responsive.mjs",
|
|
82
|
+
"check:adaptive": "node scripts/check-adaptive.mjs",
|
|
83
|
+
"check:sidebar-focus": "node scripts/check-sidebar-focus.mjs",
|
|
84
|
+
"check:json-render": "node scripts/check-json-render.mjs",
|
|
85
|
+
"check:motion": "node scripts/check-motion.mjs",
|
|
86
|
+
"check:topology": "node scripts/check-topology.mjs",
|
|
87
|
+
"check:wizard": "node scripts/check-wizard.mjs",
|
|
88
|
+
"check:edges": "node scripts/check-edges.mjs",
|
|
89
|
+
"check:data-grid": "node scripts/check-data-grid.mjs",
|
|
90
|
+
"check:truncation": "node scripts/check-truncation.mjs",
|
|
91
|
+
"check:link": "node scripts/check-link.mjs",
|
|
92
|
+
"check:text-select": "node scripts/check-text-select.mjs",
|
|
93
|
+
"check:date-picker": "node scripts/check-date-picker.mjs",
|
|
94
|
+
"check:full-page": "node scripts/check-full-page.mjs",
|
|
95
|
+
"check:short-viewport": "node scripts/check-short-viewport.mjs",
|
|
96
|
+
"check:phone-layout": "node scripts/check-phone-layout.mjs",
|
|
97
|
+
"check:text-zoom": "node scripts/check-text-zoom.mjs",
|
|
98
|
+
"check:focus-restore": "node scripts/check-focus-restore.mjs",
|
|
99
|
+
"check:detail-surface": "node scripts/check-detail-surface.mjs",
|
|
100
|
+
"check:internal": "node --test scripts/check-internal-content.test.mjs scripts/check-npm-metadata.test.mjs && node scripts/check-internal-content.mjs --repo .",
|
|
101
|
+
"check:npm-metadata": "node scripts/check-npm-metadata.mjs",
|
|
102
|
+
"check:package": "node scripts/check-package-content.mjs",
|
|
103
|
+
"check:public-api-consumer": "node scripts/check-public-api-consumer.mjs",
|
|
104
|
+
"notices:update": "node scripts/third-party-notices.mjs --write",
|
|
105
|
+
"notices:check": "node scripts/third-party-notices.mjs --check",
|
|
106
|
+
"check": "bash scripts/docker-check.sh \"npm run gates\"",
|
|
107
|
+
"check:fast": "bash scripts/docker-check.sh \"npm run gates\"",
|
|
108
|
+
"check:local-browser": "node scripts/app-local-browser.mjs",
|
|
109
|
+
"check:infotip": "node scripts/check-infotip.mjs",
|
|
110
|
+
"release:stage": "node scripts/stage-release.mjs",
|
|
111
|
+
"check:polish": "bash scripts/docker-check.sh --build --out .polish-shots 'node scripts/check-polish.mjs'",
|
|
112
|
+
"check:acceptance": "node scripts/check-acceptance.mjs",
|
|
113
|
+
"check:palette": "node scripts/check-palette.mjs",
|
|
114
|
+
"gates": "node scripts/gates.mjs",
|
|
115
|
+
"gates:full": "node scripts/gates.mjs --full",
|
|
116
|
+
"check:layout": "node scripts/check-layout.mjs",
|
|
117
|
+
"release:smoke": "node scripts/release-smoke.mjs",
|
|
118
|
+
"check:browser-install": "playwright install chromium"
|
|
119
|
+
},
|
|
63
120
|
"peerDependencies": {
|
|
64
121
|
"vue": "^3.5.0"
|
|
65
122
|
},
|
|
@@ -110,6 +167,13 @@
|
|
|
110
167
|
"engines": {
|
|
111
168
|
"node": ">=20"
|
|
112
169
|
},
|
|
170
|
+
"packageManager": "pnpm@10.33.2",
|
|
171
|
+
"pnpm": {
|
|
172
|
+
"onlyBuiltDependencies": [
|
|
173
|
+
"esbuild",
|
|
174
|
+
"vue-demi"
|
|
175
|
+
]
|
|
176
|
+
},
|
|
113
177
|
"config": {
|
|
114
178
|
"bundledRuntime": {
|
|
115
179
|
"@tanstack/vue-virtual": "3.13.39",
|
|
@@ -117,61 +181,5 @@
|
|
|
117
181
|
"reka-ui": "2.11.0"
|
|
118
182
|
}
|
|
119
183
|
},
|
|
120
|
-
"homepage": "https://www.varvis.me/docs/"
|
|
121
|
-
|
|
122
|
-
"check:docs": "node --test scripts/check-doc-versions.test.mjs && node scripts/check-doc-versions.mjs",
|
|
123
|
-
"dev": "vite build --watch",
|
|
124
|
-
"build": "vite build && vite build --mode json && vite build --mode rich-text && vite build --mode grid && vite build --mode cli && vite build --mode preview && vite build --mode embedded && node scripts/postbuild.mjs",
|
|
125
|
-
"lint": "eslint .",
|
|
126
|
-
"typecheck": "vue-tsc --noEmit -p tsconfig.json",
|
|
127
|
-
"test": "vitest run",
|
|
128
|
-
"test:watch": "vitest",
|
|
129
|
-
"test:changed": "echo \"test:changed skips the tests the changes since ${SYNOPKGLAND_GATE_BASE:-origin/master} do not reach (vitest --changed); pnpm check runs them all\" && vitest run --changed ${SYNOPKGLAND_GATE_BASE:-origin/master}",
|
|
130
|
-
"story": "histoire dev",
|
|
131
|
-
"story:build": "histoire build",
|
|
132
|
-
"check:host": "node scripts/dsm-host-shots.mjs",
|
|
133
|
-
"check:switch": "node scripts/check-switch.mjs",
|
|
134
|
-
"check:tabs": "node scripts/check-tabs.mjs",
|
|
135
|
-
"check:mobile": "node scripts/check-mobile-overflow.mjs",
|
|
136
|
-
"check:ui-patch": "node scripts/check-ui-patch.mjs",
|
|
137
|
-
"check:narrow": "node scripts/check-narrow-controls.mjs",
|
|
138
|
-
"check:drawer-form": "node scripts/check-drawer-form.mjs",
|
|
139
|
-
"check:responsive": "node scripts/check-responsive.mjs",
|
|
140
|
-
"check:adaptive": "node scripts/check-adaptive.mjs",
|
|
141
|
-
"check:sidebar-focus": "node scripts/check-sidebar-focus.mjs",
|
|
142
|
-
"check:json-render": "node scripts/check-json-render.mjs",
|
|
143
|
-
"check:motion": "node scripts/check-motion.mjs",
|
|
144
|
-
"check:topology": "node scripts/check-topology.mjs",
|
|
145
|
-
"check:wizard": "node scripts/check-wizard.mjs",
|
|
146
|
-
"check:edges": "node scripts/check-edges.mjs",
|
|
147
|
-
"check:data-grid": "node scripts/check-data-grid.mjs",
|
|
148
|
-
"check:truncation": "node scripts/check-truncation.mjs",
|
|
149
|
-
"check:link": "node scripts/check-link.mjs",
|
|
150
|
-
"check:text-select": "node scripts/check-text-select.mjs",
|
|
151
|
-
"check:date-picker": "node scripts/check-date-picker.mjs",
|
|
152
|
-
"check:full-page": "node scripts/check-full-page.mjs",
|
|
153
|
-
"check:short-viewport": "node scripts/check-short-viewport.mjs",
|
|
154
|
-
"check:phone-layout": "node scripts/check-phone-layout.mjs",
|
|
155
|
-
"check:focus-restore": "node scripts/check-focus-restore.mjs",
|
|
156
|
-
"check:detail-surface": "node scripts/check-detail-surface.mjs",
|
|
157
|
-
"check:internal": "node --test scripts/check-internal-content.test.mjs scripts/check-npm-metadata.test.mjs && node scripts/check-internal-content.mjs --repo .",
|
|
158
|
-
"check:npm-metadata": "node scripts/check-npm-metadata.mjs",
|
|
159
|
-
"check:package": "node scripts/check-package-content.mjs",
|
|
160
|
-
"check:public-api-consumer": "node scripts/check-public-api-consumer.mjs",
|
|
161
|
-
"notices:update": "node scripts/third-party-notices.mjs --write",
|
|
162
|
-
"notices:check": "node scripts/third-party-notices.mjs --check",
|
|
163
|
-
"check": "bash scripts/docker-check.sh \"npm run gates\"",
|
|
164
|
-
"check:fast": "bash scripts/docker-check.sh \"npm run gates\"",
|
|
165
|
-
"check:local-browser": "node scripts/app-local-browser.mjs",
|
|
166
|
-
"check:infotip": "node scripts/check-infotip.mjs",
|
|
167
|
-
"release:stage": "node scripts/stage-release.mjs",
|
|
168
|
-
"check:polish": "bash scripts/docker-check.sh --build --out .polish-shots 'node scripts/check-polish.mjs'",
|
|
169
|
-
"check:acceptance": "node scripts/check-acceptance.mjs",
|
|
170
|
-
"check:palette": "node scripts/check-palette.mjs",
|
|
171
|
-
"gates": "node scripts/gates.mjs",
|
|
172
|
-
"gates:full": "node scripts/gates.mjs --full",
|
|
173
|
-
"check:layout": "node scripts/check-layout.mjs",
|
|
174
|
-
"release:smoke": "node scripts/release-smoke.mjs",
|
|
175
|
-
"check:browser-install": "playwright install chromium"
|
|
176
|
-
}
|
|
177
|
-
}
|
|
184
|
+
"homepage": "https://www.varvis.me/docs/"
|
|
185
|
+
}
|