@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/shared

Peer 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.png into your app's /public/images/ directory, or pass a custom src.


<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:

  1. Added to the relevant file in src/
  2. Exported from src/index.ts and src/components/index.ts
  3. Documented in this README
  4. Built with npm run build and published via npm publish --access public