{"id":13989665,"url":"https://github.com/ExodusMovement/shakl","last_synced_at":"2025-07-22T11:31:09.051Z","repository":{"id":57340464,"uuid":"133265887","full_name":"ExodusMovement/shakl","owner":"ExodusMovement","description":"A utility to create styled components in React Native.","archived":false,"fork":false,"pushed_at":"2024-11-19T11:08:51.000Z","size":346,"stargazers_count":20,"open_issues_count":2,"forks_count":3,"subscribers_count":28,"default_branch":"master","last_synced_at":"2024-11-19T12:36:47.461Z","etag":null,"topics":["react-native","styled-components"],"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/ExodusMovement.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":"2018-05-13T19:00:08.000Z","updated_at":"2024-11-04T17:29:03.000Z","dependencies_parsed_at":"2022-09-26T16:33:22.061Z","dependency_job_id":"b12cfa5b-0008-4466-b227-d701192f96b1","html_url":"https://github.com/ExodusMovement/shakl","commit_stats":{"total_commits":119,"total_committers":4,"mean_commits":29.75,"dds":0.4117647058823529,"last_synced_commit":"af85c966ba8a9da1c1c99518322fb09cc1e7e76d"},"previous_names":["sonaye/shakl","sonaye/react-native-styled"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExodusMovement%2Fshakl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExodusMovement%2Fshakl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExodusMovement%2Fshakl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExodusMovement%2Fshakl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ExodusMovement","download_url":"https://codeload.github.com/ExodusMovement/shakl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227079544,"owners_count":17728031,"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":["react-native","styled-components"],"created_at":"2024-08-09T13:01:55.885Z","updated_at":"2024-11-29T08:32:01.246Z","avatar_url":"https://github.com/ExodusMovement.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Shakl\n\n[![npm version](https://badge.fury.io/js/shakl.svg)](https://badge.fury.io/js/shakl) [![Build Status](https://travis-ci.org/sonaye/shakl.svg?branch=master)](https://travis-ci.org/sonaye/shakl)\n\n\u003cimg src=\"shakl.svg\" alt=\"Shakl logo\" width=\"128\"\u003e\n\n## Features\n\n- Exposes basic [primitives](#primitives) such as `View`, `Text` and `Touchable`,\n- Supports static and [dynamic styles](#dynamic-styles) (based on props).\n- Supports component extension using [`extend()`](#extending-styles) and [`attrs()`](#custom-props).\n- Supports component composition using [`withComponent()`](#wrapping-another-component) and [`withChild()`](#wrapping-a-child).\n- Uses regular inline styles under the hood (performance boost).\n- Works [with React DOM](#usage-with-react-dom) too, same simple API, same benefits.\n- No dependencies, all just React goodness.\n- ~2 KB in size, with less than 100 lines of code.\n\n### Benchmark\n\nTime required to create a simple styled component (in ms).\n\n\u003cimg src=\"benchmark/results.png\" alt=\"Shakl Benchmark\" width=\"600\"\u003e\n\n## Install\n\n```bash\nyarn add shakl\n```\n\n## Usage\n\n### Creating a styled component\n\n```js\nimport styled from 'shakl'\n\nconst Foo = styled(View)({ flex: 1 })\n\n\u003cFoo /\u003e // \u003cView style={{ flex: 1 }} /\u003e\n```\n\n### Primitives\n\nBy default, React Native's `View`, `Text`, and `TouchableOpacity` are exposed to you, they can be directly used.\n\n```js\nstyled.View({ flex: 1 })\nstyled.Text({ color: 'blue' })\nstyled.Touchable({ padding: 10 })\n\n// equivalent to\nstyled(View)({ flex: 1 })\nstyled(Text)({ color: 'blue' })\nstyled(TouchableOpacity)({ padding: 10 })\n```\n\n### Dynamic styles\n\n```js\nconst Foo = styled.View((props) =\u003e ({ padding: props.padded ? 10 : 0 }))\n\n\u003cFoo /\u003e // \u003cView style={{ padding: 0 }} /\u003e\n\u003cFoo padded /\u003e // \u003cView style={{ padding: 10 }} /\u003e\n```\n\n### Extending styles\n\n```js\nconst Title = styled.Text({ fontSize: 20 })\n// \u003cText style={{ fontSize: 20 }} /\u003e\n\nconst BoldTitle = Title.extend({ fontWeight: 'bold' })\n// \u003cText style={{ fontSize: 20, fontWeight: 'bold' }} /\u003e\n\nconst RedBoldTitle = BoldTitle.extend({ color: 'red' })\n// \u003cText style={{ fontSize: 20, fontWeight: 'bold', color: 'red' }} /\u003e\n\nconst RedHeadline = styled(RedBoldTitle)({ fontSize: 28 }) // this works too\n```\n\n### Custom props\n\n```js\nconst Foo = styled.Text({ color: 'blue' }).attrs({ numberOfLines: 1 })\n// \u003cText style={{ color: 'blue' }} numberOfLines={1} /\u003e\n\n// attrs({ .. }) only overwrites the `defaultProps` of the component\n// for dynamic props however we can pass it a function\n\nconst MyText = styled.Text({ color: 'red' }).attrs((props) =\u003e ({\n  numberOfLines: props.oneLiner ? 1 : 3\n}))\n\n// equivalent to\n\u003cMyText /\u003e // \u003cText style={{ color: 'red }} numberOfLines={3} /\u003e\n\u003cMyText oneLiner /\u003e // \u003cText style={{ color: 'red }} numberOfLines={1} /\u003e\n```\n\n### Wrapping another component\n\n```js\nconst Button = styled(TouchableOpacity)({ flex: 1 })\nconst HighlightedButton = Button.withComponent(TouchableHighlight)\n\n// equivalent to\nconst Button = (props) =\u003e \u003cTouchableOpacity style={{ flex: 1 }} {...props} /\u003e\n\nconst HighlightedButton = (props) =\u003e (\n  \u003cTouchableHighlight style={{ flex: 1 }} {...props} /\u003e\n)\n```\n\n### Wrapping a child\n\n```js\nconst ButtonText = styled.Text({ color: 'blue' })\nconst Button = styled.Touchable({ flex: 1 }).withChild(ButtonText)\n\n// equivalent to\nconst Button = ({ children, ...props }) =\u003e (\n  \u003cTouchableOpacity style={{ flex: 1 }} {...props}\u003e\n    \u003cText style={{ color: 'blue' }}\u003e{children}\u003c/Text\u003e\n  \u003c/TouchableOpacity\u003e\n)\n\n// to pass a ref to a child, use the `childRef` prop on the parent\n// to pass custom props to a child, use `.withChild(Child, childProps)`\nconst CardText = styled.Text({ color: 'blue' })\nconst Card = styled.View({ flex: 1 }).withChild(CardText, { numberOfLines: 3 })\n\n// equivalent to\nconst Card = ({ children, childRef, ...props }) =\u003e (\n  \u003cView style={{ flex: 1 }} {...props}\u003e\n    \u003cText ref={childRef} style={{ color: 'blue' }} numberOfLines={3}\u003e\n      {children}\n    \u003c/Text\u003e\n  \u003c/View\u003e\n)\n\n// you can also access parent props by passing a function `.withChild(Child, (parentProps) =\u003e childProps)`\nconst Card = styled.View({ flex: 1 }).withChild(CardText, (parentProps) =\u003e ({\n  numberOfLines: parentProps.onLiner ? 1 : 3\n}))\n\n\u003cCard /\u003e\n// \u003cView ..\u003e\n//   \u003cText ..\u003e{children}\u003c/Text\u003e\n// \u003c/View\u003e\n\n\u003cCard onLiner /\u003e\n// \u003cView ..\u003e\n//   \u003cText numberOfLines={1} ..\u003e{children}\u003c/Text\u003e\n// \u003c/View\u003e\n```\n\n### Using refs\n\n```js\nconst List = styled(FlatList)({ flex: 1 })\n\n\u003cList ref={this.list} /\u003e // based on React's forwardRef API (16.3.0)\n\n// this.list.scrollTo({ y: 0 })\n// or this.list.current.scrollTo({ y: 0 }) (with React.createRef)\n```\n\n### Using a custom display name for debugging\n\n```js\nstyled(View, { name: 'YetAnotherView' })\n```\n\nDefault display names are `styled(View)`, `styled(Text)`, `styled(Touchable)`, `styled(Component)`, etc.\n\n### Using propTypes and defaultProps\n\n```js\nconst Foo = styled.View({ flex: 1 })\n\nFoo.propTypes = { .. }\nFoo.defaultProps = { .. }\n```\n\n### Usage with React DOM\n\nShakl is internally decoupled from React Native and can be used in the DOM.\n\n```js\nimport styled from 'shakl'\n\n// no exposed primitives however, feel free to add your own\nconst styled.div = styled('div')\n\nconst Foo = styled.div({ background: '#eee' })\n\n\u003cFoo /\u003e // \u003cdiv style={{ background: '#eee' }} /\u003e\n```\n\n[![Edit shakl](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/52kw0jrn94?module=%2Fsrc%2FExample.js)\n\n### Testing with Jest\n\nSince Shakl supports both React Native and the DOM, we need to explicitly tell Jest that we are testing for React Native, otherwise it will test against the DOM by default, which causes exposed primitives to fail (`.. shakl.default.View is not a function ..` error).\n\n```js\n// add the follwing to your Jest configuration when testing for React Native\n\"moduleNameMapper\": {\n  \"shakl\": \"\u003crootDir\u003e/node_modules/shakl/lib/rn.js\"\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FExodusMovement%2Fshakl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FExodusMovement%2Fshakl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FExodusMovement%2Fshakl/lists"}