Live video on the AT Protocol
at eli/docker-deployment-docs 814 lines 16 kB view raw
1/** 2 * Theme atoms - Enhanced exports with pairify function for array-style syntax 3 * These provide direct access to static design tokens and support composition 4 */ 5 6import { Platform } from "react-native"; 7import { 8 animations, 9 borderRadius, 10 colors as rawColors, 11 shadows, 12 spacing, 13 touchTargets, 14 typography, 15} from "./tokens"; 16 17// Type for style objects that can be spread 18type StyleValue = Record<string, any>; 19 20/** 21 * Pairify function - converts nested objects into key-value pairs that return style objects 22 * This allows for array-style syntax like style={[a.borders.green[300], a.shadows.xl]} 23 */ 24function pairify<T extends Record<string, any>>( 25 obj: T, 26 styleKeyPrefix: string, 27): Record<keyof T, StyleValue> { 28 const result: Record<string, StyleValue> = {}; 29 30 for (const [key, value] of Object.entries(obj)) { 31 if (typeof value === "object" && value !== null && !Array.isArray(value)) { 32 // For nested objects (like color scales), create another level 33 result[key] = {}; 34 for (const [nestedKey, nestedValue] of Object.entries(value)) { 35 result[key][nestedKey] = { [styleKeyPrefix]: nestedValue }; 36 } 37 } else { 38 // For simple values, create the style object directly 39 result[key] = { [styleKeyPrefix]: value }; 40 } 41 } 42 43 return result as Record<keyof T, StyleValue>; 44} 45 46/** 47 * Create pairified style atoms for easy composition 48 */ 49 50// Re-export static design tokens that don't change with theme 51export { animations, borderRadius, shadows, spacing, touchTargets }; 52 53// Export raw color tokens for advanced use cases 54export const colors = rawColors; 55 56// Platform-aware typography helper 57export const getPlatformTypography = () => { 58 if (Platform.OS === "ios") { 59 return typography.ios; 60 } else if (Platform.OS === "android") { 61 return typography.android; 62 } 63 return typography.universal; 64}; 65 66// Export all typography scales 67export const typographyAtoms = { 68 platform: getPlatformTypography(), 69 universal: typography.universal, 70 ios: typography.ios, 71 android: typography.android, 72}; 73 74// Static icon sizes (colors are handled by theme) 75export const iconSizes = { 76 sm: 16, 77 md: 20, 78 lg: 24, 79 xl: 32, 80}; 81 82// Common layout utilities 83export const layout = { 84 flex: { 85 center: { 86 justifyContent: "center" as const, 87 alignItems: "center" as const, 88 }, 89 alignCenter: { 90 alignItems: "center" as const, 91 }, 92 justifyCenter: { 93 justifyContent: "center" as const, 94 }, 95 row: { 96 flexDirection: "row" as const, 97 }, 98 column: { 99 flexDirection: "column" as const, 100 }, 101 spaceBetween: { 102 justifyContent: "space-between" as const, 103 }, 104 spaceAround: { 105 justifyContent: "space-around" as const, 106 }, 107 spaceEvenly: { 108 justifyContent: "space-evenly" as const, 109 }, 110 direction: pairify( 111 { 112 row: "row", 113 column: "column", 114 "row-reverse": "row-reverse", 115 "column-reverse": "column-reverse", 116 }, 117 "flexDirection", 118 ), 119 justify: pairify( 120 { 121 start: "flex-start", 122 end: "flex-end", 123 center: "center", 124 between: "space-between", 125 around: "space-around", 126 evenly: "space-evenly", 127 }, 128 "justifyContent", 129 ), 130 align: pairify( 131 { 132 start: "flex-start", 133 end: "flex-end", 134 center: "center", 135 stretch: "stretch", 136 baseline: "baseline", 137 }, 138 "alignItems", 139 ), 140 wrap: pairify( 141 { 142 wrap: "wrap", 143 nowrap: "nowrap", 144 reverse: "wrap-reverse", 145 }, 146 "flexWrap", 147 ), 148 }, 149 position: pairify( 150 { 151 absolute: "absolute", 152 relative: "relative", 153 static: "static", 154 }, 155 "position", 156 ), 157}; 158 159// Enhanced border utilities with pairified colors and widths 160export const borders = { 161 width: pairify( 162 { 163 thin: 1, 164 medium: 2, 165 thick: 4, 166 }, 167 "borderWidth", 168 ), 169 170 style: pairify( 171 { 172 solid: "solid", 173 dashed: "dashed", 174 dotted: "dotted", 175 }, 176 "borderStyle", 177 ), 178 179 // Pairified color borders 180 color: pairify(rawColors, "borderColor"), 181 182 // Top border utilities 183 top: { 184 width: pairify( 185 { 186 thin: 1, 187 medium: 2, 188 thick: 4, 189 }, 190 "borderTopWidth", 191 ), 192 color: pairify(rawColors, "borderTopColor"), 193 }, 194 195 // Bottom border utilities 196 bottom: { 197 width: pairify( 198 { 199 thin: 1, 200 medium: 2, 201 thick: 4, 202 }, 203 "borderBottomWidth", 204 ), 205 color: pairify(rawColors, "borderBottomColor"), 206 }, 207 208 // Left border utilities 209 left: { 210 width: pairify( 211 { 212 thin: 1, 213 medium: 2, 214 thick: 4, 215 }, 216 "borderLeftWidth", 217 ), 218 color: pairify(rawColors, "borderLeftColor"), 219 }, 220 221 // Right border utilities 222 right: { 223 width: pairify( 224 { 225 thin: 1, 226 medium: 2, 227 thick: 4, 228 }, 229 "borderRightWidth", 230 ), 231 color: pairify(rawColors, "borderRightColor"), 232 }, 233}; 234 235// Pairified spacing utilities 236export const spacingAtoms = { 237 margin: pairify(spacing, "margin"), 238 marginTop: pairify(spacing, "marginTop"), 239 marginRight: pairify(spacing, "marginRight"), 240 marginBottom: pairify(spacing, "marginBottom"), 241 marginLeft: pairify(spacing, "marginLeft"), 242 marginHorizontal: pairify(spacing, "marginHorizontal"), 243 marginVertical: pairify(spacing, "marginVertical"), 244 245 padding: pairify(spacing, "padding"), 246 paddingTop: pairify(spacing, "paddingTop"), 247 paddingRight: pairify(spacing, "paddingRight"), 248 paddingBottom: pairify(spacing, "paddingBottom"), 249 paddingLeft: pairify(spacing, "paddingLeft"), 250 paddingHorizontal: pairify(spacing, "paddingHorizontal"), 251 paddingVertical: pairify(spacing, "paddingVertical"), 252}; 253 254// Pairified border radius utilities 255export const radiusAtoms = { 256 all: pairify(borderRadius, "borderRadius"), 257 top: pairify(borderRadius, "borderTopLeftRadius"), 258 topRight: pairify(borderRadius, "borderTopRightRadius"), 259 bottom: pairify(borderRadius, "borderBottomLeftRadius"), 260 bottomRight: pairify(borderRadius, "borderBottomRightRadius"), 261 left: pairify(borderRadius, "borderTopLeftRadius"), 262 right: pairify(borderRadius, "borderTopRightRadius"), 263}; 264 265// Background color utilities 266export const backgrounds = pairify(rawColors, "backgroundColor"); 267 268// Text color utilities 269export const textColors = pairify(rawColors, "color"); 270 271// Percentage-based sizes 272const percentageSizes = { 273 "10": "10%", 274 "20": "20%", 275 "25": "25%", 276 "30": "30%", 277 "33": "33.333333%", 278 "40": "40%", 279 "50": "50%", 280 "60": "60%", 281 "66": "66.666667%", 282 "70": "70%", 283 "75": "75%", 284 "80": "80%", 285 "90": "90%", 286 "100": "100%", 287} as const; 288 289// Size utilities (width and height) 290export const sizes = { 291 width: { 292 ...pairify(spacing, "width"), 293 percent: pairify(percentageSizes, "width"), 294 }, 295 height: { 296 ...pairify(spacing, "height"), 297 percent: pairify(percentageSizes, "height"), 298 }, 299 minWidth: { 300 ...pairify(spacing, "minWidth"), 301 percent: pairify(percentageSizes, "minWidth"), 302 }, 303 minHeight: { 304 ...pairify(spacing, "minHeight"), 305 percent: pairify(percentageSizes, "minHeight"), 306 }, 307 maxWidth: { 308 ...pairify(spacing, "maxWidth"), 309 percent: pairify(percentageSizes, "maxWidth"), 310 }, 311 maxHeight: { 312 ...pairify(spacing, "maxHeight"), 313 percent: pairify(percentageSizes, "maxHeight"), 314 }, 315}; 316 317// Flex utilities 318export const flex = { 319 values: pairify( 320 { 321 0: 0, 322 1: 1, 323 2: 2, 324 3: 3, 325 4: 4, 326 5: 5, 327 }, 328 "flex", 329 ), 330 331 grow: pairify( 332 { 333 0: 0, 334 1: 1, 335 }, 336 "flexGrow", 337 ), 338 339 shrink: pairify( 340 { 341 0: 0, 342 1: 1, 343 }, 344 "flexShrink", 345 ), 346 347 basis: { 348 ...pairify(spacing, "flexBasis"), 349 ...pairify(percentageSizes, "flexBasis"), 350 auto: { flexBasis: "auto" }, 351 }, 352}; 353 354// Opacity utilities 355export const opacity = pairify( 356 { 357 0: 0, 358 5: 0.05, 359 10: 0.1, 360 20: 0.2, 361 25: 0.25, 362 30: 0.3, 363 40: 0.4, 364 50: 0.5, 365 60: 0.6, 366 70: 0.7, 367 75: 0.75, 368 80: 0.8, 369 90: 0.9, 370 95: 0.95, 371 100: 1, 372 }, 373 "opacity", 374); 375 376// Z-index utilities 377export const zIndex = pairify( 378 { 379 0: 0, 380 10: 10, 381 20: 20, 382 30: 30, 383 40: 40, 384 50: 50, 385 auto: "auto", 386 }, 387 "zIndex", 388); 389 390// Overflow utilities 391export const overflow = pairify( 392 { 393 visible: "visible", 394 hidden: "hidden", 395 scroll: "scroll", 396 }, 397 "overflow", 398); 399 400// Text alignment utilities 401export const textAlign = pairify( 402 { 403 left: "left", 404 center: "center", 405 right: "right", 406 justify: "justify", 407 auto: "auto", 408 }, 409 "textAlign", 410); 411 412// Font weight utilities 413export const fontWeight = pairify( 414 { 415 thin: "100", 416 extralight: "200", 417 light: "300", 418 normal: "400", 419 medium: "500", 420 semibold: "600", 421 bold: "700", 422 extrabold: "800", 423 black: "900", 424 }, 425 "fontWeight", 426); 427 428// Font size utilities (separate from typography for quick access) 429export const fontSize = pairify( 430 { 431 xs: 12, 432 sm: 14, 433 base: 16, 434 lg: 18, 435 xl: 20, 436 "2xl": 24, 437 "3xl": 30, 438 "4xl": 36, 439 "5xl": 48, 440 "6xl": 60, 441 "7xl": 72, 442 "8xl": 96, 443 "9xl": 128, 444 }, 445 "fontSize", 446); 447 448// Line height utilities 449export const lineHeight = pairify( 450 { 451 none: 1, 452 tight: 1.25, 453 snug: 1.375, 454 normal: 1.5, 455 relaxed: 1.625, 456 loose: 2, 457 3: 12, 458 4: 16, 459 5: 20, 460 6: 24, 461 7: 28, 462 8: 32, 463 9: 36, 464 10: 40, 465 }, 466 "lineHeight", 467); 468 469// Letter spacing utilities 470export const letterSpacing = pairify( 471 { 472 tighter: -0.5, 473 tight: -0.25, 474 normal: 0, 475 wide: 0.25, 476 wider: 0.5, 477 widest: 1, 478 }, 479 "letterSpacing", 480); 481 482// Text transform utilities 483export const textTransform = pairify( 484 { 485 uppercase: "uppercase", 486 lowercase: "lowercase", 487 capitalize: "capitalize", 488 none: "none", 489 }, 490 "textTransform", 491); 492 493// Text decoration utilities 494export const textDecoration = pairify( 495 { 496 none: "none", 497 underline: "underline", 498 lineThrough: "line-through", 499 underlineLineThrough: "underline line-through", 500 }, 501 "textDecorationLine", 502); 503 504// Text align vertical utilities (React Native specific) 505export const textAlignVertical = pairify( 506 { 507 auto: "auto", 508 top: "top", 509 bottom: "bottom", 510 center: "center", 511 }, 512 "textAlignVertical", 513); 514 515// Transform utilities 516export const transforms = { 517 rotate: pairify( 518 { 519 0: 0, 520 1: 1, 521 2: 2, 522 3: 3, 523 6: 6, 524 12: 12, 525 45: 45, 526 90: 90, 527 180: 180, 528 270: 270, 529 }, 530 "rotate", 531 ), 532 533 scale: pairify( 534 { 535 0: 0, 536 50: 0.5, 537 75: 0.75, 538 90: 0.9, 539 95: 0.95, 540 100: 1, 541 105: 1.05, 542 110: 1.1, 543 125: 1.25, 544 150: 1.5, 545 200: 2, 546 }, 547 "scale", 548 ), 549 550 scaleX: pairify( 551 { 552 0: 0, 553 50: 0.5, 554 75: 0.75, 555 90: 0.9, 556 95: 0.95, 557 100: 1, 558 105: 1.05, 559 110: 1.1, 560 125: 1.25, 561 150: 1.5, 562 200: 2, 563 }, 564 "scaleX", 565 ), 566 567 scaleY: pairify( 568 { 569 0: 0, 570 50: 0.5, 571 75: 0.75, 572 90: 0.9, 573 95: 0.95, 574 100: 1, 575 105: 1.05, 576 110: 1.1, 577 125: 1.25, 578 150: 1.5, 579 200: 2, 580 }, 581 "scaleY", 582 ), 583 584 translateX: pairify(spacing, "translateX"), 585 translateY: pairify(spacing, "translateY"), 586}; 587 588// Absolute positioning utilities 589export const position = { 590 top: pairify(spacing, "top"), 591 right: pairify(spacing, "right"), 592 bottom: pairify(spacing, "bottom"), 593 left: pairify(spacing, "left"), 594 595 // Common position combinations 596 topLeft: (top: number, left: number) => ({ 597 position: "absolute" as const, 598 top, 599 left, 600 }), 601 topRight: (top: number, right: number) => ({ 602 position: "absolute" as const, 603 top, 604 right, 605 }), 606 bottomLeft: (bottom: number, left: number) => ({ 607 position: "absolute" as const, 608 bottom, 609 left, 610 }), 611 bottomRight: (bottom: number, right: number) => ({ 612 position: "absolute" as const, 613 bottom, 614 right, 615 }), 616 617 // Percentage-based positioning 618 percent: { 619 top: pairify( 620 { 621 0: "0%", 622 25: "25%", 623 50: "50%", 624 75: "75%", 625 100: "100%", 626 }, 627 "top", 628 ), 629 right: pairify( 630 { 631 0: "0%", 632 25: "25%", 633 50: "50%", 634 75: "75%", 635 100: "100%", 636 }, 637 "right", 638 ), 639 bottom: pairify( 640 { 641 0: "0%", 642 25: "25%", 643 50: "50%", 644 75: "75%", 645 100: "100%", 646 }, 647 "bottom", 648 ), 649 left: pairify( 650 { 651 0: "0%", 652 25: "25%", 653 50: "50%", 654 75: "75%", 655 100: "100%", 656 }, 657 "left", 658 ), 659 }, 660}; 661 662// Aspect ratio utilities (React Native 0.71+) 663export const aspectRatio = pairify( 664 { 665 square: 1, 666 video: 16 / 9, 667 photo: 4 / 3, 668 portrait: 3 / 4, 669 wide: 21 / 9, 670 ultrawide: 32 / 9, 671 "1/1": 1, 672 "3/2": 3 / 2, 673 "4/3": 4 / 3, 674 "16/9": 16 / 9, 675 "21/9": 21 / 9, 676 }, 677 "aspectRatio", 678); 679 680// Gap utilities (React Native 0.71+) 681export const gap = { 682 row: pairify(spacing, "rowGap"), 683 column: pairify(spacing, "columnGap"), 684 all: pairify(spacing, "gap"), 685}; 686 687// Common layout patterns 688export const layouts = { 689 // Full screen 690 fullScreen: { 691 position: "absolute" as const, 692 top: 0, 693 left: 0, 694 right: 0, 695 bottom: 0, 696 }, 697 698 // Centered content 699 centered: { 700 flex: 1, 701 justifyContent: "center" as const, 702 alignItems: "center" as const, 703 }, 704 705 // Centered modal/overlay 706 overlay: { 707 position: "absolute" as const, 708 top: 0, 709 left: 0, 710 right: 0, 711 bottom: 0, 712 justifyContent: "center" as const, 713 alignItems: "center" as const, 714 backgroundColor: "rgba(0, 0, 0, 0.5)", 715 }, 716 717 // Safe area friendly 718 safeContainer: { 719 flex: 1, 720 paddingTop: Platform.OS === "ios" ? 44 : 0, // Status bar height 721 }, 722 723 // Row with space between 724 spaceBetweenRow: { 725 flexDirection: "row" as const, 726 justifyContent: "space-between" as const, 727 alignItems: "center" as const, 728 }, 729 730 // Sticky header 731 stickyHeader: { 732 position: "absolute" as const, 733 top: 0, 734 left: 0, 735 right: 0, 736 zIndex: 10, 737 }, 738 739 // Bottom sheet style 740 bottomSheet: { 741 position: "absolute" as const, 742 bottom: 0, 743 left: 0, 744 right: 0, 745 borderTopLeftRadius: 16, 746 borderTopRightRadius: 16, 747 }, 748}; 749 750// Export everything as a combined atoms object for convenience 751export const atoms = { 752 colors: rawColors, 753 spacing, 754 borderRadius, 755 radius: radiusAtoms, 756 typography: typographyAtoms, 757 shadows, 758 touchTargets, 759 animations, 760 iconSizes, 761 layout, 762 borders, 763 backgrounds, 764 textColors, 765 spacingAtoms, 766 sizes, 767 flex, 768 opacity, 769 zIndex, 770 overflow, 771 textAlign, 772 fontWeight, 773 fontSize, 774 lineHeight, 775 letterSpacing, 776 textTransform, 777 textDecoration, 778 textAlignVertical, 779 transforms, 780 position, 781 aspectRatio, 782 gap, 783 layouts, 784}; 785 786// Convenient shorthand aliases 787export const a = atoms; 788export const bg = backgrounds; 789export const text = textColors; 790export const m = spacingAtoms.margin; 791export const mt = spacingAtoms.marginTop; 792export const mr = spacingAtoms.marginRight; 793export const mb = spacingAtoms.marginBottom; 794export const ml = spacingAtoms.marginLeft; 795export const mx = spacingAtoms.marginHorizontal; 796export const my = spacingAtoms.marginVertical; 797export const p = spacingAtoms.padding; 798export const pt = spacingAtoms.paddingTop; 799export const pr = spacingAtoms.paddingRight; 800export const pb = spacingAtoms.paddingBottom; 801export const pl = spacingAtoms.paddingLeft; 802export const px = spacingAtoms.paddingHorizontal; 803export const py = spacingAtoms.paddingVertical; 804export const w = sizes.width; 805export const h = sizes.height; 806export const r = radiusAtoms.all; 807export const top = position.top; 808export const right = position.right; 809export const bottom = position.bottom; 810export const left = position.left; 811export const rotate = transforms.rotate; 812export const scale = transforms.scale; 813export const translateX = transforms.translateX; 814export const translateY = transforms.translateY;