No description
  • TypeScript 88.3%
  • JavaScript 11.7%
Find a file
Tamjid Rahman d6f4ba2bc5
Some checks failed
Verify dist/ / verify (push) Failing after 3s
v0.2.0: rebuild as a shadcn-based component library
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>
2026-06-08 06:31:57 -04:00
.forgejo/workflows scaffold @tamjid/ui — shared React auth chrome 2026-06-07 23:28:38 -04:00
dist v0.2.0: rebuild as a shadcn-based component library 2026-06-08 06:31:57 -04:00
scripts v0.2.0: rebuild as a shadcn-based component library 2026-06-08 06:31:57 -04:00
src v0.2.0: rebuild as a shadcn-based component library 2026-06-08 06:31:57 -04:00
.gitignore scaffold @tamjid/ui — shared React auth chrome 2026-06-07 23:28:38 -04:00
package-lock.json v0.2.0: rebuild as a shadcn-based component library 2026-06-08 06:31:57 -04:00
package.json v0.2.0: rebuild as a shadcn-based component library 2026-06-08 06:31:57 -04:00
README.md v0.2.0: rebuild as a shadcn-based component library 2026-06-08 06:31:57 -04:00
tsconfig.json scaffold @tamjid/ui — shared React auth chrome 2026-06-07 23:28:38 -04:00
vite.config.ts v0.2.0: rebuild as a shadcn-based component library 2026-06-08 06:31:57 -04:00

@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-out
  • AccessDeniedModal — inline modal for 403 when user lacks required group
  • ThemeToggle — system / light / dark switcher with localStorage persistence
  • useTheme() — 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:

  1. Edit src/
  2. npm run build
  3. git add src/ dist/
  4. Commit + push
  5. 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:

  1. Look up the component on https://ui.shadcn.com
  2. Copy its source into src/components/ui/<name>.tsx
  3. Adjust imports: import { cn } from '../../lib/utils'
  4. Re-export from src/index.ts
  5. 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.
  • useTheme writes data-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).