Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/@react-spectrum/s2/src/CoachMark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
import {ButtonContext} from './Button';
import {Card} from './Card';
import {CheckboxContext} from './Checkbox';
import {colorScheme, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
import {ColorSchemeContext} from './Provider';
import {ContentContext, FooterContext, KeyboardContext, TextContext} from './Content';
import {
Expand All @@ -40,6 +39,7 @@ import {
} from 'react';
import {DividerContext} from './Divider';
import {forwardRefType} from './types';
import {getAllowedOverrides, setColorScheme, StyleProps} from './style-utils' with {type: 'macro'};
import {GlobalDOMAttributes} from '@react-types/shared';
import {ImageContext} from './Image';
import {ImageCoordinator} from './ImageCoordinator';
Expand Down Expand Up @@ -106,7 +106,7 @@ const slideLeftKeyframes = keyframes(`
`);

let popover = style({
...colorScheme(),
...setColorScheme(),
'--s2-container-bg': {
type: 'backgroundColor',
value: 'layer-2'
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-spectrum/s2/src/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
* governing permissions and limitations under the License.
*/

import {colorScheme} from './style-utils' with {type: 'macro'};
import {ColorSchemeContext} from './Provider';
import {DOMRef, GlobalDOMAttributes} from '@react-types/shared';
import {forwardRef, MutableRefObject, useCallback, useContext} from 'react';
import {ModalOverlay, ModalOverlayProps, Modal as RACModal, useLocale} from 'react-aria-components';
import {setColorScheme} from './style-utils' with {type: 'macro'};
import {style} from '../style' with {type: 'macro'};
import {useDOMRef} from '@react-spectrum/utils';

Expand All @@ -28,7 +28,7 @@ interface ModalProps extends Omit<ModalOverlayProps, 'className' | 'style' | 're
}

const modalOverlayStyles = style({
...colorScheme(),
...setColorScheme(),
position: 'absolute',
top: 0,
left: 0,
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-spectrum/s2/src/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import {
OverlayTriggerStateContext,
useLocale
} from 'react-aria-components';
import {colorScheme, getAllowedOverrides, heightProperties, UnsafeStyles, widthProperties} from './style-utils' with {type: 'macro'};
import {ColorSchemeContext} from './Provider';
import {createContext, ForwardedRef, forwardRef, ReactNode, useCallback, useContext, useMemo} from 'react';
import {DOMRef, DOMRefValue, GlobalDOMAttributes} from '@react-types/shared';
import {getAllowedOverrides, heightProperties, setColorScheme, UnsafeStyles, widthProperties} from './style-utils' with {type: 'macro'};
import {lightDark, style} from '../style' with {type: 'macro'};
import {mergeRefs} from '@react-aria/utils';
import {mergeStyles} from '../style/runtime';
Expand Down Expand Up @@ -62,7 +62,7 @@ export interface PopoverProps extends UnsafeStyles, Omit<AriaPopoverProps,
}

let popover = style({
...colorScheme(),
...setColorScheme(),
'--s2-container-bg': {
type: 'backgroundColor',
value: {
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-spectrum/s2/src/Provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
*/

import type {ColorScheme, Router} from '@react-types/provider';
import {colorScheme, UnsafeStyles} from './style-utils' with {type: 'macro'};
import {createContext, JSX, ReactNode, useContext} from 'react';
import {DOMProps} from '@react-types/shared';
import {filterDOMProps} from '@react-aria/utils';
import {Fonts} from './Fonts';
import {generateDefaultColorSchemeStyles} from './page.macro' with {type: 'macro'};
import {I18nProvider, RouterProvider, useLocale} from 'react-aria-components';
import {mergeStyles} from '../style/runtime';
import {setColorScheme, UnsafeStyles} from './style-utils' with {type: 'macro'};
import {style} from '../style' with {type: 'macro'};
import {StyleString} from '../style/types';

Expand Down Expand Up @@ -77,7 +77,7 @@ export function Provider(props: ProviderProps): JSX.Element {
generateDefaultColorSchemeStyles();

let providerStyles = style({
...colorScheme(),
...setColorScheme(),
'--s2-container-bg': {
type: 'backgroundColor',
value: {
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-spectrum/s2/src/TableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {
Virtualizer
} from 'react-aria-components';
import {ButtonGroup} from './ButtonGroup';
import {centerPadding, colorScheme, controlFont, getAllowedOverrides, StylesPropWithHeight, UnsafeStyles} from './style-utils' with {type: 'macro'};
import {centerPadding, controlFont, getAllowedOverrides, setColorScheme, StylesPropWithHeight, UnsafeStyles} from './style-utils' with {type: 'macro'};
import {Checkbox} from './Checkbox';
import Checkmark from '../s2wf-icons/S2_Icon_Checkmark_20_N.svg';
import Chevron from '../ui-icons/Chevron';
Expand Down Expand Up @@ -1186,7 +1186,7 @@ const editableCell = style<CellRenderProps & S2TableProps & {isDivider: boolean,
});

let editPopover = style({
...colorScheme(),
...setColorScheme(),
'--s2-container-bg': {
type: 'backgroundColor',
value: 'layer-2'
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-spectrum/s2/src/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
TooltipRenderProps,
useLocale
} from 'react-aria-components';
import {centerPadding, colorScheme, UnsafeStyles} from './style-utils' with {type: 'macro'};
import {centerPadding, setColorScheme, UnsafeStyles} from './style-utils' with {type: 'macro'};
import {ColorScheme} from '@react-types/provider';
import {ColorSchemeContext} from './Provider';
import {createContext, forwardRef, MutableRefObject, ReactNode, useCallback, useContext, useState} from 'react';
Expand All @@ -44,7 +44,7 @@ export interface TooltipProps extends Omit<AriaTooltipProps, 'children' | 'class
}

const tooltip = style<TooltipRenderProps & {colorScheme: ColorScheme | 'light dark' | null}>({
...colorScheme(),
...setColorScheme(),
justifyContent: 'center',
alignItems: 'center',
maxWidth: 160,
Expand Down
18 changes: 18 additions & 0 deletions packages/@react-spectrum/s2/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ export {TreeView, TreeViewItem, TreeViewItemContent, TreeViewLoadMoreItem} from

export {pressScale} from './pressScale';

export {
centerPadding,
setColorScheme
} from './style-utils';

export {mergeStyles} from '../style/runtime';

export {Autocomplete, Collection, FileTrigger, parseColor, useLocale} from 'react-aria-components';
export {useListData, useTreeData, useAsyncList} from 'react-stately';

Expand Down Expand Up @@ -171,3 +178,14 @@ export type {TooltipProps} from './Tooltip';
export type {TreeViewProps, TreeViewItemProps, TreeViewItemContentProps, TreeViewLoadMoreItemProps} from './TreeView';
export type {AutocompleteProps, FileTriggerProps, TooltipTriggerComponentProps as TooltipTriggerProps, SortDescriptor, Color, Key, Selection, RouterConfig} from 'react-aria-components';
export type {ListData, TreeData, AsyncListData} from 'react-stately';

export type {
StylesProp,
StylesPropWithHeight,
StylesPropWithoutWidth,
UnsafeClassName,
UnsafeStyles,
StyleProps,
WidthProperties,
HeightProperties
} from './style-utils';
44 changes: 43 additions & 1 deletion packages/@react-spectrum/s2/src/style-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,25 @@ import {CSSProperties} from 'react';
import {fontRelative} from '../style';
import {StyleString} from '../style/types';

/**
* Calculates vertical padding to center a single line of text within a container.
* Uses the CSS `self()` function and `1lh` unit to compute the padding based on
* the container's minimum height and border widths.
* This is useful for precise vertical centering without introducing a flex/grid layout to the container.
*
* @param minHeight - A CSS expression for the minimum height to center within. Defaults to `'self(minHeight)'`.
* @returns A CSS `calc()` expression wrapped as an arbitrary style value.
*
* @example
* ```tsx
* import {centerPadding} from '@react-spectrum/s2';
* import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
*
* const styles = style({
* paddingY: centerPadding()
* });
* ```
*/
export function centerPadding(minHeight: string = 'self(minHeight)'): `[${string}]` {
return `[calc((${minHeight} - self(borderTopWidth, 0px) - self(borderBottomWidth, 0px) - 1lh) / 2)]`;
}
Expand Down Expand Up @@ -113,7 +132,24 @@ export const fieldInput = () => ({
containIntrinsicWidth: 'calc(var(--defaultWidth) - self(paddingStart, 0px) - self(paddingEnd, 0px) - self(borderStartWidth, 0px) - self(borderEndWidth, 0px))'
} as const);

export const colorScheme = () => ({
/**
* Returns style properties that set the CSS `color-scheme` for a component.
* Defaults to the page's color scheme and supports `'light'`, `'dark'`, and `'light dark'` values
* via the `colorScheme` render prop condition.
* Intended for root containers (e.g. providers, modals, and popovers), and not needed for individual components.
*
* @example
* ```tsx
* import {setColorScheme} from '@react-spectrum/s2';
* import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
*
* const styles = style({
* ...setColorScheme(),
* backgroundColor: 'layer-1'
* });
* ```
*/
export const setColorScheme = () => ({
colorScheme: {
// Default to page color scheme if none is defined.
default: '[var(--lightningcss-light, light) var(--lightningcss-dark, dark)]',
Expand Down Expand Up @@ -312,13 +348,19 @@ export const widthProperties = [
'maxWidth'
] as const;

/** The set of width-related CSS property names (`width`, `minWidth`, `maxWidth`). */
export type WidthProperties = (typeof widthProperties)[number];

export const heightProperties = [
'size',
'height',
'minHeight',
'maxHeight'
] as const;

/** The set of height-related CSS property names (`height`, `minHeight`, `maxHeight`) plus `size` (which controls both width and height). */
export type HeightProperties = (typeof heightProperties)[number];

export type StylesProp = StyleString<(typeof allowedOverrides)[number] | (typeof widthProperties)[number]>;
export type StylesPropWithHeight = StyleString<(typeof allowedOverrides)[number] | (typeof widthProperties)[number] | (typeof heightProperties)[number]>;
export type StylesPropWithoutWidth = StyleString<(typeof allowedOverrides)[number]>;
Expand Down
66 changes: 63 additions & 3 deletions packages/@react-spectrum/s2/style/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,63 @@ import {Inset, fontRelative as internalFontRelative, space as internalSpace, Spa
import type {MacroContext} from '@parcel/macros';
import {StyleString} from './types';

export {baseColor, color, lightDark, colorMix, size, style} from './spectrum-theme';
export {baseColor, color, lightDark, colorMix, linearGradient, size, style} from './spectrum-theme';
export {raw, keyframes} from './style-macro';
export type {StyleString} from './types';

// Wrap these functions in arbitrary value syntax when called from the outside.
/**
* Converts a pixel value to a Spectrum spacing token in `rem` units.
*
* @param px - The spacing in pixels.
* @returns A `rem` value wrapped as an arbitrary style value.
*
* @example
* ```tsx
* import {space} from '@react-spectrum/s2/style' with {type: 'macro'};
*
* const styles = style({
* gap: space(12) // 12/16 = 0.75rem
* });
* ```
*/
export function space(px: number): `[${string}]` {
return `[${internalSpace(px)}]`;
}

export function fontRelative(base: number, baseFontSize?: number): `[${string}]` {
/**
* Converts a pixel value to a font-relative `em` length. Useful for sizing elements
* relative to the current font size. Defaults to a 14px base.
*
* @param base - The pixel value to convert.
* @param baseFontSize - The base font size in pixels to divide by. Defaults to `14`.
* @returns A CSS `em` value wrapped as an arbitrary style value.
*
* @example
* ```tsx
* import {fontRelative} from '@react-spectrum/s2/style' with {type: 'macro'};
*
* const styles = style({
* gap: fontRelative(2) // 2/14 = ~0.143em
* });
* ```
*/
export function fontRelative(base: number, baseFontSize = 14): `[${string}]` {
return `[${internalFontRelative(base, baseFontSize)}]`;
}

/**
* Returns consistent Spectrum focus ring outline styles for interactive components.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we override the results of this one a *lot
I don't think we should export it until we consolidate those into an easier to use macro. Otherwise we semi-lock ourselves into the current implementation and we'd need to build a wrapper macro function

a handful of examples:



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just realised, we already export this don't we... womp womp, we should probably build that wrapper around it for ourselves

*
* @example
* ```tsx
* import {focusRing, style} from '@react-spectrum/s2/style' with {type: 'macro'};
*
* const styles = style({
* ...focusRing(),
* borderRadius: 'lg'
* });
* ```
*/
export const focusRing = () => ({
outlineStyle: {
default: 'none',
Expand Down Expand Up @@ -78,6 +123,21 @@ const iconSizes = {
XL: 26
} as const;

/**
* Generates styles for an icon element with the given size, color, and layout options.
* Must be imported with `{type: 'macro'}`.
*
* @param options - Icon styling options including `size` (XS–XL), `color`, and layout properties.
* @returns A `StyleString` that can be applied to an icon element.
*
* @example
* ```tsx
* import {iconStyle} from '@react-spectrum/s2/style' with {type: 'macro'};
* import Edit from '@react-spectrum/s2/icons/Edit';
*
* <Edit styles={iconStyle({size: 'XL', color: 'positive'})} />
* ```
*/
export function iconStyle(this: MacroContext | void, options: IconStyle): StyleString<Exclude<keyof IconStyle, 'color' | 'size'>> {
let {size = 'M', color, ...styles} = options;

Expand Down
16 changes: 16 additions & 0 deletions packages/@react-spectrum/s2/style/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ import {StyleString} from './types';
// };
// }

/**
* Merges multiple style strings together, combining the CSS properties from each.
* Later styles take precedence over earlier ones for the same property.
* Useful for composing styles from multiple `style()` macro calls.
*
* @example
* ```tsx
* import {mergeStyles} from '@react-spectrum/s2';
* import {style} from '@react-spectrum/s2/style' with {type: 'macro'};
*
* const baseStyles = style({padding: 8});
* const overrideStyles = style({padding: 16, color: 'heading'});
* const merged = mergeStyles(baseStyles, overrideStyles);
* // merged has `padding: 16` and `color: heading`.
* ```
*/
export function mergeStyles(...styles: (StyleString | null | undefined)[]): StyleString {
let definedStyles = styles.filter(Boolean) as StyleString[];
if (definedStyles.length === 1) {
Expand Down
Loading