-
Notifications
You must be signed in to change notification settings - Fork 645
Add sx prop for theme-aware ad-hoc styles
#755
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
Conversation
|
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/primer/primer-components/76xp9rlse |
|
@BinaryMuse What if we did something like this? import styled from "styled-components";
import css from "@styled-system/css";
import {COMMON} from "./constants";
const sx = props => css(props.sx);
const Label = styled.div`
background: red;
${COMMON};
${sx};
`;
export default function App() {
return (
<div>
<Label sx={{ bg: "lightblue", "&:hover": { bg: "pink" } }}>Foo</Label>
</div>
);
}We'd add the |
Thank you, Cole; for some reason I didn't consider this, I think it's the ideal solution for that problem. I changed up the abstraction a bit and managed: with only this change to the components: diff --git a/src/BorderBox.js b/src/BorderBox.js
index 2cdde7b5..778fd593 100644
--- a/src/BorderBox.js
+++ b/src/BorderBox.js
@@ -1,10 +1,10 @@
-import styled from 'styled-components'
+import sx from './sx'
import PropTypes from 'prop-types'
import Box from './Box'
import theme from './theme'
import {BORDER} from './constants'
-const BorderBox = styled(Box)(BORDER)
+const BorderBox = sx.styled(Box)(BORDER)
BorderBox.defaultProps = {
theme,
diff --git a/src/Heading.js b/src/Heading.js
index f75ff14c..81705fd4 100644
--- a/src/Heading.js
+++ b/src/Heading.js
@@ -1,9 +1,9 @@
-import styled from 'styled-components'
+import sx from './sx'
import PropTypes from 'prop-types'
import {TYPOGRAPHY, COMMON, get} from './constants'
import theme from './theme'
-const Heading = styled.h1`
+const Heading = sx.styled.h1`
font-weight: ${get('fontWeights.bold')};
font-size: ${get('fontSizes.5')};
margin: 0;Can you think of any downsides to this strategy? |
|
Oh, interesting. What are the benefits of |
That only works if you don't want to define any additional styles, right? What about inside the library itself? By wrapping -import styled from 'styled-components'
+import styled from './sx-styled' |
Isn't that what we already do with other system props? const Heading = styled.h1`
font-weight: ${get('fontWeights.bold')};
font-size: ${get('fontSizes.5')};
margin: 0;
${TYPOGRAPHY};
${COMMON};
+ ${sx};
`I'm not sure with approach I prefer yet. I'm just trying to understand the differences. I'd love to hear @jxnblk's thoughts :) |
Gotcha. I don't think there are any, as appending One advantage of having to add it to the literal itself is that one is less likely to forget to add the appropriate prop types... |
|
@colebemis most people tend to use the |
|
@jxnblk Oh, I like the idea of including |
|
@colebemis Despite what the docs say, not every component currently gets |
|
@BinaryMuse Yeah, that makes sense. I’m fine with either. I think I prefer the more explicit approach of listing it the styled definition. I’m interested in @emplums thoughts on this too. |
|
@emplums I've updated the CONTRIBUTING doc and also started on a document for the https://primer-components-git-mkt-sx-prop.primer.now.sh/components/overriding-styles |
|
@BinaryMuse The documentation looks good! It might be nice to also include an example of responsive values with the |
emplums
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left a couple more notes on the documentation!
|
I believe I've addressed all the comments so far; please feel free to keep the feedback coming if you see anything else. |
colebemis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, @BinaryMuse! 🎉 Thanks for going the extra mile on this (e.g. writing great documentation, streamlining tests). Ship it 🚢

The
sxprop is used by some UI libraries to allow users to apply inline, ad-hoc styles to a component while still making the styles theme-aware. I experimented a bit this afternoon and was able to come up with a proof-of-concept:You can see the basic implementation in this PR, which is based around
@styled-system/css, but it is essentially the following:While exploring this PoC and the implementations from other libraries, I've identified the following constraints and challenges:
Compatibility with plain DOM nodes
This implementation only works for components explicitly wrapped with the HoC, so it wouldn't be possible to do something like
<span sx={{color: 'red.5'}}>...</span>. To enable that, we'd have to create a JSX pragma, similar to Theme UI, which would need to be used in all our components and any JS file a consumer writes that wants to use thesxprop on a plain DOM node. This pragma function would then generate the correct element tree to power the prop. This would of course need some JSX intrinsics types for TypeScript.Another option is to introduce a hook that can produce
style-prop-compatible objects with themeable values, à lauseStyles({ color: 'red.5' }).However, neither option addresses the following concern:
Nested styles
styled-systems's
cssutility, which powers this implementation, allows for more than just plain CSS properties in thesxprop object. For example, this is a valid value for the prop:However, since this generates style objects with media queries and selectors, it can't be passed directly to any DOM node.
One solution is to use styled-component's
cssprop; however, the use of this prop necessitates the use of the Babel plugin (or macro) to work, and as a result, I don't think we'd be able to implement thesxprop in Primer Components (because the Babel plugin won't run). Instead, a user could use the option that@styled-system/cssdemonstrates:Since the user would have to set up the Babel plugin anyway in this case, they could simply write the above code themselves, instead of us providing a special
sxprop.Part of me wonders if one could generate a styled-components-wrapped component on the fly using the
sxprop, but my guess is no, because if so then why does styled-components require a Babel plugin for thecssprop?