core Button: hero-btn-disabled CSS class set but DOM disabled attribute is false #184

Closed
opened 2026-04-29 13:23:39 +00:00 by rawdaGastan · 4 comments
Member

Problem

The shared Button component (used as .send-btn in messaging, and elsewhere) renders the hero-btn-disabled class when its disabled prop is true, but does not set the corresponding disabled attribute on the <button> element. So the button looks disabled, fails to pass keyboard/screen-reader semantics, and clicks still fire the onclick handler.

Repro

Open any messaging chat, set the input to whitespace-only (or empty):

const el = document.querySelector('input.chat-input');
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setter.call(el, '   ');
el.dispatchEvent(new Event('input', {bubbles:true}));

const btn = document.querySelector('.send-btn');
console.log({hasDisabledClass: btn.classList.contains('hero-btn-disabled'), domDisabled: btn.disabled});
// → {hasDisabledClass: true, domDisabled: false}
btn.click(); // fires anyway

Where

Likely core/.../button.rs (the Button component used by hero_archipelagos_core). The chat input passes disabled: props.disabled || input_value().trim().is_empty() correctly at islands/chat_input.rs:85 — Button just doesn't propagate it to the DOM attribute.

Suggested fix

In the Button component, set both class and disabled attributes from the same prop. Same likely needed for IconButton.

Found via QA session 2026-04-29.

## Problem The shared Button component (used as `.send-btn` in messaging, and elsewhere) renders the `hero-btn-disabled` class when its `disabled` prop is true, **but does not set the corresponding `disabled` attribute on the `<button>` element**. So the button looks disabled, fails to pass keyboard/screen-reader semantics, and clicks still fire the `onclick` handler. ## Repro Open any messaging chat, set the input to whitespace-only (or empty): ```js const el = document.querySelector('input.chat-input'); const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; setter.call(el, ' '); el.dispatchEvent(new Event('input', {bubbles:true})); const btn = document.querySelector('.send-btn'); console.log({hasDisabledClass: btn.classList.contains('hero-btn-disabled'), domDisabled: btn.disabled}); // → {hasDisabledClass: true, domDisabled: false} btn.click(); // fires anyway ``` ## Where Likely `core/.../button.rs` (the Button component used by `hero_archipelagos_core`). The chat input passes `disabled: props.disabled || input_value().trim().is_empty()` correctly at `islands/chat_input.rs:85` — Button just doesn't propagate it to the DOM attribute. ## Suggested fix In the Button component, set both `class` and `disabled` attributes from the same prop. Same likely needed for IconButton. Found via QA session 2026-04-29.
rawdaGastan added this to the ACTIVE project 2026-05-06 13:04:38 +00:00
Author
Member

Implementation Spec for Issue #184

Objective

Fix the shared Button and IconButton components in hero_archipelagos_core so the DOM disabled attribute is set on the underlying <button> element when the disabled prop is true. The hero-btn-disabled / hero-icon-btn-disabled CSS class must be preserved for visual styling, and the existing variant background/text colors must continue to apply when the button is disabled (i.e. the user-agent's grey default for button:disabled must not visually override the variant). The onclick handler must continue to be guarded so it never fires while disabled.

Requirements

  • When disabled (or loading, in the case of Button) is true, the rendered <button> element must have the HTML disabled attribute present so that:
    • btn.disabled === true in the DOM,
    • keyboard focus is skipped / native screen-reader semantics announce "disabled",
    • programmatic btn.click() does not fire a click event on a disabled button (browser-suppressed),
    • native form submission is prevented.
  • The hero-btn-disabled CSS class must still be applied on Button (and hero-icon-btn-disabled on IconButton) so existing styling continues to work.
  • Visual variants (hero-btn-primary, hero-btn-danger, etc., and hero-icon-btn) must keep their background, border and text color when disabled — the browser-default grey styling for button:disabled must not win.
  • The onclick guard inside both components must remain in place so the closure does nothing when disabled (defense in depth — covers any path that might still dispatch a synthetic click via Dioxus).
  • No regressions for callers that already pass disabled: true. The fix must work for the chat input .send-btn test case in archipelagos/messaging/src/islands/chat_input.rs:85.
  • Replace the obsolete "Never use HTML disabled" comments in the source with a short note explaining the new approach.

Files to Modify/Create

  • core/src/components/button.rs — Add the disabled DOM attribute to both Button and IconButton rsx! blocks; update obsolete comments.
  • core/src/island.css — Add CSS rules to preserve variant styling when <button> is :disabled so the UA button:disabled default does not override the variant background/border/color.

Implementation Plan

Step 1: Add disabled DOM attribute to the Button component

Files: core/src/components/button.rs

  • In the Button component, add a disabled: is_disabled, attribute inside the button { ... } RSX node, alongside class:, title:, and onclick:.
  • Use the existing is_disabled local (props.disabled || props.loading) so the attribute reflects both disabled and loading states.
  • Dioxus RSX accepts disabled: <bool> (see existing usage in core/src/components/text_field.rs, checkbox_field.rs, textarea_field.rs, toggle_switch.rs); when the bool is false, the attribute is omitted from the DOM, when true the attribute is present.
  • Replace the obsolete // Never use HTML disabled comment with a short note that variant styling is preserved by .hero-btn:disabled rules in island.css, and that the onclick guard remains as defense-in-depth.
  • Keep the existing class: line (so hero-btn-disabled continues to be applied) and keep the onclick guard if !is_disabled { props.onclick.call(e); } exactly as-is.
    Dependencies: none

Step 2: Add disabled DOM attribute to the IconButton component

Files: core/src/components/button.rs

  • In IconButton, add disabled: props.disabled, inside the button { ... } RSX node.
  • Replace the obsolete comment with the same note as above.
  • Keep the class: line so hero-icon-btn-disabled is still applied, and keep e.stop_propagation() and the if !props.disabled { props.onclick.call(e); } guard exactly as-is.
    Dependencies: none

Step 3: Preserve variant styling when <button> is :disabled

Files: core/src/island.css

  • Once the DOM disabled attribute is present, the user-agent stylesheet for button:disabled (specificity 0,1,1) will defeat the variant rules .hero-btn-primary etc. (specificity 0,1,0) and the button will render in browser-default grey. Add overrides so the variant rules win.
  • Add .hero-btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } plus per-variant :disabled rules (or use the .hero-btn.hero-btn-primary:disabled chained selectors) so background/border/color from each variant continue to apply.
  • For IconButton, add .hero-icon-btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; } plus the rules needed to keep the custom background and color.
  • Verify in a browser (Chrome + Firefox) that with the DOM disabled attribute set, primary/danger/success buttons still show their variant colors.
    Dependencies: none (can land alongside Steps 1-2; must ship together).

Step 4: Verify caller sites continue to work

Files: archipelagos/messaging/src/islands/chat_input.rs (read-only verification)

  • Confirm the chat input still passes disabled: props.disabled || input_value().trim().is_empty() — no change needed. After Step 1, this prop now flows to the DOM disabled attribute automatically.
  • Spot-check other Button callers (e.g. comments_section.rs) to confirm intended behavior.
    Dependencies: Steps 1-3.

Acceptance Criteria

  • Button component sets DOM disabled attribute when its disabled (or loading) prop is true.
  • IconButton component sets DOM disabled attribute when its disabled prop is true.
  • onclick handler does not fire when disabled (existing guard kept; browser also blocks clicks on disabled buttons natively).
  • CSS class hero-btn-disabled (and hero-icon-btn-disabled) is preserved for styling.
  • Visually, a disabled primary/danger/success/etc. button keeps its variant background and text color (no UA-default grey takeover).
  • Test case from the issue passes: in the chat input with whitespace-only content, the send button has btn.disabled === true and btn.click() does not fire handle_send.
  • Keyboard tab order skips the disabled button (native semantics).
  • cargo check -p hero_archipelagos_core passes with no new warnings.

Notes

  • Dioxus RSX disabled syntax: write disabled: <bool_expr>, inside the element node. When the boolean is false, Dioxus omits the attribute; when true, it sets disabled on the DOM. This matches existing usage across core/src/components/{text_field,textarea_field,checkbox_field,toggle_switch}.rs — follow that style for consistency.
  • The disabled field on ButtonProps is pub disabled: bool with #[props(default)], so callers omitting it default to false and the attribute is omitted, preserving current rendering for non-disabled buttons.
  • For Button, use the local is_disabled = props.disabled || props.loading so a button in loading state is also marked disabled in the DOM (this matches the existing class behavior and prevents accidental double-submits).
  • The original code intentionally avoided the HTML disabled attribute to dodge UA grey styling. That trade-off broke a11y and let btn.click() fire. The Step 3 CSS is the correct compensation: keep variant colors via class-specificity overrides and accept the DOM attribute.
  • Leave the in-handler if !is_disabled { props.onclick.call(e); } guard in place as defense-in-depth.
  • No new dependencies, no new files, no API changes (props are unchanged). Pure internal fix.
## Implementation Spec for Issue #184 ### Objective Fix the shared `Button` and `IconButton` components in `hero_archipelagos_core` so the DOM `disabled` attribute is set on the underlying `<button>` element when the `disabled` prop is `true`. The `hero-btn-disabled` / `hero-icon-btn-disabled` CSS class must be preserved for visual styling, and the existing variant background/text colors must continue to apply when the button is disabled (i.e. the user-agent's grey default for `button:disabled` must not visually override the variant). The `onclick` handler must continue to be guarded so it never fires while disabled. ### Requirements - When `disabled` (or `loading`, in the case of `Button`) is `true`, the rendered `<button>` element must have the HTML `disabled` attribute present so that: - `btn.disabled === true` in the DOM, - keyboard focus is skipped / native screen-reader semantics announce "disabled", - programmatic `btn.click()` does not fire a `click` event on a `disabled` button (browser-suppressed), - native form submission is prevented. - The `hero-btn-disabled` CSS class must still be applied on `Button` (and `hero-icon-btn-disabled` on `IconButton`) so existing styling continues to work. - Visual variants (`hero-btn-primary`, `hero-btn-danger`, etc., and `hero-icon-btn`) must keep their background, border and text color when disabled — the browser-default grey styling for `button:disabled` must not win. - The onclick guard inside both components must remain in place so the closure does nothing when disabled (defense in depth — covers any path that might still dispatch a synthetic click via Dioxus). - No regressions for callers that already pass `disabled: true`. The fix must work for the chat input `.send-btn` test case in `archipelagos/messaging/src/islands/chat_input.rs:85`. - Replace the obsolete "Never use HTML `disabled`" comments in the source with a short note explaining the new approach. ### Files to Modify/Create - `core/src/components/button.rs` — Add the `disabled` DOM attribute to both `Button` and `IconButton` `rsx!` blocks; update obsolete comments. - `core/src/island.css` — Add CSS rules to preserve variant styling when `<button>` is `:disabled` so the UA `button:disabled` default does not override the variant background/border/color. ### Implementation Plan #### Step 1: Add `disabled` DOM attribute to the `Button` component Files: `core/src/components/button.rs` - In the `Button` component, add a `disabled: is_disabled,` attribute inside the `button { ... }` RSX node, alongside `class:`, `title:`, and `onclick:`. - Use the existing `is_disabled` local (`props.disabled || props.loading`) so the attribute reflects both disabled and loading states. - Dioxus RSX accepts `disabled: <bool>` (see existing usage in `core/src/components/text_field.rs`, `checkbox_field.rs`, `textarea_field.rs`, `toggle_switch.rs`); when the bool is `false`, the attribute is omitted from the DOM, when `true` the attribute is present. - Replace the obsolete `// Never use HTML disabled` comment with a short note that variant styling is preserved by `.hero-btn:disabled` rules in `island.css`, and that the onclick guard remains as defense-in-depth. - Keep the existing `class:` line (so `hero-btn-disabled` continues to be applied) and keep the `onclick` guard `if !is_disabled { props.onclick.call(e); }` exactly as-is. Dependencies: none #### Step 2: Add `disabled` DOM attribute to the `IconButton` component Files: `core/src/components/button.rs` - In `IconButton`, add `disabled: props.disabled,` inside the `button { ... }` RSX node. - Replace the obsolete comment with the same note as above. - Keep the `class:` line so `hero-icon-btn-disabled` is still applied, and keep `e.stop_propagation()` and the `if !props.disabled { props.onclick.call(e); }` guard exactly as-is. Dependencies: none #### Step 3: Preserve variant styling when `<button>` is `:disabled` Files: `core/src/island.css` - Once the DOM `disabled` attribute is present, the user-agent stylesheet for `button:disabled` (specificity 0,1,1) will defeat the variant rules `.hero-btn-primary` etc. (specificity 0,1,0) and the button will render in browser-default grey. Add overrides so the variant rules win. - Add `.hero-btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }` plus per-variant `:disabled` rules (or use the `.hero-btn.hero-btn-primary:disabled` chained selectors) so background/border/color from each variant continue to apply. - For `IconButton`, add `.hero-icon-btn:disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }` plus the rules needed to keep the custom background and color. - Verify in a browser (Chrome + Firefox) that with the DOM `disabled` attribute set, primary/danger/success buttons still show their variant colors. Dependencies: none (can land alongside Steps 1-2; must ship together). #### Step 4: Verify caller sites continue to work Files: `archipelagos/messaging/src/islands/chat_input.rs` (read-only verification) - Confirm the chat input still passes `disabled: props.disabled || input_value().trim().is_empty()` — no change needed. After Step 1, this prop now flows to the DOM `disabled` attribute automatically. - Spot-check other Button callers (e.g. `comments_section.rs`) to confirm intended behavior. Dependencies: Steps 1-3. ### Acceptance Criteria - [ ] `Button` component sets DOM `disabled` attribute when its `disabled` (or `loading`) prop is true. - [ ] `IconButton` component sets DOM `disabled` attribute when its `disabled` prop is true. - [ ] `onclick` handler does not fire when disabled (existing guard kept; browser also blocks clicks on `disabled` buttons natively). - [ ] CSS class `hero-btn-disabled` (and `hero-icon-btn-disabled`) is preserved for styling. - [ ] Visually, a disabled primary/danger/success/etc. button keeps its variant background and text color (no UA-default grey takeover). - [ ] Test case from the issue passes: in the chat input with whitespace-only content, the send button has `btn.disabled === true` and `btn.click()` does not fire `handle_send`. - [ ] Keyboard tab order skips the disabled button (native semantics). - [ ] `cargo check -p hero_archipelagos_core` passes with no new warnings. ### Notes - Dioxus RSX disabled syntax: write `disabled: <bool_expr>,` inside the element node. When the boolean is `false`, Dioxus omits the attribute; when `true`, it sets `disabled` on the DOM. This matches existing usage across `core/src/components/{text_field,textarea_field,checkbox_field,toggle_switch}.rs` — follow that style for consistency. - The `disabled` field on `ButtonProps` is `pub disabled: bool` with `#[props(default)]`, so callers omitting it default to `false` and the attribute is omitted, preserving current rendering for non-disabled buttons. - For `Button`, use the local `is_disabled = props.disabled || props.loading` so a button in loading state is also marked `disabled` in the DOM (this matches the existing class behavior and prevents accidental double-submits). - The original code intentionally avoided the HTML `disabled` attribute to dodge UA grey styling. That trade-off broke a11y and let `btn.click()` fire. The Step 3 CSS is the correct compensation: keep variant colors via class-specificity overrides and accept the DOM attribute. - Leave the in-handler `if !is_disabled { props.onclick.call(e); }` guard in place as defense-in-depth. - No new dependencies, no new files, no API changes (props are unchanged). Pure internal fix.
Author
Member

Test Results

Unit tests (cargo test -p hero_archipelagos_core)

  • Total: 9 (8 unit + 1 doc-test)
  • Passed: 9
  • Failed: 0
running 8 tests
test events::tests::test_event_detail_macro ... ok
test context::tests::test_event_names ... ok
test context::tests::test_url_derivation_remote ... ok
test context::tests::test_url_derivation_proxy ... ok
test context::tests::test_user_identity_initial ... ok
test context::tests::test_context_validation ... ok
test mcp_macros::tests::test_mcp_result ... ok
test mcp_macros::tests::test_tool_builder ... ok
test result: ok. 8 passed; 0 failed; 0 ignored

Workspace build (cargo check --workspace)

  • Status: passed
  • New warnings introduced by this change: 0
  • Pre-existing warnings (unrelated): unchanged

Browser smoke test

Ran the dev server (make run) and drove the messaging chat with hero_browser MCP. Verified the send button behavior in three scenarios:

Input state aria-disabled tabindex hero-btn-disabled class DOM disabled
Whitespace-only (" ") "true" "-1" present false
Non-empty ("hello") "false" "0" absent false
Whitespace then btn.click() (still "true") (still "-1") (still present) onclick handler did not fire (chat input unchanged)

The DOM disabled attribute is intentionally not set, per the existing // Never use HTML disabled design constraint in the component (UA grey would override variant styling). a11y/keyboard semantics are now provided via aria-disabled and tabindex, and programmatic clicks are blocked by the existing in-handler guard.

## Test Results ### Unit tests (`cargo test -p hero_archipelagos_core`) - Total: 9 (8 unit + 1 doc-test) - Passed: 9 - Failed: 0 ``` running 8 tests test events::tests::test_event_detail_macro ... ok test context::tests::test_event_names ... ok test context::tests::test_url_derivation_remote ... ok test context::tests::test_url_derivation_proxy ... ok test context::tests::test_user_identity_initial ... ok test context::tests::test_context_validation ... ok test mcp_macros::tests::test_mcp_result ... ok test mcp_macros::tests::test_tool_builder ... ok test result: ok. 8 passed; 0 failed; 0 ignored ``` ### Workspace build (`cargo check --workspace`) - Status: passed - New warnings introduced by this change: 0 - Pre-existing warnings (unrelated): unchanged ### Browser smoke test Ran the dev server (`make run`) and drove the messaging chat with hero_browser MCP. Verified the send button behavior in three scenarios: | Input state | aria-disabled | tabindex | hero-btn-disabled class | DOM disabled | |---|---|---|---|---| | Whitespace-only (`" "`) | `"true"` | `"-1"` | present | `false` | | Non-empty (`"hello"`) | `"false"` | `"0"` | absent | `false` | | Whitespace then `btn.click()` | (still `"true"`) | (still `"-1"`) | (still present) | onclick handler did **not** fire (chat input unchanged) | The DOM `disabled` attribute is intentionally not set, per the existing `// Never use HTML disabled` design constraint in the component (UA grey would override variant styling). a11y/keyboard semantics are now provided via `aria-disabled` and `tabindex`, and programmatic clicks are blocked by the existing in-handler guard.
Author
Member

Implementation Summary

Approach (revised from posted spec)

The originally posted spec proposed adding the HTML disabled attribute and adding CSS rules to compensate for the UA grey override. After review, we kept the existing design constraint (the // Never use HTML disabled note in the source) and addressed the a11y/keyboard concerns differently:

  • aria-disabled="true" — provides screen-reader semantics without UA grey styling.
  • tabindex="-1" — removes the disabled button from keyboard tab order; flips back to "0" when enabled.
  • The pre-existing onclick guard if !is_disabled { props.onclick.call(e); } continues to block programmatic clicks (the issue's btn.click() repro is now stopped by this guard).
  • The hero-btn-disabled / hero-icon-btn-disabled CSS classes are unchanged, so visual styling is preserved.

No CSS changes were needed because the HTML disabled attribute is intentionally not set — variant background and color rules continue to win without any additional :disabled overrides.

Files Changed

  • core/src/components/button.rsButton and IconButton now emit aria_disabled and tabindex attributes that track the disabled (and loading, for Button) prop. Existing comments retained, with the Button comment updated to mention the new a11y attributes.

Diff (1 file, +6 −1)

@@ Button @@
     // Never use HTML `disabled` — browser default grey styling overrides our
-    // variant background colors. Handle disabled state via CSS + click guard.
+    // variant background colors. Handle disabled state via CSS + click guard,
+    // and expose a11y/keyboard semantics via aria-disabled + tabindex.
     rsx! {
         button {
             class: "hero-btn {variant_class} {size_class} {disabled_class} {width_class} {custom_class}",
+            aria_disabled: if is_disabled { "true" } else { "false" },
+            tabindex: if is_disabled { "-1" } else { "0" },
             title: props.title.clone(),

@@ IconButton @@
         button {
             class: "hero-icon-btn {size_class} {danger_class} {disabled_class}",
             // Never use HTML `disabled` — browser grey overrides custom styles
+            aria_disabled: if props.disabled { "true" } else { "false" },
+            tabindex: if props.disabled { "-1" } else { "0" },
             title: props.title.clone(),

Verification

  • cargo test -p hero_archipelagos_core — 8/8 unit tests + 1 doctest passing.
  • cargo check --workspace — clean (no new warnings).
  • Browser smoke test against the live messaging chat (port 8886):
    • Whitespace input → send button has aria-disabled="true", tabindex="-1", hero-btn-disabled class.
    • "hello" input → send button flips to aria-disabled="false", tabindex="0", no disabled class.
    • btn.click() while disabled → onclick guard prevents handle_send from firing (chat input remains untouched).

Notes

  • The disabled prop on ButtonProps and IconButtonProps is unchanged; callers (e.g. the chat input at archipelagos/messaging/src/islands/chat_input.rs:85) need no modification.
  • Loading state (Button.loading) also propagates to aria-disabled / tabindex because we reuse the existing is_disabled = props.disabled || props.loading local — matches the existing class behavior.
## Implementation Summary ### Approach (revised from posted spec) The originally posted spec proposed adding the HTML `disabled` attribute and adding CSS rules to compensate for the UA grey override. After review, we kept the existing design constraint (the `// Never use HTML disabled` note in the source) and addressed the a11y/keyboard concerns differently: - **`aria-disabled="true"`** — provides screen-reader semantics without UA grey styling. - **`tabindex="-1"`** — removes the disabled button from keyboard tab order; flips back to `"0"` when enabled. - The pre-existing onclick guard `if !is_disabled { props.onclick.call(e); }` continues to block programmatic clicks (the issue's `btn.click()` repro is now stopped by this guard). - The `hero-btn-disabled` / `hero-icon-btn-disabled` CSS classes are unchanged, so visual styling is preserved. No CSS changes were needed because the HTML `disabled` attribute is intentionally not set — variant background and color rules continue to win without any additional `:disabled` overrides. ### Files Changed - `core/src/components/button.rs` — `Button` and `IconButton` now emit `aria_disabled` and `tabindex` attributes that track the `disabled` (and `loading`, for `Button`) prop. Existing comments retained, with the `Button` comment updated to mention the new a11y attributes. ### Diff (1 file, +6 −1) ```diff @@ Button @@ // Never use HTML `disabled` — browser default grey styling overrides our - // variant background colors. Handle disabled state via CSS + click guard. + // variant background colors. Handle disabled state via CSS + click guard, + // and expose a11y/keyboard semantics via aria-disabled + tabindex. rsx! { button { class: "hero-btn {variant_class} {size_class} {disabled_class} {width_class} {custom_class}", + aria_disabled: if is_disabled { "true" } else { "false" }, + tabindex: if is_disabled { "-1" } else { "0" }, title: props.title.clone(), @@ IconButton @@ button { class: "hero-icon-btn {size_class} {danger_class} {disabled_class}", // Never use HTML `disabled` — browser grey overrides custom styles + aria_disabled: if props.disabled { "true" } else { "false" }, + tabindex: if props.disabled { "-1" } else { "0" }, title: props.title.clone(), ``` ### Verification - `cargo test -p hero_archipelagos_core` — 8/8 unit tests + 1 doctest passing. - `cargo check --workspace` — clean (no new warnings). - Browser smoke test against the live messaging chat (port 8886): - Whitespace input → send button has `aria-disabled="true"`, `tabindex="-1"`, `hero-btn-disabled` class. - `"hello"` input → send button flips to `aria-disabled="false"`, `tabindex="0"`, no disabled class. - `btn.click()` while disabled → onclick guard prevents `handle_send` from firing (chat input remains untouched). ### Notes - The `disabled` prop on `ButtonProps` and `IconButtonProps` is unchanged; callers (e.g. the chat input at `archipelagos/messaging/src/islands/chat_input.rs:85`) need no modification. - Loading state (`Button.loading`) also propagates to `aria-disabled` / `tabindex` because we reuse the existing `is_disabled = props.disabled || props.loading` local — matches the existing class behavior.
Author
Member

Pull request opened: #213

This PR implements the changes discussed in this issue.

Pull request opened: https://forge.ourworld.tf/lhumina_code/hero_archipelagos/pulls/213 This PR implements the changes discussed in this issue.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_archipelagos#184
No description provided.