{"id":13468664,"url":"https://github.com/Nozbe/zacs","last_synced_at":"2025-03-26T05:31:21.710Z","repository":{"id":35098134,"uuid":"201777469","full_name":"Nozbe/zacs","owner":"Nozbe","description":"Zero Abstraction Cost Styling ⚡️👨‍🎨 (for React DOM \u0026 React Native)","archived":false,"fork":false,"pushed_at":"2023-08-28T11:06:09.000Z","size":18680,"stargazers_count":430,"open_issues_count":1,"forks_count":8,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-03-21T15:12:31.046Z","etag":null,"topics":["hacktoberfest","react","react-native"],"latest_commit_sha":null,"homepage":"","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/Nozbe.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-11T14:47:50.000Z","updated_at":"2025-02-22T11:21:40.000Z","dependencies_parsed_at":"2024-10-29T21:43:34.486Z","dependency_job_id":null,"html_url":"https://github.com/Nozbe/zacs","commit_stats":{"total_commits":316,"total_committers":4,"mean_commits":79.0,"dds":"0.044303797468354444","last_synced_commit":"9fcb77b75064d1f0165a8d52c0f12c064cd9fd46"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nozbe%2Fzacs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nozbe%2Fzacs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nozbe%2Fzacs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nozbe%2Fzacs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nozbe","download_url":"https://codeload.github.com/Nozbe/zacs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245597284,"owners_count":20641864,"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":["hacktoberfest","react","react-native"],"created_at":"2024-07-31T15:01:16.113Z","updated_at":"2025-03-26T05:31:16.700Z","avatar_url":"https://github.com/Nozbe.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/Nozbe/zacs/raw/master/assets/logo.png\" alt=\"ZACS: Zero Abstraction Cost Styling\" width=\"472\" /\u003e\n\u003c/p\u003e\n\n\u003ch4 align=\"center\"\u003e\n  👨‍🎨 Component styling with no performance penalty for React and React Native ⚡️\n\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"MIT License\"\u003e\n  \u003c/a\u003e\n  \u003c!--\n  \u003ca href=\"https://travis-ci.com/Nozbe/WatermelonDB\"\u003e\n    \u003cimg src=\"https://api.travis-ci.com/Nozbe/WatermelonDB.svg?branch=master\" alt=\"CI Status\"\u003e\n  \u003c/a\u003e--\u003e\n\n  \u003ca href=\"https://www.npmjs.com/package/@nozbe/zacs\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/@nozbe/zacs.svg\" alt=\"npm\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## What is `zacs`?\n\n| \u003ca href=\"https://youtube.com/watch?v=ryMvNklnDjU\"\u003e\u003cimg src=\"https://github.com/Nozbe/ZACS/raw/master/assets/youtube-thumbnail.jpg\" alt=\"React Native EU: A Successful Web \u0026 React Native Sharing Strategy\" width=\"400\" /\u003e\u003c/a\u003e |\n| ---- |\n| \u003cp align=\"center\"\u003e\u003ca href=\"https://youtube.com/watch?v=ryMvNklnDjU\"\u003e📺 \u003cstrong\u003eA Successful Web \u0026 React Native Sharing Strategy\u003c/strong\u003e\u003c/p\u003e |\n\n**ZACS** turns React components that look like this:\n\n```js\nimport zacs from '@nozbe/zacs'\nimport style from './style'\n\nconst Box = zacs.view(style.box, { isHighlighted: style.highlighted })\n\nconst rendered = \u003cBox isHighlighted /\u003e\n```\n\nInto **optimized** code that looks like this (**web**):\n\n```js\nconst rendered = \u003cdiv className={style.box + ' ' + style.highlighted} /\u003e\n```\n\nOr this (**React Native**):\n\n```js\nimport { View } from 'react-native'\n\nconst rendered = \u003cView style={[style.box, style.highlighted]} /\u003e\n```\n\n## Pitch\n\n**ZACS** (Zero Abstraction Cost Styling) is a super-fast component styling library for cross-platform React web and React Native apps.\n\n**Super-fast** as in: there is no difference between using ZACS and writing `\u003cdiv className\u003e` and `\u003cView style\u003e` manually. That's because **the library doesn't actually _exist_** at runtime, it's entirely implemented as a [Babel](https://babeljs.io) plugin, which compiles the \"styled components\" syntax down to bare metal.\n\nAnd because **ZACS** hides the API difference between web (DOM) and React Native, you can build a web and RN app with shared codebase without [`react-native-web`](https://github.com/necolas/react-native-web).\n\n## Installation\n\n```sh\nnpm install @nozbe/zacs\n```\n\nor\n\n```sh\nyarn add @nozbe/zacs\n```\n\nThen add ZACS to your Babel config (`.babelrc` or `babel.config.js`):\n\n```diff\n{\n  \"plugins\": [\n+   [\"@nozbe/zacs/babel\", {\n+     \"platform\": \"web\", // \"web\" or \"native\"\n+     \"production\": false / true, // pass `false` to enable debug attributes\n+     \"keepDeclarations\": false // (optional) pass `true` to keep zacs.xxx variable declarations in output\n+   }]\n  ]\n}\n```\n\n## Using `zacs`\n\n### Unstyled view or text\n\n```js\nimport zacs from '@nozbe/zacs'\n\nconst Box = zacs.view() // or zacs.view(null)\nconst Label = zacs.text()\n\nconst rendered = \u003cBox\u003e\u003cLabel\u003eHello\u003c/Label\u003e\u003c/Box\u003e\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  const rendered = \u003cdiv\u003e\u003cspan\u003eHello\u003c/span\u003e\u003c/div\u003e\n  ```\n\n  **React Native:**\n\n  ```js\n  import { View, Text } from 'react-native'\n\n  const rendered = \u003cView\u003e\u003cText\u003eHello\u003c/Text\u003e\u003c/View\u003e\n  ```\n\u003c/details\u003e\n\n### Simple styled view or text\n\n```js\nimport styles from './styles'\n\nconst Box = zacs.view(styles.box) // or zacs.text\n\nconst rendered = \u003cBox /\u003e\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  const rendered = \u003cdiv className={styles.box} /\u003e\n  ```\n\n  **React Native:**\n\n  ```js\n  import { View } from 'react-native'\n\n  const rendered = \u003cView style={styles.box} /\u003e\n  ```\n\u003c/details\u003e\n\n### Conditional styles\n\n```js\nconst Box = zacs.view(styles.box, {\n  isHighlighted: styles.isHighlighted,\n  isVisible: styles.isVisible,\n})\n\nconst rendered = \u003cBox isHighlighted={reactions \u003e 0} isVisible /\u003e\n```\n\nDeclare conditional styles as `{ [propName: string]: RNStyleOrClassName }`. If a specified prop is\npassed to the component with a truthy value, the corresponding style will be added.\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  const rendered = \u003cdiv className={styles.box\n                                  + ' ' + styles.isVisible\n                                  + (reactions \u003e 0) ? (' ' + styles.isHighlighted) : ''} /\u003e\n  ```\n\n  Please note:\n\n  - conditions are inlined whenever possible (when a constant is passed to a styling prop)\n  - styling props are removed from the compiled output\n\n  **React Native:**\n\n  ```js\n  import { View } from 'react-native'\n\n  const rendered = \u003cView style={[styles.box,\n                                styles.isVisible,\n                                reactions \u003e 0 \u0026\u0026 styles.isHighlighted]} /\u003e\n  ```\n\u003c/details\u003e\n\n### Adding style attributes (via individual props)\n\n```js\nconst Box = zacs.view(styles.box, null, {\n  width: 'width',\n  color: 'backgroundColor',\n})\n\nconst rendered = \u003cBox width={100} color=\"#80EADC\" /\u003e\n```\n\nDeclare CSS / React Native `StyleSheet` attributes available as component properties with `{ [propName: string]: CSSOrRNStyleAttributeName }`.\n\n**Gotcha:** If you pass a style attribute at all, *it will override* the main and conditional styles, even if the value is `undefined`.\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  const rendered = \u003cdiv className={styles.box} style={{ width: 100, backgroundColor: '#80EADC' }} /\u003e\n  ```\n\n  **React Native:**\n\n  ```js\n  import { View } from 'react-native'\n\n  const rendered = \u003cView style={[styles.box, { width: 100, backgroundColor: '#80EADC' }]} /\u003e\n  ```\n\u003c/details\u003e\n\n### Adding styles directly\n\n```js\nconst Box = zacs.view(styles.box)\n\nconst rendered = \u003cBox zacs:style={{ width: 100, color: '#80EADC' }} /\u003e\n```\n\nThis is equivalent to the example above, but instead of predefining list of props that turn into styles,\nwe pass styles directly. Note that this only works on ZACS components.\n\n### Multiple unconditional styles\n\n```js\nimport styles from './styles'\n\nconst TitleText = zacs.text([styles.text, styles.titleText])\n\nconst rendered = \u003cTitleText /\u003e\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  const rendered = \u003cspan className={styles.text + ' ' + styles.titleText} /\u003e\n  ```\n\n  **React Native:**\n\n  ```js\n  import { Text } from 'react-native'\n\n  const rendered = \u003cText style={[styles.text, styles.titleText]} /\u003e\n  ```\n\u003c/details\u003e\n\n### Styling custom components\n\n```js\nimport Touchable from 'components/Touchable'\n\nconst Button = zacs.styled(Touchable, styles.button, null, {\n  width: 'width'\n})\n\nconst rendered = \u003cButton width={500} /\u003e\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  import Touchable from 'components/Touchable'\n\n  const rendered = \u003cTouchable className={styles.button} style={{ width: 500 }} /\u003e\n  ```\n\n  **React Native:**\n\n  ```js\n  import Touchable from 'components/Touchable'\n\n  const rendered = \u003cTouchable style={[styles.button, { width: 500 }]} /\u003e\n  ```\n\u003c/details\u003e\n\n### Making stylable components\n\nTo define new components that you can style using `zacs.styled`, use the special `zacs:inherit` prop\nto let **ZACS** know you want styles from `props.style` / `props.className` added in.\n\n```js\nconst Root = zacs.view(styles.root)\n\nexport default const Touchable = props =\u003e {\n  return \u003cRoot zacs:inherit={props} /\u003e\n}\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  export default const Touchable = props =\u003e {\n    return \u003cdiv className={styles.root + ' ' + (props.className || '')} style={props.style} /\u003e\n  }\n  ```\n\n  **React Native:**\n\n  ```js\n  import { View } from 'react-native'\n\n  export default const Touchable = props =\u003e {\n    return \u003cView style={[styles.root].concat(props.style || [])} /\u003e\n  }\n```\n\u003c/details\u003e\n\n### Configuring output component/element types\n\nSometimes you need to style a different component on `web` and `native`. To do this, use\n`zacs.styled` with `{ web: ComponentType, native: ComponentType }` instead of a direct component reference.\n\n```js\nconst Paragraph = zacs.styled({ web: 'p', native: zacs.text }, styles.paragraph)\n\nconst rendered = \u003cParagraph\u003eHello world!\u003c/Paragraph\u003e\n```\n\nAs parameters, you can pass:\n- built in element type (string - `p`, `div`, `form`)\n- a component reference\n- magic `zacs.text` or `zacs.view` (this is so you can easily fall back to RN View/Text without importing `react-native`)\n\nIf you're only building for one platform, you can also reference built-ins like this:\n\n```js\nconst Paragraph = zacs.styled('p', styles.paragraph) // NOTE: No web/native, because this is web-only code\n```\n\n**TODO:** Passing `zacs.text/view` as parameter seems magic and gross. If you have a better idea for this API, let us know!\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  const rendered = \u003cp className={styles.paragraph}\u003eHello world!\u003c/p\u003e\n  ```\n\n  **React Native:**\n\n  ```js\n  import { Text } from 'react-native'\n\n  const rendered = \u003cText style={styles.paragraph}\u003eHello world!\u003c/Text\u003e\n  ```\n\u003c/details\u003e\n\n### Exporting ZACS components\n\n`zacs.view/text/styled` are special **declarations** for the compiler, not real components — that's the whole point of \"zero abstraction cost styling\".\n\nUnfortunately, this means that you can only use those components in the same file in which they're defined, and you can't export it. And this is how you should use `zacs` most of the time. But sometimes, to avoid repetitive code, you really need this.\n\nIn that case, use `zacs.createView/Text/Styled`, which actually creates a real component:\n\n```js\nexport const Box = zacs.createView(styles.box)\nexport const Label = zacs.createText(styles.label, {\n  isBold: style.labelBold,\n}, null, ['title', 'numberOfLines'])\nexport const Wrapper = zacs.createView(styles.wrapper, null, null, ['ref'])\n```\n\nYou must declare (in the last argument) all non-zacs props you want to be able to pass into the component you're styling (component props, DOM attributes, `ref`, and `zacs:inherit`, `zacs:style`).\n\n\u003cdetails\u003e\n  \u003csummary\u003eHey, that's really annoying, why do I need to do this?\u003c/summary\u003e\n\n  A distinction between `view` and `createView` is necessary because Babel is a single file compiler, and\n  it does not have visibility to imports, so an imported component can't be magically transformed into a `\u003cdiv /\u003e` or `\u003cView /\u003e`.\n\n  So we have to de-optimize and do the next best thing -- export an actual component. Not quite `zero abstraction cost`, but almost.\n\n  There is another limitation: because the declaration doesn't see the callsite, we don't know whether\n  someone wants to pass props (DOM attributes or View/Text RN props) to the underlying component,\n  and we can't use `{...props}`, because you can't pass arbitrary attributes to DOM elements in ReactDOM\n  (it will throw errors and can have unexpected side effects).\n\n  Honestly, needing to declare all used props is super annoying and I hate it. If you have a better idea on how to tackle this while staying as close as possible to the _zero abstraction cost_ ideal, **please let us know!**.\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003eSee compiled output\u003c/summary\u003e\n\n  **Web:**\n\n  ```js\n  export const Box = (props) =\u003e {\n    return \u003cdiv className={styles.box}\u003e{props.children}\u003c/div\u003e\n  }\n\n  export const Label = (props) =\u003e {\n    return (\n      // Note that `numberOfLines` is not passed on because it's not a DOM attribute\n      \u003cspan className={styles.label + (props.isBold ? ' ' + styles.labelBold : '')} title={props.title}\u003e\n        {props.children}\n      \u003c/span\u003e\n    )\n  }\n\n  // We add forwardRef if `ref` is an allowed attribute\n  export const Wrapper = React.forwardRef((props, ref) =\u003e {\n    return \u003cdiv className={styles.wrapper} ref={ref}\u003e{props.children}\u003c/div\u003e\n  })\n  ```\n\n  **React Native:**\n\n  ```js\n  import { View, Text } from 'react-native'\n\n  export const Box = (props) =\u003e {\n    return \u003cText style={styles.box}\u003e{props.children}\u003c/Text\u003e\n  }\n\n  export const Label = (props) =\u003e {\n    return (\n      \u003cText style={[styles.label, props.isBold \u0026\u0026 styles.labelBold]}\n            title={props.title}\n            numberOfLines={props.numberOfLines}\u003e\n        {props.children}\n      \u003c/Text\u003e\n    )\n  }\n\n  export const Wrapper = React.forwardRef((props, ref) =\u003e {\n    return \u003cText style={styles.wrapper} ref={ref}\u003e{props.children}\u003c/Text\u003e\n  })\n  ```\n\u003c/details\u003e\n\n### Style precedence\n\nFrom least important to most important:\n\n- main style (first argument to `zacs.xxx()`)\n- conditional styles (second argument to `zacs.xxx()`)\n- styles added via props (third argument to `zacs.xxx()`)\n- styles added via `zacs:style`\n- styles added via `zacs:inherit`\n\nFor example, `width` passed via `zacs:inherit` will override `width` added via props.\n\n## Defining styles\n\nUnlike popular \"CSS-in-JS\" libraries, `zacs` only provides the \"component styling\" part, but styles\nthemselves are defined in a separate file. Here is how you define them.\n\n**React Native**\n\n```js\n// style.native.js\nimport { StyleSheet } from 'react-native'\n\nexport default StyleSheet.create({\n  box: {\n    backgroundColor: \"#80EADC\",\n    width: 500,\n  },\n  highlighted: {\n    // ...\n  },\n})\n```\n\nSee [React Native documentation](https://facebook.github.io/react-native/docs/stylesheet) for more details.\n\n**Web**\n\nWe recommend using [PostCSS](https://postcss.org) in your Webpack config to make CSS styles importable from JS.\n\n```css\n/* style.web.css */\n.box {\n  background: #80EADC;\n  width: 500px;\n}\n\n.highlighted {\n  /* ... */\n}\n```\n\n**ZACS shared styles**\n\nWe're thinking of extending ZACS to defining styles, so that you can declare styles once in CSS and have them compile to both CSS and React Native StyleSheet in a \"zero abstraction cost\" fashion. If you're interested in this project — please contact us!\n\n## Troubleshooting\n\nWIP - Please contribute!\n\n## Contributing\n\n\u003cimg src=\"https://github.com/Nozbe/zacs/raw/master/assets/needyou.jpg\" alt=\"We need you\" width=\"220\" /\u003e\n\n**ZACS is an open-source project and it needs your help to thrive!**\n\nIf there's a missing feature, a bug, poor documentation, or other improvement you'd like, don't ask what we can do to help you, **ask what you can do to help the community**. Feel free to open an issue to get some guidance, and then please [send a pull request](https://github.com/Nozbe/zacs/compare) addressing your issue!\n\nIf you make a non-trivial contribution, email me, and I'll send you a nice ZACS sticker!\n\nIf you make an app using ZACS, please let us know!\n\n## Author and license\n\n**ZACS** was created by [@Nozbe](https://github.com/Nozbe). Main author and maintainer is [Radek Pietruszewski](https://github.com/radex).\n\n**ZACS** is available under the MIT license. See the [LICENSE file](./LICENSE) for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNozbe%2Fzacs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNozbe%2Fzacs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNozbe%2Fzacs/lists"}