@kollegioai/ui
v1.3.3
Published
Kollegio shared React components — framework-agnostic (plain CSS + tokens)
Readme
@kollegioai/ui
Kollegio's shared React component library — framework-agnostic (plain CSS + design tokens, no Chakra/Tailwind/emotion dependency). Published to npmjs.com.
Companion to
@kollegioai/tokens(colors + typography). This package is the component layer.
Setup (all apps)
Load the token variables and component styles once, at the app entry:
import '@kollegioai/tokens/vars.css'; // --kollegio-* design-token variables
import '@kollegioai/ui/styles.css'; // component CSS (wrapped in @layer kollegio-ui)All component CSS lives in @layer kollegio-ui, so any unlayered app CSS (emotion, Tailwind utilities, inline styles) automatically wins over the variant presets — no specificity fights.
Peer dependency: react >= 18. Nothing else.
Two consumption modes
1. Direct (non-Chakra apps — e.g. landing-v2)
Use the components as-is. Bespoke styling via className or the CSS-variable hooks on style (these also apply inside hover/active states):
import { Button } from '@kollegioai/ui/Button';
<Button variant="primary" buttonSize="lg" onClick={...}>Save</Button>
// override paint via --kui-* hooks
<Button
variant="outline"
style={{ '--kui-btn-radius': '999px', '--kui-btn-hover-bg': 'var(--kollegio-backgroundPrimarySubtle)' }}
>
Pill
</Button>Button hooks: --kui-btn-bg, -color, -border-color, -hover-bg, -hover-color, -hover-border-color, -active-bg, -radius, -h, -min-h, -px, -py, -fs, -gap, -disabled-opacity.
Tooltip hooks: --kui-tooltip-bg, -color, -fs, -radius, -px, -py, -max-w, -z.
2. Chakra bridge (Chakra v3 apps — client/client-src, counsellor)
Wrap the universal component with Chakra's chakra() factory at one adapter seam. Call sites then keep the full Chakra style-prop API (w="150px", bg="backgroundPrimary", _hover={{…}}, responsive values), resolved against the app's own theme:
// src/shared/components/KollegioButton.tsx (adapter seam — once per app)
import { chakra, type HTMLChakraProps } from '@chakra-ui/react';
import { Button as UniversalButton } from '@kollegioai/ui/Button';
const KollegioButton = chakra(UniversalButton, {}, {
// non-style props the factory must pass through to the component
forwardProps: ['variant', 'buttonSize', 'fullWidth', 'isLoading', 'loadingText',
'spinnerPlacement', 'leftIcon', 'rightIcon', 'enableTouchEndClickFix'],
});Because the package CSS is layered, the emotion styles generated by the bridge always override the variant presets.
Caveat: as= / asChild on a bridged component replaces the universal Button itself (losing its logic and classes) — don't use them; handle navigation in onClick instead.
New shared components automatically get both modes — author them universally here, and each Chakra app's seam file decides whether to bridge.
Components
Subpath exports keep bundles lean (sideEffects: false):
| Import | Description |
|---|---|
| @kollegioai/ui/Button | Brand button. Variants: primary, primaryStrong, accent, outline, secondary, tertiary, light, ghost, oauth, logOut. Sizes via buttonSize (sm/md/lg). Also: fullWidth, isLoading/loadingText/spinnerPlacement, leftIcon/rightIcon, enableTouchEndClickFix. |
| @kollegioai/ui/Spinner | Loading spinner (size sm/md/lg, optional label). Inherits currentColor. |
| @kollegioai/ui/Input | Text input. inputSize (sm/md/lg), invalid, fullWidth, leftElement/rightElement adornments. className + chakra-bridge style props target the bordered group. |
| @kollegioai/ui/Textarea | Multiline input. invalid, fullWidth, rows; shares the --kui-input-* paint vars with Input. |
| @kollegioai/ui/Field | Form-field layout: label row (+ required, labelExtra) + control + error/helperText. Pair with Input/Textarea (pass matching id/htmlFor and invalid={!!error}). |
| @kollegioai/ui/Tooltip | Radix-based tooltip (plain-text/ReactNode label), tap-to-open on touch devices. offset accepts a number or { mainAxis, crossAxis }. |
| @kollegioai/ui/TooltipMarkdown | Tooltip whose label renders markdown (pulls in react-markdown — only pay for it if you import this entry). |
| @kollegioai/ui/EllipsisMenu | Three-dot overflow menu (EllipsisMenu + EllipsisMenuOption). |
| @kollegioai/ui | Barrel of all of the above (except TooltipMarkdown). |
Variant/size presets are data in src/recipes/button.ts; scripts/gen-button-css.mjs generates styles/button.css from it at build time.
Storybook
pnpm --filter @kollegioai/ui storybookRenders every component/variant against the token CSS variables — no provider needed.
Build, version & publish
The registry rejects re-publishing an existing version, so every publish starts with a version bump.
1. Bump the version in package.json (semver — patch for fixes, minor for new components, major for breaking prop changes).
// packages/ui/package.json
"version": "1.3.3",Option A — CI auto-publish (recommended)
Just merge the version bump to main. .github/workflows/publish-ui.yml builds and runs pnpm --filter @kollegioai/ui publish --no-git-checks automatically on any change under packages/ui/** (story-only changes are excluded). It authenticates with the NPM_TOKEN repo secret — no local token needed. This is how releases normally go out.
Option B — Manual local publish (fallback)
Use when you can't wait for CI. The flow below reads NPM_TOKEN (npm automation token, bypasses 2FA) from client/client-src/.env and never writes the secret to disk — the temp .npmrc holds only the literal ${NPM_TOKEN} placeholder, which npm expands at publish time.
cd "/Users/mac/Documents/Kollegio AI/landing-v2/packages/ui"
# 1) Load NPM_TOKEN from client-src/.env into this shell (not printed)
export NPM_TOKEN="$(grep -E '^NPM_TOKEN=' "/Users/mac/Documents/Kollegio AI/client/client-src/.env" | cut -d= -f2- | tr -d '"'\''' | tr -d '\r')"
[ -n "$NPM_TOKEN" ] && echo "token loaded (${#NPM_TOKEN} chars)" || echo "TOKEN NOT FOUND"
# 2) Build (codegen CSS → tsup dual ESM .mjs + CJS .js + .d.ts)
pnpm build
# 3) Temp .npmrc with the placeholder only (gitignored; removed in step 5)
printf '//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n' > .npmrc
# 4) Publish (pnpm rewrites workspace:^ deps to real versions automatically)
NPM_TOKEN=$NPM_TOKEN pnpm publish --no-git-checks --access public
# 5) ALWAYS remove the temp .npmrc afterward
rm -f .npmrcThe token can live in any
.envyou prefer — just point thegrepin step 1 at that file. Keep.npmrcgitignored and never commit it.
Verify & consume
npm view @kollegioai/ui version # confirm the new version is live
# repoint consumers (landing apps auto-update via workspace:^)
cd "/Users/mac/Documents/Kollegio AI/client/client-src" && npm install @kollegioai/ui@^<new-version>
cd "/Users/mac/Documents/Kollegio AI/counsellor" && npm install @kollegioai/ui@^<new-version>For local testing before publishing, skip the registry entirely: pnpm pack and install the resulting tarball in a consumer.
