@oxyhq/bloom 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/docs/accordion.mdx +69 -0
- package/docs/activity-heatmap.mdx +71 -0
- package/docs/admonition.mdx +77 -0
- package/docs/alert-dialog.mdx +75 -0
- package/docs/alert.mdx +93 -0
- package/docs/animated-check.mdx +53 -0
- package/docs/aspect-ratio.mdx +45 -0
- package/docs/avatar-group.mdx +91 -0
- package/docs/avatar.mdx +126 -0
- package/docs/badge.mdx +74 -0
- package/docs/benefit-list.mdx +56 -0
- package/docs/bottom-sheet.mdx +103 -0
- package/docs/button.mdx +218 -0
- package/docs/card.mdx +106 -0
- package/docs/checkbox.mdx +59 -0
- package/docs/chip.mdx +53 -0
- package/docs/code.mdx +50 -0
- package/docs/combobox.mdx +64 -0
- package/docs/command.mdx +85 -0
- package/docs/composition-bar.mdx +63 -0
- package/docs/connection-dots.mdx +50 -0
- package/docs/connection-status.mdx +56 -0
- package/docs/content-panel.mdx +62 -0
- package/docs/context-menu.mdx +65 -0
- package/docs/design-tokens.mdx +254 -0
- package/docs/dialog.mdx +277 -0
- package/docs/divider.mdx +56 -0
- package/docs/dot-grid-meter.mdx +66 -0
- package/docs/dropdown-menu.mdx +161 -0
- package/docs/error-boundary.mdx +72 -0
- package/docs/fab.mdx +67 -0
- package/docs/field.mdx +70 -0
- package/docs/fill.mdx +58 -0
- package/docs/fonts.mdx +144 -0
- package/docs/frosted-icon-button.mdx +60 -0
- package/docs/getting-started.mdx +75 -0
- package/docs/grid.mdx +64 -0
- package/docs/hooks.mdx +81 -0
- package/docs/icon-circle.mdx +66 -0
- package/docs/icons.mdx +69 -0
- package/docs/image-aspect-ratio-cache.mdx +63 -0
- package/docs/image-resolver.mdx +66 -0
- package/docs/index.mdx +44 -0
- package/docs/input-group.mdx +58 -0
- package/docs/item.mdx +69 -0
- package/docs/kbd.mdx +57 -0
- package/docs/label.mdx +50 -0
- package/docs/link-preview.mdx +66 -0
- package/docs/list.mdx +72 -0
- package/docs/loading.mdx +129 -0
- package/docs/media-inset-border.mdx +52 -0
- package/docs/menubar.mdx +86 -0
- package/docs/migrating-to-1.0.mdx +434 -0
- package/docs/motion.mdx +83 -0
- package/docs/overlay.mdx +75 -0
- package/docs/popover.mdx +73 -0
- package/docs/portal.mdx +63 -0
- package/docs/pressable-scale.mdx +51 -0
- package/docs/profile-card.mdx +66 -0
- package/docs/progressive-blur.mdx +54 -0
- package/docs/prompt-input.mdx +183 -0
- package/docs/provider.mdx +64 -0
- package/docs/radio-indicator.mdx +60 -0
- package/docs/radio.mdx +65 -0
- package/docs/scroll.mdx +66 -0
- package/docs/search.mdx +67 -0
- package/docs/segmented-control.mdx +76 -0
- package/docs/select.mdx +93 -0
- package/docs/settings-list.mdx +186 -0
- package/docs/skeleton.mdx +66 -0
- package/docs/slider.mdx +73 -0
- package/docs/stat-bar.mdx +59 -0
- package/docs/stat-widgets.mdx +33 -0
- package/docs/styles.mdx +61 -0
- package/docs/subtle-hover.mdx +53 -0
- package/docs/surfaces.mdx +72 -0
- package/docs/switch.mdx +90 -0
- package/docs/tab-bar.mdx +386 -0
- package/docs/tabs.mdx +75 -0
- package/docs/text-field.mdx +187 -0
- package/docs/theme.mdx +67 -0
- package/docs/toast.mdx +285 -0
- package/docs/tooltip.mdx +67 -0
- package/docs/typography.mdx +88 -0
- package/docs/user-hover-card.mdx +140 -0
- package/docs/zoomable-image-gallery.mdx +66 -0
- package/lib/commonjs/design-tokens/color-roles.js +21 -1
- package/lib/commonjs/design-tokens/color-roles.js.map +1 -1
- package/lib/commonjs/design-tokens/index.js +6 -0
- package/lib/commonjs/design-tokens/index.js.map +1 -1
- package/lib/commonjs/design-tokens/tailwind-preset.js +3 -1
- package/lib/commonjs/design-tokens/tailwind-preset.js.map +1 -1
- package/lib/commonjs/design-tokens/theme-css.js +1 -0
- package/lib/commonjs/design-tokens/theme-css.js.map +1 -1
- package/lib/commonjs/design-tokens/theme.css +3 -0
- package/lib/commonjs/design-tokens/tokens-json.js +13 -4
- package/lib/commonjs/design-tokens/tokens-json.js.map +1 -1
- package/lib/commonjs/design-tokens/tokens.json +6227 -265
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +7 -0
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/theme/ambient-store.js +16 -1
- package/lib/commonjs/theme/ambient-store.js.map +1 -1
- package/lib/commonjs/theme/color-policy.js +30 -33
- package/lib/commonjs/theme/color-policy.js.map +1 -1
- package/lib/commonjs/theme/color-presets.js +413 -166
- package/lib/commonjs/theme/color-presets.js.map +1 -1
- package/lib/commonjs/theme/color-scope/seed-scope.js +1 -0
- package/lib/commonjs/theme/color-scope/seed-scope.js.map +1 -1
- package/lib/commonjs/theme/glass-colors.js +18 -21
- package/lib/commonjs/theme/glass-colors.js.map +1 -1
- package/lib/commonjs/theme/index.js +30 -0
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/commonjs/theme/index.web.js +30 -0
- package/lib/commonjs/theme/index.web.js.map +1 -1
- package/lib/module/design-tokens/color-roles.js +20 -0
- package/lib/module/design-tokens/color-roles.js.map +1 -1
- package/lib/module/design-tokens/index.js +2 -2
- package/lib/module/design-tokens/index.js.map +1 -1
- package/lib/module/design-tokens/tailwind-preset.js +4 -2
- package/lib/module/design-tokens/tailwind-preset.js.map +1 -1
- package/lib/module/design-tokens/theme-css.js +2 -1
- package/lib/module/design-tokens/theme-css.js.map +1 -1
- package/lib/module/design-tokens/theme.css +3 -0
- package/lib/module/design-tokens/tokens-json.js +14 -5
- package/lib/module/design-tokens/tokens-json.js.map +1 -1
- package/lib/module/design-tokens/tokens.json +6227 -265
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +1 -1
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/theme/ambient-store.js +16 -1
- package/lib/module/theme/ambient-store.js.map +1 -1
- package/lib/module/theme/color-policy.js +30 -33
- package/lib/module/theme/color-policy.js.map +1 -1
- package/lib/module/theme/color-presets.js +412 -165
- package/lib/module/theme/color-presets.js.map +1 -1
- package/lib/module/theme/color-scope/seed-scope.js +2 -1
- package/lib/module/theme/color-scope/seed-scope.js.map +1 -1
- package/lib/module/theme/glass-colors.js +18 -21
- package/lib/module/theme/glass-colors.js.map +1 -1
- package/lib/module/theme/index.js +1 -1
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/index.web.js +1 -1
- package/lib/module/theme/index.web.js.map +1 -1
- package/lib/typescript/commonjs/design-tokens/color-roles.d.ts +20 -0
- package/lib/typescript/commonjs/design-tokens/color-roles.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/index.d.ts +3 -3
- package/lib/typescript/commonjs/design-tokens/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/tailwind-preset.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/theme-css.d.ts.map +1 -1
- package/lib/typescript/commonjs/design-tokens/tokens-json.d.ts +12 -3
- package/lib/typescript/commonjs/design-tokens/tokens-json.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.web.d.ts +2 -2
- package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/ambient-store.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-policy.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-presets.d.ts +386 -62
- package/lib/typescript/commonjs/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/seed-scope.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/glass-colors.d.ts +18 -21
- package/lib/typescript/commonjs/theme/glass-colors.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/index.d.ts +2 -2
- package/lib/typescript/commonjs/theme/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/index.web.d.ts +2 -2
- package/lib/typescript/commonjs/theme/index.web.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/color-roles.d.ts +20 -0
- package/lib/typescript/module/design-tokens/color-roles.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/index.d.ts +3 -3
- package/lib/typescript/module/design-tokens/index.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/tailwind-preset.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/theme-css.d.ts.map +1 -1
- package/lib/typescript/module/design-tokens/tokens-json.d.ts +12 -3
- package/lib/typescript/module/design-tokens/tokens-json.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/index.web.d.ts +2 -2
- package/lib/typescript/module/index.web.d.ts.map +1 -1
- package/lib/typescript/module/theme/ambient-store.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-policy.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-presets.d.ts +386 -62
- package/lib/typescript/module/theme/color-presets.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/seed-scope.d.ts.map +1 -1
- package/lib/typescript/module/theme/glass-colors.d.ts +18 -21
- package/lib/typescript/module/theme/glass-colors.d.ts.map +1 -1
- package/lib/typescript/module/theme/index.d.ts +2 -2
- package/lib/typescript/module/theme/index.d.ts.map +1 -1
- package/lib/typescript/module/theme/index.web.d.ts +2 -2
- package/lib/typescript/module/theme/index.web.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/__tests__/support/collision-fixture-barrel.ts +20 -0
- package/src/__tests__/support/constructed-style-sheets.ts +68 -0
- package/src/__tests__/support/press-host.ts +30 -0
- package/src/__tests__/support/rendered-style.ts +99 -0
- package/src/__tests__/support/unread-hook-fixture.ts +33 -0
- package/src/design-tokens/color-roles.ts +21 -0
- package/src/design-tokens/index.ts +3 -2
- package/src/design-tokens/tailwind-preset.ts +3 -2
- package/src/design-tokens/theme-css.ts +2 -1
- package/src/design-tokens/theme.css +3 -0
- package/src/design-tokens/tokens-json.ts +29 -9
- package/src/design-tokens/tokens.json +6227 -265
- package/src/glass/GlassSurface.tsx +2 -2
- package/src/index.ts +2 -0
- package/src/index.web.ts +2 -0
- package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +4082 -0
- package/src/theme/__tests__/fixtures/color-engine-golden.json +1 -0
- package/src/theme/ambient-store.ts +21 -1
- package/src/theme/color-policy.ts +33 -38
- package/src/theme/color-presets.ts +473 -146
- package/src/theme/color-scope/seed-scope.ts +2 -1
- package/src/theme/glass-colors.ts +18 -21
- package/src/theme/index.ts +16 -1
- package/src/theme/index.web.ts +16 -1
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Migrating to 1.0
|
|
3
|
+
description: Bloom 1.0.0 moves with @oxyhq/services 30.0.1 and @oxyhq/core 21.0.0 as one upgrade, plus every breaking change between 0.89.0 and 1.0.0, derived from the published tarballs rather than from commit messages.
|
|
4
|
+
order: 2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Migrating to 1.0
|
|
8
|
+
|
|
9
|
+
Bloom follows the clean-cut rule: there are no `@deprecated` aliases and no
|
|
10
|
+
old-name shims anywhere in this release. **This page is the only bridge.** If a
|
|
11
|
+
name below is gone, it is gone in the build you install — nothing re-exports it
|
|
12
|
+
under the old spelling.
|
|
13
|
+
|
|
14
|
+
<Callout variant="info" title="Which version are you on?">
|
|
15
|
+
`0.87.1 → 0.88.0 → 0.89.0` was **purely additive**: seven new symbols on
|
|
16
|
+
`@oxyhq/bloom/design-tokens` plus the `./design-tokens/tokens.json` subpath, and
|
|
17
|
+
nothing removed or renamed. So every change on this page applies in full whether
|
|
18
|
+
you are coming from 0.87.1, 0.88.0 or 0.89.0.
|
|
19
|
+
</Callout>
|
|
20
|
+
|
|
21
|
+
<Callout variant="warning" title="`^` means something different now">
|
|
22
|
+
Under a `0.x` version, `^0.89.0` resolves to `>=0.89.0 <0.90.0` — a caret pinned
|
|
23
|
+
you to one minor. From 1.0.0, `^1.0.0` resolves to `>=1.0.0 <2.0.0`, so a caret
|
|
24
|
+
now accepts every future minor. That is the intended semantics, but it is a
|
|
25
|
+
change in what your existing range does.
|
|
26
|
+
</Callout>
|
|
27
|
+
|
|
28
|
+
## Before anything else: this is a three-package upgrade
|
|
29
|
+
|
|
30
|
+
Bloom 1.0.0 is not a package you can bump on its own. If your app also uses
|
|
31
|
+
`@oxyhq/services`, the move is **three packages in one commit**:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
"@oxyhq/bloom": "^1.0.0",
|
|
35
|
+
"@oxyhq/services": "^30.0.1",
|
|
36
|
+
"@oxyhq/core": "^21.0.0"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Not three steps. Both intermediate states are broken, and neither announces
|
|
40
|
+
itself: one cannot produce a warning at all, and the other produces exactly one
|
|
41
|
+
line, on one run.
|
|
42
|
+
|
|
43
|
+
### Bloom alone breaks `@oxyhq/services`, silently
|
|
44
|
+
|
|
45
|
+
Every published `@oxyhq/services` below `30.0.0` imports two subpaths that 1.0.0
|
|
46
|
+
deleted, from its own source:
|
|
47
|
+
|
|
48
|
+
| File in `@oxyhq/services` | Imports |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| `src/ui/components/FollowTargetButton.tsx` | `@oxyhq/bloom/menu` |
|
|
51
|
+
| `src/ui/components/authChooser/TroubleDisclosure.tsx` | `@oxyhq/bloom/collapsible` |
|
|
52
|
+
|
|
53
|
+
Both are present in the published `29.0.0` tarball in `src/` **and** in both
|
|
54
|
+
compiled `lib/` trees, so it does not matter whether your bundler reads Bloom's
|
|
55
|
+
source or its build. Neither is in `30.0.1`. `FollowTargetButton` is the one that
|
|
56
|
+
usually surfaces first, because it is on the follow button most apps render.
|
|
57
|
+
|
|
58
|
+
Nothing warns you, and it is not that the warning is easy to miss — **no warning
|
|
59
|
+
is possible.** Every `@oxyhq/services` below 30 declares its Bloom peer as
|
|
60
|
+
`">=0.59.0"` (or looser), an unbounded range that `1.0.0` satisfies. There is no
|
|
61
|
+
mismatch for a package manager to report. `@oxyhq/bloom` declares no `@oxyhq/*`
|
|
62
|
+
peer of its own, so it cannot report one either. The failure appears at bundle
|
|
63
|
+
time as an unresolved module — on web, typically a white screen.
|
|
64
|
+
|
|
65
|
+
### `@oxyhq/services` alone breaks against an old `@oxyhq/core`
|
|
66
|
+
|
|
67
|
+
`@oxyhq/services@30.0.1` names **12 `@oxyhq/core` symbols across 10 of its own
|
|
68
|
+
source files that do not exist in `@oxyhq/core@20.1.0`**: `DeviceContext`,
|
|
69
|
+
`SharedDeviceCredential`, `SharedDeviceCredentialRead`,
|
|
70
|
+
`SharedDeviceCredentialStore`, `SwitcherContextRow`, `SwitcherPrincipalRow`,
|
|
71
|
+
`buildSwitcherRows`, `createSharedMirroringAuthStateStore`,
|
|
72
|
+
`normalizeSharedDeviceSessionRead`, `projectDevicePrincipals`,
|
|
73
|
+
`resolveActiveContext` and `showsPrincipalHeaders`. All 144 core bindings
|
|
74
|
+
services imports exist in `21.0.0`.
|
|
75
|
+
|
|
76
|
+
This is not new in 30 — `@oxyhq/services@29.0.0` names the same 12. The core
|
|
77
|
+
floor moved to `^21.0.0` in services 29, so if you are skipping several majors
|
|
78
|
+
you are crossing it whichever version you land on.
|
|
79
|
+
|
|
80
|
+
Here the package manager does speak, but only just:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
warn: incorrect peer dependency "@oxyhq/core@20.1.0"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
One line, naming the version but not which package wanted what, on the run that
|
|
87
|
+
actually resolves. Re-run `bun install` against a lockfile that already
|
|
88
|
+
satisfies the tree and you get `Checked N installs ... (no changes)` and no
|
|
89
|
+
warning at all — which is the state most people are in by the time they look.
|
|
90
|
+
|
|
91
|
+
<Callout variant="warning" title="Do not install `@oxyhq/services@30.0.0`">
|
|
92
|
+
It is deprecated on npm: it published without `lib/`, so 26 of its 33 exports
|
|
93
|
+
targets are missing. `30.0.1` is the version.
|
|
94
|
+
</Callout>
|
|
95
|
+
|
|
96
|
+
### Whether your typecheck can see any of this
|
|
97
|
+
|
|
98
|
+
`@oxyhq/services` publishes its main entry with a `react-native` condition that
|
|
99
|
+
is a **bare string** pointing at TypeScript source:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
"exports": { ".": { "react-native": "./src/index.ts", "import": { … }, … } }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
So a project whose `customConditions` include `react-native` resolves
|
|
106
|
+
`@oxyhq/services` to `node_modules/@oxyhq/services/src/index.ts` and typechecks
|
|
107
|
+
the package's own source — the core mismatch lands as errors under
|
|
108
|
+
`node_modules/`, all naming `"@oxyhq/core"` (19 of them for `30.0.1` against
|
|
109
|
+
core `20.1.0`). A project without that condition resolves to
|
|
110
|
+
`lib/typescript/module/index.d.ts` instead, and with `skipLibCheck: true` — the
|
|
111
|
+
default in the Expo and Vite templates — **sees nothing at all: zero errors.**
|
|
112
|
+
Its build is green and its web bundle white-screens.
|
|
113
|
+
|
|
114
|
+
Which side you are on:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# from the package directory, not the monorepo root, and name the tsconfig your
|
|
118
|
+
# typecheck script names — a references-only root tsconfig resolves nothing
|
|
119
|
+
bunx tsc -p tsconfig.json --showConfig | grep customConditions
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`["react-native"]` means your typecheck is a real check. No output means it is
|
|
123
|
+
not, and a green typecheck is not evidence about this upgrade. Expo apps get the
|
|
124
|
+
condition from `expo/tsconfig.base`; DOM/Vite packages generally do not.
|
|
125
|
+
|
|
126
|
+
If you are on the blind side and want one run that can see it, typecheck once
|
|
127
|
+
with `--skipLibCheck false`: services' shipped declarations reference the same
|
|
128
|
+
core symbols, so a stale core produces errors there too (fewer of them — 4 in
|
|
129
|
+
the case measured — but enough to fail the run).
|
|
130
|
+
|
|
131
|
+
### Assert the version that resolved, not the one you declared
|
|
132
|
+
|
|
133
|
+
Two ways a "done" upgrade is not done:
|
|
134
|
+
|
|
135
|
+
- **A `packages/*` workspace glob does not match the repo root manifest.** A
|
|
136
|
+
version pinned in five places, four of them under `packages/`, edits cleanly,
|
|
137
|
+
and `bun install` answers `Checked 1539 installs (no changes)` — the root
|
|
138
|
+
manifest still pins the old major and `node_modules` never moved.
|
|
139
|
+
- **A caret range that a lockfile already satisfies does not re-resolve.**
|
|
140
|
+
|
|
141
|
+
Both are invisible if you read `package.json`. Read what resolved instead, from
|
|
142
|
+
each workspace package:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
bun pm ls | grep -E '@oxyhq/(bloom|services|core)@'
|
|
146
|
+
# or, from the package directory:
|
|
147
|
+
node -e 'for (const p of ["@oxyhq/bloom","@oxyhq/services","@oxyhq/core"]) console.log(p, require(p + "/package.json").version)'
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Expect `1.0.0`, `30.0.1` and `21.0.0`. Anything else means the edit did not land.
|
|
151
|
+
|
|
152
|
+
<Callout variant="info" title="How the numbers on this page were measured">
|
|
153
|
+
Everything above was read out of the published npm tarballs and registry
|
|
154
|
+
metadata for `@oxyhq/bloom@1.0.0`, `@oxyhq/services@29.0.0` and `30.0.1`, and
|
|
155
|
+
`@oxyhq/core@20.1.0` and `21.0.0`, on 2026-08-17. The symbol counts come from
|
|
156
|
+
an AST walk of services' `src/` cross-checked against each core version's
|
|
157
|
+
exported symbol set through the TypeScript checker; the error counts and the
|
|
158
|
+
warning text come from installing that pair into a scratch project and running
|
|
159
|
+
`tsc` and `bun install` against it. Version numbers move — re-read yours with
|
|
160
|
+
the commands above rather than trusting these.
|
|
161
|
+
</Callout>
|
|
162
|
+
|
|
163
|
+
## How this list was derived
|
|
164
|
+
|
|
165
|
+
Not from commit subjects. The published `@oxyhq/bloom@0.87.1` and `0.89.0`
|
|
166
|
+
tarballs were unpacked and their `package.json#exports` compared against this
|
|
167
|
+
build's, and the exported symbol set of each of the 88 typed subpaths was read
|
|
168
|
+
out of `lib/typescript/` on both sides through the TypeScript checker, so a
|
|
169
|
+
re-export barrel is followed rather than guessed. Prop shapes were diffed the
|
|
170
|
+
same way, because a removed prop leaves `ButtonProps` present and its shape
|
|
171
|
+
changed.
|
|
172
|
+
|
|
173
|
+
One correction that matters if you re-run such a census yourself: a symbol moved
|
|
174
|
+
**into a namespace** looks exactly like a deleted symbol when you only read
|
|
175
|
+
top-level module exports. The fifteen colour-engine names below are in that
|
|
176
|
+
class — they are renamed, not removed.
|
|
177
|
+
|
|
178
|
+
## Subpaths removed
|
|
179
|
+
|
|
180
|
+
Four subpaths no longer exist. An import of any of them is a hard resolution
|
|
181
|
+
failure, not a type error.
|
|
182
|
+
|
|
183
|
+
| Removed | Replacement | Why |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `@oxyhq/bloom/menu` | `@oxyhq/bloom/dropdown-menu` | The family was ported to shadcn/react-native-reusables' public API. Every `Menu*` export is `DropdownMenu*`. |
|
|
186
|
+
| `@oxyhq/bloom/collapsible` | `@oxyhq/bloom/accordion` | `Collapsible` called `UIManager.setLayoutAnimationEnabledExperimental(true)` at module scope and reached the root barrel through `export *`, so that side effect ran for every barrel consumer on Android old-arch whether or not the component was used. |
|
|
187
|
+
| `@oxyhq/bloom/grouped-buttons` | `@oxyhq/bloom/settings-list` + `@oxyhq/bloom/item` | The only one of the three row renderers with no `accessibilityRole`, no `accessibilityLabel` and no `aria-disabled`, built on `TouchableOpacity`/`activeOpacity` instead of `useInteractionState`. |
|
|
188
|
+
| `@oxyhq/bloom/pressable-with-hover` | none — inline it | One internal user. It now lives inside `zoomable-image-gallery` as `NavArrow`, the package's only hover-styled control. |
|
|
189
|
+
|
|
190
|
+
### Subpaths added
|
|
191
|
+
|
|
192
|
+
`@oxyhq/bloom/dropdown-menu`, `@oxyhq/bloom/menubar`, `@oxyhq/bloom/radio`,
|
|
193
|
+
`@oxyhq/bloom/aspect-ratio`, and the `@oxyhq/bloom/design-tokens/tokens.json`
|
|
194
|
+
asset (the last already shipped in 0.89.0).
|
|
195
|
+
|
|
196
|
+
## Renamed symbols
|
|
197
|
+
|
|
198
|
+
Mechanical renames — same component, new name.
|
|
199
|
+
|
|
200
|
+
| Old | New | Subpath |
|
|
201
|
+
| --- | --- | --- |
|
|
202
|
+
| `Menu`, `MenuTrigger`, `MenuContent`, `MenuItem`, `MenuGroup` | `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuGroup` | `./dropdown-menu` |
|
|
203
|
+
| `MenuDivider`, `ContextMenuDivider` | `DropdownMenuSeparator`, `ContextMenuSeparator` | `./dropdown-menu`, `./context-menu` |
|
|
204
|
+
| `useMenuControl`, `useMenuContext`, `MenuControlProps` | declared by `dropdown-menu` itself, no longer re-exported from `dialog` under an alias | `./dropdown-menu` |
|
|
205
|
+
| `Addon` | `InputGroupAddon` | `./input-group` |
|
|
206
|
+
| `Provider`, `Outlet` | `PortalProvider`, `PortalOutlet` | `./portal` |
|
|
207
|
+
| `Attachment` | `PromptInputAttachment` | `./prompt-input` |
|
|
208
|
+
| `zIndex` | `Z_INDEX` | `./styles` |
|
|
209
|
+
| `ChipVariant`, `BadgeVariant` | `AccentFill` | `./theme` |
|
|
210
|
+
| `ChipColor`, `BadgeColor` | `AccentTone` | `./theme` |
|
|
211
|
+
|
|
212
|
+
`InputGroup.Addon`, the static alias, is gone too — `InputGroupAddon` is the one
|
|
213
|
+
spelling. No Bloom compound carries a static alias.
|
|
214
|
+
|
|
215
|
+
For `Chip` and `Badge` the accepted **values** are almost unchanged; only the
|
|
216
|
+
type names moved. `BadgeVariant` was already `'solid' | 'subtle' | 'outlined'`,
|
|
217
|
+
which is exactly `AccentFill`, and `BadgeColor` is exactly `AccentTone`. The one
|
|
218
|
+
value that changed is Chip's `'soft'`, which is now `'subtle'`; `AccentTone` also
|
|
219
|
+
adds `'info'` for Chip, which previously had it on `Badge` only.
|
|
220
|
+
|
|
221
|
+
### The colour engine is one namespace
|
|
222
|
+
|
|
223
|
+
Fifteen flat exports of `@oxyhq/bloom/theme` are now members of the `ColorEngine`
|
|
224
|
+
namespace, still published from the same subpath:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
// before
|
|
228
|
+
import { argbFromHex, generateRoleColors, quantizeImage } from '@oxyhq/bloom/theme';
|
|
229
|
+
|
|
230
|
+
// after
|
|
231
|
+
import { ColorEngine } from '@oxyhq/bloom/theme';
|
|
232
|
+
ColorEngine.argbFromHex(...);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Affected: `argbFromHex`, `hexFromArgb`, `argbFromRgb`, `redFromArgb`,
|
|
236
|
+
`greenFromArgb`, `blueFromArgb`, `quantizeImage`, `seedsFromImagePixels`,
|
|
237
|
+
`seedHexFromImagePixels`, `generateRoleColors`, and the types `RoleName`,
|
|
238
|
+
`RoleColors`, `GenerateOptions`, `SchemeVariant`, `AccentSources`.
|
|
239
|
+
|
|
240
|
+
`useTheme`, `BloomThemeProvider` and the tokens stay flat — those are used
|
|
241
|
+
fleet-wide and did not move.
|
|
242
|
+
|
|
243
|
+
### Theme symbols that moved subpath
|
|
244
|
+
|
|
245
|
+
`getPresetVars`, `buildSeedScopeVars`, `SeedScopeOptions` and `ExplicitAccents`
|
|
246
|
+
were reachable from two subpaths. They now live only in the react-free barrel a
|
|
247
|
+
build script can import:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
-import { getPresetVars } from '@oxyhq/bloom/theme';
|
|
251
|
+
+import { getPresetVars } from '@oxyhq/bloom/design-tokens';
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`getPresetVars` and `ExplicitAccents` are also on `@oxyhq/bloom/preset-vars`.
|
|
255
|
+
|
|
256
|
+
### `alert()` and `confirm()` moved to the surface stack
|
|
257
|
+
|
|
258
|
+
`alert()` lived on `./dialog`, `confirm()` on `./alert-dialog`. Both are now on
|
|
259
|
+
`@oxyhq/bloom/surfaces`, along with `AlertButton` and `AlertButtonStyle`.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
-import { alert } from '@oxyhq/bloom/dialog';
|
|
263
|
+
-import { confirm } from '@oxyhq/bloom/alert-dialog';
|
|
264
|
+
+import { alert, confirm } from '@oxyhq/bloom/surfaces';
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Their hosts — `BloomDialogProvider` and `AlertDialogHost` — are **deleted**, not
|
|
268
|
+
renamed. Both were module-scope FIFO queues with a host component each; remove
|
|
269
|
+
them from your provider tree and mount the single `<SurfaceHost>` instead. A
|
|
270
|
+
one-at-a-time queue orders by arrival into its own queue, so it could not layer
|
|
271
|
+
over a surface it did not know about. Consequence: **two `alert()` calls in a row
|
|
272
|
+
now stack rather than queue.**
|
|
273
|
+
|
|
274
|
+
`SurfaceConfirmOptions.message` and `SurfacePromptOptions.message` are now
|
|
275
|
+
`description`, matching `DialogProps`.
|
|
276
|
+
|
|
277
|
+
## Deleted with no replacement
|
|
278
|
+
|
|
279
|
+
| Symbol | Was on | Note |
|
|
280
|
+
| --- | --- | --- |
|
|
281
|
+
| `BloomDialogProvider` | `./dialog` | see above |
|
|
282
|
+
| `AlertDialogHost`, `ConfirmOptions` | `./alert-dialog` | see above |
|
|
283
|
+
| `TooltipSheetCompatProvider` | `./tooltip` | `return <>{children}</>` on both platforms, zero importers — an unfinished port, not a redundant shim |
|
|
284
|
+
| `SkeletonLoadingProps` | `./loading` | `Loading variant="skeleton"` drew static Views under the same name as the `skeleton` family, which has a real shimmer loop |
|
|
285
|
+
| `useSharedInputStyles` | `./text-field` | module-private; was never usable |
|
|
286
|
+
| `useIsFullscreen` | `./prompt-input` | zero callers |
|
|
287
|
+
| `DIALOG_SHEET_BACKDROP_TESTID` | `./dialog` | off the public surface |
|
|
288
|
+
| `usePopoverControl`, `usePopoverContext`, `PopoverControlProps`, `PopoverPlacement`, `PopoverTriggerRenderProps`, `PopoverTriggerState` | `./popover` | Popover took the Radix shape (`open`/`defaultOpen`/`onOpenChange`, `asChild`, `side`/`align`/`sideOffset`); the render-prop trigger is gone |
|
|
289
|
+
| `MenuItemText`, `MenuItemIcon`, `ContextMenuItemText`, `ContextMenuItemIcon` | `./menu`, `./context-menu` | The four menu families share one row vocabulary now. A row is `DropdownMenuItem` / `ContextMenuItem` with its label as children; use `DropdownMenuShortcut` for trailing text and `DropdownMenuLabel` for a group heading. There is no `*ItemText` or `*ItemIcon` in 1.0.0. |
|
|
290
|
+
|
|
291
|
+
## Prop changes
|
|
292
|
+
|
|
293
|
+
| Component | Change |
|
|
294
|
+
| --- | --- |
|
|
295
|
+
| `Button` | `activeOpacity` removed. Press feedback is the library's decision, not a per-call knob. |
|
|
296
|
+
| `Loading` | `variant` no longer accepts `"skeleton"`. Use the `skeleton` family. |
|
|
297
|
+
| `Chip` | `variant="soft"` → `variant="subtle"`, one spelling matching the token family. `color="info"` now exists (it was on `Badge` only). The `onClose` affordance draws `TimesLarge_Stroke2_Corner0_Rounded`, not the `×` glyph. |
|
|
298
|
+
| `Badge` | A `dot` Badge paints the tone's fill whatever the variant — `dot variant="outlined"` used to be a fully transparent circle. |
|
|
299
|
+
| `Popover` | `PopoverContentProps.offset` and `.placement` removed (use `sideOffset` and `side`/`align`); `PopoverProps.control` removed. |
|
|
300
|
+
| `TextField` | `TextFieldIcon` and `TextFieldSuffix` now **throw** outside a `<TextField>`. `TextFieldInput` and `TextFieldLabel` deliberately still work standalone. |
|
|
301
|
+
|
|
302
|
+
## Behavioural changes with no API change
|
|
303
|
+
|
|
304
|
+
These break nothing at compile time and change what you see.
|
|
305
|
+
|
|
306
|
+
- **`Button` heights are now exactly 32 / 36 / 44** (small / medium / large) on
|
|
307
|
+
both platforms and in every variant. Hit slop brings the two smaller sizes up
|
|
308
|
+
to the 44dp touch-target floor.
|
|
309
|
+
- **`Button`'s `primary` and `destructive` fills are a glass material** at fill
|
|
310
|
+
alpha 0.85. `inverse` stays opaque. Full AA cost table in
|
|
311
|
+
[Button](/docs/bloom/main/button).
|
|
312
|
+
- **`AspectRatio` rejects a degenerate ratio** — a `ratio` that is zero,
|
|
313
|
+
negative or non-finite falls back to the default instead of collapsing the
|
|
314
|
+
box.
|
|
315
|
+
- **`ActivityHeatmap`'s default palette declares only the steps it can paint.**
|
|
316
|
+
`DEFAULT_ALPHAS` carried five steps but the first could never be reached.
|
|
317
|
+
Rendered with the default props this is byte-identical to what shipped; if you
|
|
318
|
+
passed your own `levels` **and** relied on the default alphas, re-check the
|
|
319
|
+
pairing.
|
|
320
|
+
- **`AvatarGroup` resolves identity as `displayName?.trim() || handle`.** It used
|
|
321
|
+
`??`, which falls through on null/undefined only, so a whitespace-only
|
|
322
|
+
`displayName` — which the API does send — beat the handle behind it and showed
|
|
323
|
+
the default avatar image.
|
|
324
|
+
- **Popover positions through the shared placement engine.** Its own resolver
|
|
325
|
+
clamped only the top, so a panel that fit neither above nor below its trigger
|
|
326
|
+
ran off the bottom of the viewport.
|
|
327
|
+
- **`Switch`, `Slider` and `DotGridMeter` warn once, in dev only, when they
|
|
328
|
+
mount with no accessible name.** All three draw no text, so nothing in the
|
|
329
|
+
component can supply one. `Switch` and `DotGridMeter` gained an optional
|
|
330
|
+
`accessibilityLabel` prop for it; the warning never fires in production, and
|
|
331
|
+
an empty or whitespace-only name counts as missing, because `aria-label=""`
|
|
332
|
+
names nothing.
|
|
333
|
+
- **The neutral surface ramp was respaced** so adjacent surfaces are actually
|
|
334
|
+
distinguishable in every preset — M3's 2-tone step measured below the
|
|
335
|
+
just-noticeable difference here. If you pinned Bloom token values in your own
|
|
336
|
+
fixtures or screenshots, expect them to move.
|
|
337
|
+
- **`AvatarGroup`'s hover card renders off Metro.** Its web fork named
|
|
338
|
+
`'../portal'` rather than `'../portal/index.web'`, and export conditions do
|
|
339
|
+
not apply to relative specifiers, so Vite/webpack/SSR got the native `Portal`
|
|
340
|
+
— which needs a `PortalOutlet` a web app never mounts — and the card drew
|
|
341
|
+
nothing.
|
|
342
|
+
- **`PromptInput` and `BloomColorScope`/`BloomSeedScope` now work off Metro.**
|
|
343
|
+
Both shipped `.web` forks with no `browser` condition, so Vite/webpack/SSR
|
|
344
|
+
resolved the native build: `onImagePaste` was missing entirely and the colour
|
|
345
|
+
scopes emitted no CSS custom properties at all.
|
|
346
|
+
|
|
347
|
+
## Structural changes that are not API changes
|
|
348
|
+
|
|
349
|
+
Every family is now `index.ts` (a pure barrel) + `<Pascal>.tsx` + `types.ts`,
|
|
350
|
+
hooks are `use-kebab-case.ts`, and prop types live in `types.ts`. **The export
|
|
351
|
+
surface is unchanged by that refactor** — measured symbol-for-symbol across all
|
|
352
|
+
barrels, lost=0 gained=0. You only notice if you deep-import a file path inside
|
|
353
|
+
the package, which is not a supported entry point.
|
|
354
|
+
|
|
355
|
+
## `jest.mock` call sites
|
|
356
|
+
|
|
357
|
+
A renamed subpath breaks `jest.mock('@oxyhq/bloom/<sub>')` silently — the string
|
|
358
|
+
is not an import, so no type checker and no import census sees it.
|
|
359
|
+
|
|
360
|
+
Measured across the Oxy fleet: **131 `jest.mock('@oxyhq/bloom/…')` call sites in
|
|
361
|
+
48 files, referencing 20 distinct subpaths — all 20 still exist in 1.0.0.** No
|
|
362
|
+
mock needs to change. The subpaths mocked are `.`, `./avatar`, `./avatar-group`,
|
|
363
|
+
`./bottom-sheet`, `./button`, `./dialog`, `./hooks`, `./icons`, `./item`,
|
|
364
|
+
`./loading`, `./pressable-scale`, `./search`, `./settings-list`, `./skeleton`,
|
|
365
|
+
`./subtle-hover`, `./switch`, `./tab-bar`, `./text-field`, `./theme`, `./toast`.
|
|
366
|
+
|
|
367
|
+
If you add a mock for `./menu`, `./collapsible`, `./grouped-buttons` or
|
|
368
|
+
`./pressable-with-hover`, it will now silently mock a module that does not exist.
|
|
369
|
+
|
|
370
|
+
## Measured impact across the Oxy fleet
|
|
371
|
+
|
|
372
|
+
<Callout variant="warning" title="This section is a scan of local checkouts, not of what you have installed">
|
|
373
|
+
The counts below come from scanning 15,544 tracked source files in the working
|
|
374
|
+
copies of every repo under `~/Oxy` on one machine. A working copy is whatever
|
|
375
|
+
branch someone left it on, which is not the same as any published version — the
|
|
376
|
+
`OxyHQServices` checkout scanned here sat on a topic branch pinned to
|
|
377
|
+
`@oxyhq/services@26.0.1`, four majors behind the published `30.0.1`, and that
|
|
378
|
+
one stale tree is why an earlier revision of this page named only one of the two
|
|
379
|
+
files services imports from a removed subpath. Treat this as a worked example of
|
|
380
|
+
what an upgrade turns up, not as an inventory of your repo. Scan your own:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
git grep -nE "@oxyhq/bloom/(menu|collapsible|grouped-buttons|pressable-with-hover)['\"]"
|
|
384
|
+
```
|
|
385
|
+
</Callout>
|
|
386
|
+
|
|
387
|
+
As scanned, excluding Bloom's own tree: **68 call sites in 51 files across 15
|
|
388
|
+
repos need an edit** — Alia, Astro, CRM, Clarity, CrowdSource, Homiio, Inbox,
|
|
389
|
+
Mention, Mercaria, Moovo, Noted, OxyHQServices, Station, Syra and website. Four
|
|
390
|
+
of the 68 were imports of a deleted subpath and the other 64 were named
|
|
391
|
+
bindings, so 68 is a floor: the scan missed `FollowTargetButton.tsx` below,
|
|
392
|
+
which does not exist on the branch the `OxyHQServices` checkout was parked on.
|
|
393
|
+
|
|
394
|
+
Imports of a removed subpath fail to resolve. They fall into two groups, and the
|
|
395
|
+
fix is different for each.
|
|
396
|
+
|
|
397
|
+
**In `@oxyhq/services`' own code — fixed by upgrading, not by editing.** Read
|
|
398
|
+
out of the published `29.0.0` tarball rather than from a checkout, so it holds
|
|
399
|
+
for anyone below 30 regardless of what any working copy says:
|
|
400
|
+
|
|
401
|
+
| File in `@oxyhq/services` (any version below 30) | Subpath |
|
|
402
|
+
| --- | --- |
|
|
403
|
+
| `src/ui/components/FollowTargetButton.tsx` | `./menu` |
|
|
404
|
+
| `src/ui/components/authChooser/TroubleDisclosure.tsx` | `./collapsible` |
|
|
405
|
+
|
|
406
|
+
**In app code — you edit these.** Present on `origin/main` of each repo as of
|
|
407
|
+
2026-08-17:
|
|
408
|
+
|
|
409
|
+
| File | Subpath |
|
|
410
|
+
| --- | --- |
|
|
411
|
+
| `Inbox/packages/frontend/components/settings/sections/AccountSection.tsx` | `./grouped-buttons` |
|
|
412
|
+
| `Homiio/packages/frontend/app/host/calendar.tsx` | `./menu` |
|
|
413
|
+
|
|
414
|
+
Named bindings that no longer exist, by symbol:
|
|
415
|
+
|
|
416
|
+
| Symbol | Sites | Fix |
|
|
417
|
+
| --- | --- | --- |
|
|
418
|
+
| `getPresetVars` | 10 | import from `@oxyhq/bloom/design-tokens` |
|
|
419
|
+
| `confirm` (from `./alert-dialog`) | 12 | import from `@oxyhq/bloom/surfaces` |
|
|
420
|
+
| `Provider` / `Outlet` (from `./portal`) | 6 + 6 | `PortalProvider` / `PortalOutlet` |
|
|
421
|
+
| `generateRoleColors` | 6 | `ColorEngine.generateRoleColors` |
|
|
422
|
+
| `BadgeColor` | 5 | `AccentTone` |
|
|
423
|
+
| `alert` (from `./dialog`) | 3 | import from `@oxyhq/bloom/surfaces` |
|
|
424
|
+
| `ChipColor`, `ChipVariant` | 2 + 2 | `AccentTone`, `AccentFill` |
|
|
425
|
+
| `BadgeVariant` | 1 | `AccentFill` |
|
|
426
|
+
| `BloomDialogProvider` | 2 | delete; mount `<SurfaceHost>` |
|
|
427
|
+
| `AlertDialogHost` | 1 | delete; mount `<SurfaceHost>` |
|
|
428
|
+
| `Menu*` + `useMenuControl` (one file, `Syra/packages/frontend/components/MediaCard.tsx`) | 7 | `DropdownMenu*` |
|
|
429
|
+
| `Collapsible` (`Astro/webui/.../collapsible-group.tsx`) | 1 | `Accordion` |
|
|
430
|
+
|
|
431
|
+
`@oxyhq/bloom/icons` is byte-identical between 0.87.1 and 1.0.0 — a census will
|
|
432
|
+
appear to flag three icon names in Bloom's own
|
|
433
|
+
`src/__tests__/icon-references.test.ts`, but those are that suite's own fixture
|
|
434
|
+
strings, not call sites.
|
package/docs/motion.mdx
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Motion
|
|
3
|
+
description: Shared durations and easings — and the three web failure modes that make animation code lie.
|
|
4
|
+
order: 57
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Motion
|
|
8
|
+
|
|
9
|
+
Shared enter/exit presets and a screen transition, so a sheet and a menu on the
|
|
10
|
+
same screen do not open at subtly different speeds.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
import {
|
|
14
|
+
ScaleAndFadeIn,
|
|
15
|
+
ScaleAndFadeOut,
|
|
16
|
+
ShrinkAndPop,
|
|
17
|
+
ScreenTransition,
|
|
18
|
+
} from '@oxyhq/bloom/motion';
|
|
19
|
+
|
|
20
|
+
<Animated.View entering={ScaleAndFadeIn} exiting={ScaleAndFadeOut}>
|
|
21
|
+
<Panel />
|
|
22
|
+
</Animated.View>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The value of shared presets is not the numbers — it is that a change to "how
|
|
26
|
+
fast things feel" happens in one place rather than being renegotiated per
|
|
27
|
+
component. It is also where the web rules below are already applied, which is
|
|
28
|
+
the stronger reason to use them rather than hand-writing an enter.
|
|
29
|
+
|
|
30
|
+
## Three ways animation silently does nothing on web
|
|
31
|
+
|
|
32
|
+
These are not Bloom quirks; they are how Reanimated behaves in the
|
|
33
|
+
configuration every Oxy web app ships. Each fails with **no console output**.
|
|
34
|
+
|
|
35
|
+
### An animation started from a mapper never ticks
|
|
36
|
+
|
|
37
|
+
Returning `withTiming` / `withRepeat` from `useDerivedValue` or
|
|
38
|
+
`useAnimatedStyle` does nothing on web. Drive a shared value **imperatively**;
|
|
39
|
+
`useAnimatedStyle` only ever READS.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
// ✅ someone calls this
|
|
43
|
+
progress.value = withTiming(1, { duration: 200 });
|
|
44
|
+
|
|
45
|
+
const style = useAnimatedStyle(() => ({ opacity: progress.value }), [progress]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Without the worklets Babel plugin, mappers re-run on their DEPS ARRAY
|
|
49
|
+
|
|
50
|
+
Not on auto-tracked reads. Omit an animating shared value from the deps and the
|
|
51
|
+
mapper runs once and freezes at frame 1. **List every shared value the mapper
|
|
52
|
+
reads** — note the `[progress]` above, which is doing real work.
|
|
53
|
+
|
|
54
|
+
`useAnimatedReaction` does not fix this: key it on a changing prop, not on a
|
|
55
|
+
shared value.
|
|
56
|
+
|
|
57
|
+
### A custom `Keyframe` as `entering` freezes the element
|
|
58
|
+
|
|
59
|
+
An animation name absent from Reanimated's built-in map also schedules a
|
|
60
|
+
cleanup that pins the element — `position: absolute` plus a frozen box — at
|
|
61
|
+
`duration × 5`. It is a layout bug with no error, and `exiting` is unaffected,
|
|
62
|
+
so it looks intermittent.
|
|
63
|
+
|
|
64
|
+
## Bloom's rule: pick the mechanism per DIRECTION
|
|
65
|
+
|
|
66
|
+
- **`entering`** runs on the real element. It is EITHER a predefined builder
|
|
67
|
+
(`FadeIn`, `SlideInUp`) OR driven imperatively from a shared value. Never a
|
|
68
|
+
`Keyframe`, never a custom builder.
|
|
69
|
+
- **`exiting`** runs on a throwaway clone, so a custom `Keyframe` is safe there —
|
|
70
|
+
and is the only way to express a multi-property, multi-stop shape.
|
|
71
|
+
|
|
72
|
+
Consequences to accept rather than work around: a predefined builder cannot
|
|
73
|
+
combine fade with scale, so a web enter drops one; and `Keyframe` has no
|
|
74
|
+
`.easing()`, so web keyframes run linear unless a per-stop easing happens to
|
|
75
|
+
resolve to one of Reanimated's built-in names. Add intermediate stops if the
|
|
76
|
+
curve matters.
|
|
77
|
+
|
|
78
|
+
## Verifying
|
|
79
|
+
|
|
80
|
+
Jest and `tsc` catch none of these. Verify in a real **foregrounded** tab — a
|
|
81
|
+
hidden tab freezes plain CSS transitions too, which mimics "the class didn't
|
|
82
|
+
apply". Read the class attribute, or set `transitionProperty: 'none'` before
|
|
83
|
+
reading computed styles.
|
package/docs/overlay.mdx
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Overlay
|
|
3
|
+
description: How a portaled surface takes its place in the stack — and the pointer-events rule that fails totally and silently.
|
|
4
|
+
order: 37
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Overlay
|
|
8
|
+
|
|
9
|
+
`overlay/` exports `OverlayRoot` and `Backdrop`: the one way a portaled surface
|
|
10
|
+
establishes its interactive root, its position in the stack, and its
|
|
11
|
+
press-to-dismiss dim. Do not hand-roll either.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { OverlayRoot, Backdrop } from '@oxyhq/bloom/overlay';
|
|
15
|
+
|
|
16
|
+
{open ? (
|
|
17
|
+
<Portal>
|
|
18
|
+
<OverlayRoot>
|
|
19
|
+
<Backdrop onPress={close} progress={progress} />
|
|
20
|
+
<Panel />
|
|
21
|
+
</OverlayRoot>
|
|
22
|
+
</Portal>
|
|
23
|
+
) : null}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## A surface opened LATER paints above one opened earlier
|
|
27
|
+
|
|
28
|
+
That is decided once, in `overlay/stack.ts`, and applied by `OverlayRoot`.
|
|
29
|
+
**Never give an overlay surface a `zIndex` of its own.**
|
|
30
|
+
|
|
31
|
+
A per-component constant answers "which is on top" by what a surface *is*
|
|
32
|
+
rather than by when it was opened, so some pairings end up permanently
|
|
33
|
+
inverted — a menu that can never appear above a dialog, whatever order the user
|
|
34
|
+
opened them in.
|
|
35
|
+
|
|
36
|
+
To add a surface: render its portal body inside `<OverlayRoot>`, and put that
|
|
37
|
+
`OverlayRoot` **inside the guard that makes the surface appear**. The rank is
|
|
38
|
+
taken on mount, so the depth is right on the first paint. Order the parts
|
|
39
|
+
*within* one surface using `useOverlayLayerContext()`.
|
|
40
|
+
|
|
41
|
+
Toasts are the one deliberate exception: `ToastHost` mounts for the app's whole
|
|
42
|
+
life, so taking a rank would wedge the counter, and it passes an explicit
|
|
43
|
+
`zIndex` instead.
|
|
44
|
+
|
|
45
|
+
## The `pointerEvents` rule
|
|
46
|
+
|
|
47
|
+
`box-none` and `box-only` are React Native-only values and **must be passed as
|
|
48
|
+
a prop, never inside a `style` object**.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<View pointerEvents="box-none" style={styles.root} /> // ✅
|
|
52
|
+
<View style={[styles.root, { pointerEvents: 'box-none' }]} /> // ✗ silently dropped
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
react-native-web resolves those two values from the prop path only; as a style
|
|
56
|
+
entry they are dropped with no warning. `'auto'` and `'none'` *do* survive as
|
|
57
|
+
styles, which is exactly what makes the mistake easy to make and hard to see.
|
|
58
|
+
|
|
59
|
+
Why it is catastrophic: the web `Portal` root is `pointer-events: none`, and the
|
|
60
|
+
property **inherits**. A dropped opt-in makes the entire surface click-through —
|
|
61
|
+
backdrops do not dismiss, buttons do not press, clicks land on the app behind.
|
|
62
|
+
Escape still works, so it presents as a dismissal bug rather than as a
|
|
63
|
+
pointer-events bug. Outside a portal the same mistake inverts, and a full-bleed
|
|
64
|
+
band silently eats presses.
|
|
65
|
+
|
|
66
|
+
Verify dismissal in a real browser. Jest cannot see inheritance through a
|
|
67
|
+
portal, and the markup is valid either way.
|
|
68
|
+
|
|
69
|
+
## Not everything overlay-shaped is in the stack
|
|
70
|
+
|
|
71
|
+
The **web** tooltip is not portaled and has no rank; the native one is and does.
|
|
72
|
+
Check for a `Portal` before assuming a surface participates.
|
|
73
|
+
|
|
74
|
+
On native there is a second mechanism z-index cannot reach: each of these
|
|
75
|
+
surfaces is its own RN `<Modal>` window.
|
package/docs/popover.mdx
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Popover
|
|
3
|
+
description: Arbitrary content anchored to a trigger — a sheet on native, a panel on web.
|
|
4
|
+
order: 21
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Popover
|
|
8
|
+
|
|
9
|
+
A small surface of arbitrary content, anchored to what opened it. An anchored panel on web, a bottom sheet on native.
|
|
10
|
+
|
|
11
|
+
Three parts, shadcn's own: `Popover`, `PopoverTrigger`, `PopoverContent`.
|
|
12
|
+
|
|
13
|
+
Reach for it when the content is not a list of actions. When it IS a list of actions, reach for [DropdownMenu](/docs/bloom/main/dropdown-menu) — it gives you the rows, the keyboard state and the ARIA roles for free.
|
|
14
|
+
|
|
15
|
+
## Basic
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Popover, PopoverContent, PopoverTrigger } from '@oxyhq/bloom/popover';
|
|
19
|
+
import { Button } from '@oxyhq/bloom/button';
|
|
20
|
+
import { P } from '@oxyhq/bloom/typography';
|
|
21
|
+
|
|
22
|
+
<Popover>
|
|
23
|
+
<PopoverTrigger asChild label="What is this?">
|
|
24
|
+
<Button variant="secondary">What is this?</Button>
|
|
25
|
+
</PopoverTrigger>
|
|
26
|
+
|
|
27
|
+
<PopoverContent label="Two-factor authentication">
|
|
28
|
+
<View style={{ padding: 16, gap: 8 }}>
|
|
29
|
+
<P>A second step when you sign in, so a leaked password is not enough on its own.</P>
|
|
30
|
+
</View>
|
|
31
|
+
</PopoverContent>
|
|
32
|
+
</Popover>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## The panel has no padding of its own
|
|
36
|
+
|
|
37
|
+
Deliberate, and worth knowing before you wonder where the inset went. A popover's body is as often a list of `Item` rows (the combobox, the dialog header's overflow) as it is prose, and rows have to reach the panel edge to show their full-width highlight. So the panel keeps only the 4px vertical rhythm every anchored Bloom surface has, and a prose popover pads its own body — as the example above does.
|
|
38
|
+
|
|
39
|
+
## Controlled
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
const [open, setOpen] = useState(false);
|
|
43
|
+
|
|
44
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Uncontrolled by default. `onOpenChange` fires either way. Closing it from inside its own content is a `setOpen(false)`.
|
|
48
|
+
|
|
49
|
+
## Placement (web)
|
|
50
|
+
|
|
51
|
+
`PopoverContent` takes `side` (`'top' | 'bottom'`, default `'bottom'`), `align` (`'start' | 'center' | 'end'`, default `'center'`), `sideOffset`, `alignOffset`, `minWidth` and `maxWidth`. The surface flips to the other side when the named one does not fit and clamps into the viewport when neither does.
|
|
52
|
+
|
|
53
|
+
A popover centres on its trigger by default, where a menu starts at its left edge — that is the one placement default the two families disagree on, and it matches shadcn.
|
|
54
|
+
|
|
55
|
+
## Shape
|
|
56
|
+
|
|
57
|
+
shadcn's popover is `w-72 p-4`: a fixed 288px card with a 16px inset, not a shrink-wrap around its content. Bloom applies both, first in the style array, so a body that is a list of rows rather than prose overrides them from `style` — the same thing a shadcn call site says with `className="w-auto p-0"`:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<PopoverContent label="Actions" style={{ width: 'auto', padding: 4 }}>
|
|
61
|
+
<Item title="Profile" onPress={openProfile} />
|
|
62
|
+
</PopoverContent>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Rows have to reach the panel's edge to draw a full-width highlight, which is why `Combobox` and the dialog header's overflow menu both do this.
|
|
66
|
+
|
|
67
|
+
On native these are ignored; the sheet is anchored to the screen.
|
|
68
|
+
|
|
69
|
+
## Dismissal
|
|
70
|
+
|
|
71
|
+
Press outside or hit Escape. `dismissible={false}` turns both off, for a popover the user has to answer.
|
|
72
|
+
|
|
73
|
+
Escape stops at the innermost open surface, so a popover opened from inside a dialog closes the popover and leaves the dialog up.
|