diff --git a/packages/@react-spectrum/s2/style/index.ts b/packages/@react-spectrum/s2/style/index.ts index 33c4aa925b3..7ca5b24f0b4 100644 --- a/packages/@react-spectrum/s2/style/index.ts +++ b/packages/@react-spectrum/s2/style/index.ts @@ -77,7 +77,7 @@ const iconSizes = { export function iconStyle(this: MacroContext | void, options: IconStyle): StyleString> { let {size = 'M', color, ...styles} = options; - + if (color) { styles['--iconPrimary'] = { type: 'fill', diff --git a/packages/dev/s2-docs/pages/react-aria/styling.mdx b/packages/dev/s2-docs/pages/react-aria/styling.mdx index c1b480a52ae..5ea84abab72 100644 --- a/packages/dev/s2-docs/pages/react-aria/styling.mdx +++ b/packages/dev/s2-docs/pages/react-aria/styling.mdx @@ -242,9 +242,28 @@ With this configured, all states for React Aria Components can be accessed with ``` +## Style macro + +If you want to build custom components that follow Spectrum design tokens and styling, you can use the [style macro](../s2/styling.html) from React Spectrum. The `style` macro is a build-time CSS generator that provides type safe access to Spectrum 2 design tokens including colors, spacing, sizing, and typography. + +```tsx +import {Checkbox} from 'react-aria-components'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + + +``` + ## Animation -React Aria Components supports both [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions/Using_CSS_transitions) and [keyframe animations](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes), and works with JavaScript animation libraries like [Framer Motion](https://www.framer.com/motion/). +React Aria Components supports both [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions/Using_CSS_transitions) and [keyframe animations](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes), and works with JavaScript animation libraries like [Motion](https://motion.dev/). ### CSS transitions diff --git a/packages/dev/s2-docs/pages/s2/Icons.mdx b/packages/dev/s2-docs/pages/s2/icons.mdx similarity index 100% rename from packages/dev/s2-docs/pages/s2/Icons.mdx rename to packages/dev/s2-docs/pages/s2/icons.mdx diff --git a/packages/dev/s2-docs/pages/s2/index.mdx b/packages/dev/s2-docs/pages/s2/index.mdx index 5856d0107d9..c6774e6e4ed 100644 --- a/packages/dev/s2-docs/pages/s2/index.mdx +++ b/packages/dev/s2-docs/pages/s2/index.mdx @@ -49,7 +49,7 @@ export const title = 'Home'; {id: 'Divider', name: 'Divider', href: 'Divider.html'}, {id: 'DropZone', name: 'DropZone', href: 'DropZone.html'}, {id: 'Form', name: 'Form', href: 'Form.html'}, - {id: 'Icons', name: 'Icons', href: 'Icons.html'}, + {id: 'Icons', name: 'Icons', href: 'icons.html'}, {id: 'IllustratedMessage', name: 'IllustratedMessage', href: 'IllustratedMessage.html'}, {id: 'Illustrations', name: 'Illustrations', href: 'Illustrations.html'}, {id: 'Image', name: 'Image', href: 'Image.html'}, diff --git a/packages/dev/s2-docs/pages/s2/reference.mdx b/packages/dev/s2-docs/pages/s2/reference.mdx new file mode 100644 index 00000000000..4d312d9a2ca --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/reference.mdx @@ -0,0 +1,82 @@ +import {Layout} from '../../src/Layout'; +import {InlineAlert, Heading, Content, Link} from '@react-spectrum/s2'; +import {S2Colors} from '../../src/S2Colors'; +import {S2Typography} from '../../src/S2Typography'; +import {StyleMacroProperties} from '../../src/types'; +import {getPropertyDefinitions, getShorthandDefinitions} from '../../src/styleProperties'; +export default Layout; + +export const section = 'Components'; +export const tags = ['style', 'macro', 'spectrum', 'custom', 'values', 'reference']; +export const description = 'Reference table for the style macro'; + +# Style Macro + +The `style` macro supports a constrained set of values per property that conform to Spectrum 2. + +## Colors + +All Spectrum 2 color tokens are available across color properties (e.g., `backgroundColor`, `color`, `borderColor`). +`baseColors` consists of the semantic and global colors listed below. + + + + + +## Dimensions + +Spacing props like `margin` and `padding` accept values on a **4px grid**. These are specified in `px` and get converted to `rem`. In addition to numbers, these named options are available: + +- `edge-to-text` – default spacing between the edge of a control and its text. Relative to control height. +- `pill` – default spacing between the edge of a pill-shaped control and its text. Relative to control height. +- `text-to-control` – default spacing between text and a control (e.g., label and input). Scales with font size. +- `text-to-visual` – default spacing between text and a visual element (e.g., icon). Scales with font size. + +Size props like `width` and `height` accept arbitrary pixel values. Values are converted to `rem` and multiplied by 1.25x on touch devices to increase hit targets. + + + +## Text + +Spectrum 2 typography can be applied via the `font` [shorthand](#shorthands), which sets `fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, and `color`. You can override any of these individually. +Note that `font` should be applied on a per element basis rather than globally so as to properly conform with Spectrum designs. + +```tsx +
+

Heading

+

Body

+
    +
  • List item
  • +
+
+``` + +Type scales include: UI, Body, Heading, Title, Detail, and Code. Each scale has a default and additional t-shirt sizes (e.g., `ui-sm`, `heading-2xl`, `code-xl`). + + + + + + + +## Effects + + + +## Layout + + + +## Misc + + + +## Shorthands + +Shorthands apply their provided value to commonly grouped properties. + + + +## Conditions + + diff --git a/packages/dev/s2-docs/pages/s2/styling.mdx b/packages/dev/s2-docs/pages/s2/styling.mdx index 3735835c868..1f244ade31e 100644 --- a/packages/dev/s2-docs/pages/s2/styling.mdx +++ b/packages/dev/s2-docs/pages/s2/styling.mdx @@ -1,8 +1,7 @@ import {Layout} from '../../src/Layout'; import {InlineAlert, Heading, Content, Link} from '@react-spectrum/s2'; -import {S2Colors} from '../../src/S2Colors'; -import {S2Typography} from '../../src/S2Typography'; import {S2StyleProperties} from '../../src/S2StyleProperties'; +import {S2FAQ} from '../../src/S2FAQ'; export default Layout; export const section = 'Guides'; @@ -11,11 +10,13 @@ export const description = 'Styling in React Spectrum'; # Styling -React Spectrum includes a build-time style macro that generates atomic CSS and lets you apply Spectrum tokens directly in your components with type-safe autocompletion. +React Spectrum includes a build-time `style` macro that generates atomic CSS and lets you apply Spectrum tokens directly in your components with type-safe autocompletion. ## Style macro -The `style` macro runs at build time and returns a class name for applying Spectrum 2 design tokens (colors, spacing, sizing, typography, etc.). +The `style` macro runs at build time and returns a class name for applying Spectrum 2 design tokens (colors, spacing, sizing, typography, etc.). As can been seen below, +the keys of the object passed to the `style` macro correspond to a CSS property, each paired with the property's desired value. See [here](./reference.html) for a full list +of supported values. ```tsx import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; @@ -37,6 +38,14 @@ Colocating styles with your component code means: - Develop more efficiently – no switching files or writing selectors. - Refactor with confidence – changes are isolated; deleting a component removes its styles. + + Important Note + + Due to the atomic nature of the generated CSS rules, it is strongly recommended that you follow the CSS optimization guide listed [below](#css-optimization). + Failure to do so can result in large number of duplicate rules and obtuse styling bugs. + + + ## Spectrum components The `styles` prop accepts a limited set of CSS properties, including layout, spacing, sizing, and positioning. Other styles such as colors and internal padding cannot be customized within Spectrum components. @@ -86,77 +95,6 @@ import {Button} from '@react-spectrum/s2'; 'visibility' ]} /> -### UNSAFE Style Overrides - -We highly discourage overriding the styles of React Spectrum components because it may break at any time when we change our implementation, making it difficult for you to update in the future. Consider using [React Aria Components](https://react-spectrum.adobe.com/react-aria/) with our style macro to build a custom component with Spectrum styles instead. - -With that being said, the `UNSAFE_className` and `UNSAFE_style` props are supported on Spectrum 2 components as last-resort escape hatches. - -```tsx -/* YourComponent.tsx */ -import {Button} from '@react-spectrum/s2'; -import './YourComponent.css'; - -function YourComponent() { - return ; -} -``` - -```css -/* YourComponent.css */ -.your-unsafe-class { - background: red; -} -``` - -## Values - -The `style` macro supports a constrained set of values per property that conform to Spectrum 2. This improves consistency and maintainability. - -### Colors - -All Spectrum 2 color tokens are available across color properties (e.g., `backgroundColor`, `color`, `borderColor`). - - - -### Spacing - -Spacing props like `margin` and `padding` accept values on a **4px grid**. These are specified in `px` and get converted to `rem`. In addition to numbers, these named options are available: - -- `edge-to-text` – default spacing between the edge of a control and its text. Relative to control height. -- `pill` – default spacing between the edge of a pill-shaped control and its text. Relative to control height. -- `text-to-control` – default spacing between text and a control (e.g., label and input). Scales with font size. -- `text-to-visual` – default spacing between text and a visual element (e.g., icon). Scales with font size. - -### Sizing - -Size props like `width` and `height` accept arbitrary pixel values. Values are converted to `rem` and multiplied by 1.25x on touch devices to increase hit targets. - -### Typography - -Spectrum 2 typography is applied via the `font` shorthand, which sets `fontFamily`, `fontSize`, `fontWeight`, `lineHeight`, and `color`. You can override any of these individually. - -```tsx -
-

Heading

-

Body

-
    -
  • List item
  • -
-
-``` - -Type scales include: UI, Body, Heading, Title, Detail, and Code. Each scale has a default and additional t-shirt sizes (e.g., `ui-sm`, `heading-2xl`, `code-xl`). - - - - - Important Note - - Only use `` and `` inside Spectrum components with predefined styles (e.g., ``, ``). They are unstyled by default and should not be used standalone. Use HTML elements with the style macro instead. - - - ## Conditional styles Define conditional values as objects to handle media queries, UI states (hover/press), and variants. This keeps all values for a property together. @@ -166,12 +104,19 @@ Define conditional values as objects to handle media queries, UI states (hover/p className={style({ padding: { default: 8, - lg: 32 + lg: 32, + '@media (min-width: 2560px)': 64 } })} /> ``` +In the example above, the keys of the nested object now map out the "conditions" that govern the padding of the `div`. This translates to the following: + +- If the viewport is larger than `2560px`, as specified by a user defined [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries), the padding of the `div` is set to `64px`. +- If the viewport matches the `style` macro's predefined `lg` [breakpoint](./reference.html#conditions) (i.e. the viewport is larger than `1024px`), but does not exceed previous condition, the padding of the `div` is set to `32px` +- Otherwise, default to a padding of `8px`. + Conditions are mutually exclusive and ordered. The macro uses CSS cascade layers so the last matching condition wins without specificity issues. ### Runtime conditions @@ -195,13 +140,14 @@ function MyComponent({variant}: {variant: 'primary' | 'secondary'}) { } ``` -Boolean conditions starting with `is` can be used directly without nesting: +Boolean conditions starting with `is` or `allows` can be used directly without nesting: ```tsx const styles = style({ backgroundColor: { default: 'gray-100', - isSelected: 'gray-900' + isSelected: 'gray-900', + allowsRemoving: 'gray-400' } }); @@ -222,7 +168,7 @@ import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; isSelected: 'gray-900' } })} -/> +/> ``` ### Nesting conditions @@ -253,6 +199,11 @@ const styles = style({ Extract common styles into constants and spread them into `style` calls. These must be in the same file or imported from another file as a macro. ```tsx +// style-utils.ts +export const bannerBackground = () => 'blue-1000' as const; + +// component.tsx +import {bannerBackground} from './style-utils' with {type: 'macro'}; const horizontalStack = { display: 'flex', alignItems: 'center', @@ -261,6 +212,7 @@ const horizontalStack = { const styles = style({ ...horizontalStack, + backgroundColor: bannerBackground(), columnGap: 4 }); ``` @@ -308,9 +260,28 @@ const buttonStyle = style({ ``` +## Setting CSS variables + +CSS variables can be directly defined in a `style` macro, allowing child elements to then access them in their own styles. +A `type` should be provided to specify the CSS property type the `value` represents. + +```tsx +const parentStyle = style({ + '--rowBackgroundColor': { + type: 'backgroundColor', + value: 'gray-400' + } +}); + +const childStyle = style({ + backgroundColor: '--rowBackgroundColor' +}); +``` + ## CSS optimization -The style macro relies on CSS bundling and minification for optimal output. Follow these best practices: +The `style` macro relies on CSS bundling and minification for optimal output. Failure to perform this optimization will result in a suboptimal developer experience and obtuse styling bugs. +Follow these best practices: - Ensure styles are extracted into a CSS bundle; do not inject at runtime with `