Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@nozbe/zacs
Advanced tools
zacs
?ZACS turns React components that look like this:
import zacs from '@nozbe/zacs'
import style from './style'
const Box = zacs.view(style.box, { isHighlighted: style.highlighted })
const rendered = <Box isHighlighted />
Into optimized code that looks like this (web):
const rendered = <div className={style.box + ' ' + style.highlighted} />
Or this (React Native):
import { View } from 'react-native'
const rendered = <View style={[style.box, style.highlighted]} />
ZACS (Zero Abstraction Cost Styling) is a super-fast component styling library for cross-platform React web and React Native apps.
Super-fast as in: there is no difference between using ZACS and writing <div className>
and <View style>
manually. That's because the library doesn't actually exist at runtime, it's entirely implemented as a Babel plugin, which compiles the "styled components" syntax down to bare metal.
And 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
.
npm install @nozbe/zacs
or
yarn add @nozbe/zacs
Then add ZACS to your Babel config (.babelrc
or babel.config.js
):
{
"plugins": [
+ ["@nozbe/zacs/babel", {
+ "platform": "web", // "web" or "native"
+ "production": false / true, // pass `false` to enable debug attributes
+ "keepDeclarations": false // (optional) pass `true` to keep zacs.xxx variable declarations in output
+ }]
]
}
zacs
import zacs from '@nozbe/zacs'
const Box = zacs.view() // or zacs.view(null)
const Label = zacs.text()
const rendered = <Box><Label>Hello</Label></Box>
Web:
const rendered = <div><span>Hello</span></div>
React Native:
import { View, Text } from 'react-native'
const rendered = <View><Text>Hello</Text></View>
import styles from './styles'
const Box = zacs.view(styles.box) // or zacs.text
const rendered = <Box />
Web:
const rendered = <div className={styles.box} />
React Native:
import { View } from 'react-native'
const rendered = <View style={styles.box} />
const Box = zacs.view(styles.box, {
isHighlighted: styles.isHighlighted,
isVisible: styles.isVisible,
})
const rendered = <Box isHighlighted={reactions > 0} isVisible />
Declare conditional styles as { [propName: string]: RNStyleOrClassName }
. If a specified prop is
passed to the component with a truthy value, the corresponding style will be added.
Web:
const rendered = <div className={styles.box
+ ' ' + styles.isVisible
+ (reactions > 0) ? (' ' + styles.isHighlighted) : ''} />
Please note:
React Native:
import { View } from 'react-native'
const rendered = <View style={[styles.box,
styles.isVisible,
reactions > 0 && styles.isHighlighted]} />
const Box = zacs.view(styles.box, null, {
width: 'width',
color: 'backgroundColor',
})
const rendered = <Box width={100} color="#80EADC" />
Declare CSS / React Native StyleSheet
attributes available as component properties with { [propName: string]: CSSOrRNStyleAttributeName }
.
Gotcha: If you pass a style attribute at all, it will override the main and conditional styles, even if the value is undefined
.
Web:
const rendered = <div className={styles.box} style={{ width: 100, backgroundColor: '#80EADC' }} />
React Native:
import { View } from 'react-native'
const rendered = <View style={[styles.box, { width: 100, backgroundColor: '#80EADC' }]} />
const Box = zacs.view(styles.box)
const rendered = <Box zacs:style={{ width: 100, color: '#80EADC' }} />
This is equivalent to the example above, but instead of predefining list of props that turn into styles, we pass styles directly. Note that this only works on ZACS components.
import styles from './styles'
const TitleText = zacs.text([styles.text, styles.titleText])
const rendered = <TitleText />
Web:
const rendered = <span className={styles.text + ' ' + styles.titleText} />
React Native:
import { Text } from 'react-native'
const rendered = <Text style={[styles.text, styles.titleText]} />
import Touchable from 'components/Touchable'
const Button = zacs.styled(Touchable, styles.button, null, {
width: 'width'
})
const rendered = <Button width={500} />
Web:
import Touchable from 'components/Touchable'
const rendered = <Touchable className={styles.button} style={{ width: 500 }} />
React Native:
import Touchable from 'components/Touchable'
const rendered = <Touchable style={[styles.button, { width: 500 }]} />
To define new components that you can style using zacs.styled
, use the special zacs:inherit
prop
to let ZACS know you want styles from props.style
/ props.className
added in.
const Root = zacs.view(styles.root)
export default const Touchable = props => {
return <Root zacs:inherit={props} />
}
Web:
export default const Touchable = props => {
return <div className={styles.root + ' ' + (props.className || '')} style={props.style} />
}
React Native:
import { View } from 'react-native'
export default const Touchable = props => {
return <View style={[styles.root].concat(props.style || [])} />
}
Sometimes you need to style a different component on web
and native
. To do this, use
zacs.styled
with { web: ComponentType, native: ComponentType }
instead of a direct component reference.
const Paragraph = zacs.styled({ web: 'p', native: zacs.text }, styles.paragraph)
const rendered = <Paragraph>Hello world!</Paragraph>
As parameters, you can pass:
p
, div
, form
)zacs.text
or zacs.view
(this is so you can easily fall back to RN View/Text without importing react-native
)If you're only building for one platform, you can also reference built-ins like this:
const Paragraph = zacs.styled('p', styles.paragraph) // NOTE: No web/native, because this is web-only code
TODO: Passing zacs.text/view
as parameter seems magic and gross. If you have a better idea for this API, let us know!
Web:
const rendered = <p className={styles.paragraph}>Hello world!</p>
React Native:
import { Text } from 'react-native'
const rendered = <Text style={styles.paragraph}>Hello world!</Text>
zacs.view/text/styled
are special declarations for the compiler, not real components — that's the whole point of "zero abstraction cost styling".
Unfortunately, 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.
In that case, use zacs.createView/Text/Styled
, which actually creates a real component:
export const Box = zacs.createView(styles.box)
export const Label = zacs.createText(styles.label, {
isBold: style.labelBold,
}, null, ['title', 'numberOfLines'])
export const Wrapper = zacs.createView(styles.wrapper, null, null, ['ref'])
You 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
).
A distinction between view
and createView
is necessary because Babel is a single file compiler, and
it does not have visibility to imports, so an imported component can't be magically transformed into a <div />
or <View />
.
So we have to de-optimize and do the next best thing -- export an actual component. Not quite zero abstraction cost
, but almost.
There is another limitation: because the declaration doesn't see the callsite, we don't know whether
someone wants to pass props (DOM attributes or View/Text RN props) to the underlying component,
and we can't use {...props}
, because you can't pass arbitrary attributes to DOM elements in ReactDOM
(it will throw errors and can have unexpected side effects).
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!.
Web:
export const Box = (props) => {
return <div className={styles.box}>{props.children}</div>
}
export const Label = (props) => {
return (
// Note that `numberOfLines` is not passed on because it's not a DOM attribute
<span className={styles.label + (props.isBold ? ' ' + styles.labelBold : '')} title={props.title}>
{props.children}
</span>
)
}
// We add forwardRef if `ref` is an allowed attribute
export const Wrapper = React.forwardRef((props, ref) => {
return <div className={styles.wrapper} ref={ref}>{props.children}</div>
})
React Native:
import { View, Text } from 'react-native'
export const Box = (props) => {
return <Text style={styles.box}>{props.children}</Text>
}
export const Label = (props) => {
return (
<Text style={[styles.label, props.isBold && styles.labelBold]}
title={props.title}
numberOfLines={props.numberOfLines}>
{props.children}
</Text>
)
}
export const Wrapper = React.forwardRef((props, ref) => {
return <Text style={styles.wrapper} ref={ref}>{props.children}</Text>
})
From least important to most important:
zacs.xxx()
)zacs.xxx()
)zacs.xxx()
)zacs:style
zacs:inherit
For example, width
passed via zacs:inherit
will override width
added via props.
Unlike popular "CSS-in-JS" libraries, zacs
only provides the "component styling" part, but styles
themselves are defined in a separate file. Here is how you define them.
React Native
// style.native.js
import { StyleSheet } from 'react-native'
export default StyleSheet.create({
box: {
backgroundColor: "#80EADC",
width: 500,
},
highlighted: {
// ...
},
})
See React Native documentation for more details.
Web
We recommend using PostCSS in your Webpack config to make CSS styles importable from JS.
/* style.web.css */
.box {
background: #80EADC;
width: 500px;
}
.highlighted {
/* ... */
}
ZACS shared styles
We'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!
WIP - Please contribute!
ZACS is an open-source project and it needs your help to thrive!
If 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 addressing your issue!
If you make a non-trivial contribution, email me, and I'll send you a nice ZACS sticker!
If you make an app using ZACS, please let us know!
ZACS was created by @Nozbe. Main author and maintainer is Radek Pietruszewski.
ZACS is available under the MIT license. See the LICENSE file for more info.
FAQs
Zero Abstraction Cost Styling (for React DOM and React Native)
We found that @nozbe/zacs demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.