A type-safe design system with CSS custom properties and TypeScript tokens.
npm install nice-stylesImport all CSS variables:
@import 'nice-styles/variables.css';
.card {
padding: var(--gap-base);
border-radius: var(--border-radius-base);
color: var(--foreground-color-base);
}Or import individual token groups:
@import 'nice-styles/css/fontSize.css';
@import 'nice-styles/css/gap.css';For backward compatibility with deprecated variables:
@import 'nice-styles/deprecated.css';Option 1: Import from main package (recommended)
import { fontSize, foregroundColor, gap } from 'nice-styles'
console.log(fontSize.base) // "16px"
console.log(foregroundColor.link) // "hsla(202, 100%, 50%, 1)"
console.log(gap.large) // "32px"Option 2: Import from specific files
// Import tokens
import { fontSize } from 'nice-styles/tokens.js'
// Import constants
import { FONT_SIZE_BASE } from 'nice-styles/constants.js'nice-styles provides design tokens in three complementary formats:
Raw constant values exported individually:
import { FONT_SIZE_BASE, FOREGROUND_COLOR_LINK } from 'nice-styles'
console.log(FONT_SIZE_BASE) // "16px"
console.log(FOREGROUND_COLOR_LINK) // "hsla(202, 100%, 50%, 1)"Use when: You need direct access to individual constant values.
Organized token objects with semantic keys:
import { fontSize, foregroundColor } from 'nice-styles'
console.log(fontSize.base) // "16px"
console.log(fontSize.large) // "20px"
console.log(foregroundColor.link) // "hsla(202, 100%, 50%, 1)"
console.log(foregroundColor.error) // "hsla(10, 92%, 63%, 1)"Use when: You want semantic organization and better autocomplete.
CSS variables for runtime styling:
:root {
--font-size-base: "16px";
--font-size-large: "20px";
--foreground-color-link: "hsla(202, 100%, 50%, 1)";
--foreground-color-error: "hsla(10, 92%, 63%, 1)";
}Use when: You need runtime CSS theming and custom properties.
┌─────────────────────┐
│ src/tokens.json │ ← Central token definitions
│ (Source of truth) │
└──────────┬──────────┘
│
├────────────────────────┬──────────────────────┐
↓ ↓ ↓
┌──────────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ scripts/generateCss │ │scripts/generate │ │ src/services/ │
│ │ │ Types │ │ *.ts files │
└──────────┬───────────┘ └──────────┬───────┘ └────────┬────────┘
│ │ │
↓ ↓ │
┌──────────────┐ ┌──────────────┐ │
│variables.css │ │dist/types.d.ts│ │
│dist/css/*.css│ └──────────────┘ │
└──────────────┘ ↑ │
(bypasses tsc) ↓
┌─────────────┐
│ TypeScript │
│ Compiler │
│ (excludes │
│ utilities) │
└──────┬──────┘
│
↓
┌─────────────┐
│ dist/ │
│ services/ │
│ *.js │
│ *.d.ts │
└─────────────┘
The dist/ directory contains all compiled outputs consumed by users:
-
dist/index.js+dist/index.d.ts- Main entry point
- Exports all constants, tokens, and types
- Used when:
import { fontSize } from 'nice-styles'
-
dist/constants.js+dist/constants.d.ts- All constant values (SCREAMING_SNAKE_CASE)
- Compiled from
src/constants.ts
-
dist/tokens.js+dist/tokens.d.ts- All token objects (camelCase)
- Generated from constants.ts, compiled by TypeScript
-
dist/types.d.ts- TypeScript type definitions for token keys
- Auto-generated from tokens.json (bypasses TypeScript compiler)
- Exports:
AnimationDurationType,FontSizeType,ForegroundColorType, etc. - Each type is a union of valid keys for that token group
-
variables.css(root level)- All CSS custom properties in one file
- Used when:
@import 'nice-styles/variables.css'
-
dist/css/*.css(individual token files)animationDuration.cssfontSize.cssforegroundColor.cssgap.css- ...and 11 more
- Used when:
@import 'nice-styles/dist/css/fontSize.css'
| Token | Keys | Example |
|---|---|---|
animationDuration |
base, slow | "300ms", "600ms" |
animationEasing |
base | "ease-in-out" |
backgroundColor |
base, alternate | "hsla(0, 100%, 100%, 1)" |
borderColor |
base, dark, darker | "hsla(240, 9%, 91%, 1)" |
borderRadius |
smaller, small, base, large, larger | "2px" to "32px" |
borderWidth |
base, large | "1.5px", "2px" |
boxShadow |
downBase, downLarge, upBase, upLarge | Shadow values |
cellHeight |
smaller, small, base, large, larger | "24px" to "72px" |
foregroundColor |
lighter, light, medium, dark, base, link, success, warning, error | Color values |
fontFamily |
base, code, heading | Font stacks |
fontSize |
smaller, small, base, large, larger | "12px" to "24px" |
fontWeight |
light, base, medium, semibold, bold, extrabold, black | "300" to "900" |
gap |
smaller, small, base, large, larger | "4px" to "48px" |
iconStrokeWidth |
base, large | "1.5px", "2px" |
lineHeight |
condensed, base, expanded | "1.25" to "1.75" |
import { fontSize, foregroundColor, gap } from 'nice-styles'
const styles = {
fontSize: fontSize.base,
color: foregroundColor.base,
padding: gap.base,
linkColor: foregroundColor.link,
errorColor: foregroundColor.error,
}.button {
font-size: var(--font-size-base);
padding: var(--gap-small) var(--gap-base);
border-radius: var(--border-radius-base);
background: var(--background-color-base);
color: var(--foreground-color-base);
font-weight: var(--font-weight-medium);
}
.button-primary {
background: var(--foreground-color-link);
color: var(--background-color-base);
}
.alert-error {
color: var(--foreground-color-error);
border: var(--border-width-base) solid var(--foreground-color-error);
border-radius: var(--border-radius-small);
padding: var(--gap-small);
}import { getToken } from 'nice-styles'
// Get token with CSS variable and raw value
const fontSize = getToken('fontSize')
console.log(fontSize.key) // "--font-size-base"
console.log(fontSize.var) // "var(--font-size-base)"
console.log(fontSize.value) // "16px"
// Get specific token item
const large = getToken('fontSize', 'large')
console.log(large.value) // "24px"Each token group has a corresponding type that represents all valid keys:
import type { FontSizeType, ForegroundColorType, GapType } from 'nice-styles'
// Type-safe token key usage
function setFontSize(size: FontSizeType) {
return fontSize[size]
}
setFontSize('base') // ✓ Valid
setFontSize('large') // ✓ Valid
setFontSize('huge') // ✗ Type error
// Use in component props
interface ButtonProps {
size?: FontSizeType
color?: ForegroundColorType
spacing?: GapType
}
const Button = ({ size = 'base', color = 'base', spacing = 'base' }: ButtonProps) => ({
fontSize: fontSize[size],
color: foregroundColor[color],
padding: gap[spacing],
})Available types:
AnimationDurationType- "base" | "slow"AnimationEasingType- "base"BackgroundColorType- "base" | "alternate"BorderColorType- "base" | "heavy" | "heavier"BorderRadiusType- "smaller" | "small" | "base" | "large" | "larger"BorderWidthType- "base" | "large"BoxShadowType- "downBase" | "downLarge" | "upBase" | "upLarge"CellHeightType- "smaller" | "small" | "base" | "large" | "larger"ForegroundColorType- "lighter" | "light" | "medium" | "heavy" | "base" | "disabled" | "link" | "success" | "warning" | "error"FontFamilyType- "base" | "code" | "heading"FontSizeType- "smaller" | "small" | "base" | "large" | "larger"FontWeightType- "light" | "base" | "medium" | "semibold" | "bold" | "extrabold" | "black"GapType- "smaller" | "small" | "base" | "large" | "larger"LineHeightType- "condensed" | "base" | "expanded"
- Edit
src/constants.tsand add your constants with a// Token:comment:
// Token: BUTTON_SIZE
export const BUTTON_SIZE_SMALL = "32px"
export const BUTTON_SIZE_MEDIUM = "40px"
export const BUTTON_SIZE_LARGE = "48px"- Run the token generator:
npm run build:tokensThis automatically generates:
src/tokens.tswithbuttonSizetoken objectsrc/types.tswithButtonSizeType = "small" | "medium" | "large"- CSS variables in
variables.css - Individual
dist/css/buttonSize.cssfile
- Compile TypeScript:
npm run build:tsOr run both with:
npm run build# Generate tokens from constants
npm run build:tokens
# Compile TypeScript
npm run build:ts
# Run both
npm run build
# Watch mode
npm run watchVersion 4.0.0 removes all deprecated numbered token variants (e.g., FONT_SIZE_1, BORDER_RADIUS_2).
Before (v3.x):
import { FONT_SIZE_1, borderRadius1 } from 'nice-styles'After (v4.x):
import { FONT_SIZE_BASE, borderRadius } from 'nice-styles'
console.log(borderRadius.base)All numbered variants have been removed. Use semantic names instead:
_1→.baseor.small_2→.largeor.alternate- etc.
MIT © Mohammed Ibrahim