@synopackageland/ui 0.20.3 → 0.20.5
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 +35 -0
- package/LICENSES/THIRD_PARTY.md +2 -32
- package/LICENSES/ux-foundation.md +83 -0
- package/LICENSES/ux-grid-baserow.md +35 -0
- package/LICENSES/ux-grid-teable.md +48 -0
- package/README.md +3 -0
- package/dist/components/app/app-layout/AppLayout.vue.d.ts +52 -5
- package/dist/components/app/app-layout/pageTitle.d.ts +24 -0
- package/dist/components/app/app-root/AppRoot.vue.d.ts +9 -0
- package/dist/components/app/app-tab-bar/AppTabBar.vue.d.ts +1 -1
- package/dist/components/app/aside-panel/AsidePanel.vue.d.ts +69 -0
- package/dist/components/app/aside-panel/asidePreference.d.ts +28 -0
- package/dist/components/app/section-tabs/SectionTabs.vue.d.ts +22 -4
- package/dist/components/composite/about-dialog/AboutDialog.vue.d.ts +1 -1
- package/dist/components/composite/adaptive-panel/AdaptivePanel.vue.d.ts +61 -0
- package/dist/components/composite/connection-topology/ConnectionTopology.vue.d.ts +1 -1
- package/dist/components/composite/data-grid/DataGrid.vue.d.ts +19 -1
- package/dist/components/composite/data-grid/labels.d.ts +15 -0
- package/dist/components/composite/data-grid/listLayout.d.ts +26 -0
- package/dist/components/composite/data-table/DataTable.vue.d.ts +33 -7
- package/dist/components/composite/file-picker/FilePicker.vue.d.ts +2 -2
- package/dist/components/composite/form/FormGroup.vue.d.ts +1 -1
- package/dist/components/composite/form/context.d.ts +13 -0
- package/dist/components/composite/form-actions/FormActions.vue.d.ts +6 -2
- package/dist/components/composite/mention-list/MentionList.vue.d.ts +2 -2
- package/dist/components/composite/overflow-toolbar/OverflowToolbar.vue.d.ts +2 -2
- package/dist/components/composite/record-panel/RecordPanel.vue.d.ts +91 -0
- package/dist/components/composite/record-panel/recordPanelWidth.d.ts +54 -0
- package/dist/components/composite/settings-section/SettingsSection.vue.d.ts +1 -1
- package/dist/components/composite/summary-card/SummaryCard.vue.d.ts +1 -1
- package/dist/components/composite/tag-editor/TagEditor.vue.d.ts +4 -4
- package/dist/components/composite/tree-view/TreeView.vue.d.ts +4 -4
- package/dist/components/composite/wizard/Wizard.vue.d.ts +2 -2
- package/dist/components/composite/wizard/WizardDialog.vue.d.ts +4 -4
- package/dist/components/containers/card/Card.vue.d.ts +1 -1
- package/dist/components/containers/card/CardField.vue.d.ts +26 -0
- package/dist/components/containers/card-grid/CardCover.vue.d.ts +16 -0
- package/dist/components/containers/card-grid/CardGrid.vue.d.ts +1 -1
- package/dist/components/containers/card-grid/VirtualCardGrid.vue.d.ts +71 -0
- package/dist/components/containers/expandable-item/ExpandableItem.vue.d.ts +1 -1
- package/dist/components/containers/fieldset/Fieldset.vue.d.ts +1 -1
- package/dist/components/containers/list-detail/ListDetail.vue.d.ts +6 -4
- package/dist/components/containers/split-pane/SplitPane.vue.d.ts +1 -1
- package/dist/components/feedback/notification/Notification.vue.d.ts +1 -1
- package/dist/components/feedback/notification/NotificationProvider.vue.d.ts +1 -1
- package/dist/components/feedback/validation-popup/ValidationPopup.vue.d.ts +1 -1
- package/dist/components/primitives/breadcrumb/Breadcrumb.vue.d.ts +2 -2
- package/dist/components/primitives/color-picker/ColorPicker.vue.d.ts +4 -4
- package/dist/components/primitives/combobox/Combobox.vue.d.ts +4 -4
- package/dist/components/primitives/context-menu/ContextMenu.vue.d.ts +5 -2
- package/dist/components/primitives/context-menu/ContextMenuItems.vue.d.ts +4 -5
- package/dist/components/primitives/dialog/Dialog.vue.d.ts +16 -8
- package/dist/components/primitives/dropdown-menu/DropdownMenu.vue.d.ts +7 -3
- package/dist/components/primitives/dropdown-menu/DropdownMenuItems.vue.d.ts +6 -3
- package/dist/components/primitives/dropdown-menu/DropdownMenuSheet.vue.d.ts +28 -0
- package/dist/components/primitives/input/Input.vue.d.ts +8 -1
- package/dist/components/primitives/input/Textarea.vue.d.ts +1 -1
- package/dist/components/primitives/input-steppers/InputSteppers.vue.d.ts +1 -1
- package/dist/components/primitives/motion/PushTransition.vue.d.ts +38 -0
- package/dist/components/primitives/multiple-select/MultipleSelect.vue.d.ts +4 -4
- package/dist/components/primitives/pagination/Pagination.vue.d.ts +22 -9
- package/dist/components/primitives/pagination/PaginationSheet.vue.d.ts +27 -0
- package/dist/components/primitives/pagination/labels.d.ts +6 -0
- package/dist/components/primitives/popover/Popover.vue.d.ts +11 -3
- package/dist/components/primitives/popover/floating.d.ts +27 -0
- package/dist/components/primitives/popover/overlay.d.ts +36 -0
- package/dist/components/primitives/segment-control/SegmentControl.vue.d.ts +4 -1
- package/dist/components/primitives/segment-control/context.d.ts +3 -0
- package/dist/components/primitives/select/Select.vue.d.ts +4 -0
- package/dist/components/primitives/select/internal/ComboboxField.vue.d.ts +6 -5
- package/dist/components/primitives/select/internal/SelectSheet.vue.d.ts +42 -0
- package/dist/components/primitives/select/internal/types.d.ts +2 -0
- package/dist/components/primitives/sheet/BottomSheet.vue.d.ts +69 -0
- package/dist/components/primitives/sheet/Sheet.vue.d.ts +13 -5
- package/dist/components/primitives/sheet/phone.d.ts +7 -0
- package/dist/components/primitives/skeleton/SectionSkeleton.vue.d.ts +25 -4
- package/dist/components/primitives/skeleton/Skeleton.vue.d.ts +3 -0
- package/dist/components/primitives/superbox-select/SuperboxSelect.vue.d.ts +4 -4
- package/dist/components/primitives/tag/Tag.vue.d.ts +2 -2
- package/dist/components/primitives/toast/ToastProvider.vue.d.ts +0 -2
- package/dist/components/primitives/toast/toast.d.ts +21 -1
- package/dist/components/primitives/tooltip/Tooltip.vue.d.ts +8 -3
- package/dist/components/primitives/tooltip/tooltipStyle.d.ts +11 -1
- package/dist/components/rich-text-editor/RichTextEditor.vue.d.ts +1 -1
- package/dist/composables/backDismiss.d.ts +19 -0
- package/dist/composables/formControl.d.ts +18 -0
- package/dist/composables/phone.d.ts +8 -0
- package/dist/composables/phoneSurface.d.ts +35 -0
- package/dist/composables/softKeyboard.d.ts +26 -4
- package/dist/composables/visualViewport.d.ts +31 -0
- package/dist/dnd/autoScroll.d.ts +57 -0
- package/dist/dnd/dragKit.d.ts +111 -0
- package/dist/dnd/index.d.ts +4 -0
- package/dist/dnd/layout.d.ts +57 -0
- package/dist/dnd/vue.d.ts +16 -0
- package/dist/feedback/StatusSlot.vue.d.ts +24 -0
- package/dist/feedback/showAfter.d.ts +15 -0
- package/dist/feedback/stackMotion.d.ts +13 -0
- package/dist/feedback/timing.d.ts +23 -0
- package/dist/grid/cells/draw.d.ts +57 -0
- package/dist/grid/cells/images.d.ts +5 -0
- package/dist/grid/cells/index.d.ts +7 -0
- package/dist/grid/cells/number.d.ts +5 -0
- package/dist/grid/cells/registry.d.ts +56 -0
- package/dist/grid/cells/renderers.d.ts +8 -0
- package/dist/grid/cells/status.d.ts +6 -0
- package/dist/grid/cells/types.d.ts +200 -0
- package/dist/grid/cells/use.d.ts +7 -0
- package/dist/grid/clipboard/copy.d.ts +17 -0
- package/dist/grid/clipboard/index.d.ts +4 -0
- package/dist/grid/clipboard/proposal.d.ts +35 -0
- package/dist/grid/clipboard/tsv.d.ts +14 -0
- package/dist/grid/clipboard/write.d.ts +21 -0
- package/dist/grid/contracts.d.ts +200 -0
- package/dist/grid/edit/cell-status.d.ts +33 -0
- package/dist/grid/edit/edit-session.d.ts +97 -0
- package/dist/grid/edit/use-grid-edit.d.ts +48 -0
- package/dist/grid/focus/editor-style.d.ts +67 -0
- package/dist/grid/host/GridHost.vue.d.ts +137 -0
- package/dist/grid/host/controller.d.ts +219 -0
- package/dist/grid/host/dom.d.ts +10 -0
- package/dist/grid/index.d.ts +8 -0
- package/dist/grid/range/block.d.ts +30 -0
- package/dist/grid/range/fill.d.ts +27 -0
- package/dist/grid/range/index.d.ts +2 -0
- package/dist/grid/theme.d.ts +61 -0
- package/dist/grid-core/auto-scroll.d.ts +67 -0
- package/dist/grid-core/column-resize.d.ts +43 -0
- package/dist/grid-core/config.d.ts +39 -0
- package/dist/grid-core/coordinate.d.ts +64 -0
- package/dist/grid-core/drag.d.ts +46 -0
- package/dist/grid-core/freeze.d.ts +11 -0
- package/dist/grid-core/index.d.ts +17 -0
- package/dist/grid-core/keyboard.d.ts +34 -0
- package/dist/grid-core/keys.d.ts +38 -0
- package/dist/grid-core/lru.d.ts +10 -0
- package/dist/grid-core/range.d.ts +28 -0
- package/dist/grid-core/region.d.ts +73 -0
- package/dist/grid-core/renderer/base.d.ts +92 -0
- package/dist/grid-core/renderer/layout.d.ts +129 -0
- package/dist/grid-core/selection-controller.d.ts +62 -0
- package/dist/grid-core/selection.d.ts +35 -0
- package/dist/grid-core/theme.d.ts +81 -0
- package/dist/grid-core/types.d.ts +178 -0
- package/dist/grid.js +6272 -0
- package/dist/i18n/text.d.ts +17 -0
- package/dist/index.d.ts +39 -4
- package/dist/index.js +23037 -14390
- package/dist/interaction/editSession.d.ts +49 -0
- package/dist/interaction/focus.d.ts +28 -0
- package/dist/interaction/gesture.d.ts +49 -0
- package/dist/interaction/ime.d.ts +55 -0
- package/dist/interaction/index.d.ts +6 -0
- package/dist/interaction/outside.d.ts +40 -0
- package/dist/interaction/vue.d.ts +21 -0
- package/dist/json-ai-prompt.md +1 -1
- package/dist/json-preview/preview.js +16780 -11595
- package/dist/json.js +10 -10
- package/dist/lib/adaptiveLayout.d.ts +32 -2
- package/dist/lib/deviceViewport.d.ts +2 -1
- package/dist/lib/motion.d.ts +12 -0
- package/dist/reka-runtime.json +15 -12
- package/dist/rich-text.js +1 -1
- package/dist/theme.css +1 -1
- package/dist/themes/dsm7-embedded-reset.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/dist/virtual/cardHeight.d.ts +39 -0
- package/dist/virtual/galleryColumns.d.ts +11 -0
- package/dist/virtual/layoutMotion.d.ts +30 -0
- package/docs/adaptive-layout.md +148 -25
- package/docs/app-local-modal.md +1 -1
- package/docs/callout.md +22 -1
- package/docs/layout-rules.md +14 -0
- package/docs/list-detail.md +60 -0
- package/docs/mobile-native.md +502 -0
- package/docs/release-checks.md +28 -0
- package/docs/theme-hooks.md +28 -7
- package/docs/ux-foundation.md +133 -0
- package/docs/ux-grid.md +217 -0
- package/package.json +100 -2
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# Mobile-native rules
|
|
2
|
+
|
|
3
|
+
How the UI SDK behaves on a phone (390×844) and a touch tablet (768×1024).
|
|
4
|
+
|
|
5
|
+
## Width containment and the release gate
|
|
6
|
+
|
|
7
|
+
Typography tables scroll horizontally within their parent, including a flex
|
|
8
|
+
or grid parent. Wizard footer groups wrap within the wizard; stage topology
|
|
9
|
+
nodes shrink with the available width. Topology card tracks shrink rather
|
|
10
|
+
than force their container wider. FormGroup uses a 12px indent level on a
|
|
11
|
+
touch phone and 28px elsewhere; an explicit CSS length keeps its value.
|
|
12
|
+
Settings switch rows retain a 44px touch target, and a Slider's value bubble
|
|
13
|
+
stays inside its track at both ends. Heading permalinks use the shared touch
|
|
14
|
+
hit area. Text fields inside a BottomSheet are at least 44px tall on touch
|
|
15
|
+
tablets as well as phones. Long Select values ellipsize within the trigger.
|
|
16
|
+
Numeric preset rows are at least 44px tall on touch. The rich text toolbar
|
|
17
|
+
counts its 44px touch buttons when deciding which tools fit; the rest move
|
|
18
|
+
into the existing More menu.
|
|
19
|
+
|
|
20
|
+
`pnpm check:mobile` checks the gallery at both phone and tablet sizes. It is
|
|
21
|
+
a blocking step of `gates:full`; the basic gate selection stays the same.
|
|
22
|
+
Any remaining audit exceptions are bounded in
|
|
23
|
+
`scripts/fixtures/mobile-overflow-allow.json`, with an audit reference and a
|
|
24
|
+
reason. Normal checks never add exceptions.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Signals
|
|
28
|
+
|
|
29
|
+
| Signal | Where it comes from | What keys on it |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| Narrow | the app root is 640px wide or narrower: AppRoot sets `data-ui-presentation="mobile"`, AppLayout `data-ui-layout-mode="mobile"` | layout shape: drawer, sheets (as before) |
|
|
32
|
+
| Phone | narrow **and** `(pointer: coarse)` | phone tokens (type, control height, spacing, rows, header) |
|
|
33
|
+
| Touch | `@media (pointer: coarse)` | 16px fields, 44px tap boxes, tap feedback, touch-tablet control sizes |
|
|
34
|
+
| Hover | `@media (hover: hover)` | every hand-written `:hover` rule in the theme |
|
|
35
|
+
|
|
36
|
+
A touch tablet at 768px gets the touch rules but not the phone tokens. A
|
|
37
|
+
desktop gets neither, so it looks exactly as before. That includes a DSM
|
|
38
|
+
window or browser window narrowed below 640px and used with a mouse: it keeps
|
|
39
|
+
the narrow layout with desktop sizes. The phone tokens need a coarse pointer
|
|
40
|
+
too, because without that gate the 1280 gallery's narrow solo shots changed.
|
|
41
|
+
|
|
42
|
+
Every touch rule is keyed on `[data-ui-app]`, the root every app has (AppRoot).
|
|
43
|
+
Before this change they were keyed on `[data-ui-theme='dsm7']`. Nothing sets
|
|
44
|
+
that attribute (Studio sets its own theme name), so the rules never applied.
|
|
45
|
+
|
|
46
|
+
## Tokens and base guards
|
|
47
|
+
|
|
48
|
+
### Type and size tokens
|
|
49
|
+
|
|
50
|
+
Each size is one token, so changing a size changes one line. Phone values are
|
|
51
|
+
set in `src/theme/adaptive.css` (the phone block), touch values in the
|
|
52
|
+
`@media (pointer: coarse)` blocks of `adaptive.css` and `dsm7.css`, and
|
|
53
|
+
desktop values in `dsm7.css`. An explicit `data-ui-density` keeps the app's
|
|
54
|
+
control and grid sizes; it does not change the 16px field rule.
|
|
55
|
+
|
|
56
|
+
| Token | Desktop | Touch tablet | Phone | Used for |
|
|
57
|
+
|---|---|---|---|---|
|
|
58
|
+
| `--ui-font-size` | 13 | 13 | **15** | primary text: list and record titles, field values, body copy, menu items |
|
|
59
|
+
| `--ui-font-size-sm` | 12 | 12 | **13** | secondary text: labels, meta, captions, helper text, badges |
|
|
60
|
+
| `--ui-font-size-xs` (new) | 12 | 12 | 12 | the smallest text anywhere |
|
|
61
|
+
| `--ui-line-height` | 20 | 20 | **22** | body line |
|
|
62
|
+
| `--ui-font-size-title` | 16 | 16 | **17** | header and dialog titles |
|
|
63
|
+
| `--ui-grid-font-size` (new) | 13 | 13 | **14** | grid cell text (not yet read by the canvas grid; see below) |
|
|
64
|
+
| `--ui-font-size-field` (new) | 16 | 16 | 16 | every field on a coarse pointer, to stop iOS focus zoom |
|
|
65
|
+
| `--ui-control-md` | 28 | **36** | **44** | fields, selects, form and sheet buttons |
|
|
66
|
+
| `--ui-control-sm` | 24 | **32** | **36** | inline and toolbar buttons (the tap box comes from `--ui-hit-min`) |
|
|
67
|
+
| `--ui-hit-min` (new) | 0 | **44** | **44** | smallest tap box |
|
|
68
|
+
| `--ui-tap-gap` (new) | 0 | **8** | **8** | room between tap targets; `--ui-icon-button-gap` reads it on touch |
|
|
69
|
+
| `--ui-grid-row-height` / `-header-height` | 28 | **36** | **32** | canvas grid rows (docs/ux-grid.md) |
|
|
70
|
+
| `--ui-list-row-height` (new) | 32 | **44** | **48** | list rows |
|
|
71
|
+
| `--ui-menu-row-height` | 32 | **40** | **48** | menu, select and sheet list rows |
|
|
72
|
+
| `--ui-page-padding-x` / `-y` | 16 (narrow layout 12) | 16 | **16** | page gutter |
|
|
73
|
+
| `--ui-section-gap` | 12 | 12 | **20** | space between sections |
|
|
74
|
+
| `--ui-card-padding` | 20 (Card default) | 20 | **16** | card inset |
|
|
75
|
+
| `--ui-chrome-height` | 44 (web 48, narrow layout 44) | – | **48** | the phone header |
|
|
76
|
+
| `--ui-radius-sheet` (new) | 12 | 12 | 12 | top corners of a bottom sheet |
|
|
77
|
+
| `--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` |
|
|
78
|
+
| `--ui-pressed` (new) | 8% of the text colour | same | same | the pressed tint |
|
|
79
|
+
|
|
80
|
+
Density comes from spacing, not smaller text: phone rows are 44–48px, and
|
|
81
|
+
components add no padding beyond the tokens.
|
|
82
|
+
|
|
83
|
+
Phone motion tokens (all read 0ms under reduced motion or `data-ui-motion="off"`):
|
|
84
|
+
|
|
85
|
+
| Token | Value | Use |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `--ui-motion-sheet-in` / `-out` | 280 / 200ms | bottom sheet |
|
|
88
|
+
| `--ui-motion-sheet-ease` | `cubic-bezier(0.32, 0.72, 0, 1)` | sheet, push |
|
|
89
|
+
| `--ui-motion-drawer-in` / `-out` | 240 / 180ms | side drawer |
|
|
90
|
+
| `--ui-motion-push` / `-pop` | 280 / 240ms | push navigation |
|
|
91
|
+
| `--ui-motion-spring-back` | 180ms | a sheet released above its close threshold |
|
|
92
|
+
| `--ui-motion-press` | 150ms | pressed tint fading out (it shows at once) |
|
|
93
|
+
|
|
94
|
+
### Base guards (`preflight.css`)
|
|
95
|
+
|
|
96
|
+
These rules are in the base layer at zero specificity (`:where()`), so any
|
|
97
|
+
component rule overrides them.
|
|
98
|
+
|
|
99
|
+
- No blanket `min-width: 0`. The spec asked for one, but the 1280 gallery
|
|
100
|
+
diff showed it squeezing nowrap rows (Space "wrap off" went one letter per
|
|
101
|
+
line) and a fixed-size brand icon on the desktop. A component whose flex
|
|
102
|
+
child must shrink sets `min-w-0` on it instead (a later change handles the remaining
|
|
103
|
+
width offenders).
|
|
104
|
+
- `overflow-wrap: break-word` on the app root. A word longer than its line
|
|
105
|
+
breaks. We use `break-word`, not `anywhere`, because it only breaks text
|
|
106
|
+
that would overflow and never changes how wide a table column or flex item
|
|
107
|
+
wants to be. Desktop layouts stay the same.
|
|
108
|
+
- `img`, `video` and `iframe` get `max-width: 100%`, and `img` and `video` also
|
|
109
|
+
get `height: auto`. `svg` is left alone so icons keep their size.
|
|
110
|
+
- Don't write `100vw` in components. Use `100%` or the viewport variables from
|
|
111
|
+
the viewport layer. The `check:mobile` script enforces this.
|
|
112
|
+
|
|
113
|
+
### Tap feedback (`touch.css`)
|
|
114
|
+
|
|
115
|
+
- Buttons, links, labels and every element with a button, tab, option,
|
|
116
|
+
menuitem, switch, checkbox, radio or treeitem role get
|
|
117
|
+
`-webkit-tap-highlight-color: transparent` and `touch-action: manipulation`.
|
|
118
|
+
This removes the grey flash and the 300ms double-tap wait. Pinch zoom still
|
|
119
|
+
works. A component's own `touch-none` overrides it.
|
|
120
|
+
- Pressed tint on a coarse pointer: an inset `--ui-pressed` shadow appears
|
|
121
|
+
while the finger is down and fades over `--ui-motion-press`. Button and
|
|
122
|
+
IconButton draw their own pressed state (`data-variant`), so they're
|
|
123
|
+
skipped. A component that sets its own shadow or `active:` style also keeps
|
|
124
|
+
its own.
|
|
125
|
+
- Hover: every hand-written `:hover` rule in the theme sits inside
|
|
126
|
+
`@media (hover: hover)`, so a tap leaves no stuck hover look. Tailwind's
|
|
127
|
+
`hover:` utilities already do this.
|
|
128
|
+
|
|
129
|
+
### Hit area (`hit-area.css`)
|
|
130
|
+
|
|
131
|
+
On a coarse pointer, a control smaller than `--ui-hit-min` keeps its drawn
|
|
132
|
+
size, and an invisible `::before` grows its tap box evenly on all sides to the
|
|
133
|
+
minimum:
|
|
134
|
+
|
|
135
|
+
```css
|
|
136
|
+
inset-block: min(0px, calc((100% - var(--ui-hit-min)) / 2));
|
|
137
|
+
inset-inline: min(0px, calc((100% - var(--ui-hit-min)) / 2));
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
These controls get the hit area:
|
|
141
|
+
|
|
142
|
+
- **Marked with `data-ui-hit`:** `Button`, `IconButton`
|
|
143
|
+
- **Matched by role or attribute:** `button[role=checkbox]` (Checkbox),
|
|
144
|
+
`button[role=radio]` (Radio, ToggleButton), `button[role=switch]` (Switch),
|
|
145
|
+
`[data-tag][role=button]` (a tappable Tag), `[data-tag-remove]`,
|
|
146
|
+
`[data-ui-password-toggle]`
|
|
147
|
+
|
|
148
|
+
A new tappable control can opt in by adding `data-ui-hit`. When two tap boxes
|
|
149
|
+
overlap, the later control wins the overlap. Clusters keep `--ui-tap-gap`
|
|
150
|
+
between controls. The rule sits in the base layer, so a component that sets
|
|
151
|
+
its own `position` or draws its own `::before` keeps it, and loses the larger
|
|
152
|
+
tap box.
|
|
153
|
+
|
|
154
|
+
### Not done in this layer
|
|
155
|
+
|
|
156
|
+
- **Hardcoded font sizes:** components that write a literal size, such as
|
|
157
|
+
`text-[13px]` in Checkbox, Switch, Radio, Input and the menu rows, don't
|
|
158
|
+
pick up the 15px phone text until they read `--ui-font-size`. Their owners
|
|
159
|
+
(later changes) switch them over. Fields are already 16px on touch.
|
|
160
|
+
- **Canvas grid:** the grid draws text at fixed sizes from
|
|
161
|
+
`src/grid-core/theme.ts`. Reading `--ui-grid-font-size` needs a change in
|
|
162
|
+
the grid.
|
|
163
|
+
- **Form gap:** `Form` writes its own `--ui-form-gap` inline, from
|
|
164
|
+
`--ui-field-gap`. The 16px phone form gap belongs to Form.
|
|
165
|
+
- **Hover rules in `adaptive.css`:** the brand action, sidebar collapse and
|
|
166
|
+
resize handles still use plain `:hover`. They belong to the shell tasks.
|
|
167
|
+
|
|
168
|
+
## Floating layer
|
|
169
|
+
|
|
170
|
+
Every anchored popup is placed by one preset, `src/components/primitives/popover/floating.ts`, on Reka's popper (floating-ui flip, shift, size and hide). A component binds it on its Reka `*Content` and never picks its own collision props.
|
|
171
|
+
|
|
172
|
+
| Setting | Value |
|
|
173
|
+
|---|---|
|
|
174
|
+
| Boundary | the app window in app scope (`useCollisionBoundary`), else the visual viewport (Reka's root boundary for fixed popups) |
|
|
175
|
+
| Padding | 8px from each edge, plus `--ui-safe-*` (or `env(safe-area-inset-*)`), plus `--ui-kb-inset` at the bottom. Read when the popup opens and again on a visual-viewport resize |
|
|
176
|
+
| Shift | `sticky: 'partial'` |
|
|
177
|
+
| Detached anchor | `hideWhenDetached`: the popup hides once its anchor has left the boundary |
|
|
178
|
+
| Max size | `FLOATING_SIZE_CLASS`: `--reka-popper-available-width` and `-height`. Never `100vw` |
|
|
179
|
+
| Autofocus | none on a coarse pointer (`skipAutoFocusOnTouch` in `overlay.ts`) |
|
|
180
|
+
|
|
181
|
+
```vue
|
|
182
|
+
<script setup lang="ts">
|
|
183
|
+
import { FLOATING_SIZE_CLASS, useFloating } from '@/components/primitives/popover/floating'
|
|
184
|
+
const floating = useFloating(() => trigger.value?.$el, () => open.value)
|
|
185
|
+
</script>
|
|
186
|
+
<SomeContent v-bind="floating" :class="[ownClasses, FLOATING_SIZE_CLASS]" />
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Portal layer.** `src/theme/floating.css` makes the AppRoot `[data-ui-portals]` layer `position: absolute; inset: 0; overflow: clip; pointer-events: none`. It covers the app root exactly, so app-local dialogs keep the app as their containing block, and nothing in it can add page width or height. Its direct children take presses again; Reka's inline `pointer-events` still wins while a modal surface is open.
|
|
190
|
+
|
|
191
|
+
**Tooltip on touch.** `openOnTap` defaults to on with a coarse pointer: a tap toggles the tooltip and a tap anywhere else closes it. Pass `:open-on-tap="false"` when the tap must only run the trigger's action.
|
|
192
|
+
|
|
193
|
+
**Uses it today:** Popover, Tooltip, OverflowTooltip and all anchored popups listed below.
|
|
194
|
+
|
|
195
|
+
## All anchored popups
|
|
196
|
+
|
|
197
|
+
Menus (including nested menus and Breadcrumb), Select and the shared ComboboxField,
|
|
198
|
+
DatePicker, ColorPicker, InputSteppers presets, InfoTip, ValidationPopup,
|
|
199
|
+
MentionList and OverflowToolbar now use the floating preset above. SplitButton,
|
|
200
|
+
MultipleSelect and SuperboxSelect inherit it through their shared primitives.
|
|
201
|
+
Menu sublevels inherit the parent's live padding, so an open submenu follows
|
|
202
|
+
safe-area and keyboard changes. Preferred and trigger widths are capped by
|
|
203
|
+
`--reka-popper-available-width`; lists scroll within the available height.
|
|
204
|
+
Opening a searchable Select, calendar or palette on touch does not autofocus
|
|
205
|
+
an input or swatch. On a phone, pickers and long menus open as bottom sheets instead
|
|
206
|
+
(see Pickers and menus on a phone); inline validation comes later.
|
|
207
|
+
|
|
208
|
+
## Toast, notification and hover-only controls
|
|
209
|
+
|
|
210
|
+
`usePhone()` (`src/composables/phone.ts`) is the phone signal in script: the
|
|
211
|
+
enclosing AppRoot is narrow and the pointer is coarse, the same test as the
|
|
212
|
+
phone tokens. A provider sets `data-ui-phone` from it, and the placement rules
|
|
213
|
+
are in `src/feedback/feedback.css`.
|
|
214
|
+
|
|
215
|
+
| | Desktop, tablet, narrow window with a mouse | Phone |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| Toast | bottom right, 360px, swipe right, a stack | bottom, 8px from each side, above the visible tab bar or the keyboard (`--ui-kb-inset`), whichever is higher, plus `--ui-safe-bottom` and 8px; swipe down; only the newest shows, with a `+N` count of the others still open |
|
|
218
|
+
| Notification | the chosen corner, 360px | top, 8px from each side, below `--ui-safe-top` plus 8px; `alignment` is ignored |
|
|
219
|
+
|
|
220
|
+
- Toasts behind the front one keep their own timers, so a short one can
|
|
221
|
+
expire unseen. Danger toasts stay until closed and come forward in turn.
|
|
222
|
+
- Toast and notification text reads `--ui-font-size` and
|
|
223
|
+
`--ui-font-size-sm` (13/12 on a desktop, 15/13 on a phone). A static
|
|
224
|
+
Notification card no longer has a 320px minimum width.
|
|
225
|
+
- Neither provider writes `100vw` any more.
|
|
226
|
+
|
|
227
|
+
**Hover-only controls.** VirtualCardGrid card actions, the ScheduleTable edit
|
|
228
|
+
pen and the TypoHeader anchor carry `[@media(hover:none)]:opacity-100`, so a
|
|
229
|
+
touch screen shows them at rest. A component that hides a control until hover
|
|
230
|
+
does the same.
|
|
231
|
+
|
|
232
|
+
**Card long press.** On touch, holding a VirtualCardGrid card for 250ms opens
|
|
233
|
+
the card's context menu (`menuItems`) through Reka's long press
|
|
234
|
+
(`ContextMenu` `pressOpenDelay`). The click that ends that press doesn't open
|
|
235
|
+
the card. A card without entries opens no menu. The menu is the plain
|
|
236
|
+
context menu for now; it becomes a phone sheet once pickers move to sheets.
|
|
237
|
+
|
|
238
|
+
## Forms and inputs
|
|
239
|
+
|
|
240
|
+
The keyboard depends on each field's type, so these are component defaults,
|
|
241
|
+
not tokens.
|
|
242
|
+
|
|
243
|
+
### Keyboard hints by type
|
|
244
|
+
|
|
245
|
+
`Input` sets these from its `type`, and `SearchField` from `search`. An
|
|
246
|
+
attribute of the same name on the component overrides each one.
|
|
247
|
+
|
|
248
|
+
| `type` | Native type | `inputmode` | Other hints |
|
|
249
|
+
|---|---|---|---|
|
|
250
|
+
| `email` | email | email | `autocapitalize=off spellcheck=false` |
|
|
251
|
+
| `tel` | tel | tel | – |
|
|
252
|
+
| `url` | url | url | `autocapitalize=off` |
|
|
253
|
+
| `integer` (new) | text | numeric | – |
|
|
254
|
+
| `decimal` (new) | text | decimal | – |
|
|
255
|
+
| `password` | password | – | `autocapitalize=off spellcheck=false` (still right once revealed) |
|
|
256
|
+
| `search`, `SearchField` | search | – | `enterkeyhint=search` |
|
|
257
|
+
| `text` and others | as given | – | – |
|
|
258
|
+
|
|
259
|
+
Use `integer` or `decimal` for numbers typed on a phone. They open the number
|
|
260
|
+
keyboard and skip the native number field's spinner and value parsing.
|
|
261
|
+
`type="number"` stays the native field. `Input` and `SearchField` read
|
|
262
|
+
`--ui-font-size` (15px on a phone), and every field is 16px on a coarse
|
|
263
|
+
pointer (see Tokens and base guards). The clear button (`data-ui-hit`) and the password toggle get the
|
|
264
|
+
44px tap box.
|
|
265
|
+
|
|
266
|
+
### Next and Done
|
|
267
|
+
|
|
268
|
+
Inside a `Form`, each `Input` that is the primary control of a `FormItem`
|
|
269
|
+
joins the form's field order, which is document order, taken when the field
|
|
270
|
+
mounts. Disabled and read-only fields are skipped. The last field gets
|
|
271
|
+
`enterkeyhint=done` and the others get `next`. On a coarse pointer, Enter on a
|
|
272
|
+
`next` field moves focus to the next field with `focusWithoutScroll`, so the
|
|
273
|
+
page never jumps. It does not submit the form. With a mouse, Enter keeps the
|
|
274
|
+
browser's behaviour. A caller's own `enterkeyhint` turns both off for that
|
|
275
|
+
field. A `SearchField` keeps `search` and is not part of the order. Other
|
|
276
|
+
controls (`InputSteppers`, `Textarea`) are not in the order yet.
|
|
277
|
+
|
|
278
|
+
`useFormControl({ keyboard: { type, element, active } })` is how a text
|
|
279
|
+
control opts in.
|
|
280
|
+
|
|
281
|
+
### Phone form layout
|
|
282
|
+
|
|
283
|
+
- `Form` with `spacing="auto"` puts 16px between items on a phone. Below
|
|
284
|
+
400px it already stacks labels over fields.
|
|
285
|
+
- `FormActions` is sticky by default on a phone (narrow and coarse,
|
|
286
|
+
`usePhone()`, exported). Elsewhere it stays off unless you set `sticky`, and
|
|
287
|
+
`:sticky="false"` turns it off on a phone too. A sticky bar sits at
|
|
288
|
+
`bottom: var(--ui-kb-inset, 0px)`, so it rides above the iOS keyboard, and
|
|
289
|
+
it sets `--ui-scroll-footer` on its scroller so a revealed field clears it.
|
|
290
|
+
On a phone its bottom padding is `max(12px, safe-bottom)`, or 8px while the
|
|
291
|
+
keyboard is open.
|
|
292
|
+
- On a phone, `FormActions` buttons share the row equally, and a single
|
|
293
|
+
button is full width. Put the primary button last so it sits on the right.
|
|
294
|
+
|
|
295
|
+
## BottomSheet
|
|
296
|
+
|
|
297
|
+
`BottomSheet` is the one bottom sheet, built on Reka's `Drawer` (reka-ui
|
|
298
|
+
2.11.0). It has a 36×4 grabber in a 20px zone, a 48px header with the title
|
|
299
|
+
and a 44px close button, a body that scrolls with `overscroll-behavior:
|
|
300
|
+
contain`, a pinned footer and safe-area padding. The top corners use
|
|
301
|
+
`--ui-radius-sheet`.
|
|
302
|
+
|
|
303
|
+
- `height`: `auto` (content, up to 92%), `large` (92%; the strip of page left
|
|
304
|
+
above it closes the sheet when tapped), `full` (the whole screen, below the
|
|
305
|
+
top safe area), or a CSS height capped at 92%. `snap-points` passes through
|
|
306
|
+
to the Drawer.
|
|
307
|
+
- A drag follows the finger. Letting go past 30% of the sheet or faster than
|
|
308
|
+
0.5 px/ms closes it; otherwise it springs back over
|
|
309
|
+
`--ui-motion-spring-back` (180ms). The two thresholds are hash-checked
|
|
310
|
+
patch hunks on Reka (`build/reka-patches.json`); Reka's own defaults are
|
|
311
|
+
40px and 0.3 px/ms. A drag that starts in the body moves the sheet only when
|
|
312
|
+
the body is scrolled to the top.
|
|
313
|
+
- The Drawer's keyboard provider publishes `--drawer-keyboard-inset` on the
|
|
314
|
+
viewport. The viewport pads its bottom by it, so the sheet and its footer
|
|
315
|
+
sit above the keyboard and the heights shrink instead of the sheet moving
|
|
316
|
+
off the screen.
|
|
317
|
+
- In 280ms on `--ui-motion-sheet-ease`, out 200ms, the scrim
|
|
318
|
+
(`--ui-drawer-backdrop`) fading with it. Reduced motion uses an 80ms fade.
|
|
319
|
+
- `confirm-close` (set while a form is dirty) asks "Discard changes?" in a
|
|
320
|
+
nested sheet before a swipe, Back, Escape, an outside press or the close
|
|
321
|
+
button closes it. Nested sheets stack, and each closes only itself.
|
|
322
|
+
- On a phone, Back closes the top sheet (`useBackDismiss`); `back-dismiss`
|
|
323
|
+
overrides that.
|
|
324
|
+
|
|
325
|
+
`Dialog` on a phone (an app 640px or narrower) renders as a `BottomSheet`:
|
|
326
|
+
`auto`, or `full` for `fullscreen` and for a form with more than 3 fields. The
|
|
327
|
+
CSS-only dialog-as-sheet in `adaptive.css` is gone. `Sheet side="bottom"` is
|
|
328
|
+
a `BottomSheet` everywhere; without `size` it fits its content. Left and right
|
|
329
|
+
sheets are unchanged, and their footer keeps clear of the home indicator.
|
|
330
|
+
an input or swatch. On a phone, pickers and long menus open as bottom sheets instead
|
|
331
|
+
(see Pickers and menus on a phone); inline validation comes later.
|
|
332
|
+
|
|
333
|
+
## Push navigation
|
|
334
|
+
|
|
335
|
+
On a phone a detail is a page pushed over the page it came from, not a split
|
|
336
|
+
view. `PushTransition` draws one pushed page:
|
|
337
|
+
|
|
338
|
+
- It slides in from the right over `--ui-motion-push` (280ms) and pops back
|
|
339
|
+
over `--ui-motion-pop` (240ms), both on `--ui-motion-sheet-ease`. The page
|
|
340
|
+
under it (`under`) moves 30% left, dims 10% and is `inert` while covered.
|
|
341
|
+
- Its 48px back header (with `title` or the `title` slot) has Back at the
|
|
342
|
+
start, the title in the middle and a `header-actions` slot at the end.
|
|
343
|
+
- Back, the browser's Back (Android back, the iOS edge swipe, through
|
|
344
|
+
`useBackDismiss`) and a swipe that starts within 20px of the page's left
|
|
345
|
+
edge pop it (`pop`). A swipe let go past 35% of the width or faster than
|
|
346
|
+
0.5px/ms pops; a shorter one springs back over `--ui-motion-spring-back`.
|
|
347
|
+
- Reduced motion or `data-ui-motion="off"`: no slide or parallax, a fade that
|
|
348
|
+
the zero durations make instant.
|
|
349
|
+
- With `enabled` off (a split view, a desktop) it shows and hides at once and
|
|
350
|
+
leaves the page under it alone.
|
|
351
|
+
- The covered page leaves the flow (absolute, clipped to the pushed page) and
|
|
352
|
+
the pushed page stays in flow, so a container with no height takes the
|
|
353
|
+
pushed page's height. The container is `position: relative` with
|
|
354
|
+
`overflow-x: clip`, so the page off to the right never widens the document.
|
|
355
|
+
|
|
356
|
+
Where it is used:
|
|
357
|
+
|
|
358
|
+
| Component | Phone (640px or less) | Wider |
|
|
359
|
+
|---|---|---|
|
|
360
|
+
| `ListDetail` | the list fills the room; the detail is pushed over it, with Back in place of Close | the split view, unchanged |
|
|
361
|
+
| `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 |
|
|
362
|
+
| `SectionTabs`, four or fewer sections | a full-width SegmentControl with equal segments | the strip |
|
|
363
|
+
| `SectionTabs` with `phone="strip"` | the scrolling strip, for tabs inside a pushed page or a sheet | the strip |
|
|
364
|
+
|
|
365
|
+
`phone` (`auto`, `list`, `segment`, `strip`) overrides the choice. A phone
|
|
366
|
+
is the App's presentation (`mobile`), set by AppRoot or AppLayout. The
|
|
367
|
+
`#actions` slot sits above the section list. A `SettingsPage` inside a pushed
|
|
368
|
+
section keeps its sticky footer.
|
|
369
|
+
|
|
370
|
+
## RecordPanel and AsidePanel on the BottomSheet
|
|
371
|
+
|
|
372
|
+
Both panels use `BottomSheet` on a phone, so they share its grabber, drag,
|
|
373
|
+
Back, keyboard inset, scrim and motion. Neither has swipe code of its own.
|
|
374
|
+
|
|
375
|
+
- `RecordPanel` compact (a host 640px or narrower, or too narrow for a 320px
|
|
376
|
+
panel beside 240px of view) is a `large` sheet (92%). The record's title
|
|
377
|
+
names it, and the `header` slot sits next to the 44px close button.
|
|
378
|
+
`has-previous` and `has-next` add a footer with full-width Previous record
|
|
379
|
+
and Next record buttons (`@previous`, `@next`). The footer stays above the
|
|
380
|
+
keyboard and the home indicator. `secondary-open` pushes the comments over
|
|
381
|
+
the body with `PushTransition`, titled by `secondary-label`. Its Back (the
|
|
382
|
+
button, the edge swipe or the browser's) closes that page before the sheet
|
|
383
|
+
and emits `update:secondary-open`. Close reasons are `escape`,
|
|
384
|
+
`button`, `outside` and `swipe` (a drag or Back).
|
|
385
|
+
- `AsidePanel` on a phone is a `large` sheet with `snap-points` `[0.5, 0.92]`.
|
|
386
|
+
`expanded` makes it `full`. Its expand button is 44px. The phone button
|
|
387
|
+
hides while it is open and while a record sheet is open.
|
|
388
|
+
- Neither panel remounts the caller's content. The header, body, thread and
|
|
389
|
+
composer render once and move between the docked panel, the sheet and a
|
|
390
|
+
hidden holder. A switch between docked and sheet (a rotation or a resize)
|
|
391
|
+
keeps fields, focus and caret. A collapsed assistant keeps its draft and
|
|
392
|
+
thread scroll.
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
## Phone shell
|
|
396
|
+
|
|
397
|
+
On a phone (`AppLayout` 640px or narrower) the layout draws one bar above
|
|
398
|
+
the page, `--ui-chrome-height` tall (48px on a phone, 44px in a narrow window
|
|
399
|
+
driven by a mouse):
|
|
400
|
+
|
|
401
|
+
- **Leading:** the menu button, or a Back button when `back` is set (it
|
|
402
|
+
emits `back`; the app pops its own route). While an `AppTabBar` More tab
|
|
403
|
+
opens the drawer, the menu button steps aside.
|
|
404
|
+
- **Title:** the `title` prop, else the title of the page's `PageHeader`
|
|
405
|
+
(the last one mounted in the layout's content column), else the brand. 17px
|
|
406
|
+
semibold, one line, cut with an ellipsis; centred while nothing follows it,
|
|
407
|
+
start-aligned beside actions. A `PageHeader` whose title moves into the bar
|
|
408
|
+
keeps its heading for screen readers only, and takes no room when it has
|
|
409
|
+
nothing else to show (no description, meta or actions). There is no large
|
|
410
|
+
collapsing title.
|
|
411
|
+
- **Actions:** `headerActions` (DropdownMenu entries). The first plain entry
|
|
412
|
+
is one 44px button, its icon (the label is its accessible name) or else its
|
|
413
|
+
label; the rest open from "…". A pick emits `headerAction` with the key.
|
|
414
|
+
|
|
415
|
+
Every bar button is a 44px target.
|
|
416
|
+
|
|
417
|
+
The navigation drawer is Reka's `Drawer` with `swipe-direction="left"`,
|
|
418
|
+
portalled like `BottomSheet` (into the AppRoot in app scope). It is 85% of
|
|
419
|
+
the screen, up to 320px, and mounted only while open. A drag left follows the
|
|
420
|
+
finger and closes past 30% or above 0.5 px/ms, otherwise it springs back; it
|
|
421
|
+
moves in 240ms and out 180ms (`--ui-motion-drawer-in/-out`) with the scrim
|
|
422
|
+
fading, and reduced motion uses an 80ms fade. Escape, a press on the scrim, a
|
|
423
|
+
link in it and Back (`useBackDismiss`) close it; focus goes to its close
|
|
424
|
+
button and returns to what opened it. The page keeps its width.
|
|
425
|
+
|
|
426
|
+
`AppTabBar` hides while the keyboard is open (`useSoftKeyboard`, confirmed by
|
|
427
|
+
the visual viewport), pads by `--ui-safe-bottom`, and shows at most five
|
|
428
|
+
slots: with more tabs, the fifth is More, which opens a menu of the rest (and
|
|
429
|
+
the drawer, inside an AppLayout with a sidebar). More is marked current when
|
|
430
|
+
the current tab is in it.
|
|
431
|
+
|
|
432
|
+
## Pickers and menus on a phone
|
|
433
|
+
|
|
434
|
+
In an app 640px wide or narrower (the same signal that turns a Dialog into a
|
|
435
|
+
BottomSheet), a popup that holds a list, a search, a form, more than five
|
|
436
|
+
actions or a submenu opens as a `BottomSheet` instead of a popover. A touch
|
|
437
|
+
tablet and a desktop window keep popovers. One composable makes the choice
|
|
438
|
+
for every picker: `usePhoneSurface(content)` returns `popover` or `sheet`,
|
|
439
|
+
and `menuContent(items)` counts a menu's first-level actions (separators do
|
|
440
|
+
not count) and finds submenus. Each component keeps one open state, and only
|
|
441
|
+
one surface shows it.
|
|
442
|
+
|
|
443
|
+
| Component | Phone sheet |
|
|
444
|
+
|---|---|
|
|
445
|
+
| Select, Combobox, MultipleSelect, SuperboxSelect | Titled with the field's label; 48px rows; a search pinned above the list (when the field searches or creates) that is not focused on open. From the first search until the sheet closes it keeps 60% of the screen, so the sheet does not jump as results shrink. The field itself is read-only, so a tap opens the sheet instead of the keyboard. A multiple select toggles rows and closes on Done; a free label is added from an `Add "…"` row or Enter |
|
|
446
|
+
| DatePicker | The calendar with 44px days and 16px time selects, and Done in the footer. A date-only pick still closes it |
|
|
447
|
+
| DropdownMenu, ContextMenu | More than five actions, or any submenu: an action sheet with 48px rows, danger rows in red and a Cancel row. A submenu pushes inside the sheet with a back row. Checkbox rows toggle in place; any other pick closes the sheet. Five actions or fewer stay a popover |
|
|
448
|
+
| ColorPicker | The palette with 32px swatches and 44px Reset and Custom buttons |
|
|
449
|
+
|
|
450
|
+
Escape, a swipe down and Back each close only the top sheet, and an Escape
|
|
451
|
+
that ends an IME composition in the search closes nothing (the BottomSheet's
|
|
452
|
+
guard). `AdaptivePanel` already follows this rule for its own content: a form
|
|
453
|
+
panel, a sheet once its host is 640px or narrower.
|
|
454
|
+
|
|
455
|
+
## Pagination and the older tables
|
|
456
|
+
|
|
457
|
+
These key on the narrow signal (an app 640px or narrower, `usePhonePresentation`),
|
|
458
|
+
because they change layout, not size. Sizes still come from the tokens, so a
|
|
459
|
+
narrow window used with a mouse keeps desktop-sized controls.
|
|
460
|
+
|
|
461
|
+
**Pagination.** The pager becomes `‹ Page 1 of N ›`: previous and next are
|
|
462
|
+
`--ui-control-md` icon buttons (44px on a phone), the total text sits on the
|
|
463
|
+
left, and there are no first, last or page-number buttons. Tapping the label
|
|
464
|
+
opens a `BottomSheet` with a page field (`type="integer"`, `enterkeyhint=go`)
|
|
465
|
+
and, when `items-per-page` is set, the page sizes as 48px rows. A single page
|
|
466
|
+
without sizes shows the label as plain text. An endless pager shows
|
|
467
|
+
`‹ Page 5 ›`, and its sheet only has the sizes. Above 640px the pager is
|
|
468
|
+
unchanged.
|
|
469
|
+
|
|
470
|
+
**DataGrid and DataTable.** `mobile-view="auto"` (the default) shows a list
|
|
471
|
+
instead of the table. Each row is at least `--ui-list-row-height` (48px on a
|
|
472
|
+
phone): the title column on the first line, up to two columns joined with `·`
|
|
473
|
+
on the second, and trailing columns at the end. Columns place themselves with
|
|
474
|
+
`list`:
|
|
475
|
+
|
|
476
|
+
| `list` | Place |
|
|
477
|
+
|---|---|
|
|
478
|
+
| unset | the first column is the title, the next two the subtitle; an `actions` column trails |
|
|
479
|
+
| `title` | the first line |
|
|
480
|
+
| `subtitle` | the second line; marked columns replace the unmarked ones |
|
|
481
|
+
| `trailing` | the end of the row, e.g. a status badge |
|
|
482
|
+
| `none` | not in the list |
|
|
483
|
+
|
|
484
|
+
The same `cell-*` slots render in the list. `mobile-view="table"` keeps the
|
|
485
|
+
table; DataTable's `cards` keeps the fully labeled cards.
|
|
486
|
+
|
|
487
|
+
DataGrid only:
|
|
488
|
+
|
|
489
|
+
- **Select mode.** With `selectable`, a 500ms long press on a row selects it
|
|
490
|
+
(`onLongPress` from VueUse, cancelled by a 10px move or a scroll). Having a
|
|
491
|
+
selection is select mode: each row gets a checkbox with a 44px tap box, a tap
|
|
492
|
+
toggles the row, and a bottom bar replaces the footer with select all, the
|
|
493
|
+
count, the `selection-actions` slot (`keys`, `rows`, `clear`) and Cancel,
|
|
494
|
+
which clears the selection. Outside select mode a tap is `rowClick`.
|
|
495
|
+
- **View options.** With a sortable column or `column-chooser`, a bar above the
|
|
496
|
+
list shows the sort and a button that opens a sheet: sort by (a tap on the
|
|
497
|
+
sorted column flips it, as the header does) and the columns to show, with
|
|
498
|
+
Reset columns.
|
|
499
|
+
- **Footer.** The phone `Pagination` (one page per step) with the item count
|
|
500
|
+
and refresh.
|
|
501
|
+
- Groups and expandable rows work in the list. Row drag reorder, column
|
|
502
|
+
resizing and column reorder are table-only.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Release checks
|
|
2
|
+
|
|
3
|
+
`npm run gates` runs basic release checks. `npm run gates:full` adds browser,
|
|
4
|
+
workbench and registry consumer integrations only when explicitly requested.
|
|
5
|
+
`SPL_FULL_GATE=1` aliases `--full`. `npm run gates -- --plan` prints the exact
|
|
6
|
+
selection without running checks. `pnpm check` and `pnpm check:fast` run basic
|
|
7
|
+
checks through the optional build-isolation wrapper.
|
|
8
|
+
|
|
9
|
+
| Entry point | Default | Explicit full |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `npm run gates`, `scripts/gates.mjs` | Internal-content checks, build, lint, typecheck, unit tests, notices, isolated pack and tarball content audit | Adds Chromium installation, responsive/adaptive/date-picker/JSON/host/switch/layout/InfoTip/motion browser checks, Histoire build, exact-tarball registry consumer smoke |
|
|
12
|
+
| `pnpm check`, `check:fast` | Basic, with optional Docker host build isolation | `SPL_FULL_GATE=1` selects the same full plan |
|
|
13
|
+
| `fast-ci.yml`, composite verify action | Basic | No automatic extended checks |
|
|
14
|
+
| Disabled CI/release workflow templates | Basic, version/tag/ancestry checks, stage, pack and actual archive audit | No implicit browser or registry consumer smoke |
|
|
15
|
+
| `scripts/stage-release.mjs`, `release:stage` | Validate existing dist and copy allowlisted publication inputs | No gate invocation |
|
|
16
|
+
| `scripts/ci-release.mjs preflight/audit/dry-run` | Release metadata, archive checks or credential-free npm validation | No browser/workbench/consumer integration |
|
|
17
|
+
| `scripts/ci-release.mjs smoke`, `release:smoke` | Explicit manual consumer integration | Included in full; release:smoke requires full opt-in |
|
|
18
|
+
| `prepack`, `prepublishOnly` | No hooks defined | No hidden gate invocation |
|
|
19
|
+
|
|
20
|
+
Stage the package only after a successful build/basic check. Preserve version,
|
|
21
|
+
ancestry, immutable-publication, metadata and credential boundaries. Registry
|
|
22
|
+
availability checks needed to publish are distinct from optional consumer tests.
|
|
23
|
+
No full-gate stamp is required for publication.
|
|
24
|
+
|
|
25
|
+
The full plan includes `check:mobile` at 390×844 and 768×1024. A mobile
|
|
26
|
+
failure returns a nonzero status and blocks that plan, like the other checks.
|
|
27
|
+
The mobile allow-list records only bounded audit exceptions with reasons;
|
|
28
|
+
normal runs never add exceptions.
|
package/docs/theme-hooks.md
CHANGED
|
@@ -46,14 +46,26 @@ here stay implementation details and can change in any release.
|
|
|
46
46
|
| `data-ui-brand-action` | native button around the default brand when `AppLayout` has a `brand` listener | present |
|
|
47
47
|
| `data-ui-sidebar-body` | wrapper of the `sidebar` slot | present |
|
|
48
48
|
| `data-ui-sidebar-footer` | wrapper of the `sidebar-footer` slot | present |
|
|
49
|
-
| `data-ui-sidebar-collapse` | the web
|
|
49
|
+
| `data-ui-sidebar-collapse` | the web sidebar's collapse button, at the end of its head row | present |
|
|
50
|
+
| `data-ui-layout-aside` | the right side region holding the `aside` slot | present; `data-ui-aside-state` is `open`, `collapsed`, `expanded` |
|
|
51
|
+
| `data-ui-aside` | `AppLayout` root, when it has an `aside` slot | `open`, `collapsed`, `expanded` |
|
|
52
|
+
| `data-ui-aside-overlay` | `AppLayout` root | `true` while the open aside floats over the page |
|
|
53
|
+
| `data-ui-aside-resize` | the aside's resize handle | present |
|
|
54
|
+
| `data-ui-aside-resizing` | `AppLayout` root | `true` while the aside's handle is dragged |
|
|
55
|
+
| `data-ui-side-moving` | `AppLayout` root | `true` while the sidebar or the aside changes width |
|
|
56
|
+
| `data-ui-aside-panel-root` | `AsidePanel` root inside the aside region | `data-ui-aside-mode` is `docked`, `floating`, `sheet`; `data-ui-aside-floating` while the panel floats or slides away from floating |
|
|
57
|
+
| `data-ui-aside-panel` | the panel (`complementary`; on a phone the `BottomSheet` dialog) | `data-ui-aside-state` is `open`, `collapsed`, `expanded` |
|
|
58
|
+
| `data-ui-aside-rail` / `data-ui-aside-open` | the 48px rail and its button | present |
|
|
59
|
+
| `data-ui-aside-fab` | the phone button | present |
|
|
60
|
+
| `data-ui-aside-collapse` / `data-ui-aside-expand` / `data-ui-aside-restore` | header buttons | present |
|
|
61
|
+
| `data-ui-aside-body` / `data-ui-aside-composer` | the thread scroller and the composer | present |
|
|
50
62
|
| `data-ui-mobile-navigation` | the phone bar above the page | present |
|
|
51
63
|
| `data-ui-mobile-menu` | the phone bar's menu (hamburger) button | present |
|
|
52
64
|
| `data-ui-mobile-sidebar-header` | the drawer's header row | present |
|
|
53
65
|
| `data-ui-mobile-backdrop` | the drawer's backdrop | present |
|
|
54
66
|
| `data-ui-app-toolbar` | `AppToolbar` | present |
|
|
55
67
|
| `data-ui-page-header` | `PageHeader` | present |
|
|
56
|
-
| `data-ui-page-header-meta`, `data-ui-page-header-actions` | `PageHeader`'s meta and
|
|
68
|
+
| `data-ui-page-header-meta`, `data-ui-page-header-actions`, `data-ui-page-header-description` | `PageHeader`'s meta, actions and description | present |
|
|
57
69
|
| `data-page-surface` | `PageContent` | `auto`, `card`, `flat`, `none` |
|
|
58
70
|
| `data-ui-motion` | set by the app on any ancestor (an `AppRoot`, `<html>`) | `off`: the library's motion is instant inside it, as under reduced motion ([Motion](#motion)) |
|
|
59
71
|
|
|
@@ -95,6 +107,7 @@ here stay implementation details and can change in any release.
|
|
|
95
107
|
| `data-menu-description` | a `DropdownMenu` entry's second line | present |
|
|
96
108
|
| `data-ui-segment-count` | a `SegmentItem`'s count pill | present |
|
|
97
109
|
| `data-ui-segment-control` | `SegmentControl` | `segment`, `sheet` |
|
|
110
|
+
| `data-ui-segment-size` | `SegmentControl` with `size="sm"` | `sm` |
|
|
98
111
|
| `data-ui-segment-moving` | `SegmentControl` while its selection slides (`.ui-segment-thumb` is shown, the new item's own fill waits) | present |
|
|
99
112
|
| `data-ui-swap` | `MotionSwap` and `AppLayout`'s `main` | present |
|
|
100
113
|
| `data-ui-swapped` | the same, after the first change: plays the rise-in | present |
|
|
@@ -103,7 +116,7 @@ here stay implementation details and can change in any release.
|
|
|
103
116
|
| `data-ui-stat-tile` | `StatTile` (`data-tone`; a filter tile also `data-state` `on` / `off`) | present |
|
|
104
117
|
| `data-ui-stat-tile-label`, `-value`, `-unit`, `-note` | `StatTile` parts | present |
|
|
105
118
|
| `data-ui-stat-tile-group` | `StatTileGroup` | present |
|
|
106
|
-
| `data-ui-page-toolbar`, `-start`, `-actions` | `PageToolbar` and its two sides | present |
|
|
119
|
+
| `data-ui-page-toolbar`, `-start`, `-actions` | `PageToolbar` and its two sides | present; the start side carries `data-overflow-start` and `data-overflow-end` while it continues past an edge |
|
|
107
120
|
| `data-ui-summary-card` | `SummaryCard` (`data-state` `loading`, `error`, `ready`; `data-wide`) | present |
|
|
108
121
|
| `data-ui-summary-card-title`, `-state`, `-figures`, `-figure`, `-body`, `-note`, `-error` | `SummaryCard` parts | present |
|
|
109
122
|
| `data-ui-skeleton` | `SectionSkeleton` | `lines`, `figures`, `rows` (parts `data-ui-skeleton-line`, `-figure`, `-row`) |
|
|
@@ -188,14 +201,22 @@ does not animate its rows.
|
|
|
188
201
|
|
|
189
202
|
| Token | Value | Use |
|
|
190
203
|
| --- | --- | --- |
|
|
191
|
-
| `--ui-motion-fast` |
|
|
192
|
-
| `--ui-motion-normal` |
|
|
204
|
+
| `--ui-motion-fast` | 150ms | colour and state changes, the tab notch and segment fill sliding, menus opening, a dialog closing |
|
|
205
|
+
| `--ui-motion-normal` | 200ms | dialogs opening, a new row, a tab panel, switched content and pages, the loading mask, a card's hover shadow |
|
|
193
206
|
| `--ui-motion-slow` | 240ms | height, notifications and toasts arriving, a sheet sliding in |
|
|
207
|
+
| `--ui-motion-panel` | 300ms | a side region: the web sidebar and its rail, an aside, a record panel |
|
|
208
|
+
| `--ui-motion-exit` | 100ms | a popup, menu or tooltip closing |
|
|
209
|
+
| `--ui-motion-stack` | 200ms (`--ui-motion-stack-ease`) | a toast stack closing up |
|
|
210
|
+
| `--ui-motion-flash` | 1600ms | the tint on something changed elsewhere |
|
|
211
|
+
| `--ui-skeleton-pulse` | 1200ms (`--ui-skeleton-pulse-ease`) | one skeleton pulse |
|
|
212
|
+
| `--ui-motion-drag` | 200ms, `cubic-bezier(0.2, 0, 0, 1)` (`--ui-motion-drag-ease`) | siblings making room while dragging |
|
|
213
|
+
| `--ui-motion-drag-settle` | 260ms, `cubic-bezier(0.2, 1, 0.1, 1)` (`--ui-motion-drag-settle-ease`) | a dropped item settling |
|
|
214
|
+
| `--ui-motion-ease` | `cubic-bezier(0.4, 0, 0.2, 1)` | colour and hover fades |
|
|
194
215
|
| `--ui-motion-ease-out` | `cubic-bezier(0.22, 1, 0.36, 1)` | arriving |
|
|
195
216
|
| `--ui-motion-ease-in` | `cubic-bezier(0.4, 0, 1, 1)` | leaving |
|
|
196
|
-
| `--ui-motion-ease-in-out` | `cubic-bezier(0.4, 0, 0.2, 1)` | height between two resting states |
|
|
217
|
+
| `--ui-motion-ease-in-out` | `cubic-bezier(0.4, 0, 0.2, 1)` | height or width between two resting states |
|
|
197
218
|
|
|
198
|
-
Under `prefers-reduced-motion: reduce` all of it is instant. To turn it off
|
|
219
|
+
Under `prefers-reduced-motion: reduce` all of it is instant, and every duration token reads 0ms, so script that reads them waits for nothing either. To turn it off
|
|
199
220
|
for part of a page, or the whole App, set `data-ui-motion="off"` on any
|
|
200
221
|
ancestor:
|
|
201
222
|
|