npm package: @smartyweb/ui
Description: Shared React component library consumed by multiple Next.js microservices via private npm.
Latest version: 1.2.5

# @smartyweb/ui

Shared React component library consumed by multiple Next.js microservices via private npm.

## Stack

- React 19, TypeScript, SCSS Modules
- Vite (outputs ESM + CJS + types to `dist/`)
- Storybook v10 for component development and documentation
- Changesets for versioning and changelogs
- Style Dictionary v4 for design token transformation

## Getting Started

```bash
npm install
```

### Development

```bash
npm run storybook
```

This also runs `node sd.config.js` first to generate CSS tokens from `tokens.json`.

### Build

```bash
npm run build
```

### Tokens only

```bash
npm run tokens
```

Regenerates `src/styles/_tokens.generated.scss` from `tokens.json` without building or starting Storybook.

## Usage

Install the package in your consuming app:

```bash
npm install @smartyweb/ui
```

Import styles once in your app's entry point:

```js
import "@smartyweb/ui/styles.css";
```

Then import components:

```js
import { H1, Paragraph } from "@smartyweb/ui";
```

### Font Setup

This library does not bundle fonts. Consumers are responsible for loading fonts and exposing them as CSS custom properties. The library reads these three variables:

| CSS variable        | Font         | Used by                           |
| ------------------- | ------------ | --------------------------------- |
| `--ui-font-primary` | TT Hoves Pro | All heading and button components |
| `--ui-font-body`    | Inter        | Body and paragraph components     |
| `--ui-font-code`    | Hack         | Code component                    |

In a Next.js app using `next/font/local`, set these on the root element:

```tsx
// app/layout.tsx
import localFont from "next/font/local";

const ttHovesPro = localFont({
  src: [...],
  variable: "--font-tthoves",
});

export default function RootLayout({ children }) {
  return (
    <html className={ttHovesPro.variable}>
      <body style={{
        "--ui-font-primary": "var(--font-tthoves, 'TT Hoves Pro', system-ui, sans-serif)",
      }}>
        {children}
      </body>
    </html>
  );
}
```

If a variable is not set, components fall back to `system-ui` (headings/body) or `monospace` (code).

## Project Structure

```
sd.config.js                     # Style Dictionary config — tokens.json → _tokens.generated.scss
tokens.json                      # Designer-owned, synced from Tokens Studio (Figma plugin)
src/
  text/
    ComponentName/
      ComponentName.tsx          # "use client" at top
      ComponentName.module.scss  # uses var(--token-*) CSS custom properties
      ComponentName.stories.tsx
      index.ts                   # named export
    index.ts                     # barrel export
  styles/
    _variables.scss              # SCSS variables (fonts, weights, breakpoints, font-feature-settings)
    _tokens.generated.scss       # Auto-generated by sd.config.js — do not edit manually
    global.scss
  index.ts                       # main entry point
```

## Publishing

This package uses [Changesets](https://github.com/changesets/changesets) for versioning and changelog management.

### Semver policy

| Change type                                                                | Bump    |
| -------------------------------------------------------------------------- | ------- |
| Token value changes (e.g. heading size goes from 64px → 60px)              | `patch` |
| New CSS custom property names added (new tokens consumers could reference) | `minor` |
| New components added                                                       | `minor` |
| Removed components, renamed props, or changed component API                | `major` |

Visual changes (token values) are always `patch` — semver communicates API stability, not pixel perfection.

### Step 1 — On your feature branch

Create a changeset to describe the change and bump type:

```bash
npm run changeset
```

This opens an interactive prompt:

1. **Which packages changed?** — select `@smartyweb/ui`
2. **Bump type?** — see semver policy above
3. **Summary?** — plain English description (goes into the changelog)

Then apply the version bump immediately on the same branch:

```bash
npx changeset version   # bumps package.json and updates CHANGELOG.md
```

Commit everything and push:

```bash
git add .
git commit -m "chore: version bump"
git push
```

### Step 2 — Open a PR and merge to master

Open a PR from your feature branch → `master` on Bitbucket. The PR will include both your code changes and the version bump. Get it reviewed and merged.

### Step 3 — Publish to npm (automated)

Publishing happens automatically when the PR merges to `master`. Bitbucket Pipelines runs the build and `npm run release` (`changeset publish`), then pushes the git tags.

No manual action needed after merge.

<details>
<summary>Manual publish (if running outside CI)</summary>

```bash
git checkout master && git pull
npm run build
export NPM_TOKEN=<your-token>
npm run release   # runs `changeset publish` → publishes to npm and creates git tags
git push --tags
```

</details>

### CI Setup (one-time)

Two secured repository variables must be configured under **Bitbucket repo → Settings → Pipelines → Repository variables**:

| Variable          | Where to get it                                               |
| ----------------- | ------------------------------------------------------------- |
| `NPM_TOKEN`       | npmjs.org → Account → Access Tokens → type: "Automation"      |
| `BITBUCKET_TOKEN` | Bitbucket repo → Settings → Access tokens (Repository: Write) |

Mark both as **Secured** so they are masked in logs.

## Design Tokens

Design tokens flow from Figma → code via this pipeline:

1. **Designer** makes changes in Tokens Studio (Figma plugin) and syncs to the `design/tokens-update` branch
2. **CI** (Bitbucket Pipelines) runs `npm run tokens` on push, regenerates `src/styles/_tokens.generated.scss`, and commits the diff automatically
3. **Developer** opens a PR from `design/tokens-update` → `master`, runs `npx changeset add` on the branch, and merges after review
4. **Build** on `master` picks up the updated tokens and publishes a new npm version

### Reviewing a token PR

When reviewing a `design/tokens-update` PR, check:

- The `_tokens.generated.scss` diff — values should match the designer's intent in Figma
- Spot-check affected components in Storybook (deployed on Vercel, or run `npm run storybook` locally)
- Confirm a changeset file is present in `.changeset/` before merging

A changeset must be committed to the branch before the PR is merged. The developer opening the PR is responsible for this — the pipeline cannot do it automatically because bump type and description require human input.

`sd.config.js` reads only the `Responsive/Desktop`, `Responsive/Tablet`, and `Responsive/Mobile` token sets, outputting ~99 CSS custom properties like:

```css
:root {
	--h1-desktop-font-size: 64px;
	--h1-desktop-line-height: 64px;
	--h1-desktop-letter-spacing: -0.02em;
	/* ... */
}
```

Text components consume these via `var(--{component}-{breakpoint}-{property})`. Font families, weights, and OpenType feature settings remain as SCSS variables in `_variables.scss`.

Note: tablet CSS custom properties are generated but components intentionally do not use them. Components apply desktop values down to 768px, then switch to mobile. Tablet tokens are kept in `tokens.json` as source data for the planned fluid typography migration (see Roadmap).

## Roadmap

### Fluid typography

The current breakpoint approach (desktop / mobile with a hard switch at 768px) will be replaced with fluid typography using CSS `clamp()`. The desktop and mobile token values will become the max and min of the clamp range respectively, scaling continuously across the viewport.

The planned implementation: Style Dictionary computes the `clamp()` formula at build time (it has access to the raw numbers), outputting a single token per property (e.g. `--h1-font-size: clamp(44px, ..., 64px)`) instead of the current per-breakpoint triplets. Component SCSS becomes a single `font-size: var(--h1-font-size)` with no media queries.

The tablet token set in `tokens.json` will be retired from the Style Dictionary transform when this ships.

## Architecture

- **Component library only** — no routing, no pages, no app logic.
- **All components include `"use client"`** for compatibility with Next.js App Router and Pages Router.
- **CSS is compiled to a single file at build time.** No runtime CSS injection.
- **React is a peerDependency**, not bundled.
- **Stories are co-located with components.**
- **`_tokens.generated.scss` is committed** so token changes are visible in PR diffs, but never edit it manually — always regenerate via `npm run tokens`.