Codeskill

Learn to code, step by step

Mini project: publishable SCSS package (tokens + components)

This mini project pulls together the advanced tutorials: a small publishable npm package with design tokens, two components, compile scripts, and a consumer example that installs and uses the package.

Goal

By the end you will have @your-org/ui-tokens (or a local file path stand-in) that exports SCSS tokens, compiled CSS, and documented components another project can install without copy-pasting files.

Package layout

ui-tokens/
  package.json
  README.md
  scss/
    _index.scss
    tokens/
      _core.scss
      _brand-default.scss
    components/
      _button.scss
      _card.scss
    main.scss          /* full bundle entry */
  dist/
    main.css           /* compiled - gitignore or commit for publish */
    main.css.map
  scripts/
    build.js           /* optional: sass + postcss chain */

Step 1: Core tokens

// scss/tokens/_core.scss
$core-space: (
  'sm': 0.5rem,
  'md': 1rem,
  'lg': 2rem,
);

$core-radius: (
  'md': 8px,
);

// scss/tokens/_brand-default.scss
$brand: (
  'color-primary': #0066cc,
  'color-text': #1a1a1a,
  'color-surface': #ffffff,
);

Step 2: Components using tokens

// scss/components/_button.scss
@use '../tokens/core' as core;
@use '../tokens/brand-default' as brand;

.btn {
  --btn-bg: #{map-get(brand.$brand, 'color-primary')};
  display: inline-flex;
  padding: map-get(core.$core-space, 'sm') map-get(core.$core-space, 'md');
  border-radius: map-get(core.$core-radius, 'md');
  background: var(--btn-bg);
  color: map-get(brand.$brand, 'color-surface');
  border: none;
  cursor: pointer;
}

// scss/components/_card.scss
@use '../tokens/core' as core;
@use '../tokens/brand-default' as brand;

.card {
  padding: map-get(core.$core-space, 'md');
  background: map-get(brand.$brand, 'color-surface');
  color: map-get(brand.$brand, 'color-text');
  border-radius: map-get(core.$core-radius, 'md');
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.08);
}

Step 3: Barrel and entry

// scss/_index.scss - SCSS consumers @use this
@forward 'tokens/core';
@forward 'tokens/brand-default';
@forward 'components/button';
@forward 'components/card';

// scss/main.scss - compiles full CSS bundle
@use 'components/button';
@use 'components/card';

Step 4: package.json

{
  "name": "@your-org/ui-tokens",
  "version": "1.0.0",
  "description": "Shared tokens and components",
  "files": ["scss", "dist"],
  "style": "dist/main.css",
  "sass": "scss/_index.scss",
  "scripts": {
    "build": "sass scss/main.scss dist/main.css --style=compressed",
    "prepublishOnly": "npm run build"
  },
  "devDependencies": {
    "sass": "^1.77.0"
  }
}

files controls what npm publishes. style points CSS consumers at the bundle. sass points SCSS consumers at the barrel for @use '@your-org/ui-tokens'.

Step 5: Consumer project

// Option A - link locally while developing
npm install ../ui-tokens

// consumer/scss/site.scss
@use '@your-org/ui-tokens' as ui;

.hero-card {
  @extend .card; /* or duplicate markup classes in HTML */
}

// Option B - CSS only, no Sass in consumer
// index.html
<link rel="stylesheet" href="node_modules/@your-org/ui-tokens/dist/main.css">
<button class="btn">Click</button>
<article class="card">...</article>

Prefer class markup in HTML over @extend across package boundaries – extend ties consumer CSS to library internals. The HTML approach is the stable public API.

Step 6: README essentials

  • Install instructions (npm and local path)
  • SCSS vs CSS consumption paths
  • List of public custom properties (--btn-bg)
  • Semver policy for token renames
  • Build command and Node version

Optional extensions

  • Second brand map + second compiled CSS file (dist/main-beta.css)
  • PostCSS pass after Sass (autoprefixer, cssnano)
  • One-page HTML style guide in docs/ linking dist/main.css
  • Export tokens.json for design tooling

Done when

  • npm run build produces dist/main.css without errors
  • A second folder installs the package and renders button + card with package styles
  • README explains both consumption paths in plain language
  • You can state which tokens and classes are public API vs internal

That is Going deep with SCSS complete. You can structure multi-brand systems, control compiled output size, wire PostCSS, decide when Sass still pays off, document components honestly, ship library boundaries, migrate incrementally, and package styles for reuse.

PreviousMigration strategies (CSS, SCSS, native nesting)