- TypeScript 88.3%
- JavaScript 11.7%
|
Some checks failed
Verify dist/ / verify (push) Failing after 3s
Major refactor — @tamjid/ui is now a real component library built on
shadcn/ui primitives, designed to be the single visual source of truth
across all tamjid/basha.cloud apps.
New exports:
- ThemeToggle: system / light / dark switcher with localStorage
- useTheme(): theme hook (selection + resolved + setter)
- Shadcn primitives re-exported: Button, Avatar, Badge, Dialog, DropdownMenu
- cn() helper (clsx + tailwind-merge)
- @tamjid/ui/tailwind-preset: drop-in Tailwind config extension
AccountMenu + AccessDeniedModal rewritten using shadcn primitives.
Same public API (props), much smaller component code thanks to
reusing the design system.
Architecture changes:
- No CSS shipped from this package. Consumers' Tailwind scans
node_modules/@tamjid/ui/src/**/*.{ts,tsx} (via the preset) and
generates the CSS into their own bundle. Single Tailwind run.
- Brand tokens are the source of truth (shadcn-named, R G B channels
in https://brand.basha.cloud/tokens.css). The preset maps Tailwind
color names to those vars so `bg-primary/50` etc. just work.
- No more hand-written .tui-* CSS — shadcn-style utility classes
everywhere.
Peer deps added: tailwindcss, tailwindcss-animate, class-variance-
authority, clsx, tailwind-merge, @radix-ui/react-slot.
Bundle: ~15.5kB ESM (4kB gzip). All shadcn + Radix + Tailwind
infrastructure stays in the CONSUMER bundle (no duplicates).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .forgejo/workflows | ||
| dist | ||
| scripts | ||
| src | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
@tamjid/ui
Brand-aware React component library for tamjid/basha.cloud apps. Built on
shadcn/ui primitives + brand tokens from https://brand.basha.cloud/tokens.css.
What's exported:
AccountMenu— avatar dropdown with name/email/group chips + sign-outAccessDeniedModal— inline modal for 403 when user lacks required groupThemeToggle— system / light / dark switcher with localStorage persistenceuseTheme()— theme state hook (selection + resolved + setter)- Shadcn primitives:
Button,Avatar,Badge,Dialog,DropdownMenu cn()— clsx + tailwind-merge helper
Dark mode auto-switches with the OS by default; ThemeToggle adds manual override.
Install
"dependencies": {
"@tamjid/ui": "git+https://git.tamjid.io/tamjid/ui.git#v0.2.0"
}
Pin to a tag (not main) so unexpected changes don't break your build.
Peer deps the consumer must provide
{
"react": ">=18",
"react-dom": ">=18",
"@radix-ui/react-avatar": ">=1",
"@radix-ui/react-dialog": ">=1",
"@radix-ui/react-dropdown-menu": ">=2",
"@radix-ui/react-slot": ">=1",
"class-variance-authority": ">=0.7",
"clsx": ">=2",
"tailwind-merge": ">=2",
"tailwindcss": ">=3",
"tailwindcss-animate": ">=1"
}
Setup
1. Load brand tokens in your <head>
<link rel="stylesheet" href="https://brand.basha.cloud/tokens.css" />
(Plus the rest of the brand head snippet — favicon, fonts, theme-color.
See https://brand.basha.cloud/head.html for the full lockup.)
2. Wire Tailwind
In your tailwind.config.js:
import preset from '@tamjid/ui/tailwind-preset';
export default {
presets: [preset],
content: [
'./index.html',
'./src/**/*.{ts,tsx}',
// CRITICAL: scan ui's source so Tailwind picks up the classes
// its components reference.
'./node_modules/@tamjid/ui/src/**/*.{ts,tsx}',
],
};
The preset maps shadcn color names (primary, card, muted, etc.)
to the brand's CSS variables. The <alpha-value> placeholder enables
bg-primary/50-style opacity utilities.
3. Use the components
import { AccountMenu, AccessDeniedModal, ThemeToggle, type Me } from '@tamjid/ui';
function Header({ me }: { me: Me }) {
return (
<header className="flex items-center gap-2">
<h1 className="text-foreground">my app</h1>
<div className="ml-auto flex items-center gap-1">
<ThemeToggle />
<AccountMenu me={me} />
</div>
</header>
);
}
function App({ me, denied }: { me: Me; denied: boolean }) {
return (
<>
<Editor />
<AccessDeniedModal
open={denied}
me={me}
appName="my app"
requiredGroup="my-app-users"
/>
</>
);
}
Development
npm install
npm run build # vite + tsc → dist/
npm run dev # vite build --watch
dist/ is committed — consumers install via git+https so the
built output must be in the repo. CI verifies dist/ is in sync on every
push; if it's stale, the build fails with instructions to rebuild locally.
Workflow for changes:
- Edit
src/ npm run buildgit add src/ dist/- Commit + push
- Shipping:
git tag v0.x.y && git push --tags, then bump consumer's pin to the new tag.
Adding a new shadcn primitive
Don't use the shadcn CLI — vendor manually:
- Look up the component on https://ui.shadcn.com
- Copy its source into
src/components/ui/<name>.tsx - Adjust imports:
import { cn } from '../../lib/utils' - Re-export from
src/index.ts - Rebuild + commit
Architecture
- Shadcn-vendored components (
src/components/ui/*) use shadcn class names that resolve to brand tokens via the Tailwind preset. - Composed components (
AccountMenu, etc.) build on the primitives — no direct Tailwind usage on their own that bypasses the design system. - No CSS shipped — consumers' Tailwind setup generates the CSS by scanning ui's source files.
useThemewritesdata-theme="dark"/"light"to<html>for manual override (or removes the attribute for OS-follow). Brand tokens honor both via[data-theme]rules +@media (prefers-color-scheme).