Skip to content

[feat]: Integration of layout-agnostic resizable partials#1128

Draft
banana-three-join wants to merge 1 commit into
layer5io:masterfrom
banana-three-join:feature/banana-three-join/resizable-element
Draft

[feat]: Integration of layout-agnostic resizable partials#1128
banana-three-join wants to merge 1 commit into
layer5io:masterfrom
banana-three-join:feature/banana-three-join/resizable-element

Conversation

@banana-three-join

@banana-three-join banana-three-join commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Showcase

https://www.loom.com/share/43e69c7fc24945e5bb353d4650dd28ee

Changes

Resizable component (new, layout-agnostic — distributed via this parent module)

Removed the previous resizable sidebar implementation and replaced it with a layout-agnostic component distributed through this module. In Hugo, a theme can be consumed in different contexts, and the same resizable markup needs to behave correctly whether the host arranges its layout with Bootstrap's grid, CSS grid, or plain flex. To support that, this module serves as the parent module that downstream themes import the resizable items from: the component governs its own width through a CSS custom property (--resizable-w) and makes no assumptions about the host's layout system, so importing themes get resize behavior without inheriting any layout coupling. Host-specific concerns (e.g. Bootstrap's .row interactions) live in a separate override file that stays with the consuming theme, keeping the distributed component portable. The component also offers local persistence — widths are stored per-origin in localStorage and restored before paint — so a reader's chosen sizing carries across pages and sessions in whichever theme imports it.

Responsive behavior across widths

Verified and corrected the layout across its full range of viewport widths: at desktop the sidebar and TOC are resizable, sticky, and in-row; below the mobile breakpoint the row stacks (sidebar full-width on top, main below), resize is disabled, and the handle is hidden. The desktop width is preserved through localStorage and restored when returning from a mobile width — the stored value is never overwritten by the mobile layout. Also removed a redundant sticky offset that was creating a gap between the header and the content.

Shortcode for structured content

Integrating the resizable partials into baseof.html surfaced a latent content problem: a page's _index.md contained hand-written raw HTML with an unclosed tag, which caused the parser to swallow surrounding closing tags and reparent elements — breaking the surrounding layout. The restructure itself was minor; its value here is that it exposed why certain content needs to be handled differently from ordinary Markdown. Rather than patch the single page, this PR introduces a shortcode that emits balanced HTML by construction. Content authors pass parameters and text instead of hand-writing <div> structures, so a missing closing tag can no longer cascade into a layout break. This addresses the entire class of issue and is the durable fix. Converting the affected pages to the shortcode also makes clear which content blocks are structural (and therefore belong in a template that guarantees balance) versus ordinary prose — which leads directly to the styling cleanup below.

CSS consolidation

With those structural blocks now owned by the shortcode, their styling no longer belongs in the content either. The affected pages had previously inlined identical callout-box styles as <style> blocks inside two separate _index.md files. As part of moving the structure into the shortcode, those styles are consolidated into shared SCSS — defined once, applied everywhere the shortcode renders. This removes the duplication, eliminates a per-page <style> block that was itself a parse-risk point, and completes the separation of concerns: structure lives in the shortcode template, styling lives in shared SCSS, and the Markdown content goes back to being just content.

Notes for Reviewers

  • A follow-up PR will be opened up from Meshery as the inclusion of this feature is vital for the Meshery Docs to integrate.
  • Final clean-up needed and assurance of the correct behavior within Meshery Docs is needed for this PR to be considered successful.

Signed commits

  • Yes, I signed my commits.

…of previous resizable and fix of minor bugs

Signed-off-by: Lenox Wiltshire <lenoxwiltshire@gmail.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the sidebar and table of contents (TOC) resizing functionality into a reusable, layout-agnostic resizable component, replacing the old grid-based resizer logic across various layouts and introducing a new ecosystem-box shortcode to clean up inline styles in content files. The review feedback suggests improving the robustness of the resizable JavaScript initialization by avoiding hardcoded fallback values, wrapping localStorage access in try-catch blocks, optimizing early returns, and handling pointercancel events. Additionally, it is recommended to consolidate CSS by removing inline styles from the ecosystem-box shortcode and defining proper classes in the SCSS files.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread assets/js/resizable.js
Comment on lines +6 to +52
function init(el) {
if (el.__resizableReady) return;
el.__resizableReady = true;

var side = el.dataset.resizableSide || "right";
var key = "resizable:" + el.dataset.resizableKey;
var min = parseFloat(el.dataset.resizableMin) || 0;
var max = parseFloat(el.dataset.resizableMax) || Infinity;
var def = parseFloat(el.dataset.resizableDefault) || min || 280;
var handle = el.querySelector(":scope > .resizable__handle");
if (!handle || !el.dataset.resizableKey) return;

function setWidth(px) {
el.style.setProperty("--resizable-w", clamp(px, min, max) + "px");
}
function save() {
localStorage.setItem(key, parseFloat(getComputedStyle(el).width));
}

var saved = parseFloat(localStorage.getItem(key));
setWidth(isNaN(saved) ? def : saved);

var startX = 0, startW = 0;

function onMove(e) {
var delta = side === "right" ? e.clientX - startX : startX - e.clientX;
setWidth(startW + delta);
}
function onUp(e) {
handle.releasePointerCapture(e.pointerId);
window.removeEventListener("pointermove", onMove);
window.removeEventListener("pointerup", onUp);
el.classList.remove("is-resizing");
document.body.classList.remove("is-resizing");
save();
}

handle.addEventListener("pointerdown", function (e) {
e.preventDefault();
startX = e.clientX;
startW = el.getBoundingClientRect().width;
handle.setPointerCapture(e.pointerId);
el.classList.add("is-resizing");
document.body.classList.add("is-resizing");
window.addEventListener("pointermove", onMove);
window.addEventListener("pointerup", onUp);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Refactor Resizable Initialization for Robustness and Adherence to Style Rules

This refactoring addresses several critical areas of improvement:

  1. Avoid Hardcoded Fallbacks: Resolves CSS custom properties (--resizable-min, --resizable-max, --resizable-default) directly from getComputedStyle(el) instead of hardcoding fallback values in JavaScript, adhering to the general rules.
  2. Safe localStorage Access: Wraps all localStorage reads and writes in try-catch blocks to prevent script crashes in environments where local storage is blocked or unavailable (e.g., private browsing, iframe previews).
  3. Early Return Optimization: Validates el.dataset.resizableKey and handle before setting el.__resizableReady = true to avoid marking invalid elements as ready.
  4. Robust Gesture Handling: Adds a listener for pointercancel to ensure event listeners are cleaned up and resizing state is reset if the gesture is interrupted.
  function init(el) {
    var key = el.dataset.resizableKey;
    var handle = el.querySelector(":scope > .resizable__handle");
    if (!handle || !key) return;

    if (el.__resizableReady) return;
    el.__resizableReady = true;

    var side = el.dataset.resizableSide || "right";
    var storageKey = "resizable:" + key;

    var style = getComputedStyle(el);
    var min = parseFloat(style.getPropertyValue("--resizable-min"));
    var max = parseFloat(style.getPropertyValue("--resizable-max"));
    var def = parseFloat(style.getPropertyValue("--resizable-default"));
    if (isNaN(min) || isNaN(max) || isNaN(def)) return;

    function setWidth(px) {
      el.style.setProperty("--resizable-w", clamp(px, min, max) + "px");
    }
    function save() {
      try {
        localStorage.setItem(storageKey, parseFloat(getComputedStyle(el).width));
      } catch (e) {
        console.warn("localStorage is not available:", e);
      }
    }

    var saved;
    try {
      saved = parseFloat(localStorage.getItem(storageKey));
    } catch (e) {
      saved = NaN;
    }
    setWidth(isNaN(saved) ? def : saved);

    var startX = 0, startW = 0;

    function onMove(e) {
      var delta = side === "right" ? e.clientX - startX : startX - e.clientX;
      setWidth(startW + delta);
    }
    function onUp(e) {
      handle.releasePointerCapture(e.pointerId);
      window.removeEventListener("pointermove", onMove);
      window.removeEventListener("pointerup", onUp);
      window.removeEventListener("pointercancel", onUp);
      el.classList.remove("is-resizing");
      document.body.classList.remove("is-resizing");
      save();
    }

    handle.addEventListener("pointerdown", function (e) {
      e.preventDefault();
      startX = e.clientX;
      startW = el.getBoundingClientRect().width;
      handle.setPointerCapture(e.pointerId);
      el.classList.add("is-resizing");
      document.body.classList.add("is-resizing");
      window.addEventListener("pointermove", onMove);
      window.addEventListener("pointerup", onUp);
      window.addEventListener("pointercancel", onUp);
    });
References
  1. Avoid hardcoding fallback values in JavaScript for CSS custom properties (variables), as this creates multiple sources of truth for UI styling. If the script cannot resolve the CSS variables, it is better to halt execution or handle the error rather than falling back to hardcoded defaults.

Comment on lines +22 to +37
<a href="{{ ref . $link }}" class="{{ $class }}" style="display:flex; gap:1.5rem; text-decoration:none; color:inherit;">
<span style="min-width:50px; align-self:center; line-height:1.85rem;">
{{- if $isImage -}}
<img src="{{ $icon | relURL }}" alt="{{ $title }} logo" style="height:65px; width:65px; border:0; background:transparent;">
{{- else -}}
{{- with resources.Get (printf "icons/%s.svg" $icon) -}}
{{- .Content | safeHTML -}}
{{- else -}}
<!-- SVG not found: {{ $icon }} -->
{{- end -}}
{{- end -}}
</span>
<span style="margin:auto; color:#ccc; line-height:1.85rem;">
<strong>{{ $title }}</strong> {{ .Inner | .Page.RenderString }}
</span>
</a> No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consolidate CSS and Remove Inline Styles

To complete the CSS consolidation and separate structure from styling, remove the inline styles from the shortcode template and use the .ecosystem-box, .ecosystem-box__icon, and .ecosystem-box__text classes. This utilizes the classes already partially defined in the SCSS and keeps the HTML clean and maintainable.

<a href="{{ ref . $link }}" class="{{ $class }} ecosystem-box">
  <span class="ecosystem-box__icon">
    {{- if $isImage -}}
      <img src="{{ $icon | relURL }}" alt="{{ $title }} logo">
    {{- else -}}
      {{- with resources.Get (printf "icons/%s.svg" $icon) -}}
        {{- .Content | safeHTML -}}
      {{- else -}}
        <!-- SVG not found: {{ $icon }} -->
      {{- end -}}
    {{- end -}}
  </span>
  <span class="ecosystem-box__text">
    <strong>{{ $title }}</strong> {{ .Inner | .Page.RenderString }}
  </span>
</a>

border: 0;
background: transparent;
}
.hidden-highlight-box .ecosystem-box__text { padding-left: 1rem; } No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add Ecosystem Box Layout and Typography Styles

Consolidate the styles removed from the shortcode by adding the .ecosystem-box classes to the SCSS file. This keeps the styling defined once and applied everywhere the shortcode renders.

.hidden-highlight-box .ecosystem-box__text { padding-left: 1rem; }

.ecosystem-box {
  display: flex;
  gap: 1.5rem;
  text-decoration: none;
  color: inherit;

  &__icon {
    min-width: 50px;
    align-self: center;
    line-height: 1.85rem;
  }

  &__text {
    margin: auto;
    color: #ccc;
    line-height: 1.85rem;
  }
}

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployment: https://docs.layer5.io/pr-preview/pr-1128/

Note: Preview may take a moment (GitHub Pages deployment in progress). Please wait and refresh. Track deployment here

@banana-three-join banana-three-join self-assigned this Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant