> For the complete documentation index, see [llms.txt](https://developer.singular.live/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.singular.live/software-development-kits/widget-sdk/guides-and-examples/widget-example-css-patterns.md).

# Widget example: CSS patterns

## **Widget UI**

The widget UI exposes the following properties and groups them in a single card.

<table><thead><tr><th width="164">Name</th><th width="128">Id</th><th width="127">Type</th><th>Description</th></tr></thead><tbody><tr><td>Select Pattern</td><td>patternId</td><td>selection</td><td>Selection of pattern names</td></tr><tr><td>Back Color</td><td>backColorId</td><td>color</td><td>Background color</td></tr><tr><td>Front Color</td><td>frontColorId</td><td>color</td><td>Foreground color</td></tr><tr><td>Spacing</td><td>spacingId</td><td>number</td><td>Spacing between pattern repetitions</td></tr></tbody></table>

<figure><img src="/files/uQHJPoCmQd8sOx8gK9s2" alt=""><figcaption><p>Widget UI</p></figcaption></figure>

<details>

<summary>UI definition</summary>

```json
{
  "model": {
    "fields": [
      {
        "id": "patternId",
        "type": "selection",
        "title": "Select Pattern",
        "defaultValue": "Rectangles",
        "selections": [
          {
            "id": "Circles",
            "title": "Circles"
          },
          {
            "id": "Rectangles",
            "title": "Rectangles"
          },
          {
            "id": "Triangles",
            "title": "Triangles"
          }
        ]
      },
      {
        "id": "backColorId",
        "type": "color",
        "title": "Back Color",
        "defaultValue": "#E5E5F7"
      },
      {
        "id": "frontColorId",
        "type": "color",
        "title": "Front Color",
        "defaultValue": "#444CF7"
      },
      {
        "id": "spacingId",
        "type": "number",
        "title": "Spacing",
        "defaultValue": "25",
        "min": "1",
        "max": "100",
        "step": "0.1",
        "unit": "unit",
        "format": "0.1"
      }
    ],
    "groups": [
      {
        "id": "group1Id",
        "title": "Pattern Options",
        "width": "single",
        "toolTip": "Select a Pattern and modify options",
        "childIds": ["patternId", "backColorId", "frontColorId", "spacingId"]
      }
    ]
  }
}
```

</details>

## Widget code

{% hint style="info" %}
The widget loads and uses the tinycolor.js library to convert various color values (RGB, HEX, HSL, RGBA, or HSLA) to HEX color definition.
{% endhint %}

<details>

<summary>output.html</summary>

```html
<!DOCTYPE html>
<html>
  <head>
    <style>
      /* Default widget style to make output match the bouding box */
      html,
      body {
        background: none;
        margin: 0;
        padding: 0;
        height: 100%;
        overflow: hidden;
      }
      /* Add your custom styles here */
    </style>
  </head>
  <body>
    <!-- Your custom html goes here -->
    <div id="pattern" style="position: absolute; left:0%; top:0%; width: 100%; height: 100%"></div>
    
    <!-- Singular Widget Library -->
    <script src="https://app.singular.live/libs/singularwidget/1.0.4/singularwidget.js"></script>
    <!-- Load tinycolor library from cloudflare cdn -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.5.2/tinycolor.min.js" integrity="sha512-gV2/CUt/1tn0eFseTyMjNqZU3kvhXacxA5eFoTwTd9c4b/Ems0BF00LOG/KcWnGZRdo62tqYNS8IiKtU4PGVoA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
      let windowWidth = 0;
      let windowHeight = 0;
      let elePattern = undefined;
      let patternChanged = false;
      let pattern = "Rectangles";
      let spacing = 50;
      let bgColor = "#E5E5F7";
      let fgColor = "#444CF7";
      // we define pattern styles in a map
      const patternStyles = {
        "Circles": {
          draw: (params) => {
            const cssBgImage = "radial-gradient(circle at center center, {{fgColor}}, {{bgColor}}), repeating-radial-gradient(circle at center center, {{fgColor}}, {{fgColor}}, {{spacing}}%, transparent {{spacing2x}}%, transparent {{spacing}}%)";
            elePattern.style.backgroundImage = cssBgImage.replaceAll("{{fgColor}}", params.fgColor).replaceAll("{{bgColor}}", params.bgColor).replaceAll("{{spacing2x}}", params.spacing * 2).replaceAll("{{spacing}}", params.spacing);
            elePattern.style.backgroundBlendMode = "multiply";
          }
        },
        "Rectangles": {
          draw: (params) => {
            const cssBackground = "repeating-conic-gradient({{bgColor}} 0% 25%, {{fgColor}} 0% 50%) 50%/{{spacing}}vw {{spacing}}vh";
            elePattern.style.background = cssBackground.replaceAll("{{fgColor}}", params.fgColor).replaceAll("{{bgColor}}", params.bgColor).replaceAll("{{spacing}}", params.spacing);
          }
        },
        "Triangles": {
          draw: (params) => {
            const cssBackground = "conic-gradient(from 26.56505deg, {{bgColor}} 0% 63.43495deg, {{fgColor}} 0% 126.8699deg, {{bgColor}} 0% 50%, {{fgColor}} 0% 243.43495deg, {{bgColor}} 0% 306.8699deg, {{fgColor}} 0%) 0 0/ calc(9/16*{{spacing}}vh) {{spacing}}vh";
            elePattern.style.background = cssBackground.replaceAll("{{fgColor}}", params.fgColor).replaceAll("{{bgColor}}", params.bgColor).replaceAll("{{spacing}}", params.spacing);
          }
        }
      };
      /**
       * initialize Singular widget object and define callback functions
       */
      SingularWidget.init({
        onInit: onSingularInit,
        onValue: onSingularValue
      });
      /**
       * addEventListener for window resizing
       * relevant when your widget displays HTML content or uses widget compositions
       */
      window.addEventListener("resize", function() {
        if (windowWidth != window.innerWidth || windowHeight != window.innerHeight) {
          windowWidth = window.innerWidth;
          windowHeight = window.innerHeight;
        }
      });
      /**
       * onSingularInit()
       * called when the widget instance is created
       */
      function onSingularInit(params) {
        console.log("onSingularInit() - SingularWidget =", SingularWidget);
        console.log("onSingularInit() - params =", params);
        windowWidth = window.innerWidth;
        windowHeight = window.innerHeight;
        elePattern = document.getElementById('pattern');
      }
      /**
       * onSingularValue()
       * called when the widget instance is created or the instance data is changed
       */
      function onSingularValue(json) {
        console.log("onSingularValue() - json =", json);
        patternChanged = false;
        if (json.patternId !== undefined && json.patternId != pattern) {
          pattern = json.patternId;
          patternChanged = true;
        }
        if (json.spacingId !== undefined && Number(json.spacingId) != spacing) {
          spacing = Number(json.spacingId);
          patternChanged = true;
        }
        const newBgColor = tinycolor(json.backColorId).toHex8String();
        if (json.backColorId !== undefined && newBgColor != bgColor) {
          bgColor = newBgColor;
          patternChanged = true;
        }
        const newFgColor = tinycolor(json.frontColorId).toHex8String();
        if (json.frontColorId !== undefined && newFgColor != fgColor) {
          fgColor = newFgColor;
          patternChanged = true;
        }
        if (patternChanged === true) {
          redrawPattern({
            spacing: spacing,
            bgColor: bgColor,
            fgColor: fgColor
          });
        }
      }
      /**
       * redrawPattern()
       */
      const redrawPattern = function(params) {
        // we reset all style properties
        elePattern.style.background = "";
        elePattern.style.backgroundImage = "";
        elePattern.style.backgroundPosition = "";
        elePattern.style.backgroundSize = "";
        elePattern.style.backgroundBlendMode = "";
        // draw pattern
        patternStyles[pattern].draw(params);
      }
    </script>
  </body>
</html>
```

</details>

<figure><img src="/files/oI42TenvRY9xDevcJKvc" alt=""><figcaption><p>Pattern Widget UI in Composer</p></figcaption></figure>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.singular.live/software-development-kits/widget-sdk/guides-and-examples/widget-example-css-patterns.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
