Skip to content

kirilinsky/varth

Repository files navigation

var-th

codecov npm downloads npm bundle size license

var-th

Stop writing CSS variables by hand. Define your themes as compact arrays, get type-safe utilities back. No runtime overhead, no magic — just your tokens turned into --custom-properties and a few helpers to apply them.

Install

npm install var-th

Core

Setup

import { defineThemes } from "var-th";

const { getVarths, inject, toCSS, toTypes, themeNames } = defineThemes({
  tokens: ["accent", "bg", "color", "textSm"] as const,
  themes: {
    light: ["#3b82f6", "#ffffff", "#111827", "16px"],
    dark: ["#60a5fa", "#0f172a", "#f1f5f9", "16px"],
  },
});

prefix is optional and defaults to "th" — so your variables come out as --th-accent, --th-bg, --th-color, --th-textSm without any extra config. Set it explicitly if you want something else:

defineThemes({
  prefix: "brand",
  tokens: ["accent", "bg"] as const,
  themes: { ... },
});
// → --brand-accent, --brand-bg

getVarths(name)

Returns a CSS variable object ready to drop into style={}. The fastest way to apply a theme to any element:

<div style={getVarths("light")}>...</div>

// getVarths("light") returns:
{
  "--th-accent": "#3b82f6",
  "--th-bg": "#ffffff",
  "--th-color": "#111827",
  "--th-textSm": "16px",
}

inject()

Injects all themes into <head> once at app startup. After that, switching themes is just changing a data-theme attribute anywhere in the DOM — no re-renders, no JS:

inject(); // appends <style id="var-th-th"> to document.head
<div data-theme="dark">...</div>

Calling inject() multiple times is safe — it updates the same tag, never duplicates it.


toCSS()

Generates a full CSS string with :root and [data-theme] blocks. Use it for SSR or static exports:

// Next.js layout.tsx
<style dangerouslySetInnerHTML={{ __html: toCSS() }} />

Output:

:root,
[data-theme="light"] {
  --th-accent: #3b82f6;
  --th-bg: #ffffff;
  --th-color: #111827;
  --th-textSm: 16px;
}

[data-theme="dark"] {
  --th-accent: #60a5fa;
  --th-bg: #0f172a;
  --th-color: #f1f5f9;
  --th-textSm: 16px;
}

toTypes()

Codegen step — run once, commit the result, get autocomplete on your CSS variables everywhere:

import { writeFileSync } from "fs";
writeFileSync("./src/theme.d.ts", toTypes());

Produces:

export type ThemeToken =
  | "--th-accent"
  | "--th-bg"
  | "--th-color"
  | "--th-textSm";

export type ThemeName = "light" | "dark";

themeNames

Array of all defined theme names — handy for building a theme picker:

themeNames.map(t => (
  <button onClick={() => setTheme(t)}>{t}</button>
))

Frameworks

React

import { ThemeProvider, useTheme } from "var-th/react";

<ThemeProvider default="light" inject={inject} themeNames={themeNames}>
  <App />
</ThemeProvider>;
const { theme, setTheme, themeNames } = useTheme()

<button onClick={() => setTheme("dark")}>switch theme</button>

Theme name is persisted to localStorage automatically.


License

MIT

About

CSS theme creator/injector. Native JS | React and more.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors