Skip to content

Box-Shadow Generator — TypeScript source

Design layered CSS box-shadows with offset, blur, spread, color, and inset. Live preview and copy-ready CSS.

This is the TypeScript implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Pure CSS box-shadow builder - no React, no DOM, deterministic.
// Formats one or more shadow layers and joins them. Never throws.

export interface ShadowLayer {
  inset: boolean;
  offsetX: number;
  offsetY: number;
  blur: number;
  spread: number;
  color: string;
}

const NAMED_COLORS = new Set([
  'transparent', 'black', 'white', 'red', 'green', 'blue', 'yellow',
  'orange', 'purple', 'pink', 'gray', 'grey', 'brown', 'cyan', 'magenta',
]);

export function parseColor(color: string): { ok: boolean; error: string | null } {
  const c = (color || '').trim().toLowerCase();
  if (!c) return { ok: false, error: 'empty color' };
  if (NAMED_COLORS.has(c)) return { ok: true, error: null };
  if (/^#[0-9a-f]{3}([0-9a-f]{3})?$/.test(c)) return { ok: true, error: null };
  if (/^#[0-9a-f]{8}$/.test(c)) return { ok: true, error: null };
  if (/^rgba?\([^)]+\)$/.test(c)) return { ok: true, error: null };
  if (/^hsla?\([^)]+\)$/.test(c)) return { ok: true, error: null };
  return { ok: false, error: `invalid color: ${color}` };
}

function normalizeColor(color: string): string {
  return parseColor(color).ok ? color.trim() : 'rgba(0,0,0,0.5)';
}

export function formatLayer(layer: ShadowLayer): string {
  const { inset, offsetX, offsetY, blur, spread, color } = layer;
  return `${inset ? 'inset ' : ''}${offsetX}px ${offsetY}px ${blur}px ${spread}px ${normalizeColor(color)}`;
}

export function buildBoxShadow(layers: ShadowLayer[]): string {
  if (!layers.length) return 'none';
  return layers.map(formatLayer).join(', ');
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →