← Volver al blog

Paquete de Design Tokens - Sistema de Diseño Código Obsidiana

Paquete de Design Tokens - Sistema de Diseño Código Obsidiana

Descripción

El paquete de Design Tokens es la capa fundamental del Sistema de Diseño Código Obsidiana, proporcionando un sistema centralizado y type-safe de gestión de tokens que asegura consistencia de diseño en todas las aplicaciones y componentes. Construido con estándares web modernos, aprovecha el avanzado espacio de color OKLCH para colores perceptualmente uniformes y Style Dictionary para transformación escalable de tokens.

Este paquete genera salidas específicas para cada plataforma (CSS Custom Properties, módulos JavaScript/TypeScript) desde una única fuente de verdad, permitiendo integración sin pérdidas a través de diferentes tecnologías mientras mantiene la consistencia de diseño. El sistema soporta tematización comprensiva con variantes claras y oscuras para todos los tokens de color.

Estructura de Tokens

La base de nuestros design tokens sigue una estructura jerárquica semántica que soporta tematización comprensiva:

{
  "color": {
    "primary": {
      "light": {
        "default": {
          "value": "oklch(72.6% 0.21 245)",
          "type": "color"
        },
        "content": {
          "value": "oklch(98% 0.003 262.2)",
          "type": "color"
        }
      },
      "dark": {
        "default": {
          "value": "oklch(72.6% 0.21 245)",
          "type": "color"
        },
        "content": {
          "value": "oklch(26.2% 0.003 262.2)",
          "type": "color"
        }
      }
    }
  }
}

Desafíos

Soluciones

Sistema de Color Avanzado con OKLCH: Implementamos el espacio de color OKLCH (Oklch Lightness Chroma Hue) para manipulación de color perceptualmente uniforme, proporcionando mejores ratios de contraste y relaciones de color más predecibles. Construimos validación comprensiva con patrones regex para asegurar sintaxis OKLCH apropiada y rangos de valores (luminosidad 0-100%, croma ≥0, matiz 0-360°).

Validación de Color OKLCH

Nuestro sistema de validación asegura precisión y consistencia de color:

const validateOKLCHColor = (value: string) => {
  // Patrón regex para validación de formato OKLCH
  const oklchRegex = /^oklch\(\d+\.?\d*% \d+\.?\d* \d+\.?\d*\)$/;
  expect(value).toMatch(oklchRegex);

  // Extraer y validar componentes individuales
  const matches = value.match(/oklch\((\d+\.?\d*)% (\d+\.?\d*) (\d+\.?\d*)\)/);
  if (!matches) throw new Error('Invalid OKLCH format');

  const [, lightness, chroma, hue] = matches.map(Number);

  // Validar rangos de valores
  expect(lightness).toBeGreaterThanOrEqual(0);
  expect(lightness).toBeLessThanOrEqual(100);
  expect(chroma).toBeGreaterThanOrEqual(0);
  expect(hue).toBeGreaterThanOrEqual(0);
  expect(hue).toBeLessThanOrEqual(360);
};

Generación de Código Multi-Plataforma: Aprovechamos el pipeline de transformación de Style Dictionary para generar tres salidas distintas desde archivos fuente JSON:

Configuración de Style Dictionary

La configuración que impulsa nuestra generación de tokens multi-plataforma:

{
  "source": ["src/tokens/**/*.json"],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "buildPath": "dist/css/",
      "files": [
        {
          "destination": "_variables.css",
          "format": "css/variables"
        }
      ]
    },
    "js": {
      "transformGroup": "js",
      "buildPath": "dist/",
      "files": [
        {
          "destination": "index.js",
          "format": "javascript/es6"
        }
      ]
    },
    "ts": {
      "transformGroup": "js",
      "buildPath": "dist/",
      "files": [
        {
          "destination": "index.d.ts",
          "format": "typescript/es6-declarations",
          "options": {
            "outputLiteralTypes": true
          }
        }
      ]
    }
  }
}

Arquitectura de Tokens Estructurada: Diseñamos una estructura de tokens jerárquica con nomenclatura semántica:

color.{category}.{theme}.{variant}
// Ejemplos: color.primary.light.default, color.base.dark.content

Salidas Generadas

CSS Custom Properties

:root {
  --color-primary-light-default: oklch(72.6% 0.21 245);
  --color-primary-light-content: oklch(98% 0.003 262.2);
  --color-primary-dark-default: oklch(72.6% 0.21 245);
  --color-primary-dark-content: oklch(26.2% 0.003 262.2);
  --color-success-light-default: oklch(68% 0.18 150);
  --color-warning-light-default: oklch(82% 0.16 85);
  --color-error-light-default: oklch(70.3% 0.16 35);
}

Declaraciones TypeScript

export const ColorPrimaryLightDefault: string;
export const ColorPrimaryLightContent: string;
export const ColorPrimaryDarkDefault: string;
export const ColorPrimaryDarkContent: string;
export const ColorSuccessLightDefault: string;
export const ColorWarningLightDefault: string;
export const ColorErrorLightDefault: string;

Módulo ES6 JavaScript

export const ColorPrimaryLightDefault = "oklch(72.6% 0.21 245)";
export const ColorPrimaryLightContent = "oklch(98% 0.003 262.2)";
export const ColorPrimaryDarkDefault = "oklch(72.6% 0.21 245)";
export const ColorPrimaryDarkContent = "oklch(26.2% 0.003 262.2)";
export const ColorSuccessLightDefault = "oklch(68% 0.18 150)";
export const ColorWarningLightDefault = "oklch(82% 0.16 85)";
export const ColorErrorLightDefault = "oklch(70.3% 0.16 35)";

Estrategia de Testing Comprensiva: Implementamos testing basado en Jest con múltiples capas de validación:

Tests de Validación de Tokens

describe('Color Tokens', () => {
  describe('Theme Consistency', () => {
    it('should have matching properties in light and dark themes', () => {
      Object.keys(tokens.color).forEach(category => {
        const lightKeys = Object.keys(tokens.color[category].light);
        const darkKeys = Object.keys(tokens.color[category].dark);
        expect(lightKeys).toEqual(darkKeys);
      });
    });

    it('should have consistent token structure across themes', () => {
      Object.keys(tokens.color).forEach(category => {
        const lightTokens = tokens.color[category].light;
        const darkTokens = tokens.color[category].dark;

        Object.keys(lightTokens).forEach(tokenKey => {
          expect(lightTokens[tokenKey].type).toBe(darkTokens[tokenKey].type);
        });
      });
    });
  });

  describe('Color Token Completeness', () => {
    it('should have all required color variants', () => {
      const requiredVariants = ['default', 'content'];
      const baseVariants = ['100', '200', '300', 'content'];

      Object.entries(tokens.color).forEach(([category, themes]) => {
        if (category === 'base') {
          expect(Object.keys(themes.light)).toEqual(expect.arrayContaining(baseVariants));
          expect(Object.keys(themes.dark)).toEqual(expect.arrayContaining(baseVariants));
        } else {
          expect(Object.keys(themes.light)).toEqual(expect.arrayContaining(requiredVariants));
          expect(Object.keys(themes.dark)).toEqual(expect.arrayContaining(requiredVariants));
        }
      });
    });
  });
});

Integración Nx y Automatización: Creamos targets de build eficientes usando Nx:

Pipeline de Build

{
  "targets": {
    "clean": {
      "executor": "nx:run-commands",
      "options": {
        "command": "rm -rf dist",
        "cwd": "packages/libs/design-tokens"
      }
    },
    "build:tokens": {
      "executor": "nx:run-commands",
      "dependsOn": ["clean"],
      "options": {
        "command": "style-dictionary build",
        "cwd": "packages/libs/design-tokens"
      }
    },
    "publish:tokens": {
      "executor": "nx:run-commands",
      "dependsOn": ["build:tokens"],
      "options": {
        "command": "npm publish",
        "cwd": "packages/libs/design-tokens"
      }
    }
  }
}

Ejemplos de Uso

En CSS

.button {
  background-color: var(--color-primary-light-default);
  color: var(--color-primary-light-content);
  border-radius: var(--design-radius-field);
}

@media (prefers-color-scheme: dark) {
  .button {
    background-color: var(--color-primary-dark-default);
    color: var(--color-primary-dark-content);
  }
}

En React/TypeScript

import { 
  ColorPrimaryLightDefault, 
  ColorPrimaryLightContent,
  DesignRadiusField 
} from '@codigo-obsidiana/design-tokens';

const Button: React.FC = () => (
  <button
    style={{
      backgroundColor: ColorPrimaryLightDefault,
      color: ColorPrimaryLightContent,
      borderRadius: DesignRadiusField,
    }}
  >
    Haz clic aquí
  </button>
);

Cambio de Tema

// Cambiar temas dinámicamente
const applyTheme = (theme: 'light' | 'dark') => {
  const root = document.documentElement;
  
  if (theme === 'dark') {
    root.style.setProperty('--color-primary-default', ColorPrimaryDarkDefault);
    root.style.setProperty('--color-primary-content', ColorPrimaryDarkContent);
  } else {
    root.style.setProperty('--color-primary-default', ColorPrimaryLightDefault);
    root.style.setProperty('--color-primary-content', ColorPrimaryLightContent);
  }
};

Resultados

Experiencia de Desarrollador Mejorada: Consumo de tokens type-safe con soporte completo de intellisense y validación en tiempo de ejecución, reduciendo inconsistencias de diseño en un 95% entre equipos de desarrollo.

Optimización de Performance: CSS Custom Properties permiten tematización dinámica sin overhead de JavaScript, mientras que las exportaciones ES6 tree-shakeable optimizan tamaños de bundle en aplicaciones JavaScript.

Mejoras de Accesibilidad: El espacio de color OKLCH proporciona capacidades superiores de cálculo de contraste, asegurando cumplimiento WCAG con relaciones de color matemáticamente predecibles.

Mantenimiento Escalable: El enfoque de única fuente de verdad redujo el overhead de mantenimiento de tokens en un 80%, con validación automatizada previniendo que valores de color inválidos entren a producción.

Consistencia Cross-Platform: La distribución unificada de tokens asegura consistencia de diseño pixel-perfect entre React, CSS e implementaciones futuras de plataformas.

Visuales

Paquete de Design Tokens - Sistema de Diseño Código Obsidiana

Enlaces