{"action":"created","issue":{"url":"https://api.github.com/repos/styled-components/styled-components/issues/1055","repository_url":"https://api.github.com/repos/styled-components/styled-components","labels_url":"https://api.github.com/repos/styled-components/styled-components/issues/1055/labels{/name}","comments_url":"https://api.github.com/repos/styled-components/styled-components/issues/1055/comments","events_url":"https://api.github.com/repos/styled-components/styled-components/issues/1055/events","html_url":"https://github.com/styled-components/styled-components/issues/1055","id":247269509,"number":1055,"title":"Reversing the relationship in theming","user":{"login":"thejameskyle","id":952783,"avatar_url":"https://avatars0.githubusercontent.com/u/952783?v=4","gravatar_id":"","url":"https://api.github.com/users/thejameskyle","html_url":"https://github.com/thejameskyle","followers_url":"https://api.github.com/users/thejameskyle/followers","following_url":"https://api.github.com/users/thejameskyle/following{/other_user}","gists_url":"https://api.github.com/users/thejameskyle/gists{/gist_id}","starred_url":"https://api.github.com/users/thejameskyle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thejameskyle/subscriptions","organizations_url":"https://api.github.com/users/thejameskyle/orgs","repos_url":"https://api.github.com/users/thejameskyle/repos","events_url":"https://api.github.com/users/thejameskyle/events{/privacy}","received_events_url":"https://api.github.com/users/thejameskyle/received_events","type":"User","site_admin":false},"labels":[{"id":461725494,"url":"https://api.github.com/repos/styled-components/styled-components/labels/discussion","name":"discussion","color":"4a558a","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":8,"created_at":"2017-08-02T04:23:25Z","updated_at":"2017-08-11T01:36:34Z","closed_at":null,"body":"Heyo,\r\n\r\nRight now the recommended pattern across styled-components and [theming](https://github.com/iamstarkov/theming) (including the packages that consume it like [emotion](https://github.com/tkh44/emotion)) is to pass specific styles down from the root of your app:\r\n\r\n```js\r\nconst Button = styled.button`\r\n  color: ${props => props.theme.main};\r\n`;\r\n\r\nconst theme = {\r\n  main: 'mediumseagreen',\r\n};\r\n\r\n<ThemeProvider theme={theme}>\r\n  <Button>Themed</Button>\r\n</ThemeProvider>\r\n```\r\n\r\nThis works for certain types of problems. But when you're writing a full theme for your app, you're touching hundreds or even thousands of styles. Structuring it this way becomes painful.\r\n\r\nYou quickly start having this separate set of styles that lives at the top of your app that is separate from your components:\r\n\r\n```js\r\nconst themeLight = {\r\n  buttonDefaultColor: \"#123456\",\r\n  buttonDefaultHoverColor: \"#123456\",\r\n  buttonDefaultActiveColor: \"#123456\",\r\n  buttonDefaultFocusColor: \"#123456\",\r\n  buttonPrimaryColor: \"#123456\",\r\n  buttonPrimaryHoverColor: \"#123456\",\r\n  buttonPrimaryActiveColor: \"#123456\",\r\n  buttonPrimaryFocusColor: \"#123456\",\r\n  // ...\r\n};\r\n\r\nconst themeDark = {\r\n  buttonDefaultColor: \"#123456\",\r\n  buttonDefaultHoverColor: \"#123456\",\r\n  buttonDefaultActiveColor: \"#123456\",\r\n  // ...\r\n};\r\n```\r\n\r\nAnd your components start filling up with lookup functions and lose their scan-ability.\r\n\r\n```js\r\nconst Button = styled.button`\r\n  color: ${props => props.theme.buttonDefaultColor};\r\n  background-color: ${props => props.theme.buttonDefaultBackgroundColor};\r\n  border-color: ${props => props.theme.buttonDefaultBorderColor};\r\n\r\n  &:hover {\r\n    color: ${props => props.theme.buttonDefaultHoverColor};\r\n    background-color: ${props => props.theme.buttonDefaultHoverBackgroundColor};\r\n    border-color: ${props => props.theme.buttonDefaultHoverBorderColor};\r\n  }\r\n\r\n  &:active {\r\n    color: ${props => props.theme.buttonDefaultActiveColor};\r\n    background-color: ${props => props.theme.buttonDefaultActiveBackgroundColor};\r\n    border-color: ${props => props.theme.buttonDefaultActiveBorderColor};\r\n  }\r\n\r\n  &:focus {\r\n    color: ${props => props.theme.buttonDefaultFocusColor};\r\n    background-color: ${props => props.theme.buttonDefaultFocusBackgroundColor};\r\n    border-color: ${props => props.theme.buttonDefaultFocusBorderColor};\r\n  }\r\n`;\r\n```\r\n\r\nThen you start getting into component variants where you are depending on other properties and every one of these functions gets more complicated:\r\n\r\n```js\r\nconst Button = styled.button`\r\n  color: ${props => {\r\n    if (props.kind === \"default\") return props.theme.buttonDefaultColor;\r\n    if (props.kind === \"primary\") return props.theme.buttonPrimaryColor;\r\n    if (props.kind === \"success\") return props.theme.buttonSuccessColor;\r\n    if (props.kind === \"warning\") return props.theme.buttonWarningColor;\r\n    if (props.kind === \"danger\") return props.theme.buttonDangerColor;\r\n  }};\r\n  // ...\r\n`;\r\n```\r\n\r\nI've seen people try to abstract all of this work and accidentally introduce performance problems. (When you're making calculations for thousands of styles across hundreds of components on every render cycle, it's hard not to have a performance problem)\r\n\r\nIt also is tough to try and abstract the giant object of styles at the root of your app. Trying to abstract a design system from the code side is a bit of a recipe for disaster long term.\r\n\r\n# Reversing the relationship\r\n\r\nColocation is a huge feature of CSS-in-JS, it leads to a whole lot of wins (which anyone using css-in-js already knows so I won't get into it here). The problems I've described with theming are because we've given up the colocation of styles and put them in two places.\r\n\r\nIf we made a couple minor changes to our theming apis, and started documenting/teaching it differently we could set developers up for success.\r\n\r\nLet me give an example:\r\n\r\nInstead of passing specific styles from the root of the app. We could instead pass down descriptors like:\r\n\r\n```js\r\n<ThemeProvider theme={{ mode: \"dark\" }}>\r\n```\r\n\r\nThen from the component side it initially looks like this:\r\n\r\n```js\r\nconst buttonColor = {\r\n  default: { light: \"#123456\", dark: \"#123456\" },\r\n  primary: { light: \"#123456\", dark: \"#123456\" },\r\n  success: { light: \"#123456\", dark: \"#123456\" },\r\n  warning: { light: \"#123456\", dark: \"#123456\" },\r\n  danger: { light: \"#123456\", dark: \"#123456\" },   \r\n};\r\n\r\nconst Button = styled.button`\r\n  color: ${props => buttonColor[props.kind][props.theme]};\r\n  // ...\r\n`;\r\n```\r\n\r\nWriting it out like this colocates your themes with your styles with your component, and we're back to the awesome workflow of CSS-in-JS.\r\n\r\n## API Design\r\n\r\nI've designed a tiny little API to encourage this pattern called [styled-theming](https://github.com/thejameskyle/styled-theming). It basically just abstracts this:\r\n\r\n```js\r\nconst fontSize = {\r\n  small: '1em',\r\n  large: '1.2em',\r\n};\r\n\r\nconst buttonColor = {\r\n  default: { light: \"#123456\", dark: \"#123456\" },\r\n  // ...\r\n};\r\n\r\nconst Button = styled.button`\r\n  font-size: ${props => fontSize[props.theme.size]};\r\n  color: ${props => buttonColor[props.kind][props.theme.mode]};\r\n  // ...\r\n`;\r\n```\r\n\r\nInto this:\r\n\r\n```js\r\nconst fontSize = theme('size', {\r\n  small: '1em',\r\n  large: '1.2em',\r\n});\r\n\r\nconst buttonColor = theme.variants('mode', 'kind', {\r\n  default: { light: \"#123456\", dark: \"#123456\" },\r\n  // ...\r\n});\r\n\r\nconst Button = styled.button`\r\n  font-size: ${fontSize};\r\n  color: ${buttonColor};\r\n  // ...\r\n`;\r\n```\r\n\r\nWe've started using this API on a project and it has simplified a _lot_ of work being done.\r\n\r\n---\r\n\r\n# Encouraging this pattern\r\n\r\nWe should, as a community, encourage developers to work in a way that sets them up for success. We can do this via api design, documentation, teaching, etc.\r\n\r\nI would like to see tools include a very lightweight api for encouraging this pattern, it could be the one I created with styled-theming, but also does not have to be.\r\n\r\nFurther, I would like to see tools like styled-components to start documenting theming this way. I'm happy to write up new docs for the styled-components site and the [theming](https://github.com/iamstarkov/theming) library. But wanted to put this issue out there first.\r\n\r\nThanks for reading\r\n\r\ncc @iamstarkov"},"comment":{"url":"https://api.github.com/repos/styled-components/styled-components/issues/comments/321714960","html_url":"https://github.com/styled-components/styled-components/issues/1055#issuecomment-321714960","issue_url":"https://api.github.com/repos/styled-components/styled-components/issues/1055","id":321714960,"user":{"login":"geelen","id":23264,"avatar_url":"https://avatars3.githubusercontent.com/u/23264?v=4","gravatar_id":"","url":"https://api.github.com/users/geelen","html_url":"https://github.com/geelen","followers_url":"https://api.github.com/users/geelen/followers","following_url":"https://api.github.com/users/geelen/following{/other_user}","gists_url":"https://api.github.com/users/geelen/gists{/gist_id}","starred_url":"https://api.github.com/users/geelen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/geelen/subscriptions","organizations_url":"https://api.github.com/users/geelen/orgs","repos_url":"https://api.github.com/users/geelen/repos","events_url":"https://api.github.com/users/geelen/events{/privacy}","received_events_url":"https://api.github.com/users/geelen/received_events","type":"User","site_admin":false},"created_at":"2017-08-11T01:36:34Z","updated_at":"2017-08-11T01:36:34Z","body":"This is excellent work. I've always considered both the props and theming APIs as kind of a minimum viable approach that anyone can build their own interfaces on top of, once we had a bit more real-world usage.\r\n\r\n> @mxstbr do you also want to migrate the API of styled-theming into styled-components or move the styled-theming repo into the styled-components org?\r\n\r\nI would prefer to keep it as a separate package, but under the organisation, and fully integrated into the documentation. That way, SC itself keeps the low level API (if your interpolation returns a function, we'll call it) and, for anyone who looks closely, it's clear how you can build up your own designs from it.\r\n\r\nMy only question is similar to @nelix's—it's not clear to me how I might provide a custom theme. In fact, it feels like more of a way to generate _variations_ of a component rather than _theme_ them. (I guess that's kinda the issue title now I think about it). I think, if the API could be extended somehow to allow outside-in theming it might be enough to cover all use-cases.\r\n\r\nPerhaps that can be done by treating `'mode'` as special (or some other word). Then, you could set a top-level theme variable that `mode=\"custom\"` or something, which then caused each component to look outside of its immediate definitions to something somewhere else that's also provided by the inherited theme. In the case of a complete theming override, I think it's fair to assume that the consumer will have gone into the source of the component and looked at its internal structure, so you could pass an object that has the exact same structure as `fontSize` and `buttonColor`, there just needs to be a way to wire it up.\r\n\r\nIf nothing else, though, I really like the way this approach makes it easier for components to not hard-code so much of their CSS. I think the easier that is, the more people will use it, and the more shareable and reusable everyone's components will be by default."}}
{"actor":{"display_login":"geelen"}}