Skip to content

Latest commit

 

History

History
358 lines (277 loc) · 10.2 KB

File metadata and controls

358 lines (277 loc) · 10.2 KB

.kcomponent File Format

Overview

The .kcomponent file format is a YAML-based format for defining custom components that can be imported into Karbonized. It contains a manifest with metadata and the component's HTML, CSS, and JavaScript code.

File Structure

A .kcomponent file is a YAML file with the following structure. Only manifest.name and html are required; css and js can be omitted.

manifest:
  name: "Component Name"
  author: "Author Name"
  description: "Component description"
  version: "1.0.0"
  category: "Category Name"
  tags:
    - "tag1"
    - "tag2"

html: |
  <div class="component">
    <!-- HTML content -->
  </div>

css: |
  :root {
    /* CSS variables with type annotations */
    /* @type:color */
    --primary-color: #3b82f6;
    /* @type:number min:0 max:50 step:1 unit:px */
    --spacing: 16px;
  }

js: |
  // JavaScript code
  // @var variable:type = "value"
  // @action:Action Label
  // action code

Manifest Fields

Field Type Required Description
name string Yes The name of the component (max. 80 chars)
author string No The author of the component (max. 80 chars)
description string No A brief description of the component (max. 500 chars)
version string No Version following semantic versioning (e.g., "1.0.0")
category string No Category for organizing components (e.g., "UI Components", "Forms")
tags array of strings No Tags for searching and filtering (max. 12, comma separated text is also accepted)
width number No Preferred block width in px when added to the canvas (16–4096)
height number No Preferred block height in px when added to the canvas (16–4096)
thumbnail string No Preview image shown in the library: an https:// URL or an inline data:image/…;base64, value

name and author together identify a component: importing a file with the same pair updates the existing entry instead of creating a duplicate.

HTML Section

The html field contains the HTML markup for the component. This HTML will be rendered within a Shadow DOM to prevent style conflicts with other components.

Example:

html: |
  <div class="custom-button">
    <button class="btn">Click Me</button>
  </div>

CSS Section

The css field contains the CSS styles for the component. Karbonized supports a special system of CSS variables with type annotations that automatically generate UI controls in the properties panel.

CSS Variable Types

Color Variables

/* @type:color */
--primary-color: #3b82f6;

Generates a color picker in the properties panel.

Number Variables

/* @type:number min:0 max:50 step:1 unit:px */
--border-radius: 8px;

Generates a slider control with the specified range and unit.

Parameters:

  • min: Minimum value
  • max: Maximum value
  • step: Increment step
  • unit: Unit to display (e.g., "px", "em", "%")

Shadow Variables

/* @type:shadow */
--box-shadow: 0px 2px 4px rgba(0,0,0,0.2);

Generates a shadow editor in the properties panel.

Boolean Variables

/* @type:boolean */
--show-shadow: true;

Generates a toggle switch in the properties panel.

Icon Variables

/* @type:icon */
--feature-icon: FaRocket;

Generates an icon picker in the properties panel with the built-in Font Awesome icons (FaRocket) and the icons of the installed icon packs (acme:bolt). When the block renders, the name becomes the image of the icon, meant for a mask. Draw it with the built-in .k-icon helper, which takes the text color and is 1em square:

<span class="k-icon" style="--k-icon: var(--feature-icon)"></span>

Font Awesome icons are always there. An icon from a pack the user has not installed draws nothing, so a shared component should default to a Font Awesome icon or say which pack it needs.

Icon packs

A .kcomponent with manifest.type: icon-pack holds icons instead of HTML: an icons map of names to SVG markup, plus a prefix that names the pack's icons (acme:bolt). Importing one adds its icons to the Icon block, to @type:icon variables and to the component library. See icon-packs.md for the full format, the SVG rules and the yarn icon-pack script that builds a pack from a folder of SVG files.

Fonts

Name a Google font in font-family and the app loads it for the block (a @import of Google Fonts is also understood). Fonts have to be loaded by the page: a shadow root cannot load them by itself.

Example CSS

css: |
  :root {
    /* @type:color */
    --btn-color: #3b82f6;
    /* @type:color */
    --btn-text: #ffffff;
    /* @type:number min:0 max:50 step:1 unit:px */
    --btn-radius: 8px;
    /* @type:number min:8 max:32 step:1 unit:px */
    --btn-padding: 12px;
    /* @type:shadow */
    --btn-shadow: 0px 2px 4px rgba(0,0,0,0.2);
    /* @type:boolean */
    --show-shadow: true;
  }
  
  .btn {
    background: var(--btn-color);
    color: var(--btn-text);
    padding: var(--btn-padding) 24px;
    border-radius: var(--btn-radius);
    box-shadow: var(--btn-shadow);
  }

JavaScript Section

The js field contains JavaScript code that can interact with the component and the Karbonized environment.

JavaScript Variables

You can define JavaScript variables that will appear in the properties panel:

// @var message:string = "Hello!"

This creates a text input in the properties panel for the message variable.

Custom Actions

You can define custom actions that can be triggered from the component or the properties panel:

// @action:Show Alert
alert(message);

This creates a button labeled "Show Alert" in the properties panel that executes the code.

HTML Block API

The JavaScript code has access to the htmlBlockAPI object with the following methods:

  • log(message): Log a message to the dev console
  • warn(message): Log a warning
  • error(message): Log an error
  • refresh(): Force refresh the Shadow DOM
  • registerAction(label, callback): Register a custom action
  • getVariable(name): Get the value of a CSS variable
  • setVariable(name, value): Set the value of a CSS variable

Example JavaScript

js: |
  // @var message:string = "Hello from custom component!"
  
  // @action:Show Alert
  alert(message);
  
  // @action:Change Color
  const colors = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b'];
  const randomColor = colors[Math.floor(Math.random() * colors.length)];
  document.documentElement.style.setProperty('--btn-color', randomColor);

Complete Example

manifest:
  name: "Custom Button"
  author: "Your Name"
  description: "A customizable button component"
  version: "1.0.0"
  category: "UI Components"
  tags:
    - "button"
    - "interactive"

html: |
  <div class="custom-button">
    <button class="btn">Click Me</button>
  </div>

css: |
  :root {
    /* @type:color */
    --btn-color: #3b82f6;
    /* @type:color */
    --btn-text: #ffffff;
    /* @type:number min:0 max:50 step:1 unit:px */
    --btn-radius: 8px;
    /* @type:number min:8 max:32 step:1 unit:px */
    --btn-padding: 12px;
    /* @type:number min:12 max:24 step:1 unit:px */
    --btn-font-size: 16px;
    /* @type:shadow */
    --btn-shadow: 0px 2px 4px rgba(0,0,0,0.2);
    /* @type:boolean */
    --show-shadow: true;
  }
  
  .custom-button {
    padding: 20px;
  }
  
  .btn {
    background: var(--btn-color);
    color: var(--btn-text);
    border: none;
    padding: var(--btn-padding) 24px;
    border-radius: var(--btn-radius);
    cursor: pointer;
    font-size: var(--btn-font-size);
    transition: opacity 0.2s;
    box-shadow: var(--btn-shadow);
  }
  
  .btn:hover {
    opacity: 0.9;
  }

js: |
  // @var message:string = "Hello from custom component!"
  
  // @action:Show Alert
  alert(message);
  
  // @action:Change Color
  const colors = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6'];
  const randomColor = colors[Math.floor(Math.random() * colors.length)];
  document.documentElement.style.setProperty('--btn-color', randomColor);
  
  // @action:Toggle Shadow
  const currentShadow = getComputedStyle(document.documentElement)
    .getPropertyValue('--show-shadow')
    .trim();
  const btn = document.querySelector('.btn');
  if (currentShadow === 'true') {
    btn.classList.add('no-shadow');
    document.documentElement.style.setProperty('--show-shadow', 'false');
  } else {
    btn.classList.remove('no-shadow');
    document.documentElement.style.setProperty('--show-shadow', 'true');
  }

Validation Rules

The importer separates hard errors (the file is rejected) from warnings (the file is imported, but a field was dropped or normalized).

Errors:

  • The YAML must be valid; syntax errors report the offending line
  • The manifest field is required and must be an object
  • manifest.name is required
  • html is required and cannot be empty
  • html, css and js must be text, and each section must stay under 512 KB
  • The whole file must stay under 1 MB

Warnings:

  • css or js missing: the component is imported without styles or actions
  • Unknown fields, at the root or inside the manifest, are ignored
  • Text fields longer than their limit are truncated
  • Duplicate tags are removed, and only the first 12 are kept
  • width/height outside 16–4096 px are ignored
  • A thumbnail that is not an https:// URL or an inline base64 image is ignored
  • A version that does not look like 1.2.3 is kept but flagged

Sizes and Limits

Limit Value
Maximum file size 1 MB
Maximum size per html/css/js section 512 KB
Maximum components in the library 200

Best Practices

  1. Use Shadow DOM-safe CSS: Since components render in Shadow DOM, avoid relying on global styles
  2. Use CSS Variables: Leverage the variable system for easy customization
  3. Provide meaningful descriptions: Help users understand what your component does
  4. Use appropriate tags: Make your components discoverable through search
  5. Version your components: Use semantic versioning to track changes
  6. Test actions: Ensure all custom actions work correctly
  7. Provide sensible defaults: Set reasonable default values for CSS variables