@systemvci/shared-ui

v0.1.4

Published

Shared UI for the TC micro-frontends: framework-agnostic Web Components (<mfe-sidebar>, error pages) plus a React + Ant Design component kit (Table, Select, DatePicker, Modal, Upload, ...). Ships bundled CSS and a Tailwind preset.

Readme

@systemvci/shared-ui

Published on npm as @systemvci/shared-ui (public). Inside this monorepo it is also reachable as @mfe/shared-ui via the tsconfig.base.json path alias (source consumption) — the published name is what external projects install.

Shared UI for the TC micro-frontends. Two layers:

  1. Framework-agnostic Web Components<mfe-sidebar>, error pages (work in React, Angular, vanilla).
  2. React + Ant Design component kitTableComponent, SelectComponent, DatePickerComponent, ModalConfirmComponent, UploadComponent, ... (the share-components set), plus design tokens (getAntTheme, tailwindColors).

The cross-UI counterpart to @mfe/shared-state: instead of sharing state, this shares markup + style + behavior so the UI never drifts.

Build

Run from libs/shared-ui:

| Command | What it does | |---|---| | npm run build | Raw tsup build → dist/ (ESM + CJS + .d.ts + index.css). | | npm run package | Build + verify: builds, checks every expected output file exists, asserts peers stay external (no antd/react/rc-* bundled), then lists the tarball contents. Use this before publishing. | | npm run package:dry | npm run package + npm publish --dry-run — preview exactly what would be sent, without publishing. | | npm run typecheck | tsc --noEmit type check. | | npm run dev | tsup in watch mode. |

npm run package / package:dry / release are driven by scripts/build.mjs (cross-platform Node, fails fast with a clear message on any broken step).

Outputs in dist/:

| File | Purpose | |---|---| | index.js / index.cjs | JS bundle (ESM + CJS), peer deps externalized | | index.d.ts | Type declarations | | index.css | Bundled stylesheet: CSS color variables + per-component antd overrides | | tailwind-preset.js / .cjs | Tailwind JS preset exposing the brand color tokens |

Consuming in another project

Target stack: antd ^6.4.4, tailwindcss 3.4.3, react >=18.

1. Install

npm i @systemvci/shared-ui antd dayjs lucide-react formik react react-dom

These are declared as peerDependencies, so install them in the consuming app (formik is optional — only FormikErrorMessage needs it).

2. Import the stylesheet + components

// once, at app entry — :root color variables + per-component antd overrides
import '@systemvci/shared-ui/styles.css'

import { TableComponent, SelectComponent, getAntTheme } from '@systemvci/shared-ui'

Wrap the app in antd's ConfigProvider theme={getAntTheme(isDark)} for the matching antd theme.

3. Set up Tailwind (v3.4.3)

The kit's components are authored with Tailwind utility classes, so the consuming app runs Tailwind itself. shared-ui ships no compiled utilities — only the color tokens (the preset) + antd overrides (styles.css); the host's Tailwind generates the utilities by scanning the kit's dist.

3a. Install the build deps

npm i -D [email protected] postcss autoprefixer \
  @tailwindcss/container-queries tailwind-scrollbar tailwindcss-animate

(@tailwindcss/container-queries, tailwind-scrollbar, tailwindcss-animate are the plugins the TC FE apps standardize on — drop any your app doesn't use.)

3b. postcss.config.js

module.exports = {
  plugins: { tailwindcss: {}, autoprefixer: {} },
}

3c. tailwind.config.js — the shared-ui essentials are presets (so bg-bg-main / text-text-main / border-border resolve onto the styles.css variables) and the kit's dist entry in content (so the utility classes used inside its components get generated). The rest below — darkMode, screens, plugins — are the TC FE house conventions:

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: ['class'],
  // shared-ui essential: provides the brand color tokens (the same map the apps
  // would otherwise import from a local tailwind-colors.mjs).
  presets: [require('@systemvci/shared-ui/tailwind-preset')],
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx}',
    // shared-ui essential: scan the kit so its component utilities get generated.
    './node_modules/@systemvci/shared-ui/dist/**/*.{js,cjs}',
  ],
  theme: {
    extend: {
      colors: {
        background: 'var(--color-bg-main)',
        foreground: 'var(--color-text-main)',
      },
      screens: {
        sm: '640px',
        md: '768px',
        lg: '1024px',
        xl: '1280px',
        '2xl': '1536px',
        layout: '1840px',
      },
    },
  },
  plugins: [
    require('@tailwindcss/container-queries'),
    require('tailwind-scrollbar')({ nocompatible: true }),
    require('tailwindcss-animate'),
  ],
}

3d. Entry CSS — add the Tailwind directives (the kit stylesheet is already imported in step 2):

@tailwind base;
@tailwind components;
@tailwind utilities;

Monorepo note: under a workspace runner (e.g. Nx) that builds from the repo root rather than the app folder, anchor the content globs with __dirname and pass config: to the tailwindcss PostCSS plugin explicitly — otherwise neither file is found from the wrong cwd.

Publishing a new version

One command (from libs/shared-ui) — verifies auth, bumps the version, rebuilds and publishes:

npm run release                          # build + verify + bump patch + npm publish
node scripts/build.mjs --publish=minor   # ... bump minor instead (patch | minor | major)

release will: check npm whoami, build & verify the output, run npm version <type> --no-git-tag-version (npm forbids re-publishing the same version), then npm publish (access: public is already set in package.json). If publish fails, the version bump is kept so you can just re-run npm publish after fixing the cause.

Prefer to drive it by hand:

npm run package            # build + verify (no publish)
npm version patch          # bump version
npm publish                # publish

The publish token lives in ~/.npmrc. On Windows, make sure that file is saved with LF line endings — a trailing \r (CRLF) corrupts the _authToken and causes 401 errors. The release script detects this and warns you; fix with sed -i 's/\r$//' ~/.npmrc.

<mfe-sidebar>

One implementation of the sidebar, used by both remotes. Register it once, then render the custom element from any framework.

import { defineMfeSidebar } from '@systemvci/shared-ui';
defineMfeSidebar(); // idempotent across bundles/remotes

| Input | Type | Notes | |---|---|---| | items | MfeSidebarItem[] (property) or JSON string (attr) | { key, label }[] | | active | string | currently selected key | | heading | string (attr) | panel title | | badge | string (attr) | short badge letter | | accent | string (attr) | optional CSS color override (default = unified brand #6d28d9) |

Emits a select CustomEvent<{ key }> (bubbles + composed, so it crosses Shadow DOM).

// React
<mfe-sidebar ref={ref} heading="React MFE" badge="R" /> // set .items / .active + listen 'select'
<!-- Angular (needs CUSTOM_ELEMENTS_SCHEMA) -->
<mfe-sidebar heading="Angular MFE" badge="A" [items]="items" [active]="active" (select)="onSelect($event)"></mfe-sidebar>

The component owns its own Shadow DOM, so its styles are identical everywhere regardless of the host (Angular light DOM, React Shadow DOM, or the React/Vite shell).