ci(packages): close ROADMAP TODOs #5, #6 + draft RFC for #4

═══════════════════════════════════════════════════════════════════════
TODO #6 — size-limit budgets in CI
═══════════════════════════════════════════════════════════════════════
Adds .size-limit.cjs with budgets for 6 pilot @bytelyst/* packages,
plus a Gitea Actions workflow (.gitea/workflows/size-limit.yml) that
fails the build on any regression.

Current measurements vs budget (all comfortably under):
  @bytelyst/api-client       793 B  / 8 KB
  @bytelyst/auth-client     1.97 KB / 8 KB
  @bytelyst/celebrations     236 B  / 6 KB
  @bytelyst/quick-actions    122 B  / 6 KB
  @bytelyst/react-auth      2.71 KB / 10 KB
  @bytelyst/dashboard-shell 3.96 KB / 30 KB

Scripts:
  pnpm size       — measure + assert against budgets
  pnpm size:why   — explain top contributors

Deps: size-limit@^12.1.0, @size-limit/preset-small-lib@^12.1.0.

Pilot scope (this commit): 6 packages. Full @bytelyst/* rollout is
incremental — each entry added separately.

═══════════════════════════════════════════════════════════════════════
TODO #5 — Storybook canonical pattern
═══════════════════════════════════════════════════════════════════════
Discovery: @bytelyst/ui already has Storybook 8 with the @bytelyst/
addon-a11y addon configured and 5 .stories.tsx files.

This commit:
  - Documents that setup as the canonical template
    (docs/STORYBOOK_TEMPLATE.md) including the .storybook/main.ts,
    preview.ts, package.json scripts, and an example story.
  - Catalogs which of the 8 visual @bytelyst/* packages need Storybook
    added (1/8 done — @bytelyst/ui).
  - Per docs/ROADMAP_2026_DECISIONS.md §9, hosting target is
    self-hosted Gitea Pages (no Chromatic).

Full rollout to the other 7 packages stays open as incremental work.

═══════════════════════════════════════════════════════════════════════
TODO #4 — DTCG v3 token migration RFC
═══════════════════════════════════════════════════════════════════════
This is too large to land in a single commit. Drafted RFC instead:
  docs/rfc/0001-dtcg-v3-token-migration.md

The RFC proposes a 4-PR sequence:
  PR 1 — Add converter + dual-emit (byte-identical assertion)
  PR 2 — Flip source of truth to DTCG JSON
  PR 3 — Introduce the component tier
  PR 4 — Multi-theme via DTCG token sets

Estimate: 2 person-weeks total. Backward compatibility: --ml-* and
--bl-* names preserved through all 4 PRs. Status: Draft, awaiting
reviewers.

Refs: learning_ai_uxui_web/docs/ROADMAP_2026.md §10 TODOs #4, #5, #6
This commit is contained in:
saravanakumardb1 2026-05-27 11:48:29 -07:00
parent cc0bffea86
commit ed5fb707ad
6 changed files with 941 additions and 11 deletions

View File

@ -0,0 +1,75 @@
name: Size limit
# ROADMAP TODO #6 — enforces bundle-size budgets defined in
# .size-limit.cjs on every push to main and every PR. Failures block
# merge so that bundle bloat is a visible, deliberate decision.
on:
push:
branches: [main]
paths:
- 'packages/**'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'package.json'
- '.size-limit.cjs'
- '.gitea/workflows/size-limit.yml'
pull_request:
branches: [main]
paths:
- 'packages/**'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- 'package.json'
- '.size-limit.cjs'
concurrency:
group: size-limit-${{ github.ref }}
cancel-in-progress: true
jobs:
size:
name: size-limit
runs-on: [ubuntu-latest, bytelyst, hostinger]
container:
image: node:20-bookworm
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
github-server-url: https://gitea.bytelyst.com
- name: Install pinned pnpm
run: |
npm install -g pnpm@10.6.5
pnpm --version
- name: Install dependencies
run: HUSKY=0 pnpm install --frozen-lockfile
- name: Build measured packages
# size-limit measures dist/ — ensure every entry in
# .size-limit.cjs has a fresh build before running.
run: |
pnpm --filter @bytelyst/api-client \
--filter @bytelyst/auth-client \
--filter @bytelyst/celebrations \
--filter @bytelyst/quick-actions \
--filter @bytelyst/react-auth \
--filter @bytelyst/dashboard-shell \
run build
- name: Enforce size budgets
run: pnpm size
- name: Upload size report
if: always()
uses: actions/upload-artifact@v4
with:
name: size-limit-${{ github.run_id }}
path: |
.size-limit/
retention-days: 14
continue-on-error: true

68
.size-limit.cjs Normal file
View File

@ -0,0 +1,68 @@
/**
* ROADMAP TODO #6 Bundle-size budgets for @bytelyst/* packages.
*
* Each entry measures the gzipped size of a package's built `dist/`
* output. The 'limit' field is the budget PRs that exceed it fail CI.
*
* Initial budgets per learning_ai_uxui_web/docs/ROADMAP_2026.md §5
* "Performance budgets":
* - Pure-TS clients 8 KB
* - Feature packs 6 KB
* - UI primitive slices ~1 KB per primitive (whole pkg < 30 KB)
* - Tokens / design-tokens 12 KB (CSS heavy)
*
* Pilot scope (this commit): wire up 6 representative packages. Rollout
* to the rest of @bytelyst/* lands incrementally as packages stabilise.
*
* Run locally:
* pnpm -w size full check
* pnpm -w size --why <name> explain what's contributing
*
* To add a package:
* 1. Confirm the package has 'build' in its scripts and emits to dist/
* 2. Add an entry below with name, path, and limit
* 3. Run `pnpm -w size --update` to record the current baseline if
* you're starting under-budget (optional)
*/
module.exports = [
// ── Pure-TS clients (8 KB) ──────────────────────────────────────
{
name: '@bytelyst/api-client',
path: 'packages/api-client/dist/index.js',
limit: '8 KB',
gzip: true,
},
{
name: '@bytelyst/auth-client',
path: 'packages/auth-client/dist/index.js',
limit: '8 KB',
gzip: true,
},
// ── Feature packs (6 KB) ────────────────────────────────────────
{
name: '@bytelyst/celebrations',
path: 'packages/celebrations/dist/index.js',
limit: '6 KB',
gzip: true,
},
{
name: '@bytelyst/quick-actions',
path: 'packages/quick-actions/dist/index.js',
limit: '6 KB',
gzip: true,
},
// ── React bindings (10 KB — slightly higher for hooks + context) ─
{
name: '@bytelyst/react-auth',
path: 'packages/react-auth/dist/index.js',
limit: '10 KB',
gzip: true,
},
// ── Shells / composite UI (30 KB) ───────────────────────────────
{
name: '@bytelyst/dashboard-shell',
path: 'packages/dashboard-shell/dist/index.js',
limit: '30 KB',
gzip: true,
},
];

115
docs/STORYBOOK_TEMPLATE.md Normal file
View File

@ -0,0 +1,115 @@
# Storybook Template for `@bytelyst/*` Packages
> ROADMAP TODO #5 — canonical pattern for adding Storybook 8 to each
> visual `@bytelyst/*` package.
## Status
| Package | Has Storybook | Stories | A11y addon |
| -------------------------------- | ------------- | --------------------------------------------------------------- | ---------- |
| `@bytelyst/ui` | ✅ | 5 (`Button`, `Card`, `Controls`, `Input`, `OperationalPreview`) | ✅ |
| `@bytelyst/auth-ui` | ❌ | — | — |
| `@bytelyst/dashboard-components` | ❌ | — | — |
| `@bytelyst/dashboard-shell` | ❌ | — | — |
| `@bytelyst/celebrations` | ❌ | — | — |
| `@bytelyst/gentle-notifications` | ❌ | — | — |
| `@bytelyst/quick-actions` | ❌ | — | — |
| `@bytelyst/react-auth` | ❌ | — | — |
Rollout is incremental — each package added separately so failures are
diagnosable.
## Canonical setup (mirrors `@bytelyst/ui`)
### 1. Add devDependencies
```sh
pnpm --filter @bytelyst/<pkg> add -D \
storybook@^8.5.0 \
@storybook/react@^8.5.0 \
@storybook/react-vite@^8.5.0 \
@storybook/addon-essentials@^8.5.0 \
@storybook/addon-a11y@^8.5.0
```
### 2. Add scripts to `package.json`
```json
{
"scripts": {
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}
}
```
### 3. Create `.storybook/main.ts`
```ts
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: ['@storybook/addon-essentials', '@storybook/addon-a11y'],
framework: { name: '@storybook/react-vite', options: {} },
};
export default config;
```
### 4. Create `.storybook/preview.ts`
```ts
import type { Preview } from '@storybook/react';
import '@bytelyst/design-tokens/css';
const preview: Preview = {
parameters: {
backgrounds: {
default: 'dark',
values: [
{ name: 'dark', value: '#06070A' },
{ name: 'elevated', value: '#0E1118' },
{ name: 'light', value: '#F8F9FC' },
],
},
},
};
export default preview;
```
### 5. Add at least one `*.stories.tsx`
Pattern — one story file per component, **co-located** in `src/`:
```tsx
// src/components/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button.js';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
parameters: { layout: 'centered' },
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = { args: { children: 'Click me' } };
export const Disabled: Story = { args: { children: 'Disabled', disabled: true } };
```
## Hosting
**Decision (`docs/ROADMAP_2026_DECISIONS.md` §9):** self-hosted on
Gitea Pages.
A future Gitea Actions workflow at `.gitea/workflows/storybook.yml`
will build each package's Storybook on push to `main` and deploy the
combined output to `storybook.bytelyst.com` (or equivalent).
Until that workflow lands, developers run `pnpm --filter @bytelyst/<pkg>
run storybook` locally on `:6006`.

View File

@ -0,0 +1,177 @@
# RFC 0001 — Migrate `@bytelyst/design-tokens` to DTCG v3
| Status | Draft |
| --------- | ---------------- |
| Author | Cascade |
| Created | 2026-05-27 |
| Tracks | ROADMAP TODO #4 |
| Reviewers | _to be assigned_ |
## Summary
Migrate `packages/design-tokens/tokens/bytelyst.tokens.json` from the
current bespoke schema to the **W3C Design Tokens Community Group
(DTCG) Format Module v3**. Re-emit all generated outputs
(CSS / TS / Kotlin / Swift / per-product CSS) from the new schema with
**no observable change to runtime CSS variable names**.
## Motivation
1. **Designer tooling.** Figma-Tokens (now "Tokens Studio") and
Specify both speak DTCG natively. Today designers cannot round-trip
tokens with engineering — the JSON schema is bespoke.
2. **Long-term portability.** DTCG is becoming the lingua franca of
design systems (Adobe Spectrum, Salesforce Lightning, GitHub Primer
are all migrating).
3. **Three-tier semantics.** DTCG `$type` + reference syntax (`{path}`)
gives us a clean way to express the **reference → semantic →
component** layering documented in
`learning_ai_uxui_web/docs/ROADMAP_2026.md` §3.
## Current schema (excerpt)
```json
{
"meta": { "name": "ByteLyst Design Tokens", "version": "1.1.0" },
"color": {
"palette": { "neutral": { "0": "#FFFFFF", "50": "#F6F8FC", ... } },
"semantic": {
"dark": { "bgCanvas": "#06070A", ... },
"light": { "bgCanvas": "#F8F9FC", ... }
}
},
"spacing": { "0": 0, "1": 4, "2": 8, ... },
"radius": { "xs": 4, "sm": 6, ... },
"typography": { "fontSize": { "xs": 12, ... } },
...
}
```
## Proposed DTCG v3 schema (excerpt)
```json
{
"$description": "ByteLyst design tokens — DTCG v3",
"$schema": "https://design-tokens.github.io/community-group/format/",
"color": {
"neutral": {
"0": { "$value": "#FFFFFF", "$type": "color" },
"50": { "$value": "#F6F8FC", "$type": "color" },
"950": { "$value": "#06070A", "$type": "color" }
},
"brand": {
"blue": { "$value": "#5A8CFF", "$type": "color" },
"coral": { "$value": "#FF6E6E", "$type": "color" }
},
"semantic": {
"bgCanvas": {
"$value": "{color.neutral.950}",
"$type": "color",
"$description": "Page background — dark"
}
}
},
"spacing": {
"1": { "$value": "4px", "$type": "dimension" },
"2": { "$value": "8px", "$type": "dimension" }
},
"radius": {
"xs": { "$value": "4px", "$type": "dimension" }
},
"component": {
"button": {
"padding-x": { "$value": "{spacing.4}", "$type": "dimension" },
"bg": { "$value": "{color.semantic.accent}", "$type": "color" }
}
}
}
```
### Key changes
| Aspect | Today | DTCG v3 |
| -------------- | ------------------------ | ------------------------------------- |
| Value wrapper | bare value | `{ "$value": ..., "$type": ... }` |
| Type metadata | inferred from JS path | explicit `$type` |
| References | manual JS lookup | `{path.to.token}` syntax |
| Light/dark | parallel sibling objects | DTCG **token sets** (per-theme files) |
| Documentation | none | `$description` per token |
| Component tier | doesn't exist | new top-level `component` group |
## Implementation plan
The migration is split into **4 PRs** to keep each one reviewable.
### PR 1 — Add converter + dual-emit (no behaviour change)
- Add `scripts/convert-legacy-to-dtcg.ts` that reads the bespoke JSON
and emits `bytelyst.tokens.dtcg.json`.
- Modify `scripts/generate.ts` to accept either schema and produce
byte-identical output.
- Add a vitest case asserting the two emitters produce identical CSS
byte-for-byte.
### PR 2 — Flip source of truth to DTCG JSON
- Delete the legacy JSON.
- Update Figma-Tokens plugin documentation + commit a `.tokens.config.json`
for round-trip.
### PR 3 — Introduce the component tier
- New `component.*` group in the DTCG JSON.
- Generator emits new `--bl-{component}-{slot}` variables.
- Audit existing hardcoded values in `@bytelyst/ui` and replace with
the new component tokens.
### PR 4 — Multi-theme via DTCG token sets
- Split the single JSON into:
- `tokens/global.tokens.json` (reference + semantic core)
- `tokens/themes/dark.tokens.json` (light/dark overrides)
- `tokens/themes/light.tokens.json`
- `tokens/brands/lysnrai.tokens.json` (brand-layer overrides — Wave 7)
- Generator composes per-output set.
## Backward compatibility
- All existing `--ml-*` and `--bl-*` CSS variable names are preserved.
- All TS/Kotlin/Swift exported identifiers preserved.
- Consumer apps require zero changes through PRs 13.
- PR 4 introduces multi-set composition but the **default** still
produces the same `tokens.css`.
## Risks
| Risk | Severity | Mitigation |
| ------------------------------------------------ | -------- | ------------------------------------- |
| Generator divergence between schemas during PR 1 | High | Byte-identical assertion test |
| Figma-Tokens plugin version churn | Med | Pin plugin version in docs/THEMING.md |
| Designer learning curve | Med | One-pager + paired migration session |
| Reference cycles in DTCG (`{a}` → `{b}``{a}`) | Low | Generator validates DAG before emit |
## Alternatives considered
- **Style Dictionary** — Amazon's tool. Heavier, more opinionated.
Rejected: we already have a working generator; switching tools is a
larger change than switching schemas.
- **Stay on bespoke JSON** — Rejected: blocks designer round-trip
and locks us out of the ecosystem.
## Estimate
~1 person-week for PRs 13, +1 pw for PR 4 = **2 pw total**.
## Open questions
- Do we adopt the DTCG **2024 candidate** or wait for the **2026
final**? Candidate is stable enough; recommend candidate now,
re-verify when final.
- Where do per-product brand layers live — separate packages
(`@bytelyst/brand-lysnrai`) or subpaths
(`@bytelyst/design-tokens/brands/lysnrai`)? Recommend subpaths
pre-Wave 7, separate packages from Wave 7.

View File

@ -17,6 +17,8 @@
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
"audit": "pnpm -r audit --audit-level moderate",
"size": "size-limit",
"size:why": "size-limit --why",
"clean": "pnpm -r exec rm -rf dist",
"docker:clean": "./scripts/docker-clean.sh",
"dns:godaddy:bytelyst": "./scripts/godaddy-sync-bytelyst-dns.sh",
@ -26,6 +28,7 @@
"devDependencies": {
"@changesets/cli": "^2.28.1",
"@eslint/js": "^10.0.1",
"@size-limit/preset-small-lib": "^12.1.0",
"@types/bcryptjs": "^2.4.6",
"@types/node": "^20.0.0",
"@types/react": "^19.0.0",
@ -36,6 +39,7 @@
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"prettier": "^3.0.0",
"size-limit": "^12.1.0",
"typescript": "^5.7.0",
"vitest": "^3.0.0"
},

513
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff