Search

Advanced Node Styles

Styling

Create nodes with custom shapes, multiple labels and advanced styles.

Advanced Node Styles
View live example →

The right node design can help you visualise any type or amount of data in an intuitive way, provide the best user interface and experience for chart users and integrate your charts seamlessly into any application.

For more details about the advanced styling features in KeyLines, see also our Node and Combo Styling docs.

Custom shaped nodes

Set the width (w) and height (h) of nodes to create rectangular nodes. You can also add one or multiple rounded corners by setting the borderRadius property.

Alternatively, you can get any node shape by overlapping nodes with font icons that are added as labels in the t property array of objects.

Multiple styled labels

The t array of objects can be used to add multiple image, text and font icon labels. Each label can have its own styling and positioning and serve different purposes such as a source of information or an additional node styling feature.

Custom label interactions

Labels can serve as UI elements such as buttons, icons or toggles. Use pointer events and hover events to add custom interactions and interaction styling.

You can also design labels with dynamically changing styling and create your own graphical elements such as progress bars or stack bars.

Images inside labels

To create images on nodes with multiple styled labels, simply set the label's u property.

Key functions used:

import {
  data,
  invertedInteractiveNode,
  normalInteractiveNode,
  interactionStylePresets,
} from "./data.js";

import KeyLines from "keylines";

let chart;
// keeps track of the current state/styling of items on the chart
const itemsState = [...data.items];

function highlightSelection(id) {
  // if the user hasn't clicked an item resize back to fit
  if (!id) {
    chart.foreground(() => true);
    return;
  }
  // if there is a clicked item set it to be foregrounded
  const clickedItem = chart.getItem(id);
  if (clickedItem) {
    chart.foreground((item) => clickedItem.id === item.id);
  }
}

function formatPrintString(key, value) {
  if (key === "x" || key === "y" || key === "bg") {
    return undefined;
  }
  // prints the borderRadius array as a single value if all directions are set to the same value
  if (key === "borderRadius") {
    const set = new Set(value);
    if (set.size === 1) {
      return value[0];
    }
  }
  return value;
}

// Formats the code snippets neatly in the display box on the right-hand side
function prettyPrint(id) {
  const item = chart.getItem(id);
  if (item) {
    const json = JSON.stringify(item, formatPrintString, 1);
    document.getElementById("display").innerHTML = json;
  } else {
    document.getElementById("display").innerHTML = "No node selected";
  }
}

// Called when KeyLines is created
function initializeInteractions() {
  // Zoom the view to its original position when double-clicking the background
  chart.on("double-click", ({ id, preventDefault }) => {
    if (!chart.getItem(id)) {
      chart.zoom("fit", { animate: true, time: 500 });
    }
    // Default behaviour is to zoom in - we preventDefault to override this
    preventDefault();
  });

  let interactiveNodesState = {};
  const labelButtons = {};
  const buttonGroupLabels = {};

  function registerLabelButton(itemId, labelIndex, onItemInteractionLabelStyle, onClick) {
    labelButtons[itemId] = labelButtons[itemId] || {};
    labelButtons[itemId][labelIndex] = { onClick };
    labelButtons[itemId][onItemInteractionLabelStyle] = onItemInteractionLabelStyle;
  }

  function registerLabelButtonGroup(itemId, labelIndexArray, onItemInteractionLabelStyle, onClick) {
    for (const labelIndex of labelIndexArray) {
      registerLabelButton(itemId, labelIndex, onItemInteractionLabelStyle, onClick);
      buttonGroupLabels[itemId] = { ...buttonGroupLabels[itemId], [labelIndex]: labelIndexArray };
    }
  }

  // Highlight and pretty print when a selection is made or if we click an interactive label
  chart.on("click", ({ id, subItem, preventDefault }) => {
    prettyPrint(id);
    highlightSelection(id);

    if (subItem.type === "label") {
      const labelIndex = subItem.index;
      if (labelButtons[id] && labelButtons[id][labelIndex]) {
        labelButtons[id][labelIndex].onClick();
        prettyPrint(id);
      }
    }
  });

  let hoveredButton = null;

  chart.on("hover", async ({ id, subItem }) => {
    const style = {};

    const labelIndex = subItem?.type === "label" ? subItem.index : -1;
    const isHoverableLabel = id !== null && labelButtons[id] && labelButtons[id][labelIndex];
    const isAButtonGroup = isHoverableLabel && !!buttonGroupLabels?.[id]?.[labelIndex];
    const hoverStyle = isHoverableLabel && labelButtons[id][style];
    const sourceItem = itemsState.find((item) => item.id === id);
    const chartElement = document.querySelector("#klchart canvas[style]");

    // Always clear any hovered button first
    if (hoveredButton) {
      const hoveredButtonSourceItem = itemsState.find((item) => item.id === hoveredButton);
      await chart.setProperties({ id: hoveredButton, t: hoveredButtonSourceItem?.t });
      hoveredButton = null;
    }
    // clear cursor style
    chartElement.classList.remove("cursor");

    if (isHoverableLabel && !isAButtonGroup) {
      const label = [...sourceItem.t];

      label[labelIndex] = {
        ...label[labelIndex],
        ...hoverStyle,
      };

      await chart.setProperties({ id, t: label });
      hoveredButton = id;
      chartElement.classList.toggle("cursor");
    } else if (isAButtonGroup) {
      const label = [...sourceItem.t];

      for (const index of buttonGroupLabels[id][labelIndex]) {
        label[index] = {
          ...label[index],
          ...hoverStyle,
        };
      }

      await chart.setProperties({ id, t: label });
      hoveredButton = id;
      chartElement.classList.toggle("cursor");
    }
  });

  async function toggleNodeStyle(itemId) {
    const interactiveNodeState = interactiveNodesState[itemId];

    const nodeStyle = !interactiveNodeState ? invertedInteractiveNode : normalInteractiveNode;
    // remove x and y from the styling to prevent the node snapping back to it's original position
    delete nodeStyle.x;
    delete nodeStyle.y;

    // updtate the item state with the new toggled style
    const itemIndex = itemsState.findIndex((item) => item.id === itemId);
    itemsState[itemIndex] = nodeStyle;

    await chart.setProperties({ id: itemId, ...nodeStyle });

    interactiveNodesState[itemId] = !interactiveNodesState[itemId];
  }

  async function toggleLabelButtons(itemId, labelIndex, style) {
    // track which buttons are toggled
    const currentNodeInteractiveState = interactiveNodesState[itemId] ?? [];

    currentNodeInteractiveState[labelIndex] =
      currentNodeInteractiveState[labelIndex] === undefined
        ? true
        : !currentNodeInteractiveState[labelIndex];

    interactiveNodesState[itemId] = currentNodeInteractiveState;

    // make sure all buttons are styled correctly depending on their toggle state
    const currentStyle = itemsState.find((item) => item.id === itemId);
    const originalStyle = data.items.find((item) => item.id === itemId);

    const toggleStyle = { ...currentStyle, t: [...currentStyle.t] };
    toggleStyle.t[labelIndex] = {
      ...toggleStyle.t[labelIndex],
      ...style,
    };

    // remove x and y from the styling to prevent the node snapping back to it's original position
    delete toggleStyle.x;
    delete toggleStyle.y;

    // update the item state with the new toggled style
    const itemIndex = itemsState.findIndex((item) => item.id === itemId);
    itemsState[itemIndex] = toggleStyle;

    currentNodeInteractiveState.forEach((toggleValue, index) => {
      const originalLabelStyle = { ...originalStyle.t[index] };
      const labelStyle = toggleValue ? style : originalLabelStyle;
      toggleStyle.t[index] = {
        ...toggleStyle.t[index],
        ...labelStyle,
      };
    });

    await chart.setProperties({ id: itemId, ...toggleStyle });
  }

  registerLabelButton("t2", 2, interactionStylePresets.interactiveNodeHover, () => {
    toggleNodeStyle("t2");
  });

  registerLabelButtonGroup("t2", [3, 4], interactionStylePresets.interactiveNodeHover, () =>
    window.open("/stylingnodes.htm#advancedlabelstyling", "_blank")
  );

  registerLabelButton(
    "outside-buttons-rectangle",
    3,
    interactionStylePresets.outsideLabelActive,
    () => {
      toggleLabelButtons("outside-buttons-rectangle", 3, interactionStylePresets.outsideLabelHover);
    }
  );
  registerLabelButton(
    "outside-buttons-rectangle",
    5,
    interactionStylePresets.outsideLabelActive,
    () => {
      toggleLabelButtons("outside-buttons-rectangle", 5, interactionStylePresets.outsideLabelHover);
    }
  );
  registerLabelButton(
    "outside-buttons-rectangle",
    7,
    interactionStylePresets.outsideLabelActive,
    () => {
      toggleLabelButtons("outside-buttons-rectangle", 7, interactionStylePresets.outsideLabelHover);
    }
  );

  registerLabelButton(
    "outside-buttons-circle",
    3,
    interactionStylePresets.outsideLabelActive,
    () => {
      toggleLabelButtons("outside-buttons-circle", 3, interactionStylePresets.outsideLabelHover);
    }
  );
  registerLabelButton(
    "outside-buttons-circle",
    4,
    interactionStylePresets.outsideLabelActive,
    () => {
      toggleLabelButtons("outside-buttons-circle", 4, interactionStylePresets.outsideLabelHover);
    }
  );
  registerLabelButton(
    "outside-buttons-circle",
    5,
    interactionStylePresets.outsideLabelActive,
    () => {
      toggleLabelButtons("outside-buttons-circle", 5, interactionStylePresets.outsideLabelHover);
    }
  );
}

async function startKeyLines() {
  const options = {
    logo: { u: "/images/Logo.png" },
    handMode: true,
    selectionColour: "#FF9933",
    iconFontFamily: "Font Awesome 5 Free",
    overview: { icon: false },
    navigation: { shown: false },
    hover: 0,
  };

  chart = await KeyLines.create({ container: "klchart", options });
  await chart.load(data);
  await chart.zoom("fit");
  initializeInteractions();
}

function loadFontsAndStart() {
  document.fonts.ready
    .then(() =>
      Promise.all([
        document.fonts.load('1em "Font Awesome 5 Free"'),
        document.fonts.load('1em "Raleway"'),
        document.fonts.load('1em "Montserrat"'),
        document.fonts.load('1em "Varela Round"'),
        document.fonts.load('1em "Material Icons"'),
      ])
    )
    .then(startKeyLines);
}

window.addEventListener("DOMContentLoaded", loadFontsAndStart);
import KeyLines from "keylines";

export const interactionStylePresets = {
  outsideLabelActive: { fbc: "#d1fae5" },
  outsideLabelHover: { fbc: "#6ee7b7" },
  interactiveNodeHover: { fbc: "#9A9B9B" },
};

const lineSVG = `data:image/svg+xml;base64,${btoa(
  `<svg xmlns="http://www.w3.org/2000/svg" fill="#e2e8f0" width="256px" height="5px">
  <g id="SVGRepo_bgCarrier" stroke-width="0">
  </g>
  <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round">
  </g>
  <g id="SVGRepo_iconCarrier">
    <rect width="256" height="5" />
  </g>
</svg>`
)}`;

function makeProfileCardNode(id, x, y) {
  return {
    type: "node",
    id,
    x,
    y,
    c: "#f1f5f9",
    w: 300,
    h: "auto",
    borderRadius: "10 10 6 6",
    t: [
      {
        fi: { t: "fas fa-user" },
        fs: 100,
        fc: "rgb(81, 136, 168)",
        fbc: "#f1f5f9",
        b: "rgb(0, 85, 135)",
        bw: 5,
        borderRadius: 60,
        minHeight: 100,
        minWidth: 100,
        margin: "-50 0 0 0",
        position: {
          vertical: "top",
        },
      },
      {
        t: "John Doe",
        fs: 30,
        fb: true,
        fc: "rgb(0, 85, 135)",
        fbc: "transparent",
        margin: "10 0 0 0",
      },
      {
        fi: { t: "fas fa-envelope" },
        fs: 12,
        fc: "rgb(81, 136, 168)",
        fbc: "transparent",
        margin: "10 0 0 0",
      },
      {
        t: "[email protected]",
        fs: 10,
        fc: "rgb(81, 136, 168)",
        fbc: "transparent",
        margin: "10 0 0 0",
        position: {
          vertical: "inherit",
        },
      },
      {
        fi: { t: "fas fa-code" },
        fs: 12,
        fc: "rgb(81, 136, 168)",
        fbc: "transparent",
        margin: "10 0 0 0",
      },
      {
        t: "Software Developer",
        fs: 10,
        margin: "10 0 0 0",
        fc: "black",
        fbc: "transparent",
        position: {
          vertical: "inherit",
        },
      },
      {
        t: "[email protected]",
        fs: 10,
        fc: "white",
        fbc: "rgb(81, 136, 168)",
        borderRadius: "0 0 6 6",
        margin: "10 0 0 0",
        padding: "10 0 10 0",
        minWidth: 300,
        position: {
          vertical: "bottom",
        },
      },
    ],
  };
}

function makeBusinessProcessNode(id, x, y) {
  return {
    type: "node",
    id,
    x,
    y,
    c: "#ecfdf5",
    w: 400,
    h: 120,
    b: "#e5e5e5",
    bw: 2,
    borderRadius: 10,
    t: [
      {
        t: "Business Process 1",
        fs: 26,
        fc: "#334155",
        fbc: "transparent",
        margin: {
          top: 8,
          right: 0,
          bottom: 0,
          left: 8,
        },
        position: {
          vertical: "top",
          horizontal: "left",
        },
      },
      {
        t: "2140.0",
        fs: 26,
        fc: "#334155",
        fbc: "transparent",
        textAlignment: {
          vertical: "bottom",
        },
        margin: {
          top: 0,
          right: 0,
          bottom: 20,
          left: 8,
        },
        position: {
          vertical: "bottom",
          horizontal: "left",
        },
      },
      {
        t: "GBP",
        fs: 20,
        fc: "#334155",
        fbc: "transparent",
        textAlignment: {
          vertical: "bottom",
        },
        minHeight: 36,
        margin: {
          top: 0,
          right: 0,
          bottom: 21,
          left: 0,
        },
        position: {
          vertical: "bottom",
        },
      },
      {
        fi: {
          t: "fas fa-caret-up",
          c: "#10b981",
        },
        fs: 36,
        fbc: "transparent",
        textAlignment: {
          vertical: "bottom",
        },
        minHeight: 36,
        margin: {
          top: 0,
          right: 2,
          bottom: 15,
          left: 0,
        },
        position: {
          vertical: "bottom",
          horizontal: "right",
        },
      },
      {
        t: "55.12%",
        fs: 20,
        fc: "#10b981",
        fbc: "transparent",
        textAlignment: {
          vertical: "bottom",
        },
        position: {
          vertical: "inherit",
        },
        margin: {
          top: 0,
          right: 8,
          bottom: 20,
          left: 0,
        },
        minHeight: 36,
      },
      {
        fs: 0,
        fbc: "#e5e5e5",
        minWidth: 402,
        minHeight: 18,
        borderRadius: [0, 0, 9, 9],
        position: {
          vertical: 103,
          horizontal: "centre",
        },
        padding: 0,
      },
      {
        minWidth: 394,
        fs: 10,
        borderRadius: [0, 0, 10, 10],
        fbc: "#f1f5f9",
        position: {
          vertical: "bottom",
          horizontal: "left",
        },
        margin: 1,
      },
      {
        borderRadius: [0, 0, 0, 10],
        margin: {
          top: 1,
          right: 1,
          bottom: 1,
          left: 1,
        },
        fs: 10,
        fbc: "#10b981",
        minWidth: 230.5,
        position: {
          horizontal: "left",
          vertical: "bottom",
        },
      },
      {
        fbc: "#f1f5f9",
        minWidth: 40,
        minHeight: 60,
        position: { vertical: "middle", horizontal: -46 },
        borderRadius: [5, 0, 0, 5],
        bw: 2,
        b: "#e5e5e5",
        fs: 26,
        fi: {
          t: "fas fa-people-arrows",
          c: "#334155",
        },
      },
    ],
  };
}

function makeShadowCircleNode(id, x, y, colour, icon, text) {
  return {
    type: "node",
    id,
    x,
    y,
    c: colour,
    b: "#283618",
    bw: 2,
    e: 1.5,
    ff: "Varela Round",
    ha0: {
      c: "rgba(220,220,220,1)",
      r: 29,
      w: 1,
    },
    ha1: {
      c: "rgba(220,220,220,0.8)",
      r: 30,
      w: 1,
    },
    ha2: {
      c: "rgba(220,220,220,0.6)",
      r: 31,
      w: 1,
    },
    ha3: {
      c: "rgba(220,220,220,0.4)",
      r: 32,
      w: 1,
    },
    ha4: {
      c: "rgba(220,220,220,0.2)",
      r: 33,
      w: 1,
    },
    t: [
      {
        fi: { t: icon, ff: "Material Icons" },
        fs: 30,
        fc: "#283618",
        fbc: "rgba(0, 0, 0, 0)",
        padding: {
          top: 6,
          right: 2,
          bottom: 0,
          left: 2,
        },
        position: { vertical: "top" },
      },
      {
        t: text,
        fs: 10,
        fb: true,
        fc: "#283618",
        fbc: "rgba(0, 0, 0, 0)",
      },
    ],
  };
}

function makeMaterialIconLabel(icon, fs, colour) {
  return {
    position: {
      horizontal: "centre",
      vertical: "middle",
    },
    ff: "Material Icons",
    fs: fs,
    fc: colour,
    fbc: "transparent",
    fi: {
      t: icon,
    },
    padding: {
      top: 5,
      right: 2,
      bottom: 0,
      left: 2,
    },
  };
}

function makeInteractiveNode(id, x, y, backgroundColour, accentColour) {
  return {
    type: "node",
    id,
    x,
    y,
    b: accentColour,
    borderRadius: [0, 10, 20, 10],
    c: backgroundColour,
    w: 305,
    h: 85,
    t: [
      {
        fbc: "transparent",
        fc: accentColour,
        fi: { t: "fas fa-desktop" },
        position: {
          horizontal: "left",
          vertical: "middle",
        },
        fs: 35,
        margin: {
          left: 17.5,
        },
      },
      {
        fbc: "transparent",
        fc: accentColour,
        fb: true,
        fs: 16,
        padding: "0 10 0 0",
        position: {
          horizontal: 70,
          vertical: 15,
        },
        t: "Interactive Node",
        textAlignment: {
          horizontal: "left",
        },
      },
      {
        fbc: accentColour,
        fc: backgroundColour,
        borderRadius: 10,
        fb: true,
        margin: "10 0",
        minWidth: 60,
        padding: {
          top: 8,
          right: 10,
          bottom: 6,
          left: 10,
        },
        position: {
          horizontal: 70,
        },
        t: "Invert Style",
      },
      {
        fb: true,
        fc: backgroundColour,
        fbc: accentColour,
        fs: 14,
        margin: {
          top: 10,
          right: 10,
          bottom: 0,
          left: 10,
        },
        borderRadius: [10, 0, 0, 10],
        padding: {
          top: 9,
          right: 10,
          bottom: 9.75,
          left: 10,
        },
        position: {
          vertical: "inherit",
        },
        fi: { t: "fas fa-external-link-alt" },
      },
      {
        fbc: accentColour,
        fc: backgroundColour,
        borderRadius: [0, 10, 10, 0],
        fb: true,
        margin: {
          top: 10,
          right: 0,
          bottom: 0,
          left: 30,
        },
        minWidth: 60,
        padding: {
          top: 8,
          right: 10,
          bottom: 6,
          left: 1,
        },
        position: {
          horizontal: 170,
          vertical: "inherit",
        },
        t: "Go to Docs ",
      },
    ],
  };
}

export const normalInteractiveNode = makeInteractiveNode("t2", 425, 100, "#f4f3ee", "#3d405b");
export const invertedInteractiveNode = makeInteractiveNode("t2", 425, 100, "#3d405b", "#f4f3ee");

export const data = {
  type: "LinkChart",
  items: [
    {
      id: "t1-a",
      type: "node",
      c: "#A485D5",
      w: 100,
      h: 150,
      x: -475,
      y: -145,
      bw: 3,
      b: "#69738C",
      borderRadius: 10,
      t: [
        {
          fi: { t: "fas fa-server" },
          fs: 50,
          fbc: "transparent",
          fc: "white",
          position: { vertical: 24 },
        },
        {
          t: "Rack A",
          fs: 15,
          fbc: "transparent",
          fc: "white",
          fb: true,
          margin: "10 0 0 0",
        },
        {
          t: "OK",
          fs: 11,
          fbc: "transparent",
          fc: "white",
          padding: "0 20",
          margin: "5 0 0 0",
        },
        {
          fs: 0,
          fbc: "#69738C",
          borderRadius: "0 0 8.5 8.5",
          margin: 0,
          padding: 0,
          minWidth: 103,
          minHeight: 21.5,
          position: {
            horizontal: -1.5,
            vertical: 130,
          },
        },
        {
          fs: 0,
          fbc: "#FACA16",
          margin: 0,
          padding: 0,
          minWidth: 60,
          minHeight: 17,
          position: {
            horizontal: 0,
            vertical: 133,
          },
          borderRadius: "0 0 0 7",
        },
      ],
    },
    {
      id: "t1-b",
      type: "node",
      c: "#A485D5",
      w: 100,
      h: 150,
      x: -350,
      y: -145,
      bw: 3,
      b: "#69738C",
      borderRadius: 10,
      t: [
        {
          fi: { t: "fas fa-server" },
          fs: 50,
          fbc: "transparent",
          fc: "white",
          position: { vertical: 24 },
        },
        {
          t: "Rack B",
          fs: 15,
          fbc: "transparent",
          fc: "white",
          fb: true,
          margin: "10 0 0 0",
        },
        {
          t: "OK",
          fs: 11,
          fbc: "transparent",
          fc: "white",
          padding: "0 20",
          margin: "5 0 0 0",
        },
        {
          fs: 0,
          fbc: "#69738C",
          borderRadius: "0 0 8.5 8.5",
          margin: 0,
          padding: 0,
          minWidth: 103,
          minHeight: 21.5,
          position: {
            horizontal: -1.5,
            vertical: 130,
          },
        },
        {
          fs: 0,
          fbc: "#FF3C38",
          margin: 0,
          padding: 0,
          minWidth: 80,
          minHeight: 17,
          position: {
            horizontal: 0,
            vertical: 133,
          },
          borderRadius: "0 0 0 7",
        },
      ],
    },
    {
      id: "t1-c",
      type: "node",
      c: "#61A899",
      w: 100,
      h: 150,
      x: -597,
      y: -145,
      bw: 3,
      b: "#69738C",
      borderRadius: 10,
      t: [
        {
          fi: { t: "fas fa-hdd" },
          fs: 50,
          fbc: "transparent",
          fc: "white",
          position: { vertical: 24 },
        },
        {
          t: "Blade C",
          fs: 15,
          fbc: "transparent",
          fc: "white",
          fb: true,
          margin: "10 0 0 0",
        },
        {
          t: "High CPU usage",
          fs: 11,
          fbc: "transparent",
          fc: "white",
          padding: "0 20",
          margin: "5 0 0 0",
        },
        {
          fs: 0,
          fbc: "#69738C",
          borderRadius: "0 0 8.5 8.5",
          margin: 0,
          padding: 0,
          minWidth: 103,
          minHeight: 21.5,
          position: {
            horizontal: -1.5,
            vertical: 130,
          },
        },
        {
          fs: 0,
          fbc: "#65B540",
          margin: 0,
          padding: 0,
          minWidth: 30,
          minHeight: 17,
          position: {
            horizontal: 0,
            vertical: 133,
          },
          borderRadius: "0 0 0 7",
        },
      ],
    },
    normalInteractiveNode,
    {
      id: "j1-rectangle",
      type: "node",
      c: "#34d399",
      ff: "Montserrat",
      w: 230,
      h: 70,
      x: -105,
      y: -461,
      minHeight: 80,
      t: [
        {
          fi: { t: "fas fa-random" },
          fs: 35,
          fc: "#34d399",
          fbc: "#404347",
          minHeight: 55,
          margin: "0 0 0 7.5",
          padding: {
            top: 2,
            right: 2,
            bottom: 0,
            left: 10,
          },
        },
        {
          t: "Generate Data",
          fs: "auto",
          fb: true,
          fc: "white",
          fbc: "#404347",
          minHeight: 55,
          margin: {
            top: 0,
            right: 7.5,
            bottom: 0,
            left: 0,
          },
          padding: {
            top: 2,
            right: 13,
            bottom: 0,
            left: 13,
          },
          position: { vertical: "inherit" },
        },
      ],
    },
    {
      id: "j2-rectangle",
      type: "node",
      c: "#FFE642",
      ff: "Montserrat",
      w: 230,
      h: 70,
      x: -105,
      y: -355,
      minHeight: 80,
      t: [
        {
          fi: { t: "fas fa-database" },
          fs: 35,
          fc: "#FFE642",
          fbc: "#404347",
          minHeight: 55,
          margin: "0 0 0 7.5",
          padding: {
            top: 2,
            right: 2,
            bottom: 0,
            left: 10,
          },
        },
        {
          t: "Open Database",
          fs: "auto",
          fb: true,
          fc: "white",
          fbc: "#404347",
          minHeight: 55,
          margin: {
            top: 0,
            right: 7.5,
            bottom: 0,
            left: 0,
          },
          padding: {
            top: 2,
            right: 13,
            bottom: 0,
            left: 13,
          },
          position: { vertical: "inherit" },
        },
        {
          t: "Unavailable",
          fs: 12,
          fc: "white",
          fbc: "#404347",
          bw: 3,
          b: "#FFE642",
          borderRadius: 20,
          margin: { bottom: -20, left: -60 },
          padding: {
            top: 8,
            right: 8,
            bottom: 6,
            left: 8,
          },
          position: "ne",
        },
      ],
    },
    {
      id: "j3-rectangle",
      type: "node",
      c: "#fb7185",
      ff: "Montserrat",
      w: 230,
      h: 70,
      x: -105,
      y: -240,
      minHeight: 80,
      t: [
        {
          fi: { t: "fas fa-bug" },
          fs: 35,
          fc: "#fb7185",
          fbc: "#404347",
          minHeight: 55,
          margin: "0 0 0 7.5",
          padding: {
            top: 2,
            right: 2,
            bottom: 0,
            left: 10,
          },
        },
        {
          t: "Debug Console",
          fs: "auto",
          fb: true,
          fc: "white",
          fbc: "#404347",
          minHeight: 55,
          margin: {
            top: 0,
            right: 7.5,
            bottom: 0,
            left: 0,
          },
          padding: {
            top: 2,
            right: 13,
            bottom: 0,
            left: 13,
          },
          position: { vertical: "inherit" },
        },
        {
          t: "Unavailable",
          fs: 12,
          fc: "white",
          fbc: "#404347",
          bw: 3,
          b: "#fb7185",
          borderRadius: 20,
          margin: { bottom: -20, left: -60 },
          padding: {
            top: 8,
            right: 8,
            bottom: 6,
            left: 8,
          },
          position: "ne",
        },
      ],
    },
    makeShadowCircleNode("j4-circle", 247, -100, "#fefae0", "local_florist", "Bloom"),
    makeShadowCircleNode("j5-circle", 120, -122, "#f4f3ee", "air", "Blow"),
    makeShadowCircleNode("j6-circle", 203, -222, "#cd4631", "local_fire_department", "Burn"),
    makeProfileCardNode("j7-profile-card", -500, -400),
    {
      id: "c1-circle",
      type: "node",
      sh: "circle",
      c: "rgba(0,0,0,0)",
      e: 1.7,
      x: -40,
      y: -25,
      t: [
        makeMaterialIconLabel("circle", 75, "#0f2e18"),
        makeMaterialIconLabel("circle", 70, "#FFF"),
        makeMaterialIconLabel("circle", 65, "#0f2e18"),
        makeMaterialIconLabel("circle", 60, "#FFF"),
        makeMaterialIconLabel("circle", 55, "#0f2e18"),
        {
          ...makeMaterialIconLabel("flight", 30, "#FFF"),
          padding: { top: 0, right: 0, bottom: 0, left: 1 },
        },
      ],
    },
    {
      id: "c1-hexagon",
      type: "node",
      sh: "box",
      c: "rgba(0,0,0,0)",
      e: 1.7,
      x: -105,
      y: 100,
      t: [
        makeMaterialIconLabel("hexagon", 75, "#001845"),
        makeMaterialIconLabel("hexagon", 70, "#FFF"),
        makeMaterialIconLabel("hexagon", 65, "#001845"),
        makeMaterialIconLabel("hexagon", 60, "#FFF"),
        makeMaterialIconLabel("hexagon", 55, "#001845"),
        makeMaterialIconLabel("place", 30, "#FFF"),
      ],
    },
    {
      id: "c1-square",
      type: "node",
      sh: "box",
      c: "rgba(0,0,0,0)",
      e: 1.7,
      x: 45,
      y: 100,
      t: [
        makeMaterialIconLabel("square", 75, "#540b0e"),
        makeMaterialIconLabel("square", 70, "#FFF"),
        makeMaterialIconLabel("square", 65, "#540b0e"),
        makeMaterialIconLabel("square", 60, "#FFF"),
        makeMaterialIconLabel("square", 55, "#540b0e"),
        makeMaterialIconLabel("question_answer", 30, "#FFF"),
      ],
    },
    makeBusinessProcessNode("c1", 380, -434),
    {
      id: "node_Emanuela Cooper",
      type: "node",
      w: "auto",
      h: "auto",
      c: "#734b6d",
      b: "white",
      bw: 1,
      borderRadius: 10,
      t: [
        {
          u: `/im/people/Person3.png`,
          position: { horizontal: "left" },
          margin: 10,
        },
        {
          t: "Emanuela Cooper",
          position: { vertical: "inherit" },
          margin: { right: 10 },
          fbc: "transparent",
          fc: "#faf5ff",
        },
      ],
      x: 480,
      y: -250,
      g: [
        {
          p: "ne",
          fi: {
            t: "fas fa-plus",
            ff: "Font Awesome 5 Free",
            c: "white",
          },
          b: "transparent",
          c: "rgba(183, 92, 170, 1)",
          e: 1.2,
        },
        {
          fi: {
            t: "fas fa-expand-arrows-alt",
            c: "white",
            ff: "Font Awesome 5 Free",
          },
          c: "#513BC4",
          b: "transparent",
          p: "se",
          e: 1.2,
        },
      ],
    },
    {
      id: "node_Vito Morello",
      type: "node",
      w: "auto",
      h: 35,
      c: "#734b6d",
      b: "white",
      bw: 1,
      borderRadius: 10,
      t: [
        {
          t: "Vito Morello",

          margin: { left: 10 },
          position: { horizontal: "left" },
          fbc: "transparent",
          fc: "#faf5ff",
        },
        {
          u: `/im/people/Person9.png`,
          position: { vertical: "inherit" },
          margin: {
            left: 10,
            right: 20,
          },
        },
      ],
      x: 480,
      y: -145,
      g: [
        {
          p: "ne",
          fi: {
            t: "fas fa-plus",
            ff: "Font Awesome 5 Free",
            c: "white",
          },
          b: "transparent",
          c: "rgba(183, 92, 170, 1)",
          e: 1.2,
        },
        {
          fi: {
            t: "fas fa-expand-arrows-alt",
            c: "white",
            ff: "Font Awesome 5 Free",
          },
          c: "#513BC4",
          b: "transparent",
          p: "se",
          e: 1.2,
        },
      ],
    },
    {
      id: "people",
      type: "node",
      w: "auto",
      h: 50,
      c: "#734b6d",
      b: "white",
      bw: 1,
      borderRadius: 10,
      t: [
        {
          t: "Team",
          margin: {
            left: 10,
          },
          position: {
            horizontal: "left",
          },
          fbc: "transparent",
          fc: "#faf5ff",
        },
        {
          u: `/im/people/Person2.png`,
          position: {
            vertical: "inherit",
          },
          maxWidth: 30,
          maxHeight: 30,
          margin: {
            left: 10,
          },
        },
        {
          u: `/im/people/Person13.png`,
          position: {
            vertical: "inherit",
          },
          maxWidth: 30,
          maxHeight: 30,
          margin: {
            left: -5,
          },
        },
        {
          u: `/im/people/Person8.png`,
          position: {
            vertical: "inherit",
          },
          maxWidth: 30,
          maxHeight: 30,
          margin: {
            left: -5,
          },
        },
        {
          u: `/im/people/Person5.png`,
          position: {
            vertical: "inherit",
          },
          maxWidth: 30,
          maxHeight: 30,
          margin: {
            left: -5,
          },
        },
        {
          fi: { t: "fas fa-ellipsis-h", c: "#334155" },
          position: {
            vertical: "inherit",
          },
          fs: 20,
          minWidth: 30,
          minHeight: 29.5,
          borderRadius: 180,
          fbc: "#f8fafc",
          padding: [1, 0, 0, 0],
          margin: {
            left: -5,
            right: 10,
          },
        },
      ],
      x: 480,
      y: -55,
    },
    {
      id: "outside-buttons-rectangle",
      type: "node",
      x: -597,
      y: 80,
      c: "#f3f4f6",
      w: 100,
      h: 130,
      borderRadius: 10,
      bw: 1,
      b: "rgba(229, 231, 235, 0.8)",
      t: [
        {
          fi: { t: "fas fa-file-invoice", c: "#0d9488" },
          fbc: "rgba(0,0,0,0)",
          fs: 40,
          position: { horizontal: "centre", vertical: "top" },
          margin: {
            top: 15,
            bottom: 10,
          },
        },
        {
          t: "Insurance Policy",
          fbc: "rgba(0,0,0,0)",
          fc: "#334155",
          textWrap: "normal",
        },
        {
          t: "id: 842 921",
          fbc: "rgba(0,0,0,0)",
          fc: "#64748b",
          textWrap: "normal",
          fs: 12,
          margin: { top: 10, bottom: 10 },
        },
        // side buttons
        {
          fi: { t: "fas fa-check", c: "#0f766e" },
          fbc: "#f3f4f6",
          fs: 18,
          minHeight: 30,
          minWidth: 30,
          position: { horizontal: 110, vertical: "top" },
          borderRadius: [5, 5, 0, 0],
        },
        {
          maxHeight: 1,
          maxWidth: 34,
          fbc: "red",
          u: lineSVG,
        },
        {
          fi: { t: "fas fa-bell-slash", c: "#0f766e" },
          fbc: "#f3f4f6",
          fs: 18,
          minHeight: 30,
          minWidth: 30,
        },
        {
          maxHeight: 1,
          maxWidth: 34,
          fbc: "red",
          u: lineSVG,
        },
        {
          fi: { t: "fas fa-eye-slash", c: "#0f766e" },
          fbc: "#f3f4f6",
          fs: 18,
          minHeight: 30,
          minWidth: 30,
          borderRadius: [0, 0, 5, 5],
        },
      ],
    },
    {
      id: "outside-buttons-circle",
      type: "node",
      x: -400,
      y: 80,
      c: "#f3f4f6",
      w: 130,
      h: 130,
      borderRadius: 180,
      bw: 1,
      b: "rgba(229, 231, 235, 0.8)",
      t: [
        {
          fi: { t: "fas fa-file-invoice", c: "#0d9488" },
          fbc: "rgba(0,0,0,0)",
          fs: 40,
          position: { horizontal: "centre", vertical: "top" },
          margin: {
            top: 15,
            bottom: 10,
          },
        },
        {
          t: "Insurance Policy",
          fbc: "rgba(0,0,0,0)",
          fc: "#334155",
          textWrap: "normal",
        },
        {
          t: "id: 842 921",
          fbc: "rgba(0,0,0,0)",
          fc: "#64748b",
          textWrap: "normal",
          fs: 12,
          margin: { top: 10, bottom: 10 },
        },
        // side buttons
        {
          fi: { t: "fas fa-check", c: "#0f766e" },
          fbc: "#f3f4f6",
          fs: 18,
          minHeight: 32,
          minWidth: 32,
          position: { horizontal: 135, vertical: 10 },
          borderRadius: 180,
          margin: 2.5,
          padding: 0,
        },
        {
          fi: { t: "fas fa-bell-slash", c: "#0f766e" },
          fbc: "#f3f4f6",
          fs: 18,
          minHeight: 32,
          minWidth: 32,
          borderRadius: 180,
          margin: 2.5,
          padding: 0,
        },
        {
          fi: { t: "fas fa-eye-slash", c: "#0f766e" },
          fbc: "#f3f4f6",
          fs: 18,
          minHeight: 32,
          minWidth: 32,
          borderRadius: 180,
          margin: 2.5,
          padding: 0,
        },
      ],
    },
  ],
};
<!doctype html>
<html lang="en" style="background-color: #2d383f">
  <head>
    <meta charset="utf-8" />
    <title>Advanced Node Styles</title>
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
    <link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/fontawesome.css"
    />
    <link
      rel="stylesheet"
      type="text/css"
      href="@fortawesome/[email protected]/css/solid.css"
    />
    <link
      rel="stylesheet"
      type="text/css"
      href="https://fonts.googleapis.com/css2?family=Raleway&amp;family=Montserrat&amp;family=Varela+Round"
    />
    <link rel="stylesheet" href="./style.css" />
  </head>
  <body>
    <div id="klchart" class="klchart"></div>
    <script type="module" src="./code.js"></script>
  </body>
</html>
@import url("https://fonts.googleapis.com/icon?family=Material+Icons");

/* Enable cursor overriding while hovering the chart */
.cursor {
  cursor: pointer !important;
}

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.