JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q89975F
  • 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

codefolio-ui

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

npm version license types


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.


For vibe coders

Using an AI coding assistant (Cursor, Claude, Copilot, ChatGPT)? Copy the prompt below and paste it at the start of your chat. It tells the agent to read the ai.md file shipped inside the package — a complete machine-readable API reference with every component, prop, variant, and working example.

You are helping me build a UI using the codefolio-ui component library.

Before writing any code, read the full API reference at:
  node_modules/codefolio-ui/ai.md

Key rules from that file:
- Always mount <ThemeProvider theme="system" style={{ display: 'none' }} /> once at the app root — components will not render correctly without it.
- Import React components from 'codefolio-ui/react', not 'codefolio-ui'.
- Button and Chip use a label prop, not children.
- Never invent prop names or variant strings — only use the ones in ai.md.
- Table requires all 7 sub-components (Table, TableHead, TableHeadRow, TableHeadCell, TableBody, TableRow, TableCell).

Once you have read ai.md, confirm and then help me build my UI.

The ai.md file is published inside the npm package at node_modules/codefolio-ui/ai.md and is also available on the CDN at https://cdn.jsdelivr.net/npm/codefolio-ui/ai.md.


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