Coral UI

@reallygoodwork/coral-to-html

Convert Coral specifications to HTML markup.

npm

Convert Coral specifications to HTML markup. This package transforms Coral JSON specifications into clean, formatted HTML with inline styles.

Installation

npm install @reallygoodwork/coral-to-html
pnpm add @reallygoodwork/coral-to-html
yarn add @reallygoodwork/coral-to-html

Overview

This package generates formatted HTML from Coral UI specifications. It produces clean, properly indented HTML with inline styles, making it perfect for:

  • Static Site Generation - Generate HTML pages from Coral specs
  • Email Templates - Create email-compatible HTML
  • Preview Generation - Display component previews
  • Documentation - Generate HTML examples from Coral components

Key Features

  • Semantic HTML - Generates clean, semantic HTML markup
  • Inline Styles - Automatic conversion of Coral styles to CSS inline styles
  • Component Composition - Automatic flattening of component instances
  • Automatic Formatting - Beautiful, formatted HTML output via Prettier
  • Self-Closing Tags - Proper handling of void elements (img, br, hr, etc.)
  • Type Support - Handles dimensions, colors, and complex style values

API Reference

coralToHTML

Converts a Coral specification to formatted HTML markup. Supports component composition with automatic flattening of component instances.

Prop

Type

Returns: Promise<string> - Formatted HTML string

Options

Prop

Type

Basic Example

import { coralToHTML } from '@reallygoodwork/coral-to-html';

const spec = {
  name: 'card',
  elementType: 'div',
  styles: {
    padding: '20px',
    backgroundColor: '#ffffff',
    borderRadius: '8px',
    boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
  },
  children: [
    {
      name: 'title',
      elementType: 'h2',
      textContent: 'Card Title',
      styles: {
        fontSize: '24px',
        color: '#333'
      }
    }
  ]
};

const html = await coralToHTML(spec);

Output:

<div style="padding: 20px; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1)">
  <h2 style="font-size: 24px; color: #333">Card Title</h2>
</div>

Component Composition

When your Coral spec contains component instances (references to other components), you can automatically flatten them to their resolved HTML structure.

Learn more: For comprehensive information about component composition, prop bindings, and slot bindings, see the Component Composition guide and Component Composition API.

Basic Usage

import { loadPackage } from '@reallygoodwork/coral-core';
import { coralToHTML } from '@reallygoodwork/coral-to-html';
import * as fs from 'fs/promises';

// Load your package
const pkg = await loadPackage('./coral.config.json', {
  readFile: (path) => fs.readFile(path, 'utf-8'),
});

// Spec with component instance
const cardSpec = {
  name: 'Card',
  elementType: 'div',
  children: [
    {
      type: 'COMPONENT_INSTANCE',
      $component: { ref: './button.coral.json' },
      propBindings: {
        label: 'Save',
        intent: 'primary'
      }
    }
  ]
};

// Generate HTML with flattened composition
const html = await coralToHTML(cardSpec, {
  package: pkg,
  flattenComposition: true // Default: true
});

The component instance will be resolved from the package and flattened to its actual HTML structure before conversion.

Disabling Flattening

If you want to keep component instances as-is (useful for debugging or when instances should remain as placeholders):

const html = await coralToHTML(cardSpec, {
  flattenComposition: false
});

Exported Types

CoralToHTMLOptions

Configuration options for HTML generation.

Prop

Type


Style Conversion

The package automatically converts Coral style objects to CSS inline styles. Here's how different value types are handled:

Dimension Objects

Dimension objects with value and unit properties are converted to CSS strings:

// Coral
{
  padding: { value: 20, unit: 'px' },
  width: { value: 50, unit: '%' },
  margin: { value: 2, unit: 'rem' }
}

// HTML Output
style="padding: 20px; width: 50%; margin: 2rem"

Numeric Values

Numeric values are automatically converted:

  • Most properties get px units added
  • Unitless properties (like fontWeight, lineHeight, zIndex) remain as numbers
// Coral
{
  padding: 20,        // → padding: 20px
  fontSize: 16,       // → font-size: 16px
  fontWeight: 700,    // → font-weight: 700 (no unit)
  lineHeight: 1.5,    // → line-height: 1.5 (no unit)
  zIndex: 10         // → z-index: 10 (no unit)
}

// HTML Output
style="padding: 20px; font-size: 16px; font-weight: 700; line-height: 1.5; z-index: 10"

Color Objects

Coral color objects are converted using their hex property:

// Coral
{
  backgroundColor: {
    hex: '#007bff',
    rgb: { r: 0, g: 123, b: 255, a: 1 },
    hsl: { h: 211, s: 100, l: 50, a: 1 }
  },
  color: {
    hex: '#ffffff'
  }
}

// HTML Output
style="background-color: #007bff; color: #ffffff"

CamelCase to Kebab-Case

CSS property names are automatically converted from camelCase to kebab-case:

// Coral (camelCase)
{
  backgroundColor: '#fff',
  fontSize: '16px',
  borderTopLeftRadius: '4px',
  marginTop: '10px'
}

// HTML (kebab-case)
style="background-color: #fff; font-size: 16px; border-top-left-radius: 4px; margin-top: 10px"

String Values

String values are used as-is, with special handling for font families:

// Coral
{
  fontFamily: 'Inter',           // → font-family: Inter, sans-serif (auto-fallback)
  fontFamily: 'Arial, sans-serif', // → font-family: Arial, sans-serif (preserved)
  color: '#333',
  display: 'flex'
}

// HTML Output
style="font-family: Inter, sans-serif; color: #333; display: flex"

Element Attributes

HTML attributes are converted from the elementAttributes property. The package handles different attribute types:

String and Number Attributes

String and number values are converted directly:

const spec = {
  elementType: 'a',
  elementAttributes: {
    href: 'https://example.com',
    target: '_blank',
    'data-id': '123',
    tabIndex: 0
  },
  textContent: 'Visit Example'
};

// Output: <a href="https://example.com" target="_blank" data-id="123" tabindex="0">Visit Example</a>

Boolean Attributes

Boolean attributes are handled specially:

  • true values render as the attribute name only
  • false values are omitted
const spec = {
  elementType: 'input',
  elementAttributes: {
    type: 'checkbox',
    checked: true,      // → checked (attribute present)
    disabled: false,   // → (attribute omitted)
    required: true      // → required (attribute present)
  }
};

// Output: <input type="checkbox" checked required />

Array Attributes

Array values are joined with spaces (useful for class attributes):

const spec = {
  elementType: 'div',
  elementAttributes: {
    class: ['container', 'mx-auto', 'p-4'],
    'data-attributes': ['attr1', 'attr2']
  }
};

// Output: <div class="container mx-auto p-4" data-attributes="attr1 attr2"></div>

Self-Closing Tags

Void HTML elements are automatically rendered as self-closing tags:

const spec = {
  elementType: 'img',
  elementAttributes: {
    src: '/image.jpg',
    alt: 'Description'
  },
  styles: {
    width: '100%',
    borderRadius: '8px'
  }
};

// Output: <img src="/image.jpg" alt="Description" style="width: 100%; border-radius: 8px" />

Supported void elements: area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr


Examples

Simple Button

import { coralToHTML } from '@reallygoodwork/coral-to-html';

const buttonSpec = {
  elementType: 'button',
  textContent: 'Click Me',
  styles: {
    padding: '10px 20px',
    backgroundColor: '#007bff',
    color: '#ffffff',
    border: 'none',
    borderRadius: '4px',
    cursor: 'pointer'
  }
};

const html = await coralToHTML(buttonSpec);
// Output: <button style="padding: 10px 20px; background-color: #007bff; color: #ffffff; border: none; border-radius: 4px; cursor: pointer">Click Me</button>

Card Component with Nested Elements

const cardSpec = {
  elementType: 'div',
  styles: {
    padding: '24px',
    borderRadius: '12px',
    backgroundColor: '#ffffff',
    boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
  },
  children: [
    {
      elementType: 'h3',
      textContent: 'Product Name',
      styles: {
        fontSize: '20px',
        fontWeight: 'bold',
        marginBottom: '8px'
      }
    },
    {
      elementType: 'p',
      textContent: 'Description',
      styles: {
        color: '#666',
        lineHeight: '1.5'
      }
    }
  ]
};

const html = await coralToHTML(cardSpec);

Form with Input Elements

const formSpec = {
  elementType: 'form',
  elementAttributes: {
    action: '/submit',
    method: 'POST'
  },
  styles: {
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  },
  children: [
    {
      elementType: 'label',
      textContent: 'Email:',
      elementAttributes: {
        for: 'email'
      }
    },
    {
      elementType: 'input',
      elementAttributes: {
        type: 'email',
        id: 'email',
        required: true,
        placeholder: 'Enter your email'
      },
      styles: {
        padding: '8px 12px',
        border: '1px solid #ccc',
        borderRadius: '4px'
      }
    },
    {
      elementType: 'button',
      elementAttributes: { type: 'submit' },
      textContent: 'Submit',
      styles: {
        padding: '10px 20px',
        backgroundColor: '#007bff',
        color: '#ffffff',
        border: 'none',
        borderRadius: '4px'
      }
    }
  ]
};

const html = await coralToHTML(formSpec);

Complete HTML Document

import fs from 'fs/promises';
import { coralToHTML } from '@reallygoodwork/coral-to-html';

const spec = {
  elementType: 'div',
  styles: { padding: '20px' },
  children: [
    { elementType: 'h1', textContent: 'Hello World' }
  ]
};

const html = await coralToHTML(spec);

// Create complete HTML document
const document = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Generated Component</title>
</head>
<body>
  ${html}
</body>
</html>`;

await fs.writeFile('./output.html', document);

Special Behaviors

Font Family Fallbacks

The package automatically adds sans-serif fallback for the Inter font family:

// Coral
{
  fontFamily: 'Inter'
}

// HTML Output
style="font-family: Inter, sans-serif"

If sans-serif is already present in the font stack, it won't be duplicated.

Style Filtering

Pseudo-selectors and media queries are automatically filtered out (inline styles limitation):

// Coral
{
  color: '#333',
  ':hover': { color: '#000' },        // Ignored
  '@media (min-width: 768px)': {      // Ignored
    padding: '20px'
  }
}

// HTML Output
style="color: #333"

Only direct style properties are included in the output.


Output Formatting

HTML output is automatically formatted with Prettier for readability:

  • ✅ Proper indentation (2 spaces)
  • ✅ Consistent spacing
  • ✅ Readable structure
  • ✅ Self-closing tag syntax
  • ✅ Proper attribute formatting
const html = await coralToHTML(spec);
// Output is beautifully formatted and ready to use

Limitations

Due to the inline styles approach, the following features are not supported:

  • No CSS Classes - Only inline styles (no external stylesheets or class names)
  • No Pseudo-Classes - :hover, :focus, :active not supported
  • No Media Queries - Responsive styles are filtered out
  • No Animations - Limited animation support (only basic CSS animations)
  • Email Compatibility - Some CSS properties may not work in email clients

Tip: For CSS classes, pseudo-classes, and advanced features, use @reallygoodwork/coral-to-react instead.


Use Cases

Static Site Generation

Generate HTML pages from Coral specifications for static sites:

import { coralToHTML } from '@reallygoodwork/coral-to-html';
import * as fs from 'fs/promises';

const pages = [
  { path: 'index.html', spec: homeSpec },
  { path: 'about.html', spec: aboutSpec },
  { path: 'contact.html', spec: contactSpec }
];

for (const page of pages) {
  const html = await coralToHTML(page.spec);
  const document = `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>${page.path}</title>
</head>
<body>${html}</body>
</html>`;

  await fs.writeFile(`./dist/${page.path}`, document);
}

Email Templates

Create email-compatible HTML from Coral specs:

const emailSpec = {
  elementType: 'div',
  styles: {
    fontFamily: 'Arial, sans-serif',
    maxWidth: '600px',
    margin: '0 auto',
    padding: '20px',
    backgroundColor: '#ffffff'
  },
  children: [
    {
      elementType: 'h1',
      textContent: 'Welcome!',
      styles: { color: '#333', fontSize: '24px' }
    },
    {
      elementType: 'p',
      textContent: 'Thank you for signing up.',
      styles: { color: '#666', fontSize: '16px' }
    }
  ]
};

const emailHTML = await coralToHTML(emailSpec);
// Use with your email service provider

Component Previews

Generate HTML previews for design system documentation:

import { loadPackage } from '@reallygoodwork/coral-core';
import { coralToHTML } from '@reallygoodwork/coral-to-html';

const pkg = await loadPackage('./coral.config.json', options);

// Generate preview for each component
for (const [name, component] of pkg.components) {
  const html = await coralToHTML(component, { package: pkg });
  // Display in iframe or preview pane
  savePreview(name, html);
}

Documentation Examples

Generate HTML examples from Coral components for documentation:

const examples = {
  button: buttonSpec,
  card: cardSpec,
  form: formSpec
};

for (const [name, spec] of Object.entries(examples)) {
  const html = await coralToHTML(spec);
  generateDocExample(name, html);
}

Additional Resources

On this page