# Nova App Build Task (for devs)

**URL:** <https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268>\
**Category:** Elements\
**Created:** [November 6, 2025, 2:17pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268 "2025-11-06T14:17:38Z")\
**Posts on this page:** 18\
**Page:** 2

<div class="post-metadata">

**Author:** ![handshaper](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/handshaper/32/27373_2.png) [@handshaper](https://forums.realmacsoftware.com/u/handshaper)\
**Post date:** [November 9, 2025, 7:00pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/21 "2025-11-09T19:00:57Z")

</div>

@dan Wow, these examples are incredibly useful. Additionally, I can now obtain the correct IDs for some of the icons.

QUESTION: With these property examples, are there any dependencies on code in a hooks.js file for processing properties, or can these be used as is?

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 9, 2025, 7:14pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/22 "2025-11-09T19:14:26Z")

</div>

A lot of them will need to pass through hooks to filter down to the user selected values.  
Examples soon.

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 9, 2025, 7:16pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/23 "2025-11-09T19:16:15Z")

</div>

Pre-release video (will release later today).  
You will have to handle the hooks file yourselves, but for now this is the fastest way to get the core groups UI.

[![](https://europe1.discourse-cdn.com/flex005/uploads/realmacsoftware1/original/3X/7/9/79b52ebc72184401794df777cff3635606f1158b.jpeg "Component Props Beta") ](https://www.youtube.com/watch?v=IqocVw6lf34)

Just need to create an icon, at a loss what represents this app 🤷‍♂️  
Might just have to go with something generic like a flower or something 🤷‍♂️

 ![CleanShot 2025-11-09 at 7 .22.54@2x](https://europe1.discourse-cdn.com/flex005/uploads/realmacsoftware1/original/3X/f/f/ff6790f74d3c7443e09cb3b4552f82fa81db0187.jpeg)

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 9, 2025, 7:59pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/24 "2025-11-09T19:59:33Z")

</div>

Here you go…Initial release. 1.0.0

[Download Component Props App](https://www.doobox.co.uk/appcasts/xcode/component_props/Component%20Props.zip)

Requires macOS 26.0 Tahoe or greater.

Please please backup your properties.json file somewhere before experimenting, there may be bugs. The properties.json file is the only file it will amend.

Shipped with Sparkle updater, Component Props \> Check for Updates.

icon needs love, don’t judge me 😂

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 9, 2025, 8:05pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/25 "2025-11-09T20:05:59Z")

</div>

> [@handshaper](#):
>
> Additionally, I can now obtain the correct IDs for some of the icons.

You need SFSymbols app from Apple, All the icons are SFSymbols.  
If you’re trying to match an icon, screenshot it and ask chat jippety “Which SF symbol is this?” Its good at identifying.

---

<div class="post-metadata">

**Author:** ![handshaper](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/handshaper/32/27373_2.png) [@handshaper](https://forums.realmacsoftware.com/u/handshaper)\
**Post date:** [November 9, 2025, 8:08pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/26 "2025-11-09T20:08:21Z")

</div>

Yes, that is what I’m using, but I want to try and use the exact same icons that RM are using, which is why these samples are handy. I had asked some time back for them to provide a list of the icons they use.

I’ll have to see how ChatGPT does at identifying them, had not thought of trying that yet.

---

<div class="post-metadata">

**Author:** ![handshaper](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/handshaper/32/27373_2.png) [@handshaper](https://forums.realmacsoftware.com/u/handshaper)\
**Post date:** [November 9, 2025, 8:09pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/27 "2025-11-09T20:09:00Z")

</div>

Use one of the icons that RM use for properties, maybe!

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 10, 2025, 6:13am UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/28 "2025-11-10T06:13:12Z")

</div>

So the plan going forward to aid in processing the core groups output through your hooks.js file.

I will add a small javascript payload to each of core group items in the right hand inspector.  
On right click you will get the option to “Copy Javascript” which will copy to your clipboard.

An example might look like this for Tag in the general group, ready to paste into your hooks.js…

 ![CleanShot 2025-11-10 at 6 .09.04@2x](https://europe1.discourse-cdn.com/flex005/uploads/realmacsoftware1/original/3X/f/8/f8120551b74e1392810bbb04c736f975ec3a3fbc.png)

```javascript
// Processed Tag
  const { tag, customTag } = rw.props;
  const pTag = tag === "custom" ? customTag || "div" : tag;

```

You’d then add pTag to the rw.setProps and it will be usable in your html as {{pTag}}

That’s the current plan anyway.

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 10, 2025, 8:23am UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/29 "2025-11-10T08:23:31Z")

</div>

> [@Doobox](#):
>
> That’s the current plan anyway.

Holy shit, just to handle the color group and still needs work. Am I holding something wrong here? Is this the reality of it?

```javascript
const transformHook = (rw) => {
  const p = rw.props || {};
  const { assetPath } = rw.component;

  // tag
  const pTag = p.tag === "custom" ? p.customTag || "div" : p.tag;

  // helpers -----------------------------------------------------
  const toks = [];
  const add = (v) => {
    if (!v) return;
    if (Array.isArray(v)) {
      v.forEach(add);
      return;
    }
    const s = String(v).trim();
    if (s) toks.push(s);
  };

  // Map "Over" to a hover prefix
  // self -> "hover:" ; others -> "group-hover/<over>:"
  const overToPrefix = (over) =>
    over && over !== "self" ? `group-hover/${over}:` : "hover:";

  // Normalize a single token into the correct hover context
  const reHover = /^hover:/;
  const reGroupHover = /^group-hover(?:\/[A-Za-z0-9_-]+)?:/;
  const toHoverContext = (token, over) => {
    const want = overToPrefix(over);
    if (reHover.test(token) || reGroupHover.test(token)) {
      // Replace any existing hover/group-hover prefix with the one we want
      return token.replace(reGroupHover, want).replace(reHover, want);
    }
    return want + token;
  };

  // Add a space-separated string of tokens into hover/group-hover context
  const addInHoverContext = (s, over) => {
    if (!s) return;
    String(s)
      .split(/\s+/)
      .forEach((t) => {
        if (!t) return;
        add(toHoverContext(t, over));
      });
  };

  // Safe URL for arbitrary classes
  const safeUrl = (u) => String(u).replace(/"/g, '\\"');

  // -------------------------------------------------------------
  const type = p.globalControlTypeBg; // "static" | "hover"
  const style = p.globalBgType; // "color" | "gradient" | "image"
  const over = p.globalHoverGroupBg; // "self" | "parent" | "container" | "grid" | "flex" | "custom"

  // ===== STATIC =================================================
  if (type === "static") {
    if (style === "color") {
      add(p.globalTextColor);
      add(p.globalTextColorOpacity);
    }

    if (style === "gradient") {
      // TEXT gradient (base)
      add(p.globalBgGradientDirection);
      add(p.globalBgGradientFromColor);
      add(p.globalBgGradientFromOpacity);
      add(p.globalBgGradientFromPosition);
      add(p.globalBgGradientViaColor);
      add(p.globalBgGradientViaOpacity);
      add(p.globalBgGradientViaPosition);
      add(p.globalBgGradientToColor);
      add(p.globalBgGradientToOpacity);
      add(p.globalBgGradientToPosition);
      add("bg-clip-text");
      add("text-transparent");
    }

    if (style === "image") {
      const baseUrl = p.globalBgImageResource && p.globalBgImageResource.url;
      if (baseUrl) {
        add(`[--bg-url:url("${safeUrl(baseUrl)}")]`);
        add("[background-image:var(--bg-url)]");
      }
      add(p.globalBgImagePosition);
      add(p.globalBgImageSize);
      add(p.globalBgImageRepeat);
    }
  }

  // ===== HOVER ==================================================
  if (type === "hover") {
    // START (base) — always included
    if (style === "color") {
      add(p.globalTextColor);
      add(p.globalTextColorOpacity);
    }
    if (style === "gradient") {
      add(p.globalBgGradientDirection);
      add(p.globalBgGradientFromColor);
      add(p.globalBgGradientFromOpacity);
      add(p.globalBgGradientFromPosition);
      add(p.globalBgGradientViaColor);
      add(p.globalBgGradientViaOpacity);
      add(p.globalBgGradientViaPosition);
      add(p.globalBgGradientToColor);
      add(p.globalBgGradientToOpacity);
      add(p.globalBgGradientToPosition);
      add("bg-clip-text");
      add("text-transparent");
    }
    if (style === "image") {
      const baseUrl = p.globalBgImageResource && p.globalBgImageResource.url;
      if (baseUrl) {
        add(`[--bg-url:url("${safeUrl(baseUrl)}")]`);
        add("[background-image:var(--bg-url)]");
      }
      add(p.globalBgImagePosition);
      add(p.globalBgImageSize);
      add(p.globalBgImageRepeat);
    }

    // END (hovered) — respect Over for ALL hover-side tokens
    if (style === "color") {
      addInHoverContext(p.globalTextColorHover, over);
      addInHoverContext(p.globalTextColorOpacityHover, over);
    }

    if (style === "gradient") {
      addInHoverContext(p.globalBgGradientDirectionEnd, over);
      addInHoverContext(p.globalBgGradientFromColorEnd, over);
      addInHoverContext(p.globalBgGradientFromOpacityEnd, over);
      addInHoverContext(p.globalBgGradientViaColorEnd, over);
      addInHoverContext(p.globalBgGradientViaOpacityEnd, over);
      addInHoverContext(p.globalBgGradientToColorEnd, over);
      addInHoverContext(p.globalBgGradientToOpacityEnd, over);
      // Ensure text gradient effect also switches on hover
      addInHoverContext("bg-clip-text", over);
      addInHoverContext("text-transparent", over);
      // If we output position classes for end, include them here too:
      addInHoverContext(p.globalBgGradientFromPositionEnd, over);
      addInHoverContext(p.globalBgGradientViaPositionEnd, over);
      addInHoverContext(p.globalBgGradientToPositionEnd, over);
    }

    if (style === "image") {
      const endUrl =
        p.globalBgImageResourceEnd && p.globalBgImageResourceEnd.url;
      if (endUrl)
        addInHoverContext(`[--bg-url:url("${safeUrl(endUrl)}")]`, over);
      addInHoverContext(p.globalBgImagePositionEnd, over);
      addInHoverContext(p.globalBgImageSizeEnd, over);
      addInHoverContext(p.globalBgImageRepeatEnd, over);
    }
  }

  // ==============================================================
  const colorMacro = toks.join(" ").replace(/\s+/g, " ").trim();

  rw.setProps({
    pTag,
    node: rw.node,
    assetPath,
    colorMacro,
  });
};

exports.transformHook = transformHook;

```

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 10, 2025, 12:01pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/31 "2025-11-10T12:01:17Z")

</div>

Very close to getting a failsafe colorMacro mapped to the output of “Colour” group. Just almost perfect in one file without reaching out to helpers.

BUT! I think it’s fair to say, developers are not going to be able to map out all the required js in Hooks on their own.

This will need a solution in the api I’d say. Something like the ability to call out to elements helpers from hooks. (spitballing).

I think the takeaway is “patience”. Sometime soon elements will need to provide an api solution.

```javascript
const transformHook = (rw) => {
  const p = rw.props || {};
  const { assetPath } = rw.component;

  // tag
  const pTag = p.tag === "custom" ? p.customTag || "div" : p.tag;

  // helpers -----------------------------------------------------
  const toks = [];
  const add = (v) => {
    if (!v) return;
    if (Array.isArray(v)) {
      v.forEach(add);
      return;
    }
    const s = String(v).trim();
    if (s) toks.push(s);
  };

  // Map "Over" to a hover prefix
  const overToPrefix = (over) =>
    over && over !== "self" ? `group-hover/${over}:` : "hover:";

  const reHover = /^hover:/;
  const reGroupHover = /^group-hover(?:\/[A-Za-z0-9_-]+)?:/;
  const toHoverContext = (token, over) => {
    const want = overToPrefix(over);
    if (reHover.test(token) || reGroupHover.test(token)) {
      return token.replace(reGroupHover, want).replace(reHover, want);
    }
    return want + token;
  };

  const addInHoverContext = (s, over) => {
    if (!s) return;
    String(s)
      .split(/\s+/)
      .forEach((t) => {
        if (!t) return;
        add(toHoverContext(t, over));
      });
  };

  // Robust resource resolver (handles string or object forms)
  const resolveResourceUrl = (val) => {
    if (!val) return null;
    if (typeof val === "string") return val;
    if (typeof val === "object") {
      return (
        val.url ||
        val.src ||
        val.path ||
        val.href ||
        (typeof val.value === "string" ? val.value : null) ||
        null
      );
    }
    return null;
  };

  // Build bg-[url(...)] safely (escape ])
  const makeBgUrlClass = (u) => {
    if (!u) return null;
    const v = String(u).replace(/\]/g, "\\]");
    return `bg-[url(${v})]`;
  };

  // -------------------------------------------------------------
  const type = p.globalControlTypeBg; // "static" | "hover"
  const style = p.globalBgType; // "color" | "gradient" | "image"
  const over = p.globalHoverGroupBg; // "self" | "parent" | "container" | "grid" | "flex" | "custom"

  // ===== STATIC =================================================
  if (type === "static") {
    if (style === "color") {
      add(p.globalTextColor);
      add(p.globalTextColorOpacity);
    }

    if (style === "gradient") {
      add(p.globalBgGradientDirection);
      add(p.globalBgGradientFromColor);
      add(p.globalBgGradientFromOpacity);
      add(p.globalBgGradientFromPosition);
      add(p.globalBgGradientViaColor);
      add(p.globalBgGradientViaOpacity);
      add(p.globalBgGradientViaPosition);
      add(p.globalBgGradientToColor);
      add(p.globalBgGradientToOpacity);
      add(p.globalBgGradientToPosition);
      add("bg-clip-text");
      add("text-transparent");
    }

    if (style === "image") {
      const baseUrl = resolveResourceUrl(p.globalBgImageResource);
      add("bg-clip-text");
      add("text-[transparent]");
      if (baseUrl) add(makeBgUrlClass(baseUrl));
      add(p.globalBgImagePosition);
      add(p.globalBgImageSize);
      add(p.globalBgImageRepeat);
    }
  }

  // ===== HOVER ==================================================
  if (type === "hover") {
    // START (base)
    if (style === "color") {
      add(p.globalTextColor);
      add(p.globalTextColorOpacity);
    }

    if (style === "gradient") {
      add(p.globalBgGradientDirection);
      add(p.globalBgGradientFromColor);
      add(p.globalBgGradientFromOpacity);
      add(p.globalBgGradientFromPosition);
      add(p.globalBgGradientViaColor);
      add(p.globalBgGradientViaOpacity);
      add(p.globalBgGradientViaPosition);
      add(p.globalBgGradientToColor);
      add(p.globalBgGradientToOpacity);
      add(p.globalBgGradientToPosition);
      add("bg-clip-text");
      add("text-transparent");
    }

    if (style === "image") {
      const baseUrl = resolveResourceUrl(p.globalBgImageResource);
      add("bg-clip-text");
      add("text-[transparent]");
      if (baseUrl) add(makeBgUrlClass(baseUrl));
      add(p.globalBgImagePosition);
      add(p.globalBgImageSize);
      add(p.globalBgImageRepeat);
    }

    // END (hovered) — respect Over
    if (style === "color") {
      addInHoverContext(p.globalTextColorHover, over);
      addInHoverContext(p.globalTextColorOpacityHover, over);
    }

    if (style === "gradient") {
      addInHoverContext(p.globalBgGradientDirectionEnd, over);
      addInHoverContext(p.globalBgGradientFromColorEnd, over);
      addInHoverContext(p.globalBgGradientFromOpacityEnd, over);
      addInHoverContext(p.globalBgGradientViaColorEnd, over);
      addInHoverContext(p.globalBgGradientViaOpacityEnd, over);
      addInHoverContext(p.globalBgGradientToColorEnd, over);
      addInHoverContext(p.globalBgGradientToOpacityEnd, over);
      addInHoverContext("bg-clip-text", over);
      addInHoverContext("text-transparent", over);
      addInHoverContext(p.globalBgGradientFromPositionEnd, over);
      addInHoverContext(p.globalBgGradientViaPositionEnd, over);
      addInHoverContext(p.globalBgGradientToPositionEnd, over);
    }

    if (style === "image") {
      const endUrl = resolveResourceUrl(p.globalBgImageResourceEnd);
      if (endUrl) addInHoverContext(makeBgUrlClass(endUrl), over);
      addInHoverContext(p.globalBgImagePositionEnd, over);
      addInHoverContext(p.globalBgImageSizeEnd, over);
      addInHoverContext(p.globalBgImageRepeatEnd, over);
    }
  }

  // ==============================================================
  const colorMacro = toks.join(" ").replace(/\s+/g, " ").trim();

  rw.setProps({
    pTag,
    node: rw.node,
    assetPath,
    colorMacro,
  });
};

exports.transformHook = transformHook;

```

---

<div class="post-metadata">

**Author:** ![ben](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/ben/32/28488_2.png) [@ben](https://forums.realmacsoftware.com/u/ben)\
**Post date:** [November 10, 2025, 12:23pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/32 "2025-11-10T12:23:01Z")

</div>

If you’re using the controls built for the Core Components, then yes there is quite a bit of javascript logic running behind the scenes to make those controls work 😅

We spent a lot of time designing the controls to be as intuitive and user-friendly as possible, which meant moving most of the complexity out of the UI and into the `hooks.js` files. The controls may look simple, but behind the scenes there’s a lot of logic ensuring each one maps correctly to the right Tailwind classes, and that those classes are applied properly. This approach also ensures Elements doesn’t force you (or us) into any single framework or version.

If you’d like to recreate our controls in your own components, you’ll need to implement some of that javascript logic yourself to handle things like state, logic, and class formatting, just as you’re doing in your example above.

One tip is to use the `format` property as much as possible. It allows Elements to handle some of the heavy lifting for you, including responsive prefixes. For example, a colour control can automatically output a class string such as:  
`text-red-500 md:text-blue-500 lg:text-green-500`

Some of the more advanced controls, such as those in the Background group, require even more logic to work correctly, meaning your `hooks.js` file will likely grow quite a bit. My advice is to structure it cleanly, and remember that it’s a standard javascript file, so you can add helper methods and organise it however you like, as long as it includes:  
`exports.transformHook = transformHook;`

If you have any other specific questions, or need help or advice on doing something in a component, just let me know 🙂

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 10, 2025, 12:41pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/33 "2025-11-10T12:41:45Z")

</div>

Yes, it all became apparent just how much logic mapping out a (on the face of it) simple control in the hooks file is required.

Most of the components I have developed would benefit from the inclusion of at least some of the core groups, and I expect that would be the case for all third party components.

I can map them out and make them re-usable. Just hadn’t foresaw the amount of js this was going to require.

I do hope in the future elements api will find a way (in layman’s terms) to ask for any core group, and have the macro for for the result of that call documented in the api. That seems like it would make things more approachable for third parties.

---

<div class="post-metadata">

**Author:** ![MultiThemes](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/multithemes/32/25117_2.png) [@MultiThemes](https://forums.realmacsoftware.com/u/MultiThemes)\
**Post date:** [November 10, 2025, 4:38pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/34 "2025-11-10T16:38:53Z")

</div>

> [@Doobox](#):
>
> I do hope in the future elements api will find a way (in layman’s terms) to ask for any core group, and have the macro for for the result of that call documented in the api. That seems like it would make things more approachable for third parties.

💯

I’m working on it too, and I’ve realized there’s a lot of work to be done. Users will probably ask for it when they can compare our work with native components.  
In any case, replication isn’t always necessary, but maintaining certain options is important to make the component familiar to the user 😀

---

<div class="post-metadata">

**Author:** ![handshaper](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/handshaper/32/27373_2.png) [@handshaper](https://forums.realmacsoftware.com/u/handshaper)\
**Post date:** [November 10, 2025, 7:19pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/35 "2025-11-10T19:19:25Z")

</div>

@ben This brings up an interesting question for me. Do all properties HAVE to be processed through the hooks.js file?

I have a lot of properties that work just fine that are not going through a hooks file, so I’m trying to understand if I need to fix that now.

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 10, 2025, 7:53pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/36 "2025-11-10T19:53:46Z")

</div>

> [@handshaper](#):
>
> Do all properties HAVE to be processed through the hooks.js file?

Not really. take this example of the “Advanced” group in the core components

```javascript
{
      "icon": "gearshape",
      "properties": [
        {
          "id": "cssClasses",
          "textArea": {
            "default": ""
          },
          "title": "CSS Classes"
        },
        {
          "id": "globalID",
          "responsive": false,
          "text": {
            "default": ""
          },
          "title": "ID"
        }
      ],
      "title": "Advanced"
    }

```

There is only one possible state, so you don’t need to worry about state.  
you could just use {{cssClasses}} and {{globalID}} directly.

Mostly the issue comes when there are multiple possible states that the group can be in, like when there are options via select controls. Then you need to map the state in hooks to your own processed macro {{processedMacro}}

if you look at the colour group, there are 6 main states

2 types x 3 styles

Those need processing in hooks.

 ![CleanShot 2025-11-10 at 7 .59.16@2x](https://europe1.discourse-cdn.com/flex005/uploads/realmacsoftware1/original/3X/e/2/e25c677acb20c24c4bbacf6c246bab46cf67128b.png)

---

<div class="post-metadata">

**Author:** ![handshaper](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/handshaper/32/27373_2.png) [@handshaper](https://forums.realmacsoftware.com/u/handshaper)\
**Post date:** [November 10, 2025, 7:59pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/37 "2025-11-10T19:59:55Z")

</div>

@Doobox Thanks for clarifying that, Gary. That is essentially the approach I have taken, where simple things don’t go through the hooks file.

It is incredible, though, how complex the whole property process can become if you are trying to do it correctly. I’m finding that to make a component viable for the average user, you have to be very strict about matching the core components.

Which is where your tool will be invaluable and a huge timesaver.

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 10, 2025, 8:07pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/38 "2025-11-10T20:07:32Z")

</div>

> [@Doobox](#):
>
> you could just use {{cssClasses}} and {{globalID}} directly.

But regardless that you can use these directly without processing. Even things like this can benefit from processing through hooks. in this example you could process in case the user entered junk, and not valid id and or classes. It’s endless what you can do via processing through hooks.

You might for example have in input where the user enters an email address. well while you could use that directly, it would benefit from email address validation via hooks.

---

<div class="post-metadata">

**Author:** ![Doobox](https://dub1.discourse-cdn.com/flex005/user_avatar/forums.realmacsoftware.com/doobox/32/32373_2.png) [@Doobox](https://forums.realmacsoftware.com/u/Doobox)\
**Post date:** [November 11, 2025, 6:59pm UTC](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268/39 "2025-11-11T18:59:27Z")

</div>

[![](https://europe1.discourse-cdn.com/flex005/uploads/realmacsoftware1/original/3X/6/8/68a5db7ba9a178f9d6be678320009bd9c7dfd656.jpeg "Hooks js implimentation") ](https://www.youtube.com/watch?v=vzUYwObPqK4)

[Previous page](https://forums.realmacsoftware.com/t/nova-app-build-task-for-devs/52268.md?page=1)
