create-atomic-resources
v4.4.3
Published
Creates a scss framework for use with atomic-bomb & Storybook templates
Maintainers
Readme
Creates a reusable SCSS resources setup for projects that use atomic design, Storybook, and optionally atomic-bomb.
Usage
Run the package from the root of the project that should receive the resources:
npx create-atomic-resources ./srcThe command expects one argument:
npx create-atomic-resources <destination-dir>Example:
npx create-atomic-resources ./srcUpdating Existing Resources
Run the latest release against the same destination to update the bundled resource files:
npx create-atomic-resources@latest ./srcIf src/resources/design/tokens.json already exists, the installer asks before replacing it. Press Enter or answer n to keep the existing project tokens; the default is no. Answer y or yes to replace it with the latest bundled token file. Other resource files are updated either way.
For web projects, this creates or updates:
src/
└── resources/
├── design/
│ ├── tokens.example.json
│ └── tokens.json
├── fonts/
└── styles/
├── fonts/
├── functions/
├── headings/
├── page/
├── reset/
├── root/
├── tokens/
├── utility/
├── vars/
└── main.scssFor Expo or React Native projects, the installer detects expo or
react-native in package.json and creates a native-only resource tree instead:
src/
├── hooks/
│ └── useFont/
└── resources/
├── design/
│ └── tokens.json
├── fonts/
│ └── arial.ttf
├── scripts/
│ └── tokens-to-native.mjs
└── styles/
├── colors.ts
├── fonts.ts
├── index.ts
└── main.tsWhat It Does
The installer:
- copies the bundled
resourcesdirectory into the destination directory - installs
json-to-scss,sass, andprettieras dev dependencies for web projects - installs only
prettieras a dev dependency for Expo or React Native projects - detects
pnpm-lock.yaml,yarn.lock, orpackage-lock.jsonto choose the package manager - defaults to
npmwhen no lockfile exists - retries npm installs with
--legacy-peer-depswhen npm reports anERESOLVEpeer dependency conflict - preserves an existing destination
tokens.jsonunless replacement is explicitly confirmed - adds resource scripts to the project
package.json
The generated web scripts are:
{
"token": "json-to-scss ./src/resources/design/tokens.json ./src/resources/styles/tokens/_tokens.scss",
"scss": "sass --quiet ./src/resources/styles/main.scss ./src/resources/styles/main.css",
"nice": "prettier -w ./src/**"
}The generated Expo or React Native scripts are:
{
"token:native": "node ./src/resources/scripts/tokens-to-native.mjs",
"token-to-native": "node ./src/resources/scripts/tokens-to-native.mjs",
"nice": "prettier -w ./src/**"
}The Design Token
The design token file is the source of truth for the generated SCSS token map:
src/resources/design/tokens.jsonThe token script converts that JSON file into:
src/resources/styles/tokens/_tokens.scssThe generated _tokens.scss file exports a $tokens map. src/resources/styles/tokens/_config.scss reads that map and exposes typed SCSS variables such as $colors, $fonts, $headings, $theme, $spacing, $font-sizes, $border-radius, $box-shadow, $semantic-colors, $z-index, $opacity, and $forms.
Token File Shape
The token file is plain JSON. Keys use the names that the bundled SCSS expects, so keep the top-level names stable unless you also update the SCSS modules that read them.
{
"unit": "rem",
"page": {},
"colors": [],
"theme": {},
"fonts": [],
"headings": {},
"spacing": {},
"fontSizes": { "generic": {}, "semantic": {} },
"borderRadius": {},
"boxShadow": {},
"semanticColors": {},
"zIndex": {},
"opacity": {},
"forms": {}
}Top-level token groups:
unit: the base unit label used by the design system. The bundled file usesrem.page: page-level defaults.backgroundColor,margin, andpaddingare used by the root/page styles.colors: an array of named color entries. Each entry has atype, a CSS color value incolor, and an optionalshadesarray.theme: descriptive component or content styling groups. Group keys and item keys are flexible, and all item options are optional.fonts: an array of named font entries. Each entry has atype, a font fileuri, and supportedsizes.headings: heading font metadata. It contains a sharedtype, a fonturi, and avariantarray withh1throughh6size values.spacing: named spacing scale values.fontSizes: generic size values and semantic aliases that reference the generic scale.borderRadius: named radius values. The generated SCSS exposes this group as$border-radius.boxShadow: named CSS shadow values.semanticColors: named intent colors for UI states such assuccess,warning,danger, andinfo.zIndex: named stacking values for layers such as dropdowns, sticky elements, modals, and toasts.opacity: named opacity values for disabled, muted, and overlay states.forms: form-control tokens split intoinput,focus,disabled, anderrorgroups.
Colors
Color tokens are an array so the SCSS can generate utility classes from each entry:
{
"type": "bright-green-100",
"color": "rgb(146, 191, 48)",
"shades": [20, 30]
}For every color entry, src/resources/styles/tokens/_config.scss generates:
.bg-{type}
.fg-{type}For example, a color with "type": "bright-green-100" creates .bg-bright-green-100 and .fg-bright-green-100.
Theme
Theme tokens describe reusable visual recipes that reference the color and spacing scales:
{
"theme": {
"buttons": {
"primary": {
"backgroundColor": "bright-green-100",
"foregroundColor": "black",
"iconColor": "black",
"paddingHorizontal": "medium",
"paddingVertical": "small",
"gap": "small",
"fontName": "label-text",
"fontSize": "label",
"borderRadius": "medium",
"cursor": "pointer",
"width": "fit-content"
}
},
"links": {},
"headings": {}
}
}Theme group keys and item keys are flexible. The example above generates a .theme-buttons-primary class and can also be used as a mixin:
@use './src/resources/styles/utility' as utility;
.button {
@include utility.theme(buttons, primary);
}Supported item options are optional:
backgroundColor
hoverColor
foregroundColor
textColor
iconColor
paddingHorizontal
paddingVertical
gap
fontName
fontSize
borderRadius
cursor
widthColor options should use names from colors. hoverColor may also use a generated shade name such as bright-green-100-dark-20; when it is omitted and backgroundColor has a configured 20 shade, the hover background uses that shade's CSS variable. Otherwise it falls back to a computed 20% darker color. Spacing options should use names from spacing. fontName accepts a name from fonts, or a direct CSS font-family value. fontSize accepts generic or semantic names from fontSizes, or a direct CSS font-size value. borderRadius accepts a name from borderRadius, or a direct CSS radius. cursor accepts any CSS cursor value.
Font Sizes
Generic font sizes hold reusable values. Semantic font sizes alias that generic scale for usage-based names:
{
"fontSizes": {
"generic": {
"small": "0.875rem",
"medium": "1rem",
"large": "1.25rem"
},
"semantic": {
"caption": "small",
"label": "small",
"body": "medium",
"title": "large"
}
}
}These generate CSS custom properties such as --font-size-small and --font-size-body. Semantic custom properties reference their generic counterparts.
Fonts
Font tokens describe available text families and sizes:
{
"type": "main-text-regular",
"uri": "'../fonts/freesans'",
"sizes": ["0.75rem", "0.875rem", "1rem", "1.125rem", "1.25rem"]
}The uri points to the generated resources font path from the compiled CSS. Keep the quotes inside the JSON string when the value should be emitted as a Sass string.
Headings
Heading tokens define the heading font and the size for each heading level:
{
"type": "heading",
"uri": "'../fonts/freesansbold'",
"variant": [
{ "h1": "2.625rem" },
{ "h2": "2rem" },
{ "h3": "1.75rem" },
{ "h4": "1.625rem" },
{ "h5": "1.5rem" },
{ "h6": "1.375rem" }
]
}Forms
Form tokens keep the default, focus, disabled, and error styles together:
{
"forms": {
"input": {
"height": "2.5rem",
"padding-x": "0.75rem",
"padding-y": "0.5rem",
"border": "1px solid #d0d0d0",
"border-radius": "0.5rem",
"background-color": "#fff",
"text-color": "#161616",
"placeholder-color": "rgba(0, 0, 0, 0.45)"
},
"focus": {
"border": "1px solid #0288d1",
"outline-color": "rgba(2, 136, 209, 0.32)",
"outline-width": "0.1875rem"
},
"disabled": {},
"error": {}
}
}Use the same hyphenated keys that appear in the bundled example when a CSS property name needs to be represented directly.
Example Token File
A complete example token file is included at:
src/resources/design/tokens.example.jsonUse it as a reference when changing tokens.json, or copy it over tokens.json in a generated project before running the token build.
Requirements
- Node.js
- npm, pnpm, or Yarn
- a
package.jsonin the directory where the command is run
The command should be run from the application root, not from inside the destination folder.
Storybook
Import the generated stylesheet in .storybook/preview.[js|ts]:
import "../src/resources/styles/main.css"If you install into a different destination directory, adjust the import path to match that directory.
Design Tokens
The design token file should live in:
src/resources/design/tokens.jsonAfter changing tokens.json, rebuild the SCSS token file:
npm run tokenThis runs the json-to-scss tooling that converts tokens.json into src/resources/styles/tokens/_tokens.scss. To run it directly:
npx json-to-scss ./src/resources/design/tokens.json ./src/resources/styles/tokens/_tokens.scssThen rebuild the compiled stylesheet:
npm run scssSCSS
The main SCSS entrypoint is:
src/resources/styles/main.scssThe compiled CSS output is:
src/resources/styles/main.cssThe bundled SCSS uses Dart Sass modules with @use and avoids deprecated global Sass APIs.
Flex Mixins
The utility module exposes flex positioning mixins for common layout alignment:
@use './src/resources/styles/utility' as utility;
.toolbar {
@include utility.flex-center-center;
}
.actions {
@include utility.flex-top-right;
}Available named mixins:
flex-top-left
flex-top-center
flex-top-right
flex-center-left
flex-center-center
flex-center-right
flex-bottom-left
flex-bottom-center
flex-bottom-rightButton Mixins
The utility module exposes a base button mixin for clickable div-based controls that should size to their text. It only sets layout and pointer behavior; add padding, gap, color, radius, and other visual treatment separately.
@use './src/resources/styles/utility' as utility;
.button {
@include utility.theme(buttons, primary);
@include utility.button;
}Output from the base button mixin:
display: inline-flex;
width: fit-content;
cursor: pointer;Additional button helpers:
.nativeButton {
@include utility.button-reset;
}
.disabledButton {
@include utility.button-disabled;
}
.fullWidthButton {
@include utility.button-full-width;
}
.buttonIcon {
@include utility.button-icon(1.25rem);
}Interaction And Focus Mixins
Use shared interaction helpers for consistent transitions and keyboard focus states:
.control {
@include utility.interactive-transition;
@include utility.focus-ring-visible;
}
.control:focus {
@include utility.focus-ring;
}Layout Mixins
Use stack and cluster helpers for common component layout:
.stack {
@include utility.stack(small);
}
.actions {
@include utility.cluster(medium);
}Spacing arguments may be spacing token names such as small, medium, and large, or direct CSS lengths.
Text And Media Mixins
.label {
@include utility.truncate;
}
.screenReaderOnly {
@include utility.visually-hidden;
}
.icon {
@include utility.icon-size(small);
}Surface Mixins
.panel {
@include utility.surface;
}Form Mixins
Form helpers use the generated --form-* variables:
.field {
@include utility.form-field;
}
.field:focus {
@include utility.form-field-focus;
}
.field:disabled {
@include utility.form-field-disabled;
}
.field[aria-invalid='true'] {
@include utility.form-field-error;
}Theme Mixins
The utility module also exposes a generic theme recipe mixin backed by theme tokens from tokens.json:
@use './src/resources/styles/utility' as utility;
.button {
@include utility.theme(buttons, primary);
}Theme classes are generated with the same group and item names:
.theme-{group}-{name}For example:
.theme-buttons-primary
.theme-buttons-secondary
.theme-buttons-tertiaryUse the mixin when component Sass owns the selector. Use generated classes when templates can consume utility classes directly.
With atomic-bomb
When using the generated resources together with atomic-bomb, uncomment the component imports in src/resources/styles/main.scss:
/* Uncomment when using atomic-bomb */
//@use '../../components/atoms';
//@use '../../components/molecules';
//@use '../../components/organisms';
//@use '../../components/templates';
//@use '../../components/pages';Those imports point at the Sass barrel files that atomic-bomb creates in each component folder:
src/components/atoms/_index.scss
src/components/molecules/_index.scss
src/components/organisms/_index.scss
src/components/templates/_index.scss
src/components/pages/_index.scssWhen atomic-bomb creates a component, it appends that component to the matching _index.scss barrel. When a generated component is removed with atomic-bomb --remove [NAME], the matching Sass barrel entry is removed as well, so the resource imports in main.scss can stay stable.
Manual Installation
You can also clone the repository with degit:
degit https://github.com/ReneKrewinkel/create-atomic-resources.git <destination-dir>The npx workflow is preferred because it also installs dependencies and updates package.json scripts.
Troubleshooting
npm peer dependency conflicts
If npm reports an ERESOLVE peer dependency conflict while installing the helper packages, the installer retries automatically with:
npm install --save-dev --legacy-peer-deps json-to-scss [email protected] prettierThis is useful in projects with strict or outdated peer dependency ranges.
Sass requires a newer Node version
Recent Sass releases may require a newer Node.js version than your project uses. The installer pins Sass to a Node 18-compatible version:
npm install --save-dev json-to-scss [email protected] prettierIf you install helper packages manually on Node 18, use the same Sass version pin.
No lockfile detected
When no pnpm-lock.yaml, yarn.lock, or package-lock.json is found, the installer uses npm.
Custom destination directories
The copied resources follow the destination directory you pass to the CLI. The generated package scripts currently target ./src/resources, so if you install into another directory, update the generated token, scss, and nice scripts in your project package.json.
Development
Check the CLI syntax:
npm testFormat the CLI:
npm run nicePublish workflow:
npm run deploydeploy runs predeploy, which checks the CLI and bumps the package patch version before pushing commits and tags.
