Skip to content

[Accepted] ADR: Use Box + sx to author components #2020

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

Merged
merged 12 commits into from
Apr 25, 2022
Merged
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"lib/**/*",
"lib-*/**/*",
"types/**/*",
"consumer-test/**/*"
"consumer-test/**/*",
"contributor-docs/adrs/*"
],
"globals": {
"__DEV__": "readonly"
Expand Down
62 changes: 62 additions & 0 deletions contributor-docs/adrs/adr-005-box-sx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# ADR 005: Use Box as a building block for all other components

## Status

Approved

## Context

In Primer React and consuming applications, we use many different patterns for creating React components. Two common patterns are:

1. Creating components with styled-components

```tsx
const Avatar = styled.img.attrs<StyledAvatarProps>(props => ({
height: props.size,
width: props.size
}))<StyledAvatarProps>`
display: inline-block;
overflow: hidden;
line-height: ${get('lineHeights.condensedUltra')};
border-radius: ${props => getBorderRadius(props)};
${sx}
`
```

[Show full code example →](https://github.com/primer/react/pull/2019/files?diff=split&w=0)

2. Creating components with Box

```tsx
const Avatar: React.FC<AvatarProps> = ({size = 20, alt = '', square = false, sx = {}, ...props}) => {
const styles:BetterSystemStyleObject = {
display: 'inline-block',
overflow: 'hidden',
lineHeight: 'condensedUltra',
borderRadius: getBorderRadius({size, square})
}

return (
<Box
as="img"
alt={alt}
sx={merge<BetterSystemStyleObject>(styles, sx)} // styles needs to merge with props.sx
{...props}
/>
)
}
```

[Show full code example →](https://github.com/primer/react/pull/2019/files?diff=split&w=0)

&nbsp;

## Decision

Prefer using method #2: Creating components with Box for the following reasons:
Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, this is good to know...I am building new components in dotcom right now, but I suppose if we wanted them upstreamed into Primer React, it would make more sense to follow this guidance!

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd recommend using the same method in dotcom as well.

We want to improve the way product specific React components can consume primer primitives and those improvements will ship in Box. (more here)

Also, replacing styled-components with a more performant styling framework is on our radar, although not soon.


- Better authoring experience with Typescript. With Box, we can improve the API and autocomplete for consuming primitives. [See research](https://github.com/github/primer/discussions/755#discussioncomment-2318144)
- The styling library (i.e. styled-components) becomes an implementation detail and can be changed later with minimal breaking changes for consumers. (Avoids leaky abstractions)
Copy link
Contributor

Choose a reason for hiding this comment

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

In what cases what styled-comopnents be appropriate?

Copy link
Member Author

@siddharthkp siddharthkp Apr 13, 2022

Choose a reason for hiding this comment

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

In what cases what styled-comopnents be appropriate?

I want to say that if you're using primer primitives, then Box + sx is always recommended over styled-components.

@colebemis are there any use cases where this wouldn't be true?

There is a slight syntax difference between string with styled-components and an object with Box, which can be a tradeoff in developer experience. The planned improvements with Box should make that tradeoff easy to make though.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think there's anything you can do with styled-components that you can't do with Box + sx. Styled-components has a slightly more convenient API in some cases but it comes with the tradeoffs you listed in this ADR

Copy link
Contributor

Choose a reason for hiding this comment

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

Could the additional runtime overheads to using Box + sx be highlighted in this ADR as a known risk? Particularly as we're recommending authors use the costlier implementation approach at this time.

Copy link
Contributor

Choose a reason for hiding this comment

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

@siddharthkp @colebemis related to

I don't think there's anything you can do with styled-components that you can't do with Box + sx.

Should Primer expose a replacement for the styled-components built in version of keyframes and createGlobalStyle (even if they're just re-exports for now)? I believe they may be the only apis from styled-components that consumers would still need to call directly.

This might enable applications that are "all in" on using primer primitives for styling, and allow for things like an eslint plugin that bans internal usage of styled-components directly, directing consumers to the primer-based alternatives?

Copy link
Member Author

Choose a reason for hiding this comment

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

I like that idea 🤔

Just to make sure, could this result in multiple versions of styled-components on the page because of version mismatch?

Copy link
Contributor

Choose a reason for hiding this comment

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

As long as it's still just a peer of primer I don't think there's any risk of multiple versions for sc.

I think that the only limit on styled components be via an eslint rule saying, use box and ..... Instead of using styled components directly, which should provide a nice way to nudge other sc usage towards primer apis

- We have had issues with exporting types, we can increase confidence by keeping the exported types close to what we author.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you expand on the issues we've had with exporting types?

Copy link
Member Author

Choose a reason for hiding this comment

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

sorry for my lazy phrasing here 😅

What I'm trying to convey here is that

  1. We've had a couple of bugs like #1959 where we were extracting types from a "styled component"
  2. Folks on the memex side have reported that the typew are hard to debug because they have a lot of layers (styled components + styled system)

I feel more confident having more control over what we export + keeping it as flat as possible. Types deserves an ADR of it's own, to be honest.

Copy link
Member

@lesliecdubs lesliecdubs Apr 23, 2022

Choose a reason for hiding this comment

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

Would it be useful to amend the ADR to include some of these additional details for context, either now or in a follow-up PR?

Copy link
Member Author

@siddharthkp siddharthkp Apr 25, 2022

Choose a reason for hiding this comment

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

Going to create a new ADR for types (in new PR), there's more to discuss there because we don't have a standard way right now to propose :)


See diff for moving Avatar from approach 1 to 2: https://github.com/primer/react/pull/2019/files?diff=split&w=0