JSX for Designers

Picture of the author

Travis Arnold

Design Systems Frame.io

@souporserious

Design Handoff

Compilers

Static Analysis

1export default function App() {
2 return (
3 <figure className="bg-gray-100 rounded-xl p-8">
4 <img
5 className="w-32 h-32 rounded-full mx-auto"
6 src="/react-finland.jpg"
7 alt="React Finland Logo"
8 />
9 <div className="pt-6 text-center space-y-4">
10 <blockquote>
11 <p className="text-lg font-semibold">
12 “React makes it painless to create interactive UIs.
13 </p>
14 </blockquote>
15 <figcaption className="font-medium">
16 <div className="text-cyan-600">React</div>
17 <div className="text-gray-500">
18 A JavaScript library for building user interfaces
19 </div>
20 </figcaption>
21 </div>
22 </figure>
23 )
24}
Source: souporserious.com/use-babel-to-statically-analyze-jsx/

Style Props

1export default function App() {
2 return (
3 <Stack as="figure" background="gray.100" cornerRadius="xl">
4 <Image
5 source="/react-finland.jpg"
6 width="32px"
7 height="32px"
8 alt="React Finland Logo"
9 />
10 <Stack spaceYStart={6} spaceBetween={4}>
11 <Stack as="blockquote">
12 <Text size="large" weight="semibold">
13 “React makes it painless to create interactive UIs.
14 </Text>
15 </Stack>
16 <Stack as="figcaption">
17 <Text color="cyan.600">React</Text>
18 <Text color="gray.500">
19 A JavaScript library for building user interfaces
20 </Text>
21 </Stack>
22 </Stack>
23 </Stack>
24 )
25}
26

Compiler Components

TokensTransformsPolymorphism
VariantsAssetsOverrides
type TextProps = {
color: ThemeValue["colors"]
}
const Text = createComponent<TextProps>({
defaults: {
color: "foreground",
},
transforms: {
color: (value, theme) => theme.colors[value],
},
variants: {
heading1: {
defaults: {
size: "large",
},
web: {
as: "h1",
},
native: {
as: "Text",
accessibilityRole: "header",
},
},
},
platforms: {
web: {
as: "span",
},
native: {
as: "Text",
},
},
})

Tokens

In
<Stack space="large" background="brand" />
Out
<div
css={{
display: "flex",
flexDirection: "column",
flexShrink: 0,
padding: "40px",
background: "#8D6CEE",
}}
/>

Transforms

const transforms = {
width: (value) => ({
width: theme.lineLengths[value] || value,
}),
size: (value) => {
const systemFontSize = theme.fontSizes[value]
return {
fontSize: systemFontSize
? systemFontSize.default || systemFontSize
: value,
}
},
alignment: (value) => ({
textAlign: value,
}),
opacity: (value) => ({
opacity: value,
}),
shadow: (value) => ({
filter: value,
}),
color: (value) => ({
color: theme.colors[value] || value,
}),
}

Polymorphism

In
<Stack
as="button"
onClick={() => send({ type: "FETCH_DOG" })}
/>
Out
<button
onClick={() =>
send({
type: "FETCH_DOG",
})
}
css={{
display: "flex",
flexDirection: "column",
flexShrink: 0,
}}
/>

Variants

const variants = {
heading1: {
defaults: {
size: "xlarge",
},
web: {
as: "h1",
},
native: {
as: "Text",
accessibilityRole: "header",
},
},
body1: {
defaults: {
size: "medium",
letterSpacing: 0.8,
},
web: {
as: "p",
},
native: {
as: "Text",
},
},
}

Component Variants

In
<Text variant="heading3">
The Future Is Compiled
</Text>
Out
<h3
css={{
whiteSpace: "pre-wrap",
margin: 0,
cursor: "default",
fontSize: 48,
color: "rgba(255, 255, 255, 0.8)",
}}
>
The Future Is Compiled
</h3>

Prop Variants

In
<Text
size={[
["default", "medium"],
["breakpoints.large", "large"],
]}
/>
Out
<span
css={{
whiteSpace: "pre-wrap",
margin: 0,
cursor: "default",
color: "white",
fontSize: 48,
"@media screen and (min-width: 1200px)": {
fontSize: 72,
},
}}
/>

Assets

In
<Graphic
name="design-handoff-linear"
width="48vw"
/>
Out
<svg
height="700"
fill="none"
viewBox="0 0 1625 700"
width="48vw"
>
<g id="design-handoff-linear">
<path
id="arrow-2"
d="M1189 383L1114 339.699L1114 426.301L1189..."
fill="#CBABFE"
/>
...
</g>
</svg>

Platforms

const figmaVisitor = {
JSXElement(path, state) {
path.node.openingElement.attributes.push(
t.jsxAttribute(
t.jsxIdentifier("style"),
t.jsxExpressionContainer(
t.objectExpression(
state.styleProperties
)
)
)
)
},
}
const inkVisitor = {
JSXElement(path, state) {
path.node.openingElement.attributes =
path.node.openingElement.attributes.concat(
state.styleAttributes
)
},
}
Benefits
Design SystemsCross-PlatformImplementation AgnosticReports / LintingBest-In-Class DX/UX
Implications
Not everything can be compiledHarder to debugExisting systems

Why Is This Important

Offers consistencyFocus on better tasksFully declarativePit of success for all

Open Source

Glamor

JSXStyle

BlocksUI

React Primitives

Rebass

ThemeUI

What's Next?

Thank You

www.jsxui.com

@souporserious