# How Border and Background Styles Are Applied to Group Layers in CLI-Anything

> Learn how CLI-Anything applies border and background styles to group layers by injecting an auxiliary background rectangle. Understand this unique container approach.

- Repository: [✨Data Intelligence Lab@HKU✨/CLI-Anything](https://github.com/HKUDS/CLI-Anything)
- Tags: how-to-guide
- Published: 2026-08-16

---

**CLI-Anything applies border and background styles to group layers by injecting an auxiliary background rectangle as the first child of the group, which carries the visual properties while the group itself functions as a pure container.**

CLI-Anything is an open-source framework that converts natural language commands into structured design artifacts. When exporting to Sketch format, understanding how border and background styles are applied to group layers requires examining the [`builder.js`](https://github.com/HKUDS/CLI-Anything/blob/main/builder.js) harness, which translates JSON specifications into visual layer hierarchies.

## The Background Rectangle Pattern

In the Sketch agent of HKUDS/CLI-Anything, group layers are strictly organizational containers that do not natively support visual styling properties. To achieve the appearance of borders or backgrounds on groups, the builder implements a proxy pattern using auxiliary rectangles.

### Style Resolution in builder.js

Before constructing the layer tree, each specification undergoes style resolution. In [`sketch/agent-harness/src/builder.js`](https://github.com/HKUDS/CLI-Anything/blob/main/sketch/agent-harness/src/builder.js), the `resolveStyle` function processes the `style` field of each node, resolving design token references into concrete color and dimension values. These resolved values are stored on the specification object as `_resolvedStyle`, making normalized `backgroundColor`, `borderColor`, `borderWidth`, and `cornerRadius` values available for downstream logic.

### Group Construction Logic

When the builder encounters a node with `type: 'group'`, it recursively processes children via `buildLayerTree`, then checks the `_resolvedStyle` object. If the style contains a `backgroundColor` or `borderColor`, the builder creates a rectangle using `primitives.createRectangle` with dimensions matching the group's frame. This rectangle is prepended to the children array before the group is instantiated via `primitives.createGroup`.

```javascript
// sketch/agent-harness/src/builder.js – group case (excerpt)
case 'group': {
  const children = buildLayerTree(spec.children || [], spec._childLayout || [], tokens);

  // If the group has a background/border style, insert a bg rect first
  const groupChildren = [];
  if (resolved.backgroundColor || resolved.borderColor) {
    groupChildren.push(
      primitives.createRectangle({
        name: (spec.name || 'Group') + '_bg',
        x: 0,
        y: 0,
        width: frame.width,
        height: frame.height,
        backgroundColor: resolved.backgroundColor,
        borderColor: resolved.borderColor,
        borderWidth: resolved.borderWidth,
        cornerRadius: resolved.cornerRadius || 0,
      })
    );
  }
  groupChildren.push(...children);
  return primitives.createGroup(props, groupChildren);
}

```

Because this rectangle is positioned at `(0, 0)` with the full group dimensions and placed at the bottom of the stacking order, it visually serves as the group's background and border while actual child layers render on top.

## Practical Implementation Examples

### JSON Specification for Styled Groups

When defining a group in your specification, apply styles directly to the group node. The builder automatically extracts these properties and creates the background rectangle.

```json
{
  "type": "group",
  "name": "Card",
  "style": {
    "backgroundColor": "#f0f4ff",
    "borderColor": "#3366ff",
    "borderWidth": 2,
    "cornerRadius": 6
  },
  "children": [
    {
      "type": "rectangle",
      "name": "Icon",
      "width": 40,
      "height": 40,
      "style": { "backgroundColor": "#ffdd00" }
    },
    {
      "type": "text",
      "value": "Hello CLI‑Anything",
      "fontSize": 14,
      "style": { "color": "#222222" }
    }
  ]
}

```

This specification produces a layer hierarchy where `Card_bg` carries the blue border and light blue background, while the Icon and text layers sit above it.

### Programmatic Group Creation

You can replicate this behavior directly using the primitives API:

```javascript
const { primitives } = require('./sketch/agent-harness/src/primitives');

const resolved = {
  backgroundColor: '#f0f4ff',
  borderColor: '#3366ff',
  borderWidth: 2,
  cornerRadius: 6,
};

// Create the background rectangle (as builder.js does automatically)
const bgRect = primitives.createRectangle({
  name: 'Card_bg',
  x: 0,
  y: 0,
  width: 200,
  height: 120,
  backgroundColor: resolved.backgroundColor,
  borderColor: resolved.borderColor,
  borderWidth: resolved.borderWidth,
  cornerRadius: resolved.cornerRadius,
});

// Create content layers
const icon = primitives.createRectangle({ 
  name: 'Icon', x: 10, y: 10, width: 40, height: 40 
});
const label = primitives.createText({ 
  name: 'Label', x: 60, y: 20, value: 'Hello' 
});

// Assemble with background first to ensure correct z-order
const group = primitives.createGroup(
  { name: 'Card', x: 0, y: 0, width: 200, height: 120 },
  [bgRect, icon, label]
);

```

## Core Source Files

The border and background styling mechanism for group layers spans two primary files in the `sketch/agent-harness` directory:

- **[`src/builder.js`](https://github.com/HKUDS/CLI-Anything/blob/main/src/builder.js)**: Contains the `resolveStyle` function and the group case logic that conditionally inserts background rectangles based on resolved style properties.
- **[`src/primitives.js`](https://github.com/HKUDS/CLI-Anything/blob/main/src/primitives.js)**: Defines `createRectangle` and `createGroup` factory functions that generate the actual Sketch layer objects with proper property schemas.

## Summary

- **Group layers in CLI-Anything are style-agnostic containers** that rely on auxiliary rectangles for visual properties.
- **`resolveStyle` in builder.js** normalizes token references and inline styles into concrete values before layer construction.
- **Background rectangles are injected as the first child** of any group defining `backgroundColor` or `borderColor`, using the group's full frame dimensions.
- **The naming convention `GroupName_bg`** makes these proxy layers identifiable in the resulting Sketch file hierarchy.
- **Stacking order is critical**: The background rectangle must remain at the bottom of the group's child array for proper visual rendering.

## Frequently Asked Questions

### Why doesn't the group layer itself have background properties?

Sketch's group layer specification treats groups as organizational containers without inherent fill or stroke properties. CLI-Anything respects this constraint by generating a separate rectangle layer that visually represents the group's background, ensuring compatibility with the Sketch format while allowing designers to specify styles intuitively in the JSON spec.

### What happens if a group has no background or border color defined?

If the resolved style lacks both `backgroundColor` and `borderColor`, the builder skips the rectangle creation entirely and passes only the content children to `primitives.createGroup`. The resulting group functions purely as a layout container with no auxiliary background layer.

### Can corner radius be applied to group borders?

Yes. When the builder creates the background rectangle, it passes the `cornerRadius` value from the resolved style. The rectangle respects this property, creating rounded corners that visually appear as the group's border radius since the rectangle spans the entire group frame.

### How does this pattern affect layer selection in Sketch?

Because the background is a distinct rectangle layer named with the `_bg` suffix, users can select it independently from the group container or its content children. This separation allows for individual manipulation of the background fill without affecting the group's structural organization or the positioning of nested elements.