august-design-system
Version:
A comprehensive React Native design system following Apple Human Interface Guidelines
2,193 lines (2,176 loc) • 253 kB
JavaScript
'use strict';
var React19 = require('react');
var reactNative = require('react-native');
var MaterialIcons = require('react-native-vector-icons/MaterialIcons');
var Animated6 = require('react-native-reanimated');
var reactNativeSafeAreaContext = require('react-native-safe-area-context');
var Svg = require('react-native-svg');
var reactNativeGestureHandler = require('react-native-gesture-handler');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var React19__default = /*#__PURE__*/_interopDefault(React19);
var MaterialIcons__default = /*#__PURE__*/_interopDefault(MaterialIcons);
var Animated6__default = /*#__PURE__*/_interopDefault(Animated6);
var Svg__default = /*#__PURE__*/_interopDefault(Svg);
// src/design-system/components/primitives/Box.tsx
// src/design-system/tokens/colors.ts
var palette = {
// Gray scale - iOS system grays
gray: {
50: "#F2F2F7",
100: "#E5E5EA",
200: "#D1D1D6",
300: "#C7C7CC",
400: "#AEAEB2",
500: "#8E8E93"},
// System colors - iOS standard palette
// Light mode values
red: {
light: "#FF3B30"},
orange: {
light: "#FF9500"},
yellow: {
light: "#FFCC00"},
green: {
light: "#34C759"},
mint: {
light: "#00C7BE"},
teal: {
light: "#30B0C7"},
cyan: {
light: "#32ADE6"},
blue: {
light: "#007AFF"},
indigo: {
light: "#5856D6"},
purple: {
light: "#AF52DE"},
pink: {
light: "#FF2D55"},
brown: {
light: "#A2845E"}
};
var lightColors = {
// Background colors - iOS grouped table view style layering
background: {
primary: "#FFFFFF",
secondary: "#F2F2F7",
tertiary: "#FFFFFF",
grouped: "#F2F2F7",
groupedSecondary: "#FFFFFF",
groupedTertiary: "#F2F2F7"
},
// Label colors - for text content
label: {
primary: "#000000",
// 100% black
secondary: "rgba(60, 60, 67, 0.6)",
// 60% opacity
tertiary: "rgba(60, 60, 67, 0.3)",
// 30% opacity
quaternary: "rgba(60, 60, 67, 0.18)"
// 18% opacity
},
// Fill colors - for thin and small shapes
fill: {
primary: "rgba(120, 120, 128, 0.2)",
secondary: "rgba(120, 120, 128, 0.16)",
tertiary: "rgba(118, 118, 128, 0.12)",
quaternary: "rgba(116, 116, 128, 0.08)"
},
// Separator colors
separator: {
opaque: "#C6C6C8",
nonOpaque: "rgba(60, 60, 67, 0.36)"
},
// System colors - Apple standard
system: {
red: palette.red.light,
orange: palette.orange.light,
yellow: palette.yellow.light,
green: palette.green.light,
mint: palette.mint.light,
teal: palette.teal.light,
cyan: palette.cyan.light,
blue: palette.blue.light,
indigo: palette.indigo.light,
purple: palette.purple.light,
pink: palette.pink.light,
brown: palette.brown.light,
gray: palette.gray[500],
gray2: palette.gray[400],
gray3: palette.gray[300],
gray4: palette.gray[200],
gray5: palette.gray[100],
gray6: palette.gray[50]
},
// Semantic colors - functional meaning
semantic: {
success: palette.green.light,
warning: palette.orange.light,
error: palette.red.light,
info: palette.blue.light
},
// Interactive colors
interactive: {
tint: palette.blue.light,
tintPressed: "#0062CC",
// Darkened blue for pressed state
tintDisabled: "rgba(0, 122, 255, 0.3)",
destructive: palette.red.light,
destructivePressed: "#CC2F26"
},
// Material/Blur backgrounds - approximated for non-blur fallback
material: {
thin: "rgba(255, 255, 255, 0.6)",
regular: "rgba(255, 255, 255, 0.72)",
thick: "rgba(255, 255, 255, 0.85)",
chrome: "rgba(247, 247, 247, 0.8)"
}
};
// src/design-system/tokens/typography.ts
var fontFamily = {
regular: "System",
medium: "System",
semibold: "System",
bold: "System",
heavy: "System",
monospace: "Menlo",
// Falls back to platform monospace
rounded: "System"
// SF Pro Rounded on iOS
};
var typography = {
// ==========================================================================
// DISPLAY STYLES
// Large, prominent text for titles and headers
// ==========================================================================
/**
* Large Title - Used for main screen titles in navigation bars.
* iOS: 34pt Regular
*/
largeTitle: {
fontFamily: fontFamily.regular,
fontSize: 34,
lineHeight: 41,
// 1.2x
letterSpacing: 0.37,
fontWeight: "400"
},
/**
* Title 1 - Primary content titles.
* iOS: 28pt Regular
*/
title1: {
fontFamily: fontFamily.regular,
fontSize: 28,
lineHeight: 34,
// 1.21x
letterSpacing: 0.36,
fontWeight: "400"
},
/**
* Title 2 - Secondary titles.
* iOS: 22pt Regular
*/
title2: {
fontFamily: fontFamily.regular,
fontSize: 22,
lineHeight: 28,
// 1.27x
letterSpacing: 0.35,
fontWeight: "400"
},
/**
* Title 3 - Tertiary titles.
* iOS: 20pt Regular
*/
title3: {
fontFamily: fontFamily.regular,
fontSize: 20,
lineHeight: 25,
// 1.25x
letterSpacing: 0.38,
fontWeight: "400"
},
// ==========================================================================
// HEADLINE STYLES
// For section headers and emphasized text
// ==========================================================================
/**
* Headline - Section headers, emphasized body text.
* iOS: 17pt Semibold
*/
headline: {
fontFamily: fontFamily.semibold,
fontSize: 17,
lineHeight: 22,
// 1.29x
letterSpacing: -0.41,
fontWeight: "600"
},
/**
* Subheadline - Subordinate section headers.
* iOS: 15pt Regular
*/
subheadline: {
fontFamily: fontFamily.regular,
fontSize: 15,
lineHeight: 20,
// 1.33x
letterSpacing: -0.24,
fontWeight: "400"
},
// ==========================================================================
// BODY STYLES
// Primary reading text
// ==========================================================================
/**
* Body - Primary reading text throughout the app.
* iOS: 17pt Regular
*/
body: {
fontFamily: fontFamily.regular,
fontSize: 17,
lineHeight: 22,
// 1.29x
letterSpacing: -0.41,
fontWeight: "400"
},
/**
* Callout - Secondary text that's slightly smaller than body.
* iOS: 16pt Regular
*/
callout: {
fontFamily: fontFamily.regular,
fontSize: 16,
lineHeight: 21,
// 1.31x
letterSpacing: -0.32,
fontWeight: "400"
},
// ==========================================================================
// SUPPORTING STYLES
// Smaller text for captions, footnotes, and labels
// ==========================================================================
/**
* Footnote - Smaller supporting text.
* iOS: 13pt Regular
*/
footnote: {
fontFamily: fontFamily.regular,
fontSize: 13,
lineHeight: 18,
// 1.38x
letterSpacing: -0.08,
fontWeight: "400"
},
/**
* Caption 1 - Primary caption style.
* iOS: 12pt Regular
*/
caption1: {
fontFamily: fontFamily.regular,
fontSize: 12,
lineHeight: 16,
// 1.33x
letterSpacing: 0,
fontWeight: "400"
},
/**
* Caption 2 - Secondary caption style (smallest).
* iOS: 11pt Regular
*/
caption2: {
fontFamily: fontFamily.regular,
fontSize: 11,
lineHeight: 13,
// 1.18x
letterSpacing: 0.07,
fontWeight: "400"
}
};
function withWeight(style, weight) {
return {
...style,
fontWeight: weight
};
}
function emphasized(style) {
return withWeight(style, "600");
}
({
bodyEmphasis: emphasized(typography.body),
calloutEmphasis: emphasized(typography.callout),
footnoteEmphasis: emphasized(typography.footnote),
caption1Emphasis: emphasized(typography.caption1),
subheadlineEmphasis: emphasized(typography.subheadline)
});
// src/design-system/tokens/spacing.ts
var spacing = {
// Base scale
none: 0,
xxs: 2,
xs: 4,
sm: 8,
md: 12,
lg: 16,
xl: 20,
xxl: 24,
xxxl: 32,
xxxxl: 40,
xxxxxl: 48,
// Semantic spacing - Inset (padding)
// Used for content padding inside containers
inset: {
none: 0,
xs: 4,
sm: 8,
md: 12,
lg: 16,
// Standard content inset
xl: 20
},
// Semantic spacing - Stack (vertical)
// Used for vertical spacing between elements
stack: {
none: 0,
xs: 4,
sm: 8,
// Tight grouping
md: 12,
lg: 16,
// Standard section spacing
xl: 24
// Large section spacing
},
// Semantic spacing - Inline (horizontal)
// Used for horizontal spacing between elements
inline: {
none: 0,
xs: 4,
sm: 8,
// Icon to text spacing
md: 12,
lg: 16,
// Standard element spacing
xl: 24
}
};
// src/design-system/tokens/radius.ts
var radius = {
none: 0,
xs: 4,
sm: 8,
md: 12,
lg: 16,
xl: 20,
xxl: 24,
full: 9999
};
({
/**
* Button corner radius (matches iOS default).
*/
button: radius.sm,
/**
* Small button corner radius.
*/
buttonSmall: radius.xs,
/**
* Card corner radius.
*/
card: radius.md,
/**
* Input field corner radius.
*/
input: radius.sm,
/**
* Modal/Dialog corner radius.
*/
modal: radius.lg,
/**
* Bottom sheet corner radius.
*/
sheet: radius.xl,
/**
* Image thumbnail radius.
*/
thumbnail: radius.sm,
/**
* Avatar (circular) radius.
*/
avatar: radius.full,
/**
* Badge/Chip radius.
*/
badge: radius.full,
/**
* Pill button radius.
*/
pill: radius.full,
/**
* Tag/Label radius.
*/
tag: radius.xs,
/**
* Toast notification radius.
*/
toast: radius.md,
/**
* Tooltip radius.
*/
tooltip: radius.sm,
/**
* Popover radius.
*/
popover: radius.md,
/**
* Search bar radius.
*/
searchBar: radius.sm,
/**
* Segment control radius.
*/
segmentControl: radius.sm,
/**
* Slider track radius.
*/
slider: radius.full,
/**
* Progress bar radius.
*/
progressBar: radius.full
});
// src/design-system/tokens/shadows.ts
var shadowColors = {
light: "rgba(0, 0, 0, 1)"};
var lightShadows = {
/**
* No shadow.
*/
none: {
shadowColor: shadowColors.light,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0,
shadowRadius: 0,
elevation: 0
},
/**
* Extra small shadow - subtle lift.
* Use for: Subtle hover states, pressed buttons.
*/
xs: {
shadowColor: shadowColors.light,
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.04,
shadowRadius: 2,
elevation: 1
},
/**
* Small shadow - light elevation.
* Use for: Cards, list items, buttons.
*/
sm: {
shadowColor: shadowColors.light,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
elevation: 2
},
/**
* Medium shadow - standard elevation.
* Use for: Dropdown menus, popovers, floating action buttons.
*/
md: {
shadowColor: shadowColors.light,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.12,
shadowRadius: 8,
elevation: 4
},
/**
* Large shadow - prominent elevation.
* Use for: Modals, dialogs, navigation overlays.
*/
lg: {
shadowColor: shadowColors.light,
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.15,
shadowRadius: 16,
elevation: 8
},
/**
* Extra large shadow - high elevation.
* Use for: Bottom sheets, side panels.
*/
xl: {
shadowColor: shadowColors.light,
shadowOffset: { width: 0, height: 12 },
shadowOpacity: 0.18,
shadowRadius: 24,
elevation: 12
},
/**
* XXL shadow - maximum elevation.
* Use for: Full-screen overlays, critical modals.
*/
xxl: {
shadowColor: shadowColors.light,
shadowOffset: { width: 0, height: 16 },
shadowOpacity: 0.22,
shadowRadius: 32,
elevation: 16
}
};
({
/**
* Card shadow.
*/
card: lightShadows.sm,
/**
* Button shadow (when elevated).
*/
button: lightShadows.xs,
/**
* Pressed button shadow (reduced).
*/
buttonPressed: lightShadows.none,
/**
* Floating action button shadow.
*/
fab: lightShadows.md,
/**
* Dropdown/Popover shadow.
*/
dropdown: lightShadows.md,
/**
* Modal/Dialog shadow.
*/
modal: lightShadows.lg,
/**
* Bottom sheet shadow.
*/
sheet: lightShadows.xl,
/**
* Toast notification shadow.
*/
toast: lightShadows.md,
/**
* Navigation header shadow.
*/
header: lightShadows.xs,
/**
* Tab bar shadow.
*/
tabBar: lightShadows.xs
});
// src/design-system/tokens/animation.ts
var duration = {
instant: 0,
fastest: 50,
faster: 100,
fast: 150,
normal: 250,
slow: 350,
slower: 500,
slowest: 700
};
var easing = {
/**
* Linear - constant speed (rarely used for UI).
*/
linear: [0, 0, 1, 1],
/**
* Ease In - slow start, fast end.
* Use for: Elements leaving the screen.
*/
easeIn: [0.42, 0, 1, 1],
/**
* Ease Out - fast start, slow end.
* Use for: Elements entering the screen.
* This is the most common easing for iOS-style animations.
*/
easeOut: [0, 0, 0.58, 1],
/**
* Ease In Out - slow start and end.
* Use for: Elements that start and end on screen.
*/
easeInOut: [0.42, 0, 0.58, 1],
/**
* Default spring - balanced, natural feel.
* iOS default animation spring characteristics.
* Use for: Most interactive animations.
*/
spring: {
damping: 15,
stiffness: 150,
mass: 1
},
/**
* Gentle spring - soft, slow settling.
* Use for: Subtle movements, floating elements.
*/
springGentle: {
damping: 20,
stiffness: 100,
mass: 1
},
/**
* Bouncy spring - energetic, playful feel.
* Use for: Emphasis, celebrations, playful interactions.
* Use sparingly to avoid overwhelming users.
*/
springBouncy: {
damping: 10,
stiffness: 200,
mass: 1
}
};
var animation = {
duration,
easing
};
({
/**
* Button press feedback.
*/
buttonPress: {
duration: duration.faster,
easing: easing.easeOut
},
/**
* Button release feedback.
*/
buttonRelease: {
duration: duration.fast,
easing: easing.spring
},
/**
* Modal/Sheet appear.
*/
modalEnter: {
duration: duration.normal,
easing: easing.spring
},
/**
* Modal/Sheet dismiss.
*/
modalExit: {
duration: duration.fast,
easing: easing.easeIn
},
/**
* Page/Screen transition.
*/
pageTransition: {
duration: duration.slow,
easing: easing.easeInOut
},
/**
* Fade in content.
*/
fadeIn: {
duration: duration.normal,
easing: easing.easeOut
},
/**
* Fade out content.
*/
fadeOut: {
duration: duration.fast,
easing: easing.easeIn
},
/**
* Slide in from bottom.
*/
slideInUp: {
duration: duration.normal,
easing: easing.spring
},
/**
* Slide out to bottom.
*/
slideOutDown: {
duration: duration.fast,
easing: easing.easeIn
},
/**
* Scale up (appearing).
*/
scaleIn: {
duration: duration.normal,
easing: easing.springBouncy
},
/**
* Scale down (disappearing).
*/
scaleOut: {
duration: duration.fast,
easing: easing.easeIn
},
/**
* Expand/Collapse accordion.
*/
expand: {
duration: duration.normal,
easing: easing.easeInOut
},
/**
* Switch/Toggle animation.
*/
toggle: {
duration: duration.fast,
easing: easing.spring
},
/**
* Skeleton loading shimmer.
*/
skeleton: {
duration: duration.slowest,
easing: easing.linear
},
/**
* Toast notification appear.
*/
toastEnter: {
duration: duration.normal,
easing: easing.springBouncy
},
/**
* Toast notification dismiss.
*/
toastExit: {
duration: duration.fast,
easing: easing.easeIn
},
/**
* Haptic feedback timing.
*/
haptic: {
duration: duration.instant,
easing: easing.linear
}
});
({
/**
* Replaces spring/bouncy animations with simple fade.
*/
default: {
duration: duration.fast,
easing: easing.easeOut
},
/**
* Replaces sliding animations with fade.
*/
slide: {
duration: duration.faster,
easing: easing.easeOut
},
/**
* Replaces scale animations with fade.
*/
scale: {
duration: duration.faster,
easing: easing.easeOut
},
/**
* Instant change (no animation).
*/
instant: {
duration: duration.instant,
easing: easing.linear
}
});
// src/design-system/tokens/sizes.ts
var sizes = {
// Touch targets - Apple HIG minimum 44pt
touchTarget: {
/**
* Minimum touch target size (44pt).
* Required by Apple HIG for all interactive elements.
*/
minimum: 44,
/**
* Comfortable touch target (48pt).
* Recommended for frequently used actions.
*/
comfortable: 48,
/**
* Spacious touch target (56pt).
* For primary actions and accessibility.
*/
spacious: 56
},
// Icon sizes
icon: {
/**
* Extra small icon (12pt).
* Use for: Inline badges, tiny indicators.
*/
xs: 12,
/**
* Small icon (16pt).
* Use for: Inline text icons, compact UI.
*/
sm: 16,
/**
* Medium icon (20pt).
* Use for: Standard inline icons.
*/
md: 20,
/**
* Large icon (24pt).
* Use for: Navigation icons, tab bar icons.
*/
lg: 24,
/**
* Extra large icon (32pt).
* Use for: Featured icons, prominent actions.
*/
xl: 32,
/**
* XXL icon (40pt).
* Use for: Illustrations, large feature icons.
*/
xxl: 40
},
// Avatar sizes
avatar: {
/**
* Extra small avatar (24pt).
* Use for: Inline mentions, compact lists.
*/
xs: 24,
/**
* Small avatar (32pt).
* Use for: Comments, messaging.
*/
sm: 32,
/**
* Medium avatar (40pt).
* Use for: Standard list items.
*/
md: 40,
/**
* Large avatar (56pt).
* Use for: Profile cards, detailed lists.
*/
lg: 56,
/**
* Extra large avatar (72pt).
* Use for: Profile headers.
*/
xl: 72,
/**
* XXL avatar (96pt).
* Use for: Profile pages, settings.
*/
xxl: 96
},
// Button heights
button: {
/**
* Small button (32pt).
* Use for: Compact UI, inline actions.
* Note: May not meet touch target minimum.
*/
sm: 32,
/**
* Medium button (44pt) - DEFAULT.
* Use for: Standard buttons.
* Meets Apple HIG minimum touch target.
*/
md: 44,
/**
* Large button (50pt).
* Use for: Primary actions, CTAs.
*/
lg: 50,
/**
* Extra large button (56pt).
* Use for: Hero actions, onboarding.
*/
xl: 56
},
// Input heights
input: {
/**
* Small input (36pt).
* Use for: Compact forms, filters.
*/
sm: 36,
/**
* Medium input (44pt) - DEFAULT.
* Use for: Standard form inputs.
* Meets Apple HIG minimum touch target.
*/
md: 44,
/**
* Large input (52pt).
* Use for: Search bars, prominent inputs.
*/
lg: 52
}
};
var zIndex = {
/**
* Base level - standard content.
*/
base: 0,
/**
* Dropdown level - menus, selects.
*/
dropdown: 1e3,
/**
* Sticky level - headers, navigation.
*/
sticky: 1100,
/**
* Overlay level - background overlays.
*/
overlay: 1200,
/**
* Modal level - dialogs, sheets.
*/
modal: 1300,
/**
* Popover level - tooltips, popovers.
*/
popover: 1400,
/**
* Tooltip level - floating hints.
*/
tooltip: 1500,
/**
* Toast level - notifications (highest).
*/
toast: 1600
};
var breakpoints = {
/**
* Extra small - small phones.
*/
xs: 0,
/**
* Small - standard phones (iPhone SE+).
* iPhone SE: 375pt width.
*/
sm: 375,
/**
* Medium - large phones (iPhone Pro Max).
* iPhone Pro Max: 428pt width.
*/
md: 428,
/**
* Large - small tablets (iPad Mini).
* iPad Mini portrait: 744pt width.
*/
lg: 744,
/**
* Extra large - large tablets (iPad Pro).
* iPad Pro portrait: 1024pt width.
*/
xl: 1024
};
var opacity = {
/**
* Fully transparent.
*/
transparent: 0,
/**
* Disabled state opacity.
* iOS uses 0.3-0.4 for disabled elements.
*/
disabled: 0.38,
/**
* Medium opacity - overlays, secondary elements.
*/
medium: 0.6,
/**
* High opacity - emphasized content.
*/
high: 0.87,
/**
* Fully opaque.
*/
opaque: 1
};
// src/design-system/theme/defaultTheme.ts
var lightTheme = {
name: "August Light",
mode: "light",
colors: lightColors,
typography,
fontFamily,
spacing,
radius,
shadows: lightShadows,
animation,
sizes,
zIndex,
breakpoints,
opacity
};
var defaultContextValue = {
theme: lightTheme,
colorMode: "light",
colorModePreference: "system",
toggleColorMode: () => {
console.warn("ThemeProvider not found in component tree");
},
setColorMode: () => {
console.warn("ThemeProvider not found in component tree");
},
isDark: false,
isLight: true
};
var ThemeContext = React19.createContext(defaultContextValue);
function useTheme() {
const context = React19.useContext(ThemeContext);
if (context === defaultContextValue) {
console.warn(
"useTheme must be used within a ThemeProvider. Falling back to default light theme."
);
}
return context;
}
// src/design-system/components/primitives/Box.tsx
var Box = React19.forwardRef(function Box2({
children,
style,
backgroundColor,
padding,
paddingHorizontal,
paddingVertical,
paddingTop,
paddingRight,
paddingBottom,
paddingLeft,
margin,
marginHorizontal,
marginVertical,
marginTop,
marginRight,
marginBottom,
marginLeft,
borderRadius,
flex,
flexDirection,
alignItems,
justifyContent,
gap,
width,
height,
minHeight,
maxWidth,
shadow,
borderWidth,
borderColor,
position,
overflow,
visible = true,
...viewProps
}, ref) {
const { theme } = useTheme();
const resolveSpacing = (value) => {
if (value === void 0) return void 0;
if (typeof value === "number") return value;
return theme.spacing[value];
};
const resolveRadius = (value) => {
if (value === void 0) return void 0;
if (typeof value === "number") return value;
return theme.radius[value];
};
const resolveBackgroundColor = (color) => {
if (!color) return void 0;
if (color.includes(".")) {
const [group, key] = color.split(".");
const colorGroup = theme.colors[group];
if (colorGroup && typeof colorGroup === "object" && key in colorGroup) {
return colorGroup[key];
}
}
return color;
};
const computedStyle = React19.useMemo(() => {
const styles3 = {};
const bgColor = resolveBackgroundColor(backgroundColor);
if (bgColor) styles3.backgroundColor = bgColor;
if (padding !== void 0) styles3.padding = resolveSpacing(padding);
if (paddingHorizontal !== void 0) styles3.paddingHorizontal = resolveSpacing(paddingHorizontal);
if (paddingVertical !== void 0) styles3.paddingVertical = resolveSpacing(paddingVertical);
if (paddingTop !== void 0) styles3.paddingTop = resolveSpacing(paddingTop);
if (paddingRight !== void 0) styles3.paddingRight = resolveSpacing(paddingRight);
if (paddingBottom !== void 0) styles3.paddingBottom = resolveSpacing(paddingBottom);
if (paddingLeft !== void 0) styles3.paddingLeft = resolveSpacing(paddingLeft);
if (margin !== void 0) styles3.margin = resolveSpacing(margin);
if (marginHorizontal !== void 0) styles3.marginHorizontal = resolveSpacing(marginHorizontal);
if (marginVertical !== void 0) styles3.marginVertical = resolveSpacing(marginVertical);
if (marginTop !== void 0) styles3.marginTop = resolveSpacing(marginTop);
if (marginRight !== void 0) styles3.marginRight = resolveSpacing(marginRight);
if (marginBottom !== void 0) styles3.marginBottom = resolveSpacing(marginBottom);
if (marginLeft !== void 0) styles3.marginLeft = resolveSpacing(marginLeft);
if (borderRadius !== void 0) styles3.borderRadius = resolveRadius(borderRadius);
if (flex !== void 0) styles3.flex = flex;
if (flexDirection) styles3.flexDirection = flexDirection;
if (alignItems) styles3.alignItems = alignItems;
if (justifyContent) styles3.justifyContent = justifyContent;
if (gap !== void 0) styles3.gap = resolveSpacing(gap);
if (width !== void 0) styles3.width = width;
if (height !== void 0) styles3.height = height;
if (minHeight !== void 0) styles3.minHeight = minHeight;
if (maxWidth !== void 0) styles3.maxWidth = maxWidth;
if (shadow && shadow !== "none") {
const shadowStyle = theme.shadows[shadow];
Object.assign(styles3, shadowStyle);
}
if (borderWidth !== void 0) styles3.borderWidth = borderWidth;
if (borderColor) {
styles3.borderColor = resolveBackgroundColor(borderColor) || borderColor;
}
if (position) styles3.position = position;
if (overflow) styles3.overflow = overflow;
return styles3;
}, [
theme,
backgroundColor,
padding,
paddingHorizontal,
paddingVertical,
paddingTop,
paddingRight,
paddingBottom,
paddingLeft,
margin,
marginHorizontal,
marginVertical,
marginTop,
marginRight,
marginBottom,
marginLeft,
borderRadius,
flex,
flexDirection,
alignItems,
justifyContent,
gap,
width,
height,
minHeight,
maxWidth,
shadow,
borderWidth,
borderColor,
position,
overflow
]);
if (!visible) {
return null;
}
return /* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
ref,
style: [computedStyle, style],
...viewProps
},
children
);
});
// src/design-system/components/Icon/iconMap.ts
var iconMap = {
// ===========================================================================
// Actions
// ===========================================================================
"checkmark": {
ios: "checkmark",
android: "check"
},
"close": {
ios: "xmark",
android: "close"
},
"plus": {
ios: "plus",
android: "add"
},
"minus": {
ios: "minus",
android: "remove"
},
"search": {
ios: "magnifyingglass",
android: "search"
},
"edit": {
ios: "pencil",
android: "edit"
},
"delete": {
ios: "trash",
android: "delete"
},
"share": {
ios: "square.and.arrow.up",
android: "share"
},
"copy": {
ios: "doc.on.doc",
android: "content-copy"
},
"paste": {
ios: "doc.on.clipboard",
android: "content-paste"
},
"refresh": {
ios: "arrow.clockwise",
android: "refresh"
},
"download": {
ios: "arrow.down.circle",
android: "file-download"
},
"upload": {
ios: "arrow.up.circle",
android: "file-upload"
},
"send": {
ios: "paperplane.fill",
android: "send"
},
"reply": {
ios: "arrowshape.turn.up.left",
android: "reply"
},
"forward": {
ios: "arrowshape.turn.up.right",
android: "forward"
},
// ===========================================================================
// Navigation
// ===========================================================================
"back": {
ios: "chevron.left",
android: "arrow-back"
},
"forward-nav": {
ios: "chevron.right",
android: "arrow-forward"
},
"up": {
ios: "chevron.up",
android: "keyboard-arrow-up"
},
"down": {
ios: "chevron.down",
android: "keyboard-arrow-down"
},
"menu": {
ios: "line.3.horizontal",
android: "menu"
},
"more-horizontal": {
ios: "ellipsis",
android: "more-horiz"
},
"more-vertical": {
ios: "ellipsis",
android: "more-vert"
},
"home": {
ios: "house",
android: "home"
},
"settings": {
ios: "gearshape",
android: "settings"
},
// ===========================================================================
// Communication
// ===========================================================================
"chat": {
ios: "bubble.left",
android: "chat-bubble-outline"
},
"chat-fill": {
ios: "bubble.left.fill",
android: "chat-bubble"
},
"phone": {
ios: "phone",
android: "phone"
},
"video": {
ios: "video",
android: "videocam"
},
"mic": {
ios: "mic.fill",
android: "mic"
},
"mic-off": {
ios: "mic.slash.fill",
android: "mic-off"
},
"speaker": {
ios: "speaker.wave.2.fill",
android: "volume-up"
},
"speaker-off": {
ios: "speaker.slash.fill",
android: "volume-off"
},
// ===========================================================================
// Media
// ===========================================================================
"camera": {
ios: "camera.fill",
android: "camera-alt"
},
"image": {
ios: "photo",
android: "image"
},
"play": {
ios: "play.fill",
android: "play-arrow"
},
"pause": {
ios: "pause.fill",
android: "pause"
},
"stop": {
ios: "stop.fill",
android: "stop"
},
"volume": {
ios: "speaker.wave.2.fill",
android: "volume-up"
},
"volume-off": {
ios: "speaker.slash.fill",
android: "volume-off"
},
// ===========================================================================
// People
// ===========================================================================
"person": {
ios: "person",
android: "person-outline"
},
"person-fill": {
ios: "person.fill",
android: "person"
},
"people": {
ios: "person.2",
android: "people-outline"
},
"people-fill": {
ios: "person.2.fill",
android: "people"
},
// ===========================================================================
// Status
// ===========================================================================
"info": {
ios: "info.circle",
android: "info-outline"
},
"warning": {
ios: "exclamationmark.triangle",
android: "warning"
},
"error": {
ios: "exclamationmark.circle",
android: "error-outline"
},
"success": {
ios: "checkmark.circle",
android: "check-circle-outline"
},
"help": {
ios: "questionmark.circle",
android: "help-outline"
},
// ===========================================================================
// Objects
// ===========================================================================
"attach": {
ios: "paperclip",
android: "attach-file"
},
"link": {
ios: "link",
android: "link"
},
"location": {
ios: "location.fill",
android: "location-on"
},
"calendar": {
ios: "calendar",
android: "event"
},
"clock": {
ios: "clock",
android: "schedule"
},
"heart": {
ios: "heart",
android: "favorite-outline"
},
"heart-fill": {
ios: "heart.fill",
android: "favorite"
},
"star": {
ios: "star",
android: "star-outline"
},
"star-fill": {
ios: "star.fill",
android: "star"
},
"bookmark": {
ios: "bookmark",
android: "bookmark-outline"
},
"bookmark-fill": {
ios: "bookmark.fill",
android: "bookmark"
},
"bell": {
ios: "bell",
android: "notifications-none"
},
"bell-fill": {
ios: "bell.fill",
android: "notifications"
},
"lock": {
ios: "lock.fill",
android: "lock"
},
"unlock": {
ios: "lock.open.fill",
android: "lock-open"
},
"eye": {
ios: "eye",
android: "visibility"
},
"eye-off": {
ios: "eye.slash",
android: "visibility-off"
},
"document": {
ios: "doc",
android: "description"
},
"folder": {
ios: "folder",
android: "folder"
},
// ===========================================================================
// Arrows & Indicators
// ===========================================================================
"arrow-up": {
ios: "arrow.up",
android: "arrow-upward"
},
"arrow-down": {
ios: "arrow.down",
android: "arrow-downward"
},
"arrow-left": {
ios: "arrow.left",
android: "arrow-back"
},
"arrow-right": {
ios: "arrow.right",
android: "arrow-forward"
},
"external-link": {
ios: "arrow.up.right.square",
android: "open-in-new"
},
// ===========================================================================
// Read Receipts (Chat specific)
// ===========================================================================
"check-single": {
ios: "checkmark",
android: "check"
},
"check-double": {
ios: "checkmark",
// Will render two checkmarks
android: "done-all"
},
"clock-pending": {
ios: "clock",
android: "schedule"
}
};
function getPlatformIconName(commonName, platform) {
const mapping = iconMap[commonName];
if (!mapping) {
return commonName;
}
if (platform === "ios") {
return mapping.ios;
}
return mapping.web || mapping.android;
}
function isCommonIconName(name) {
return name in iconMap;
}
function getIconSize(size) {
if (typeof size === "number") {
return size;
}
const sizes2 = {
xs: 12,
sm: 16,
md: 20,
lg: 24,
xl: 32
};
return sizes2[size];
}
function getIconColor(color, theme) {
const semanticColors = {
primary: theme.colors.label.primary,
secondary: theme.colors.label.secondary,
tertiary: theme.colors.label.tertiary,
tint: theme.colors.interactive.tint,
error: theme.colors.semantic.error,
success: theme.colors.semantic.success,
warning: theme.colors.semantic.warning,
info: theme.colors.semantic.info
};
if (color in semanticColors) {
return semanticColors[color];
}
return color;
}
function createIconStyles(theme) {
return reactNative.StyleSheet.create({
container: {
alignItems: "center",
justifyContent: "center"
}
});
}
// src/design-system/components/Icon/Icon.tsx
function Icon({
// Content
name,
children,
// Appearance
size = "md",
color = "primary",
weight = "regular",
renderingMode = "monochrome",
// Animation (iOS 17+)
symbolEffect,
symbolEffectActive = true,
// Accessibility
testID,
accessibilityLabel,
accessibilityHidden = true,
// Style
style
}) {
const { theme } = useTheme();
const resolvedSize = React19.useMemo(() => getIconSize(size), [size]);
const resolvedColor = React19.useMemo(
() => getIconColor(color, theme),
[color, theme]
);
const baseStyles = React19.useMemo(() => createIconStyles(), [theme]);
const platformIconName = React19.useMemo(() => {
if (!name) return null;
const platform = reactNative.Platform.OS === "ios" ? "ios" : "android";
if (isCommonIconName(name)) {
return getPlatformIconName(name, platform);
}
return name;
}, [name]);
const accessibilityProps = React19.useMemo(
() => ({
accessible: !accessibilityHidden,
accessibilityRole: "image",
accessibilityLabel,
accessibilityElementsHidden: accessibilityHidden,
importantForAccessibility: accessibilityHidden ? "no-hide-descendants" : "auto"
}),
[accessibilityLabel, accessibilityHidden]
);
if (children) {
return /* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.container,
{ width: resolvedSize, height: resolvedSize },
style
],
testID,
...accessibilityProps
},
children
);
}
if (!platformIconName) {
return null;
}
return /* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [baseStyles.container, style],
testID,
...accessibilityProps
},
/* @__PURE__ */ React19__default.default.createElement(
MaterialIcons__default.default,
{
name: platformIconName,
size: resolvedSize,
color: resolvedColor
}
)
);
}
Icon.displayName = "Icon";
var AnimatedPressable = Animated6__default.default.createAnimatedComponent(reactNative.Pressable);
function Pressable({
// Content
children,
// Events
onPress,
onLongPress,
onPressIn,
onPressOut,
onLayout,
delayLongPress = 500,
delayPressIn = 0,
delayPressOut = 0,
// State
disabled = false,
// Visual Feedback
pressAnimation = "opacity",
pressedOpacity = 0.7,
pressedScale = 0.98,
disabledOpacity = 0.5,
enableRipple = reactNative.Platform.OS === "android",
rippleColor,
rippleBorderless = false,
// Haptics
hapticFeedback = "light",
hapticOnPressIn = true,
// Hit Area
hitSlop,
pressRetentionOffset,
// Accessibility
testID,
accessibilityLabel,
accessibilityHint,
accessibilityRole = "button",
accessibilityState,
// Styling
style,
pressedStyle,
disabledStyle
}) {
const { theme } = useTheme();
const pressed = Animated6.useSharedValue(0);
const triggerHaptic = React19.useCallback(() => {
if (hapticFeedback === "none" || disabled) return;
}, [hapticFeedback, disabled]);
const handlePressIn = React19.useCallback(
(event) => {
pressed.value = Animated6.withTiming(1, { duration: 100 });
if (hapticOnPressIn) {
triggerHaptic();
}
onPressIn?.(event);
},
[onPressIn, pressed, hapticOnPressIn, triggerHaptic]
);
const handlePressOut = React19.useCallback(
(event) => {
pressed.value = Animated6.withTiming(0, { duration: 150 });
onPressOut?.(event);
},
[onPressOut, pressed]
);
const handlePress = React19.useCallback(
(event) => {
if (disabled) return;
if (!hapticOnPressIn) {
triggerHaptic();
}
onPress?.(event);
},
[onPress, disabled, hapticOnPressIn, triggerHaptic]
);
const animatedStyle = Animated6.useAnimatedStyle(() => {
const animationStyles = {};
if (pressAnimation === "opacity" || pressAnimation === "both") {
animationStyles.opacity = Animated6.interpolate(
pressed.value,
[0, 1],
[1, pressedOpacity]
);
}
if (pressAnimation === "scale" || pressAnimation === "both") {
animationStyles.transform = [
{
scale: Animated6.interpolate(pressed.value, [0, 1], [1, pressedScale])
}
];
}
return animationStyles;
}, [pressAnimation, pressedOpacity, pressedScale]);
const computedAccessibilityState = React19.useMemo(
() => ({
disabled,
...accessibilityState
}),
[disabled, accessibilityState]
);
const androidRipple = React19.useMemo(() => {
if (!enableRipple || reactNative.Platform.OS !== "android") {
return void 0;
}
return {
color: rippleColor || theme.colors.interactive.tint,
borderless: rippleBorderless
};
}, [enableRipple, rippleColor, rippleBorderless, theme.colors.interactive.tint]);
const resolveStyle = React19.useCallback(
({ pressed: isPressed }) => {
const baseStyle = typeof style === "function" ? style({ pressed: isPressed }) : style;
return [
baseStyle,
disabled && { opacity: disabledOpacity },
disabled && disabledStyle,
isPressed && pressedStyle
];
},
[style, disabled, disabledOpacity, disabledStyle, pressedStyle]
);
const renderChildren = React19.useCallback(
({ pressed: isPressed }) => {
if (typeof children === "function") {
return children({ pressed: isPressed });
}
return children;
},
[children]
);
return /* @__PURE__ */ React19__default.default.createElement(
AnimatedPressable,
{
testID,
onPress: handlePress,
onLongPress: disabled ? void 0 : onLongPress,
onPressIn: handlePressIn,
onPressOut: handlePressOut,
onLayout,
delayLongPress,
disabled,
hitSlop,
android_ripple: androidRipple,
accessibilityLabel,
accessibilityHint,
accessibilityRole,
accessibilityState: computedAccessibilityState,
style: [animatedStyle, resolveStyle]
},
renderChildren
);
}
Pressable.displayName = "Pressable";
function createDividerStyles(theme) {
return reactNative.StyleSheet.create({
// Container for horizontal divider
containerHorizontal: {
flexDirection: "row",
alignItems: "center",
width: "100%"
},
// Container for vertical divider
containerVertical: {
flexDirection: "column",
alignItems: "center",
alignSelf: "stretch"
},
// Base line style
line: {
backgroundColor: theme.colors.separator.nonOpaque
},
// Horizontal line
lineHorizontal: {
height: reactNative.StyleSheet.hairlineWidth,
flex: 1
},
// Vertical line
lineVertical: {
width: reactNative.StyleSheet.hairlineWidth,
flex: 1
},
// Label container (for withLabel variant)
labelContainer: {
paddingHorizontal: theme.spacing.md
},
// Label text
labelText: {
...theme.typography.caption1,
color: theme.colors.label.tertiary,
textTransform: "uppercase",
letterSpacing: 0.5
}
});
}
function getDynamicDividerStyles(variant, orientation, insetLeft, insetRight, spacing2, thickness, color, theme) {
const styles3 = {
container: {},
line: {
backgroundColor: color || theme.colors.separator.nonOpaque
}
};
if (orientation === "horizontal") {
styles3.line.height = thickness;
} else {
styles3.line.width = thickness;
}
if (spacing2 > 0) {
if (orientation === "horizontal") {
styles3.container.marginVertical = spacing2;
} else {
styles3.container.marginHorizontal = spacing2;
}
}
if (orientation === "horizontal") {
switch (variant) {
case "inset":
styles3.container.paddingLeft = insetLeft;
break;
case "middle":
styles3.container.paddingLeft = insetLeft;
styles3.container.paddingRight = insetRight;
break;
}
}
return styles3;
}
// src/design-system/components/Divider/Divider.tsx
function Divider({
// Variant & Orientation
variant = "full",
orientation = "horizontal",
// Content
label,
// Appearance
thickness = reactNative.StyleSheet.hairlineWidth,
color,
insetLeft = 16,
insetRight = 16,
spacing: spacing2 = 0,
// Accessibility
testID,
decorative = true,
// Styling
style,
labelStyle,
lineStyle
}) {
const { theme } = useTheme();
const baseStyles = React19.useMemo(() => createDividerStyles(theme), [theme]);
const dynamicStyles = React19.useMemo(
() => getDynamicDividerStyles(
variant,
orientation,
insetLeft,
insetRight,
spacing2,
thickness,
color,
theme
),
[variant, orientation, insetLeft, insetRight, spacing2, thickness, color, theme]
);
const accessibilityProps = React19.useMemo(
() => ({
accessible: !decorative,
accessibilityRole: decorative ? void 0 : "none",
accessibilityElementsHidden: decorative,
importantForAccessibility: decorative ? "no-hide-descendants" : "auto"
}),
[decorative]
);
if (variant === "withLabel" && label && orientation === "horizontal") {
return /* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.containerHorizontal,
dynamicStyles.container,
style
],
testID,
...accessibilityProps
},
/* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.line,
baseStyles.lineHorizontal,
dynamicStyles.line,
lineStyle
]
}
),
/* @__PURE__ */ React19__default.default.createElement(reactNative.View, { style: baseStyles.labelContainer }, /* @__PURE__ */ React19__default.default.createElement(reactNative.Text, { style: [baseStyles.labelText, labelStyle] }, label)),
/* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.line,
baseStyles.lineHorizontal,
dynamicStyles.line,
lineStyle
]
}
)
);
}
if (orientation === "vertical") {
return /* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.containerVertical,
dynamicStyles.container,
style
],
testID,
...accessibilityProps
},
/* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.line,
baseStyles.lineVertical,
dynamicStyles.line,
lineStyle
]
}
)
);
}
return /* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.containerHorizontal,
dynamicStyles.container,
style
],
testID,
...accessibilityProps
},
/* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: [
baseStyles.line,
baseStyles.lineHorizontal,
dynamicStyles.line,
lineStyle
]
}
)
);
}
Divider.displayName = "Divider";
function useReducedMotion() {
const [reducedMotion, setReducedMotion] = React19.useState(false);
React19.useEffect(() => {
reactNative.AccessibilityInfo.isReduceMotionEnabled().then(setReducedMotion);
const subscription = reactNative.AccessibilityInfo.addEventListener(
"reduceMotionChanged",
setReducedMotion
);
return () => {
subscription.remove();
};
}, []);
return reducedMotion;
}
// src/design-system/components/Spinner/Spinner.tsx
function getSpinnerSize(size) {
if (typeof size === "number") {
return size;
}
const sizes2 = {
xs: 16,
sm: 20,
md: 28,
lg: 40
};
return sizes2[size];
}
function Spinner({
// Appearance
size = "md",
color = "tint",
// Label
label,
labelPosition = "bottom",
// Behavior
animating = true,
hidesWhenStopped = true,
// Accessibility
testID,
accessibilityLabel = "Loading",
// Styling
style,
labelStyle
}) {
const { theme } = useTheme();
const reducedMotion = useReducedMotion();
const resolvedSize = React19.useMemo(() => getSpinnerSize(size), [size]);
const resolvedColor = React19.useMemo(() => {
const colorMap = {
primary: theme.colors.label.primary,
secondary: theme.colors.label.secondary,
tint: theme.colors.interactive.tint,
onPrimary: theme.colors.background.primary,
onSurface: theme.colors.label.primary
};
return colorMap[color] || color;
}, [color, theme]);
const activityIndicatorSize = React19.useMemo(() => {
return resolvedSize <= 24 ? "small" : "large";
}, [resolvedSize]);
if (!animating && hidesWhenStopped) {
return null;
}
const containerStyle = React19.useMemo(
() => [
styles.container,
labelPosition === "right" ? styles.containerRow : styles.containerColumn,
style
],
[labelPosition, style]
);
const labelTextStyle = React19.useMemo(
() => [
styles.label,
{
...theme.typography.subheadline,
color: theme.colors.label.secondary
},
labelPosition === "right" ? styles.labelRight : styles.labelBottom,
labelStyle
],
[theme, labelPosition, labelStyle]
);
return /* @__PURE__ */ React19__default.default.createElement(
reactNative.View,
{
style: containerStyle,
testID,
accessible: true,
accessibilityLabel,
accessibilityRole: "progressbar",
accessibilityState: { busy: animating }
},
/* @__PURE__ */ React19__default.default.createElement(
reactNative.ActivityIndicator,
{
animating: animating && !reducedMotion,
size: activityIndicatorSize,
color: resolvedColor,
style: { width: resolvedSize, height: resolvedSize },
testID: testID ? `${testID}-indicator` : void 0
}
),
label && /* @__PURE__ */ React19__default.default.createElement(reactNative.Text, { style: labelTextStyle }, label)
);
}
var styles = reactNative.StyleSheet.create({
container: {
alignItems: "center",
justifyContent: "center"
},
containerColumn: {
flexDirection: "column"
},
containerRow: {
flexDirection: "row"
},
label: {
textAlign: "center"
},
labelBottom: {
marginTop: 8
},
labelRight: {
marginLeft: 8
}
});
Spinner.displayName = "Spinner";
function createToastStyles(theme) {
return reactNative.StyleSheet.create({
container: {
flexDirection: "row",
alignItems: "center",
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
borderRadius: theme.radius.md,
marginHorizontal: theme.spacing.lg,
minHeight: 48,
...theme.shadows.lg
},
content: {
flex: 1,
marginLeft: theme.spacing.sm
},
title: {
...theme.typography.subheadline,
fontWeight: "600",
marginBottom: 2
},
message: {
...theme.typography.subheadline
},
iconContainer: {
width: 24,
height: 24,
alignItems: "center",
justifyContent: "center"
},
actionButton: {
marginLeft: theme.spacing.md,
paddingVertical: theme.spacing.xs,
paddingHorizontal: theme.spacing.sm
},
actionText: {
...theme.typography.subheadline,
fontWeight: "600"
},
closeButton: {
marginLeft: theme.spacing.sm,
padding: theme.spacing.xs
}
});
}
function getVariantStyles(variant, theme) {
const styl