Awesome
story2sketch 💎
Convert Storybook stories into Sketch symbols.
Uses the amazing
html-sketchapp
. Only supports web.
Quickstart
Firstly, get Sketch and npm. Then install asketch2sketch.sketchplugin
into Sketch:
Install story2sketch
:
npm i story2sketch -g
Run story2sketch
, pointing towards a Storybook iframe URL. You can find an existing iframe URL in Storybook by clicking 'Open canvas in new tab':
See configuration for more options, or if you have a lot of stories.
story2sketch --url https://localhost:9001/iframe.html --output stories.asketch.json
Import the generated file into Sketch via Plugins > From *Almost* Sketch to Sketch
in Sketch menu bar.
Success!
Storybook 3.x
If you're using Storybook 3.3 or above (but not Storybook 4 or above), you'll want to take full control of your Storybook webpack.config.js if you haven't already done so, adding:
module.exports = (storybookBaseConfig, configType) => {
const newConfig = {
...storybookBaseConfig
};
// Add this:
// Export bundles as libraries so we can access them on page scope.
newConfig.output.library = "[name]";
return newConfig;
};
Manually export the getStorybook
function in your ./config/storybook/config.js
file:
import { getStorybook } from "@storybook/react";
...
export { getStorybook }
Run story2sketch:
story2sketch --url https://localhost:9001/iframe.html --output stories.asketch.json
Why?
As stated by react-sketchapp
, it's complicated to manage assets in a design system. Many teams building design systems or component libraries already produce Sketch files for distributing designs and use Storybook to prototype and present the developed components. It can become difficult to keep designs up to date with the latest components, with designers ever playing catchup. story2sketch
generates a Sketch file from your components via Storybook, so your Sketch designs always stay up to date.
<a name="configuration"><a/>
Configuration
You can configure story2sketch
using the API via the CLI, configuring your package.json
or adding a story2sketch.config.js
file.
CLI
Simply call story2sketch
with options from the API.
$ story2sketch --stories all --output dist/great-ui.asketch.json
package.json
Add the following to your package.json:
{
"story2sketch": {
"stories": "all",
"output": "dist/great-ui.asketch.json"
}
}
story2sketch.config.js
Create a file called story2sketch.config.js
on the root of your project:
module.exports = {
output: "dist/great-ui.asketch.json",
stories: "all"
};
<a name="api"></a>
API
Parameter | Explanation | Input Type | Default |
---|---|---|---|
output | Specifies the filename for the generated asketch.json file or a folder when outputBy === 'kind'. | string | "dist/stories.asketch.json" |
input | The location of Storybook's generated iframe.html. Use this over url if possible for performance. | string | "dist/iframe.html" |
url | Storybook iframe URL. Will end in iframe.html . Prefer input for performance if possible. | string | "http://localhost:9001/iframe.html" |
stories | Stories to extract from Storybook. You should probably override the default. | object/string | "all" |
concurrency | Number of headless Chrome tabs to run in parallel. Defaults to number of threads available on your machine. | integer | dynamic |
symbolGutter | Gutter to place between symbols in Sketch. | integer | 100 |
viewports | Viewport configuration. Will be arranged left-to-right by width. Try to avoid changing the key, as this is used to identify the symbol. | object | Mobile viewport (320px wide) and desktop viewport (1920px wide). See example below. |
querySelector | Query selector to select your node on each page. Uses document.querySelectorAll . | string | "#root" |
verbose | Verbose logging output. | boolean | false |
fixPseudo | Attempt to insert real elements in place of pseudo-elements | boolean | false |
puppeteerOptions | Options to be passed directly to puppeteer.launch . See puppeteer docs for usage. | object | {} |
removePreviewMargin | Remove preview margin from the iframe body. | boolean | true |
layoutBy | Group symbols in the sketch output by the "kind" or "group" key | "kind" | "group" | null |
outputBy | Write multiple sketch files by "kind" or the "group" key | "kind" | "group" | null |
Example configurations
Basic
Automatically detect the stories, outputting two viewports for each story in a single Sketch file as symbols.
module.exports = {
output: "dist/great-ui.asketch.json",
input: "dist/iframe.html", // Same as default
pageTitle: "great-ui"
};
Manual stories
Manually define stories to have granular control over what stories are output. This might help if you're getting empty output, since some stories may break story2sketch.
module.exports = {
stories: [
{
kind: "Buttons/Button",
stories: [
{
name: "Button"
}
]
},
{
kind: "Buttons/ButtonGroup",
stories: [
{
name: "Default",
displayName: "Horizontal"
},
{
name: "Vertical"
}
]
},
{
kind: "Table",
stories: [
{
name: "Table"
}
]
}
]
};
Custom viewports
Output symbols based on custom viewports:
module.exports = {
viewports: {
narrow: {
width: 320,
height: 1200,
symbolPrefix: "Mobile/"
},
standard: {
width: 1920,
height: 1200,
symbolPrefix: "Desktop/"
}
}
};
Split output into multiple files based on kind
Outputs one file for each Storybook "kind". Useful if managing large component libraries, allowing you to distribute smaller files.
module.exports = {
output: "dist", // Define output directory. File names are defined by "kind"
outputBy: "kind" // Also supports "group", see below.
};
Layout based on kind
Renders the sketch layout by kind, but keeps them in one file.
module.exports = {
layoutBy: "kind" // Also supports "group", see below.
};
Split output into multiple files based on custom group
This example outputs two files based on a custom grouping: dist/Buttons.asketch.json
and dist/Data.asketch.json
.
module.exports = {
output: "dist",
outputBy: "group",
stories: [
{
group: "Buttons",
kind: "Buttons/Button",
stories: [
{
name: "Button"
}
]
},
{
group: "Buttons",
kind: "Buttons/ButtonGroup",
stories: [
{
name: "Default",
displayName: "Horizontal"
},
{
name: "Vertical"
}
]
},
{
group: "Data",
kind: "Table",
stories: [
{
name: "Table"
}
]
}
]
};
Continuous Integration
If you want story2sketch
to run in a CI environment you might have to add the following configuration to puppeteer in your story2sketch.config.js
.
module.exports = {
puppeteerOptions: {
args: ['--no-sandbox', '--disable-setuid-sandbox']
},
...
};
<a name="questions"><a/>
Questions
Why does my stuff look bad?
If your stuff looks bad, either it's not supported by html-sketchapp
yet (see support here), or you need to configure story2sketch.
Why don't you use react-sketchapp
instead of html-sketchapp
?
react-sketchapp
only supports React Native, or forces you to use React Native component naming conventions. html-sketchapp
supports good ol' fashioned HTML, and doesn't care what web framework you're using.
Can I use this on anything other than Storybook?
Not yet, but we have plans to add support for multiple and custom adaptors.