diff --git a/packages/@react-spectrum/s2/src/ActionBar.tsx b/packages/@react-spectrum/s2/src/ActionBar.tsx index e7b65502b36..3bb7745b12f 100644 --- a/packages/@react-spectrum/s2/src/ActionBar.tsx +++ b/packages/@react-spectrum/s2/src/ActionBar.tsx @@ -12,6 +12,7 @@ import {ActionButtonGroup} from './ActionButtonGroup'; import {announce} from 'react-aria/private/live-announcer/LiveAnnouncer'; +import {banner, StyleProps} from './style-utils' with {type: 'macro'}; import {CloseButton} from './CloseButton'; import {ContextValue, SlotProps} from 'react-aria-components/slots'; import { @@ -31,7 +32,6 @@ import {DOMProps, DOMRef, DOMRefValue, Key} from '@react-types/shared'; import {FocusScope} from 'react-aria/FocusScope'; import intlMessages from '../intl/*.json'; import {lightDark, style} from '../style' with {type: 'macro'}; -import {StyleProps} from './style-utils' with {type: 'macro'}; import {useControlledState} from 'react-stately/useControlledState'; import {useDOMRef} from './useDOMRef'; import {useEnterAnimation, useExitAnimation} from 'react-aria/private/utils/animation'; @@ -42,6 +42,7 @@ import {useResizeObserver} from 'react-aria/private/utils/useResizeObserver'; import {useSpectrumContextProps} from './useSpectrumContextProps'; const actionBarStyles = style({ + ...banner(), borderRadius: 'lg', '--s2-container-bg': { type: 'backgroundColor', @@ -60,10 +61,7 @@ const actionBarStyles = style({ isEmphasized: 'transparent', forcedColors: 'ButtonBorder' }, - paddingX: 8, - paddingY: 12, display: 'flex', - gap: 16, alignItems: 'center', position: { isInContainer: 'absolute' @@ -194,7 +192,14 @@ const ActionBarInner = forwardRef(function ActionBarInner( {...keyboardProps} className={ (props.UNSAFE_className || '') + - actionBarStyles({isEmphasized, isInContainer: !!scrollRef, isEntering, isExiting}) + actionBarStyles({ + isEmphasized, + isInContainer: !!scrollRef, + isEntering, + isExiting, + orientation: 'horizontal', + density: 'compact' + }) } style={{ insetInlineEnd: `calc(var(--insetEnd) + ${scrollbarWidth}px)`, diff --git a/packages/@react-spectrum/s2/src/ActionButtonGroup.tsx b/packages/@react-spectrum/s2/src/ActionButtonGroup.tsx index 7530098a6d5..fa43e823ce5 100644 --- a/packages/@react-spectrum/s2/src/ActionButtonGroup.tsx +++ b/packages/@react-spectrum/s2/src/ActionButtonGroup.tsx @@ -15,6 +15,7 @@ import {ContextValue, SlotProps} from 'react-aria-components/slots'; import {createContext, ForwardedRef, forwardRef, ReactNode} from 'react'; import { getAllowedOverrides, + groupGap, StylesPropWithHeight, UnsafeStyles } from './style-utils' with {type: 'macro'}; @@ -70,20 +71,7 @@ export const actionGroupStyle = style( vertical: 'column' } }, - gap: { - density: { - compact: 2, - regular: { - size: { - XS: 4, - S: 4, - M: 8, - L: 8, - XL: 8 - } - } - } - } + gap: groupGap() }, getAllowedOverrides({height: true}) ); diff --git a/packages/@react-spectrum/s2/src/Breadcrumbs.tsx b/packages/@react-spectrum/s2/src/Breadcrumbs.tsx index 4c1c45e7829..ca0bf9d68e8 100644 --- a/packages/@react-spectrum/s2/src/Breadcrumbs.tsx +++ b/packages/@react-spectrum/s2/src/Breadcrumbs.tsx @@ -35,6 +35,7 @@ import { import {ContextValue, Provider} from 'react-aria-components/slots'; import { controlFont, + controlGap, controlSize, getAllowedOverrides, StyleProps @@ -112,13 +113,7 @@ const wrapper = style( flexGrow: 1, flexShrink: 0, flexBasis: 0, - gap: { - size: { - // TODO: why do these scale but other spacings don't? - M: size(6), // breadcrumbs-text-to-separator-medium - L: size(9) // breadcrumbs-text-to-separator-large - } - }, + gap: controlGap(), padding: 0, transition: 'default', marginTop: 0, diff --git a/packages/@react-spectrum/s2/src/ButtonGroup.tsx b/packages/@react-spectrum/s2/src/ButtonGroup.tsx index 07e86094ce2..7eb562b91a5 100644 --- a/packages/@react-spectrum/s2/src/ButtonGroup.tsx +++ b/packages/@react-spectrum/s2/src/ButtonGroup.tsx @@ -15,7 +15,7 @@ import {ContextValue, Provider, SlotProps} from 'react-aria-components/slots'; import {createContext, forwardRef, ReactNode, useCallback, useRef} from 'react'; import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared'; import {filterDOMProps} from 'react-aria/filterDOMProps'; -import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; +import {getAllowedOverrides, groupGap, StyleProps} from './style-utils' with {type: 'macro'}; import {style} from '../style' with {type: 'macro'}; import {useDOMRef} from './useDOMRef'; import {useLayoutEffect} from 'react-aria/private/utils/useLayoutEffect'; @@ -66,14 +66,7 @@ const buttongroup = style( display: 'inline-flex', position: 'relative', maxWidth: 'full', - gap: { - size: { - S: 8, - M: 12, - L: 12, - XL: 12 - } - }, + gap: groupGap(), flexDirection: { default: 'row', orientation: { diff --git a/packages/@react-spectrum/s2/src/Checkbox.tsx b/packages/@react-spectrum/s2/src/Checkbox.tsx index a324ee21473..dc2afdc5df2 100644 --- a/packages/@react-spectrum/s2/src/Checkbox.tsx +++ b/packages/@react-spectrum/s2/src/Checkbox.tsx @@ -24,6 +24,7 @@ import {ContextValue, useSlottedContext} from 'react-aria-components/slots'; import { controlBorderRadius, controlFont, + controlGap, controlSize, getAllowedOverrides, StyleProps @@ -89,7 +90,7 @@ const field = style( default: ['max-content', '1fr'], isNoVisibleLabel: ['max-content'] }, - columnGap: 'text-to-control', + columnGap: controlGap(), alignContent: 'start', width: { default: 'fit', diff --git a/packages/@react-spectrum/s2/src/CheckboxGroup.tsx b/packages/@react-spectrum/s2/src/CheckboxGroup.tsx index 071c1f6ac0e..210495a3162 100644 --- a/packages/@react-spectrum/s2/src/CheckboxGroup.tsx +++ b/packages/@react-spectrum/s2/src/CheckboxGroup.tsx @@ -25,7 +25,7 @@ import { Orientation, SpectrumLabelableProps } from '@react-types/shared'; -import {field, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; +import {field, getAllowedOverrides, groupGap, StyleProps} from './style-utils' with {type: 'macro'}; import {FieldLabel, HelpText} from './Field'; import {FormContext, useFormProps} from './Form'; import {style} from '../style' with {type: 'macro'}; @@ -104,12 +104,7 @@ export const CheckboxGroup = forwardRef(function CheckboxGroup( UNSAFE_className + style( { - ...field(), - // Double the usual gap because of the internal padding within checkbox that spectrum has. - '--field-gap': { - type: 'rowGap', - value: 'calc(var(--field-height) - 1lh)' - } + ...field() }, getAllowedOverrides() )( @@ -146,7 +141,7 @@ export const CheckboxGroup = forwardRef(function CheckboxGroup( } }, lineHeight: 'ui', - rowGap: '--field-gap', + rowGap: groupGap().density.spacious, // Spectrum uses a fixed spacing value for horizontal, // but the gap changes depending on t-shirt size in vertical. columnGap: 16, diff --git a/packages/@react-spectrum/s2/src/ColorSlider.tsx b/packages/@react-spectrum/s2/src/ColorSlider.tsx index b1031a87424..b8b09599625 100644 --- a/packages/@react-spectrum/s2/src/ColorSlider.tsx +++ b/packages/@react-spectrum/s2/src/ColorSlider.tsx @@ -17,7 +17,12 @@ import { import {ColorHandle} from './ColorHandle'; import {ContextValue} from 'react-aria-components/slots'; -import {controlFont, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; +import { + controlFont, + controlGap, + getAllowedOverrides, + StyleProps +} from './style-utils' with {type: 'macro'}; import {createContext, forwardRef, useRef} from 'react'; import { DOMRef, @@ -91,7 +96,7 @@ export const ColorSlider = forwardRef(function ColorSlider( }, gridTemplateColumns: ['1fr', 'auto'], gridTemplateAreas: ['label output', 'track track'], - rowGap: 4 + rowGap: controlGap().size.S }, getAllowedOverrides() )(renderProps, styles) diff --git a/packages/@react-spectrum/s2/src/ColorSwatchPicker.tsx b/packages/@react-spectrum/s2/src/ColorSwatchPicker.tsx index cf96d46c72d..5829193fe57 100644 --- a/packages/@react-spectrum/s2/src/ColorSwatchPicker.tsx +++ b/packages/@react-spectrum/s2/src/ColorSwatchPicker.tsx @@ -20,8 +20,8 @@ import {ColorSwatchProps, InternalColorSwatchContext} from './ColorSwatch'; import {ContextValue, SlotProps} from 'react-aria-components/slots'; import {createContext, forwardRef, ReactElement, ReactNode} from 'react'; import {DOMRef, DOMRefValue, ValueBase} from '@react-types/shared'; -import {focusRing, space, style} from '../style' with {type: 'macro'}; -import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; +import {focusRing, style} from '../style' with {type: 'macro'}; +import {getAllowedOverrides, groupGap, StyleProps} from './style-utils' with {type: 'macro'}; import {useDOMRef} from './useDOMRef'; import {useSpectrumContextProps} from './useSpectrumContextProps'; @@ -81,9 +81,9 @@ export const ColorSwatchPicker = forwardRef(function ColorSwatchPicker( flexWrap: 'wrap', gap: { density: { - compact: space(2), - regular: 4, - spacious: space(6) + compact: groupGap().density.compact, + regular: groupGap().density.default.size.XS, + spacious: groupGap().density.default.size.M } } }, diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index 231664bc8ed..e3dab81e723 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -48,6 +48,7 @@ import { control, controlBorderRadius, controlFont, + controlPadding, controlSize, field, fieldInput, @@ -292,10 +293,10 @@ export let listboxItem = style( gridTemplateAreas: ['. checkmark icon label .', '. . . description .'], gridTemplateColumns: { size: { - S: [edgeToText(24), 'auto', 'auto', 'minmax(0, 1fr)', edgeToText(24)], - M: [edgeToText(32), 'auto', 'auto', 'minmax(0, 1fr)', edgeToText(32)], - L: [edgeToText(40), 'auto', 'auto', 'minmax(0, 1fr)', edgeToText(40)], - XL: [edgeToText(48), 'auto', 'auto', 'minmax(0, 1fr)', edgeToText(48)] + S: [controlPadding().size.S, 'auto', 'auto', 'minmax(0, 1fr)', controlPadding().size.S], + M: [controlPadding().size.M, 'auto', 'auto', 'minmax(0, 1fr)', controlPadding().size.M], + L: [controlPadding().size.L, 'auto', 'auto', 'minmax(0, 1fr)', controlPadding().size.L], + XL: [controlPadding().size.XL, 'auto', 'auto', 'minmax(0, 1fr)', controlPadding().size.XL] } }, gridTemplateRows: { @@ -324,26 +325,12 @@ export let listboxHeader = style<{size?: 'S' | 'M' | 'L' | 'XL'}>({ boxSizing: 'border-box', minHeight: controlSize(), paddingY: centerPadding(), - marginX: { - size: { - S: `[${edgeToText(24)}]`, - M: `[${edgeToText(32)}]`, - L: `[${edgeToText(40)}]`, - XL: `[${edgeToText(48)}]` - } - } + marginX: controlPadding() }); const separatorWrapper = style({ display: 'flex', - marginX: { - size: { - S: `[${edgeToText(24)}]`, - M: `[${edgeToText(32)}]`, - L: `[${edgeToText(40)}]`, - XL: `[${edgeToText(48)}]` - } - }, + marginX: controlPadding(), height: 12, alignItems: 'center' }); diff --git a/packages/@react-spectrum/s2/src/ContextualHelp.tsx b/packages/@react-spectrum/s2/src/ContextualHelp.tsx index 6db9e3e1012..aaa832d1fb6 100644 --- a/packages/@react-spectrum/s2/src/ContextualHelp.tsx +++ b/packages/@react-spectrum/s2/src/ContextualHelp.tsx @@ -1,5 +1,6 @@ import {ActionButton} from './ActionButton'; import {AriaLabelingProps, DOMProps, FocusableRef, FocusableRefValue} from '@react-types/shared'; +import {containerGap, containerPadding, StyleProps} from './style-utils' with {type: 'macro'}; import { ContentContext, FooterContext, @@ -18,8 +19,7 @@ import {mergeProps} from 'react-aria/mergeProps'; import {mergeStyles} from '../style/runtime'; import {Placement} from 'react-aria-components/Popover'; import {Popover, PopoverDialogProps} from './Popover'; -import {space, style} from '../style' with {type: 'macro'}; -import {StyleProps} from './style-utils' with {type: 'macro'}; +import {style} from '../style' with {type: 'macro'}; import {TextContext} from 'react-aria-components/Text'; import {useId} from 'react-aria/useId'; import {useLabels} from 'react-aria/private/utils/useLabels'; @@ -36,7 +36,7 @@ export interface ContextualHelpPopoverProps extends Omit diff --git a/packages/@react-spectrum/s2/src/DatePicker.tsx b/packages/@react-spectrum/s2/src/DatePicker.tsx index 7b457036761..2542828f0ba 100644 --- a/packages/@react-spectrum/s2/src/DatePicker.tsx +++ b/packages/@react-spectrum/s2/src/DatePicker.tsx @@ -21,7 +21,11 @@ import {Calendar, CalendarProps} from './Calendar'; import CalendarIcon from '../s2wf-icons/S2_Icon_Calendar_20_N.svg'; import {ContextValue, Provider} from 'react-aria-components/slots'; import { + accessoryGap, + containerPadding, controlBorderRadius, + controlGap, + controlPadding, field, fieldInput, getAllowedOverrides, @@ -116,7 +120,7 @@ const inputButton = style @@ -296,12 +293,11 @@ export function CalendarPopover(
diff --git a/packages/@react-spectrum/s2/src/DateRangePicker.tsx b/packages/@react-spectrum/s2/src/DateRangePicker.tsx index 53f48264260..303ad73aad7 100644 --- a/packages/@react-spectrum/s2/src/DateRangePicker.tsx +++ b/packages/@react-spectrum/s2/src/DateRangePicker.tsx @@ -20,6 +20,8 @@ import {ContextValue} from 'react-aria-components/slots'; import {createContext, forwardRef, ReactElement, Ref, useContext, useState} from 'react'; import {DateInput, DateInputContainer, InvalidIndicator} from './DateField'; import { + accessoryGap, + controlPadding, field, fieldInput, getAllowedOverrides, @@ -165,8 +167,8 @@ export const DateRangePicker = /*#__PURE__*/ (forwardRef as forwardRefType)( styles={style({ ...fieldInput(), textWrap: 'nowrap', - paddingStart: 'edge-to-text', - paddingEnd: 4 + paddingStart: controlPadding(), + paddingEnd: accessoryGap() })({size})}> diff --git a/packages/@react-spectrum/s2/src/Disclosure.tsx b/packages/@react-spectrum/s2/src/Disclosure.tsx index 1ea0a0baa0c..f765fc0739d 100644 --- a/packages/@react-spectrum/s2/src/Disclosure.tsx +++ b/packages/@react-spectrum/s2/src/Disclosure.tsx @@ -19,20 +19,21 @@ import { forwardRefType, GlobalDOMAttributes } from '@react-types/shared'; -import { - baseColor, - centerPadding, - focusRing, - lightDark, - space, - style -} from '../style' with {type: 'macro'}; +import {baseColor, centerPadding, focusRing, lightDark, style} from '../style' with {type: 'macro'}; import {Button} from 'react-aria-components/Button'; import {CenterBaseline} from './CenterBaseline'; import Chevron from '../ui-icons/Chevron'; import {ContextValue, Provider, useSlottedContext} from 'react-aria-components/slots'; +import { + controlPadding, + getAllowedOverrides, + StyleProps, + StylesPropWithFont, + UnsafeStyles, + verticalPadding +} from './style-utils' with {type: 'macro'}; import { DisclosureStateContext, Disclosure as RACDisclosure, @@ -41,12 +42,6 @@ import { DisclosureProps as RACDisclosureProps } from 'react-aria-components/Disclosure'; import {filterDOMProps} from 'react-aria/filterDOMProps'; -import { - getAllowedOverrides, - StyleProps, - StylesPropWithFont, - UnsafeStyles -} from './style-utils' with {type: 'macro'}; import {Heading} from 'react-aria-components/Heading'; import React, {createContext, forwardRef, ReactNode, useContext} from 'react'; import {useDOMRef} from './useDOMRef'; @@ -189,9 +184,9 @@ const buttonStyles = style( display: 'flex', flexGrow: 1, alignItems: 'baseline', - paddingX: 'calc(self(minHeight) * 3/8 - 1px)', + paddingX: controlPadding(), paddingY: centerPadding(), - gap: 'calc(self(minHeight) * 3/8 - 1px)', + gap: controlPadding(), minHeight: { // compact is equivalent to 'control', but other densities have more padding. size: { @@ -282,7 +277,7 @@ function DisclosureHeaderWithForwardRef(props: DisclosureHeaderProps, ref: DOMRe
{children} @@ -359,16 +354,9 @@ const panelStyles = style({ }); const panelInner = style({ - paddingTop: 8, - paddingBottom: 16, - paddingX: { - size: { - S: 8, - M: space(9), - L: 12, - XL: space(15) - } - } + paddingTop: verticalPadding('L'), + paddingBottom: verticalPadding('2XL'), + paddingX: controlPadding() }); /** diff --git a/packages/@react-spectrum/s2/src/DragPreview.tsx b/packages/@react-spectrum/s2/src/DragPreview.tsx index 1836f505404..7b73a43cc76 100644 --- a/packages/@react-spectrum/s2/src/DragPreview.tsx +++ b/packages/@react-spectrum/s2/src/DragPreview.tsx @@ -12,7 +12,7 @@ import {baseColor, fontRelative, style} from '../style' with {type: 'macro'}; import {centerBaseline} from './CenterBaseline'; -import {controlFont} from './style-utils' with {type: 'macro'}; +import {controlFont, controlGap} from './style-utils' with {type: 'macro'}; import {DEFAULT_SLOT, Provider} from 'react-aria-components/slots'; import {DragItem} from '@react-types/shared'; import {IconContext} from './Icon'; @@ -64,7 +64,7 @@ export let iconCenterWrapper = style({ export let icon = style({ display: 'block', size: fontRelative(20), - marginEnd: 'text-to-visual', + marginEnd: controlGap().size.M, '--iconPrimary': { type: 'fill', value: 'currentColor' diff --git a/packages/@react-spectrum/s2/src/DropZone.tsx b/packages/@react-spectrum/s2/src/DropZone.tsx index d080e637af4..a7f047ddffa 100644 --- a/packages/@react-spectrum/s2/src/DropZone.tsx +++ b/packages/@react-spectrum/s2/src/DropZone.tsx @@ -10,21 +10,20 @@ * governing permissions and limitations under the License. */ +import { + containerPadding, + getAllowedOverrides, + StylesPropWithHeight, + UnsafeStyles +} from './style-utils' with {type: 'macro'}; import {ContextValue} from 'react-aria-components/slots'; - import {createContext, forwardRef, ReactNode} from 'react'; - import {DOMProps, DOMRef, DOMRefValue, GlobalDOMAttributes} from '@react-types/shared'; import { DropZoneRenderProps, DropZone as RACDropZone, DropZoneProps as RACDropZoneProps } from 'react-aria-components/DropZone'; -import { - getAllowedOverrides, - StylesPropWithHeight, - UnsafeStyles -} from './style-utils' with {type: 'macro'}; import {IllustratedMessageContext} from './IllustratedMessage'; import intlMessages from '../intl/*.json'; import {style} from '../style' with {type: 'macro'}; @@ -94,7 +93,7 @@ const dropzone = style( isFocusVisible: 'blue-800' }, borderRadius: 'lg', - padding: 24, + padding: containerPadding().size.XL, boxSizing: 'border-box' }, getAllowedOverrides({height: true}) diff --git a/packages/@react-spectrum/s2/src/Form.tsx b/packages/@react-spectrum/s2/src/Form.tsx index 9c10c0b776f..e70f87347b2 100644 --- a/packages/@react-spectrum/s2/src/Form.tsx +++ b/packages/@react-spectrum/s2/src/Form.tsx @@ -10,9 +10,9 @@ * governing permissions and limitations under the License. */ +import {controlGap, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import {createContext, forwardRef, ReactNode, useContext, useMemo} from 'react'; import {DOMRef, GlobalDOMAttributes, SpectrumLabelableProps} from '@react-types/shared'; -import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import {Form as RACForm, FormProps as RACFormProps} from 'react-aria-components/Form'; import {style} from '../style' with {type: 'macro'}; import {useDOMRef} from './useDOMRef'; @@ -108,6 +108,7 @@ export const Form = /*#__PURE__*/ forwardRef(function Form( } }, // TODO: confirm when we have tokens + // UPDATE: still no tokens rowGap: { size: { XS: 16, @@ -117,7 +118,7 @@ export const Form = /*#__PURE__*/ forwardRef(function Form( XL: 40 } }, - columnGap: 'text-to-control' + columnGap: controlGap() }, getAllowedOverrides() )({labelPosition, size}, props.styles) diff --git a/packages/@react-spectrum/s2/src/InlineAlert.tsx b/packages/@react-spectrum/s2/src/InlineAlert.tsx index b45e81af0df..92bb161c7d0 100644 --- a/packages/@react-spectrum/s2/src/InlineAlert.tsx +++ b/packages/@react-spectrum/s2/src/InlineAlert.tsx @@ -11,6 +11,7 @@ */ import AlertTriangle from '../s2wf-icons/S2_Icon_AlertTriangle_20_N.svg'; +import {banner, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import CheckmarkCircle from '../s2wf-icons/S2_Icon_CheckmarkCircle_20_N.svg'; import {ComponentType, createContext, forwardRef, ReactNode, useEffect, useRef} from 'react'; import {ContentContext, HeadingContext} from './Content'; @@ -18,7 +19,6 @@ import {ContextValue, Provider, SlotProps} from 'react-aria-components/slots'; import {DOMProps, DOMRef, DOMRefValue} from '@react-types/shared'; import {filterDOMProps} from 'react-aria/filterDOMProps'; import {focusRing, style} from '../style' with {type: 'macro'}; -import {getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import {IconContext} from './Icon'; import InfoCircle from '../s2wf-icons/S2_Icon_InfoCircle_20_N.svg'; import intlMessages from '../intl/*.json'; @@ -63,7 +63,7 @@ const inlineAlert = style( display: 'inline-block', position: 'relative', boxSizing: 'border-box', - padding: 24, + padding: banner().paddingX.density.default, // TODO design is this correct? why not follow other banners? borderRadius: 'lg', borderStyle: 'solid', borderWidth: 2, diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index 3e0664e2593..fcd95351344 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -41,6 +41,7 @@ import {ContextValue, DEFAULT_SLOT, Provider} from 'react-aria-components/slots' import { control, controlFont, + controlPadding, controlSize, getAllowedOverrides, StyleProps @@ -127,10 +128,46 @@ export const MenuContext = const menuItemGrid = { size: { - S: [edgeToText(24), 'auto', 'auto', 'minmax(0, 1fr)', 'auto', 'auto', 'auto', edgeToText(24)], - M: [edgeToText(32), 'auto', 'auto', 'minmax(0, 1fr)', 'auto', 'auto', 'auto', edgeToText(32)], - L: [edgeToText(40), 'auto', 'auto', 'minmax(0, 1fr)', 'auto', 'auto', 'auto', edgeToText(40)], - XL: [edgeToText(48), 'auto', 'auto', 'minmax(0, 1fr)', 'auto', 'auto', 'auto', edgeToText(48)] + S: [ + controlPadding().size.S, + 'auto', + 'auto', + 'minmax(0, 1fr)', + 'auto', + 'auto', + 'auto', + controlPadding().size.S + ], + M: [ + controlPadding().size.M, + 'auto', + 'auto', + 'minmax(0, 1fr)', + 'auto', + 'auto', + 'auto', + controlPadding().size.M + ], + L: [ + controlPadding().size.L, + 'auto', + 'auto', + 'minmax(0, 1fr)', + 'auto', + 'auto', + 'auto', + controlPadding().size.L + ], + XL: [ + controlPadding().size.XL, + 'auto', + 'auto', + 'minmax(0, 1fr)', + 'auto', + 'auto', + 'auto', + controlPadding().size.XL + ] } } as const; diff --git a/packages/@react-spectrum/s2/src/NumberField.tsx b/packages/@react-spectrum/s2/src/NumberField.tsx index 79fba8077a1..81bd40bc44b 100644 --- a/packages/@react-spectrum/s2/src/NumberField.tsx +++ b/packages/@react-spectrum/s2/src/NumberField.tsx @@ -10,6 +10,16 @@ * governing permissions and limitations under the License. */ +import { + accessoryGap, + controlBorderRadius, + controlGap, + controlPadding, + field, + fieldInput, + getAllowedOverrides, + StyleProps +} from './style-utils' with {type: 'macro'}; import Add from '../ui-icons/Add'; import { ButtonProps as AriaButtonProps, @@ -20,15 +30,8 @@ import { NumberField as AriaNumberField, NumberFieldProps as AriaNumberFieldProps } from 'react-aria-components/NumberField'; -import {baseColor, space, style} from '../style' with {type: 'macro'}; +import {baseColor, style} from '../style' with {type: 'macro'}; import {ContextValue, useContextProps} from 'react-aria-components/slots'; -import { - controlBorderRadius, - field, - fieldInput, - getAllowedOverrides, - StyleProps -} from './style-utils' with {type: 'macro'}; import { createContext, CSSProperties, @@ -109,7 +112,7 @@ const inputButton = style< }, height: 'auto', marginStart: { - default: 'text-to-control', + default: controlGap(), type: { increment: 0 } @@ -151,22 +154,8 @@ const iconStyles = style({ const stepperContainerStyles = style({ display: 'flex', flexDirection: 'row', - gap: { - size: { - S: 8, - M: 4, - L: 8, - XL: 8 - } - }, - marginEnd: { - size: { - S: 2, - M: 4, - L: space(6), - XL: space(6) - } - } + gap: accessoryGap(), + marginEnd: accessoryGap() }); /** @@ -249,10 +238,10 @@ export const NumberField = forwardRef(function NumberField( size={size} styles={style({ ...fieldInput(), - paddingStart: 'edge-to-text', + paddingStart: controlPadding(), paddingEnd: { default: 0, - isStepperHidden: 'edge-to-text' + isStepperHidden: controlPadding() } })({size, isStepperHidden: hideStepper})}> diff --git a/packages/@react-spectrum/s2/src/Popover.tsx b/packages/@react-spectrum/s2/src/Popover.tsx index 9fe8b4c1439..bf9a60116fe 100644 --- a/packages/@react-spectrum/s2/src/Popover.tsx +++ b/packages/@react-spectrum/s2/src/Popover.tsx @@ -317,7 +317,7 @@ const innerDivStyle = style( { padding: { padding: { - default: 8, + default: 'popover-padding', none: 0 } }, diff --git a/packages/@react-spectrum/s2/src/RadioGroup.tsx b/packages/@react-spectrum/s2/src/RadioGroup.tsx index 73b521784d8..204405c18ab 100644 --- a/packages/@react-spectrum/s2/src/RadioGroup.tsx +++ b/packages/@react-spectrum/s2/src/RadioGroup.tsx @@ -23,6 +23,7 @@ import {CenterBaseline} from './CenterBaseline'; import {ContextValue} from 'react-aria-components/slots'; import { controlFont, + controlGap, controlSize, field, getAllowedOverrides, @@ -232,7 +233,7 @@ const radioField = style( default: ['max-content', '1fr'], isNoVisibleLabel: ['max-content'] }, - columnGap: 'text-to-control', + columnGap: controlGap(), alignContent: 'start', font: controlFont(), '--field-height': { diff --git a/packages/@react-spectrum/s2/src/style-utils.ts b/packages/@react-spectrum/s2/src/style-utils.ts index 1c6cfff83bd..f8fb0120cdb 100644 --- a/packages/@react-spectrum/s2/src/style-utils.ts +++ b/packages/@react-spectrum/s2/src/style-utils.ts @@ -89,7 +89,7 @@ export const field = () => type: 'rowGap', value: centerPadding('var(--field-height)') }, - columnGap: 12, + columnGap: 12, // TODO: what token to use here? controlGap would be 6px for medium disableTapHighlight: true }) as const; @@ -239,6 +239,155 @@ const controlSizeS = { } } as const; +// Base Gap - dynamic +export const controlGap = () => + ({ + default: 'base-gap-medium', + size: { + XS: 'base-gap-extra-small', + S: 'base-gap-small', + M: 'base-gap-medium', + L: 'base-gap-large', + XL: 'base-gap-extra-large' + } + }) as const; + +// Accessory Gap - dynamic +export const accessoryGap = () => + ({ + default: 'accessory-gap-medium', + size: { + XS: 'accessory-gap-extra-small', + S: 'accessory-gap-small', + M: 'accessory-gap-medium', + L: 'accessory-gap-large', + XL: 'accessory-gap-extra-large', + '2XL': 'accessory-gap-2x-large' + } + }) as const; + +// Group Gap - dynamic +export const groupGap = () => + ({ + default: 'group-gap-medium', + size: { + default: 'group-gap-medium', + XS: 'group-gap-extra-small', + S: 'group-gap-small', + M: 'group-gap-medium', + L: 'group-gap-large', + XL: 'group-gap-extra-large' + }, + density: { + default: { + size: { + default: 'group-gap-medium', + XS: 'group-gap-extra-small', + S: 'group-gap-small', + M: 'group-gap-medium', + L: 'group-gap-large', + XL: 'group-gap-extra-large' + } + }, + compact: 'group-gap-compact', + spacious: { + size: { + XS: 'group-gap-extra-small-spacious', + S: 'group-gap-small-spacious', + M: 'group-gap-medium-spacious', + L: 'group-gap-large-spacious', + XL: 'group-gap-extra-large-spacious' + } + } + } + }) as const; + +export const containerGap = () => + ({ + default: 'container-gap-medium', + size: { + '2XS': 'container-gap-2x-extra-small', + XS: 'container-gap-extra-small', + S: 'container-gap-small', + M: 'container-gap-medium', + L: 'container-gap-large', + XL: 'container-gap-extra-large', + '2XL': 'container-gap-2x-large' + } + }) as const; + +// Base Horizontal Padding - dynamic +export const controlPadding = () => + ({ + default: 'base-padding-horizontal-medium', + size: { + XS: 'base-padding-horizontal-extra-small', + S: 'base-padding-horizontal-small', + M: 'base-padding-horizontal-medium', + L: 'base-padding-horizontal-large', + XL: 'base-padding-horizontal-extra-large', + '2XL': 'base-padding-horizontal-2x-large' + } + }) as const; + +export const containerPadding = () => + ({ + default: 'container-padding', + size: { + '2XS': 'container-padding-2x-extra-small', + XS: 'container-padding-extra-small', + S: 'container-padding-small', + M: 'container-padding-medium', + L: 'container-padding-large', + XL: 'container-padding-extra-large', + '2XL': 'container-padding-2x-large', + '3XL': 'container-padding-3x-large' + } + }) as const; + +const verticalPaddingTokens = { + XS: 'base-padding-vertical-extra-small', + S: 'base-padding-vertical-small', + M: 'base-padding-vertical-medium', + L: 'base-padding-vertical-large', + XL: 'base-padding-vertical-extra-large', + '2XL': 'base-padding-vertical-2x-large' +} as const; + +const horizontalPaddingTokens = { + XS: 'base-padding-horizontal-extra-small', + S: 'base-padding-horizontal-small', + M: 'base-padding-horizontal-medium', + L: 'base-padding-horizontal-large', + XL: 'base-padding-horizontal-extra-large', + '2XL': 'base-padding-horizontal-2x-large' +} as const; + +// Base Vertical Padding - static +export const verticalPadding = (size: keyof typeof verticalPaddingTokens = 'M') => + verticalPaddingTokens[size]; + +// Base Horizontal Padding - static +export const horizontalPadding = (size: keyof typeof horizontalPaddingTokens = 'M') => + horizontalPaddingTokens[size]; + +export const banner = () => + ({ + paddingX: { + density: { + default: 'banner-padding-horizontal', + compact: 'banner-padding-horizontal-compact' + } + }, + paddingY: 'banner-padding-vertical', + gap: { + orientation: { + horizontal: 'banner-gap-horizontal', + vertical: 'banner-gap-vertical' + } + } + }) as const; + // This generates the border radius for t-shirt sizes using the // Major Second logarithmic scale. export const controlBorderRadius = (size: 'default' | 'sm' = 'default') => @@ -277,8 +426,11 @@ interface ControlResult { height?: ReturnType; display?: 'flex'; alignItems?: 'center' | {default: 'baseline'; [iconOnly]: 'center'}; - columnGap?: 'text-to-visual'; - paddingX?: 'pill' | 'edge-to-text' | {default: 'pill' | 'edge-to-text'; [iconOnly]: 0}; + columnGap?: ReturnType; + paddingX?: + | 'pill' + | ReturnType + | {default: 'pill' | ReturnType; [iconOnly]: 0}; paddingY?: 0 | `[${string}]`; } @@ -289,7 +441,7 @@ const iconOnly = ':has([slot=icon]):not(:has([data-rsp-slot=text]))'; * The text can optionally wrap, aligning the icon with the first line of text. */ export function control(options: ControlOptions): ControlResult { - let paddingX = options.shape === 'pill' ? ('pill' as const) : ('edge-to-text' as const); + let paddingX = controlPadding(); let result: ControlResult = { font: controlFont(), display: 'flex', @@ -306,7 +458,7 @@ export function control(options: ControlOptions): ControlResult { } if (options.icon) { - result.columnGap = 'text-to-visual'; + result.columnGap = controlGap(); result.paddingX = { default: paddingX, [iconOnly]: 0 diff --git a/packages/@react-spectrum/s2/style/spectrum-theme.ts b/packages/@react-spectrum/s2/style/spectrum-theme.ts index 3690d893241..7fb7ef8df33 100644 --- a/packages/@react-spectrum/s2/style/spectrum-theme.ts +++ b/packages/@react-spectrum/s2/style/spectrum-theme.ts @@ -32,10 +32,11 @@ import { autoStaticColor, ColorRef, colorScale, - ColorToken, colorToken, + ColorToken, fontSizeToken, generateOverlayColorScale, + getSetToken, getToken, shadowToken, simpleColorScale, @@ -427,24 +428,112 @@ export function space(this: MacroContext | void, px: number): string { const relativeSpacing = { // font-size relative values + /** + * @deprecated Use `controlGap()` from `style-utils`, which selects a `base-gap-*` tier per + * component size. + */ 'text-to-control': fontRelative(10), + /** + * @deprecated Use `controlGap()` from `style-utils`, which selects a `base-gap-*` tier per + * component size. + */ 'text-to-visual': { default: fontRelative(6), // -> 5px, 5px, 6px, 7px, 8px touch: fontRelative(8, 17) // -> 6px, 7px, 8px, 9px, 10px, should be 7px, 7px, 8px, 9px, 11px }, // height relative values + /** + * @deprecated Use `controlPadding()` from `style-utils`, which selects a + * `base-padding-horizontal-*` tier per component size. + */ 'edge-to-text': 'calc(self(height, self(minHeight)) * 3 / 8)', pill: 'calc(self(height, self(minHeight)) / 2)' } as const; +// Base gap/padding tiers, selected per component size at the call site +// (see `controlGap`/`controlPadding` in `style-utils`). The token holds the +// desktop value; the `--s2-scale` variable produces the touch value. +const sizeSpacing = { + 'base-gap-extra-small': size(parseFloat(getToken('base-gap-extra-small'))), + 'base-gap-small': size(parseFloat(getToken('base-gap-small'))), + 'base-gap-medium': size(parseFloat(getToken('base-gap-medium'))), + 'base-gap-large': size(parseFloat(getToken('base-gap-large'))), + 'base-gap-extra-large': size(parseFloat(getToken('base-gap-extra-large'))), + 'base-padding-horizontal-extra-small': size( + parseFloat(getSetToken('base-padding-horizontal-extra-small')) + ), + 'accessory-gap-extra-small': size(parseFloat(getToken('accessory-gap-extra-small'))), + 'accessory-gap-small': size(parseFloat(getToken('accessory-gap-small'))), + 'accessory-gap-medium': size(parseFloat(getToken('accessory-gap-medium'))), + 'accessory-gap-large': size(parseFloat(getToken('accessory-gap-large'))), + 'accessory-gap-extra-large': size(parseFloat(getToken('accessory-gap-extra-large'))), + 'accessory-gap-2x-large': size(parseFloat(getToken('accessory-gap-2x-large'))), + 'group-gap-extra-small': size(parseFloat(getToken('group-gap-extra-small'))), + 'group-gap-small': size(parseFloat(getToken('group-gap-small'))), + 'group-gap-medium': size(parseFloat(getToken('group-gap-medium'))), + 'group-gap-large': size(parseFloat(getToken('group-gap-large'))), + 'group-gap-extra-large': size(parseFloat(getToken('group-gap-extra-large'))), + 'group-gap-compact': size(parseFloat(getToken('group-gap-compact'))), + 'group-gap-extra-small-spacious': size(parseFloat(getToken('group-gap-extra-small-spacious'))), + 'group-gap-small-spacious': size(parseFloat(getToken('group-gap-small-spacious'))), + 'group-gap-medium-spacious': size(parseFloat(getToken('group-gap-medium-spacious'))), + 'group-gap-large-spacious': size(parseFloat(getToken('group-gap-large-spacious'))), + 'group-gap-extra-large-spacious': size(parseFloat(getToken('group-gap-extra-large-spacious'))), + 'container-gap-2x-small': size(parseFloat(getToken('container-gap-2x-small'))), + 'container-gap-extra-small': size(parseFloat(getToken('container-gap-extra-small'))), + 'container-gap-small': size(parseFloat(getToken('container-gap-small'))), + 'container-gap-medium': size(parseFloat(getToken('container-gap-medium'))), + 'container-gap-large': size(parseFloat(getToken('container-gap-large'))), + 'container-gap-extra-large': size(parseFloat(getToken('container-gap-extra-large'))), + 'container-gap-2x-large': size(parseFloat(getToken('container-gap-2x-large'))), + 'base-padding-horizontal-small': size(parseFloat(getSetToken('base-padding-horizontal-small'))), + 'base-padding-horizontal-medium': size(parseFloat(getSetToken('base-padding-horizontal-medium'))), + 'base-padding-horizontal-large': size(parseFloat(getSetToken('base-padding-horizontal-large'))), + 'base-padding-horizontal-extra-large': size( + parseFloat(getSetToken('base-padding-horizontal-extra-large')) + ), + 'base-padding-horizontal-2x-large': size( + parseFloat(getSetToken('base-padding-horizontal-2x-large')) + ), + 'base-padding-vertical-extra-small': size( + parseFloat(getToken('base-padding-vertical-extra-small')) + ), + 'base-padding-vertical-small': size(parseFloat(getToken('base-padding-vertical-small'))), + 'base-padding-vertical-medium': size(parseFloat(getToken('base-padding-vertical-medium'))), + 'base-padding-vertical-large': size(parseFloat(getToken('base-padding-vertical-large'))), + 'base-padding-vertical-extra-large': size( + parseFloat(getToken('base-padding-vertical-extra-large')) + ), + 'base-padding-vertical-2x-large': size(parseFloat(getToken('base-padding-vertical-2x-large'))), + 'banner-gap-horizontal': size(parseFloat(getToken('banner-gap-horizontal'))), + 'banner-gap-vertical': size(parseFloat(getToken('banner-gap-vertical'))), + 'banner-padding-horizontal': size(parseFloat(getToken('banner-padding-horizontal'))), + 'banner-padding-horizontal-compact': size( + parseFloat(getToken('banner-padding-horizontal-compact')) + ), + 'banner-padding-vertical': size(parseFloat(getToken('banner-padding-vertical'))), + 'container-padding-2x-small': size(parseFloat(getToken('container-padding-2x-small'))), + 'container-padding-extra-small': size(parseFloat(getToken('container-padding-extra-small'))), + 'container-padding-small': size(parseFloat(getToken('container-padding-small'))), + 'container-padding-medium': size(parseFloat(getToken('container-padding-medium'))), + 'container-padding-large': size(parseFloat(getToken('container-padding-large'))), + 'container-padding-extra-large': size(parseFloat(getToken('container-padding-extra-large'))), + 'container-padding-2x-large': size(parseFloat(getToken('container-padding-2x-large'))), + 'container-padding-3x-large': size(parseFloat(getToken('container-padding-3x-large'))), + 'popover-padding': size(parseFloat(getToken('popover-padding'))), + 'popover-gap': size(parseFloat(getToken('popover-gap'))) +} as const; + const spacing = { ...baseSpacing, - ...relativeSpacing + ...relativeSpacing, + ...sizeSpacing }; const padding = { ...basePadding, - ...relativeSpacing + ...relativeSpacing, + ...sizeSpacing }; /** diff --git a/packages/@react-spectrum/s2/style/tokens.ts b/packages/@react-spectrum/s2/style/tokens.ts index 8f04d400e25..7f4518090dd 100644 --- a/packages/@react-spectrum/s2/style/tokens.ts +++ b/packages/@react-spectrum/s2/style/tokens.ts @@ -27,6 +27,14 @@ export function getToken(name: TokenName): string { return (tokens[name] as any).value; } +// Some dimension tokens store their value under a responsive set (e.g. desktop/mobile) +// rather than a top-level `value`. In S2 the touch scale is applied via the `--s2-scale` +// CSS variable, so we read the desktop value and let that variable produce the touch value. +export function getSetToken(name: TokenName, set: 'desktop' | 'mobile' = 'desktop'): string { + let token = tokens[name] as any; + return token.sets ? token.sets[set].value : token.value; +} + export interface ColorToken { type: 'color'; light: string;