@utilitywarehouse/hearth-react-native 0.35.1 → 0.35.3
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/SKILL.md +40 -3
- package/build/components/Modal/Modal.d.ts +1 -1
- package/build/components/Modal/Modal.js +2 -2
- package/build/components/Modal/Modal.shared.types.d.ts +1 -0
- package/build/components/Modal/Modal.web.d.ts +1 -1
- package/build/components/Modal/Modal.web.js +2 -2
- package/build/core/themes.d.ts +20 -0
- package/build/tokens/border-radius.d.ts +1 -0
- package/build/tokens/border-radius.js +1 -0
- package/build/tokens/color.d.ts +4 -0
- package/build/tokens/color.js +2 -0
- package/build/tokens/line-height.d.ts +1 -0
- package/build/tokens/line-height.js +1 -0
- package/build/tokens/primitive.d.ts +3 -0
- package/build/tokens/primitive.js +3 -0
- package/build/tokens/space.d.ts +1 -0
- package/build/tokens/space.js +1 -0
- package/package.json +4 -4
- package/public/llms/components/modal.md +1 -0
- package/public/llms/docs/ai-tooling.md +19 -94
- package/public/llms/docs/introduction.md +1 -1
- package/public/llms.txt +1 -1
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hearth-react-native
|
|
3
|
-
description:
|
|
3
|
+
description: MANDATORY prerequisite — you MUST load this skill BEFORE writing or editing any file that imports from `@utilitywarehouse/hearth-react-native` or `@utilitywarehouse/hearth-react-native-icons`, BEFORE adapting Figma `get_design_context` reference output into a React Native screen in a Hearth app, and BEFORE delegating research about a Hearth component's props or usage to a subagent. NEVER substitute node_modules type definitions or a grep of existing usage for this skill's own component docs — that is how this skill gets silently bypassed. This skill IS the "target project's conventions" that figma-design-to-code hands off to; loading that skill does NOT satisfy this requirement. Do not wait for an explicit mention of "Hearth React Native" — any React Native UI work in an app with the library installed requires this skill first.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Building with Hearth React Native
|
|
@@ -9,6 +9,31 @@ You are acting as a React Native engineer. Your job is to turn requirements —
|
|
|
9
9
|
either Figma designs or written requirements — into UI React Native code using
|
|
10
10
|
Hearth — Utility Warehouse's Design Systems libraries.
|
|
11
11
|
|
|
12
|
+
## MANDATORY — when you must load this skill
|
|
13
|
+
|
|
14
|
+
Load this skill — do not skip it — at each of these moments:
|
|
15
|
+
|
|
16
|
+
- **Before writing or editing any file that imports from
|
|
17
|
+
`@utilitywarehouse/hearth-react-native` or
|
|
18
|
+
`@utilitywarehouse/hearth-react-native-icons`.** If the file touches either
|
|
19
|
+
package, this skill MUST be loaded first, no exceptions.
|
|
20
|
+
- **Before adapting Figma `get_design_context` reference output into this
|
|
21
|
+
codebase.** `figma-design-to-code` hands off with "adapt the reference code
|
|
22
|
+
to the target project's conventions" without saying what those conventions
|
|
23
|
+
are. This skill IS that answer. Loading `figma-design-to-code` does NOT
|
|
24
|
+
satisfy this requirement — load both, this one before you touch the
|
|
25
|
+
reference code.
|
|
26
|
+
- **Before delegating research about a Hearth component's props, variants, or
|
|
27
|
+
usage to a subagent.** Reading `node_modules` type definitions or grepping
|
|
28
|
+
existing usage in the app is NOT a substitute for this skill's own component
|
|
29
|
+
docs (see "Before you implement" below). Spawning a research subagent to
|
|
30
|
+
answer a question this skill already answers is how this skill gets
|
|
31
|
+
silently bypassed — load this skill, or pass its instructions into the
|
|
32
|
+
subagent's prompt, before delegating.
|
|
33
|
+
|
|
34
|
+
Treat each moment above as a hard gate, not a vague "this is UI work" prompt
|
|
35
|
+
to get around to eventually.
|
|
36
|
+
|
|
12
37
|
## Package imports
|
|
13
38
|
|
|
14
39
|
| Purpose | Package |
|
|
@@ -106,6 +131,8 @@ pairing with someone, ask for it here too.
|
|
|
106
131
|
|
|
107
132
|
## Use layout components
|
|
108
133
|
|
|
134
|
+
(Source of truth: [`public/llms/docs/layout-components.md`](public/llms/docs/layout-components.md))
|
|
135
|
+
|
|
109
136
|
Use layout components to structure and space content. Do not add margin to
|
|
110
137
|
individual UI elements to achieve spacing — keep spacing decisions at the layout
|
|
111
138
|
level so UI components stay composable.
|
|
@@ -172,6 +199,8 @@ alongside their own layout-specific props (`direction`, `align`, `justify`,
|
|
|
172
199
|
|
|
173
200
|
## Styling: Unistyles, not `StyleSheet` from `react-native`
|
|
174
201
|
|
|
202
|
+
(Source of truth: [`public/llms/docs/styling.md`](public/llms/docs/styling.md))
|
|
203
|
+
|
|
175
204
|
Custom styles use `react-native-unistyles`'s `StyleSheet.create(theme => ({...}))`,
|
|
176
205
|
re-exported from the package. Only reach for this when utility props and
|
|
177
206
|
existing component variants don't cover the need.
|
|
@@ -235,6 +264,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
235
264
|
|
|
236
265
|
## Responsive breakpoints
|
|
237
266
|
|
|
267
|
+
(Source of truth: [`public/llms/docs/styling.md`](public/llms/docs/styling.md#responsive-design-with-breakpoints))
|
|
268
|
+
|
|
238
269
|
Breakpoints are configured once (via Unistyles' `StyleSheet.configure`) and
|
|
239
270
|
cascade **mobile-first**: a value applies from that breakpoint upward until
|
|
240
271
|
overridden by a larger one. See the
|
|
@@ -264,6 +295,8 @@ const styles = StyleSheet.create(theme => ({
|
|
|
264
295
|
|
|
265
296
|
## Required app-level providers
|
|
266
297
|
|
|
298
|
+
(Source of truth: [`public/llms/docs/getting-started.md`](public/llms/docs/getting-started.md))
|
|
299
|
+
|
|
267
300
|
Hearth React Native components rely on these wrapping the app root:
|
|
268
301
|
|
|
269
302
|
- `GestureHandlerRootView` (from `react-native-gesture-handler`) — required for
|
|
@@ -299,11 +332,13 @@ applies before reaching for `react-native-safe-area-context` directly:
|
|
|
299
332
|
`SafeAreaView` applies the `top` edge. Set `useSafeAreaInsets={false}` on
|
|
300
333
|
`BottomSheetModalProvider` only if the parent screen already handles
|
|
301
334
|
safe-area insets itself — don't look for the prop on the individual
|
|
302
|
-
components.
|
|
335
|
+
components. (Source of truth:
|
|
336
|
+
[`public/llms/components/modal.md`](public/llms/components/modal.md))
|
|
303
337
|
- **`NavModal`** (used when a screen is already presented by React Navigation)
|
|
304
338
|
reads safe-area insets directly from the Unistyles runtime
|
|
305
339
|
(`rt.insets.top`/`rt.insets.bottom` inside `StyleSheet.create((theme, rt) =>
|
|
306
|
-
...)`) rather than rendering a `SafeAreaView`.
|
|
340
|
+
...)`) rather than rendering a `SafeAreaView`. (Source of truth:
|
|
341
|
+
[`public/llms/components/nav-modal.md`](public/llms/components/nav-modal.md))
|
|
307
342
|
|
|
308
343
|
Don't wrap Hearth components in your own `SafeAreaView` on top of these —
|
|
309
344
|
check `BottomSheetModalProvider`'s `useSafeAreaInsets` prop (or `NavModal`'s
|
|
@@ -344,6 +379,8 @@ Compound components share state via a `<Component>Context` (check for a
|
|
|
344
379
|
|
|
345
380
|
### Themed images that adapt to light/dark mode
|
|
346
381
|
|
|
382
|
+
(Source of truth: [`public/llms/docs/dark-mode-best-practice.md`](public/llms/docs/dark-mode-best-practice.md))
|
|
383
|
+
|
|
347
384
|
```tsx
|
|
348
385
|
<ThemedImage light={require('./logo-light.png')} dark={require('./logo-dark.png')} />
|
|
349
386
|
```
|
|
@@ -3,5 +3,5 @@ import ModalProps from './Modal.props';
|
|
|
3
3
|
type Modal<T = any> = BottomSheetModalMethods<T> & {
|
|
4
4
|
triggerCloseAnimation?: () => void;
|
|
5
5
|
};
|
|
6
|
-
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, loadingDescription, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, loadingDescription, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter, testID, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export default Modal;
|
|
@@ -12,7 +12,7 @@ import { Button } from '../Button';
|
|
|
12
12
|
import { Heading } from '../Heading';
|
|
13
13
|
import { Spinner } from '../Spinner';
|
|
14
14
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
15
|
-
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', loadingDescription, fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter = true, ...props }) => {
|
|
15
|
+
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', loadingDescription, fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, stickyFooter = true, testID, ...props }) => {
|
|
16
16
|
const theme = useTheme();
|
|
17
17
|
const bottomSheetModalRef = useRef(null);
|
|
18
18
|
const viewRef = useRef(null);
|
|
@@ -90,7 +90,7 @@ const Modal = ({ ref, children, heading, description, showCloseButton = true, pr
|
|
|
90
90
|
secondaryButtonProps,
|
|
91
91
|
secondaryButtonText,
|
|
92
92
|
]);
|
|
93
|
-
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? (loadingHeading ?? 'Loading') : undefined, accessibilityHint: Platform.OS === 'android' && loadingDescription ? loadingDescription : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading }), loadingDescription ? _jsx(BodyText, { textAlign: "center", children: loadingDescription }) : null] })) : (_jsxs(View, { style: styles.container, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, !stickyFooter && shouldShowFooter ? (_jsx(View, { style: footerStyle, children: footerContent })) : null] })) }));
|
|
93
|
+
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? (loadingHeading ?? 'Loading') : undefined, accessibilityHint: Platform.OS === 'android' && loadingDescription ? loadingDescription : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading }), loadingDescription ? _jsx(BodyText, { textAlign: "center", children: loadingDescription }) : null] })) : (_jsxs(View, { style: styles.container, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, !stickyFooter && shouldShowFooter ? (_jsx(View, { style: footerStyle, children: footerContent })) : null] })) }));
|
|
94
94
|
const renderFooter = useCallback((bottomSheetFooterProps) => (_jsx(BottomSheetFooter, { ...bottomSheetFooterProps, children: _jsx(View, { onLayout: handleStickyFooterLayout, style: [styles.footerWrap, footerStyle], children: footerContent }) })), [footerContent, footerStyle, handleStickyFooterLayout]);
|
|
95
95
|
return (_jsxs(_Fragment, { children: [stickyFooter && shouldShowFooter && stickyFooterHeight === 0 ? (_jsx(View, { accessible: false, importantForAccessibility: "no-hide-descendants", pointerEvents: "none", style: styles.footerMeasurementContainer, children: _jsx(View, { onLayout: handleStickyFooterLayout, style: [styles.footerWrap, footerStyle], children: footerContent }) })) : null, _jsxs(BottomSheetModal, { ref: bottomSheetModalRef, enableDynamicSizing: true, snapPoints: image || fullscreen ? ['90%'] : props.snapPoints, showHandle: typeof loading !== 'undefined' && loading ? false : props.showHandle, accessible: false, style: styles.modal, footerComponent: stickyFooter && shouldShowFooter ? renderFooter : undefined, ...props, onChange: handleChange, children: [loading ? _jsx(View, { style: styles.loadingTop }) : null, _jsx(BottomSheetScrollView, { contentContainerStyle: [
|
|
96
96
|
styles.scrollView,
|
|
@@ -3,6 +3,7 @@ import { StyleProp, ViewProps, ViewStyle } from 'react-native';
|
|
|
3
3
|
import { ButtonWithoutChildrenProps } from '../Button/Button.props';
|
|
4
4
|
import { UnstyledIconButtonProps } from '../UnstyledIconButton';
|
|
5
5
|
export interface ModalCommonBaseProps {
|
|
6
|
+
testID?: string;
|
|
6
7
|
loading?: boolean;
|
|
7
8
|
image?: ReactNode;
|
|
8
9
|
showCloseButton?: boolean;
|
|
@@ -3,5 +3,5 @@ import ModalProps from './Modal.props';
|
|
|
3
3
|
type Modal<T = any> = BottomSheetModalMethods<T> & {
|
|
4
4
|
triggerCloseAnimation?: () => void;
|
|
5
5
|
};
|
|
6
|
-
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare const Modal: ({ ref, children, heading, description, showCloseButton, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress, closeOnSecondaryButtonPress, loading, loadingHeading, fullscreen, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, testID, ...props }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export default Modal;
|
|
@@ -9,7 +9,7 @@ import { Button } from '../Button';
|
|
|
9
9
|
import { Heading } from '../Heading';
|
|
10
10
|
import { Spinner } from '../Spinner';
|
|
11
11
|
import { UnstyledIconButton } from '../UnstyledIconButton';
|
|
12
|
-
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, ...props }) => {
|
|
12
|
+
const Modal = ({ ref, children, heading, description, showCloseButton = true, primaryButtonText, secondaryButtonText, onPressPrimaryButton, onPressCloseButton, onPressSecondaryButton, closeOnPrimaryButtonPress = true, closeOnSecondaryButtonPress = true, loading, loadingHeading = 'Loading...', fullscreen = false, image, footer, footerStyle, primaryButtonProps, secondaryButtonProps, closeButtonProps, testID, ...props }) => {
|
|
13
13
|
const bottomSheetModalRef = useRef(null);
|
|
14
14
|
const viewRef = useRef(null);
|
|
15
15
|
const scrollViewRef = useRef(null);
|
|
@@ -62,7 +62,7 @@ const Modal = ({ ref, children, heading, description, showCloseButton = true, pr
|
|
|
62
62
|
const descriptionIsText = typeof description === 'string' || typeof description === 'number';
|
|
63
63
|
const hasDescription = description !== undefined && description !== null;
|
|
64
64
|
const footerContent = footer ?? (_jsxs(View, { style: styles.footer, children: [hasPrimaryButton ? (_jsx(Button, { onPress: handlePrimaryButtonPress, text: primaryButtonText, ...primaryButtonProps, variant: primaryButtonProps?.variant ?? 'solid', colorScheme: primaryButtonProps?.colorScheme ?? 'highlight' })) : null, hasSecondaryButton ? (_jsx(Button, { onPress: handleSecondaryButtonPress, text: secondaryButtonText, ...secondaryButtonProps, variant: secondaryButtonProps?.variant ?? 'outline', colorScheme: secondaryButtonProps?.colorScheme ?? 'functional' })) : null] }));
|
|
65
|
-
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Loading' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading })] })) : (_jsxs(View, { style: styles.container, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, hasFooter ? _jsx(View, { style: footerStyle, children: footerContent }) : null] })) }));
|
|
65
|
+
const content = (_jsx(_Fragment, { children: loading ? (_jsxs(View, { style: styles.loadingContainer, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Loading' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsx(Spinner, { size: "lg" }), _jsx(Heading, { size: "lg", textAlign: "center", children: loadingHeading })] })) : (_jsxs(View, { style: styles.container, testID: testID, accessible: Platform.OS === 'android' ? true : undefined, accessibilityLabel: Platform.OS === 'android' ? 'Modal content' : undefined, screenReaderFocusable: true, ref: viewRef, children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTextContent, children: [heading && !image ? (_jsx(Heading, { size: "lg", accessible: true, children: heading })) : null, hasDescription && !image ? (descriptionIsText ? (_jsx(BodyText, { accessible: true, children: description })) : (description)) : null] }), showCloseButton ? (_jsx(UnstyledIconButton, { icon: CloseMediumIcon, onPress: handleCloseButtonPress, accessibilityLabel: "Close modal", ...closeButtonProps })) : null] }), image ? (_jsxs(View, { style: styles.imageContainer, children: [image, _jsxs(View, { style: styles.textContent, children: [heading ? (_jsx(Heading, { size: "lg", textAlign: "center", accessible: true, children: heading })) : null, hasDescription ? (descriptionIsText ? (_jsx(BodyText, { textAlign: "center", accessible: true, children: description })) : (description)) : null] })] })) : null, children, hasFooter ? _jsx(View, { style: footerStyle, children: footerContent }) : null] })) }));
|
|
66
66
|
return (_jsx(BottomSheetModal, { ref: bottomSheetModalRef, enableDynamicSizing: true, snapPoints: image || fullscreen ? ['90%'] : props.snapPoints, showHandle: typeof loading !== 'undefined' && loading ? false : props.showHandle, accessible: false, ...props, onChange: handleChange, children: _jsx(BottomSheetScrollView, { contentContainerStyle: styles.container, ref: scrollViewRef, children: content }) }));
|
|
67
67
|
};
|
|
68
68
|
const styles = StyleSheet.create(theme => ({
|
package/build/core/themes.d.ts
CHANGED
|
@@ -113,6 +113,7 @@ export declare const lightTheme: {
|
|
|
113
113
|
readonly '700': 56;
|
|
114
114
|
readonly '800': 64;
|
|
115
115
|
readonly '900': 72;
|
|
116
|
+
readonly '1000': 80;
|
|
116
117
|
};
|
|
117
118
|
readonly borderWidth: {
|
|
118
119
|
readonly '1': 1;
|
|
@@ -120,6 +121,7 @@ export declare const lightTheme: {
|
|
|
120
121
|
};
|
|
121
122
|
readonly borderRadius: {
|
|
122
123
|
readonly full: 9999;
|
|
124
|
+
readonly lg: 12;
|
|
123
125
|
readonly md: 8;
|
|
124
126
|
readonly none: 0;
|
|
125
127
|
readonly sm: 6;
|
|
@@ -153,6 +155,7 @@ export declare const lightTheme: {
|
|
|
153
155
|
readonly '600': 28;
|
|
154
156
|
readonly '700': 32;
|
|
155
157
|
readonly '800': 36;
|
|
158
|
+
readonly '900': 40;
|
|
156
159
|
readonly '950': 48;
|
|
157
160
|
readonly '975': 52;
|
|
158
161
|
readonly '1050': 62;
|
|
@@ -1185,6 +1188,7 @@ export declare const lightTheme: {
|
|
|
1185
1188
|
};
|
|
1186
1189
|
readonly cashbackLilac: {
|
|
1187
1190
|
readonly '300': "#dfbdf5";
|
|
1191
|
+
readonly '400': "#ce9cf0";
|
|
1188
1192
|
readonly '500': "#be7aeb";
|
|
1189
1193
|
readonly '700': "#8b2bc9";
|
|
1190
1194
|
readonly '900': "#7429b5";
|
|
@@ -1233,6 +1237,7 @@ export declare const lightTheme: {
|
|
|
1233
1237
|
};
|
|
1234
1238
|
readonly mobileRose: {
|
|
1235
1239
|
readonly '200': "#fad0e9";
|
|
1240
|
+
readonly '300': "#f49fd2";
|
|
1236
1241
|
readonly '400': "#ee6dbb";
|
|
1237
1242
|
readonly '800': "#a7266d";
|
|
1238
1243
|
readonly '900': "#8a3260";
|
|
@@ -1409,6 +1414,7 @@ export declare const darkTheme: {
|
|
|
1409
1414
|
readonly '700': 56;
|
|
1410
1415
|
readonly '800': 64;
|
|
1411
1416
|
readonly '900': 72;
|
|
1417
|
+
readonly '1000': 80;
|
|
1412
1418
|
};
|
|
1413
1419
|
readonly borderWidth: {
|
|
1414
1420
|
readonly '1': 1;
|
|
@@ -1416,6 +1422,7 @@ export declare const darkTheme: {
|
|
|
1416
1422
|
};
|
|
1417
1423
|
readonly borderRadius: {
|
|
1418
1424
|
readonly full: 9999;
|
|
1425
|
+
readonly lg: 12;
|
|
1419
1426
|
readonly md: 8;
|
|
1420
1427
|
readonly none: 0;
|
|
1421
1428
|
readonly sm: 6;
|
|
@@ -1449,6 +1456,7 @@ export declare const darkTheme: {
|
|
|
1449
1456
|
readonly '600': 28;
|
|
1450
1457
|
readonly '700': 32;
|
|
1451
1458
|
readonly '800': 36;
|
|
1459
|
+
readonly '900': 40;
|
|
1452
1460
|
readonly '950': 48;
|
|
1453
1461
|
readonly '975': 52;
|
|
1454
1462
|
readonly '1050': 62;
|
|
@@ -2492,6 +2500,7 @@ export declare const darkTheme: {
|
|
|
2492
2500
|
};
|
|
2493
2501
|
readonly cashbackLilac: {
|
|
2494
2502
|
readonly '300': "#dfbdf5";
|
|
2503
|
+
readonly '400': "#ce9cf0";
|
|
2495
2504
|
readonly '500': "#be7aeb";
|
|
2496
2505
|
readonly '700': "#8b2bc9";
|
|
2497
2506
|
readonly '900': "#7429b5";
|
|
@@ -2540,6 +2549,7 @@ export declare const darkTheme: {
|
|
|
2540
2549
|
};
|
|
2541
2550
|
readonly mobileRose: {
|
|
2542
2551
|
readonly '200': "#fad0e9";
|
|
2552
|
+
readonly '300': "#f49fd2";
|
|
2543
2553
|
readonly '400': "#ee6dbb";
|
|
2544
2554
|
readonly '800': "#a7266d";
|
|
2545
2555
|
readonly '900': "#8a3260";
|
|
@@ -2717,6 +2727,7 @@ export declare const themes: {
|
|
|
2717
2727
|
readonly '700': 56;
|
|
2718
2728
|
readonly '800': 64;
|
|
2719
2729
|
readonly '900': 72;
|
|
2730
|
+
readonly '1000': 80;
|
|
2720
2731
|
};
|
|
2721
2732
|
readonly borderWidth: {
|
|
2722
2733
|
readonly '1': 1;
|
|
@@ -2724,6 +2735,7 @@ export declare const themes: {
|
|
|
2724
2735
|
};
|
|
2725
2736
|
readonly borderRadius: {
|
|
2726
2737
|
readonly full: 9999;
|
|
2738
|
+
readonly lg: 12;
|
|
2727
2739
|
readonly md: 8;
|
|
2728
2740
|
readonly none: 0;
|
|
2729
2741
|
readonly sm: 6;
|
|
@@ -2757,6 +2769,7 @@ export declare const themes: {
|
|
|
2757
2769
|
readonly '600': 28;
|
|
2758
2770
|
readonly '700': 32;
|
|
2759
2771
|
readonly '800': 36;
|
|
2772
|
+
readonly '900': 40;
|
|
2760
2773
|
readonly '950': 48;
|
|
2761
2774
|
readonly '975': 52;
|
|
2762
2775
|
readonly '1050': 62;
|
|
@@ -3789,6 +3802,7 @@ export declare const themes: {
|
|
|
3789
3802
|
};
|
|
3790
3803
|
readonly cashbackLilac: {
|
|
3791
3804
|
readonly '300': "#dfbdf5";
|
|
3805
|
+
readonly '400': "#ce9cf0";
|
|
3792
3806
|
readonly '500': "#be7aeb";
|
|
3793
3807
|
readonly '700': "#8b2bc9";
|
|
3794
3808
|
readonly '900': "#7429b5";
|
|
@@ -3837,6 +3851,7 @@ export declare const themes: {
|
|
|
3837
3851
|
};
|
|
3838
3852
|
readonly mobileRose: {
|
|
3839
3853
|
readonly '200': "#fad0e9";
|
|
3854
|
+
readonly '300': "#f49fd2";
|
|
3840
3855
|
readonly '400': "#ee6dbb";
|
|
3841
3856
|
readonly '800': "#a7266d";
|
|
3842
3857
|
readonly '900': "#8a3260";
|
|
@@ -4013,6 +4028,7 @@ export declare const themes: {
|
|
|
4013
4028
|
readonly '700': 56;
|
|
4014
4029
|
readonly '800': 64;
|
|
4015
4030
|
readonly '900': 72;
|
|
4031
|
+
readonly '1000': 80;
|
|
4016
4032
|
};
|
|
4017
4033
|
readonly borderWidth: {
|
|
4018
4034
|
readonly '1': 1;
|
|
@@ -4020,6 +4036,7 @@ export declare const themes: {
|
|
|
4020
4036
|
};
|
|
4021
4037
|
readonly borderRadius: {
|
|
4022
4038
|
readonly full: 9999;
|
|
4039
|
+
readonly lg: 12;
|
|
4023
4040
|
readonly md: 8;
|
|
4024
4041
|
readonly none: 0;
|
|
4025
4042
|
readonly sm: 6;
|
|
@@ -4053,6 +4070,7 @@ export declare const themes: {
|
|
|
4053
4070
|
readonly '600': 28;
|
|
4054
4071
|
readonly '700': 32;
|
|
4055
4072
|
readonly '800': 36;
|
|
4073
|
+
readonly '900': 40;
|
|
4056
4074
|
readonly '950': 48;
|
|
4057
4075
|
readonly '975': 52;
|
|
4058
4076
|
readonly '1050': 62;
|
|
@@ -5096,6 +5114,7 @@ export declare const themes: {
|
|
|
5096
5114
|
};
|
|
5097
5115
|
readonly cashbackLilac: {
|
|
5098
5116
|
readonly '300': "#dfbdf5";
|
|
5117
|
+
readonly '400': "#ce9cf0";
|
|
5099
5118
|
readonly '500': "#be7aeb";
|
|
5100
5119
|
readonly '700': "#8b2bc9";
|
|
5101
5120
|
readonly '900': "#7429b5";
|
|
@@ -5144,6 +5163,7 @@ export declare const themes: {
|
|
|
5144
5163
|
};
|
|
5145
5164
|
readonly mobileRose: {
|
|
5146
5165
|
readonly '200': "#fad0e9";
|
|
5166
|
+
readonly '300': "#f49fd2";
|
|
5147
5167
|
readonly '400': "#ee6dbb";
|
|
5148
5168
|
readonly '800': "#a7266d";
|
|
5149
5169
|
readonly '900': "#8a3260";
|
package/build/tokens/color.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare const broadbandGreen: {
|
|
|
16
16
|
};
|
|
17
17
|
export declare const cashbackLilac: {
|
|
18
18
|
readonly '300': "#dfbdf5";
|
|
19
|
+
readonly '400': "#ce9cf0";
|
|
19
20
|
readonly '500': "#be7aeb";
|
|
20
21
|
readonly '700': "#8b2bc9";
|
|
21
22
|
readonly '900': "#7429b5";
|
|
@@ -64,6 +65,7 @@ export declare const insuranceOrange: {
|
|
|
64
65
|
};
|
|
65
66
|
export declare const mobileRose: {
|
|
66
67
|
readonly '200': "#fad0e9";
|
|
68
|
+
readonly '300': "#f49fd2";
|
|
67
69
|
readonly '400': "#ee6dbb";
|
|
68
70
|
readonly '800': "#a7266d";
|
|
69
71
|
readonly '900': "#8a3260";
|
|
@@ -636,6 +638,7 @@ declare const color: {
|
|
|
636
638
|
};
|
|
637
639
|
readonly cashbackLilac: {
|
|
638
640
|
readonly '300': "#dfbdf5";
|
|
641
|
+
readonly '400': "#ce9cf0";
|
|
639
642
|
readonly '500': "#be7aeb";
|
|
640
643
|
readonly '700': "#8b2bc9";
|
|
641
644
|
readonly '900': "#7429b5";
|
|
@@ -684,6 +687,7 @@ declare const color: {
|
|
|
684
687
|
};
|
|
685
688
|
readonly mobileRose: {
|
|
686
689
|
readonly '200': "#fad0e9";
|
|
690
|
+
readonly '300': "#f49fd2";
|
|
687
691
|
readonly '400': "#ee6dbb";
|
|
688
692
|
readonly '800': "#a7266d";
|
|
689
693
|
readonly '900': "#8a3260";
|
package/build/tokens/color.js
CHANGED
|
@@ -16,6 +16,7 @@ export const broadbandGreen = {
|
|
|
16
16
|
};
|
|
17
17
|
export const cashbackLilac = {
|
|
18
18
|
'300': '#dfbdf5',
|
|
19
|
+
'400': '#ce9cf0',
|
|
19
20
|
'500': '#be7aeb',
|
|
20
21
|
'700': '#8b2bc9',
|
|
21
22
|
'900': '#7429b5',
|
|
@@ -64,6 +65,7 @@ export const insuranceOrange = {
|
|
|
64
65
|
};
|
|
65
66
|
export const mobileRose = {
|
|
66
67
|
'200': '#fad0e9',
|
|
68
|
+
'300': '#f49fd2',
|
|
67
69
|
'400': '#ee6dbb',
|
|
68
70
|
'800': '#a7266d',
|
|
69
71
|
'900': '#8a3260',
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
declare const _default: {
|
|
5
5
|
readonly borderRadius: {
|
|
6
6
|
readonly full: 9999;
|
|
7
|
+
readonly lg: 12;
|
|
7
8
|
readonly md: 8;
|
|
8
9
|
readonly none: 0;
|
|
9
10
|
readonly sm: 6;
|
|
@@ -56,6 +57,7 @@ declare const _default: {
|
|
|
56
57
|
readonly '600': 28;
|
|
57
58
|
readonly '700': 32;
|
|
58
59
|
readonly '800': 36;
|
|
60
|
+
readonly '900': 40;
|
|
59
61
|
readonly '950': 48;
|
|
60
62
|
readonly '975': 52;
|
|
61
63
|
readonly '1050': 62;
|
|
@@ -97,6 +99,7 @@ declare const _default: {
|
|
|
97
99
|
readonly '700': 56;
|
|
98
100
|
readonly '800': 64;
|
|
99
101
|
readonly '900': 72;
|
|
102
|
+
readonly '1000': 80;
|
|
100
103
|
};
|
|
101
104
|
};
|
|
102
105
|
export default _default;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export default {
|
|
5
5
|
borderRadius: {
|
|
6
6
|
full: 9999,
|
|
7
|
+
lg: 12,
|
|
7
8
|
md: 8,
|
|
8
9
|
none: 0,
|
|
9
10
|
sm: 6,
|
|
@@ -56,6 +57,7 @@ export default {
|
|
|
56
57
|
'600': 28,
|
|
57
58
|
'700': 32,
|
|
58
59
|
'800': 36,
|
|
60
|
+
'900': 40,
|
|
59
61
|
'950': 48,
|
|
60
62
|
'975': 52,
|
|
61
63
|
'1050': 62,
|
|
@@ -97,5 +99,6 @@ export default {
|
|
|
97
99
|
'700': 56,
|
|
98
100
|
'800': 64,
|
|
99
101
|
'900': 72,
|
|
102
|
+
'1000': 80,
|
|
100
103
|
},
|
|
101
104
|
};
|
package/build/tokens/space.d.ts
CHANGED
package/build/tokens/space.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilitywarehouse/hearth-react-native",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.3",
|
|
4
4
|
"description": "Utility Warehouse React Native UI library",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -78,10 +78,10 @@
|
|
|
78
78
|
"vitest": "^4.1.7",
|
|
79
79
|
"@utilitywarehouse/hearth-storybook-utils": "0.0.0",
|
|
80
80
|
"@utilitywarehouse/hearth-fonts": "^0.1.1",
|
|
81
|
-
"@utilitywarehouse/hearth-react-icons": "^0.
|
|
82
|
-
"@utilitywarehouse/hearth-react-native-icons": "^0.
|
|
81
|
+
"@utilitywarehouse/hearth-react-icons": "^0.9.0",
|
|
82
|
+
"@utilitywarehouse/hearth-react-native-icons": "^0.9.0",
|
|
83
83
|
"@utilitywarehouse/hearth-svg-assets": "^0.6.5",
|
|
84
|
-
"@utilitywarehouse/hearth-tokens": "^0.4.
|
|
84
|
+
"@utilitywarehouse/hearth-tokens": "^0.4.4"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"@gorhom/bottom-sheet": ">=5.0.0",
|
|
@@ -106,6 +106,7 @@ The Modal component extends the `BottomSheetModal` component and accepts all of
|
|
|
106
106
|
| `footerStyle` | `StyleProp<ViewStyle>` | Styles applied to the footer container, useful for sticky footer shadows or custom spacing | - |
|
|
107
107
|
| `closeButtonProps` | `Omit<UnstyledIconButtonProps, 'children'>` | Additional props to pass to the close button | - |
|
|
108
108
|
| `fullscreen` | `boolean` | Whether the modal should take up the full screen height | `false` |
|
|
109
|
+
| `testID` | `string` | Applied to the modal content container, useful for locating the modal in e2e tests | - |
|
|
109
110
|
|
|
110
111
|
When `footer` is provided, the primary and secondary button props are not available. Build your footer actions directly inside the custom footer content instead.
|
|
111
112
|
|
|
@@ -1,110 +1,35 @@
|
|
|
1
1
|
# AI Tooling
|
|
2
2
|
|
|
3
|
-
- [
|
|
3
|
+
- [Plugin](#plugin)
|
|
4
4
|
- [llms.txt and markdown docs](#llms-txt-and-markdown-docs)
|
|
5
5
|
- [Agent Skill](#agent-skill)
|
|
6
6
|
- [Hearth React Native MCP Server](#hearth-react-native-mcp-server)
|
|
7
7
|
|
|
8
|
-
The Hearth React Native AI Tools are intended to help your AI agents use Hearth
|
|
9
|
-
components to build Utility Warehouse UIs. They provide access to
|
|
10
|
-
component documentation and usage guidelines, so your agent has the
|
|
11
|
-
needs to build with Hearth correctly.
|
|
8
|
+
The Hearth React Native AI Tools are intended to help your AI agents use Hearth
|
|
9
|
+
React Native components to build Utility Warehouse UIs. They provide access to
|
|
10
|
+
APIs, component documentation and usage guidelines, so your agent has the
|
|
11
|
+
context it needs to build with Hearth correctly.
|
|
12
12
|
|
|
13
|
-
If you're building UI from a Figma
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
If you're building UI from a design in Figma, start with Figma's MCP Server.
|
|
14
|
+
This should be the first step, since it gives your agent direct access to the
|
|
15
|
+
design you're implementing. You can read more information about setting this up
|
|
16
|
+
in the root AI Toolkit documentation.
|
|
17
|
+
|
|
18
|
+
Hearth's own AI tooling then supplements it for most use cases, filling in the
|
|
19
|
+
parts of the Hearth React Native API and usage guidance that the design alone
|
|
20
|
+
doesn't capture:
|
|
19
21
|
|
|
20
22
|
- [llms.txt](https://hearth.prod.uw.systems/react-native/llms.txt) and markdown docs
|
|
21
23
|
- [SKILL.md](https://github.com/utilitywarehouse/hearth/blob/main/packages/react-native/SKILL.md)
|
|
22
24
|
- [Hearth MCP Server](https://main--68e3ad5c6e80b57678cad6c6.chromatic.com/mcp)
|
|
23
25
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
If you're implementing a Figma design, this should be the first tool you reach
|
|
27
|
-
for. Figma's [Dev Mode MCP Server](https://developers.figma.com/docs/figma-mcp-server/)
|
|
28
|
-
gives your agent direct access to the design file itself, layout, variables,
|
|
29
|
-
and generated code for a selected frame or component. This is a Figma-provided
|
|
30
|
-
tool, separate from the Hearth React Native MCP Server below.
|
|
31
|
-
|
|
32
|
-
### If you're using Claude
|
|
33
|
-
|
|
34
|
-
Utility Warehouse has the [Figma connector](https://claude.com/connectors/figma)
|
|
35
|
-
enabled at an organisation level. It already includes the Figma MCP Server's
|
|
36
|
-
tools, so if you have it connected there's nothing else to set up — just
|
|
37
|
-
prompt Claude with a link to the Figma design you want to build, and it will
|
|
38
|
-
pull in the design context itself.
|
|
39
|
-
|
|
40
|
-
To check it's connected for you:
|
|
41
|
-
|
|
42
|
-
1. In Claude, go to **Customize > Connectors**.
|
|
43
|
-
2. Find **Figma** and click **Connect**, authenticating with your Figma account if prompted.
|
|
44
|
-
3. In a conversation, open the **+** menu (or type `/`) in the chat box, then **Connectors**, and toggle **Figma** on. Once it's connected, Claude can also bring it into a conversation on its own when a Figma link is relevant, so you don't have to toggle it every time.
|
|
45
|
-
|
|
46
|
-
If you're not using Claude, or don't have the connector available, set up the
|
|
47
|
-
Figma MCP Server manually instead.
|
|
48
|
-
|
|
49
|
-
### Manual setup
|
|
50
|
-
|
|
51
|
-
Figma recommends the **remote server** for most people, as it supports the
|
|
52
|
-
broadest set of features. Add an entry to your `mcp.json` file:
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"servers": {
|
|
57
|
-
"figma": {
|
|
58
|
-
"url": "https://mcp.figma.com/mcp",
|
|
59
|
-
"type": "http"
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
See Figma's [remote server installation guide](https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/)
|
|
66
|
-
for the full setup flow, including the OAuth step required to grant the server
|
|
67
|
-
access to your Figma account.
|
|
68
|
-
|
|
69
|
-
Alternatively, a **local server** runs from the Figma desktop app itself (Dev
|
|
70
|
-
Mode → enable desktop MCP server), which some organisations use instead of the
|
|
71
|
-
remote option:
|
|
72
|
-
|
|
73
|
-
```json
|
|
74
|
-
{
|
|
75
|
-
"servers": {
|
|
76
|
-
"figma-desktop": {
|
|
77
|
-
"url": "http://127.0.0.1:3845/mcp",
|
|
78
|
-
"type": "http"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
See Figma's [local server installation guide](https://developers.figma.com/docs/figma-mcp-server/local-server-installation/)
|
|
85
|
-
for prerequisites and setup.
|
|
86
|
-
|
|
87
|
-
### Works with Hearth components
|
|
88
|
-
|
|
89
|
-
Hearth publishes [Figma Code Connect](https://help.figma.com/hc/en-us/articles/23920389749655-Code-Connect)
|
|
90
|
-
mappings for its React Native components. In practice, this means: when the
|
|
91
|
-
Figma frame you're implementing uses a component we've mapped, the Figma MCP
|
|
92
|
-
Server returns real Hearth code, for example `<Button variant="emphasis">`,
|
|
93
|
-
rather than generic markup reconstructed from layer names and styles. You
|
|
94
|
-
don't need to know how these mappings work under the hood, just that a mapped
|
|
95
|
-
component gets you code that's ready to use, with little to no rework needed.
|
|
96
|
-
|
|
97
|
-
### Supplement it with Hearth's AI tooling
|
|
98
|
-
|
|
99
|
-
The Figma MCP Server tells your agent what design it's looking at, and, via
|
|
100
|
-
Code Connect, which Hearth component to use. It doesn't know the rest of the
|
|
101
|
-
Hearth React Native API: prop combinations a mapping doesn't cover,
|
|
102
|
-
accessibility guidance, or usage conventions.
|
|
26
|
+
## Plugin
|
|
103
27
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
28
|
+
The Hearth AI Toolkit plugin is the recommended way to set up your agent with
|
|
29
|
+
the Hearth React Native AI Tools. It will automatically add the skill and MCP
|
|
30
|
+
server to your agent configuration, and ensure that your agent has access to
|
|
31
|
+
the latest Hearth React Native documentation and usage guidance. How to install
|
|
32
|
+
and use the plugin is documented in the root AI Toolkit documentation.
|
|
108
33
|
|
|
109
34
|
## llms.txt and markdown docs
|
|
110
35
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Hearth React Native
|
|
2
2
|
|
|
3
3
|
React Native component library for building UIs at Utility Warehouse.<br/>
|
|
4
|
-
Current version: v0.35.
|
|
4
|
+
Current version: v0.35.3
|
|
5
5
|
|
|
6
6
|
Hearth React Native is a comprehensive design system library for React Native, providing reusable components, consistent styling, and tools to streamline UI development. It is built to enhance productivity and maintain design consistency across projects.
|
|
7
7
|
|