{"id":4539,"url":"https://github.com/adamterlson/cairn","last_synced_at":"2025-08-04T01:32:36.076Z","repository":{"id":57192706,"uuid":"44555361","full_name":"adamterlson/cairn","owner":"adamterlson","description":"Hierarchical shared and component-based style definitions with selector-based style application, for React Native","archived":false,"fork":false,"pushed_at":"2019-09-02T11:29:16.000Z","size":108,"stargazers_count":114,"open_issues_count":1,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-08T21:34:17.004Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adamterlson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-10-19T18:33:44.000Z","updated_at":"2024-07-11T23:05:29.000Z","dependencies_parsed_at":"2022-08-24T06:00:26.876Z","dependency_job_id":null,"html_url":"https://github.com/adamterlson/cairn","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/adamterlson/cairn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamterlson%2Fcairn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamterlson%2Fcairn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamterlson%2Fcairn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamterlson%2Fcairn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adamterlson","download_url":"https://codeload.github.com/adamterlson/cairn/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adamterlson%2Fcairn/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268636408,"owners_count":24282085,"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","status":"online","status_checked_at":"2025-08-03T02:00:12.545Z","response_time":2577,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-01-05T20:17:15.645Z","updated_at":"2025-08-04T01:32:35.690Z","avatar_url":"https://github.com/adamterlson.png","language":"JavaScript","funding_links":[],"categories":["Components"],"sub_categories":["Styling"],"readme":"# Cairn\n\n[![Build Status](https://travis-ci.org/adamterlson/cairn.svg?branch=master)](https://travis-ci.org/adamterlson/cairn)\n[![npm version](https://badge.fury.io/js/cairn.svg)](https://badge.fury.io/js/cairn)\n\nImprove the structure of your React Native stylesheets by creating shared, global styles and then extending \u0026 overriding them directly from your components.  Optionally add the provided middleware to get variable support, or write your own style-transforming middleware.  \n\nYou can even group all of your style-related attributes into one stylesheet, regardless of whether they're applied via the `style` attribute or a separate one (e.g. `underlayColor` on [`TouchableHighlight`](https://facebook.github.io/react-native/docs/touchablehighlight.html)).\n\nThese styles and props are applied with a simpler, yet more powerful, selector-based, spread syntax: `{...style('foo bar')}` instead of with the wordy (and sometimes super long) basic `style={[styles.foo, styles.bar]}` syntax.\n\nInstead of trying to shim a CSS preprocessor, Cairn embraces the power ([and advantages](https://facebook.github.io/react-native/docs/style.html)) of JavaScript-based styling. \n\nCairn plays well with [React StyleSheet](https://facebook.github.io/react-native/docs/stylesheet.html). See [Middleware](#style-prop-transformers-middleware)\n\nDependencies: None\n\n## Advantages\n\n1. **Component-specific stylesheets** - Create and apply styles right in your component which extend from a shared, global stylesheet.\n2. **\"Middleware\"** - Add support for variables, call React's `StyleSheet.create` and more in an unobtrusive way.  Write your own!\n3. **Parent-child entity relationships** - Get \"specificity\" in React Native by overriding parent entity types with child types which derive from them.\n4. **Apply style hierarchies en-masse** - Using strings instead of arrays of object references, refer to the aforementioned child entities and get all the parent styles as well.\n5. **Conditional selectors** - Conditionally apply specific styles using a `?` flag and a toggle boolean or hash of toggle values.\n6. **Set arbitrary style-related component props** - (e.g. `underlayColor` and `source`) Use the `props` keyword in your stylesheet to set any component prop!\n\nSee [Background](#background) section for more details on why you should use Cairn.\n\n\n## Install\n```\nnpm install --save cairn\n```\n\n## Basic Usage\n\nDefine your global component styles that are reusable across your entire application...\n\n```javascript\n// styles.js\n\nimport { StyleSheet } from 'react-native';\nimport cairn from 'cairn';\n\nexport default cairn({\n    first: {\n      backgroundColor: 'red',\n\n      child: {\n        backgroundColor: 'blue'\n      }\n    },\n    someImage: {\n      props: {\n        source: require('../someImage.png')\n      },\n      width: 100,\n      height: 50\n    }\n}, (styles) =\u003e StyleSheet.create(styles));\n```\n\n...then, import those global styles and (optionally) extend/override them with component-specific styles before spreading them onto the components.\n\n```jsx\n// components/MyComponent.js\n\nimport React from 'react-native';\nimport globalStyles from '../styles';\n\nconst style = globalStyles.extend({\n    second: {\n      height: 100,\n      width: 100\n    },\n    'first.child': {\n        backgroundColor: 'purple'\n    }\n});\n\nexport default () =\u003e (\n    \u003cView\u003e\n        /* Equal to:\n            \u003cView\n                style={[ \n                    globalStyles.first, \n                    globalStyles.first.child, \n                    style.first,\n                    style.first.child,\n                    style.second\n                ]}  \n            /\u003e\n        */\n        \u003cView {...style('first.child second')} /\u003e\n\n        /* Equal to:\n            \u003cImage\n                style={[ \n                    globalStyles.someImage\n                ]}\n                source={require('../someImage.png')}\n            /\u003e\n        */\n        \u003cImage {...style('someImage') /\u003e\n    \u003c/View\u003e\n);\n```\n\nSee the Examples folder for more usage help.\n\n## Style \u0026 Prop Transformers (\"Middleware\")\n\nCairn provides a chance to attach arbitrary transformational \"middleware\" for your styles and props.  These are also used for every call to `extend`.\n\nThe following are transformers included with Cairn.  See [Creating Custom Transformers](#creating-custom-transformers).\n\n- `compose(...transformers)` - Pass all transformers to be used and they will be called with the result of the former transformations in the order given.\n\n- `variables({ foo: 10, bar: {} })` - Pass a list of variables which can be referenced in your sheet, e.g. `{ one: '$foo', two: '$bar' }`.  The variable values are used by ref in place of the variable string, e.g. `{ one: 10, two: {} }`.\n\n```javascript\n  import cairn, { compose, variables } from 'cairn';\n\n  const vars = variables({ foo: 10, bar: 20 });\n\n  cairn(\n    {\n      thing: {\n        props: {\n          bar: '$bar'\n        },\n\n        foo: '$foo'\n      }\n    }, \n\n    // Style transformers\n    compose(\n      vars, \n      (styles) =\u003e StyleSheet.create(styles)\n    ),\n\n    // Prop transformers\n    vars\n  );\n```\n\n# API\n\n### `let style = cairn(stylesheet [, styleTransform, propTransform])`\n\nPass to `cairn` your stylesheet.  This **returns a new function** which is used to apply the styles to specific elements.\n\n**Parameters**\n* `stylesheet` - Object - The stylesheet of application styles.\n* `styleTransform` - Function - Optional - Called with flattened styles with props removed.  Expected return: the styles to be used.\n* `propTransform` - Function - Optional - Called with flattened props with styles removed.  Expected return: the props to be used.\n\n### `let moduleStyle = style.extend(moduleStylesheet)`\n\nCreate a new style function with access to the module-specific stylesheet.  The returned selector function has access to the extended stylesheet in addition to the global.  **No modifications are made to the global styles**. Uses the global `styleTransform` function if defined.  **Returns function used precisely the same as `cairn` itself.**\n\n**Parameters**\n* `moduleStylesheet` - Object - The stylesheet of module styles.\n\n```javascript\nimport { StyleSheet } from 'react-native';\nimport cairn from 'cairn';\n\n// styles.js\nconst globalStyles = cairn({ ... }, (styles) =\u003e StyleSheet.create(styles));\n\n// MyParentModule.js\nconst parentModuleStyles = globalStyles.extend({ ... });\n\n// MyChildModule.js\nconst childModuleStyles = parentModuleStyles.extend({ ... });\n```\n\n\u003eUsage of `parentModuleStyles` and `childModuleStyles` is precisely the same as `globalStyles`.  See [`style(selectors)`](#style-selectors) below.\n\n### `style(selectors)`\n\nApply styles by passing a space-delimited string to `style`  (the function returned from `cairn`) and then spread the result onto a component.  Selected styles are appended in order with last item having precedence. Selectors without a style definition will be ignored.\n\n\u003e `style` returns an object containing all the properties it will set on the component, so if you do not wish to use the spread syntax, you can access and apply `style` and other props directly:\n\u003e `\u003cTouchableHighlight style={style('foo').style} underlayColor={style('bar').underlayColor} /\u003e`.\n\n#### Types of Selectors\n\n**Basic: `style('foo')`**\n\nApply just the specified class by name.\n\n**Hierarchy: `style('foo.bar.baz')`**\n\nApply an entire hierarchy of classes at once.  The above is equivalent to `style={[styles.foo, styles.foo.bar, styles.foo.bar.baz]}`.\n\n**Conditional: `style('foo bar? baz?', toggle)`**\n\nConditionally apply a style based on the state of toggle.  If toggle is a boolean, the boolean's value will be used.  If an object is given, the corresponding key in the object will be used instead.  Map the class to a new property by defining the mapped key after the `?` operator: `foo?newName`.\n\n```jsx\n\u003cText {...style('p complete? error?', false)}\u003e...\u003c/Text\u003e\n\n// or:\n\u003cText {...style('p complete?name error?', { name: 'Bob', error: 'Too Short!' })}\u003e...\u003c/Text\u003e\n```\n\n**Inline: `style('foo', [{ color: 'red' }])`**\n\nSometimes necessary, inline styles can be appended via an array of inline styles or stylesheet references.  Not recommended.\n\n\n### All Selector Types\n\n```javascript\nclass MyView extends Component {\n  render() {\n    return (\n      \u003cView\u003e\n        \u003cText {...style('header')}\u003e\n          Primary Heading\n        \u003c/Text\u003e\n        \u003cText {...style('header.secondary')}\u003e\n          Primary and Secondary Heading\n        \u003c/Text\u003e\n        \u003cText {...style('error?', false)}\u003e\n           Never styled via 'error'\n        \u003c/Text\u003e\n        \u003cText {...style('error?incomplete', {\n          incomplete: true\n        })}\u003e\n          Styled via 'error' if incomplete\n        \u003c/Text\u003e\n        \u003cText {...style('header', [{\n          color: 'red'\n        }])}\u003e\n          Inline override. :(\n        \u003c/Text\u003e\n      \u003c/View\u003e\n    );\n  }\n}\n```\n\n## Creating Custom Transformers\n\nIn order for Cairn to work unobtrusively with React's `StyleSheet.create` (and even some other enhanced-styling-features-related-libraries), all props must be separated from styles, flattened, and transformed separately.\n\n### `transformer(stylesOrProps)`\n\n```javascript\nconst styleTransformer = styles =\u003e {\n  // styles ==\u003e {\n  //  'parent': {\n  //    someParentStyle: 'value'\n  //  },\n  //  'parent.child': {\n  //    someChildStyle: 'value'\n  //  }\n  // }\n  return styles;\n};\nconst propTransformer = props =\u003e {\n  // props ==\u003e {\n  //  'parent': {\n  //    someParentProp: 'value'\n  //  },\n  //  'parent.child': {\n  //    someChildProp: 'value'\n  //  }\n  // }\n  return props;\n};\nconst styles = cairn({ \n  parent: {\n    props: {\n      someChildProp: 5\n    },\n    someParentStyle: 'value',\n\n    child: {\n      props: {\n        someChildProp: 10\n      }\n      someChildStyle: 'value'\n    }\n  }\n}, styleTransformer, propTransformer);\n```\n\n\u003e The passed in props and styles objects should not themselves be modified.\n\nThe returned value from a transformer is what is actually made available to subsequent selector calls via `style` and ultimately applied to your components.\n\n\n## Background\n\nStyling in React Native is a mixed blessing.  On one hand you have the power of JS-driven styling which is superior in many ways.  There are some issues with it however:\n\n### Composing styles with arrays\n\nIn React Native, there is no cascading of styles, so when you have multiple types of something, say a pageContainer, most will begin by styling each individually:\n\n```javascript\n{\n  'pageContainer': {\n    flex: 1,\n    marginTop: 10,\n    marginBottom: 10\n  },\n  'pageContainerWithHeader': {\n    flex: 1,\n    marginTop: 20\n  },\n  'pageContainerWithFooter': {\n    flex: 1,\n    marginBottom: 20\n  },\n  'pageContainerWithHeaderAndFooter': {\n    flex: 1,\n    marginTop: 20,\n    marginBottom: 20\n  }\n}\n....\n\u003cView style={styles.pageContainerWithHeaderAndFooter}\u003eBody Text\u003c/View\u003e\n```\n\nThis can be improved upon by extracting out the common bits into their own classes and using an array of styles to apply instead:\n\n```javascript\n{\n  'pageContainer': {\n    flex: 1,\n    marginTop: 10,\n    marginBottom: 10\n  },\n  'pageContainerWithHeader': {\n    marginTop: 20\n  },\n  'pageContainerWithFooter': {\n    marginBottom: 20\n  }\n}\n....\n\u003cView style={[\n  styles.pageContainer,\n  styles.pageContainerWithHeader,\n  styles.pageContainerWithFooter ]}\u003e\n  Body Text\n\u003c/View\u003e\n```\n\nThis is better, there's less redundancy in the styles, but the length of the class array can very quickly get out of hand when each bit of style is separated.  Additionally, there is a redundancy of \"pageContainer\" in the stylesheet.  What we need is a way to get the best of both worlds:\n\n1) Be able to separate our stylesheet out into a set of parent types and child types that extend their parents, and\n\n2) Reference this child type directly and get all the parent styling for free without having to compose it manually.\n\n\n### Module-specific styles\n\nIn React Native, there is no support for a module-specific stylesheet.  Or no support for a global stylesheet.  You choose how to look at it.  It's possible to define two separate stylesheets and then, when applying styles to your component, select which stylesheet you wish to draw styles from or compose your styles array from both:\n\n`\u003cView styles={[ global.foo, componentSpecific.bar ]} /\u003e`\n\nOr you could export just the JS object globally and extend it in your component when you call `StyleSheet.create`.\n\nHowever, if you were to create such a system, there's no way to functionally extend/override styles, you simply need to apply them in order every time.  Take this to a third level and you see how unmanageable such a setup becomes.\n\nIdeally we'd have a way to set default styles then simply extend them with modifiers and additions specific to the module we're working on.\n\n\n### Style-related props that cannot be set via `style=`\n\nSome components in React Native define presentation attributes that cannot be set with the `styles` attribute, for example TouchableHighlight's `underlayColor`.  \n\nIn this example, in order to reference colors centralized in your stylesheet and then use them elsewhere on these components, you must export colors in addition to your stylesheet.  \n\nIdeally we want the definition of styles in one place, regardless of what property on our component is being set.\n\n...\n\nCairn addresses all of these issues!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamterlson%2Fcairn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadamterlson%2Fcairn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadamterlson%2Fcairn/lists"}