@kaizen-ace-it/shared
v0.1.3
Published
Shared design system, components and utilities for Kaizen Ace It applications.
Readme
@kaizen-ace-it/shared
Shared design system, components, hooks, and utilities for all Kaizen Ace It applications.
Installation
npm install @kaizen-ace-it/sharedPeer dependencies — make sure your app already has these installed:
npm install react react-dom next tailwindcss
Quick Start
1. Add the Tailwind preset
In your tailwind.config.ts, import the shared preset. This gives every app the exact same brand colours, border-radius tokens, and font stack:
// tailwind.config.ts
import type { Config } from "tailwindcss";
import sharedPreset from "@kaizen-ace-it/shared/preset";
const config: Config = {
presets: [sharedPreset],
content: ["./src/**/*.{ts,tsx}"],
};
export default config;2. Import the CSS design tokens
In your globals.css or root layout:
@import "@kaizen-ace-it/shared/tokens.css";This exposes CSS custom properties like --primary, --accent, --radius-md, etc. that the shared components rely on.
3. (Optional) Block flash of unstyled dark mode
In your root layout.tsx <head>, render the ThemeScript component. This runs before hydration so dark mode is applied without a flash:
import { ThemeScript } from "@kaizen-ace-it/shared";
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<ThemeScript />
</head>
<body>{children}</body>
</html>
);
}What's Available
🎨 Constants
import { STORAGE_KEYS, BRAND, AUTH_URLS } from "@kaizen-ace-it/shared";| Export | Value | Description |
|---|---|---|
| STORAGE_KEYS.token | "kaizen_token" | localStorage key for the auth token — use this everywhere |
| STORAGE_KEYS.theme | "kaizen_theme" | localStorage key for theme preference |
| BRAND.name | "Kaizen Ace It!" | Official app name |
| BRAND.tagline | "Empower your learning..." | Brand tagline |
| AUTH_URLS.signin | URL | The canonical sign-in URL |
| AUTH_URLS.signup | URL | The canonical sign-up URL |
🧩 UI Components
All components are React/Next.js compatible and Tailwind-powered.
import { Button, Input, PasswordInput, FormField } from "@kaizen-ace-it/shared";
// or from the sub-path:
import { Button } from "@kaizen-ace-it/shared/components";<Button />
The standard Kaizen button. Powered by class-variance-authority. Supports the asChild pattern via Radix UI's Slot — meaning you can render a Next.js <Link> that looks like a button without breaking semantic HTML.
import { Button } from "@kaizen-ace-it/shared";
import Link from "next/link";
// Default primary button
<Button>Get Started</Button>
// Variants
<Button variant="outline">Browse Courses</Button>
<Button variant="ghost">Cancel</Button>
<Button variant="destructive">Delete</Button>
<Button variant="link">Learn more →</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon"><SearchIcon /></Button>
// asChild — renders as a Next.js Link, styled as a button
<Button asChild>
<Link href="/courses">Go to Courses</Link>
</Button>
// Disabled state
<Button disabled>Submitting...</Button>| Prop | Type | Default | Description |
|---|---|---|---|
| variant | default \| outline \| secondary \| ghost \| destructive \| link | default | Visual style |
| size | default \| sm \| lg \| icon | default | Size variant |
| asChild | boolean | false | Render as child element (e.g. <Link>) |
| className | string | — | Additional Tailwind classes |
<Input />
Standard text input, styled to match the Kaizen design system.
import { Input } from "@kaizen-ace-it/shared";
<Input type="email" placeholder="[email protected]" />
<Input type="text" className="bg-gray-50" /><PasswordInput />
Same as <Input> but with a built-in show/hide toggle button.
import { PasswordInput } from "@kaizen-ace-it/shared";
<PasswordInput
placeholder="Enter password"
showToggleAriaLabel="Show my password"
/><FormField />
A labelled form field wrapper that handles spacing and label association.
import { FormField, Input, PasswordInput } from "@kaizen-ace-it/shared";
<FormField label="Email Address" htmlFor="email">
<Input id="email" type="email" />
</FormField>
<FormField label="Password" htmlFor="password">
<PasswordInput id="password" />
</FormField><BrandMark />
The Kaizen logo + wordmark. Place in navbars, sidebars, and footers.
import { BrandMark } from "@kaizen-ace-it/shared";
// Default — looks for /images/kaizen-logo.png in your /public directory
<BrandMark />
// Custom logo source, no wordmark (icon-only)
<BrandMark src="/kaizen-logo.png" showWordmark={false} size={40} />
// Inverted for dark backgrounds
<BrandMark className="invert" />⚠️ Copy
kaizen-logo.pnginto your app's/public/images/directory, or pass a customsrc.
<AuthShell />
The split-screen authentication layout used on the sign-in / sign-up pages.
import { AuthShell, FormField, Input, PasswordInput, Button } from "@kaizen-ace-it/shared";
<AuthShell tagline="Sign in to your account.">
<form>
<FormField label="Email" htmlFor="email">
<Input id="email" type="email" />
</FormField>
<FormField label="Password" htmlFor="password">
<PasswordInput id="password" />
</FormField>
<Button className="w-full mt-4">Sign In</Button>
</form>
</AuthShell><Container />
A responsive max-width wrapper. Use this to constrain page content width consistently.
import { Container } from "@kaizen-ace-it/shared";
<Container>
<h1>Page content here</h1>
</Container>
// With custom class
<Container className="py-16">
...
</Container><AnimatedSection />
A motion.div wrapper that fades in and slides up when it enters the viewport. Uses Framer Motion.
import { AnimatedSection } from "@kaizen-ace-it/shared";
<AnimatedSection className="grid grid-cols-3 gap-6">
{/* Cards, content etc. */}
</AnimatedSection><SectionHeader />
The standardised heading block used across all marketing-style sections. Shows an optional category label, a main heading with an optional gradient highlight, and a subtitle.
import { SectionHeader } from "@kaizen-ace-it/shared";
<SectionHeader
label="Why Kaizen?"
title="Built for serious"
highlight="exam success"
subtitle="Every feature is designed to help students study smarter, not harder."
/>| Prop | Type | Description |
|---|---|---|
| label | ReactNode | Small uppercase badge above the title |
| title | ReactNode | Main heading text |
| highlight | ReactNode | Gradient-coloured word(s) appended to the title |
| subtitle | ReactNode | Paragraph below the heading |
<KaizenFooter />
The full site footer. Accepts optional overrides for contact info and supported exam boards.
import { KaizenFooter } from "@kaizen-ace-it/shared";
// Using all defaults
<KaizenFooter />
// With overrides
<KaizenFooter
description="The AI-powered exam platform for African students."
contact={{
email: "[email protected]",
phone: "+233 55 000 0000",
address: "Accra, Ghana",
}}
supportedBoards={["WASSCE", "BECE", "SAT"]}
/><KaizenAvatar />
Displays user initials with an auto-generated gradient background (deterministic — same initials always get the same colour).
import { KaizenAvatar } from "@kaizen-ace-it/shared";
<KaizenAvatar initials="JD" />
<KaizenAvatar initials="AB" size="lg" />
<KaizenAvatar initials="XY" size="xl" />| size | Dimensions |
|---|---|
| sm | 32×32px |
| md (default) | 40×40px |
| lg | 48×48px |
| xl | 64×64px |
<ProgressBar />
Animated progress bar for course completion, quiz scores, etc.
import { ProgressBar } from "@kaizen-ace-it/shared";
<ProgressBar progress={72} />
<ProgressBar progress={45} colorClass="bg-green-500" /><StarRating />
Displays a static star rating.
import { StarRating } from "@kaizen-ace-it/shared";
<StarRating rating={4} />
<StarRating rating={3.5} maxRating={5} size={16} /><ExamBoardBadge />
A pill badge for showing which exam board a course/resource belongs to.
import { ExamBoardBadge } from "@kaizen-ace-it/shared";
<ExamBoardBadge board="WASSCE" />
<ExamBoardBadge board="Cambridge IGCSE" />🪝 Hooks
import { useTheme, useDebounce } from "@kaizen-ace-it/shared";
// or:
import { useTheme, useDebounce } from "@kaizen-ace-it/shared/hooks";useTheme()
Reads and writes the user's dark/light mode preference. Persists to localStorage under STORAGE_KEYS.theme and toggles the dark class on <html>. Pair with <ThemeScript /> in <head> to eliminate flash on load.
"use client";
import { useTheme } from "@kaizen-ace-it/shared";
function ThemeToggle() {
const { theme, toggle } = useTheme();
return <button onClick={toggle}>{theme === "dark" ? "☀️" : "🌙"}</button>;
}useDebounce(value, delayMs?)
Debounces any rapidly-changing value. Useful for search inputs to avoid hammering the API on every keystroke.
"use client";
import { useDebounce } from "@kaizen-ace-it/shared";
function SearchBar() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 400);
useEffect(() => {
if (debouncedQuery) fetchResults(debouncedQuery);
}, [debouncedQuery]);
return <input onChange={(e) => setQuery(e.target.value)} />;
}🔧 Utilities
import { cn } from "@kaizen-ace-it/shared";
// or:
import { cn } from "@kaizen-ace-it/shared/utils";cn(...classes)
Merges Tailwind class strings safely, resolving conflicts intelligently. Wraps clsx + tailwind-merge.
cn("px-4 py-2", isActive && "bg-primary", "px-6")
// → "py-2 bg-primary px-6" (px-4 overridden by px-6)📡 API Client Factory
import { createApiClient } from "@kaizen-ace-it/shared/api";Creates a pre-configured Axios instance that reads the auth token from localStorage and attaches it as a Bearer header automatically.
const api = createApiClient({
baseURL: process.env.NEXT_PUBLIC_API_URL,
});
// Now use like normal axios:
const { data } = await api.get("/courses");🗄️ Redux Store Factory
import { createKaizenStore } from "@kaizen-ace-it/shared/store";Creates a Redux store with redux-persist pre-configured (uses localStorage).
const store = createKaizenStore({
reducer: {
auth: authReducer,
courses: coursesReducer,
},
});🔍 SEO Metadata Factory
import { createMetadata } from "@kaizen-ace-it/shared/seo";Generates consistent Next.js Metadata objects across all apps.
// In any Next.js page or layout:
export const metadata = createMetadata({
title: "Courses | Kaizen Ace It",
description: "Browse AI-powered exam prep courses.",
});Sub-path Imports (Tree-shakeable)
All exports are also available via sub-paths, so you only pull in what you need:
| Sub-path | What it exports |
|---|---|
| @kaizen-ace-it/shared | Everything (constants, utils, hooks, seo, store, components) |
| @kaizen-ace-it/shared/components | All UI components |
| @kaizen-ace-it/shared/hooks | useTheme, useDebounce |
| @kaizen-ace-it/shared/utils | cn |
| @kaizen-ace-it/shared/api | createApiClient |
| @kaizen-ace-it/shared/store | createKaizenStore |
| @kaizen-ace-it/shared/seo | createMetadata |
| @kaizen-ace-it/shared/preset | Tailwind config preset |
| @kaizen-ace-it/shared/tokens.css | CSS design token variables |
TypeScript
Full TypeScript support is included. All components, hooks, and utilities ship with .d.ts declaration files — no @types/ package needed.
Contributing
This package lives in the kaizen-shared repo. Any new shared component, hook, or utility should be:
- Added to the relevant file in
src/ - Exported from
src/index.tsandsrc/components/index.ts - Documented in this README
- Built with
npm run buildand published vianpm publish --access public