Sourceful Energy

Using with Vite

The design system is fully compatible with Vite, Create React App, and other non-Next.js setups.

Quick Start

Install the package and import styles:

npm install @sourceful-energy/ui

Basic Setup

The only difference from Next.js is handling dark mode yourself. Here's a complete example:

// main.tsx or App.tsx
import { useState, useEffect } from "react"
import {
  DesignSystemProvider,
  Button,
  Card,
  CardHeader,
  CardTitle,
  CardContent
} from "@sourceful-energy/ui"
import "@sourceful-energy/ui/styles.css"  // Required!
import "./index.css"

function App() {
  // Handle dark mode (replaces next-themes)
  const [darkMode, setDarkMode] = useState(() =>
    window.matchMedia("(prefers-color-scheme: dark)").matches
  )

  // Toggle .dark class on <html>
  useEffect(() => {
    document.documentElement.classList.toggle("dark", darkMode)
  }, [darkMode])

  return (
    <DesignSystemProvider defaultTheme="elevated">
      <div className="min-h-screen bg-background text-foreground p-8">
        <Button onClick={() => setDarkMode(!darkMode)}>
          {darkMode ? "Light Mode" : "Dark Mode"}
        </Button>

        <Card className="mt-4">
          <CardHeader>
            <CardTitle>Works in Vite!</CardTitle>
          </CardHeader>
          <CardContent>
            All components work exactly the same.
          </CardContent>
        </Card>
      </div>
    </DesignSystemProvider>
  )
}

export default App

Dark Mode

The design system uses a .dark class on the <html> element for dark mode. In Next.js, next-themes handles this automatically. In Vite, you toggle it yourself:

// Toggle dark mode - that's it!
document.documentElement.classList.toggle("dark", isDarkMode)

You can use any state management (useState, Zustand, localStorage) to persist the user's preference.

Themes & Accessibility

The DesignSystemProvider handles visual themes and accessibility modes. It's pure React with no framework dependencies:

import {
  DesignSystemProvider,
  useAccessibility,
  Switch
} from "@sourceful-energy/ui"

// Visual themes: "base" (flat) or "elevated" (premium)
<DesignSystemProvider defaultTheme="elevated">
  <App />
</DesignSystemProvider>

// Accessibility hooks work in Vite
function AccessibilitySettings() {
  const {
    fontMode, setFontMode,        // "default" | "dyslexic"
    colorMode, setColorMode,      // color blind modes
    spacingMode, setSpacingMode,  // "default" | "comfortable"
    focusMode, setFocusMode,      // "default" | "enhanced"
  } = useAccessibility()

  return (
    <Switch
      checked={fontMode === "dyslexic"}
      onCheckedChange={(v) => setFontMode(v ? "dyslexic" : "default")}
    />
  )
}

No Next.js Dependencies

The npm package has zero Next.js dependencies. Only React and React DOM are peer dependencies:

{
  "peerDependencies": {
    "react": "^18.0.0 || ^19.0.0",
    "react-dom": "^18.0.0 || ^19.0.0"
  }
}

This means fast installs and no bloat in your Vite project.

Full Example with Persistence

Here's a more complete example with localStorage persistence for dark mode:

import { useState, useEffect } from "react"

function useDarkMode() {
  const [darkMode, setDarkMode] = useState(() => {
    // Check localStorage first, then system preference
    const stored = localStorage.getItem("darkMode")
    if (stored !== null) return stored === "true"
    return window.matchMedia("(prefers-color-scheme: dark)").matches
  })

  useEffect(() => {
    // Update class and localStorage
    document.documentElement.classList.toggle("dark", darkMode)
    localStorage.setItem("darkMode", String(darkMode))
  }, [darkMode])

  return [darkMode, setDarkMode] as const
}

// Use in your app
function App() {
  const [darkMode, setDarkMode] = useDarkMode()

  return (
    <DesignSystemProvider defaultTheme="elevated">
      <Button onClick={() => setDarkMode(!darkMode)}>
        Toggle Theme
      </Button>
    </DesignSystemProvider>
  )
}