initial commit
This commit is contained in:
36
packages/tokens/package.json
Normal file
36
packages/tokens/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@dangerousthings/tokens",
|
||||
"version": "0.1.0",
|
||||
"description": "Canonical design tokens for the Dangerous Things design system",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./brands/*": {
|
||||
"types": "./dist/brands/*.d.ts",
|
||||
"import": "./dist/brands/*.js"
|
||||
},
|
||||
"./css/*": "./dist/css/*"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc && node dist/scripts/generate-css.js",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.3.3",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
}
|
||||
66
packages/tokens/src/brands/classic.ts
Normal file
66
packages/tokens/src/brands/classic.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { BrandTokens } from "../types.js";
|
||||
|
||||
export const classic: BrandTokens = {
|
||||
id: "classic",
|
||||
name: "Classic Cyberpunk",
|
||||
description: "Original dark navy aesthetic with magenta accents",
|
||||
|
||||
dark: {
|
||||
bg: "#0a0e1a",
|
||||
surface: "#121827",
|
||||
surfaceHover: "#1a2332",
|
||||
border: "#1e293b",
|
||||
|
||||
textPrimary: "#e2e8f0",
|
||||
textSecondary: "#94a3b8",
|
||||
textMuted: "#64748b",
|
||||
|
||||
primary: "#00ffff",
|
||||
primaryDim: "#0891b2",
|
||||
secondary: "#ff00ff",
|
||||
accent: "#00ff88",
|
||||
other: "#ff00ff",
|
||||
|
||||
success: "#00ff88",
|
||||
warning: "#ffaa00",
|
||||
error: "#ff0055",
|
||||
info: "#00ffff",
|
||||
},
|
||||
|
||||
light: {
|
||||
bg: "#ffffff",
|
||||
surface: "#f8fafc",
|
||||
surfaceHover: "#f1f5f9",
|
||||
border: "#e2e8f0",
|
||||
|
||||
textPrimary: "#0f172a",
|
||||
textSecondary: "#475569",
|
||||
textMuted: "#94a3b8",
|
||||
|
||||
primary: "#0891b2",
|
||||
primaryDim: "#0e7490",
|
||||
secondary: "#c026d3",
|
||||
accent: "#00ff88",
|
||||
other: "#c026d3",
|
||||
|
||||
success: "#16a34a",
|
||||
warning: "#d97706",
|
||||
error: "#dc2626",
|
||||
info: "#0891b2",
|
||||
},
|
||||
|
||||
typography: {
|
||||
heading: "var(--font-sans)",
|
||||
body: "var(--font-sans)",
|
||||
mono: "'SF Mono', Monaco, 'Cascadia Code', monospace",
|
||||
},
|
||||
|
||||
shape: {
|
||||
bevelSm: "0px",
|
||||
bevelMd: "0px",
|
||||
bevelLg: "0px",
|
||||
radiusSm: "0.25rem",
|
||||
radius: "0.5rem",
|
||||
radiusLg: "0.75rem",
|
||||
},
|
||||
};
|
||||
66
packages/tokens/src/brands/dt.ts
Normal file
66
packages/tokens/src/brands/dt.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { BrandTokens } from "../types.js";
|
||||
|
||||
export const dt: BrandTokens = {
|
||||
id: "dt",
|
||||
name: "Dangerous Things",
|
||||
description: "Official DT brand — Tektur font, neon cyberpunk, beveled corners",
|
||||
|
||||
dark: {
|
||||
bg: "#000000",
|
||||
surface: "#0a0a0a",
|
||||
surfaceHover: "#141414",
|
||||
border: "#00ffff",
|
||||
|
||||
textPrimary: "#ffffff",
|
||||
textSecondary: "#e2e8f0",
|
||||
textMuted: "#94a3b8",
|
||||
|
||||
primary: "#00ffff",
|
||||
primaryDim: "#0891b2",
|
||||
secondary: "#ffff00",
|
||||
accent: "#00ff00",
|
||||
other: "#ff00ff",
|
||||
|
||||
success: "#00ff00",
|
||||
warning: "#ffff00",
|
||||
error: "#ff0000",
|
||||
info: "#00ffff",
|
||||
},
|
||||
|
||||
light: {
|
||||
bg: "#ffffff",
|
||||
surface: "#f8fafc",
|
||||
surfaceHover: "#f1f5f9",
|
||||
border: "#000000",
|
||||
|
||||
textPrimary: "#000000",
|
||||
textSecondary: "#475569",
|
||||
textMuted: "#94a3b8",
|
||||
|
||||
primary: "#0891b2",
|
||||
primaryDim: "#0e7490",
|
||||
secondary: "#a16207",
|
||||
accent: "#15803d",
|
||||
other: "#ff00ff",
|
||||
|
||||
success: "#15803d",
|
||||
warning: "#a16207",
|
||||
error: "#dc2626",
|
||||
info: "#0891b2",
|
||||
},
|
||||
|
||||
typography: {
|
||||
heading: "'Tektur', var(--font-sans)",
|
||||
body: "'Tektur', var(--font-sans)",
|
||||
mono: "'SF Mono', Monaco, 'Cascadia Code', monospace",
|
||||
},
|
||||
|
||||
shape: {
|
||||
bevelSm: "1rem",
|
||||
bevelMd: "2rem",
|
||||
bevelLg: "2.5rem",
|
||||
radiusSm: "0",
|
||||
radius: "0",
|
||||
radiusLg: "0",
|
||||
},
|
||||
};
|
||||
66
packages/tokens/src/brands/supra.ts
Normal file
66
packages/tokens/src/brands/supra.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { BrandTokens } from "../types.js";
|
||||
|
||||
export const supra: BrandTokens = {
|
||||
id: "supra",
|
||||
name: "VivoKey Supra",
|
||||
description: "Material Design 3 with VivoKey blue, rounded corners, elevation shadows",
|
||||
|
||||
dark: {
|
||||
bg: "#000000",
|
||||
surface: "#28292E",
|
||||
surfaceHover: "#303136",
|
||||
border: "#42667E",
|
||||
|
||||
textPrimary: "#ffffff",
|
||||
textSecondary: "#e2e8f0",
|
||||
textMuted: "#94a3b8",
|
||||
|
||||
primary: "#42667E",
|
||||
primaryDim: "#385872",
|
||||
secondary: "#8BAEC7",
|
||||
accent: "#8BAEC7",
|
||||
other: "#F67448",
|
||||
|
||||
success: "#8BB174",
|
||||
warning: "#F67448",
|
||||
error: "#ED4337",
|
||||
info: "#42667E",
|
||||
},
|
||||
|
||||
light: {
|
||||
bg: "#ffffff",
|
||||
surface: "#AFB8C2",
|
||||
surfaceHover: "#C5CCD4",
|
||||
border: "#000000",
|
||||
|
||||
textPrimary: "#000000",
|
||||
textSecondary: "#374151",
|
||||
textMuted: "#6b7280",
|
||||
|
||||
primary: "#000000",
|
||||
primaryDim: "#1f2937",
|
||||
secondary: "#F67448",
|
||||
accent: "#FDFE1B",
|
||||
other: "#F67448",
|
||||
|
||||
success: "#8BB174",
|
||||
warning: "#F67448",
|
||||
error: "#ED4337",
|
||||
info: "#42667E",
|
||||
},
|
||||
|
||||
typography: {
|
||||
heading: "var(--font-sans)",
|
||||
body: "var(--font-sans)",
|
||||
mono: "'SF Mono', Monaco, 'Cascadia Code', monospace",
|
||||
},
|
||||
|
||||
shape: {
|
||||
bevelSm: "0px",
|
||||
bevelMd: "0px",
|
||||
bevelLg: "0px",
|
||||
radiusSm: "0.5rem",
|
||||
radius: "0.625rem",
|
||||
radiusLg: "0.75rem",
|
||||
},
|
||||
};
|
||||
46
packages/tokens/src/index.ts
Normal file
46
packages/tokens/src/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export type {
|
||||
ThemeBrand,
|
||||
ThemeMode,
|
||||
ThemeDefinition,
|
||||
ColorTokens,
|
||||
TypographyTokens,
|
||||
ShapeTokens,
|
||||
BrandTokens,
|
||||
} from "./types.js";
|
||||
|
||||
export { dt } from "./brands/dt.js";
|
||||
export { classic } from "./brands/classic.js";
|
||||
export { supra } from "./brands/supra.js";
|
||||
|
||||
import { dt } from "./brands/dt.js";
|
||||
import { classic } from "./brands/classic.js";
|
||||
import { supra } from "./brands/supra.js";
|
||||
import type { BrandTokens, ThemeDefinition } from "./types.js";
|
||||
|
||||
/** All brand tokens keyed by id */
|
||||
export const brands: Record<string, BrandTokens> = { dt, classic, supra };
|
||||
|
||||
/** Theme registry for UI selectors */
|
||||
export const themes: ThemeDefinition[] = [
|
||||
{
|
||||
id: "dt",
|
||||
name: dt.name,
|
||||
description: dt.description,
|
||||
supportsModes: ["dark", "light", "auto"],
|
||||
defaultMode: "dark",
|
||||
},
|
||||
{
|
||||
id: "classic",
|
||||
name: classic.name,
|
||||
description: classic.description,
|
||||
supportsModes: ["dark", "light", "auto"],
|
||||
defaultMode: "dark",
|
||||
},
|
||||
{
|
||||
id: "supra",
|
||||
name: supra.name,
|
||||
description: supra.description,
|
||||
supportsModes: ["dark", "light", "auto"],
|
||||
defaultMode: "dark",
|
||||
},
|
||||
];
|
||||
129
packages/tokens/src/scripts/generate-css.ts
Normal file
129
packages/tokens/src/scripts/generate-css.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Generates CSS custom property files from canonical TypeScript token definitions.
|
||||
* Run via: node dist/scripts/generate-css.js
|
||||
* Output: dist/css/{brand}.css
|
||||
*/
|
||||
import { writeFileSync, mkdirSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { BrandTokens, ColorTokens } from "../types.js";
|
||||
import { dt } from "../brands/dt.js";
|
||||
import { classic } from "../brands/classic.js";
|
||||
import { supra } from "../brands/supra.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const outDir = join(__dirname, "..", "css");
|
||||
|
||||
/** Convert hex #rrggbb to "r, g, b" triplet for rgba() usage */
|
||||
function hexToRgb(hex: string): string {
|
||||
const h = hex.replace("#", "");
|
||||
const r = parseInt(h.substring(0, 2), 16);
|
||||
const g = parseInt(h.substring(2, 4), 16);
|
||||
const b = parseInt(h.substring(4, 6), 16);
|
||||
return `${r}, ${g}, ${b}`;
|
||||
}
|
||||
|
||||
/** Generate CSS custom properties from color tokens */
|
||||
function colorVars(colors: ColorTokens): string {
|
||||
return ` /* Backgrounds */
|
||||
--color-bg: ${colors.bg};
|
||||
--color-bg-rgb: ${hexToRgb(colors.bg)};
|
||||
--color-surface: ${colors.surface};
|
||||
--color-surface-rgb: ${hexToRgb(colors.surface)};
|
||||
--color-surface-hover: ${colors.surfaceHover};
|
||||
--color-border: ${colors.border};
|
||||
|
||||
/* Text */
|
||||
--color-text-primary: ${colors.textPrimary};
|
||||
--color-text-secondary: ${colors.textSecondary};
|
||||
--color-text-muted: ${colors.textMuted};
|
||||
|
||||
/* Brand Palette */
|
||||
--color-primary: ${colors.primary};
|
||||
--color-primary-rgb: ${hexToRgb(colors.primary)};
|
||||
--color-primary-dim: ${colors.primaryDim};
|
||||
--color-secondary: ${colors.secondary};
|
||||
--color-secondary-rgb: ${hexToRgb(colors.secondary)};
|
||||
--color-accent: ${colors.accent};
|
||||
--color-accent-rgb: ${hexToRgb(colors.accent)};
|
||||
--color-other: ${colors.other};
|
||||
--color-other-rgb: ${hexToRgb(colors.other)};
|
||||
|
||||
/* Status */
|
||||
--color-success: ${colors.success};
|
||||
--color-success-rgb: ${hexToRgb(colors.success)};
|
||||
--color-warning: ${colors.warning};
|
||||
--color-warning-rgb: ${hexToRgb(colors.warning)};
|
||||
--color-error: ${colors.error};
|
||||
--color-error-rgb: ${hexToRgb(colors.error)};
|
||||
--color-info: ${colors.info};
|
||||
--color-info-rgb: ${hexToRgb(colors.info)};`;
|
||||
}
|
||||
|
||||
/** Generate a full brand CSS file */
|
||||
function generateBrandCSS(brand: BrandTokens): string {
|
||||
const id = brand.id;
|
||||
|
||||
// DT is also the default (when no data-brand is set)
|
||||
const darkSelector =
|
||||
id === "dt"
|
||||
? `:root[data-brand="${id}"],\n:root:not([data-brand])`
|
||||
: `:root[data-brand="${id}"]`;
|
||||
|
||||
let css = `/* @dangerousthings/tokens — Generated CSS for "${brand.name}" */\n/* DO NOT EDIT — regenerate with: npm run build -w packages/tokens */\n\n`;
|
||||
|
||||
// Dark mode (default)
|
||||
css += `/* ${brand.name} — Dark Mode (Default) */\n`;
|
||||
css += `${darkSelector} {\n`;
|
||||
css += colorVars(brand.dark) + "\n\n";
|
||||
css += ` /* Typography */\n`;
|
||||
css += ` --font-heading: ${brand.typography.heading};\n`;
|
||||
css += ` --font-body: ${brand.typography.body};\n\n`;
|
||||
css += ` /* Shape */\n`;
|
||||
css += ` --bevel-sm: ${brand.shape.bevelSm};\n`;
|
||||
css += ` --bevel-md: ${brand.shape.bevelMd};\n`;
|
||||
css += ` --bevel-lg: ${brand.shape.bevelLg};\n`;
|
||||
css += ` --radius-sm: ${brand.shape.radiusSm};\n`;
|
||||
css += ` --radius: ${brand.shape.radius};\n`;
|
||||
css += ` --radius-lg: ${brand.shape.radiusLg};\n\n`;
|
||||
css += ` color-scheme: dark;\n`;
|
||||
css += `}\n\n`;
|
||||
|
||||
// Light mode (explicit)
|
||||
css += `/* ${brand.name} — Light Mode */\n`;
|
||||
css += `:root[data-brand="${id}"][data-theme="light"] {\n`;
|
||||
css += colorVars(brand.light) + "\n\n";
|
||||
css += ` color-scheme: light;\n`;
|
||||
css += `}\n\n`;
|
||||
|
||||
// Auto mode (prefers-color-scheme)
|
||||
css += `/* ${brand.name} — Auto Mode (follows system preference) */\n`;
|
||||
css += `@media (prefers-color-scheme: light) {\n`;
|
||||
css += ` :root[data-brand="${id}"][data-theme="auto"] {\n`;
|
||||
css += colorVars(brand.light).replace(/^/gm, " ") + "\n\n";
|
||||
css += ` color-scheme: light;\n`;
|
||||
css += ` }\n`;
|
||||
css += `}\n`;
|
||||
|
||||
// DT gets explicit dark override
|
||||
if (id === "dt") {
|
||||
css += `\n/* Force dark explicitly */\n`;
|
||||
css += `:root[data-brand="${id}"][data-theme="dark"] {\n`;
|
||||
css += ` color-scheme: dark;\n`;
|
||||
css += `}\n`;
|
||||
}
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
// --- Main ---
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
|
||||
for (const brand of [dt, classic, supra]) {
|
||||
const css = generateBrandCSS(brand);
|
||||
const path = join(outDir, `${brand.id}.css`);
|
||||
writeFileSync(path, css, "utf-8");
|
||||
console.log(` tokens: generated ${path}`);
|
||||
}
|
||||
|
||||
console.log(" tokens: CSS generation complete");
|
||||
65
packages/tokens/src/types.ts
Normal file
65
packages/tokens/src/types.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/** Brand identifiers */
|
||||
export type ThemeBrand = "dt" | "classic" | "supra";
|
||||
|
||||
/** Color mode */
|
||||
export type ThemeMode = "dark" | "light" | "auto";
|
||||
|
||||
/** Theme metadata for UI selectors */
|
||||
export interface ThemeDefinition {
|
||||
id: ThemeBrand;
|
||||
name: string;
|
||||
description: string;
|
||||
supportsModes: ThemeMode[];
|
||||
defaultMode: ThemeMode;
|
||||
}
|
||||
|
||||
/** Color tokens for a single mode */
|
||||
export interface ColorTokens {
|
||||
bg: string;
|
||||
surface: string;
|
||||
surfaceHover: string;
|
||||
border: string;
|
||||
|
||||
textPrimary: string;
|
||||
textSecondary: string;
|
||||
textMuted: string;
|
||||
|
||||
primary: string;
|
||||
primaryDim: string;
|
||||
secondary: string;
|
||||
accent: string;
|
||||
other: string;
|
||||
|
||||
success: string;
|
||||
warning: string;
|
||||
error: string;
|
||||
info: string;
|
||||
}
|
||||
|
||||
/** Typography tokens */
|
||||
export interface TypographyTokens {
|
||||
heading: string;
|
||||
body: string;
|
||||
mono: string;
|
||||
}
|
||||
|
||||
/** Shape tokens */
|
||||
export interface ShapeTokens {
|
||||
bevelSm: string;
|
||||
bevelMd: string;
|
||||
bevelLg: string;
|
||||
radiusSm: string;
|
||||
radius: string;
|
||||
radiusLg: string;
|
||||
}
|
||||
|
||||
/** Complete brand definition */
|
||||
export interface BrandTokens {
|
||||
id: ThemeBrand;
|
||||
name: string;
|
||||
description: string;
|
||||
dark: ColorTokens;
|
||||
light: ColorTokens;
|
||||
typography: TypographyTokens;
|
||||
shape: ShapeTokens;
|
||||
}
|
||||
8
packages/tokens/tsconfig.json
Normal file
8
packages/tokens/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user