@hex-core/themes

Premium theme catalog — midnight, ember, and future presets.

Install

Version 0.1.1Runtime dependency
pnpm
pnpm add @hex-core/themes

What it does

`@hex-core/themes` is the premium-theme catalog. Today it re-exports `midnightTheme` and `emberTheme` from `@hex-core/tokens` and adds catalog helpers shaped to mirror `tokens.listThemes()`. Future presets — `fintech-dark`, `editorial-warm`, `data-dense`, `pastel-soft`, `monochrome-strict` — land here without bumping `tokens`.

The split exists for release independence. `@hex-core/tokens` ships the foundational three themes plus the transformers; tagging a new preset shouldn't force every consumer to retest the transformers. Keeping the catalog separate gives both surfaces their own minor cadence.

If you only need the default theme, depend on `tokens` and skip this package. If you want the studio's full theme switcher (or want your own switcher to discover new presets automatically), depend on `themes`.

Public API

premiumThemes

ts
import { premiumThemes } from "@hex-core/themes";
import type { Theme } from "@hex-core/registry";

declare const premiumThemes: Record<string, Theme>;

Catalog object keyed by theme name. Re-exports `midnightTheme` + `emberTheme` from `@hex-core/tokens` and adds future premium presets here without tokens needing a release.

tsx
import { premiumThemes } from "@hex-core/themes";

console.log(Object.keys(premiumThemes));
// → ["midnight", "ember"]

listPremiumThemes

ts
function listPremiumThemes(): readonly { name: string; theme: Theme }[];

Enumerate the catalog. Mirrors the shape of `tokens.listThemes()` so the studio's theme switcher can concat the two arrays without normalizing.

tsx
import { listPremiumThemes } from "@hex-core/themes";

for (const { name, theme } of listPremiumThemes()) {
	console.log(name, theme.tokens.light["--color-background"].value);
}

getPremiumTheme

ts
function getPremiumTheme(name: string): Theme | undefined;

Look up a single premium theme by name. Returns `undefined` if the name isn't in the catalog — callers should fall back to `defaultTheme` from `tokens`.

tsx
import { getPremiumTheme } from "@hex-core/themes";
import { defaultTheme } from "@hex-core/tokens";

const theme = getPremiumTheme(slug) ?? defaultTheme;

Workflows

Build a unified theme switcher across both packages

Concat the two catalogs and dedupe by name. Drop into the studio sidebar or any settings panel.

tsx
import { listThemes } from "@hex-core/tokens";
import { listPremiumThemes } from "@hex-core/themes";

const all = [...listThemes(), ...listPremiumThemes()];
const byName = new Map(all.map((entry) => [entry.name, entry]));

export function ThemePicker({ value, onChange }: { value: string; onChange: (n: string) => void }) {
	return (
		<select value={value} onChange={(e) => onChange(e.target.value)}>
			{[...byName.keys()].map((n) => (
				<option key={n} value={n}>{n}</option>
			))}
		</select>
	);
}

Resolve a theme name to its strict object

Walk the catalogs in order; if neither owns the name, fall back to `defaultTheme`. Pass the result into the `tokens` transformers.

tsx
import { defaultTheme, listThemes } from "@hex-core/tokens";
import { getPremiumTheme } from "@hex-core/themes";
import type { Theme } from "@hex-core/registry";

export function resolveTheme(name: string): Theme {
	const builtIn = listThemes().find((t) => t.name === name);
	if (builtIn) return builtIn.theme;
	return getPremiumTheme(name) ?? defaultTheme;
}

Compatibility

  • Peer-free — depends only on `@hex-core/registry@^0.2.1` and `@hex-core/tokens@^1.2.0`.
  • Zero React dependencies. Same edge/runtime story as `@hex-core/tokens`.
  • `@hex-core/tokens` continues to export `midnightTheme` and `emberTheme` directly — moving them into `themes` is non-breaking.

See also