@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-uivia thetsconfig.base.jsonpath alias (source consumption) — the published name is what external projects install.
Shared UI for the TC micro-frontends. Two layers:
- Framework-agnostic Web Components —
<mfe-sidebar>, error pages (work in React, Angular, vanilla). - React + Ant Design component kit —
TableComponent,SelectComponent,DatePickerComponent,ModalConfirmComponent,UploadComponent, ... (theshare-componentsset), 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-domThese 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
contentglobs with__dirnameand passconfig:to thetailwindcssPostCSS 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 # publishThe publish token lives in
~/.npmrc. On Windows, make sure that file is saved with LF line endings — a trailing\r(CRLF) corrupts the_authTokenand causes 401 errors. The release script detects this and warns you; fix withsed -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).