A design system in a box. hip-ui.tngl.io/docs/introduction
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add critical

+943 -565
+91 -81
apps/example/src/components/KitchenSink.tsx
··· 149 149 ); 150 150 } 151 151 152 - const buttons = ["primary", "secondary", "tertiary", "outline"] as const; 152 + const buttons = [ 153 + "primary", 154 + "secondary", 155 + "tertiary", 156 + "outline", 157 + "critical", 158 + ] as const; 153 159 const buttonSizes = ["sm", "md", "lg"] as const; 154 160 155 161 function Buttons() { ··· 193 199 function ButtonGroups() { 194 200 return ( 195 201 <Flex direction="column" gap="4"> 196 - {buttons.map((button) => ( 197 - <Flex align="center" gap="2" key={button}> 198 - <ButtonGroup> 199 - <IconButton variant={button} label="Previous"> 200 - <ArrowLeft /> 201 - </IconButton> 202 - </ButtonGroup> 203 - <ButtonGroup> 204 - <ToggleButton variant={button}> 205 - <Star /> 206 - </ToggleButton> 207 - <Button variant={button}>Button 1</Button> 208 - <Button variant={button}>Button 2</Button> 209 - <Button variant={button}>Button 3</Button> 210 - <Button variant={button}>Button 4</Button> 211 - <ToggleButtonGroup> 212 - <ToggleButton variant={button}>Toggle Button 1</ToggleButton> 213 - <ToggleButton variant={button}>Toggle Button 2</ToggleButton> 214 - <ToggleButton variant={button}>Toggle Button 3</ToggleButton> 215 - </ToggleButtonGroup> 216 - </ButtonGroup> 217 - <ButtonGroup> 218 - <Button variant={button}>Action</Button> 219 - <IconButton variant={button} label="More"> 220 - <Ellipsis /> 221 - </IconButton> 222 - </ButtonGroup> 223 - </Flex> 224 - ))} 202 + {buttons.map( 203 + (button) => 204 + button !== "critical" && ( 205 + <Flex align="center" gap="2" key={button}> 206 + <ButtonGroup> 207 + <IconButton variant={button} label="Previous"> 208 + <ArrowLeft /> 209 + </IconButton> 210 + </ButtonGroup> 211 + <ButtonGroup> 212 + <ToggleButton variant={button}> 213 + <Star /> 214 + </ToggleButton> 215 + <Button variant={button}>Button 1</Button> 216 + <Button variant={button}>Button 2</Button> 217 + <Button variant={button}>Button 3</Button> 218 + <Button variant={button}>Button 4</Button> 219 + <ToggleButtonGroup> 220 + <ToggleButton variant={button}>Toggle Button 1</ToggleButton> 221 + <ToggleButton variant={button}>Toggle Button 2</ToggleButton> 222 + <ToggleButton variant={button}>Toggle Button 3</ToggleButton> 223 + </ToggleButtonGroup> 224 + </ButtonGroup> 225 + <ButtonGroup> 226 + <Button variant={button}>Action</Button> 227 + <IconButton variant={button} label="More"> 228 + <Ellipsis /> 229 + </IconButton> 230 + </ButtonGroup> 231 + </Flex> 232 + ) 233 + )} 225 234 226 235 <Flex gap="2"> 227 236 {buttons.map((button) => ( ··· 496 505 function ToggleButtonGroups() { 497 506 return ( 498 507 <Flex direction="column" gap="4"> 499 - {buttons.map((button) => ( 500 - <Flex key={button} gap="8"> 501 - {buttonSizes.map((size) => ( 502 - <ToggleButtonGroup key={`${button}-${size}`} selectionMode="single"> 503 - <ToggleButton 504 - variant={button} 505 - size={size} 506 - id={`${button}-${size}-1`} 507 - > 508 - Toggle Button 1 509 - </ToggleButton> 510 - <ToggleButton 511 - variant={button} 512 - size={size} 513 - id={`${button}-${size}-2`} 514 - > 515 - Toggle Button 2 516 - </ToggleButton> 517 - <ToggleButton 518 - variant={button} 519 - size={size} 520 - id={`${button}-${size}-3`} 521 - > 522 - Toggle Button 3 523 - </ToggleButton> 524 - </ToggleButtonGroup> 525 - ))} 526 - </Flex> 527 - ))} 508 + {buttons.map( 509 + (button) => 510 + button !== "critical" && ( 511 + <Flex key={button} gap="8"> 512 + {buttonSizes.map((size) => ( 513 + <ToggleButtonGroup 514 + key={`${button}-${size}`} 515 + selectionMode="single" 516 + > 517 + <ToggleButton 518 + variant={button} 519 + size={size} 520 + id={`${button}-${size}-1`} 521 + > 522 + Toggle Button 1 523 + </ToggleButton> 524 + <ToggleButton 525 + variant={button} 526 + size={size} 527 + id={`${button}-${size}-2`} 528 + > 529 + Toggle Button 2 530 + </ToggleButton> 531 + <ToggleButton 532 + variant={button} 533 + size={size} 534 + id={`${button}-${size}-3`} 535 + > 536 + Toggle Button 3 537 + </ToggleButton> 538 + </ToggleButtonGroup> 539 + ))} 540 + </Flex> 541 + ) 542 + )} 528 543 </Flex> 529 544 ); 530 545 } ··· 665 680 style={[gray.bg, gray.text, styles.container]} 666 681 > 667 682 <CommandMenuExample /> 668 - </Flex> 669 - ); 670 - return ( 671 - <Flex 672 - direction="column" 673 - gap="10" 674 - style={[gray.bg, gray.text, styles.container]} 675 - > 676 683 <TitleCard title="Tree"> 677 684 <TreeExample /> 678 685 </TitleCard> ··· 688 695 <Flex direction="column" gap="4"> 689 696 {buttons.map((button) => ( 690 697 <Flex gap="8"> 691 - {buttonSizes.map((size) => ( 692 - <Flex key={`${button}-${size}`} gap="2"> 693 - <ToggleButton variant={button} size={size}> 694 - <Pin /> 695 - </ToggleButton> 696 - <ToggleButton variant={button} size={size}> 697 - Pin 698 - </ToggleButton> 699 - <ToggleButton variant={button} size={size}> 700 - <Pin /> 701 - Pin 702 - </ToggleButton> 703 - </Flex> 704 - ))} 698 + {buttonSizes.map( 699 + (size) => 700 + button !== "critical" && ( 701 + <Flex key={`${button}-${size}`} gap="2"> 702 + <ToggleButton variant={button} size={size}> 703 + <Pin /> 704 + </ToggleButton> 705 + <ToggleButton variant={button} size={size}> 706 + Pin 707 + </ToggleButton> 708 + <ToggleButton variant={button} size={size}> 709 + <Pin /> 710 + Pin 711 + </ToggleButton> 712 + </Flex> 713 + ) 714 + )} 705 715 </Flex> 706 716 ))} 707 717 </Flex>
+5 -4
apps/example/src/components/button-group/index.tsx
··· 1 1 "use client"; 2 2 3 + import * as stylex from "@stylexjs/stylex"; 3 4 import { Group, GroupProps } from "react-aria-components"; 4 - import * as stylex from "@stylexjs/stylex"; 5 + 5 6 import { ButtonGroupContext } from "../button/context"; 6 7 7 8 const styles = stylex.create({ ··· 29 30 ...props 30 31 }: ButtonGroupProps) => { 31 32 return ( 32 - <ButtonGroupContext.Provider value={orientation}> 33 + <ButtonGroupContext value={orientation}> 33 34 <Group 34 35 {...stylex.props( 35 36 styles.group, 36 37 orientation === "horizontal" && styles.horizontal, 37 38 orientation === "vertical" && styles.vertical, 38 - style 39 + style, 39 40 )} 40 41 {...props} 41 42 > 42 43 {children} 43 44 </Group> 44 - </ButtonGroupContext.Provider> 45 + </ButtonGroupContext> 45 46 ); 46 47 };
+1 -1
apps/example/src/components/button/index.tsx
··· 1 1 "use client"; 2 2 3 + import * as stylex from "@stylexjs/stylex"; 3 4 import { 4 5 Button as AriaButton, 5 6 ButtonProps as AriaButtonProps, 6 7 } from "react-aria-components"; 7 - import * as stylex from "@stylexjs/stylex"; 8 8 9 9 import { useButtonStyles } from "../theme/useButtonStyles"; 10 10 import { Size, ButtonVariant } from "../types";
+3 -3
apps/example/src/components/card/index.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 2 3 - import { gray } from "../theme/semantic-color.stylex"; 3 + import { Flex } from "../flex"; 4 4 import { radius } from "../theme/radius.stylex"; 5 + import { gray } from "../theme/semantic-color.stylex"; 5 6 import { spacing } from "../theme/spacing.stylex"; 6 7 import { fontFamily, fontSize, fontWeight } from "../theme/typography.stylex"; 7 - import { Flex } from "../flex"; 8 8 9 9 const styles = stylex.create({ 10 10 card: { ··· 69 69 return ( 70 70 <Flex 71 71 {...props} 72 - style={[styles.cardHeader, style]} 72 + style={[styles.cardHeader as unknown as stylex.StyleXStyles, style]} 73 73 direction="column" 74 74 gap="3" 75 75 />
+9 -8
apps/example/src/components/checkbox/index.tsx
··· 3 3 CheckboxGroupProps as AriaCheckboxGroupProps, 4 4 ValidationResult, 5 5 } from "react-aria-components"; 6 + 7 + import * as stylex from "@stylexjs/stylex"; 8 + import { Check, Minus } from "lucide-react"; 6 9 import { 7 10 Checkbox as AriaCheckbox, 8 11 CheckboxGroup as AriaCheckboxGroup, 9 12 FieldError, 10 13 } from "react-aria-components"; 11 - import * as stylex from "@stylexjs/stylex"; 12 - import { Check, Minus } from "lucide-react"; 13 14 14 - import { spacing } from "../theme/spacing.stylex"; 15 + import { Flex } from "../flex"; 16 + import { Description, Label } from "../label"; 15 17 import { radius } from "../theme/radius.stylex"; 16 18 import { gray, primary } from "../theme/semantic-color.stylex"; 19 + import { spacing } from "../theme/spacing.stylex"; 17 20 import { fontFamily, fontSize, lineHeight } from "../theme/typography.stylex"; 18 - import { Flex } from "../flex"; 19 - import { Description, Label } from "../label"; 20 21 import { Size } from "../types"; 21 22 22 23 const styles = stylex.create({ ··· 74 75 }: CheckboxGroupProps) { 75 76 return ( 76 77 <AriaCheckboxGroup {...props} {...stylex.props(styles.group, style)}> 77 - {label && <Label size={size}>{label}</Label>} 78 + {label !== null && <Label size={size}>{label}</Label>} 78 79 <Flex direction="column" gap="2"> 79 80 {children} 80 81 </Flex> ··· 102 103 ? [gray.bgSolid, gray.border, styles.checked] 103 104 : isSelected 104 105 ? [primary.bgSolid, primary.borderInteractive, styles.checked] 105 - : [gray.borderInteractive] 106 + : [gray.borderInteractive], 106 107 )} 107 108 > 108 109 {isIndeterminate ? ( ··· 111 112 <Check size={16} /> 112 113 ) : null} 113 114 </div> 114 - {children && ( 115 + {children !== null && ( 115 116 <Flex direction="column" gap="1"> 116 117 {children} 117 118 </Flex>
+19 -7
apps/example/src/components/color-field/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { useRef } from "react"; 1 3 import { 2 4 ColorFieldProps as AriaColorFieldProps, 3 5 Input, ··· 6 8 FieldError, 7 9 ColorField as AriaColorField, 8 10 } from "react-aria-components"; 9 - import * as stylex from "@stylexjs/stylex"; 11 + 10 12 import { Description, Label } from "../label"; 11 - import { useRef } from "react"; 13 + import { useInputStyles } from "../theme/useInputStyles"; 12 14 import { Size } from "../types"; 13 - import { useInputStyles } from "../theme/useInputStyles"; 14 15 15 16 export interface ColorFieldProps 16 17 extends Omit<AriaColorFieldProps, "style" | "className">, ··· 40 41 41 42 return ( 42 43 <AriaColorField {...props} {...stylex.props(inputStyles.field, style)}> 43 - <Label size={size}>{label}</Label> 44 + {label !== null && <Label size={size}>{label}</Label>} 45 + {/* 46 + This onClick is specifically for mouse users not clicking directly on the input. 47 + A keyboard user would not encounter the same issue. 48 + */} 49 + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} 44 50 <div 45 51 {...stylex.props(inputStyles.wrapper)} 46 52 onClick={() => inputRef.current?.focus()} 47 53 > 48 - {prefix && <div {...stylex.props(inputStyles.addon)}>{prefix}</div>} 54 + {prefix !== null && ( 55 + <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 56 + )} 49 57 <Input 50 58 placeholder={placeholder} 51 59 ref={inputRef} 52 60 {...stylex.props(inputStyles.input)} 53 61 /> 54 - {suffix && <div {...stylex.props(inputStyles.addon)}>{suffix}</div>} 62 + {suffix !== null && ( 63 + <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 64 + )} 55 65 </div> 56 - {description && <Description size={size}>{description}</Description>} 66 + {description !== undefined && ( 67 + <Description size={size}>{description}</Description> 68 + )} 57 69 <FieldError>{errorMessage}</FieldError> 58 70 </AriaColorField> 59 71 );
+33 -35
apps/example/src/components/combobox/index.tsx
··· 1 + import type { ListBoxProps, ValidationResult } from "react-aria-components"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { ChevronDown } from "lucide-react"; 5 + import { use } from "react"; 1 6 import { 2 7 Button, 3 8 Popover, ··· 5 10 ComboBox as AriaComboBox, 6 11 ComboBoxProps as AriaComboBoxProps, 7 12 Input, 13 + FieldError, 8 14 } from "react-aria-components"; 9 - import * as stylex from "@stylexjs/stylex"; 10 - import type { 11 - ListBoxProps, 12 - ListBoxSectionProps, 13 - ValidationResult, 14 - } from "react-aria-components"; 15 - import { FieldError } from "react-aria-components"; 16 - import { Description, Label } from "../label"; 17 - import { ChevronDown } from "lucide-react"; 18 - import { Size } from "../types"; 19 - import { 20 - ListBox, 21 - ListBoxItem, 22 - ListBoxItemProps, 23 - ListBoxSectionHeaderProps, 24 - ListBoxSectionHeader, 25 - ListBoxSeparatorProps, 26 - ListBoxSeparator, 27 - ListBoxSection, 28 - } from "../listbox"; 15 + 29 16 import { SizeContext } from "../context"; 17 + import { IconButton } from "../icon-button"; 18 + import { Description, Label } from "../label"; 19 + import { ListBox } from "../listbox"; 20 + import { spacing } from "../theme/spacing.stylex"; 30 21 import { useInputStyles } from "../theme/useInputStyles"; 31 22 import { usePopoverStyles } from "../theme/usePopoverStyles"; 32 - import { IconButton } from "../icon-button"; 23 + import { Size } from "../types"; 33 24 import { SmallBody } from "../typography"; 34 - import { spacing } from "../theme/spacing.stylex"; 35 - import { use } from "react"; 36 25 37 26 const styles = stylex.create({ 38 27 matchWidth: { ··· 98 87 const popoverStyles = usePopoverStyles(); 99 88 100 89 return ( 101 - <SizeContext.Provider value={size}> 90 + <SizeContext value={size}> 102 91 <AriaComboBox {...props} {...stylex.props(inputStyles.field, style)}> 103 92 {label && <Label size={size}>{label}</Label>} 104 93 <Button {...stylex.props(inputStyles.wrapper)}> 105 - {prefix && <div {...stylex.props(inputStyles.addon)}>{prefix}</div>} 94 + {prefix !== null && ( 95 + <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 96 + )} 106 97 <Input 107 98 {...stylex.props(inputStyles.input)} 108 99 placeholder={placeholder} 109 100 /> 110 - {suffix && <div {...stylex.props(inputStyles.addon)}>{suffix}</div>} 101 + {suffix !== null && ( 102 + <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 103 + )} 111 104 <div {...stylex.props(inputStyles.addon)}> 112 105 <IconButton size="sm" variant="secondary" label="Open combobox"> 113 106 <ChevronDown size={16} aria-hidden="true" /> ··· 132 125 </ListBox> 133 126 </Popover> 134 127 </AriaComboBox> 135 - </SizeContext.Provider> 128 + </SizeContext> 136 129 ); 137 130 } 138 131 139 - export type ComboBoxItemProps = ListBoxItemProps; 140 - export const ComboBoxItem = ListBoxItem; 141 - export type ComboBoxSectionProps<T extends object> = ListBoxSectionProps<T>; 142 - export const ComboBoxSection = ListBoxSection; 143 - export type ComboBoxSectionHeaderProps = ListBoxSectionHeaderProps; 144 - export const ComboBoxSectionHeader = ListBoxSectionHeader; 145 - export type ComboBoxSeparatorProps = ListBoxSeparatorProps; 146 - export const ComboBoxSeparator = ListBoxSeparator; 132 + export type { 133 + ListBoxItemProps as ComboBoxItemProps, 134 + ListBoxSectionProps as ComboBoxSectionProps, 135 + ListBoxSectionHeaderProps as ComboBoxSectionHeaderProps, 136 + ListBoxSeparatorProps as ComboBoxSeparatorProps, 137 + } from "../listbox"; 138 + 139 + export { 140 + ListBoxItem as ComboBoxItem, 141 + ListBoxSection as ComboBoxSection, 142 + ListBoxSectionHeader as ComboBoxSectionHeader, 143 + ListBoxSeparator as ComboBoxSeparator, 144 + } from "../listbox";
+47 -41
apps/example/src/components/command-menu/index.tsx
··· 1 + import { useControlledState } from "@react-stately/utils"; 2 + import * as stylex from "@stylexjs/stylex"; 3 + import { useEffect, useEffectEvent } from "react"; 1 4 import { 2 5 InputProps, 3 6 Modal, ··· 8 11 ModalOverlay, 9 12 AutocompleteProps as AriaAutocompleteProps, 10 13 } from "react-aria-components"; 11 - import { useControlledState } from "@react-stately/utils"; 12 - import { SearchField } from "../search-field"; 13 14 import { OverlayTriggerProps } from "react-stately"; 14 - import { 15 - MenuItem, 16 - MenuItemProps, 17 - MenuSection, 18 - MenuSectionHeader, 19 - MenuSectionHeaderProps, 20 - MenuSectionProps, 21 - MenuSeparator, 22 - MenuSeparatorProps, 23 - } from "../menu"; 24 - import { useEffect, useEffectEvent } from "react"; 25 - import * as stylex from "@stylexjs/stylex"; 26 - import { shadow } from "../theme/shadow.stylex"; 15 + 16 + import { SizeContext } from "../context"; 17 + import { SearchField } from "../search-field"; 18 + import { Separator } from "../separator"; 19 + import { animations } from "../theme/animations.stylex"; 27 20 import { radius } from "../theme/radius.stylex"; 28 21 import { gray } from "../theme/semantic-color.stylex"; 29 - import { animations } from "../theme/animations.stylex"; 22 + import { shadow } from "../theme/shadow.stylex"; 30 23 import { spacing } from "../theme/spacing.stylex"; 31 - import { SizeContext } from "../context"; 32 - import { Separator } from "../separator"; 33 24 34 25 const styles = stylex.create({ 35 26 overlay: { ··· 41 32 width: "100vw", 42 33 zIndex: 100, 43 34 44 - transitionProperty: "opacity", 45 - transitionDuration: { 46 - ":is([data-exiting])": "100ms", 35 + animationDuration: "200ms", 36 + animationName: { 37 + ":is([data-entering])": animations.fadeIn, 47 38 }, 48 - transitionTimingFunction: "ease-in-out", 39 + animationTimingFunction: "ease-in", 49 40 opacity: { 50 41 default: 1, 51 42 ":is([data-exiting])": 0, 52 43 }, 53 - animationName: { 54 - ":is([data-entering])": animations.fadeIn, 44 + transitionDuration: { 45 + ":is([data-exiting])": "100ms", 55 46 }, 56 - animationTimingFunction: "ease-in", 57 - animationDuration: "200ms", 47 + transitionProperty: "opacity", 48 + transitionTimingFunction: "ease-in-out", 58 49 }, 59 50 modal: { 60 51 borderRadius: radius["lg"], ··· 106 97 Pick<InputProps, "placeholder">, 107 98 AriaAutocompleteProps<T> { 108 99 children: React.ReactNode; 100 + disableGlobalShortcut?: boolean; 109 101 } 110 102 111 103 export function CommandMenu<T extends object>({ ··· 120 112 disableVirtualFocus, 121 113 inputValue, 122 114 onInputChange, 115 + disableGlobalShortcut = false, 123 116 }: CommandMenuProps<T>) { 124 117 const defaultFilter = useFilter({ sensitivity: "base" }); 125 118 const [isOpen, setIsOpen] = useControlledState( 126 119 isOpenProp, 127 120 defaultOpen ?? false, 128 - onOpenChange 121 + onOpenChange, 129 122 ); 130 - const onClose = useEffectEvent(() => setIsOpen(false)); 123 + const onClose = useEffectEvent(() => { 124 + setIsOpen(false); 125 + }); 131 126 132 127 useEffect(() => { 128 + if (disableGlobalShortcut) return; 129 + 133 130 function handleKeyDown(event: KeyboardEvent) { 134 131 if (event.metaKey && event.key === "k") { 135 132 setIsOpen(true); 136 133 } 137 134 } 138 135 139 - window.addEventListener("keydown", handleKeyDown); 136 + globalThis.addEventListener("keydown", handleKeyDown); 140 137 141 - return () => window.removeEventListener("keydown", handleKeyDown); 142 - }, [setIsOpen]); 138 + return () => { 139 + globalThis.removeEventListener("keydown", handleKeyDown); 140 + }; 141 + }, [setIsOpen, disableGlobalShortcut]); 143 142 144 143 return ( 145 - <SizeContext.Provider value="lg"> 144 + <SizeContext value="lg"> 146 145 <ModalOverlay 147 146 isDismissable 148 147 isOpen={isOpen} ··· 160 159 onInputChange={onInputChange} 161 160 > 162 161 <div {...stylex.props(styles.searchField)}> 162 + {/* This is part of the interaction for a CMD+K menu. */} 163 + {/* eslint-disable-next-line jsx-a11y/no-autofocus */} 163 164 <SearchField placeholder={placeholder} autoFocus /> 164 165 </div> 165 166 <Separator /> ··· 170 171 </Dialog> 171 172 </Modal> 172 173 </ModalOverlay> 173 - </SizeContext.Provider> 174 + </SizeContext> 174 175 ); 175 176 } 176 177 177 - export type CommandMenuItemProps = MenuItemProps; 178 - export const CommandMenuItem = MenuItem; 179 - export type CommandMenuSectionHeaderProps = MenuSectionHeaderProps; 180 - export const CommandMenuSectionHeader = MenuSectionHeader; 181 - export type CommandMenuSectionProps<T extends object> = MenuSectionProps<T>; 182 - export const CommandMenuSection = MenuSection; 183 - export type CommandMenuSeparatorProps = MenuSeparatorProps; 184 - export const CommandMenuSeparator = MenuSeparator; 178 + export type { 179 + MenuItemProps as CommandMenuItemProps, 180 + MenuSectionHeaderProps as CommandMenuSectionHeaderProps, 181 + MenuSectionProps as CommandMenuSectionProps, 182 + MenuSeparatorProps as CommandMenuSeparatorProps, 183 + } from "../menu"; 184 + 185 + export { 186 + MenuItem as CommandMenuItem, 187 + MenuSectionHeader as CommandMenuSectionHeader, 188 + MenuSection as CommandMenuSection, 189 + MenuSeparator as CommandMenuSeparator, 190 + } from "../menu";
+37 -32
apps/example/src/components/context-menu/index.tsx
··· 1 1 import { OverlayTriggerProps } from "@react-types/overlays"; 2 + import * as stylex from "@stylexjs/stylex"; 3 + import { 4 + Children, 5 + cloneElement, 6 + createContext, 7 + use, 8 + useCallback, 9 + useRef, 10 + useState, 11 + } from "react"; 12 + import { AriaButtonProps, useMenuTrigger } from "react-aria"; 2 13 import { 3 14 Menu as AriaMenu, 4 15 MenuProps as AriaMenuProps, ··· 10 21 Provider, 11 22 RootMenuTriggerStateContext, 12 23 } from "react-aria-components"; 13 - import * as stylex from "@stylexjs/stylex"; 24 + import { useMenuTriggerState } from "react-stately"; 14 25 15 - import { 16 - Children, 17 - cloneElement, 18 - createContext, 19 - use, 20 - useCallback, 21 - useRef, 22 - useState, 23 - } from "react"; 24 - import { Size } from "../types"; 25 26 import { SizeContext } from "../context"; 26 - import { useMenuTriggerState } from "react-stately"; 27 - import { AriaButtonProps, useMenuTrigger } from "react-aria"; 28 27 import { usePopoverStyles } from "../theme/usePopoverStyles"; 28 + import { Size } from "../types"; 29 29 30 - const ContextMenuTriggerProps = createContext< 31 - AriaButtonProps<"button"> & { ref?: React.Ref<HTMLDivElement> } 30 + const ContextMenuTriggerPropsContext = createContext< 31 + AriaButtonProps & { ref?: React.Ref<HTMLDivElement> } 32 32 >({}); 33 33 34 34 interface Position { ··· 44 44 setPosition: () => {}, 45 45 }); 46 46 47 - function ContextMenuRoot( 48 - props: OverlayTriggerProps & { children: React.ReactNode } 49 - ) { 47 + function ContextMenuRoot({ 48 + children, 49 + ...props 50 + }: OverlayTriggerProps & { children: React.ReactNode }) { 50 51 const scrollRef = useRef(null); 51 52 const state = useMenuTriggerState(props); 52 53 const ref = useRef<HTMLDivElement>(null); ··· 54 55 const { menuTriggerProps, menuProps } = useMenuTrigger( 55 56 { ...props, type: "menu" }, 56 57 state, 57 - ref 58 + ref, 58 59 ); 59 60 60 61 return ( ··· 63 64 [MenuContext, { ...menuProps, ref: scrollRef }], 64 65 [OverlayTriggerStateContext, state], 65 66 [RootMenuTriggerStateContext, state], 66 - [ContextMenuTriggerProps, { ...menuTriggerProps, ref }], 67 + [ContextMenuTriggerPropsContext, { ...menuTriggerProps, ref }], 67 68 [ContextMenuStateContext, { position, setPosition }], 68 69 [ 69 70 PopoverContext, ··· 77 78 ], 78 79 ]} 79 80 > 80 - {props.children} 81 + {children} 81 82 </Provider> 82 83 ); 83 84 } 84 85 85 - function ContextMenuTrigger( 86 - props: OverlayTriggerProps & { children: React.ReactNode } 87 - ) { 86 + function ContextMenuTrigger({ 87 + children, 88 + ...props 89 + }: OverlayTriggerProps & { children: React.ReactNode }) { 88 90 const overlayTriggerState = use(OverlayTriggerStateContext); 89 - const menuTriggerProps = use(ContextMenuTriggerProps); 91 + const menuTriggerProps = use(ContextMenuTriggerPropsContext); 90 92 const { position, setPosition } = use(ContextMenuStateContext); 91 93 const onContextMenu = useCallback( 92 94 (e: React.MouseEvent<HTMLButtonElement>) => { ··· 95 97 overlayTriggerState?.open(); 96 98 setPosition({ x: e.pageX, y: e.pageY }); 97 99 }, 98 - [overlayTriggerState, setPosition] 100 + [overlayTriggerState, setPosition], 99 101 ); 100 102 101 - if (Children.count(props.children) !== 1) { 103 + // eslint-disable-next-line @eslint-react/no-children-count 104 + if (Children.count(children) !== 1) { 102 105 throw new Error("ContextMenuTrigger must have exactly one child"); 103 106 } 104 107 105 108 return ( 106 109 <> 110 + {/* eslint-disable-next-line @eslint-react/no-clone-element */} 107 111 {cloneElement( 108 - props.children as React.ReactElement<React.HTMLAttributes<HTMLElement>>, 112 + children as React.ReactElement<React.HTMLAttributes<HTMLElement>>, 109 113 { 114 + ...props, 110 115 "aria-controls": menuTriggerProps["aria-controls"], 111 116 "aria-expanded": menuTriggerProps["aria-expanded"], 112 117 "aria-haspopup": menuTriggerProps["aria-haspopup"], 113 118 id: menuTriggerProps["id"], 114 119 onContextMenu: onContextMenu, 115 - } 120 + }, 116 121 )} 117 122 <div 118 123 ref={menuTriggerProps.ref} 119 - style={{ position: "absolute", top: position?.y, left: position?.x }} 124 + style={{ position: "absolute", top: position.y, left: position.x }} 120 125 /> 121 126 </> 122 127 ); ··· 154 159 const size = sizeProp || use(SizeContext); 155 160 156 161 return ( 157 - <SizeContext.Provider value={size}> 162 + <SizeContext value={size}> 158 163 <ContextMenuRoot 159 164 defaultOpen={defaultOpen} 160 165 isOpen={isOpen} ··· 171 176 <AriaMenu {...props} {...stylex.props(popoverStyles)} /> 172 177 </Popover> 173 178 </ContextMenuRoot> 174 - </SizeContext.Provider> 179 + </SizeContext> 175 180 ); 176 181 }
+1
apps/example/src/components/context.ts
··· 1 1 import { createContext } from "react"; 2 + 2 3 import { Size } from "./types"; 3 4 4 5 export const SizeContext = createContext<Size>("md");
+17 -7
apps/example/src/components/date-field/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { useRef } from "react"; 1 3 import { 2 4 DateFieldProps as AriaDateFieldProps, 3 5 DateInput, 4 6 DateSegment, 5 7 DateValue, 6 8 ValidationResult, 9 + FieldError, 10 + DateField as AriaDateField, 7 11 } from "react-aria-components"; 8 12 9 - import { FieldError, DateField as AriaDateField } from "react-aria-components"; 10 - import * as stylex from "@stylexjs/stylex"; 11 13 import { Description, Label } from "../label"; 12 - import { useRef } from "react"; 13 - import { Size } from "../types"; 14 14 import { useInputStyles } from "../theme/useInputStyles"; 15 + import { Size } from "../types"; 15 16 16 17 export interface DateFieldProps<T extends DateValue> 17 18 extends Omit<AriaDateFieldProps<T>, "style" | "className"> { ··· 39 40 40 41 return ( 41 42 <AriaDateField {...props} {...stylex.props(inputStyles.field, style)}> 42 - <Label size={size}>{label}</Label> 43 + {label !== null && <Label size={size}>{label}</Label>} 44 + {/* 45 + This onClick is specifically for mouse users not clicking directly on the input. 46 + A keyboard user would not encounter the same issue. 47 + */} 48 + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} 43 49 <div 44 50 {...stylex.props(inputStyles.wrapper)} 45 51 onClick={() => inputRef.current?.focus()} 46 52 > 47 - {prefix && <div {...stylex.props(inputStyles.addon)}>{prefix}</div>} 53 + {prefix !== null && ( 54 + <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 55 + )} 48 56 <DateInput {...stylex.props(inputStyles.input)} ref={inputRef}> 49 57 {(segment) => <DateSegment segment={segment} />} 50 58 </DateInput> 51 - {suffix && <div {...stylex.props(inputStyles.addon)}>{suffix}</div>} 59 + {suffix !== null && ( 60 + <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 61 + )} 52 62 </div> 53 63 {description && <Description size={size}>{description}</Description>} 54 64 <FieldError>{errorMessage}</FieldError>
+2 -1
apps/example/src/components/flex/index.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 + 2 3 import { Spacing, spacing } from "../theme/spacing.stylex"; 3 4 4 5 const styles = stylex.create({ ··· 141 142 142 143 typeof gap === "string" && styles[`gap-${gap}` as keyof typeof styles], 143 144 144 - style 145 + style, 145 146 )} 146 147 /> 147 148 );
+4 -4
apps/example/src/components/icon-button/index.tsx
··· 1 1 "use client"; 2 2 3 - import { ButtonProps as AriaButtonProps } from "react-aria-components"; 4 3 import * as stylex from "@stylexjs/stylex"; 4 + import { use } from "react"; 5 + import { ButtonProps as AriaButtonProps } from "react-aria-components"; 5 6 6 - import { Tooltip } from "../tooltip"; 7 7 import { Button } from "../button"; 8 + import { SizeContext } from "../context"; 8 9 import { spacing } from "../theme/spacing.stylex"; 10 + import { Tooltip } from "../tooltip"; 9 11 import { ButtonVariant, Size } from "../types"; 10 - import { use } from "react"; 11 - import { SizeContext } from "../context"; 12 12 13 13 const styles = stylex.create({ 14 14 sm: {
+6 -6
apps/example/src/components/label/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { use } from "react"; 1 3 import { 2 4 LabelProps as AriaLabelProps, 3 5 Text, 4 6 TextProps, 7 + Label as AriaLabel, 5 8 } from "react-aria-components"; 6 - import { Label as AriaLabel } from "react-aria-components"; 7 - import * as stylex from "@stylexjs/stylex"; 8 9 10 + import { SizeContext } from "../context"; 11 + import { gray } from "../theme/semantic-color.stylex"; 9 12 import { fontSize, fontWeight, lineHeight } from "../theme/typography.stylex"; 10 - import { gray } from "../theme/semantic-color.stylex"; 11 13 import { Size } from "../types"; 12 - import { use } from "react"; 13 - import { SizeContext } from "../context"; 14 14 15 15 const styles = stylex.create({ 16 16 label: { ··· 76 76 styles.description, 77 77 gray.textDim, 78 78 size === "sm" && styles.descriptionSm, 79 - style 79 + style, 80 80 )} 81 81 {...props} 82 82 />
+7 -8
apps/example/src/components/link/index.tsx
··· 1 - import { LinkProps as AriaLinkProps } from "react-aria-components"; 2 - import { Link as AriaLink } from "react-aria-components"; 3 1 import * as stylex from "@stylexjs/stylex"; 2 + import { use } from "react"; 3 + import { 4 + LinkProps as AriaLinkProps, 5 + Link as AriaLink, 6 + } from "react-aria-components"; 4 7 5 8 import { blue } from "../theme/colors.stylex"; 6 9 import { fontFamily, fontWeight } from "../theme/typography.stylex"; 7 - import { createContext, useContext } from "react"; 10 + import { LinkContext } from "./link-context"; 8 11 9 12 const styles = stylex.create({ 10 13 link: { ··· 22 25 }, 23 26 }); 24 27 25 - export const LinkContext = createContext<{ 26 - style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 27 - }>({}); 28 - 29 28 export interface LinkProps extends Omit<AriaLinkProps, "style" | "className"> { 30 29 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 31 30 } 32 31 33 32 export function Link({ style, ...props }: LinkProps) { 34 - const context = useContext(LinkContext); 33 + const context = use(LinkContext); 35 34 36 35 return ( 37 36 <AriaLink {...props} {...stylex.props(styles.link, context.style, style)} />
+12 -11
apps/example/src/components/listbox/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { Check } from "lucide-react"; 3 + import { use } from "react"; 1 4 import { 2 5 ListBoxProps as AriaListBoxProps, 3 6 ListBoxItem as AriaListBoxItem, ··· 8 11 Header, 9 12 SeparatorProps, 10 13 } from "react-aria-components"; 11 - import * as stylex from "@stylexjs/stylex"; 12 - import { Check } from "lucide-react"; 13 - import { spacing } from "../theme/spacing.stylex"; 14 - import { typeramp } from "../theme/typography.stylex"; 15 - import { Size } from "../types"; 14 + 16 15 import { SizeContext } from "../context"; 17 - import { use } from "react"; 18 16 import { Separator } from "../separator"; 19 17 import { gray } from "../theme/semantic-color.stylex"; 18 + import { spacing } from "../theme/spacing.stylex"; 19 + import { typeramp } from "../theme/typography.stylex"; 20 20 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 21 + import { Size } from "../types"; 21 22 22 23 const styles = stylex.create({ 23 24 listBox: { ··· 63 64 const size = sizeProp || use(SizeContext); 64 65 65 66 return ( 66 - <SizeContext.Provider value={size}> 67 + <SizeContext value={size}> 67 68 <AriaListBox {...stylex.props(styles.listBox, style)} {...props} /> 68 - </SizeContext.Provider> 69 + </SizeContext> 69 70 ); 70 71 } 71 72 ··· 97 98 > 98 99 {({ isSelected }) => ( 99 100 <div {...stylex.props(listBoxItemStyles.inner)}> 100 - {prefix && ( 101 + {prefix !== null && ( 101 102 <div {...stylex.props(listBoxItemStyles.addon)}>{prefix}</div> 102 103 )} 103 104 <div {...stylex.props(listBoxItemStyles.label)}>{children}</div> 104 - {suffix && ( 105 + {suffix !== null && ( 105 106 <div {...stylex.props(listBoxItemStyles.addon)}>{suffix}</div> 106 107 )} 107 108 {isSelected && ( ··· 154 155 styles.sectionLabel, 155 156 gray.textDim, 156 157 styles[`${size}SectionLabel`], 157 - style 158 + style, 158 159 )} 159 160 /> 160 161 );
+19 -19
apps/example/src/components/menu/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { Check, ChevronRight } from "lucide-react"; 3 + import { use } from "react"; 1 4 import { 2 5 MenuTriggerProps, 3 6 MenuTrigger, ··· 12 15 SubmenuTrigger, 13 16 PopoverProps, 14 17 } from "react-aria-components"; 15 - import * as stylex from "@stylexjs/stylex"; 16 - import { Size } from "../types"; 17 - import { 18 - ListBoxSectionHeaderProps, 19 - ListBoxSectionHeader, 20 - ListBoxSeparator, 21 - ListBoxSeparatorProps, 22 - } from "../listbox"; 18 + 23 19 import { SizeContext } from "../context"; 24 - import { Check, ChevronRight } from "lucide-react"; 20 + import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 25 21 import { usePopoverStyles } from "../theme/usePopoverStyles"; 26 - import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 27 - import { use } from "react"; 22 + import { Size } from "../types"; 28 23 29 24 export interface MenuProps<T extends object> 30 25 extends Omit<MenuTriggerProps, "trigger" | "children">, ··· 58 53 const size = sizeProp || use(SizeContext); 59 54 60 55 return ( 61 - <SizeContext.Provider value={size}> 56 + <SizeContext value={size}> 62 57 <MenuTrigger 63 58 defaultOpen={defaultOpen} 64 59 isOpen={isOpen} ··· 75 70 <AriaMenu {...props} {...stylex.props(popoverStyles)} /> 76 71 </Popover> 77 72 </MenuTrigger> 78 - </SizeContext.Provider> 73 + </SizeContext> 79 74 ); 80 75 } 81 76 ··· 163 158 > 164 159 {({ isSelected, hasSubmenu }) => ( 165 160 <div {...stylex.props(menuItemStyles.inner)}> 166 - {prefix && ( 161 + {prefix !== null && ( 167 162 <div {...stylex.props(menuItemStyles.addon)}>{prefix}</div> 168 163 )} 169 164 <div {...stylex.props(menuItemStyles.label)}>{children}</div> 170 - {suffix && ( 165 + {suffix !== null && ( 171 166 <div {...stylex.props(menuItemStyles.addon)}>{suffix}</div> 172 167 )} 173 168 {isSelected && ( ··· 186 181 ); 187 182 } 188 183 189 - export type MenuSectionHeaderProps = ListBoxSectionHeaderProps; 190 - export const MenuSectionHeader = ListBoxSectionHeader; 191 - export type MenuSeparatorProps = ListBoxSeparatorProps; 192 - export const MenuSeparator = ListBoxSeparator; 184 + export type { 185 + ListBoxSectionHeaderProps as MenuSectionHeaderProps, 186 + ListBoxSeparatorProps as MenuSeparatorProps, 187 + } from "../listbox"; 188 + 189 + export { 190 + ListBoxSectionHeader as MenuSectionHeader, 191 + ListBoxSeparator as MenuSeparator, 192 + } from "../listbox";
+19 -9
apps/example/src/components/number-field/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { Minus, Plus } from "lucide-react"; 3 + import { useRef } from "react"; 1 4 import { 2 5 NumberFieldProps as AriaNumberFieldProps, 3 6 Input, ··· 8 11 Group, 9 12 Button, 10 13 } from "react-aria-components"; 11 - import * as stylex from "@stylexjs/stylex"; 14 + 12 15 import { Description, Label } from "../label"; 13 - import { useRef } from "react"; 14 - import { Size } from "../types"; 15 - import { useInputStyles } from "../theme/useInputStyles"; 16 - import { Minus, Plus } from "lucide-react"; 16 + import { slate } from "../theme/colors.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 - import { slate } from "../theme/colors.stylex"; 19 + import { useInputStyles } from "../theme/useInputStyles"; 20 + import { Size } from "../types"; 20 21 21 22 const styles = stylex.create({ 22 23 buttons: { ··· 78 79 const buttonStyles = stylex.props( 79 80 styles.button, 80 81 gray.borderInteractive, 81 - gray.bgAction 82 + gray.bgAction, 82 83 ); 83 84 84 85 return ( 85 86 <AriaNumberField {...props} {...stylex.props(inputStyles.field, style)}> 86 87 <Label size={size}>{label}</Label> 88 + {/* 89 + This onClick is specifically for mouse users not clicking directly on the input. 90 + A keyboard user would not encounter the same issue. 91 + */} 92 + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} 87 93 <div 88 94 {...stylex.props(inputStyles.wrapper)} 89 95 onClick={() => inputRef.current?.focus()} 90 96 > 91 - {prefix && <div {...stylex.props(inputStyles.addon)}>{prefix}</div>} 97 + {prefix !== null && ( 98 + <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 99 + )} 92 100 <Input 93 101 placeholder={placeholder} 94 102 ref={inputRef} 95 103 {...stylex.props(inputStyles.input)} 96 104 /> 97 - {suffix && <div {...stylex.props(inputStyles.addon)}>{suffix}</div>} 105 + {suffix !== null && ( 106 + <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 107 + )} 98 108 <Group {...stylex.props(styles.buttons)}> 99 109 <Button slot="decrement" {...buttonStyles}> 100 110 <Minus />
+1 -1
apps/example/src/components/popover/index.tsx
··· 1 1 "use client"; 2 2 3 + import * as stylex from "@stylexjs/stylex"; 3 4 import { 4 5 OverlayArrow, 5 6 Popover as AriaPopover, ··· 8 9 DialogTriggerProps, 9 10 Dialog, 10 11 } from "react-aria-components"; 11 - import * as stylex from "@stylexjs/stylex"; 12 12 13 13 import { slate } from "../theme/colors.stylex"; 14 14 import { spacing } from "../theme/spacing.stylex";
+7 -6
apps/example/src/components/radio/index.tsx
··· 3 3 RadioGroupProps as AriaRadioGroupProps, 4 4 ValidationResult, 5 5 } from "react-aria-components"; 6 + 7 + import * as stylex from "@stylexjs/stylex"; 6 8 import { 7 9 Radio as AriaRadio, 8 10 RadioGroup as AriaRadioGroup, 9 11 FieldError, 10 12 SelectionIndicator, 11 13 } from "react-aria-components"; 12 - import * as stylex from "@stylexjs/stylex"; 13 14 14 - import { spacing } from "../theme/spacing.stylex"; 15 + import { Flex } from "../flex"; 16 + import { Description, Label } from "../label"; 15 17 import { radius } from "../theme/radius.stylex"; 16 18 import { gray, primary } from "../theme/semantic-color.stylex"; 19 + import { spacing } from "../theme/spacing.stylex"; 17 20 import { fontFamily, fontSize, lineHeight } from "../theme/typography.stylex"; 18 - import { Flex } from "../flex"; 19 - import { Description, Label } from "../label"; 20 21 import { Size } from "../types"; 21 22 22 23 const scaleIn = stylex.keyframes({ ··· 104 105 }: RadioGroupProps) { 105 106 return ( 106 107 <AriaRadioGroup {...props} {...stylex.props(styles.group, style)}> 107 - {label && <Label size={size}>{label}</Label>} 108 + {label !== null && <Label size={size}>{label}</Label>} 108 109 <Flex direction="column" gap="2"> 109 110 {children} 110 111 </Flex> ··· 132 133 ? [gray.bgSolid, gray.border, styles.checked] 133 134 : isSelected 134 135 ? [primary.bgSolid, primary.borderInteractive, styles.checked] 135 - : [gray.borderInteractive] 136 + : [gray.borderInteractive], 136 137 )} 137 138 > 138 139 <SelectionIndicator {...stylex.props(styles.selectionIndicator)} />
+19 -11
apps/example/src/components/search-field/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { SearchIcon, X } from "lucide-react"; 3 + import { useRef } from "react"; 1 4 import { 2 5 SearchFieldProps as AriaSearchFieldProps, 3 6 Input, ··· 6 9 FieldError, 7 10 SearchField as AriaSearchField, 8 11 } from "react-aria-components"; 9 - import * as stylex from "@stylexjs/stylex"; 12 + 13 + import { IconButton } from "../icon-button"; 10 14 import { Description, Label } from "../label"; 11 - import { useRef } from "react"; 12 - import { Size } from "../types"; 15 + import { spacing } from "../theme/spacing.stylex"; 13 16 import { useInputStyles } from "../theme/useInputStyles"; 14 - import { SearchIcon, X } from "lucide-react"; 15 - import { IconButton } from "../icon-button"; 16 - import { spacing } from "../theme/spacing.stylex"; 17 + import { Size } from "../types"; 17 18 18 19 const styles = stylex.create({ 19 20 wrapper: { ··· 41 42 prefix?: React.ReactNode; 42 43 suffix?: React.ReactNode; 43 44 } 45 + 46 + const defaultPrefix = <SearchIcon />; 44 47 45 48 export function SearchField({ 46 49 label, ··· 48 51 errorMessage, 49 52 style, 50 53 size, 51 - prefix = <SearchIcon />, 54 + prefix = defaultPrefix, 52 55 suffix, 53 56 placeholder, 54 57 ...props ··· 61 64 {({ isEmpty }) => { 62 65 return ( 63 66 <> 64 - {label && <Label size={size}>{label}</Label>} 67 + {label !== null && <Label size={size}>{label}</Label>} 68 + {/* 69 + This onClick is specifically for mouse users not clicking directly on the input. 70 + A keyboard user would not encounter the same issue. 71 + */} 72 + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} 65 73 <div 66 74 {...stylex.props(inputStyles.wrapper, styles.wrapper)} 67 75 onClick={() => inputRef.current?.focus()} 68 76 > 69 - {prefix && ( 77 + {prefix !== null && ( 70 78 <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 71 79 )} 72 80 <Input ··· 74 82 ref={inputRef} 75 83 {...stylex.props( 76 84 inputStyles.input, 77 - !isEmpty && styles.clearButtonPadding 85 + !isEmpty && styles.clearButtonPadding, 78 86 )} 79 87 /> 80 - {suffix && ( 88 + {suffix !== null && ( 81 89 <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 82 90 )} 83 91 {!isEmpty && (
+32 -33
apps/example/src/components/select/index.tsx
··· 1 + import type { ValidationResult } from "react-aria-components"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { ChevronDown } from "lucide-react"; 5 + import { use } from "react"; 1 6 import { 2 7 SelectProps as AriaSelectProps, 3 8 Button, 4 9 Popover, 5 10 SelectValue, 6 11 PopoverProps, 7 - } from "react-aria-components"; 8 - import { Select as AriaSelect } from "react-aria-components"; 9 - import * as stylex from "@stylexjs/stylex"; 10 - import type { 11 - ListBoxSectionProps, 12 - ValidationResult, 12 + Select as AriaSelect, 13 + FieldError, 13 14 } from "react-aria-components"; 14 - import { FieldError } from "react-aria-components"; 15 - import { Description, Label } from "../label"; 16 - import { ChevronDown } from "lucide-react"; 17 - import { Size } from "../types"; 18 - import { 19 - ListBox, 20 - ListBoxItem, 21 - ListBoxItemProps, 22 - ListBoxSectionHeaderProps, 23 - ListBoxSectionHeader, 24 - ListBoxSeparatorProps, 25 - ListBoxSeparator, 26 - ListBoxSection, 27 - } from "../listbox"; 15 + 28 16 import { SizeContext } from "../context"; 17 + import { Description, Label } from "../label"; 18 + import { ListBox } from "../listbox"; 29 19 import { useInputStyles } from "../theme/useInputStyles"; 30 20 import { usePopoverStyles } from "../theme/usePopoverStyles"; 31 - import { use } from "react"; 21 + import { Size } from "../types"; 32 22 33 23 const styles = stylex.create({ 34 24 matchWidth: { ··· 82 72 const popoverStyles = usePopoverStyles(); 83 73 84 74 return ( 85 - <SizeContext.Provider value={size}> 75 + <SizeContext value={size}> 86 76 <AriaSelect 87 77 {...props} 88 78 {...stylex.props(inputStyles.field, style)} ··· 90 80 > 91 81 {label && <Label size={size}>{label}</Label>} 92 82 <Button {...stylex.props(inputStyles.wrapper)}> 93 - {prefix && <div {...stylex.props(inputStyles.addon)}>{prefix}</div>} 83 + {prefix !== null && ( 84 + <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 85 + )} 94 86 <SelectValue {...stylex.props(inputStyles.input)}> 95 87 {({ selectedText, isPlaceholder, defaultChildren }) => { 96 88 if (isPlaceholder) return placeholder; ··· 99 91 return defaultChildren; 100 92 }} 101 93 </SelectValue> 102 - {suffix && <div {...stylex.props(inputStyles.addon)}>{suffix}</div>} 94 + {suffix !== null && ( 95 + <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 96 + )} 103 97 <div {...stylex.props(inputStyles.addon)}> 104 98 <ChevronDown size={16} aria-hidden="true" /> 105 99 </div> ··· 121 115 </ListBox> 122 116 </Popover> 123 117 </AriaSelect> 124 - </SizeContext.Provider> 118 + </SizeContext> 125 119 ); 126 120 } 127 121 128 - export type SelectItemProps = ListBoxItemProps; 129 - export const SelectItem = ListBoxItem; 130 - export type SelectSectionProps<T extends object> = ListBoxSectionProps<T>; 131 - export const SelectSection = ListBoxSection; 132 - export type SelectSectionHeaderProps = ListBoxSectionHeaderProps; 133 - export const SelectSectionHeader = ListBoxSectionHeader; 134 - export type SelectSeparatorProps = ListBoxSeparatorProps; 135 - export const SelectSeparator = ListBoxSeparator; 122 + export type { 123 + ListBoxItemProps as SelectItemProps, 124 + ListBoxSectionProps as SelectSectionProps, 125 + ListBoxSectionHeaderProps as SelectSectionHeaderProps, 126 + ListBoxSeparatorProps as SelectSeparatorProps, 127 + } from "../listbox"; 128 + 129 + export { 130 + ListBoxItem as SelectItem, 131 + ListBoxSection as SelectSection, 132 + ListBoxSectionHeader as SelectSectionHeader, 133 + ListBoxSeparator as SelectSeparator, 134 + } from "../listbox";
+4 -2
apps/example/src/components/separator/index.tsx
··· 1 - import { SeparatorProps as AriaSeparatorProps } from "react-aria-components"; 2 - import { Separator as AriaSeparator } from "react-aria-components"; 3 1 import * as stylex from "@stylexjs/stylex"; 2 + import { 3 + SeparatorProps as AriaSeparatorProps, 4 + Separator as AriaSeparator, 5 + } from "react-aria-components"; 4 6 5 7 import { slate } from "../theme/colors.stylex"; 6 8
+16 -10
apps/example/src/components/text-area/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { use, useRef } from "react"; 1 3 import { 2 4 TextArea as AriaTextArea, 3 5 TextAreaProps as AriaTextAreaProps, 4 6 InputProps, 5 7 TextFieldProps, 6 8 ValidationResult, 9 + FieldError, 10 + TextField as AriaTextField, 7 11 } from "react-aria-components"; 8 12 9 - import { FieldError, TextField as AriaTextField } from "react-aria-components"; 10 - import * as stylex from "@stylexjs/stylex"; 13 + import { SizeContext } from "../context"; 14 + import { Description, Label } from "../label"; 15 + import { slate } from "../theme/colors.stylex"; 16 + import { radius } from "../theme/radius.stylex"; 11 17 import { gray } from "../theme/semantic-color.stylex"; 12 18 import { spacing } from "../theme/spacing.stylex"; 13 - import { Description, Label } from "../label"; 14 - import { radius } from "../theme/radius.stylex"; 15 19 import { lineHeight, fontSize, fontFamily } from "../theme/typography.stylex"; 16 - import { slate } from "../theme/colors.stylex"; 17 - import { use, useRef } from "react"; 18 20 import { Size } from "../types"; 19 - import { SizeContext } from "../context"; 20 21 21 22 const styles = stylex.create({ 22 23 wrapper: { ··· 140 141 141 142 return ( 142 143 <AriaTextField {...props} {...stylex.props(styles.wrapper, style)}> 143 - <Label size={size}>{label}</Label> 144 + {label !== null && <Label size={size}>{label}</Label>} 145 + {/* 146 + This onClick is specifically for mouse users not clicking directly on the input. 147 + A keyboard user would not encounter the same issue. 148 + */} 149 + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} 144 150 <div 145 151 {...stylex.props(styles.inputWrapper, gray.bgUi, gray.text)} 146 152 onClick={() => textAreaRef.current?.focus()} 147 153 > 148 - {prefix && <div {...stylex.props(styles.addon)}>{prefix}</div>} 154 + {prefix !== null && <div {...stylex.props(styles.addon)}>{prefix}</div>} 149 155 <AriaTextArea 150 156 {...stylex.props(styles.input, styles[`${size}Input`])} 151 157 ref={textAreaRef} 152 158 placeholder={placeholder} 153 159 rows={rows} 154 160 /> 155 - {suffix && <div {...stylex.props(styles.addon)}>{suffix}</div>} 161 + {suffix !== null && <div {...stylex.props(styles.addon)}>{suffix}</div>} 156 162 </div> 157 163 {description && <Description size={size}>{description}</Description>} 158 164 <FieldError>{errorMessage}</FieldError>
+22 -15
apps/example/src/components/text-field/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { Eye, EyeOff } from "lucide-react"; 3 + import { useRef, useState, use } from "react"; 1 4 import { 2 5 TextFieldProps as AriaTextFieldProps, 3 6 InputContext, 4 7 InputProps, 5 8 ValidationResult, 6 - } from "react-aria-components"; 7 - 8 - import { 9 9 FieldError, 10 10 Input, 11 11 TextField as AriaTextField, 12 12 } from "react-aria-components"; 13 - import * as stylex from "@stylexjs/stylex"; 13 + 14 + import { IconButton } from "../icon-button"; 14 15 import { Description, Label } from "../label"; 15 - import { useRef } from "react"; 16 - import { useState } from "react"; 17 - import { IconButton } from "../icon-button"; 18 - import { Eye, EyeOff } from "lucide-react"; 19 - import { use } from "react"; 16 + import { useInputStyles } from "../theme/useInputStyles"; 20 17 import { Size } from "../types"; 21 - import { useInputStyles } from "../theme/useInputStyles"; 22 18 23 19 function PasswordToggle({ 24 20 type, ··· 39 35 size="sm" 40 36 variant="tertiary" 41 37 label="Toggle password visibility" 42 - onPress={() => setType(type === "password" ? "text" : "password")} 38 + onPress={() => { 39 + setType(type === "password" ? "text" : "password"); 40 + }} 43 41 > 44 42 {type === "password" ? <EyeOff /> : <Eye />} 45 43 </IconButton> ··· 72 70 }: TextFieldProps) { 73 71 const inputRef = useRef<HTMLInputElement>(null); 74 72 const [type, setType] = useState<TextFieldProps["type"]>( 75 - props.type || "text" 73 + props.type || "text", 76 74 ); 77 75 const isPasswordInput = props.type === "password"; 78 76 const inputStyles = useInputStyles({ size }); ··· 83 81 type={type} 84 82 {...stylex.props(inputStyles.field, style)} 85 83 > 86 - <Label size={size}>{label}</Label> 84 + {label !== null && <Label size={size}>{label}</Label>} 85 + {/* 86 + This onClick is specifically for mouse users not clicking directly on the input. 87 + A keyboard user would not encounter the same issue. 88 + */} 89 + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} 87 90 <div 88 91 {...stylex.props(inputStyles.wrapper)} 89 92 onClick={() => inputRef.current?.focus()} 90 93 > 91 - {prefix && <div {...stylex.props(inputStyles.addon)}>{prefix}</div>} 94 + {prefix !== null && ( 95 + <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 96 + )} 92 97 <Input 93 98 {...stylex.props(inputStyles.input)} 94 99 ref={inputRef} 95 100 placeholder={placeholder} 96 101 /> 97 - {suffix && <div {...stylex.props(inputStyles.addon)}>{suffix}</div>} 102 + {suffix !== null && ( 103 + <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 104 + )} 98 105 {isPasswordInput && ( 99 106 <PasswordToggle 100 107 type={type}
+63
apps/example/src/components/theme/colors.stylex.tsx
··· 287 287 "light-dark(color(display-p3 0.102 0.193 0.379), color(display-p3 0.788 0.898 0.99))", 288 288 }, 289 289 }); 290 + 291 + export const red = stylex.defineVars({ 292 + 1: { 293 + default: "light-dark(#fffcfc, #191111)", 294 + "@media (color-gamut: p3)": 295 + "light-dark(color(display-p3 0.998 0.989 0.988), color(display-p3 0.093 0.068 0.067))", 296 + }, 297 + 2: { 298 + default: "light-dark(#fff7f7, #201314)", 299 + "@media (color-gamut: p3)": 300 + "light-dark(color(display-p3 0.995 0.971 0.971), color(display-p3 0.118 0.077 0.079))", 301 + }, 302 + 3: { 303 + default: "light-dark(#feebec, #3b1219)", 304 + "@media (color-gamut: p3)": 305 + "light-dark(color(display-p3 0.985 0.925 0.925), color(display-p3 0.211 0.081 0.099))", 306 + }, 307 + 4: { 308 + default: "light-dark(#ffdbdc, #500f1c)", 309 + "@media (color-gamut: p3)": 310 + "light-dark(color(display-p3 0.999 0.866 0.866), color(display-p3 0.287 0.079 0.113))", 311 + }, 312 + 5: { 313 + default: "light-dark(#ffcdce, #611623)", 314 + "@media (color-gamut: p3)": 315 + "light-dark(color(display-p3 0.984 0.812 0.811), color(display-p3 0.348 0.11 0.142))", 316 + }, 317 + 6: { 318 + default: "light-dark(#fdbdbe, #72232d)", 319 + "@media (color-gamut: p3)": 320 + "light-dark(color(display-p3 0.955 0.751 0.749), color(display-p3 0.414 0.16 0.183))", 321 + }, 322 + 7: { 323 + default: "light-dark(#f4a9aa, #8c333a)", 324 + "@media (color-gamut: p3)": 325 + "light-dark(color(display-p3 0.915 0.675 0.672), color(display-p3 0.508 0.224 0.236))", 326 + }, 327 + 8: { 328 + default: "light-dark(#eb8e90, #b54548)", 329 + "@media (color-gamut: p3)": 330 + "light-dark(color(display-p3 0.872 0.575 0.572), color(display-p3 0.659 0.298 0.297))", 331 + }, 332 + 9: { 333 + default: "light-dark(#e5484d, #e5484d)", 334 + "@media (color-gamut: p3)": 335 + "light-dark(color(display-p3 0.83 0.329 0.324), color(display-p3 0.83 0.329 0.324))", 336 + }, 337 + 10: { 338 + default: "light-dark(#dc3e42, #ec5d5e)", 339 + "@media (color-gamut: p3)": 340 + "light-dark(color(display-p3 0.798 0.294 0.285), color(display-p3 0.861 0.403 0.387))", 341 + }, 342 + 11: { 343 + default: "light-dark(#ce2c31, #ff9592)", 344 + "@media (color-gamut: p3)": 345 + "light-dark(color(display-p3 0.744 0.234 0.222), color(display-p3 1 0.57 0.55))", 346 + }, 347 + 12: { 348 + default: "light-dark(#641723, #ffd1d9)", 349 + "@media (color-gamut: p3)": 350 + "light-dark(color(display-p3 0.36 0.115 0.143), color(display-p3 0.971 0.826 0.852))", 351 + }, 352 + });
+130 -69
apps/example/src/components/theme/semantic-color.stylex.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 - import { slate, plum } from "./colors.stylex"; 2 + 3 + import { slate, plum, red } from "./colors.stylex"; 3 4 4 5 // eslint-disable-next-line @stylexjs/enforce-extension 5 6 export const gray = stylex.create({ 6 - bg: { 7 - backgroundColor: slate[1], 7 + bg: { backgroundColor: slate[1] }, 8 + bgSubtle: { backgroundColor: slate[2] }, 9 + bgDim: { backgroundColor: slate[3] }, 10 + bgSecondary: { backgroundColor: slate[4] }, 11 + bgActive: { backgroundColor: slate[5] }, 12 + borderDim: { 13 + borderColor: slate[6], 14 + borderStyle: "solid", 15 + borderWidth: 1, 8 16 }, 9 - bgSubtle: { 10 - backgroundColor: slate[2], 11 - }, 12 - bgDim: { 13 - backgroundColor: slate[3], 14 - }, 15 - bgSecondary: { 16 - backgroundColor: slate[4], 17 + border: { 18 + borderColor: slate[7], 19 + borderStyle: "solid", 20 + borderWidth: 1, 17 21 }, 18 - bgActive: { 19 - backgroundColor: slate[5], 22 + borderInteractive: { 23 + borderColor: { 24 + default: slate[7], 25 + ":hover": slate[8], 26 + }, 27 + borderStyle: "solid", 28 + borderWidth: 1, 29 + 30 + transitionDuration: "100ms", 31 + transitionProperty: "background-color, border-color", 32 + transitionTimingFunction: "ease-in-out", 20 33 }, 21 - bgUi: { 34 + bgSolid: { backgroundColor: slate[9] }, 35 + bgSolidDark: { backgroundColor: slate[10] }, 36 + textDim: { color: slate[11] }, 37 + text: { color: slate[12] }, 38 + textContrast: { color: "white" }, 39 + 40 + bgGhost: { 22 41 backgroundColor: { 23 - default: slate[3], 42 + default: "transparent", 24 43 ":hover:not(:has(* button:hover)):not(:disabled)": slate[4], 25 44 ":active:not(:disabled)": slate[5], 26 45 ":disabled": slate[3], ··· 29 48 transitionProperty: "background-color, border-color", 30 49 transitionTimingFunction: "ease-in-out", 31 50 }, 32 - bgGhost: { 51 + bgUi: { 33 52 backgroundColor: { 34 - default: "transparent", 53 + default: slate[3], 35 54 ":hover:not(:has(* button:hover)):not(:disabled)": slate[4], 36 55 ":active:not(:disabled)": slate[5], 37 56 ":disabled": slate[3], ··· 51 70 transitionProperty: "background-color, border-color", 52 71 transitionTimingFunction: "ease-in-out", 53 72 }, 73 + }); 74 + 75 + // eslint-disable-next-line @stylexjs/enforce-extension 76 + export const primary = stylex.create({ 77 + bg: { backgroundColor: plum[1] }, 78 + bgSubtle: { backgroundColor: plum[2] }, 79 + bgDim: { backgroundColor: plum[3] }, 80 + bgSecondary: { backgroundColor: plum[4] }, 81 + bgActive: { backgroundColor: plum[5] }, 54 82 borderDim: { 55 - borderColor: slate[6], 83 + borderColor: plum[6], 56 84 borderStyle: "solid", 57 85 borderWidth: 1, 58 86 }, 59 87 border: { 60 - borderColor: slate[7], 88 + borderColor: plum[7], 61 89 borderStyle: "solid", 62 90 borderWidth: 1, 63 91 }, 64 92 borderInteractive: { 65 93 borderColor: { 66 - default: slate[7], 67 - ":hover": slate[8], 94 + default: plum[7], 95 + ":hover": plum[8], 68 96 }, 69 97 borderStyle: "solid", 70 98 borderWidth: 1, ··· 73 101 transitionProperty: "background-color, border-color", 74 102 transitionTimingFunction: "ease-in-out", 75 103 }, 76 - bgSolid: { 77 - backgroundColor: slate[9], 78 - }, 79 - bgSolidDark: { 80 - backgroundColor: slate[10], 81 - }, 82 - textDim: { 83 - color: slate[11], 84 - }, 85 - text: { 86 - color: slate[12], 87 - }, 88 - }); 104 + bgSolid: { backgroundColor: plum[9] }, 105 + bgSolidDark: { backgroundColor: plum[10] }, 106 + textDim: { color: plum[11] }, 107 + text: { color: plum[12] }, 108 + textContrast: { color: "white" }, 89 109 90 - // eslint-disable-next-line @stylexjs/enforce-extension 91 - export const primary = stylex.create({ 92 - bg: { 93 - backgroundColor: plum[1], 94 - }, 95 - bgSubtle: { 96 - backgroundColor: plum[2], 97 - }, 98 - bgDim: { 99 - backgroundColor: plum[3], 100 - }, 101 - bgSecondary: { 102 - backgroundColor: plum[4], 103 - }, 104 - bgActive: { 105 - backgroundColor: plum[5], 106 - }, 107 - bgUi: { 110 + bgGhost: { 108 111 backgroundColor: { 109 - default: plum[3], 112 + default: "transparent", 110 113 ":hover:not(:has(* button:hover)):not(:disabled)": plum[4], 111 - ":active": plum[5], 114 + ":active:not(:disabled)": plum[5], 112 115 ":disabled": plum[3], 113 116 }, 117 + transitionDuration: "100ms", 118 + transitionProperty: "background-color, border-color", 119 + transitionTimingFunction: "ease-in-out", 114 120 }, 115 - bgGhost: { 121 + bgUi: { 116 122 backgroundColor: { 117 - default: "transparent", 123 + default: plum[3], 118 124 ":hover:not(:has(* button:hover)):not(:disabled)": plum[4], 119 - ":active:not(:disabled)": plum[5], 125 + ":active": plum[5], 120 126 ":disabled": plum[3], 121 127 }, 128 + transitionDuration: "100ms", 129 + transitionProperty: "background-color, border-color", 130 + transitionTimingFunction: "ease-in-out", 122 131 }, 123 132 bgAction: { 124 133 backgroundColor: { ··· 127 136 ":active:not(:disabled)": plum[6], 128 137 ":disabled": plum[3], 129 138 }, 139 + transitionDuration: "100ms", 140 + transitionProperty: "background-color, border-color", 141 + transitionTimingFunction: "ease-in-out", 130 142 }, 143 + }); 144 + 145 + // eslint-disable-next-line @stylexjs/enforce-extension 146 + export const critical = stylex.create({ 147 + bg: { backgroundColor: red[1] }, 148 + bgSubtle: { backgroundColor: red[2] }, 149 + bgDim: { backgroundColor: red[3] }, 150 + bgSecondary: { backgroundColor: red[4] }, 151 + bgActive: { backgroundColor: red[5] }, 131 152 borderDim: { 132 - borderColor: plum[6], 153 + borderColor: red[6], 133 154 borderStyle: "solid", 134 155 borderWidth: 1, 135 156 }, 136 157 border: { 137 - borderColor: plum[7], 158 + borderColor: red[7], 138 159 borderStyle: "solid", 139 160 borderWidth: 1, 140 161 }, 141 162 borderInteractive: { 142 163 borderColor: { 143 - default: plum[7], 144 - ":hover": plum[8], 164 + default: red[7], 165 + ":hover": red[8], 145 166 }, 146 167 borderStyle: "solid", 147 168 borderWidth: 1, 169 + transitionDuration: "100ms", 170 + transitionProperty: "background-color, border-color", 171 + transitionTimingFunction: "ease-in-out", 148 172 }, 149 - bgSolid: { 150 - backgroundColor: plum[9], 173 + bgSolid: { backgroundColor: red[9] }, 174 + bgSolidDark: { backgroundColor: red[10] }, 175 + textDim: { color: red[11] }, 176 + text: { color: red[12] }, 177 + textContrast: { color: "white" }, 178 + 179 + bgGhost: { 180 + backgroundColor: { 181 + default: "transparent", 182 + ":hover:not(:has(* button:hover)):not(:disabled)": red[4], 183 + ":active:not(:disabled)": red[5], 184 + ":disabled": red[3], 185 + }, 186 + transitionDuration: "100ms", 187 + transitionProperty: "background-color, border-color", 188 + transitionTimingFunction: "ease-in-out", 151 189 }, 152 - bgSolidDark: { 153 - backgroundColor: plum[10], 190 + bgUi: { 191 + backgroundColor: { 192 + default: red[3], 193 + ":hover:not(:has(* button:hover)):not(:disabled)": red[4], 194 + ":active:not(:disabled)": red[5], 195 + ":disabled": red[3], 196 + }, 197 + transitionDuration: "100ms", 198 + transitionProperty: "background-color, border-color", 199 + transitionTimingFunction: "ease-in-out", 154 200 }, 155 - textDim: { 156 - color: plum[11], 201 + bgAction: { 202 + backgroundColor: { 203 + default: red[4], 204 + ":hover:not(:has(* button:hover)):not(:disabled)": red[5], 205 + ":active:not(:disabled)": red[6], 206 + ":disabled": red[3], 207 + }, 208 + transitionDuration: "100ms", 209 + transitionProperty: "background-color, border-color", 210 + transitionTimingFunction: "ease-in-out", 157 211 }, 158 - text: { 159 - color: plum[12], 212 + bgSolidAction: { 213 + backgroundColor: { 214 + default: red[9], 215 + ":hover:not(:has(* button:hover)):not(:disabled)": red[10], 216 + ":disabled": red[3], 217 + }, 218 + transitionDuration: "100ms", 219 + transitionProperty: "background-color, border-color", 220 + transitionTimingFunction: "ease-in-out", 160 221 }, 161 222 });
+2 -1
apps/example/src/components/theme/typography.stylex.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 + 3 + import { breakpoints } from "./breakpoints.stylex"; 2 4 import { spacing } from "./spacing.stylex"; 3 - import { breakpoints } from "./breakpoints.stylex"; 4 5 5 6 export const fontFamily = stylex.defineVars({ 6 7 sans: "Inter, sans-serif",
+14 -8
apps/example/src/components/theme/useButtonStyles.ts
··· 1 1 "use client"; 2 2 3 3 import * as stylex from "@stylexjs/stylex"; 4 + import { use } from "react"; 4 5 5 - import { spacing } from "./spacing.stylex"; 6 + import { ButtonGroupContext } from "../button/context"; 7 + import { SizeContext } from "../context"; 8 + import { Size, ButtonVariant } from "../types"; 9 + import { slate } from "./colors.stylex"; 6 10 import { radius } from "./radius.stylex"; 7 - import { gray, primary } from "./semantic-color.stylex"; 11 + import { critical, gray, primary } from "./semantic-color.stylex"; 12 + import { shadow } from "./shadow.stylex"; 13 + import { spacing } from "./spacing.stylex"; 8 14 import { 9 15 fontFamily, 10 16 fontSize, 11 17 fontWeight, 12 18 lineHeight, 13 19 } from "./typography.stylex"; 14 - import { shadow } from "./shadow.stylex"; 15 - import { slate } from "./colors.stylex"; 16 - import { use } from "react"; 17 - import { Size, ButtonVariant } from "../types"; 18 - import { ButtonGroupContext } from "../button/context"; 19 - import { SizeContext } from "../context"; 20 20 21 21 const styles = stylex.create({ 22 22 shadow: { ··· 169 169 gray.bgGhost, 170 170 gray.text, 171 171 styles.outline, 172 + styles.shadow, 173 + ], 174 + variant === "critical" && [ 175 + critical.bgSolidAction, 176 + critical.borderInteractive, 177 + critical.textContrast, 172 178 styles.shadow, 173 179 ], 174 180 size === "sm" && styles.small,
+7 -6
apps/example/src/components/theme/useInputStyles.ts
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 + import { use } from "react"; 3 + 4 + import { SizeContext } from "../context"; 5 + import { Size } from "../types"; 6 + import { slate } from "./colors.stylex"; 7 + import { radius } from "./radius.stylex"; 2 8 import { gray } from "./semantic-color.stylex"; 3 9 import { spacing } from "./spacing.stylex"; 4 - import { radius } from "./radius.stylex"; 5 10 import { lineHeight, fontSize } from "./typography.stylex"; 6 - import { slate } from "./colors.stylex"; 7 - import { Size } from "../types"; 8 - import { use } from "react"; 9 - import { SizeContext } from "../context"; 10 11 11 12 const styles = stylex.create({ 12 13 field: { ··· 107 108 field: [styles.field], 108 109 wrapper: [styles.inputWrapper, gray.bgUi, gray.text, styles[size]], 109 110 input: [styles.input, styles[`${size}Input`]], 110 - addon: [styles.addon], 111 + addon: styles.addon as unknown as stylex.StyleXStyles, 111 112 }; 112 113 }
+5 -4
apps/example/src/components/theme/useListBoxItemStyles.ts
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 - import { spacing } from "../theme/spacing.stylex"; 2 + import { use } from "react"; 3 + 4 + import { SizeContext } from "../context"; 3 5 import { plum, slate } from "../theme/colors.stylex"; 4 6 import { radius } from "../theme/radius.stylex"; 7 + import { spacing } from "../theme/spacing.stylex"; 5 8 import { 6 9 fontSize, 7 10 fontWeight, 8 11 lineHeight, 9 12 typeramp, 10 13 } from "../theme/typography.stylex"; 11 - import { SizeContext } from "../context"; 12 - import { useContext } from "react"; 13 14 14 15 const styles = stylex.create({ 15 16 item: { ··· 94 95 }); 95 96 96 97 export function useListBoxItemStyles() { 97 - const size = useContext(SizeContext); 98 + const size = use(SizeContext); 98 99 99 100 return { 100 101 wrapper: [typeramp.label, styles.item, styles[size]],
+2 -1
apps/example/src/components/theme/usePopoverStyles.ts
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 2 3 3 import { radius } from "./radius.stylex"; 4 - import { spacing } from "./spacing.stylex"; 5 4 import { gray } from "./semantic-color.stylex"; 6 5 import { shadow } from "./shadow.stylex"; 6 + import { spacing } from "./spacing.stylex"; 7 7 8 8 const styles = stylex.create({ 9 9 popover: { ··· 18 18 }, 19 19 }); 20 20 21 + // eslint-disable-next-line @eslint-react/no-unnecessary-use-prefix 21 22 export function usePopoverStyles() { 22 23 return [styles.popover, gray.bgSubtle, gray.text, gray.border]; 23 24 }
+17 -7
apps/example/src/components/time-field/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + import { useRef } from "react"; 1 3 import { 2 4 TimeFieldProps as AriaTimeFieldProps, 3 5 DateInput, 4 6 DateSegment, 5 7 TimeValue, 6 8 ValidationResult, 9 + FieldError, 10 + TimeField as AriaTimeField, 7 11 } from "react-aria-components"; 8 12 9 - import { FieldError, TimeField as AriaTimeField } from "react-aria-components"; 10 - import * as stylex from "@stylexjs/stylex"; 11 13 import { Description, Label } from "../label"; 12 - import { useRef } from "react"; 13 - import { Size } from "../types"; 14 14 import { useInputStyles } from "../theme/useInputStyles"; 15 + import { Size } from "../types"; 15 16 16 17 export interface TimeFieldProps<T extends TimeValue> 17 18 extends Omit<AriaTimeFieldProps<T>, "style" | "className"> { ··· 39 40 40 41 return ( 41 42 <AriaTimeField {...props} {...stylex.props(inputStyles.field, style)}> 42 - <Label size={size}>{label}</Label> 43 + {label !== null && <Label size={size}>{label}</Label>} 44 + {/* 45 + This onClick is specifically for mouse users not clicking directly on the input. 46 + A keyboard user would not encounter the same issue. 47 + */} 48 + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} 43 49 <div 44 50 {...stylex.props(inputStyles.wrapper)} 45 51 onClick={() => inputRef.current?.focus()} 46 52 > 47 - {prefix && <div {...stylex.props(inputStyles.addon)}>{prefix}</div>} 53 + {prefix !== null && ( 54 + <div {...stylex.props(inputStyles.addon)}>{prefix}</div> 55 + )} 48 56 <DateInput {...stylex.props(inputStyles.input)} ref={inputRef}> 49 57 {(segment) => <DateSegment segment={segment} />} 50 58 </DateInput> 51 - {suffix && <div {...stylex.props(inputStyles.addon)}>{suffix}</div>} 59 + {suffix !== null && ( 60 + <div {...stylex.props(inputStyles.addon)}>{suffix}</div> 61 + )} 52 62 </div> 53 63 {description && <Description size={size}>{description}</Description>} 54 64 <FieldError>{errorMessage}</FieldError>
+7 -5
apps/example/src/components/toggle-button-group/index.tsx
··· 1 1 "use client"; 2 2 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { use } from "react"; 3 5 import { 4 6 ToggleButtonGroupProps as AriaToggleButtonGroupProps, 5 7 ToggleButtonGroup as AriaToggleButtonGroup, 6 8 } from "react-aria-components"; 7 - import * as stylex from "@stylexjs/stylex"; 9 + 8 10 import { ButtonGroupContext } from "../button/context"; 9 - import { use } from "react"; 10 11 11 12 const styles = stylex.create({ 12 13 group: { ··· 31 32 borderTopRightRadius: { ":is(:not(:last-child)) *": "0" }, 32 33 }, 33 34 verticalContents: { 35 + // eslint-disable-next-line @stylexjs/valid-styles 34 36 borderBottomLeftWidth: { ":is(:not(:first-child)) *": "0" }, 35 37 borderBottomRightRadius: { ":is(:not(:last-child)) *": "0" }, 36 38 borderBottomWidth: { ":is(:not(:last-child)) *": "0" }, ··· 58 60 const orientation = groupOrientation || orientationProp; 59 61 60 62 return ( 61 - <ButtonGroupContext.Provider value={orientation}> 63 + <ButtonGroupContext value={orientation}> 62 64 <AriaToggleButtonGroup 63 65 {...stylex.props( 64 66 isInGroup ··· 72 74 orientation === "horizontal" && styles.horizontal, 73 75 orientation === "vertical" && styles.vertical, 74 76 ], 75 - style 77 + style, 76 78 )} 77 79 {...props} 78 80 > 79 81 {children} 80 82 </AriaToggleButtonGroup> 81 - </ButtonGroupContext.Provider> 83 + </ButtonGroupContext> 82 84 ); 83 85 };
+10 -7
apps/example/src/components/toggle-button/index.tsx
··· 1 - import { ToggleButtonProps as AriaToggleButtonProps } from "react-aria-components"; 2 - import { ToggleButton as AriaToggleButton } from "react-aria-components"; 3 1 import * as stylex from "@stylexjs/stylex"; 2 + import { Children, use } from "react"; 3 + import { 4 + ToggleButtonProps as AriaToggleButtonProps, 5 + ToggleButton as AriaToggleButton, 6 + } from "react-aria-components"; 4 7 5 - import { spacing } from "../theme/spacing.stylex"; 8 + import { SizeContext } from "../context"; 6 9 import { plum, slate } from "../theme/colors.stylex"; 7 - import { Children, use } from "react"; 10 + import { spacing } from "../theme/spacing.stylex"; 8 11 import { useButtonStyles } from "../theme/useButtonStyles"; 9 12 import { ButtonVariant, Size } from "../types"; 10 - import { SizeContext } from "../context"; 11 13 12 14 const styles = stylex.create({ 13 15 primarySelected: { ··· 92 94 export interface ToggleButtonProps 93 95 extends Omit<AriaToggleButtonProps, "style" | "className" | "children"> { 94 96 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 95 - variant?: ButtonVariant; 97 + variant?: Exclude<ButtonVariant, "critical">; 96 98 size?: Size; 97 99 children?: React.ReactNode; 98 100 } ··· 122 124 toggleButtonStyles(isSelected).className || "" 123 125 } 124 126 > 127 + {/* eslint-disable-next-line @eslint-react/no-children-map */} 125 128 {Children.map(children, (child, index) => 126 129 typeof child === "string" ? ( 127 - <span key={`${child}-${index}`}>{child}</span> 130 + <span key={`${child}-${index.toString()}`}>{child}</span> 128 131 ) : ( 129 132 child 130 133 )
+4 -4
apps/example/src/components/tooltip/index.tsx
··· 1 1 "use client"; 2 2 3 + import * as stylex from "@stylexjs/stylex"; 3 4 import { 4 5 OverlayArrow, 5 6 Tooltip as AriaTooltip, ··· 7 8 TooltipTriggerComponentProps, 8 9 TooltipProps as AriaTooltipProps, 9 10 } from "react-aria-components"; 10 - import * as stylex from "@stylexjs/stylex"; 11 11 12 + import { slateInverted } from "../theme/colors.stylex"; 13 + import { radius } from "../theme/radius.stylex"; 14 + import { shadow } from "../theme/shadow.stylex"; 12 15 import { spacing } from "../theme/spacing.stylex"; 13 - import { radius } from "../theme/radius.stylex"; 14 16 import { fontFamily, fontSize, lineHeight } from "../theme/typography.stylex"; 15 - import { shadow } from "../theme/shadow.stylex"; 16 - import { slateInverted } from "../theme/colors.stylex"; 17 17 18 18 const tooltipStyle = stylex.create({ 19 19 content: {
+13 -12
apps/example/src/components/tree/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 1 2 import { ChevronRight, GripVertical } from "lucide-react"; 3 + import { use } from "react"; 2 4 import { 3 5 Button, 4 6 Tree as AriaTree, ··· 8 10 TreeItemProps as AriaTreeItemProps, 9 11 TreeItemContentProps as AriaTreeItemContentProps, 10 12 } from "react-aria-components"; 13 + 11 14 import { Checkbox } from "../checkbox"; 12 - import * as stylex from "@stylexjs/stylex"; 13 - import { Size } from "../types"; 14 15 import { SizeContext } from "../context"; 15 - import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 16 + import { radius } from "../theme/radius.stylex"; 17 + import { gray } from "../theme/semantic-color.stylex"; 16 18 import { spacing } from "../theme/spacing.stylex"; 17 - import { gray } from "../theme/semantic-color.stylex"; 18 - import { radius } from "../theme/radius.stylex"; 19 - import { use } from "react"; 19 + import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 20 + import { Size } from "../types"; 20 21 21 22 const styles = stylex.create({ 22 23 wrapper: { ··· 118 119 styles.chevron, 119 120 gray.textDim, 120 121 listBoxItemStyles.addon, 121 - !hasChildItems && styles.hidden 122 + !hasChildItems && styles.hidden, 122 123 )} 123 124 > 124 125 <ChevronRight size={16} /> 125 126 </Button> 126 127 127 128 <div {...stylex.props(styles.content)}> 128 - {prefix && ( 129 + {prefix !== null && ( 129 130 <div {...stylex.props(listBoxItemStyles.addon, styles.addon)}> 130 131 {prefix} 131 132 </div> 132 133 )} 133 134 <div {...stylex.props(listBoxItemStyles.label)}>{children}</div> 134 - {suffix && ( 135 + {suffix !== null && ( 135 136 <div {...stylex.props(listBoxItemStyles.addon, styles.addon)}> 136 137 {suffix} 137 138 </div> ··· 169 170 listBoxItemStyles.wrapper, 170 171 styles.wrapper, 171 172 styles.itemInner, 172 - style 173 + style, 173 174 )} 174 175 > 175 176 <TreeItemContent prefix={prefix} suffix={suffix}> ··· 195 196 const size = sizeProp || use(SizeContext); 196 197 197 198 return ( 198 - <SizeContext.Provider value={size}> 199 + <SizeContext value={size}> 199 200 <AriaTree {...props} {...stylex.props(style)} /> 200 - </SizeContext.Provider> 201 + </SizeContext> 201 202 ); 202 203 }
+6 -1
apps/example/src/components/types.ts
··· 1 1 export type Size = "sm" | "md" | "lg"; 2 - export type ButtonVariant = "primary" | "secondary" | "tertiary" | "outline"; 2 + export type ButtonVariant = 3 + | "primary" 4 + | "secondary" 5 + | "tertiary" 6 + | "outline" 7 + | "critical";
+15 -10
apps/example/src/components/typography/index.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 + import { useMemo } from "react"; 3 + 4 + import { LinkContext } from "../link/link-context"; 5 + import { radius } from "../theme/radius.stylex"; 6 + import { gray } from "../theme/semantic-color.stylex"; 7 + import { spacing } from "../theme/spacing.stylex"; 2 8 import { 3 9 fontFamily, 4 10 fontSize, 5 11 lineHeight, 6 12 typeramp, 7 13 } from "../theme/typography.stylex"; 8 - import { gray } from "../theme/semantic-color.stylex"; 9 - import { spacing } from "../theme/spacing.stylex"; 10 - import { radius } from "../theme/radius.stylex"; 11 - import { useMemo } from "react"; 12 - import { LinkContext } from "../link"; 13 14 14 15 const styles = stylex.create({ 15 16 blockquote: { ··· 62 63 } 63 64 64 65 export const Heading1 = ({ style, ...props }: Heading1Props) => { 66 + // eslint-disable-next-line jsx-a11y/heading-has-content 65 67 return <h1 {...stylex.props(typeramp.heading1, style)} {...props} />; 66 68 }; 67 69 ··· 71 73 } 72 74 73 75 export const Heading2 = ({ style, ...props }: Heading2Props) => { 76 + // eslint-disable-next-line jsx-a11y/heading-has-content 74 77 return <h2 {...stylex.props(typeramp.heading2, style)} {...props} />; 75 78 }; 76 79 ··· 80 83 } 81 84 82 85 export const Heading3 = ({ style, ...props }: Heading3Props) => { 86 + // eslint-disable-next-line jsx-a11y/heading-has-content 83 87 return <h3 {...stylex.props(typeramp.heading3, style)} {...props} />; 84 88 }; 85 89 ··· 89 93 } 90 94 91 95 export const Heading4 = ({ style, ...props }: Heading4Props) => { 96 + // eslint-disable-next-line jsx-a11y/heading-has-content 92 97 return <h4 {...stylex.props(typeramp.heading4, style)} {...props} />; 93 98 }; 94 99 ··· 117 122 {...stylex.props( 118 123 typeramp.smallBody, 119 124 variant === "secondary" && gray.textDim, 120 - style 125 + style, 121 126 )} 122 127 {...props} 123 128 /> ··· 139 144 () => ({ 140 145 style: [variant === "secondary" && gray.textDim, styles.underline], 141 146 }), 142 - [variant] 147 + [variant], 143 148 ); 144 149 145 150 return ( 146 - <LinkContext.Provider value={contextValue}> 151 + <LinkContext value={contextValue}> 147 152 <p 148 153 {...stylex.props( 149 154 typeramp.sublabel, 150 155 variant === "secondary" && gray.textDim, 151 - style 156 + style, 152 157 )} 153 158 {...props} 154 159 /> 155 - </LinkContext.Provider> 160 + </LinkContext> 156 161 ); 157 162 }; 158 163
+1 -1
packages/hip-ui/src/components/card/index.tsx
··· 69 69 return ( 70 70 <Flex 71 71 {...props} 72 - style={[styles.cardHeader, style]} 72 + style={[styles.cardHeader as unknown as stylex.StyleXStyles, style]} 73 73 direction="column" 74 74 gap="3" 75 75 />
+63
packages/hip-ui/src/components/theme/colors.stylex.tsx
··· 287 287 "light-dark(color(display-p3 0.102 0.193 0.379), color(display-p3 0.788 0.898 0.99))", 288 288 }, 289 289 }); 290 + 291 + export const red = stylex.defineVars({ 292 + 1: { 293 + default: "light-dark(#fffcfc, #191111)", 294 + "@media (color-gamut: p3)": 295 + "light-dark(color(display-p3 0.998 0.989 0.988), color(display-p3 0.093 0.068 0.067))", 296 + }, 297 + 2: { 298 + default: "light-dark(#fff7f7, #201314)", 299 + "@media (color-gamut: p3)": 300 + "light-dark(color(display-p3 0.995 0.971 0.971), color(display-p3 0.118 0.077 0.079))", 301 + }, 302 + 3: { 303 + default: "light-dark(#feebec, #3b1219)", 304 + "@media (color-gamut: p3)": 305 + "light-dark(color(display-p3 0.985 0.925 0.925), color(display-p3 0.211 0.081 0.099))", 306 + }, 307 + 4: { 308 + default: "light-dark(#ffdbdc, #500f1c)", 309 + "@media (color-gamut: p3)": 310 + "light-dark(color(display-p3 0.999 0.866 0.866), color(display-p3 0.287 0.079 0.113))", 311 + }, 312 + 5: { 313 + default: "light-dark(#ffcdce, #611623)", 314 + "@media (color-gamut: p3)": 315 + "light-dark(color(display-p3 0.984 0.812 0.811), color(display-p3 0.348 0.11 0.142))", 316 + }, 317 + 6: { 318 + default: "light-dark(#fdbdbe, #72232d)", 319 + "@media (color-gamut: p3)": 320 + "light-dark(color(display-p3 0.955 0.751 0.749), color(display-p3 0.414 0.16 0.183))", 321 + }, 322 + 7: { 323 + default: "light-dark(#f4a9aa, #8c333a)", 324 + "@media (color-gamut: p3)": 325 + "light-dark(color(display-p3 0.915 0.675 0.672), color(display-p3 0.508 0.224 0.236))", 326 + }, 327 + 8: { 328 + default: "light-dark(#eb8e90, #b54548)", 329 + "@media (color-gamut: p3)": 330 + "light-dark(color(display-p3 0.872 0.575 0.572), color(display-p3 0.659 0.298 0.297))", 331 + }, 332 + 9: { 333 + default: "light-dark(#e5484d, #e5484d)", 334 + "@media (color-gamut: p3)": 335 + "light-dark(color(display-p3 0.83 0.329 0.324), color(display-p3 0.83 0.329 0.324))", 336 + }, 337 + 10: { 338 + default: "light-dark(#dc3e42, #ec5d5e)", 339 + "@media (color-gamut: p3)": 340 + "light-dark(color(display-p3 0.798 0.294 0.285), color(display-p3 0.861 0.403 0.387))", 341 + }, 342 + 11: { 343 + default: "light-dark(#ce2c31, #ff9592)", 344 + "@media (color-gamut: p3)": 345 + "light-dark(color(display-p3 0.744 0.234 0.222), color(display-p3 1 0.57 0.55))", 346 + }, 347 + 12: { 348 + default: "light-dark(#641723, #ffd1d9)", 349 + "@media (color-gamut: p3)": 350 + "light-dark(color(display-p3 0.36 0.115 0.143), color(display-p3 0.971 0.826 0.852))", 351 + }, 352 + });
+129 -69
packages/hip-ui/src/components/theme/semantic-color.stylex.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 2 3 - import { slate, plum } from "./colors.stylex"; 3 + import { slate, plum, red } from "./colors.stylex"; 4 4 5 5 // eslint-disable-next-line @stylexjs/enforce-extension 6 6 export const gray = stylex.create({ 7 - bg: { 8 - backgroundColor: slate[1], 7 + bg: { backgroundColor: slate[1] }, 8 + bgSubtle: { backgroundColor: slate[2] }, 9 + bgDim: { backgroundColor: slate[3] }, 10 + bgSecondary: { backgroundColor: slate[4] }, 11 + bgActive: { backgroundColor: slate[5] }, 12 + borderDim: { 13 + borderColor: slate[6], 14 + borderStyle: "solid", 15 + borderWidth: 1, 9 16 }, 10 - bgSubtle: { 11 - backgroundColor: slate[2], 12 - }, 13 - bgDim: { 14 - backgroundColor: slate[3], 15 - }, 16 - bgSecondary: { 17 - backgroundColor: slate[4], 17 + border: { 18 + borderColor: slate[7], 19 + borderStyle: "solid", 20 + borderWidth: 1, 18 21 }, 19 - bgActive: { 20 - backgroundColor: slate[5], 22 + borderInteractive: { 23 + borderColor: { 24 + default: slate[7], 25 + ":hover": slate[8], 26 + }, 27 + borderStyle: "solid", 28 + borderWidth: 1, 29 + 30 + transitionDuration: "100ms", 31 + transitionProperty: "background-color, border-color", 32 + transitionTimingFunction: "ease-in-out", 21 33 }, 22 - bgUi: { 34 + bgSolid: { backgroundColor: slate[9] }, 35 + bgSolidDark: { backgroundColor: slate[10] }, 36 + textDim: { color: slate[11] }, 37 + text: { color: slate[12] }, 38 + textContrast: { color: "white" }, 39 + 40 + bgGhost: { 23 41 backgroundColor: { 24 - default: slate[3], 42 + default: "transparent", 25 43 ":hover:not(:has(* button:hover)):not(:disabled)": slate[4], 26 44 ":active:not(:disabled)": slate[5], 27 45 ":disabled": slate[3], ··· 30 48 transitionProperty: "background-color, border-color", 31 49 transitionTimingFunction: "ease-in-out", 32 50 }, 33 - bgGhost: { 51 + bgUi: { 34 52 backgroundColor: { 35 - default: "transparent", 53 + default: slate[3], 36 54 ":hover:not(:has(* button:hover)):not(:disabled)": slate[4], 37 55 ":active:not(:disabled)": slate[5], 38 56 ":disabled": slate[3], ··· 52 70 transitionProperty: "background-color, border-color", 53 71 transitionTimingFunction: "ease-in-out", 54 72 }, 73 + }); 74 + 75 + // eslint-disable-next-line @stylexjs/enforce-extension 76 + export const primary = stylex.create({ 77 + bg: { backgroundColor: plum[1] }, 78 + bgSubtle: { backgroundColor: plum[2] }, 79 + bgDim: { backgroundColor: plum[3] }, 80 + bgSecondary: { backgroundColor: plum[4] }, 81 + bgActive: { backgroundColor: plum[5] }, 55 82 borderDim: { 56 - borderColor: slate[6], 83 + borderColor: plum[6], 57 84 borderStyle: "solid", 58 85 borderWidth: 1, 59 86 }, 60 87 border: { 61 - borderColor: slate[7], 88 + borderColor: plum[7], 62 89 borderStyle: "solid", 63 90 borderWidth: 1, 64 91 }, 65 92 borderInteractive: { 66 93 borderColor: { 67 - default: slate[7], 68 - ":hover": slate[8], 94 + default: plum[7], 95 + ":hover": plum[8], 69 96 }, 70 97 borderStyle: "solid", 71 98 borderWidth: 1, ··· 74 101 transitionProperty: "background-color, border-color", 75 102 transitionTimingFunction: "ease-in-out", 76 103 }, 77 - bgSolid: { 78 - backgroundColor: slate[9], 79 - }, 80 - bgSolidDark: { 81 - backgroundColor: slate[10], 82 - }, 83 - textDim: { 84 - color: slate[11], 85 - }, 86 - text: { 87 - color: slate[12], 88 - }, 89 - }); 104 + bgSolid: { backgroundColor: plum[9] }, 105 + bgSolidDark: { backgroundColor: plum[10] }, 106 + textDim: { color: plum[11] }, 107 + text: { color: plum[12] }, 108 + textContrast: { color: "white" }, 90 109 91 - // eslint-disable-next-line @stylexjs/enforce-extension 92 - export const primary = stylex.create({ 93 - bg: { 94 - backgroundColor: plum[1], 95 - }, 96 - bgSubtle: { 97 - backgroundColor: plum[2], 98 - }, 99 - bgDim: { 100 - backgroundColor: plum[3], 101 - }, 102 - bgSecondary: { 103 - backgroundColor: plum[4], 104 - }, 105 - bgActive: { 106 - backgroundColor: plum[5], 107 - }, 108 - bgUi: { 110 + bgGhost: { 109 111 backgroundColor: { 110 - default: plum[3], 112 + default: "transparent", 111 113 ":hover:not(:has(* button:hover)):not(:disabled)": plum[4], 112 - ":active": plum[5], 114 + ":active:not(:disabled)": plum[5], 113 115 ":disabled": plum[3], 114 116 }, 117 + transitionDuration: "100ms", 118 + transitionProperty: "background-color, border-color", 119 + transitionTimingFunction: "ease-in-out", 115 120 }, 116 - bgGhost: { 121 + bgUi: { 117 122 backgroundColor: { 118 - default: "transparent", 123 + default: plum[3], 119 124 ":hover:not(:has(* button:hover)):not(:disabled)": plum[4], 120 - ":active:not(:disabled)": plum[5], 125 + ":active": plum[5], 121 126 ":disabled": plum[3], 122 127 }, 128 + transitionDuration: "100ms", 129 + transitionProperty: "background-color, border-color", 130 + transitionTimingFunction: "ease-in-out", 123 131 }, 124 132 bgAction: { 125 133 backgroundColor: { ··· 128 136 ":active:not(:disabled)": plum[6], 129 137 ":disabled": plum[3], 130 138 }, 139 + transitionDuration: "100ms", 140 + transitionProperty: "background-color, border-color", 141 + transitionTimingFunction: "ease-in-out", 131 142 }, 143 + }); 144 + 145 + // eslint-disable-next-line @stylexjs/enforce-extension 146 + export const critical = stylex.create({ 147 + bg: { backgroundColor: red[1] }, 148 + bgSubtle: { backgroundColor: red[2] }, 149 + bgDim: { backgroundColor: red[3] }, 150 + bgSecondary: { backgroundColor: red[4] }, 151 + bgActive: { backgroundColor: red[5] }, 132 152 borderDim: { 133 - borderColor: plum[6], 153 + borderColor: red[6], 134 154 borderStyle: "solid", 135 155 borderWidth: 1, 136 156 }, 137 157 border: { 138 - borderColor: plum[7], 158 + borderColor: red[7], 139 159 borderStyle: "solid", 140 160 borderWidth: 1, 141 161 }, 142 162 borderInteractive: { 143 163 borderColor: { 144 - default: plum[7], 145 - ":hover": plum[8], 164 + default: red[7], 165 + ":hover": red[8], 146 166 }, 147 167 borderStyle: "solid", 148 168 borderWidth: 1, 169 + transitionDuration: "100ms", 170 + transitionProperty: "background-color, border-color", 171 + transitionTimingFunction: "ease-in-out", 149 172 }, 150 - bgSolid: { 151 - backgroundColor: plum[9], 173 + bgSolid: { backgroundColor: red[9] }, 174 + bgSolidDark: { backgroundColor: red[10] }, 175 + textDim: { color: red[11] }, 176 + text: { color: red[12] }, 177 + textContrast: { color: "white" }, 178 + 179 + bgGhost: { 180 + backgroundColor: { 181 + default: "transparent", 182 + ":hover:not(:has(* button:hover)):not(:disabled)": red[4], 183 + ":active:not(:disabled)": red[5], 184 + ":disabled": red[3], 185 + }, 186 + transitionDuration: "100ms", 187 + transitionProperty: "background-color, border-color", 188 + transitionTimingFunction: "ease-in-out", 152 189 }, 153 - bgSolidDark: { 154 - backgroundColor: plum[10], 190 + bgUi: { 191 + backgroundColor: { 192 + default: red[3], 193 + ":hover:not(:has(* button:hover)):not(:disabled)": red[4], 194 + ":active:not(:disabled)": red[5], 195 + ":disabled": red[3], 196 + }, 197 + transitionDuration: "100ms", 198 + transitionProperty: "background-color, border-color", 199 + transitionTimingFunction: "ease-in-out", 155 200 }, 156 - textDim: { 157 - color: plum[11], 201 + bgAction: { 202 + backgroundColor: { 203 + default: red[4], 204 + ":hover:not(:has(* button:hover)):not(:disabled)": red[5], 205 + ":active:not(:disabled)": red[6], 206 + ":disabled": red[3], 207 + }, 208 + transitionDuration: "100ms", 209 + transitionProperty: "background-color, border-color", 210 + transitionTimingFunction: "ease-in-out", 158 211 }, 159 - text: { 160 - color: plum[12], 212 + bgSolidAction: { 213 + backgroundColor: { 214 + default: red[9], 215 + ":hover:not(:has(* button:hover)):not(:disabled)": red[10], 216 + ":disabled": red[3], 217 + }, 218 + transitionDuration: "100ms", 219 + transitionProperty: "background-color, border-color", 220 + transitionTimingFunction: "ease-in-out", 161 221 }, 162 222 });
+7 -1
packages/hip-ui/src/components/theme/useButtonStyles.ts
··· 8 8 import { Size, ButtonVariant } from "../types"; 9 9 import { slate } from "./colors.stylex"; 10 10 import { radius } from "./radius.stylex"; 11 - import { gray, primary } from "./semantic-color.stylex"; 11 + import { critical, gray, primary } from "./semantic-color.stylex"; 12 12 import { shadow } from "./shadow.stylex"; 13 13 import { spacing } from "./spacing.stylex"; 14 14 import { ··· 169 169 gray.bgGhost, 170 170 gray.text, 171 171 styles.outline, 172 + styles.shadow, 173 + ], 174 + variant === "critical" && [ 175 + critical.bgSolidAction, 176 + critical.borderInteractive, 177 + critical.textContrast, 172 178 styles.shadow, 173 179 ], 174 180 size === "sm" && styles.small,
+3 -3
packages/hip-ui/src/components/toggle-button/index.tsx
··· 94 94 export interface ToggleButtonProps 95 95 extends Omit<AriaToggleButtonProps, "style" | "className" | "children"> { 96 96 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 97 - variant?: ButtonVariant; 97 + variant?: Exclude<ButtonVariant, "critical">; 98 98 size?: Size; 99 99 children?: React.ReactNode; 100 100 } ··· 113 113 buttonStyles, 114 114 styles[size], 115 115 isSelected ? styles[`${variant}Selected`] : undefined, 116 - style, 116 + style 117 117 ); 118 118 119 119 return ( ··· 130 130 <span key={`${child}-${index.toString()}`}>{child}</span> 131 131 ) : ( 132 132 child 133 - ), 133 + ) 134 134 )} 135 135 </AriaToggleButton> 136 136 );
+6 -1
packages/hip-ui/src/components/types.ts
··· 1 1 export type Size = "sm" | "md" | "lg"; 2 - export type ButtonVariant = "primary" | "secondary" | "tertiary" | "outline"; 2 + export type ButtonVariant = 3 + | "primary" 4 + | "secondary" 5 + | "tertiary" 6 + | "outline" 7 + | "critical";