Sign In

@mythxengine/glyphkit

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mythxengine/glyphkit

GlyphKit — 16-bit pixel-art React design system with genre-specific themes, runtime CSS variable generation, and optional Framer Motion integration.

Source
npmnpm
Version
0.1.1
Version published
Weekly downloads
13
62.5%
Maintainers
1
Weekly downloads
 
Created
Source

GlyphKit

@mythxengine/glyphkit — a 16-bit pixel-art React design system

GlyphKit is a dark-mode-first, genre-aware design system built for retro and TTRPG-style applications. Themes are generated as CSS custom properties at runtime, so swapping between Fantasy, Sci-Fi, Paranoia, Horror, and Dungeon aesthetics is a single hook call — no static CSS bundle, no FOUC.

An optional @mythxengine/glyphkit/motion subpath ships pixel-perfect Framer Motion wrappers with step-based easing that matches steps(N, end) timing.

Installation

pnpm add @mythxengine/glyphkit
# or
npm install @mythxengine/glyphkit

Peer dependencies:

PackageVersionRequired?
react^18.0.0 || ^19.0.0yes
react-dom^18.0.0 || ^19.0.0yes
framer-motion^11.0.0 || ^12.0.0optional, only if you import from @mythxengine/glyphkit/motion

Quick Start

import { ThemeProvider, themeRegistry, useTheme } from "@mythxengine/glyphkit";

function App() {
  return (
    <ThemeProvider themes={themeRegistry} defaultTheme="kingdom-quest">
      <YourApp />
    </ThemeProvider>
  );
}

function YourApp() {
  const { theme, setTheme } = useTheme();
  return (
    <div>
      <h1 style={{ color: "var(--color-primary-500)" }}>{theme.displayName}</h1>
      <button onClick={() => setTheme("neon-terminal")}>Switch to Sci-Fi</button>
    </div>
  );
}

ThemeProvider injects the active theme's CSS custom properties on <html> and listens for prefers-color-scheme changes.

For the pixel-utility classes (.border-pixel*, .shadow-pixel-*, .font-pixel, .effect-scanlines, etc.) plus reduced-motion / focus-visible defaults, import the baseline stylesheet once at app entry:

import "@mythxengine/glyphkit/styles";

The stylesheet is theme-neutral — ThemeProvider will overwrite the first-paint defaults at runtime.

Subpath exports

SubpathPurpose
@mythxengine/glyphkitTheme provider, registry, generator, components, transitions
@mythxengine/glyphkit/motionFramer Motion wrappers (PixelButton, PixelCard, hooks, …)
@mythxengine/glyphkit/iconsTheme-aware icon registry built on pixelarticons
@mythxengine/glyphkit/stylesBaseline stylesheet: pixel utility classes, effects, a11y defaults (CSS)

Theme Presets

IDNameGenrePrimaryAccent
kingdom-questKingdom QuestFantasyPurple (#9686ab)Gold (#d4a72c)
neon-terminalNeon TerminalSci-FiCyan (#00d4ff)Magenta (#ff00ff)
alpha-complexAlpha ComplexParanoiaRed (#cc0000)Yellow (#ffcc00)
shadow-realmShadow RealmHorrorCrimson (#8b2942)Sickly Green (#4a8b2a)
deep-dungeonDeep DungeonDungeonStone Gray (#4a4a58)Torchlight (#e07020)

API

<ThemeProvider />

<ThemeProvider
  themes={themeRegistry} // required
  defaultTheme="kingdom-quest" // optional initial preset
  defaultColorScheme="dark" // "dark" | "light"
  storageKey="glyphkit-theme" // localStorage key (default)
>
  {children}
</ThemeProvider>

useTheme()

const {
  theme, // PixelThemeConfig
  themeId, // ThemePresetId
  colorScheme, // "dark" | "light"
  setTheme, // (id: ThemePresetId) => void
  setColorScheme,
  transition, // TransitionVariant
  setTransition,
  isLoaded, // boolean — true after hydration
} = useTheme();

Pre-built UI

import { ThemeSelector, ThemeToggle } from "@mythxengine/glyphkit";

<ThemeSelector />            // button grid
<ThemeSelector compact />    // dropdown
<ThemeToggle />              // cycle through themes

CSS Variables

Every preset emits a consistent token surface:

/* Colors */
--color-primary-500;
--color-accent-500;
--background-primary;
--foreground-primary;
--surface-primary;

/* Pixel styling */
--grid-unit: 8px;
--border-thin: 1px;
--border-medium: 2px;
--border-thick: 4px;
--shadow-inset;
--shadow-raised;
--shadow-glow;

/* Typography */
--font-pixel: "Press Start 2P", monospace;
--font-terminal: "VT323", monospace;

/* Animation */
--timing-function: steps(4, end);
--duration-fast: 100ms;
--duration-normal: 200ms;
--duration-slow: 400ms;

Programmatic CSS

import { generateFullStylesheet, generateCSSVariables } from "@mythxengine/glyphkit";

const css = generateFullStylesheet(theme, "dark"); // full :root + [data-theme] block
const vars = generateCSSVariables(theme, "dark"); // object for inline styles or SSR

Motion Module

pnpm add framer-motion
import { PixelMotion, PixelButton, pixelEasing } from "@mythxengine/glyphkit/motion";

<PixelButton variant="pixel" onClick={onClick}>Press Start</PixelButton>

<PixelMotion
  as="div"
  steps={4}
  duration="fast"
  pixelSnap
  whileHover={{ y: -2 }}
  whileTap={{ y: 2 }}
>
  Hover me
</PixelMotion>
PresetStepsDurationUse case
instant10msSnap
retro22100msNES-style
retro44200msSNES-style (default)
retro88400msSmoother retro

Components: PixelMotion, PixelButton, PixelCard, PixelStatusBar, PixelDialog, PixelIcon. Hooks: usePixelAnimation, usePixelSpring, useReducedMotion.

All motion respects prefers-reduced-motion.

Escape hatch — when GlyphKit motion isn't enough

The motion module is opinionated about step-based easing (steps(N, end)) because that's what makes the aesthetic work. For animations that need genuinely smooth easing (bezier curves, spring physics that mid-flight, page-turn transitions, etc.), import framer-motion directly:

import { motion, AnimatePresence } from "framer-motion";

// Custom bezier, smooth — explicitly outside the step-easing model.
<motion.div animate={{ x: 100 }} transition={{ duration: 0.4, ease: [0.2, 0.8, 0.2, 1] }} />;

The genre/variants helpers (buttonVariants, cardVariantPresets, etc.) compose with raw framer-motion — pass them into your own motion.X and you keep the look without going through PixelMotion.

Motion variant state vocabulary

Each variant family has its own lifecycle state names. Stick to these when composing custom motion.X elements with a preset:

FamilyStates
buttonVariantPresetsinitial, hover, tap, disabled, pulse (neon only)
cardVariantPresetsinitial, hover, tap, selected, hidden, visible, enter, exit
dialogVariantPresetshidden, visible, exit (note: no initial)
statusBarVariantPresetscomplete, countdown, counting, critical, damage, …
cursorVariantPresetsstill, visible, blink, bob, pulse, spin, selected
genre presetseach variant exposes its own (chestOpen, hologram, bloodDrip, …)

Customising the Registry

import { createRegistry, themeRegistry, ThemeProvider } from "@mythxengine/glyphkit";

const custom = createRegistry({
  "kingdom-quest": {
    ...themeRegistry["kingdom-quest"],
    colors: {
      ...themeRegistry["kingdom-quest"].colors,
      dark: {
        ...themeRegistry["kingdom-quest"].colors.dark,
        primary: { 500: "#ff0000" },
      },
    },
  },
});

<ThemeProvider themes={custom}>...</ThemeProvider>;

Next.js (App Router)

Wrap the provider in a client component:

"use client";
import { ThemeProvider, themeRegistry } from "@mythxengine/glyphkit";

export function Providers({ children }: { children: React.ReactNode }) {
  return <ThemeProvider themes={themeRegistry}>{children}</ThemeProvider>;
}

The motion subpath requires the same "use client" boundary because Framer Motion is client-only.

Versioning & Releases

GlyphKit follows Semantic Versioning. Releases are managed with Changesets; see the monorepo .changeset/ directory for in-flight changes.

License

MIT © Josh Mabry / protoLabs. See LICENSE.

Keywords

design-system

FAQs

Package last updated on 12 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts