@bitforgehq/ui 0.3.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 +3665 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/ai/MANIFEST.index.json +383 -0
- package/ai/MANIFEST.json +7528 -0
- package/ai/REFERENCE.index.json +1461 -0
- package/ai/REFERENCE.json +7213 -0
- package/ai/UI_GUIDE.md +1159 -0
- package/ai/components/action-bar.md +209 -0
- package/ai/components/action-sheet.md +526 -0
- package/ai/components/alert-dialog.md +509 -0
- package/ai/components/alert.md +376 -0
- package/ai/components/avatar.md +146 -0
- package/ai/components/back-button.md +286 -0
- package/ai/components/badge.md +171 -0
- package/ai/components/button.md +431 -0
- package/ai/components/card.md +303 -0
- package/ai/components/checkbox.md +418 -0
- package/ai/components/chip-group.md +205 -0
- package/ai/components/chip.md +300 -0
- package/ai/components/content.md +474 -0
- package/ai/components/desktop-toast.md +420 -0
- package/ai/components/dialog.md +962 -0
- package/ai/components/empty-state.md +146 -0
- package/ai/components/field.md +319 -0
- package/ai/components/header.md +809 -0
- package/ai/components/icon-button.md +221 -0
- package/ai/components/icon.md +176 -0
- package/ai/components/input.md +455 -0
- package/ai/components/list-item.md +326 -0
- package/ai/components/list.md +178 -0
- package/ai/components/menu-button.md +377 -0
- package/ai/components/menu.md +661 -0
- package/ai/components/modal.md +613 -0
- package/ai/components/number-input.md +423 -0
- package/ai/components/page.md +299 -0
- package/ai/components/progress.md +215 -0
- package/ai/components/radio-group.md +432 -0
- package/ai/components/refresher.md +377 -0
- package/ai/components/segmented.md +517 -0
- package/ai/components/select.md +510 -0
- package/ai/components/sheet.md +845 -0
- package/ai/components/spinner.md +171 -0
- package/ai/components/stack-outlet.md +661 -0
- package/ai/components/tab-bar.md +427 -0
- package/ai/components/tab.md +295 -0
- package/ai/components/tabs.md +408 -0
- package/ai/components/textarea.md +371 -0
- package/ai/components/toast.md +522 -0
- package/ai/components/toggle.md +387 -0
- package/ai/reference/bf-action-directive.md +21 -0
- package/ai/reference/bf-action-sheet-action-role.md +27 -0
- package/ai/reference/bf-action-sheet-action.md +28 -0
- package/ai/reference/bf-action-sheet-options.md +28 -0
- package/ai/reference/bf-action-sheet-service.md +33 -0
- package/ai/reference/bf-alert-dialog-button-role.md +27 -0
- package/ai/reference/bf-alert-dialog-button.md +27 -0
- package/ai/reference/bf-alert-dialog-options.md +29 -0
- package/ai/reference/bf-alert-dialog-service.md +34 -0
- package/ai/reference/bf-alert-variant.md +28 -0
- package/ai/reference/bf-autocapitalize.md +30 -0
- package/ai/reference/bf-avatar-size.md +27 -0
- package/ai/reference/bf-avatar-variant.md +28 -0
- package/ai/reference/bf-back-at-root.md +28 -0
- package/ai/reference/bf-back-button-service.md +40 -0
- package/ai/reference/bf-badge-variant.md +29 -0
- package/ai/reference/bf-button-size.md +29 -0
- package/ai/reference/bf-button-type.md +28 -0
- package/ai/reference/bf-button-variant.md +29 -0
- package/ai/reference/bf-card-padding.md +26 -0
- package/ai/reference/bf-card-variant.md +28 -0
- package/ai/reference/bf-chip-group-mode.md +27 -0
- package/ai/reference/bf-chip-variant.md +26 -0
- package/ai/reference/bf-compare-with.md +24 -0
- package/ai/reference/bf-component-overlay-ref.md +33 -0
- package/ai/reference/bf-confirm-button-role.md +26 -0
- package/ai/reference/bf-confirm-button.md +27 -0
- package/ai/reference/bf-confirm-options.md +31 -0
- package/ai/reference/bf-confirm-size.md +26 -0
- package/ai/reference/bf-content-scroll-event.md +21 -0
- package/ai/reference/bf-defaults-options.md +32 -0
- package/ai/reference/bf-defaults.md +28 -0
- package/ai/reference/bf-desktop-toast-config.md +28 -0
- package/ai/reference/bf-desktop-toast-harness.md +33 -0
- package/ai/reference/bf-desktop-toast-options.md +28 -0
- package/ai/reference/bf-desktop-toast-position.md +28 -0
- package/ai/reference/bf-desktop-toast-service.md +42 -0
- package/ai/reference/bf-dialog-config.md +44 -0
- package/ai/reference/bf-dialog-harness.md +52 -0
- package/ai/reference/bf-dialog-ref.md +44 -0
- package/ai/reference/bf-dialog-role.md +26 -0
- package/ai/reference/bf-dialog-service.md +55 -0
- package/ai/reference/bf-dialog-size.md +31 -0
- package/ai/reference/bf-dialog-template-context.md +32 -0
- package/ai/reference/bf-empty-state-heading-level.md +26 -0
- package/ai/reference/bf-enter-key-hint.md +31 -0
- package/ai/reference/bf-field-control-class.md +32 -0
- package/ai/reference/bf-field-control-state.md +28 -0
- package/ai/reference/bf-field-size.md +29 -0
- package/ai/reference/bf-haptics-impact-style.md +27 -0
- package/ai/reference/bf-haptics-notification-type.md +27 -0
- package/ai/reference/bf-haptics-service.md +41 -0
- package/ai/reference/bf-harness-filters.md +29 -0
- package/ai/reference/bf-horizontal-scroll-directive.md +129 -0
- package/ai/reference/bf-icon-button-variant.md +28 -0
- package/ai/reference/bf-icon-size.md +27 -0
- package/ai/reference/bf-input-autocapitalize.md +36 -0
- package/ai/reference/bf-input-enter-key-hint.md +38 -0
- package/ai/reference/bf-input-input-mode.md +38 -0
- package/ai/reference/bf-input-mode.md +32 -0
- package/ai/reference/bf-input-type.md +35 -0
- package/ai/reference/bf-jsdom-shim-options.md +22 -0
- package/ai/reference/bf-keyboard-assist-behavior.md +26 -0
- package/ai/reference/bf-keyboard-assist-directive.md +185 -0
- package/ai/reference/bf-keyboard-phase.md +30 -0
- package/ai/reference/bf-keyboard-service.md +50 -0
- package/ai/reference/bf-labels-overrides.md +26 -0
- package/ai/reference/bf-labels.md +44 -0
- package/ai/reference/bf-menu-button-harness.md +40 -0
- package/ai/reference/bf-menu-button-variant.md +27 -0
- package/ai/reference/bf-menu-divider-directive.md +13 -0
- package/ai/reference/bf-menu-group-directive.md +14 -0
- package/ai/reference/bf-menu-harness.md +55 -0
- package/ai/reference/bf-menu-item-checkbox-directive.md +31 -0
- package/ai/reference/bf-menu-item-directive.md +31 -0
- package/ai/reference/bf-menu-item-harness.md +52 -0
- package/ai/reference/bf-menu-item-radio-directive.md +29 -0
- package/ai/reference/bf-menu-placement.md +29 -0
- package/ai/reference/bf-menu-trigger-directive.md +46 -0
- package/ai/reference/bf-modal-options.md +30 -0
- package/ai/reference/bf-modal-presentation.md +26 -0
- package/ai/reference/bf-modal-service.md +38 -0
- package/ai/reference/bf-nav-controller.md +59 -0
- package/ai/reference/bf-nav-direction.md +23 -0
- package/ai/reference/bf-nav-target.md +21 -0
- package/ai/reference/bf-number-input-mode.md +28 -0
- package/ai/reference/bf-number-input-step.md +25 -0
- package/ai/reference/bf-overlay-auto-focus.md +29 -0
- package/ai/reference/bf-overlay-controller.md +22 -0
- package/ai/reference/bf-overlay-host.md +36 -0
- package/ai/reference/bf-overlay-options.md +53 -0
- package/ai/reference/bf-overlay-position.md +30 -0
- package/ai/reference/bf-overlay-ref.md +53 -0
- package/ai/reference/bf-overlay-role.md +27 -0
- package/ai/reference/bf-overlay-service.md +53 -0
- package/ai/reference/bf-overlay-stack-options.md +24 -0
- package/ai/reference/bf-overlay-stack.md +81 -0
- package/ai/reference/bf-overlay-template-context.md +24 -0
- package/ai/reference/bf-overscroll-stretch-directive.md +163 -0
- package/ai/reference/bf-page-context.md +24 -0
- package/ai/reference/bf-platform-service.md +53 -0
- package/ai/reference/bf-platform.md +28 -0
- package/ai/reference/bf-progress-size.md +26 -0
- package/ai/reference/bf-progress-tone.md +28 -0
- package/ai/reference/bf-radio-group-harness.md +49 -0
- package/ai/reference/bf-radio-group-orientation.md +26 -0
- package/ai/reference/bf-radio-option.md +30 -0
- package/ai/reference/bf-refresher-refresh-event.md +22 -0
- package/ai/reference/bf-segment-option.md +29 -0
- package/ai/reference/bf-segmented-harness.md +48 -0
- package/ai/reference/bf-segmented-size.md +29 -0
- package/ai/reference/bf-select-harness.md +70 -0
- package/ai/reference/bf-select-option.md +29 -0
- package/ai/reference/bf-sheet-auto-focus.md +29 -0
- package/ai/reference/bf-sheet-options.md +32 -0
- package/ai/reference/bf-sheet-ref.md +45 -0
- package/ai/reference/bf-sheet-service.md +34 -0
- package/ai/reference/bf-shell-config-mode.md +28 -0
- package/ai/reference/bf-shell-config.md +38 -0
- package/ai/reference/bf-shell-dynamic-type-limit.md +21 -0
- package/ai/reference/bf-shell-dynamic-type.md +25 -0
- package/ai/reference/bf-shell-mode.md +28 -0
- package/ai/reference/bf-shell-testing-options.md +26 -0
- package/ai/reference/bf-size.md +45 -0
- package/ai/reference/bf-spinner-size.md +27 -0
- package/ai/reference/bf-stack-change.md +22 -0
- package/ai/reference/bf-stack-reuse-strategy.md +32 -0
- package/ai/reference/bf-stack-transition-event.md +26 -0
- package/ai/reference/bf-stack-transition.md +26 -0
- package/ai/reference/bf-stacked-overlay.md +23 -0
- package/ai/reference/bf-system-bars-style.md +38 -0
- package/ai/reference/bf-tab-bar-controller.md +51 -0
- package/ai/reference/bf-tab-bar-host.md +52 -0
- package/ai/reference/bf-tab-context.md +32 -0
- package/ai/reference/bf-tab-links.md +37 -0
- package/ai/reference/bf-testing-options.md +27 -0
- package/ai/reference/bf-text-scale-service.md +35 -0
- package/ai/reference/bf-theme.md +27 -0
- package/ai/reference/bf-toast-action.md +25 -0
- package/ai/reference/bf-toast-controller.md +17 -0
- package/ai/reference/bf-toast-dismiss-reason.md +29 -0
- package/ai/reference/bf-toast-options.md +29 -0
- package/ai/reference/bf-toast-position.md +26 -0
- package/ai/reference/bf-toast-ref.md +41 -0
- package/ai/reference/bf-toast-service.md +39 -0
- package/ai/reference/bf-toggle-label-position.md +26 -0
- package/ai/reference/bf-transition-kind.md +28 -0
- package/ai/reference/bf-validation-message.md +31 -0
- package/ai/reference/bf-validation-messages.md +33 -0
- package/ai/reference/bf-view-event.md +28 -0
- package/ai/reference/bf-view-lifecycle.md +41 -0
- package/ai/reference/bf-view-state.md +28 -0
- package/ai/reference/bf_default_labels.md +21 -0
- package/ai/reference/bf_default_values.md +21 -0
- package/ai/reference/bf_defaults.md +21 -0
- package/ai/reference/bf_desktop_toast_defaults.md +21 -0
- package/ai/reference/bf_dialog_data.md +17 -0
- package/ai/reference/bf_labels.md +21 -0
- package/ai/reference/bf_overlay_data.md +19 -0
- package/ai/reference/bf_overlay_host.md +21 -0
- package/ai/reference/bf_sheet_default_snap_points.md +17 -0
- package/ai/reference/bf_shell_config.md +21 -0
- package/ai/reference/bf_shell_defaults.md +21 -0
- package/ai/reference/bf_tab_bar.md +21 -0
- package/ai/reference/bf_tab_bar_controller.md +21 -0
- package/ai/reference/bf_tab_context.md +21 -0
- package/ai/reference/bf_tab_links.md +21 -0
- package/ai/reference/bf_theme_storage.md +19 -0
- package/ai/reference/create-transition.md +36 -0
- package/ai/reference/duration_fast.md +17 -0
- package/ai/reference/duration_md.md +17 -0
- package/ai/reference/duration_overlay.md +17 -0
- package/ai/reference/duration_push.md +17 -0
- package/ai/reference/fade-down.md +26 -0
- package/ai/reference/fade-in.md +25 -0
- package/ai/reference/fade-out.md +25 -0
- package/ai/reference/fade-up.md +26 -0
- package/ai/reference/inject-shell-mode.md +23 -0
- package/ai/reference/install-bf-jsdom-shims.md +31 -0
- package/ai/reference/ios_duration.md +17 -0
- package/ai/reference/ios_easing.md +17 -0
- package/ai/reference/is-text-entry.md +25 -0
- package/ai/reference/max_complete_ms.md +17 -0
- package/ai/reference/md-transition.md +31 -0
- package/ai/reference/md_duration.md +17 -0
- package/ai/reference/md_easing.md +17 -0
- package/ai/reference/overlay_duration.md +21 -0
- package/ai/reference/overlay_easing.md +19 -0
- package/ai/reference/overlay_reduced_duration.md +17 -0
- package/ai/reference/pop-frames-at.md +25 -0
- package/ai/reference/prefers-reduced-motion.md +25 -0
- package/ai/reference/provide-bf-defaults.md +33 -0
- package/ai/reference/provide-bf-desktop-toast.md +28 -0
- package/ai/reference/provide-bf-labels.md +38 -0
- package/ai/reference/provide-bf-shell-testing.md +34 -0
- package/ai/reference/provide-bf-shell.md +41 -0
- package/ai/reference/provide-bf-testing.md +33 -0
- package/ai/reference/reduced-transition.md +28 -0
- package/ai/reference/reduced_duration.md +17 -0
- package/ai/reference/resolve-shell-mode.md +33 -0
- package/ai/reference/scale-in.md +25 -0
- package/ai/reference/settle-duration.md +25 -0
- package/ai/reference/settle-pop.md +33 -0
- package/ai/reference/settle_easing.md +17 -0
- package/ai/reference/should-complete-pop.md +26 -0
- package/ai/reference/slide-down.md +26 -0
- package/ai/reference/slide-up.md +26 -0
- package/ai/reference/with-bf-shell-routing.md +19 -0
- package/bin/bf-ui.js +81 -0
- package/fesm2022/bitforgehq-ui-icons.mjs +230 -0
- package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs +5530 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs +680 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs +2675 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-testing.mjs +402 -0
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui.mjs +7765 -0
- package/fesm2022/bitforgehq-ui.mjs.map +1 -0
- package/llms-full.txt +25394 -0
- package/llms.txt +329 -0
- package/package.json +94 -0
- package/theme.css +253 -0
- package/types/bitforgehq-ui-icons.d.ts +50 -0
- package/types/bitforgehq-ui-mobile.d.ts +2914 -0
- package/types/bitforgehq-ui-number-input.d.ts +282 -0
- package/types/bitforgehq-ui-overlay.d.ts +1514 -0
- package/types/bitforgehq-ui-testing.d.ts +495 -0
- package/types/bitforgehq-ui.d.ts +3499 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# bf-content
|
|
2
|
+
|
|
3
|
+
Page's single scroll container that keeps focused fields clear of the soft keyboard
|
|
4
|
+
|
|
5
|
+
`BfContentComponent` from `@bitforgehq/ui/mobile` · Navigation · since 0.2.0
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, signal, viewChild } from '@angular/core';
|
|
11
|
+
import { BfButtonComponent, BfListComponent, BfListItemComponent } from '@bitforgehq/ui';
|
|
12
|
+
import { BfContentComponent, BfHeaderComponent, BfPageComponent } from '@bitforgehq/ui/mobile';
|
|
13
|
+
|
|
14
|
+
/** `bf-content` showcase: a long scroller reporting its offset, with scroll-to-top buttons. */
|
|
15
|
+
@Component({
|
|
16
|
+
selector: 'app-content-showcase',
|
|
17
|
+
imports: [
|
|
18
|
+
BfPageComponent,
|
|
19
|
+
BfHeaderComponent,
|
|
20
|
+
BfContentComponent,
|
|
21
|
+
BfButtonComponent,
|
|
22
|
+
BfListComponent,
|
|
23
|
+
BfListItemComponent,
|
|
24
|
+
],
|
|
25
|
+
template: `
|
|
26
|
+
<bf-page data-showcase="content">
|
|
27
|
+
<bf-header title="Content" [largeTitle]="false">
|
|
28
|
+
<bf-button bfSlot="end" variant="text" size="sm" (clicked)="toTop()">Top</bf-button>
|
|
29
|
+
</bf-header>
|
|
30
|
+
<bf-content [scrollEvents]="true" (scrolled)="scrollTop.set($event.scrollTop)">
|
|
31
|
+
<div class="flex flex-col gap-4 p-4">
|
|
32
|
+
<p class="text-caption text-text-muted">
|
|
33
|
+
The page's only scroller. Scroll down, then tap Top (or the button at the end) to scroll
|
|
34
|
+
back smoothly.
|
|
35
|
+
</p>
|
|
36
|
+
<p data-showcase-result class="text-body">Scrolled {{ scrollTop() }} px</p>
|
|
37
|
+
<bf-list [inset]="false">
|
|
38
|
+
@for (row of rows; track row) {
|
|
39
|
+
<bf-list-item [title]="'Item ' + row" />
|
|
40
|
+
}
|
|
41
|
+
</bf-list>
|
|
42
|
+
<bf-button variant="tonal" (clicked)="toTop()">Back to top</bf-button>
|
|
43
|
+
</div>
|
|
44
|
+
</bf-content>
|
|
45
|
+
</bf-page>
|
|
46
|
+
`,
|
|
47
|
+
})
|
|
48
|
+
export class ContentShowcasePage {
|
|
49
|
+
private readonly content = viewChild.required(BfContentComponent);
|
|
50
|
+
|
|
51
|
+
protected readonly scrollTop = signal(0);
|
|
52
|
+
protected readonly rows = Array.from({ length: 40 }, (_, index) => index + 1);
|
|
53
|
+
|
|
54
|
+
toTop(): void {
|
|
55
|
+
this.content().scrollToTop();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Overview
|
|
61
|
+
|
|
62
|
+
The page's single scroll container: fills the rest of `bf-page` under the header, keeps the
|
|
63
|
+
focused text control clear of the soft keyboard (`bfKeyboardAssist`), pads for the horizontal
|
|
64
|
+
safe areas, for the floating tab bar (`--bf-tab-bar-inset`), for a `translucent` header drawn
|
|
65
|
+
over it (`--bf-header-height`, from `BfPageContext`) and at the bottom for the height a solid
|
|
66
|
+
`bf-header` has collapsed by (`--bf-header-collapse`, written by the header, so the scroll range
|
|
67
|
+
stays the same while the large title collapses), and contains overscroll so the document
|
|
68
|
+
never scrolls. On iOS (`ios.scrollEnabled: false`) it is the only thing that scrolls. The
|
|
69
|
+
projected content sits in a block body inside the scroller: in iOS mode the body is 1 px taller
|
|
70
|
+
than the scroller, so WebKit rubber-bands even a short page; in Material mode the scroller gets
|
|
71
|
+
`bfOverscrollStretch`, the Android edge stretch the WebView does not draw itself, except in iOS
|
|
72
|
+
WebKit (Safari or a home-screen app, platform `web`), which rubber-bands natively. Both follow the
|
|
73
|
+
engine (`BfPlatformService.mode()`), not `provideBfShell({ mode })`. In the page a
|
|
74
|
+
`bf-stack-outlet` shows, the scroller is the document's `main` landmark (`role="main"`; not in a
|
|
75
|
+
`bf-modal`, see `BfPageContext.mainLandmark`). While it overflows and holds nothing focusable,
|
|
76
|
+
it is a tab stop (`tabindex="0"`), so a keyboard can scroll it in Safari too (axe
|
|
77
|
+
`scrollable-region-focusable`); otherwise Tab reaches its controls and the arrow keys scroll it.
|
|
78
|
+
|
|
79
|
+
The page's single scroll container. `bf-content` fills the rest of a `bf-page` under its header,
|
|
80
|
+
scrolls vertically with `overscroll-behavior: contain`, pads for the left and right safe areas and
|
|
81
|
+
for the floating tab bar, and keeps the focused text control clear of the soft keyboard. On iOS the
|
|
82
|
+
app runs with `ios.scrollEnabled: false`, so the document itself never scrolls: `bf-content` is the
|
|
83
|
+
only scroller, and everything that should scroll — lists, forms, pull-to-refresh — goes inside it.
|
|
84
|
+
|
|
85
|
+
## When to use
|
|
86
|
+
|
|
87
|
+
Use `bf-content` as the one scroll container of every `bf-page` in the Capacitor shell, after the page's [bf-header](https://bitforge-ui.web.app/components/header). Use the document's own scrolling instead on a desktop web page, and the [bfKeyboardAssist](https://bitforge-ui.web.app/api/bf-keyboard-assist-directive) directive only for a custom scroller that is not a `bf-content`.
|
|
88
|
+
|
|
89
|
+
## Basic usage
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { Component } from '@angular/core';
|
|
93
|
+
import { BfListComponent, BfListItemComponent } from '@bitforgehq/ui';
|
|
94
|
+
import { BfContentComponent, BfPageComponent } from '@bitforgehq/ui/mobile';
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A page's scrolling content.
|
|
98
|
+
*
|
|
99
|
+
* `bf-content` is the page's scroll container, clear of the header and the safe areas.
|
|
100
|
+
*
|
|
101
|
+
* @preview none
|
|
102
|
+
*/
|
|
103
|
+
@Component({
|
|
104
|
+
selector: 'app-example-content-basic',
|
|
105
|
+
imports: [BfContentComponent, BfListComponent, BfListItemComponent, BfPageComponent],
|
|
106
|
+
template: `
|
|
107
|
+
<bf-page>
|
|
108
|
+
<bf-content>
|
|
109
|
+
<bf-list>
|
|
110
|
+
<bf-list-item title="Inbox" />
|
|
111
|
+
<bf-list-item title="Archive" />
|
|
112
|
+
</bf-list>
|
|
113
|
+
</bf-content>
|
|
114
|
+
</bf-page>
|
|
115
|
+
`,
|
|
116
|
+
})
|
|
117
|
+
export class ContentBasicExampleComponent {}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## The only scroller
|
|
121
|
+
|
|
122
|
+
Use one `bf-content` per page and put every scrollable part of the page inside it. The host is a
|
|
123
|
+
`flex-1 min-h-0` child of `bf-page`'s column, so it takes the height left under a header and its
|
|
124
|
+
inner scroller (`h-full overflow-y-auto`) does the scrolling. With the required
|
|
125
|
+
`capacitor.config` (`ios: { scrollEnabled: false }`), content placed outside `bf-content` does not
|
|
126
|
+
scroll at all on iOS. A horizontal row inside it (a chips row or a carousel with
|
|
127
|
+
`overflow-x-auto`) pans on its own; it needs no directive.
|
|
128
|
+
|
|
129
|
+
## Fullscreen
|
|
130
|
+
|
|
131
|
+
Without a header above it, set `fullscreen` so the content starts below the status bar
|
|
132
|
+
(`pt-safe-top`) instead of under it. Inside a page-sheet `bf-modal`, whose card already sits below
|
|
133
|
+
the status bar (`BfPageContext.safeAreaTop` false), `fullscreen` adds no top padding.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { Component } from '@angular/core';
|
|
137
|
+
import { BfContentComponent, BfPageComponent } from '@bitforgehq/ui/mobile';
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Content that runs under the status bar.
|
|
141
|
+
*
|
|
142
|
+
* `fullscreen` drops the top safe-area padding, for a page without a header.
|
|
143
|
+
*
|
|
144
|
+
* @preview none
|
|
145
|
+
*/
|
|
146
|
+
@Component({
|
|
147
|
+
selector: 'app-example-content-fullscreen',
|
|
148
|
+
imports: [BfContentComponent, BfPageComponent],
|
|
149
|
+
template: `
|
|
150
|
+
<bf-page>
|
|
151
|
+
<bf-content [fullscreen]="true">
|
|
152
|
+
<h1 data-bf-page-title class="text-display">Today</h1>
|
|
153
|
+
</bf-content>
|
|
154
|
+
</bf-page>
|
|
155
|
+
`,
|
|
156
|
+
})
|
|
157
|
+
export class ContentFullscreenExampleComponent {}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Keyboard assist
|
|
161
|
+
|
|
162
|
+
`bf-content` applies `bfKeyboardAssist` to its scroller. When the soft keyboard opens it pads the
|
|
163
|
+
scroller by the part the keyboard covers and scrolls the focused `input`, `textarea` or
|
|
164
|
+
`[contenteditable]` until it sits 24 px above the keyboard, after the engine's own focus scroll,
|
|
165
|
+
with one correction once the keyboard has settled. Focus moving to the next field while the
|
|
166
|
+
keyboard is open reveals that one too. Turn it off with `keyboardAssist` for a screen that manages
|
|
167
|
+
the keyboard itself, such as a chat with a composer pinned above the keyboard.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { Component, signal } from '@angular/core';
|
|
171
|
+
import { BfButtonComponent, BfTextareaComponent } from '@bitforgehq/ui';
|
|
172
|
+
import { BfContentComponent, BfPageComponent } from '@bitforgehq/ui/mobile';
|
|
173
|
+
|
|
174
|
+
/** A chat that keeps its composer above the keyboard itself. */
|
|
175
|
+
@Component({
|
|
176
|
+
selector: 'app-chat-thread',
|
|
177
|
+
imports: [BfButtonComponent, BfTextareaComponent],
|
|
178
|
+
template: `
|
|
179
|
+
<div class="flex h-full flex-col">
|
|
180
|
+
<ul class="flex flex-1 flex-col gap-2 p-4">
|
|
181
|
+
@for (message of messages(); track $index) {
|
|
182
|
+
<li class="bg-surface rounded-card p-3">{{ message }}</li>
|
|
183
|
+
}
|
|
184
|
+
</ul>
|
|
185
|
+
<div class="pb-safe-bottom bg-bg sticky bottom-0 flex items-end gap-2 p-2">
|
|
186
|
+
<bf-textarea class="flex-1" ariaLabel="Message" [rows]="1" [(value)]="draft" />
|
|
187
|
+
<bf-button (clicked)="send()">Send</bf-button>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
`,
|
|
191
|
+
})
|
|
192
|
+
export class ChatThreadComponent {
|
|
193
|
+
protected readonly messages = signal(['Is the deck ready?']);
|
|
194
|
+
protected readonly draft = signal('');
|
|
195
|
+
|
|
196
|
+
protected send(): void {
|
|
197
|
+
this.messages.update((messages) => [...messages, this.draft()]);
|
|
198
|
+
this.draft.set('');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Content without the keyboard assist.
|
|
204
|
+
*
|
|
205
|
+
* `[keyboardAssist]="false"` leaves the keyboard to a screen that handles it itself, such as a
|
|
206
|
+
* chat with a composer pinned above the keyboard.
|
|
207
|
+
*
|
|
208
|
+
* @preview none
|
|
209
|
+
*/
|
|
210
|
+
@Component({
|
|
211
|
+
selector: 'app-example-content-keyboard',
|
|
212
|
+
imports: [BfContentComponent, BfPageComponent, ChatThreadComponent],
|
|
213
|
+
template: `
|
|
214
|
+
<bf-page>
|
|
215
|
+
<bf-content [keyboardAssist]="false">
|
|
216
|
+
<app-chat-thread />
|
|
217
|
+
</bf-content>
|
|
218
|
+
</bf-page>
|
|
219
|
+
`,
|
|
220
|
+
})
|
|
221
|
+
export class ContentKeyboardExampleComponent {}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Overscroll
|
|
225
|
+
|
|
226
|
+
The projected content sits in a block body inside the scroller, so an overscroll effect can move
|
|
227
|
+
the body without touching the scroller's own padding.
|
|
228
|
+
|
|
229
|
+
- **iOS**: WebKit rubber-bands a scroller only when its content overflows. In iOS mode the body is
|
|
230
|
+
at least 1 px taller than the scroller (`min-h-[calc(100%+1px)]`), so a short page bounces like a
|
|
231
|
+
native list too; the 1 px of scroll is imperceptible.
|
|
232
|
+
- **Android**: the WebView draws no overscroll effect for an inner scroller. In Material mode the
|
|
233
|
+
scroller carries `bfOverscrollStretch`, which stretches the body at the top or bottom edge under
|
|
234
|
+
the finger and springs it back. With an enabled `bf-refresher` on the page, only the bottom edge
|
|
235
|
+
stretches; the top pull belongs to the refresher.
|
|
236
|
+
- **iOS Safari and home-screen apps**: Capacitor reports platform `web` there, so the mode is
|
|
237
|
+
Material, but WebKit rubber-bands natively. `bf-content` detects iOS WebKit (an iPhone, iPad or
|
|
238
|
+
iPod user agent, or an iPad that reports `MacIntel` with touch points) and leaves the stretch
|
|
239
|
+
off, so it never stacks on the bounce.
|
|
240
|
+
|
|
241
|
+
The overscroll follows the engine (`BfPlatformService.mode()`, iOS only on native iOS), not
|
|
242
|
+
`provideBfShell({ mode })`: forcing `mode: 'ios'` on Android keeps the stretch and the Material pull,
|
|
243
|
+
and forcing `mode: 'md'` on iOS keeps the bounce. The bounce is WebKit's, and the refresher's iOS
|
|
244
|
+
mode reads it from a negative `scrollTop`; Chrome never reports a negative `scrollTop`, so an
|
|
245
|
+
Android WebView in the iOS look would get neither a bounce nor a working pull-to-refresh.
|
|
246
|
+
|
|
247
|
+
## Tab bar inset
|
|
248
|
+
|
|
249
|
+
Inside `bf-tabs`, the floating tab bar sets `--bf-tab-bar-inset` and `bf-content` pads its bottom
|
|
250
|
+
by it (`0px` outside tabs), so the last row scrolls clear of the bar.
|
|
251
|
+
|
|
252
|
+
## Large-title compensation
|
|
253
|
+
|
|
254
|
+
A solid `bf-header` above the scroller shrinks as its large title collapses, and the scroller grows
|
|
255
|
+
by the same height. The header writes that height as `--bf-header-collapse` on the scroller, and
|
|
256
|
+
`bf-content` adds it to the bottom padding
|
|
257
|
+
(`pb-[calc(var(--bf-tab-bar-inset,0px)+var(--bf-header-collapse,0px))]`), so the scroll range stays
|
|
258
|
+
the same through the collapse and a page that scrolls just over the 56 px collapse range never
|
|
259
|
+
flips between expanded and collapsed. While the soft keyboard is up, the keyboard assist's inline
|
|
260
|
+
bottom padding takes over.
|
|
261
|
+
|
|
262
|
+
## Scroll events and scrolling
|
|
263
|
+
|
|
264
|
+
`scrolled` emits a `BfContentScrollEvent` (`{ scrollTop }`) from a passive listener, but only while `scrollEvents` is true, so
|
|
265
|
+
plain pages add no scroll listener and no change detection while scrolling. `scrollToTop()` and
|
|
266
|
+
`scrollToPoint(x, y)` scroll smoothly by default; `scrollElement` returns the scroll container.
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { Component, signal } from '@angular/core';
|
|
270
|
+
import { BfButtonComponent, BfListComponent, BfListItemComponent } from '@bitforgehq/ui';
|
|
271
|
+
import { BfContentComponent, BfPageComponent } from '@bitforgehq/ui/mobile';
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Scroll events and scrolling to the top.
|
|
275
|
+
*
|
|
276
|
+
* `scrollEvents` turns on `scrolled`; the template reference calls `scrollToTop()`.
|
|
277
|
+
*
|
|
278
|
+
* @preview none
|
|
279
|
+
*/
|
|
280
|
+
@Component({
|
|
281
|
+
selector: 'app-example-content-scroll',
|
|
282
|
+
imports: [
|
|
283
|
+
BfButtonComponent,
|
|
284
|
+
BfContentComponent,
|
|
285
|
+
BfListComponent,
|
|
286
|
+
BfListItemComponent,
|
|
287
|
+
BfPageComponent,
|
|
288
|
+
],
|
|
289
|
+
template: `
|
|
290
|
+
<bf-page>
|
|
291
|
+
<bf-content #content [scrollEvents]="true" (scrolled)="onScroll($event.scrollTop)">
|
|
292
|
+
<bf-list>
|
|
293
|
+
@for (row of rows; track row) {
|
|
294
|
+
<bf-list-item [title]="'Task ' + row" />
|
|
295
|
+
}
|
|
296
|
+
</bf-list>
|
|
297
|
+
@if (scrolledDown()) {
|
|
298
|
+
<bf-button (clicked)="content.scrollToTop()">Back to top</bf-button>
|
|
299
|
+
}
|
|
300
|
+
</bf-content>
|
|
301
|
+
</bf-page>
|
|
302
|
+
`,
|
|
303
|
+
})
|
|
304
|
+
export class ContentScrollExampleComponent {
|
|
305
|
+
protected readonly rows = Array.from({ length: 40 }, (_, i) => i + 1);
|
|
306
|
+
protected readonly scrolledDown = signal(false);
|
|
307
|
+
|
|
308
|
+
protected onScroll(scrollTop: number): void {
|
|
309
|
+
this.scrolledDown.set(scrollTop > 400);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Desktop
|
|
315
|
+
|
|
316
|
+
`bf-content` is part of the Capacitor shell (`provideBfShell()`). A desktop web app lets the document scroll: put the page in your own layout (a `main` that scrolls with the window) and leave `bf-page`, `bf-content` and `bf-stack-outlet` out. Inside the shell on a desktop browser, wheel, trackpad and keyboard scrolling are the browser's own, the keyboard assist adds nothing (no soft keyboard), and the Android stretch never starts (it follows touch only). Safari does not let the keyboard reach a scroll container with nothing focusable in it, so while the content overflows and holds no control, the scroller is a tab stop itself: <kbd>Tab</kbd> focuses it (with the focus ring) and the arrow keys, <kbd>Page Down</kbd> and <kbd>Space</kbd> scroll it. With a control inside, Tab reaches the control instead and the scroller stays out of the Tab order.
|
|
317
|
+
|
|
318
|
+
## Theming
|
|
319
|
+
|
|
320
|
+
- Background: none of its own; it shows `bg` from the surrounding `bf-page`.
|
|
321
|
+
- Spacing: `--spacing-safe-left` / `--spacing-safe-right` (horizontal safe areas),
|
|
322
|
+
`--spacing-safe-top` when `fullscreen` (not inside a page-sheet modal), and `--bf-tab-bar-inset` plus `--bf-header-collapse`
|
|
323
|
+
(bottom, set by the tab bar and by a solid `bf-header` while its large title is collapsed, both
|
|
324
|
+
default `0px`).
|
|
325
|
+
- Focus ring (only while the scroller is a tab stop): `ring` at `ring-4`, inset.
|
|
326
|
+
- Layout: host `block min-h-0 flex-1`; scroller `h-full overflow-y-auto overscroll-contain
|
|
327
|
+
touch-pan-y`; inside it a plain block body wraps the projected content (`min-h-[calc(100%+1px)]`
|
|
328
|
+
in iOS mode). Add your own padding on the content inside it. The body's height is `auto`, so a
|
|
329
|
+
child can no longer fill the page with `h-full`: percentage heights (`h-full`, `min-h-full`, and
|
|
330
|
+
the `flex-1` of a wrapper sized with them) resolve to nothing there. Size such a child from its
|
|
331
|
+
content or the viewport on your own wrapper (a centred empty state: `min-h-[60dvh]`), not with
|
|
332
|
+
`h-full`.
|
|
333
|
+
- Page gutter: pad page content with the 16 px screen gutter (`px-gutter` or `p-4`), the inset the
|
|
334
|
+
header's large title, toasts and alert dialogs use, so text lines up down the screen.
|
|
335
|
+
|
|
336
|
+
## Accessibility
|
|
337
|
+
|
|
338
|
+
In the page a `bf-stack-outlet` shows, the scroller is the document's `main` landmark
|
|
339
|
+
(`role="main"`, from `BfPageContext.mainLandmark`), so there is exactly one `main` at any time:
|
|
340
|
+
parked pages are `aria-hidden`, and the content of a `bf-modal` (or of a `bf-page` outside an
|
|
341
|
+
outlet, such as a docs illustration) is no landmark. While the scroller overflows and holds nothing
|
|
342
|
+
focusable it is a tab stop (`tabindex="0"`, axe `scrollable-region-focusable`), so a keyboard can
|
|
343
|
+
scroll it in Safari; once a control appears inside, it leaves the Tab order again. Controls that
|
|
344
|
+
are `hidden`, inside an `inert` or `hidden` subtree or not rendered (`display: none`) do not count,
|
|
345
|
+
and the 1 px the iOS body adds for the rubber band is not overflow. The page's
|
|
346
|
+
heading comes from its content. The keyboard assist keeps the focused control visible above the soft keyboard, so what is
|
|
347
|
+
typed is always on screen, and it never moves focus. `overscroll-contain` keeps a scroll gesture
|
|
348
|
+
from chaining into the document or a parent scroller, and `touch-pan-y` leaves horizontal drags to
|
|
349
|
+
the edge swipe-back. The overscroll is pure feel, with no behaviour of its own: on iOS every page
|
|
350
|
+
bounces, short ones too, and on Android the content stretches at both edges; neither moves focus,
|
|
351
|
+
changes what is read, or prevents a touch, and the Android stretch springs back instantly under
|
|
352
|
+
`prefers-reduced-motion`.
|
|
353
|
+
|
|
354
|
+
### Keyboard
|
|
355
|
+
|
|
356
|
+
| Key | Where | Action |
|
|
357
|
+
| ------------------------------------------------------------ | ----------------------------------------- | ------------------------------------- |
|
|
358
|
+
| <kbd>Tab</kbd> | Content with nothing focusable, scrolling | Focuses the scroller |
|
|
359
|
+
| <kbd>ArrowUp</kbd> / <kbd>ArrowDown</kbd> | Scroller or a control inside it | Scrolls (the browser's own) |
|
|
360
|
+
| <kbd>Page Up</kbd> / <kbd>Page Down</kbd> / <kbd>Space</kbd> | Scroller | Scrolls by a page (the browser's own) |
|
|
361
|
+
|
|
362
|
+
## Inputs
|
|
363
|
+
|
|
364
|
+
| Name | Type | Default | Description |
|
|
365
|
+
| ---------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------- |
|
|
366
|
+
| `fullscreen` | `boolean` | `false` | Extends the content under the status bar: adds the top safe-area padding (no header above). |
|
|
367
|
+
| `keyboardAssist` | `boolean` | `true` | Keeps the focused text control clear of the soft keyboard; false for screens that handle it themselves. |
|
|
368
|
+
| `scrollEvents` | `boolean` | `false` | Emits `scrolled` on every scroll; off by default so plain pages add no listener. |
|
|
369
|
+
|
|
370
|
+
## Outputs
|
|
371
|
+
|
|
372
|
+
| Name | Payload | Description |
|
|
373
|
+
| ---------- | ---------------------- | ----------------------------------------------------------------------------- |
|
|
374
|
+
| `scrolled` | `BfContentScrollEvent` | The scroller's `scrollTop` on each scroll, only while `scrollEvents` is true. |
|
|
375
|
+
|
|
376
|
+
## Slots
|
|
377
|
+
|
|
378
|
+
- `default`: The page content, in the scroll container.
|
|
379
|
+
|
|
380
|
+
## Methods
|
|
381
|
+
|
|
382
|
+
### `scrollToTop(behavior?: ScrollBehavior): void`
|
|
383
|
+
|
|
384
|
+
Scrolls back to the top (e.g. on a second tap of the active tab).
|
|
385
|
+
|
|
386
|
+
| Parameter | Type | Description |
|
|
387
|
+
| ----------- | ---------------- | ------------------------------------ |
|
|
388
|
+
| `behavior?` | `ScrollBehavior` | `smooth` (the default) or `instant`. |
|
|
389
|
+
|
|
390
|
+
### `scrollToPoint(x: number, y: number, behavior?: ScrollBehavior): void`
|
|
391
|
+
|
|
392
|
+
Scrolls to the given offset in px.
|
|
393
|
+
|
|
394
|
+
| Parameter | Type | Description |
|
|
395
|
+
| ----------- | ---------------- | ------------------------------------ |
|
|
396
|
+
| `x` | `number` | The horizontal offset (px). |
|
|
397
|
+
| `y` | `number` | The vertical offset (px). |
|
|
398
|
+
| `behavior?` | `ScrollBehavior` | `smooth` (the default) or `instant`. |
|
|
399
|
+
|
|
400
|
+
## Notes
|
|
401
|
+
|
|
402
|
+
### Do
|
|
403
|
+
|
|
404
|
+
- Use exactly one `bf-content` per `bf-page`, after the header.
|
|
405
|
+
- Pad the page content with the 16 px gutter (`px-gutter`), so it aligns with the large title.
|
|
406
|
+
- Keep `keyboardAssist` on for any page with text fields.
|
|
407
|
+
- Set `scrollEvents` only when something listens to `scrolled`.
|
|
408
|
+
|
|
409
|
+
### Don't
|
|
410
|
+
|
|
411
|
+
- Don't make the document or another wrapper scroll; on iOS it cannot.
|
|
412
|
+
- Don't nest a second vertical scroller inside `bf-content` for the page's main content.
|
|
413
|
+
- Don't add bottom padding for the tab bar yourself; `--bf-tab-bar-inset` already covers it.
|
|
414
|
+
|
|
415
|
+
## Migration notes
|
|
416
|
+
|
|
417
|
+
### 0.3.0
|
|
418
|
+
|
|
419
|
+
New exported `BfContentScrollEvent` (`{ scrollTop: number }`), the payload of `scrolled` (was an
|
|
420
|
+
inline object type of the same shape).
|
|
421
|
+
|
|
422
|
+
JSDoc of `content.component.ts` for the API reference: `@slot` docs for `default`; `@param` docs. No
|
|
423
|
+
behaviour change.
|
|
424
|
+
|
|
425
|
+
**Behaviour:** `--bf-header-height` is written in the render phase (`afterRenderEffect`), and the
|
|
426
|
+
scroller registers in `BfPageContext` once after the first render (`afterNextRender`) instead of
|
|
427
|
+
from an `effect`; it is still cleared on destroy. Internal timing only, no API change.
|
|
428
|
+
|
|
429
|
+
`fullscreen` pads the top safe area only while the page's `BfPageContext.safeAreaTop()` is true;
|
|
430
|
+
inside a page-sheet `bf-modal` (false) it adds no top padding (Migration 29).
|
|
431
|
+
|
|
432
|
+
**Behaviour:** in the page a `bf-stack-outlet` shows, the scroller is the `main` landmark
|
|
433
|
+
(`role="main"`, while `BfPageContext.mainLandmark()` is true). While it overflows and holds nothing
|
|
434
|
+
focusable it is a tab stop (`tabindex="0"`, with an inset focus ring), so a keyboard can scroll it
|
|
435
|
+
in Safari and WKWebView (which never focus a scroll container on their own; Chromium does); a
|
|
436
|
+
control appearing inside takes it out of the Tab order again (re-checked per frame on resize and
|
|
437
|
+
content changes, including `hidden` and `inert` toggles). Hidden, inert and unrendered controls do
|
|
438
|
+
not count, and overflow means more than 2 px (the iOS body's 1 px rubber-band extra is not
|
|
439
|
+
overflow). See Migration 36.
|
|
440
|
+
|
|
441
|
+
### 0.2.0
|
|
442
|
+
|
|
443
|
+
New `bf-content`: the page's single scroll container (host `block min-h-0 flex-1`, scroller
|
|
444
|
+
`h-full overflow-y-auto overscroll-contain touch-pan-y pl-safe-left pr-safe-right` with
|
|
445
|
+
`padding-bottom: var(--bf-tab-bar-inset, 0px)`, plus `pt-safe-top` when `fullscreen`, otherwise
|
|
446
|
+
`padding-top` and `scroll-padding-top` of `var(--bf-header-height, 0px)`, a variable it writes on
|
|
447
|
+
its scroller from `BfPageContext.headerHeight` while a `translucent` header is drawn over it). It applies
|
|
448
|
+
`bfKeyboardAssist` to its scroller (`keyboardAssist`, default `true`), emits `scrolled`
|
|
449
|
+
(`{ scrollTop }`) from a passive listener only while `scrollEvents` is true, offers
|
|
450
|
+
`scrollToTop(behavior = 'smooth')`, `scrollToPoint(x, y, behavior)` and a `scrollElement` getter,
|
|
451
|
+
and registers its scroller in the page's `BfPageContext`.
|
|
452
|
+
|
|
453
|
+
Changed: the projected content now sits in a plain block body inside the scroller. In iOS mode
|
|
454
|
+
(`BfPlatformService.mode()`) the body is `min-h-[calc(100%+1px)]`, so the scroller always overflows
|
|
455
|
+
by 1 px and WebKit rubber-bands short pages too (it only bounces an overflowing scroller); in
|
|
456
|
+
Material mode the scroller carries `bfOverscrollStretch`, the Android edge stretch the WebView does
|
|
457
|
+
not draw. `bf-refresher` without a page context now tracks its nearest scrolling ancestor (past
|
|
458
|
+
the body wrapper) instead of its parent element. The stretch stays off in iOS WebKit (Safari or a
|
|
459
|
+
home-screen app, where Capacitor reports platform `web` and the mode is Material, detected from an
|
|
460
|
+
`iPhone`/`iPad`/`iPod` user agent or `MacIntel` with more than one touch point), which rubber-bands
|
|
461
|
+
natively, so the two never stack.
|
|
462
|
+
|
|
463
|
+
Migration: the body's height is `auto`, so children that filled the scroller with `h-full` (a
|
|
464
|
+
centred empty state) now collapse, and `min-h-full` or a `flex-1` inside a wrapper sized that way
|
|
465
|
+
collapses too. Size such a child from its content or the viewport on your own wrapper
|
|
466
|
+
(`min-h-[60dvh]`), not with `h-full`. The overscroll (the iOS bounce, the Android stretch and the
|
|
467
|
+
refresher's iOS bounce mode) follows the engine (`BfPlatformService.mode()`), not
|
|
468
|
+
`provideBfShell({ mode })`: Chrome never reports a negative `scrollTop`, so an Android app forced
|
|
469
|
+
into the iOS look keeps the stretch and the material pull.
|
|
470
|
+
|
|
471
|
+
Fixed: the scroller's bottom padding also adds `--bf-header-collapse`, the height a solid
|
|
472
|
+
`bf-header` has collapsed by (`pb-[calc(var(--bf-tab-bar-inset,0px)+var(--bf-header-collapse,0px))]`,
|
|
473
|
+
replacing `pb-(--bf-tab-bar-inset,0px)`), so the scroll range stays constant while the large title
|
|
474
|
+
collapses. The keyboard assist's inline `padding-bottom` still overrides it while the keyboard is up.
|