JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 37
  • Score
    100M100P100Q90211F
  • License MIT

Web Component UI library for React, Vue, Angular, and Svelte — built with Lit, dark mode ready, zero dependencies.

Package Exports

  • codefolio-ui
  • codefolio-ui/react
  • codefolio-ui/styles

Readme

codefolio-ui

Web Component UI library for React, Vue, Angular, and Svelte — built with Lit, dark mode ready, zero dependencies.

npm version license types


For AI Agents

This package ships an ai.md file — a complete, machine-readable API reference written specifically for AI coding assistants. It covers every component, every prop, every variant string, common mistakes, and a full working example.

After installing the package, point your agent at it:

node_modules/codefolio-ui/ai.md

Or read it directly from the npm registry:

https://cdn.jsdelivr.net/npm/codefolio-ui/ai.md

Overview

codefolio-ui is a design system built on Lit Web Components. Every component ships as a native custom element, meaning it works in any JavaScript framework or plain HTML — with no runtime coupling to React, Vue, Angular, or Svelte.

  • 15 components covering layout, navigation, typography, data display, and inputs
  • Dark mode via CSS custom properties and a data-theme attribute on <html>
  • React wrappers via @lit/react for React 17/18 (typed props + event forwarding)
  • React 19+ native custom element support — wrappers optional
  • ESM + CJS dual build via tsup
  • Full TypeScript — every component ships .d.ts types

Requirements

Requirement Version Notes
Node.js ≥ 18
npm ≥ 7 Peer deps auto-install on npm 7+
@lit/react ^1.0.4 Auto-installed as a peer dependency
react + react-dom ≥ 17 Only needed for React projects

@lit/react is a required peer dependency for React users. On npm 7+, it installs automatically alongside codefolio-ui. On npm 6 or older, install it manually (see below).


Installation

Run a single command — npm 7+ will automatically install @lit/react, react, and react-dom as peer dependencies:

npm install codefolio-ui

If you are on npm 6 or older, peer dependencies do not auto-install. Run:

npm install codefolio-ui @lit/react react react-dom

Verify all three peer deps are present after install:

npm ls @lit/react react react-dom

Vue / Svelte / Angular projects

No peer dependencies needed. Install the core package only:

npm install codefolio-ui

Plain HTML / CDN

No install needed — use the jsDelivr CDN directly (see CDN usage below).


Required setup — ThemeProvider

All components depend on CSS design tokens. Without ThemeProvider mounted once in your app, components will render with no colours, no spacing, and no dark mode.

ThemeProvider injects a <style> tag into <head> containing all CSS custom properties (--cf-primary, --cf-surface, --cf-on-surface, etc.) that every component uses internally.

Place it once at the root of your app, before any other components:

// App.tsx (React)
import { ThemeProvider } from 'codefolio-ui/react'

export default function App() {
  return (
    <>
      <ThemeProvider theme="system" style={{ display: 'none' }} />
      {/* rest of your app */}
    </>
  )
}
  • theme="system" — follows the user's OS preference (light/dark). Also accepts "light" or "dark".
  • style={{ display: 'none' }} — the element renders nothing visible, hide it to avoid layout gaps.
  • The resolved theme is persisted in localStorage under the key cf-theme and survives page reloads.
  • useRef is optional — only needed if you want to call themeRef.current.toggle() programmatically.

Vue / Svelte / Angular / HTML:

<codefolio-theme-provider theme="system" style="display:none"></codefolio-theme-provider>

Usage

React 17 / 18

Import from the /react sub-path. Wrappers are generated with @lit/react so typed props, onX event handlers, and refs all work natively.

import { Button, Card, Navbar } from 'codefolio-ui/react'
import type { ButtonVariant } from 'codefolio-ui/react'

export default function App() {
  return (
    <>
      <Navbar brand="My App" variant="full" position="on-top-always" />
      <Card variant="elevated" style={{ padding: '1.5rem' }}>
        <Button variant="primary" size="lg" label="Get started" />
      </Card>
    </>
  )
}

React 19+

React 19 has native Web Component support. Both patterns are valid:

Option A — React wrappers (full TypeScript types, onX events, refs)

import { Button, Card } from 'codefolio-ui/react'

export default function App() {
  return <Button variant="primary" label="Hello" />
}

Option B — Native custom elements (no wrappers needed)

import 'codefolio-ui' // registers all custom elements

export default function App() {
  return <codefolio-button variant="primary">Hello</codefolio-button>
}

Vue / Svelte

Import the main bundle to register all custom elements, then use the HTML tags directly in your templates.

// main.ts / main.js
import 'codefolio-ui'
<!-- Vue SFC / Svelte template -->
<codefolio-button variant="primary" label="Hello"></codefolio-button>
<codefolio-card variant="elevated">
  <codefolio-text variant="title-lg">Card title</codefolio-text>
</codefolio-card>

Angular

Register all custom elements in main.ts, then add CUSTOM_ELEMENTS_SCHEMA to suppress "unknown element" warnings.

// main.ts
import 'codefolio-ui'
import { bootstrapApplication } from '@angular/platform-browser'
import { AppComponent } from './app/app.component'

bootstrapApplication(AppComponent)

Standalone component:

import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'

@Component({
  selector: 'app-root',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <codefolio-button variant="primary" label="Hello"></codefolio-button>
    <codefolio-card variant="elevated">
      <codefolio-text variant="title-lg">Card title</codefolio-text>
    </codefolio-card>
  `,
})
export class AppComponent {}

NgModule (non-standalone):

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { AppComponent } from './app.component'

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  bootstrap: [AppComponent],
})
export class AppModule {}

Plain HTML / CDN

<script type="module">
  import 'https://cdn.jsdelivr.net/npm/codefolio-ui/dist/index.js'
</script>

<codefolio-button variant="primary">Hello</codefolio-button>
<codefolio-card variant="elevated">
  <codefolio-text variant="body-md">Card content</codefolio-text>
</codefolio-card>

Theming

Toggle programmatically

Add a ref only if you need to call toggle() from code:

import { ThemeProvider } from 'codefolio-ui/react'
import { useRef } from 'react'

export default function App() {
  const themeRef = useRef<any>(null)

  return (
    <>
      <ThemeProvider ref={themeRef} theme="system" style={{ display: 'none' }} />
      <button onClick={() => themeRef.current?.toggle()}>Toggle theme</button>
    </>
  )
}

Or toggle directly on the DOM without a ref:

document.documentElement.setAttribute('data-theme', 'dark')
localStorage.setItem('cf-theme', 'dark') // persisted across reloads

Listen to changes

<ThemeProvider
  theme="system"
  onThemeChange={e => console.log(e.detail.theme)} // 'light' | 'dark'
  style={{ display: 'none' }}
/>

The resolved theme is persisted in localStorage under the key cf-theme and survives page reloads.


Components

Component Custom element Description
Button <codefolio-button> 4 variants, 3 sizes, disabled state
Chip <codefolio-chip> Tag, filled, and outlined chips
Card <codefolio-card> Surface card — outlined, elevated, filled, glass
Text <codefolio-text> Full typographic scale — display, title, body, label, eyebrow
Navbar <codefolio-navbar> 4 layout variants, mobile drawer, theme toggle
BoxGroup <codefolio-box-group> Bento-grid layout system
Section <codefolio-section> Full-width page section with 4 background variants
Timeline <codefolio-timeline> Experience / history timeline
Outcomes <codefolio-outcomes> Auto-cycling metric cards
Sidebar <codefolio-sidebar> Fixed scrollspy navigation sidebar
Table <codefolio-table> Composable data table with muted/good cell variants
Terminal <codefolio-terminal> Animated terminal command sequence
CodeBlock <codefolio-code-block> Syntax-highlighted code display
ServiceScroller <codefolio-service-scroller> Infinite marquee scroller
ThemeProvider <codefolio-theme-provider> Light / dark / system theming

Component API examples

Button

<Button variant="primary"   size="lg"  label="Get started" />
<Button variant="secondary" size="md"  label="Learn more" />
<Button variant="ghost"     size="sm"  label="Cancel" />
<Button variant="cta"       label="Book a call" />
<Button variant="primary"   label="Disabled" disabled />
Prop Type Default Description
variant 'primary' | 'secondary' | 'ghost' | 'cta' 'primary' Visual style
size 'sm' | 'md' | 'lg' 'md' Controls padding and font size
label string Button text (required)
disabled boolean false Disables click interaction
onClick (e: Event) => void Fires on click

Card

<Card variant="elevated" style={{ padding: '1.5rem' }}>
  <Text variant="title-lg">Title</Text>
  <Text variant="body-sm">Description text goes here.</Text>
</Card>
Prop Type Default Description
variant 'outlined' | 'elevated' | 'filled' | 'glass' 'outlined' Surface elevation and style
href string Renders card as <a> with hover/focus styles
const NAV: NavItem[] = [
  { label: 'Home',    href: '/' },
  { label: 'About',   href: '/about' },
  { label: 'Work',    href: '/work' },
  { label: 'Contact', href: '/contact' },
]

<Navbar
  variant="full"
  position="on-top-always"
  brand="My App"
  items={NAV}
  activeHref={location.pathname}
  ctaLabel="Contact"
  ctaHref="/contact"
  onClick={e => router.push(e.detail.href)}
  onThemeToggle={() => toggleDark()}
/>
Prop Type Default Description
variant 'full' | 'minimal' | 'centered' | 'transparent' 'full' Layout and visual style
position 'on-top-always' | 'scrolled' | 'static' 'on-top-always' CSS positioning strategy
brand string Brand name shown left of nav
items NavItem[] Array of { label, href } nav items (required)
activeHref string Highlights the matching nav item
ctaLabel string Primary CTA button label

Table

import { Table, TableHead, TableHeadRow, TableHeadCell,
         TableBody, TableRow, TableCell } from 'codefolio-ui/react'

<Table>
  <TableHead>
    <TableHeadRow>
      <TableHeadCell>Metric</TableHeadCell>
      <TableHeadCell>Before</TableHeadCell>
      <TableHeadCell>After</TableHeadCell>
    </TableHeadRow>
  </TableHead>
  <TableBody>
    <TableRow>
      <TableCell>Load time</TableCell>
      <TableCell variant="muted">4.2s</TableCell>
      <TableCell variant="good">1.1s</TableCell>
    </TableRow>
  </TableBody>
</Table>

TableCell variant values: 'default' | 'muted' (strikethrough) | 'good' (highlighted, bold)

import { Sidebar } from 'codefolio-ui/react'
import type { SidebarItem } from 'codefolio-ui/react'

const SECTIONS: SidebarItem[] = [
  { id: 'intro',    label: 'Introduction', icon: 'home'        },
  { id: 'approach', label: 'Approach',     icon: 'lightbulb'   },
  { id: 'outcomes', label: 'Outcomes',     icon: 'trending_up' },
]

<Sidebar
  items={SECTIONS}
  title="On this page"
  topOffset={80}
  onSidebarChange={e => console.log(e.detail.id)}
/>

<section id="intro">...</section>
<section id="approach">...</section>
<section id="outcomes">...</section>

The sidebar uses IntersectionObserver to automatically highlight the active section as the user scrolls. Hidden below 1024px — pair with a mobile nav pattern for small screens.

Terminal

import { Terminal } from 'codefolio-ui/react'
import type { TermSequenceItem } from 'codefolio-ui/react'

const SEQ: TermSequenceItem[] = [
  {
    dir: '~/project',
    cmd: 'npm install codefolio-ui',
    out: ['added 1 package in 0.8s', 'found 0 vulnerabilities'],
  },
  {
    dir: '~/project',
    cmd: 'npm run build',
    out: ['⚡  Build success in 74ms', 'dist/index.js  97 KB'],
  },
]

<Terminal sequence={SEQ} />

Package exports

Import path Contents
codefolio-ui All custom elements (registers on import)
codefolio-ui/react React wrappers + TypeScript types
codefolio-ui/styles CSS design token stylesheet

Development

# Install dependencies
npm install

# Build the library (ESM + CJS)
npm run build

# Watch mode
npm run build:watch

# Type check
npm run type-check

# Lint
npm run lint

# Storybook
npm run storybook

Browser support

All modern browsers that support Web Components v1 (Chrome, Firefox, Safari, Edge). No polyfills required.


License

MIT — see LICENSE for details.


Author

Built by Akash Desai