{"id":23510880,"url":"https://github.com/danielkov/qcss","last_synced_at":"2025-05-13T17:17:43.957Z","repository":{"id":43986260,"uuid":"243509888","full_name":"danielkov/qcss","owner":"danielkov","description":"Framework agnostic styling pipeline and convenient use options.","archived":false,"fork":false,"pushed_at":"2023-01-05T08:28:23.000Z","size":1565,"stargazers_count":0,"open_issues_count":13,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-08T22:02:02.645Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielkov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-02-27T12:06:34.000Z","updated_at":"2020-03-02T15:24:51.000Z","dependencies_parsed_at":"2023-02-03T20:31:44.114Z","dependency_job_id":null,"html_url":"https://github.com/danielkov/qcss","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":"danielkov/single-package","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkov%2Fqcss","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkov%2Fqcss/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkov%2Fqcss/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielkov%2Fqcss/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielkov","download_url":"https://codeload.github.com/danielkov/qcss/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990506,"owners_count":21995776,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-25T12:12:41.633Z","updated_at":"2025-05-13T17:17:43.930Z","avatar_url":"https://github.com/danielkov.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Styling (name TBD)\n\n## Motivation\n\nCSS in JS is hard. A new way of styling in React appears at least once a month. Popular options out there tend to lean towards one or two different APIs and multiple different engines to do essentially the same thing. At the end of the day we just want to add some colour to our `div`s. What if you could **rely on the same API** for adding styles to your components while being free to **switch the underlying implementation** - even at run time? What if you could treat styling as a pipeline, instead of treating it like something static? Imagine coming up with a nice new pattern to convert your code into CSS and being able to **instantly apply** that to your styling library of choice, without having to fork the library.\n\nI propose all this is possible, and here's how:\n\n[![](https://mermaid.ink/img/eyJjb2RlIjoiZ3JhcGggVERcbkEoUHJvcHMpIC0tPiBCKHJlc29sdmUgLi4uUGx1Z2lucylcbkIgLS0-fENTU09iamVjdHwgQ3tFbmdpbmV9XG5DIC0tPnxDb21wb25lbnQgTWFwfCBEKFVpKVxuQyAtLT58Q29uc3RydWN0b3J8IEUoc3R5bGVkKVxuQyAtLT58TWFwcGVyfCBGKHJlc29sdmUpXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoibmV1dHJhbCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ)](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbkEoUHJvcHMpIC0tPiBCKHJlc29sdmUgLi4uUGx1Z2lucylcbkIgLS0-fENTU09iamVjdHwgQ3tFbmdpbmV9XG5DIC0tPnxDb21wb25lbnQgTWFwfCBEKFVpKVxuQyAtLT58Q29uc3RydWN0b3J8IEUoc3R5bGVkKVxuQyAtLT58TWFwcGVyfCBGKHJlc29sdmUpXG4iLCJtZXJtYWlkIjp7InRoZW1lIjoibmV1dHJhbCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ)\n\n## `combine`\n\nCreates generator for constructors that consume the output parsed by plugins\nThe returned function should be called with an engine that recieves a single\nparameter: `resolve`, which is a utility that pipes its input through its plugins\n\nBy default a \"passthrough\" plugin is applied in case the plugins array is empty.\n\n### Example with `styled-components`\n\n```ts\nimport combine from '@styling/core';\nimport styledEngine from '@styling/engine-styled-components';\n\nconst plugins = [];\n\nconst creator = combine(plugins);\n\nconst { styled } = creator(styledEngine);\n\nexport default styled;\n```\n\n### Example with `emotion`\n\n```ts\nimport combine from '@styling/core';\nimport emotionEngine from '@styling/engine-emotion';\n\nconst plugins = [];\n\nconst creator = combine(plugins);\n\nconst { styled } = creator(emotionEngine);\n\nexport default styled;\n```\n\n### Example with `glamorous`\n\n```ts\nimport combine from '@styling/core';\nimport glamorousEngine from '@styling/engine-glamorous';\n\nconst plugins = [];\n\nconst creator = combine(plugins);\n\nconst { styled } = creator(glamorousEngine);\n\nexport default styled;\n```\n\n## Engine\n\nAn engine is a consumer of resolve that returns the interface that should be interacted with when using the library. For example the most basic engine for React would be the following:\n\n```ts\nimport { generateUi } from '@styling/core';\n\nconst reactEngine = resolve =\u003e {\n  const styled = component =\u003e styles =\u003e ({ children, ...props }) =\u003e {\n    const style = resolve({ ...styles, ...props });\n    return React.createElement(component, { style }, children);\n  };\n\n  const Ui = generateUi(styled, resolve);\n\n  return { Ui, styled, resolve };\n};\n```\n\nThis concept means that the surface API (e.g.: props) depends entirely on what the plugins are doing and not on the engine, while the way of consumation is defined only by the engine, which allows you to switch engines seamlessly without having to change your components and maintain functionality so long as the plugins are working as intended.\n\nThis means that by having this setup code:\n\n```ts\n// @my-lib/styling\nimport combine from '@styling/core';\nimport alias from '@styling/plugin-alias';\nimport theme from '@styling/plugin-theme';\n\nconst plugins = [alias, theme];\n\nexport const creator = combine(plugins);\n```\n\nConsidering the following two examples:\n\n```tsx\n// @my-lib/styled-example\nimport { creator } from '@my-lib/styling';\nimport styledEngine from '@styling/engine-styled-components';\n\nconst { Ui } = creator(styledEngine);\n\nconst Example = () =\u003e (\n  \u003cUi.article m={5} p={10} d=\"flex\"\u003e\n    \u003cUi.h3 c={colors.title}\u003eExample\u003c/Ui.h3\u003e\n    \u003cUi.p c={colors.description}\u003eDescription of this example\u003c/Ui.p\u003e\n  \u003c/Ui.article\u003e\n);\n```\n\n```tsx\n// @my-lib/emotion-example\nimport { creator } from '@my-lib/styling';\nimport emotionEngine from '@styling/engine-emotion';\n\nconst { Ui } = creator(emotionEngine);\n\nconst Example = () =\u003e (\n  \u003cUi.article m={5} p={10} d=\"flex\"\u003e\n    \u003cUi.h3 c={colors.title}\u003eExample\u003c/Ui.h3\u003e\n    \u003cUi.p c={colors.description}\u003eDescription of this example\u003c/Ui.p\u003e\n  \u003c/Ui.article\u003e\n);\n```\n\nShould both yield the exact same result in terms of what the UI looks like. The only difference should be the underlying implementation, here named the `engine`.\n\n### Engine concepts\n\n#### _`Ui`_\n\nDefine components inline, while applying all the transformations defined\nby the plugins array. The input object that gets parsed is everything\nthat's passed as props.\n\nExample:\n\n```tsx\nimport { Ui } from '@my-lib/ui';\n\nconst MyArticle: FC = ({ title, description, image, ...rest }) =\u003e (\n  \u003cUi.article\n    d={flex}\n    align=\"center\"\n    justify=\"center\"\n    direction=\"column\"\n    {...rest}\n  \u003e\n    \u003cUi.h3 weight={400} color={colors.title}\u003e\n      {title}\n    \u003c/Ui.h3\u003e\n    \u003cUi.p letterSpacing={1.2}\u003e{description}\u003c/Ui.p\u003e\n    \u003cUi.img mw=\"100%\" src={image} /\u003e\n  \u003c/Ui.article\u003e\n);\n\nexport default MyArticle;\n```\n\n#### `styled`\n\nWorks the same way the `styled` constructor from `styled-components` or `emotion`.\nThis should be used to define styles on components outside render and to wrap\nalready existing components.\n\nExample:\n\n```tsx\nimport { styled } from '@my-lib/styled';\n\nimport MyArticle from 'previous-example';\n\nconst Article = styled(MyArticle)({\n  bg: 'purple',\n  c: 'white',\n});\n\nconst Example: FC = () =\u003e {\n  const { title, description, image } = useArticle();\n\n  return \u003cArticle title={title} description={description} image={image} /\u003e;\n};\n```\n\n#### `resolve`\n\nThe underlying mechanism, that applies the transformations from plugins\nto the style object. This is exported, to help interop between multiple\nstyle libraries.\n\nExample:\n\n```tsx\nimport { resolve } from '@my-lib/resolve';\nimport styled from 'styled-components';\n\nconst Example = styled.section(resolve);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielkov%2Fqcss","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielkov%2Fqcss","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielkov%2Fqcss/lists"}