@oxyhq/bloom 0.73.0 → 0.74.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/lib/commonjs/button/Button.js +104 -42
- package/lib/commonjs/button/Button.js.map +1 -1
- package/lib/commonjs/button/Button.web.js +11 -3
- package/lib/commonjs/button/Button.web.js.map +1 -1
- package/lib/commonjs/design-tokens/theme.css +22 -0
- package/lib/commonjs/fab/Fab.js +9 -0
- package/lib/commonjs/fab/Fab.js.map +1 -1
- package/lib/commonjs/fab/Fab.web.js +11 -0
- package/lib/commonjs/fab/Fab.web.js.map +1 -1
- package/lib/commonjs/theme/BloomThemeProvider.js.map +1 -1
- package/lib/commonjs/theme/build-theme-from-seed.js +7 -5
- package/lib/commonjs/theme/build-theme-from-seed.js.map +1 -1
- package/lib/commonjs/theme/build-theme.js +9 -4
- package/lib/commonjs/theme/build-theme.js.map +1 -1
- package/lib/commonjs/theme/color-policy.js +315 -0
- package/lib/commonjs/theme/color-policy.js.map +1 -0
- package/lib/commonjs/theme/color-scope/seed-scope.js +13 -1
- package/lib/commonjs/theme/color-scope/seed-scope.js.map +1 -1
- package/lib/commonjs/theme/color-scope/style-builder.js +6 -0
- package/lib/commonjs/theme/color-scope/style-builder.js.map +1 -1
- package/lib/commonjs/theme/preset-vars.js +9 -1
- package/lib/commonjs/theme/preset-vars.js.map +1 -1
- package/lib/commonjs/theme/token-registry.js +11 -1
- package/lib/commonjs/theme/token-registry.js.map +1 -1
- package/lib/module/button/Button.js +104 -42
- package/lib/module/button/Button.js.map +1 -1
- package/lib/module/button/Button.web.js +11 -3
- package/lib/module/button/Button.web.js.map +1 -1
- package/lib/module/design-tokens/theme.css +22 -0
- package/lib/module/fab/Fab.js +9 -0
- package/lib/module/fab/Fab.js.map +1 -1
- package/lib/module/fab/Fab.web.js +11 -0
- package/lib/module/fab/Fab.web.js.map +1 -1
- package/lib/module/theme/BloomThemeProvider.js.map +1 -1
- package/lib/module/theme/build-theme-from-seed.js +7 -5
- package/lib/module/theme/build-theme-from-seed.js.map +1 -1
- package/lib/module/theme/build-theme.js +9 -4
- package/lib/module/theme/build-theme.js.map +1 -1
- package/lib/module/theme/color-policy.js +310 -0
- package/lib/module/theme/color-policy.js.map +1 -0
- package/lib/module/theme/color-scope/seed-scope.js +13 -1
- package/lib/module/theme/color-scope/seed-scope.js.map +1 -1
- package/lib/module/theme/color-scope/style-builder.js +6 -0
- package/lib/module/theme/color-scope/style-builder.js.map +1 -1
- package/lib/module/theme/preset-vars.js +9 -1
- package/lib/module/theme/preset-vars.js.map +1 -1
- package/lib/module/theme/token-registry.js +11 -1
- package/lib/module/theme/token-registry.js.map +1 -1
- package/lib/typescript/commonjs/button/Button.d.ts.map +1 -1
- package/lib/typescript/commonjs/button/Button.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/button/types.d.ts +8 -0
- package/lib/typescript/commonjs/button/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/fab/Fab.d.ts.map +1 -1
- package/lib/typescript/commonjs/fab/Fab.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/fab/types.d.ts +6 -1
- package/lib/typescript/commonjs/fab/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/icons/common.d.ts +1 -1
- package/lib/typescript/commonjs/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/build-theme-from-seed.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/build-theme.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-policy.d.ts +48 -0
- package/lib/typescript/commonjs/theme/color-policy.d.ts.map +1 -0
- package/lib/typescript/commonjs/theme/color-scope/seed-scope.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/color-scope/style-builder.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/preset-vars.d.ts.map +1 -1
- package/lib/typescript/commonjs/theme/token-registry.d.ts +1 -1
- package/lib/typescript/commonjs/theme/token-registry.d.ts.map +1 -1
- package/lib/typescript/module/button/Button.d.ts.map +1 -1
- package/lib/typescript/module/button/Button.web.d.ts.map +1 -1
- package/lib/typescript/module/button/types.d.ts +8 -0
- package/lib/typescript/module/button/types.d.ts.map +1 -1
- package/lib/typescript/module/fab/Fab.d.ts.map +1 -1
- package/lib/typescript/module/fab/Fab.web.d.ts.map +1 -1
- package/lib/typescript/module/fab/types.d.ts +6 -1
- package/lib/typescript/module/fab/types.d.ts.map +1 -1
- package/lib/typescript/module/icons/common.d.ts +1 -1
- package/lib/typescript/module/theme/BloomThemeProvider.d.ts.map +1 -1
- package/lib/typescript/module/theme/build-theme-from-seed.d.ts.map +1 -1
- package/lib/typescript/module/theme/build-theme.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-policy.d.ts +48 -0
- package/lib/typescript/module/theme/color-policy.d.ts.map +1 -0
- package/lib/typescript/module/theme/color-scope/seed-scope.d.ts.map +1 -1
- package/lib/typescript/module/theme/color-scope/style-builder.d.ts.map +1 -1
- package/lib/typescript/module/theme/preset-vars.d.ts.map +1 -1
- package/lib/typescript/module/theme/token-registry.d.ts +1 -1
- package/lib/typescript/module/theme/token-registry.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/Button.test.tsx +166 -1
- package/src/__tests__/Button.web.test.tsx +16 -0
- package/src/__tests__/preset-vars.test.ts +27 -15
- package/src/button/Button.tsx +123 -40
- package/src/button/Button.web.tsx +11 -3
- package/src/button/types.ts +8 -0
- package/src/design-tokens/theme.css +22 -0
- package/src/fab/Fab.tsx +3 -0
- package/src/fab/Fab.web.tsx +3 -0
- package/src/fab/types.ts +6 -1
- package/src/theme/BloomThemeProvider.tsx +7 -4
- package/src/theme/__tests__/__fixtures__/golden-resolved-tokens.json +1300 -312
- package/src/theme/__tests__/__snapshots__/visual-gallery.test.tsx.snap +442 -442
- package/src/theme/__tests__/policy-legibility.test.ts +118 -0
- package/src/theme/__tests__/theme-colors-parity.test.ts +11 -6
- package/src/theme/build-theme-from-seed.ts +7 -4
- package/src/theme/build-theme.ts +9 -4
- package/src/theme/color-policy.ts +342 -0
- package/src/theme/color-scope/seed-scope.ts +13 -1
- package/src/theme/color-scope/style-builder.ts +6 -0
- package/src/theme/preset-vars.ts +10 -1
- package/src/theme/token-registry.ts +32 -0
- package/lib/commonjs/fonts/font-data.web.js +0 -22
- package/lib/commonjs/fonts/font-data.web.js.map +0 -1
- package/lib/module/fonts/font-data.web.js +0 -18
- package/lib/module/fonts/font-data.web.js.map +0 -1
- package/lib/typescript/commonjs/fonts/font-data.web.d.ts +0 -5
- package/lib/typescript/commonjs/fonts/font-data.web.d.ts.map +0 -1
- package/lib/typescript/module/fonts/font-data.web.d.ts +0 -5
- package/lib/typescript/module/fonts/font-data.web.d.ts.map +0 -1
- package/src/fonts/font-data.web.ts +0 -15
|
@@ -56,11 +56,23 @@ describe('getPresetVars (engine-backed)', () => {
|
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
// `--ring` follows the TEXT accent, not the fill. A focus ring is drawn on the
|
|
60
|
+
// page next to a control, so it needs the tone that is legible there — which is
|
|
61
|
+
// the job `--primary-text` exists for. Equating it with `--primary` was the same
|
|
62
|
+
// conflation that made the fill go pastel to stay readable as text.
|
|
63
|
+
it('drives sidebar-primary / ring from the legible brand accent', () => {
|
|
60
64
|
const vars = getPresetVars('blue', 'light');
|
|
61
|
-
expect(vars['--
|
|
62
|
-
expect(vars['--
|
|
63
|
-
|
|
65
|
+
expect(vars['--ring']).toBe(vars['--primary-text']);
|
|
66
|
+
expect(vars['--sidebar-primary']).toBe(vars['--primary-text']);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// The chart ramp is no longer drawn from the primary/secondary/tertiary trio,
|
|
70
|
+
// which spans ~35 degrees of hue and left adjacent series indistinguishable
|
|
71
|
+
// (minimum ΔE 6.2 across the presets). Five hues, evenly spread, one tone.
|
|
72
|
+
it('spreads the chart ramp across distinct hues', () => {
|
|
73
|
+
const vars = getPresetVars('blue', 'light');
|
|
74
|
+
const series = [1, 2, 3, 4, 5].map((i) => vars[`--chart-${i}`]);
|
|
75
|
+
expect(new Set(series).size).toBe(series.length);
|
|
64
76
|
});
|
|
65
77
|
|
|
66
78
|
it('makes secondary a real contrast color, not a mirror of primary (the fix)', () => {
|
|
@@ -72,19 +84,19 @@ describe('getPresetVars (engine-backed)', () => {
|
|
|
72
84
|
}
|
|
73
85
|
});
|
|
74
86
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
// Deliberately inverted from what this asserted before: M3's
|
|
88
|
+
// `surfaceContainerLowest` is the RECESSED step, so in dark a card SANK into
|
|
89
|
+
// the page rather than lifting off it — on all thirteen presets, which is what
|
|
90
|
+
// made it a mapping problem and not a seed one.
|
|
91
|
+
it('makes card lift off the background in BOTH modes', () => {
|
|
92
|
+
const sum = (rgb: string): number =>
|
|
93
|
+
(rgb.match(/\d+/g) ?? []).map(Number).reduce((a, b) => a + b, 0);
|
|
79
94
|
|
|
80
95
|
for (const name of APP_COLOR_NAMES) {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const dark = getPresetVars(name, 'dark');
|
|
86
|
-
// card must be darker than (or equal to) background in dark — correct inversion.
|
|
87
|
-
expect(sum(dark['--card'] ?? '')).toBeLessThanOrEqual(sum(dark['--background'] ?? ''));
|
|
96
|
+
for (const mode of ['light', 'dark'] as const) {
|
|
97
|
+
const vars = getPresetVars(name, mode);
|
|
98
|
+
expect(sum(vars['--card'] ?? '')).toBeGreaterThan(sum(vars['--background'] ?? ''));
|
|
99
|
+
}
|
|
88
100
|
}
|
|
89
101
|
});
|
|
90
102
|
|
package/src/button/Button.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useMemo, memo } from 'react';
|
|
1
|
+
import React, { useMemo, memo, type ComponentType } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
ActivityIndicator,
|
|
4
4
|
Animated,
|
|
@@ -7,9 +7,12 @@ import {
|
|
|
7
7
|
StyleSheet,
|
|
8
8
|
Text,
|
|
9
9
|
View,
|
|
10
|
+
type PressableProps,
|
|
11
|
+
type StyleProp,
|
|
10
12
|
type TextStyle,
|
|
11
13
|
type ViewStyle,
|
|
12
14
|
} from 'react-native';
|
|
15
|
+
import { styled } from 'react-native-css';
|
|
13
16
|
|
|
14
17
|
import { useTheme } from '../theme/use-theme';
|
|
15
18
|
import { usePressAnimation } from '../hooks/usePressAnimation';
|
|
@@ -69,6 +72,73 @@ const PILL_RADIUS = 999;
|
|
|
69
72
|
const PRESS_SCALE = 0.97;
|
|
70
73
|
const SCALE_VARIANTS = new Set<string>(['primary', 'secondary', 'inverse']);
|
|
71
74
|
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// The button renders ONE node — the same shape the web fork renders (one
|
|
77
|
+
// `<button>`).
|
|
78
|
+
//
|
|
79
|
+
// It used to be two: an unstyled `Animated.View` holding the press-scale
|
|
80
|
+
// transform, wrapping the `Pressable` that carried `className`, `style` and
|
|
81
|
+
// every visual. That wrapper — not the pressable — was the flex child of
|
|
82
|
+
// whatever laid the button out, and it hugged its content, so a caller's
|
|
83
|
+
// `className="flex-1"` (and `style={{ alignSelf }}`, and every other layout
|
|
84
|
+
// declaration) applied INSIDE a box that never grew. Nothing errored; the same
|
|
85
|
+
// call site behaved on web and did nothing on native, and consumers worked
|
|
86
|
+
// around it by wrapping every Bloom `Button` in their own `<View>`.
|
|
87
|
+
//
|
|
88
|
+
// Collapsing the two nodes settles "which node does a class belong on?" by
|
|
89
|
+
// removing the choice: layout and visuals are one box here exactly as they are
|
|
90
|
+
// in the DOM, so a layout class and a visual class cannot land in different
|
|
91
|
+
// places. The touch target now scales with the press (0.97 — ~1px on a 40dp
|
|
92
|
+
// button, and the press is registered before it starts), the same trade
|
|
93
|
+
// `PressableScale` made when it dropped its own two-node structure.
|
|
94
|
+
//
|
|
95
|
+
// `styled()` comes from Bloom's own `react-native-css` dependency rather than
|
|
96
|
+
// passing `className` as a bare prop and hoping the consumer's NativeWind
|
|
97
|
+
// interop recognises a component Bloom built at module scope. Same reasoning,
|
|
98
|
+
// and the same silent failure if skipped, as `typography/styled-text.ts`.
|
|
99
|
+
//
|
|
100
|
+
// Both wrappers are built ONCE at module scope so the element type is stable
|
|
101
|
+
// across renders — one built during render remounts the subtree every time.
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Exactly the prop surface `Button` hands to the pressable, and no wider.
|
|
106
|
+
* `Animated.createAnimatedComponent` maps `WithAnimatedValue` over every prop of
|
|
107
|
+
* the component it wraps, and doing that to the whole of `PressableProps`
|
|
108
|
+
* overflows the checker (`TS2590: union type that is too complex to represent`).
|
|
109
|
+
* Narrowing is also honest: the function form of `style` is deliberately
|
|
110
|
+
* excluded — see the `useInteractionState` comment in the component body for why
|
|
111
|
+
* Bloom never uses it.
|
|
112
|
+
*/
|
|
113
|
+
type ButtonPressableProps = Pick<
|
|
114
|
+
PressableProps,
|
|
115
|
+
| 'accessibilityHint'
|
|
116
|
+
| 'accessibilityLabel'
|
|
117
|
+
| 'accessibilityRole'
|
|
118
|
+
| 'accessibilityState'
|
|
119
|
+
| 'children'
|
|
120
|
+
| 'className'
|
|
121
|
+
| 'disabled'
|
|
122
|
+
| 'hitSlop'
|
|
123
|
+
| 'onPress'
|
|
124
|
+
| 'onPressIn'
|
|
125
|
+
| 'onPressOut'
|
|
126
|
+
| 'testID'
|
|
127
|
+
> & { style?: StyleProp<ViewStyle> };
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The same `Pressable` object, typed down to {@link ButtonPressableProps}.
|
|
131
|
+
* `styled()` derives its mapping type from a dot-path union over the wrapped
|
|
132
|
+
* component's ENTIRE prop type, and the one over `PressableProps` also overflows
|
|
133
|
+
* the checker. Nothing changes at runtime — this is the identical component.
|
|
134
|
+
*/
|
|
135
|
+
const ButtonPressable: ComponentType<ButtonPressableProps> = Pressable;
|
|
136
|
+
|
|
137
|
+
const StyledPressable: ComponentType<ButtonPressableProps> = styled(ButtonPressable, {
|
|
138
|
+
className: 'style',
|
|
139
|
+
});
|
|
140
|
+
const AnimatedPressable = Animated.createAnimatedComponent(StyledPressable);
|
|
141
|
+
|
|
72
142
|
const ButtonComponent: React.FC<ButtonProps> = ({
|
|
73
143
|
onPress,
|
|
74
144
|
children,
|
|
@@ -170,6 +240,16 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
170
240
|
styles.borderRadius = PILL_RADIUS;
|
|
171
241
|
break;
|
|
172
242
|
case 'icon':
|
|
243
|
+
// Resolved tokens, not `className="bg-background border border-border"`.
|
|
244
|
+
// The class form made the caller's `className` and the icon's own chrome
|
|
245
|
+
// compete for one slot — `<IconButton className="flex-1" />` replaced the
|
|
246
|
+
// default and shipped a transparent, borderless icon button — and it left
|
|
247
|
+
// the chrome dependent on the consumer's Tailwind pipeline resolving
|
|
248
|
+
// Bloom's color names. These are the SAME tokens `Button.web.tsx`'s
|
|
249
|
+
// `resolveVariantStyle('icon')` already writes inline.
|
|
250
|
+
styles.backgroundColor = theme.colors.background;
|
|
251
|
+
styles.borderWidth = 1;
|
|
252
|
+
styles.borderColor = theme.colors.border;
|
|
173
253
|
styles.borderRadius = PILL_RADIUS;
|
|
174
254
|
styles.padding = 8;
|
|
175
255
|
styles.width = sizeConfig.minHeight;
|
|
@@ -220,7 +300,6 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
220
300
|
|
|
221
301
|
const defaultHitSlop = variant === 'icon' ? ICON_HIT_SLOP : undefined;
|
|
222
302
|
const resolvedActiveOpacity = activeOpacity ?? (variant === 'icon' ? 0.7 : 0.8);
|
|
223
|
-
const resolvedClassName = className ?? (variant === 'icon' ? 'bg-background border border-border' : undefined);
|
|
224
303
|
|
|
225
304
|
const content = (
|
|
226
305
|
<>
|
|
@@ -233,44 +312,48 @@ const ButtonComponent: React.FC<ButtonProps> = ({
|
|
|
233
312
|
);
|
|
234
313
|
|
|
235
314
|
return (
|
|
236
|
-
<
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
315
|
+
<AnimatedPressable
|
|
316
|
+
className={className}
|
|
317
|
+
style={[
|
|
318
|
+
baseStyles,
|
|
319
|
+
disabled && !loading && { opacity: 0.5 },
|
|
320
|
+
pressed && !hasScaleFeedback && !isInteractionBlocked && { opacity: resolvedActiveOpacity },
|
|
321
|
+
// Before the caller's `style`, so `style` keeps winning the whole array
|
|
322
|
+
// the way it always has (and the way the web fork spreads it last). The
|
|
323
|
+
// cost is that a caller who sets `transform` on a scale variant replaces
|
|
324
|
+
// the press scale instead of composing with it — when the transform sat
|
|
325
|
+
// on a separate wrapper node the two multiplied.
|
|
326
|
+
hasScaleFeedback ? { transform: [{ scale: scaleAnim }] } : null,
|
|
327
|
+
style,
|
|
328
|
+
]}
|
|
329
|
+
onPress={isInteractionBlocked ? undefined : onPress}
|
|
330
|
+
onPressIn={handlePressIn}
|
|
331
|
+
onPressOut={handlePressOut}
|
|
332
|
+
disabled={isInteractionBlocked}
|
|
333
|
+
hitSlop={hitSlop ?? defaultHitSlop}
|
|
334
|
+
accessibilityLabel={accessibilityLabel}
|
|
335
|
+
accessibilityHint={accessibilityHint}
|
|
336
|
+
accessibilityRole="button"
|
|
337
|
+
accessibilityState={loading ? { disabled: isInteractionBlocked, busy: true } : { disabled: isInteractionBlocked }}
|
|
338
|
+
testID={testID}
|
|
339
|
+
>
|
|
340
|
+
{loading ? (
|
|
341
|
+
<>
|
|
342
|
+
<View
|
|
343
|
+
style={styles.loadingHiddenContent}
|
|
344
|
+
importantForAccessibility="no-hide-descendants"
|
|
345
|
+
accessibilityElementsHidden
|
|
346
|
+
>
|
|
347
|
+
{content}
|
|
348
|
+
</View>
|
|
349
|
+
<View style={styles.loadingOverlay}>
|
|
350
|
+
<ActivityIndicator size="small" color={loadingColor ?? resolvedTextColor} />
|
|
351
|
+
</View>
|
|
352
|
+
</>
|
|
353
|
+
) : (
|
|
354
|
+
content
|
|
355
|
+
)}
|
|
356
|
+
</AnimatedPressable>
|
|
274
357
|
);
|
|
275
358
|
};
|
|
276
359
|
|
|
@@ -18,9 +18,17 @@ import type { ButtonProps, ButtonSize, ButtonVariant } from './types';
|
|
|
18
18
|
export type { ButtonProps, ButtonVariant, ButtonSize } from './types';
|
|
19
19
|
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
|
-
// Geometry — mirrors the native (`Button.tsx`) impl
|
|
22
|
-
//
|
|
23
|
-
//
|
|
21
|
+
// Geometry — mirrors the native (`Button.tsx`) impl so the web button is
|
|
22
|
+
// pixel-consistent with native. Sizes below are the resolved primitives; the
|
|
23
|
+
// shadcn-style aliases (`sm | md | lg | icon`) are normalized first.
|
|
24
|
+
//
|
|
25
|
+
// ONE deliberate exception, in `containerStyle`: `text`/`link` clear
|
|
26
|
+
// `minHeight` here so the control hugs its label and sits inline (which is what
|
|
27
|
+
// makes `asChild` anchors work), while native keeps the size config's value to
|
|
28
|
+
// preserve the touch target. The 4/8 padding override those variants get is NOT
|
|
29
|
+
// an exception — native applies exactly the same one, so `text` is a compact
|
|
30
|
+
// affordance and `ghost` a full-size button without a background on both
|
|
31
|
+
// platforms. Only the variant COLOR table below is shared between them.
|
|
24
32
|
// ---------------------------------------------------------------------------
|
|
25
33
|
|
|
26
34
|
type NativeSize = 'small' | 'medium' | 'large';
|
package/src/button/types.ts
CHANGED
|
@@ -11,6 +11,14 @@ import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
|
|
|
11
11
|
* the set. On native they fall back to a sensible equivalent
|
|
12
12
|
* (`outline → secondary`, `link → text`, `destructive → primary` tinted with
|
|
13
13
|
* the negative token) so passing them never crashes a native consumer.
|
|
14
|
+
*
|
|
15
|
+
* `text` (and its web alias `link`) is DELIBERATELY not a transparent `ghost`:
|
|
16
|
+
* it is the compact inline affordance, so it overrides the size config's padding
|
|
17
|
+
* down to 4/8 on BOTH platforms. `ghost` keeps the full padding — it is a
|
|
18
|
+
* regular button that happens to have no background. The one geometry that does
|
|
19
|
+
* differ by platform is `minHeight`: web clears it for `text`/`link` so the
|
|
20
|
+
* control hugs its label and sits inline, native keeps the size config's value
|
|
21
|
+
* because a finger needs the target and a cursor does not.
|
|
14
22
|
*/
|
|
15
23
|
export type ButtonVariant =
|
|
16
24
|
| 'primary'
|
|
@@ -62,6 +62,28 @@
|
|
|
62
62
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
63
63
|
--color-sidebar-border: var(--sidebar-border);
|
|
64
64
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
65
|
+
--color-primary-text: var(--primary-text);
|
|
66
|
+
--color-primary-subtle: var(--primary-subtle);
|
|
67
|
+
--color-secondary-text: var(--secondary-text);
|
|
68
|
+
--color-secondary-subtle: var(--secondary-subtle);
|
|
69
|
+
--color-tertiary-text: var(--tertiary-text);
|
|
70
|
+
--color-tertiary-subtle: var(--tertiary-subtle);
|
|
71
|
+
--color-success: var(--success);
|
|
72
|
+
--color-success-foreground: var(--success-foreground);
|
|
73
|
+
--color-success-text: var(--success-text);
|
|
74
|
+
--color-success-subtle: var(--success-subtle);
|
|
75
|
+
--color-error: var(--error);
|
|
76
|
+
--color-error-foreground: var(--error-foreground);
|
|
77
|
+
--color-error-text: var(--error-text);
|
|
78
|
+
--color-error-subtle: var(--error-subtle);
|
|
79
|
+
--color-warning: var(--warning);
|
|
80
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
81
|
+
--color-warning-text: var(--warning-text);
|
|
82
|
+
--color-warning-subtle: var(--warning-subtle);
|
|
83
|
+
--color-info: var(--info);
|
|
84
|
+
--color-info-foreground: var(--info-foreground);
|
|
85
|
+
--color-info-text: var(--info-text);
|
|
86
|
+
--color-info-subtle: var(--info-subtle);
|
|
65
87
|
--color-destructive-foreground: #ffffff;
|
|
66
88
|
--color-divider: var(--border);
|
|
67
89
|
--spacing-space-2: 2px;
|
package/src/fab/Fab.tsx
CHANGED
|
@@ -206,6 +206,9 @@ function resolveVariant(
|
|
|
206
206
|
): { background: string; foreground: string } {
|
|
207
207
|
switch (variant) {
|
|
208
208
|
case 'secondary':
|
|
209
|
+
return { background: colors.secondary, foreground: colors.secondaryForeground };
|
|
210
|
+
case 'tertiary':
|
|
211
|
+
return { background: colors.tertiary, foreground: colors.tertiaryForeground };
|
|
209
212
|
case 'surface':
|
|
210
213
|
return { background: colors.card, foreground: colors.text };
|
|
211
214
|
case 'primary':
|
package/src/fab/Fab.web.tsx
CHANGED
|
@@ -166,6 +166,9 @@ function resolveVariant(
|
|
|
166
166
|
): { background: string; foreground: string; ring: string } {
|
|
167
167
|
switch (variant) {
|
|
168
168
|
case 'secondary':
|
|
169
|
+
return { background: c.secondary, foreground: c.secondaryForeground, ring: c.secondary };
|
|
170
|
+
case 'tertiary':
|
|
171
|
+
return { background: c.tertiary, foreground: c.tertiaryForeground, ring: c.tertiary };
|
|
169
172
|
case 'surface':
|
|
170
173
|
return { background: c.card, foreground: c.text, ring: c.primary };
|
|
171
174
|
case 'primary':
|
package/src/fab/types.ts
CHANGED
|
@@ -9,7 +9,12 @@ import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
|
|
|
9
9
|
* A lower-emphasis FAB that still reads as elevated.
|
|
10
10
|
* - `surface` — alias of `secondary`, named for Material-style "surface" FABs.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* M3 defines four FAB colour variants. Bloom previously mapped `secondary` onto
|
|
14
|
+
* the neutral card surface, so a "secondary FAB" was a grey circle and the
|
|
15
|
+
* accent trio had no home in the component that most wants one.
|
|
16
|
+
*/
|
|
17
|
+
export type FabVariant = 'primary' | 'secondary' | 'tertiary' | 'surface';
|
|
13
18
|
|
|
14
19
|
/**
|
|
15
20
|
* FAB size presets, matching Material-ish conventions but in Bloom's scale:
|
|
@@ -410,10 +410,13 @@ export function BloomThemeProvider({
|
|
|
410
410
|
const themeColors = useMemo(
|
|
411
411
|
() =>
|
|
412
412
|
effectiveSeed
|
|
413
|
-
? buildThemeFromSeed(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
413
|
+
? buildThemeFromSeed(
|
|
414
|
+
effectiveSeed,
|
|
415
|
+
resolved,
|
|
416
|
+
undefined,
|
|
417
|
+
undefined,
|
|
418
|
+
{ secondarySeed: effectiveSecondary, tertiarySeed: effectiveTertiary }
|
|
419
|
+
)
|
|
417
420
|
: buildTheme(colorPreset, resolved, isAdaptive, explicitAccents),
|
|
418
421
|
[
|
|
419
422
|
effectiveSeed,
|