-
Notifications
You must be signed in to change notification settings - Fork 1.3k
docs: Style macro docs #9090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
docs: Style macro docs #9090
Changes from 20 commits
edeaa2f
8a1f97e
28eb788
457c59a
a15c24f
819c076
3b991ea
42b0406
36f1a6f
e2b29c9
4c94cd0
83262ef
5d2b172
980d3d6
59e9400
a414886
0b2751d
4ef4996
b04b7af
807beb0
0c6ac3f
a3d57a9
30bab22
44b0d8d
5084b6b
34ad8cc
6bf1724
6e508f2
4b38f98
2e13868
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For those reviewing this page, compare the current experience with the old: https://reactspectrum.blob.core.windows.net/reactspectrum/5084b6bfa7066fbe90e520c9575422bf0df27dd0/s2-docs/s2/styling.html In particular the organization/existence of subpage/contents, open to opinions as to which is preferred |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,15 +3,20 @@ 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'; | ||
| export const tags = ['style', 'macro', 'spectrum', 'custom']; | ||
| export const description = 'Styling in React Spectrum'; | ||
| export const relatedPages = [ | ||
| {title: 'Advanced', url: './styling/advanced.html'}, | ||
| {title: 'Reference Table', url: './styling/reference.html'} | ||
| ]; | ||
|
|
||
| # 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 | ||
|
|
||
|
|
@@ -37,6 +42,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. | ||
|
|
||
| <InlineAlert variant="informative"> | ||
| <Heading>Important Note</Heading> | ||
| <Content> | ||
| Due to the atomic nature of the generated CSS rules, it is strongly recommended that you follow the best practices listed [below](#css-optimization). | ||
| Failure to do so can result in large number of duplicate rules and obtuse styling bugs. | ||
| </Content> | ||
| </InlineAlert> | ||
|
Comment on lines
41
to
47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe overkill, but it felt like it was important to mention this up front so people realize that this is important |
||
|
|
||
| ## 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. | ||
|
|
@@ -88,7 +101,7 @@ import {Button} from '@react-spectrum/s2'; | |
|
|
||
| ### UNSAFE Style Overrides | ||
snowystinger marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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. | ||
| 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. | ||
|
|
||
|
|
@@ -111,7 +124,7 @@ function YourComponent() { | |
|
|
||
| ## Values | ||
|
|
||
| The `style` macro supports a constrained set of values per property that conform to Spectrum 2. This improves consistency and maintainability. | ||
| The `style` macro supports a constrained set of values per property that conform to Spectrum 2. This improves consistency and maintainability. See the [reference](./styling/reference.html) page for a full list of available style macro properties. | ||
snowystinger marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Colors | ||
|
|
||
|
|
@@ -153,164 +166,13 @@ Type scales include: UI, Body, Heading, Title, Detail, and Code. Each scale has | |
| <InlineAlert variant="notice"> | ||
| <Heading>Important Note</Heading> | ||
| <Content> | ||
| Only use `<Heading>` and `<Text>` inside Spectrum components with predefined styles (e.g., `<Dialog>`, `<MenuItem>`). They are unstyled by default and should not be used standalone. Use HTML elements with the style macro instead. | ||
| Only use `<Heading>` and `<Text>` inside Spectrum components with predefined styles (e.g., `<Dialog>`, `<MenuItem>`). They are unstyled by default and should not be used standalone. Use HTML elements with the `style` macro instead. | ||
|
||
| </Content> | ||
| </InlineAlert> | ||
|
|
||
| ## 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. | ||
|
|
||
| ```tsx | ||
| <div | ||
| className={style({ | ||
| padding: { | ||
| default: 8, | ||
| lg: 32 | ||
| } | ||
| })} | ||
| /> | ||
| ``` | ||
|
|
||
| Conditions are mutually exclusive and ordered. The macro uses CSS cascade layers so the last matching condition wins without specificity issues. | ||
|
|
||
| ### Runtime conditions | ||
|
|
||
| When runtime conditions are detected (e.g., variants, UI states), the macro returns a function to resolve styles at runtime. | ||
|
|
||
| ```tsx | ||
| import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; | ||
|
|
||
| const styles = style({ | ||
| backgroundColor: { | ||
| variant: { | ||
| primary: 'accent', | ||
| secondary: 'neutral' | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| function MyComponent({variant}: {variant: 'primary' | 'secondary'}) { | ||
| return <div className={styles({variant})} /> | ||
| } | ||
| ``` | ||
|
|
||
| Boolean conditions starting with `is` can be used directly without nesting: | ||
|
|
||
| ```tsx | ||
| const styles = style({ | ||
| backgroundColor: { | ||
| default: 'gray-100', | ||
| isSelected: 'gray-900' | ||
| } | ||
| }); | ||
|
|
||
| <div className={styles({isSelected: true})} /> | ||
| ``` | ||
|
|
||
| Runtime conditions work well with render props in React Aria Components. If you inline styles, you’ll get autocomplete for available conditions. | ||
|
|
||
| ```tsx | ||
| import {Checkbox} from 'react-aria-components'; | ||
| import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; | ||
|
|
||
| <Checkbox | ||
| className={style({ | ||
| backgroundColor: { | ||
| default: 'gray-100', | ||
| isHovered: 'gray-200', | ||
| isSelected: 'gray-900' | ||
| } | ||
| })} | ||
| /> | ||
| ``` | ||
|
|
||
| ### Nesting conditions | ||
|
|
||
| Nest conditions to apply styles when multiple conditions are true. Conditions at the same level are mutually exclusive; order determines precedence. | ||
|
|
||
| ```tsx | ||
| const styles = style({ | ||
| backgroundColor: { | ||
| default: 'gray-25', | ||
| isSelected: { | ||
| default: 'neutral', | ||
| isEmphasized: 'accent', | ||
| forcedColors: 'Highlight', | ||
| isDisabled: { | ||
| default: 'gray-400', | ||
| forcedColors: 'GrayText' | ||
| } | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| <div className={styles({isSelected, isEmphasized, isDisabled})} /> | ||
| ``` | ||
|
|
||
| ## Reusing styles | ||
|
|
||
| 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 | ||
| const horizontalStack = { | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| columnGap: 8 | ||
| } as const; | ||
|
|
||
| const styles = style({ | ||
| ...horizontalStack, | ||
| columnGap: 4 | ||
| }); | ||
| ``` | ||
|
|
||
| Create custom utilities by defining your own macros. | ||
|
|
||
| ```ts | ||
| // style-utils.ts | ||
| export function horizontalStack(gap: number) { | ||
| return { | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| columnGap: gap | ||
| } as const; | ||
| } | ||
| ``` | ||
|
|
||
| Usage: | ||
|
|
||
| ```tsx | ||
| // component.tsx | ||
| import {horizontalStack} from './style-utils' with {type: 'macro'}; | ||
| import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; | ||
|
|
||
| const styles = style({ | ||
| ...horizontalStack(4), | ||
| backgroundColor: 'base' | ||
| }); | ||
| ``` | ||
|
|
||
| ### Built-in utilities | ||
|
|
||
| Use `focusRing()` to add the standard Spectrum focus ring. | ||
|
|
||
| ```tsx | ||
| "use client"; | ||
| import {style, focusRing} from '@react-spectrum/s2/style' with {type: 'macro'}; | ||
| import {Button} from '@react-spectrum/s2'; | ||
|
|
||
| const buttonStyle = style({ | ||
| ...focusRing(), | ||
| // ...other styles | ||
| }); | ||
|
|
||
| <Button styles={buttonStyle}>Press me</Button> | ||
| ``` | ||
|
|
||
| ## CSS optimization | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optimization sounds so optional maybe it'd be better to structure all bundler setups into their own page which includes how the optimization setup works, but then it all sounds much more mandatory There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. happy for the team to weigh in here then, especially since it would fall within the changes made in #9126 |
||
|
|
||
| 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. Follow these best practices: | ||
|
|
||
| - Ensure styles are extracted into a CSS bundle; do not inject at runtime with `<style>` tags. | ||
| - Use a CSS minifier like `lightningcss` to deduplicate common rules (consider in dev for easier debugging). | ||
|
|
@@ -362,4 +224,22 @@ CSS resets are strongly discouraged. Global CSS selectors can unintentionally af | |
| /* App.css */ | ||
| @layer reset, _; | ||
| @import "reset.css" layer(reset); | ||
| ``` | ||
| ``` | ||
|
|
||
| ## Developing with style macros | ||
|
|
||
| Since `style` macros are quite different from using `className`/`style` directly, many may find it initially challenging to debug and develop against. | ||
| Below are some useful tools that may benefit your developer experience: | ||
|
|
||
| - The [atomic-css-devtools](https://github.com/astahmer/atomic-css-devtools) extension presents an inspected element's atomic CSS rules | ||
| in a non-atomic format, making it easier to scan. | ||
|
Comment on lines
+375
to
+376
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. todo: replace with the new devtool extension @snowystinger is making |
||
|
|
||
| - This [sandbox](https://codesandbox.io/p/devbox/react-spectrum-s2-style-macro-template-h6fpsq) is preconfigured to support React Spectrum S2, React Aria Components, and | ||
snowystinger marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| the `style` macros for quick prototyping. | ||
|
|
||
| - If you are using Cursor, we offer a set of [Cursor rules](https://github.com/adobe/react-spectrum/blob/main/rules/style-macro.mdc) to use when developing with style macros. Additionally, | ||
| we have MCP servers for [React Aria](#TODO) and [React Spectrum](https://www.npmjs.com/package/@react-spectrum/mcp) respectively that interface with the docs. | ||
|
||
|
|
||
| ## FAQ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we make use of disclosures here? (but maybe a follow-up if it takes too long to style and what not) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thats a good suggestion, I was a bit torn on if the current style was better or now (inspired by our own FAQ in github) but I've definitely seen both styles on other sites like MUI and Untitled UI. I'll bring this up with the rest of the team and see what people like |
||
|
|
||
| <S2FAQ /> | ||
snowystinger marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Uh oh!
There was an error while loading. Please reload this page.