Skip to content

feat: port accordion + tabs block families - #568

Merged
ViewFromTheBox merged 2 commits into
feature/495-port-crosswinds-blocksfrom
feature/497-cw1-accordion-tabs
Jun 11, 2026
Merged

ViewFromTheBox merged 2 commits into
feature/495-port-crosswinds-blocksfrom
feature/497-cw1-accordion-tabs

Conversation

@ViewFromTheBox

@ViewFromTheBox ViewFromTheBox commented Jun 11, 2026 •

Copy link
Copy Markdown
Member

Description

Adds six new artisanpack/* blocks covering two interactive families — accordion + tabs — with three-renderer parity (Blade + React + Vue), front-end CSS, and a vanilla interactivity script.

Closes: #497

Type of Change

  • New feature (adds new functionality)
  • Enhancement (improves existing functionality)

Related Issue

Issue: #497

Motivation and Context

Phase CW1 of the broader port to first-party ArtisanPack UI blocks (umbrella #495). These two families exercise the parent / child / grandchild inner-block pattern (accordions → accordion → accordion-title + accordion-body) and the parent / child pattern (tabs → tab-section), plus JS-driven interactivity. Both families ship as first-party blocks under artisanpack/* — the upstream attribution lives in the umbrella issue, not the block code itself.

Changes Made

Accordion family

  • accordions parent owns the wrapper; accordion child owns the panelId + panelIcon, auto-generated from the clientId on mount.
  • accordion-title and accordion-body are content-only — the accordion renderer walks its children and re-wraps them with matching id / aria-controls / aria-labelledby / hidden wiring, so authors only ever set the panel id once.

Tabs family

  • tab-section owns its own label + tabId (both auto-fill on mount: label as Tab N, tabId as tab-<clientId-suffix>).
  • tabs renderer walks innerBlocks to build the tablist AND re-wraps each section's grandchildren with the resolved tabId, so trigger aria-controls always matches panel id. Includes positional fallback for missing tabIds and dedup for duplicate slugs.

Renderer infrastructure

  • BlockRenderer.php now passes the raw innerBlocks array to Blade partials alongside the pre-rendered innerBlocksHtml so partials can walk children when they need to derive parent state.

Front-end + editor canvas

  • Per-family CSS + a vanilla interactivity script ship from visual-editor-renderer-blade under public/vendor/.../frontend/, wired into the new <x-ve-blocks-styles /> emission.
  • The interactivity script handles accordion toggle (click + Enter/Space) and tab switching (click + arrow keys + Home/End). Respects server-rendered initial aria-selected. Re-binds on window.dispatchEvent(new Event('ap:rebind')) for SPA navigation.
  • Editor canvas iframe picks up the styles via canvas-styles with editor-only overrides that keep every panel / section expanded for editing.

How Has This Been Tested?

Testing Environment:

  • Operating System: macOS Darwin 25.5.0
  • Browser: Chrome (latest)
  • PHP Version: 8.4.3
  • Project Version: feature/495-port-crosswinds-blocks HEAD

Tests Performed:

  1. Inserted Accordions block in editor — panel id auto-generated, title shows toggle icon, body editable
  2. Switched accordion icon (plus/minus → arrows) — title icon updates instantly via useSelect
  3. Inserted Tabs block — tab labels auto-fill as "Tab 1", "Tab 2"; "+ Tab" appends new section
  4. Edited tab section label / id via Inspector — tablist updates
  5. Rendered both blocks on the public preview page — interactivity works (toggle, tab switch, keyboard nav)

Accessibility Tests Run

  • Keyboard navigation tested
  • ARIA labels checked

Details: Accordion title is role="button" with aria-expanded toggling on Enter / Space; body is role="region" with aria-labelledby pointing at the title. Tabs use role="tablist" / role="tab" / role="tabpanel" with full keyboard support (Tab, arrow keys, Home, End). Color contrast and screen-reader testing left for a dedicated accessibility audit pass.

Tests Added

  • Unit tests added/updated
  • All tests passing

Test details: 6 new Blade tests, 7 new React tests, 7 new Vue tests covering happy-path rendering, safe-default fallbacks for invalid enums, empty-id edge cases, positional auto-fill, and tabId dedup. All 1031 Pest tests + 1915 vitest tests + renderer-parity check green.

Documentation

  • Inline code documentation added

Documentation details: Each new block, renderer, and helper carries a file-level docblock explaining intent + parent/child relationship. Editor-side comments call out non-obvious patterns (e.g., why the accordion renderer reaches into pre-rendered children to re-wrap them).

Screenshots

Editor + front-end screenshots captured during local manual testing; see the issue thread for a record.

Pre-Submission Checklist

  • Followed contributing guidelines
  • Code passes all tests
  • Self-review completed
  • Comments added for complex code
  • No new warnings generated

Summary by CodeRabbit

  • New Features
    • Added Accordion and Tabs block families with accessible toggle/tab behaviors, keyboard navigation, customizable icons, alignment/spacing options, and editor UI for composing sections.
  • Style
    • New styles for accordion and tabs for both editor preview and frontend, plus conditional asset loading when interactive features are enabled.
  • Tests
    • Added unit and renderer tests covering rendering, ARIA wiring, sanitization, and ID deduplication.

Adds the artisanpack/accordions, accordion, accordion-title,
accordion-body, tabs, and tab-section blocks plus three-renderer
parity (Blade + React + Vue) and renderer-parity tests.

Accordion family:
- accordions parent owns the wrapper; accordion child owns the
  panelId + panelIcon, auto-generated from clientId on mount.
- accordion-title / accordion-body are content-only — the accordion
  renderer walks its children and re-wraps them with the matching
  id / aria-controls / aria-labelledby / hidden wiring, so authors
  only ever set the panel id once.

Tabs family:
- tab-section owns its own label + tabId (both auto-fill on mount).
- tabs renderer walks innerBlocks to build the tablist AND re-wraps
  each section's grandchildren with the resolved tabId, so trigger
  aria-controls always lines up with panel id. Adds positional
  fallback for missing tabIds and dedup for duplicates.

Front-end:
- Per-family CSS + a vanilla interactivity script ship from
  visual-editor-renderer-blade under public/vendor/.../frontend/,
  wired into the new <x-ve-blocks-styles /> emission. The
  interactivity script handles accordion toggle (click + Enter /
  Space) and tab switching (click + arrow keys + Home / End).
- Editor canvas iframe also picks up the styles via canvas-styles
  with editor-only overrides that keep every panel / section
  expanded for editing.

Renderer infrastructure:
- BlockRenderer.php now passes the raw innerBlocks array to Blade
  partials (alongside the pre-rendered innerBlocksHtml) so partials
  can walk children when they need to derive parent state from them.

Closes #497
@coderabbitai

coderabbitai Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2e39502a-836f-45ec-b607-8988f5fdb1ad

📥 Commits

Reviewing files that changed from the base of the PR and between 7cc0033 and 0e1d9fa.

📒 Files selected for processing (5)
  • packages/visual-editor-renderer-blade/resources/assets/frontend/accordion.css
  • packages/visual-editor-renderer-blade/resources/assets/frontend/tabs.css
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tab-section.blade.php
  • resources/js/visual-editor/blocks/accordion/accordion.css
  • resources/js/visual-editor/blocks/tabs/tabs.css
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tab-section.blade.php
  • packages/visual-editor-renderer-blade/resources/assets/frontend/accordion.css
  • resources/js/visual-editor/blocks/accordion/accordion.css
  • resources/js/visual-editor/blocks/tabs/tabs.css
  • packages/visual-editor-renderer-blade/resources/assets/frontend/tabs.css

📝 Walkthrough

Walkthrough

This PR adds artisanpack accordion and tabs block families across editor manifests and entrypoints, editor authoring components (auto-fill/slugging and inspector controls), Blade/React/Vue renderers with ID sanitization and deduplication, frontend accordion/tabs CSS and an interactivity JS runtime, registration/parity updates, and tests covering rendering and ARIA/id behavior.

Changes

Accordion & Tabs Block Families

Layer / File(s) Summary
Parity + canvas wiring
packages/renderer-parity.json, resources/js/visual-editor/editor/canvas-styles.ts, resources/js/visual-editor/editor/__tests__/canvas-styles.test.ts
Adds artisanpack block names to parity manifest and injects accordion/tabs CSS and editor-preview overrides into the canvas iframe bundle.
Editor block contracts & entrypoints
resources/js/visual-editor/blocks/*/block.json, */index.ts, */save.tsx, */*.css
Adds block.json manifests and entry modules for accordions, accordion, accordion-title, accordion-body, tabs, and tab-section, and includes editor/front CSS and save stubs.
Editor authoring flows
resources/js/visual-editor/blocks/*/edit.tsx
Editor-side components: inner-block templates/constraints, inspector controls (panelId/icon, tabsAlign/tabsSpacing), auto-fill/slugging for ids/labels, tabs active state and add-tab insertion.
Blade templates, renderer plumbing & assets
packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/*, src/BlockRenderer.php, src/View/Components/BlocksStylesComponent.php, resources/views/components/blocks-styles.blade.php, VisualEditorRendererBladeServiceProvider.php
Blade partials render accordion/tabs with ARIA/id wiring and normalized innerBlocks; BlockRenderer now supplies normalized innerBlocks; BlocksStylesComponent conditionally emits accordion/tabs CSS and interactivity JS and the provider publishes frontend assets.
Frontend interactivity & CSS (Blade/frontend assets)
packages/visual-editor-renderer-blade/resources/assets/frontend/interactivity.js, accordion.css, tabs.css, plus resources/js/visual-editor/blocks/*/*css
Adds vanilla JS for idempotent accordion toggles and accessible tab roving/keyboard navigation; adds accordion and tabs styles for editor and public rendering.
React renderer components & registry
packages/visual-editor-renderer-react/src/blocks/artisanpack/*.tsx, registerCoreBlocks.ts, tests/BlockTree.test.tsx
Adds React renderers for accordion/tabs families with icon/align/spacing normalization, tab-id sanitization and uniqueness, and tests validating nested rendering, ID deduplication, sanitization, and ARIA wiring.
Vue renderer components & registry
packages/visual-editor-renderer-vue/src/blocks/artisanpack/*, registerCoreBlocks.ts, tests/BlockTree.test.ts
Adds Vue renderers that extract slot content from VNodes, normalize attributes, ensure unique sanitized tab IDs, render tab triggers/panels and accordion panels, and include parity tests.
Renderer/unit tests (Blade/React/Vue)
packages/visual-editor-renderer-blade/tests/Unit/BlockRendererTest.php, packages/visual-editor-renderer-react/tests/BlockTree.test.tsx, packages/visual-editor-renderer-vue/tests/BlockTree.test.ts
Adds tests asserting nested title/body/section rendering, panel/icon/ARIA/id wiring, safe sanitization, deduplication of generated IDs, label/id auto-fill by position, and omission of wiring when ids are empty.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Poem

🐇 In code I hop and gently tap,

Accordions fold, tabs clap—snap!
Three renderers hum the tune,
Styles and keys make UI swoon,
A tiny rabbit gives a clap!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: port accordion + tabs block families' directly and clearly summarizes the main change: porting two interactive block families (accordion and tabs) into the codebase.
Linked Issues check ✅ Passed All six blocks (accordions, accordion, accordion-title, accordion-body, tabs, tab-section) are implemented with three-renderer parity (Blade, React, Vue), parent/child relationships preserved, interactivity wired, and comprehensive tests added across all renderers.
Out of Scope Changes check ✅ Passed All changes are directly related to the accordion and tabs block families. Modifications to shared infrastructure (BlockRenderer, BlocksStylesComponent, canvas-styles) are necessary to support the new blocks and do not introduce unrelated functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/497-cw1-accordion-tabs

Comment @coderabbitai help to get the list of available commands and usage tips.

@ViewFromTheBox ViewFromTheBox moved this from Backlog to In Review in ArtisanPack UI Overview Jun 11, 2026
@ViewFromTheBox ViewFromTheBox linked an issue Jun 11, 2026 that may be closed by this pull request
5 tasks
@ViewFromTheBox
ViewFromTheBox marked this pull request as ready for review June 11, 2026 20:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/visual-editor-renderer-blade/src/BlockRenderer.php (1)

200-217: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider passing $innerBlocks through the dynamic block fallback path.

When a dynamic block lookup fails (line 205) or throws an exception (line 216), the fallback to renderStatic doesn't receive the structured $innerBlocks array because it's out of scope. Partials that depend on $innerBlocks (like the new accordion.blade.php) will receive an empty array in these fallback scenarios, breaking features that iterate the structured block tree.

To fix, pass $innerBlocks to renderDynamic and forward it to the fallback calls:

-protected function renderDynamic( string $name, array $attributes, string $innerBlocksHtml ): string
+protected function renderDynamic( string $name, array $attributes, string $innerBlocksHtml, array $innerBlocks = [] ): string
 {
 	$block = $this->dynamicBlocks->get( $name );

 	if ( null === $block ) {
-		return $this->renderStatic( $name, $attributes, $innerBlocksHtml );
+		return $this->renderStatic( $name, $attributes, $innerBlocksHtml, $innerBlocks );
 	}

 	try {
 		$validated = $block->validateAttrs( $attributes );
 		$result    = $block->render( $validated );

 		return $this->coerceToString( $result );
 	} catch ( Throwable $e ) {
 		report( $e );

-		return $this->renderStatic( $name, $attributes, $innerBlocksHtml );
+		return $this->renderStatic( $name, $attributes, $innerBlocksHtml, $innerBlocks );
 	}
 }

And update the call site at line 113:

-return $this->renderDynamic( $name, $attributes, $innerBlocksHtml );
+return $this->renderDynamic( $name, $attributes, $innerBlocksHtml, $innerBlocks );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/visual-editor-renderer-blade/src/BlockRenderer.php` around lines 200
- 217, The renderDynamic method currently lacks the structured $innerBlocks
parameter, so when a dynamic block is missing or throws (in renderDynamic) the
fallback renderStatic receives no $innerBlocks; update the renderDynamic
signature to accept array $innerBlocks, pass that $innerBlocks through to both
renderStatic fallback calls, and update the place that invokes renderDynamic
(the call site that previously passed name, attributes, innerBlocksHtml) to
supply the structured $innerBlocks argument in addition to existing args so
partials like accordion.blade.php receive the correct structured block tree.
🧹 Nitpick comments (8)
packages/visual-editor-renderer-vue/src/blocks/artisanpack/accordion.ts (1)

116-116: 💤 Low value

Consider using boolean true for the hidden attribute to align with Vue 3 best practices and React parity.

The Vue renderer uses hidden: '' while the React renderer uses hidden: true (line 98 in accordion.tsx). Both render correctly in their respective frameworks, but Vue 3.3+ recommends using actual boolean values for boolean attributes rather than empty strings (which is legacy Vue 2 style). For cross-renderer consistency and clarity, consider changing to hidden: true.

♻️ Suggested alignment with React and Vue 3 best practices
             const bodyAttrs: Record<string, unknown> = {
                 class: 'ap-accordion__body',
                 role: 'region',
-                hidden: '',
+                hidden: true,
             };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/visual-editor-renderer-vue/src/blocks/artisanpack/accordion.ts` at
line 116, The accordion block sets hidden: '' using legacy string syntax; update
the property to hidden: true to use a boolean (aligning with Vue 3+ and the
React renderer) by locating the accordion block definition in
packages/visual-editor-renderer-vue/src/blocks/artisanpack/accordion.ts and
replacing the hidden property value from an empty string to the boolean true.
resources/js/visual-editor/blocks/accordion/edit.tsx (1)

46-51: ⚡ Quick win

Consider extracting the slugify function to a shared utility.

The slugify function here is duplicated in tab-section/edit.tsx (lines 33-38). Extracting it to a shared utility module would eliminate duplication and ensure consistent ID normalization across both block families.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/js/visual-editor/blocks/accordion/edit.tsx` around lines 46 - 51,
The local slugify implementation is duplicated; extract the slugify function
into a shared utility module (e.g., export function slugify in a common utils
file) and replace the local slugify implementations in both edit.tsx files to
import and use that shared slugify; ensure you export the function with the same
signature (slugify(value: string): string), update imports in the components
that used the old local function, and run/type-check to confirm no breaking type
issues.
resources/js/visual-editor/blocks/tab-section/index.ts (1)

12-12: 💤 Low value

Shared CSS import pattern noted.

Both tabs/index.ts and tab-section/index.ts import tabs.css. This is intentional co-location since the stylesheet defines styles for both the parent (.ap-tabs) and child (.ap-tab-section) blocks. The bundler (Vite) should deduplicate this import automatically.

This is working as designed - no action required unless the team prefers to centralize CSS imports only at the parent block level.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/js/visual-editor/blocks/tab-section/index.ts` at line 12, The
import of '../tabs/tabs.css' in tab-section/index.ts duplicates the same import
present in tabs/index.ts; since Vite will deduplicate CSS this is acceptable, so
no change is required, but if you prefer centralizing imports remove the import
from tab-section/index.ts and ensure tabs/index.ts remains the single importer
of 'tabs.css' so styles for .ap-tabs and .ap-tab-section are still bundled by
Vite.
resources/js/visual-editor/blocks/tabs/tabs.css (1)

83-83: ⚡ Quick win

Fix currentColor casing to satisfy stylelint rule.

The stylelint value-keyword-case rule requires lowercase keywords. While currentColor is valid CSS, the project's linting configuration expects currentcolor.

🎨 Proposed fix
 .ap-tabs__list a[aria-selected="true"],
 .ap-tabs__list button[aria-selected="true"] {
     font-weight: 600;
-    border-bottom: 2px solid currentColor;
+    border-bottom: 2px solid currentcolor;
 }

 .ap-tabs.align-tabs-vertical .ap-tabs__list a[aria-selected="true"],
 .ap-tabs.align-tabs-vertical .ap-tabs__list button[aria-selected="true"] {
     border-bottom: 0;
-    border-right: 2px solid currentColor;
+    border-right: 2px solid currentcolor;
 }

Also applies to: 89-89

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/js/visual-editor/blocks/tabs/tabs.css` at line 83, The CSS uses the
keyword "currentColor" with incorrect casing for our stylelint rule; update the
occurrences in tabs.css (e.g., the border-bottom declaration in the tabs block
and the other occurrence noted around line 89) to use lowercase "currentcolor"
so the value-keyword-case lint rule passes, ensuring you change every instance
of currentColor to currentcolor (search for "currentColor" to catch all spots).

Source: Linters/SAST tools

packages/visual-editor-renderer-blade/resources/assets/frontend/tabs.css (1)

74-74: ⚡ Quick win

Use lowercase currentcolor for consistency.

Stylelint reports that currentColor should be currentcolor (lines 74, 79). While CSS color keywords are case-insensitive, lowercase is the conventional style.

♻️ Apply the linter's suggested casing
 .ap-tabs__list a[aria-selected="true"] {
     font-weight: 600;
-    border-bottom: 2px solid currentColor;
+    border-bottom: 2px solid currentcolor;
 }

 .ap-tabs.align-tabs-vertical .ap-tabs__list a[aria-selected="true"] {
     border-bottom: 0;
-    border-right: 2px solid currentColor;
+    border-right: 2px solid currentcolor;
 }

Also applies to: 79-79

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/visual-editor-renderer-blade/resources/assets/frontend/tabs.css` at
line 74, Replace the CSS color keyword casing for the two occurrences of
"currentColor" used in the border rules (the lines with "border-bottom: 2px
solid currentColor;") to the lowercase form "currentcolor" for consistency with
the project's style/linter; update both occurrences (the one at the shown diff
and the other reported) so the rule reads "border-bottom: 2px solid
currentcolor;".
packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tab-section.blade.php (1)

4-7: ⚡ Quick win

Sanitize tabId for HTML id validity (consistency with parent).

Lines 4–7 cast tabId to string and apply htmlspecialchars for attribute safety, but do not sanitize for HTML id validity (e.g., removing spaces, non-alphanumeric characters). The parent tabs.blade.php (lines 27–32) uses a sanitizeTabId helper that lowercases, replaces non-alphanumeric with hyphens, and trims. This inconsistency means a tab-section rendered outside a tabs parent could emit an invalid id if tabId contains spaces or special characters.

♻️ Proposed fix to align sanitization with parent

Extract a shared sanitization helper or inline the same logic here:

 `@php`
 	use ArtisanPackUI\VisualEditorRendererBlade\Support\BlockSupports;
 
-	$tabId = isset( $attributes['tabId'] ) ? (string) $attributes['tabId'] : '';
-	// Escape so quotes / brackets in the persisted slug can't break the
-	// surrounding attribute.
-	$tabIdSafe = htmlspecialchars( $tabId, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
+	$rawTabId = isset( $attributes['tabId'] ) ? (string) $attributes['tabId'] : '';
+	
+	// Sanitize tabId for HTML id validity (mirror tabs.blade.php logic).
+	$slug = strtolower( $rawTabId );
+	$slug = preg_replace( '/[^a-z0-9-]+/', '-', $slug ) ?? '';
+	$slug = trim( $slug, '-' );
+	$tabId = '' === $slug ? '' : $slug;
+	
+	$tabIdSafe = htmlspecialchars( $tabId, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
 
 	$idAttr        = '' === $tabId ? '' : ' id="tabs-panel-' . $tabIdSafe . '"';
 	$labelledByAttr = '' === $tabId ? '' : ' aria-labelledby="tabs-tab-' . $tabIdSafe . '"';
 `@endphp`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tab-section.blade.php`
around lines 4 - 7, The tab-section currently casts $tabId to string and only
calls htmlspecialchars into $tabIdSafe, but needs the same ID normalization as
the parent: apply the sanitizeTabId logic (lowercase, replace non-alphanumeric
chars with hyphens, collapse/trim hyphens) before HTML-escaping so the produced
id is valid; either call the existing sanitizeTabId helper used by
tabs.blade.php or inline that normalization for $tabId then pass the result
through htmlspecialchars to set $tabIdSafe.
packages/visual-editor-renderer-blade/resources/assets/frontend/accordion.css (2)

40-40: ⚡ Quick win

Use lowercase currentcolor per CSS spec.

Line 40 uses currentColor, but CSS keyword values should be lowercase (currentcolor) per the CSS specification and common linting rules.

🎨 Proposed fix
 .ap-accordion__title-content:focus-visible {
-    outline: 2px solid currentColor;
+    outline: 2px solid currentcolor;
     outline-offset: -2px;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/visual-editor-renderer-blade/resources/assets/frontend/accordion.css`
at line 40, The CSS declaration "outline: 2px solid currentColor;" uses the
wrong casing for the keyword; update the property value to use the lowercase CSS
keyword "currentcolor" (i.e., replace currentColor with currentcolor) wherever
that declaration appears (e.g., in the selector containing the outline rule) to
comply with the CSS spec and linters.

Source: Linters/SAST tools


58-58: ⚡ Quick win

Use lowercase currentcolor per CSS spec.

Line 58 uses currentColor, but CSS keyword values should be lowercase (currentcolor) per the CSS specification and common linting rules.

🎨 Proposed fix
 .ap-accordion__icon::before,
 .ap-accordion__icon::after {
     content: "";
     position: absolute;
-    background: currentColor;
+    background: currentcolor;
     transition: transform 0.15s ease-out;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/visual-editor-renderer-blade/resources/assets/frontend/accordion.css`
at line 58, Replace the CSS keyword usage "currentColor" with the lowercase form
"currentcolor" in the background declaration; locate the declaration background:
currentColor; in the accordion.css (e.g., the rule that sets background:
currentColor;) and change it to background: currentcolor; (and search for any
other occurrences of "currentColor" to normalize them to lowercase).

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tab-section.blade.php`:
- Around line 4-12: The template risks generating invalid or duplicate
id/aria-labelledby attributes: sanitize $tabId into a valid HTML id (e.g.,
normalize to lowercase, replace invalid chars with hyphens, ensure it starts
with a letter, and fallback to empty), then build $tabIdSafe from that
deterministic slug (not just htmlspecialchars) and only emit $idAttr and
$labelledByAttr when no id was already produced by BlockSupports::wrapperAttrs —
detect this by checking $attributes['id'] (or equivalent input key) before
adding id="tabs-panel-…" and aria-labelledby="tabs-tab-…"; update the code
around $tabId/$tabIdSafe and the logic that sets $idAttr/$labelledByAttr
accordingly.

In `@resources/js/visual-editor/blocks/accordion/accordion.css`:
- Line 39: The CSS uses the keyword "currentColor" with uppercase C which
triggers stylelint's value-keyword-case rule; update all occurrences (e.g., the
outline property in accordion.css where you see "outline: 2px solid
currentColor;") to the lowercase form "currentcolor" so the keyword is fully
lowercase and satisfies the linter.

---

Outside diff comments:
In `@packages/visual-editor-renderer-blade/src/BlockRenderer.php`:
- Around line 200-217: The renderDynamic method currently lacks the structured
$innerBlocks parameter, so when a dynamic block is missing or throws (in
renderDynamic) the fallback renderStatic receives no $innerBlocks; update the
renderDynamic signature to accept array $innerBlocks, pass that $innerBlocks
through to both renderStatic fallback calls, and update the place that invokes
renderDynamic (the call site that previously passed name, attributes,
innerBlocksHtml) to supply the structured $innerBlocks argument in addition to
existing args so partials like accordion.blade.php receive the correct
structured block tree.

---

Nitpick comments:
In
`@packages/visual-editor-renderer-blade/resources/assets/frontend/accordion.css`:
- Line 40: The CSS declaration "outline: 2px solid currentColor;" uses the wrong
casing for the keyword; update the property value to use the lowercase CSS
keyword "currentcolor" (i.e., replace currentColor with currentcolor) wherever
that declaration appears (e.g., in the selector containing the outline rule) to
comply with the CSS spec and linters.
- Line 58: Replace the CSS keyword usage "currentColor" with the lowercase form
"currentcolor" in the background declaration; locate the declaration background:
currentColor; in the accordion.css (e.g., the rule that sets background:
currentColor;) and change it to background: currentcolor; (and search for any
other occurrences of "currentColor" to normalize them to lowercase).

In `@packages/visual-editor-renderer-blade/resources/assets/frontend/tabs.css`:
- Line 74: Replace the CSS color keyword casing for the two occurrences of
"currentColor" used in the border rules (the lines with "border-bottom: 2px
solid currentColor;") to the lowercase form "currentcolor" for consistency with
the project's style/linter; update both occurrences (the one at the shown diff
and the other reported) so the rule reads "border-bottom: 2px solid
currentcolor;".

In
`@packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tab-section.blade.php`:
- Around line 4-7: The tab-section currently casts $tabId to string and only
calls htmlspecialchars into $tabIdSafe, but needs the same ID normalization as
the parent: apply the sanitizeTabId logic (lowercase, replace non-alphanumeric
chars with hyphens, collapse/trim hyphens) before HTML-escaping so the produced
id is valid; either call the existing sanitizeTabId helper used by
tabs.blade.php or inline that normalization for $tabId then pass the result
through htmlspecialchars to set $tabIdSafe.

In `@packages/visual-editor-renderer-vue/src/blocks/artisanpack/accordion.ts`:
- Line 116: The accordion block sets hidden: '' using legacy string syntax;
update the property to hidden: true to use a boolean (aligning with Vue 3+ and
the React renderer) by locating the accordion block definition in
packages/visual-editor-renderer-vue/src/blocks/artisanpack/accordion.ts and
replacing the hidden property value from an empty string to the boolean true.

In `@resources/js/visual-editor/blocks/accordion/edit.tsx`:
- Around line 46-51: The local slugify implementation is duplicated; extract the
slugify function into a shared utility module (e.g., export function slugify in
a common utils file) and replace the local slugify implementations in both
edit.tsx files to import and use that shared slugify; ensure you export the
function with the same signature (slugify(value: string): string), update
imports in the components that used the old local function, and run/type-check
to confirm no breaking type issues.

In `@resources/js/visual-editor/blocks/tab-section/index.ts`:
- Line 12: The import of '../tabs/tabs.css' in tab-section/index.ts duplicates
the same import present in tabs/index.ts; since Vite will deduplicate CSS this
is acceptable, so no change is required, but if you prefer centralizing imports
remove the import from tab-section/index.ts and ensure tabs/index.ts remains the
single importer of 'tabs.css' so styles for .ap-tabs and .ap-tab-section are
still bundled by Vite.

In `@resources/js/visual-editor/blocks/tabs/tabs.css`:
- Line 83: The CSS uses the keyword "currentColor" with incorrect casing for our
stylelint rule; update the occurrences in tabs.css (e.g., the border-bottom
declaration in the tabs block and the other occurrence noted around line 89) to
use lowercase "currentcolor" so the value-keyword-case lint rule passes,
ensuring you change every instance of currentColor to currentcolor (search for
"currentColor" to catch all spots).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 733fb8ee-70aa-4ea6-bb48-1f178fe5045f

📥 Commits

Reviewing files that changed from the base of the PR and between fb258ee and 7cc0033.

📒 Files selected for processing (51)
  • packages/renderer-parity.json
  • packages/visual-editor-renderer-blade/resources/assets/frontend/accordion.css
  • packages/visual-editor-renderer-blade/resources/assets/frontend/interactivity.js
  • packages/visual-editor-renderer-blade/resources/assets/frontend/tabs.css
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/accordion-body.blade.php
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/accordion-title.blade.php
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/accordion.blade.php
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/accordions.blade.php
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tab-section.blade.php
  • packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/tabs.blade.php
  • packages/visual-editor-renderer-blade/resources/views/components/blocks-styles.blade.php
  • packages/visual-editor-renderer-blade/src/BlockRenderer.php
  • packages/visual-editor-renderer-blade/src/View/Components/BlocksStylesComponent.php
  • packages/visual-editor-renderer-blade/src/VisualEditorRendererBladeServiceProvider.php
  • packages/visual-editor-renderer-blade/tests/Unit/BlockRendererTest.php
  • packages/visual-editor-renderer-react/src/blocks/artisanpack/accordion.tsx
  • packages/visual-editor-renderer-react/src/blocks/artisanpack/tabs.tsx
  • packages/visual-editor-renderer-react/src/blocks/registerCoreBlocks.ts
  • packages/visual-editor-renderer-react/tests/BlockTree.test.tsx
  • packages/visual-editor-renderer-vue/src/blocks/artisanpack/accordion.ts
  • packages/visual-editor-renderer-vue/src/blocks/artisanpack/tabs.ts
  • packages/visual-editor-renderer-vue/src/blocks/registerCoreBlocks.ts
  • packages/visual-editor-renderer-vue/tests/BlockTree.test.ts
  • resources/js/visual-editor/blocks/accordion-body/block.json
  • resources/js/visual-editor/blocks/accordion-body/edit.tsx
  • resources/js/visual-editor/blocks/accordion-body/index.ts
  • resources/js/visual-editor/blocks/accordion-body/save.tsx
  • resources/js/visual-editor/blocks/accordion-title/block.json
  • resources/js/visual-editor/blocks/accordion-title/edit.tsx
  • resources/js/visual-editor/blocks/accordion-title/index.ts
  • resources/js/visual-editor/blocks/accordion-title/save.tsx
  • resources/js/visual-editor/blocks/accordion/accordion.css
  • resources/js/visual-editor/blocks/accordion/block.json
  • resources/js/visual-editor/blocks/accordion/edit.tsx
  • resources/js/visual-editor/blocks/accordion/index.ts
  • resources/js/visual-editor/blocks/accordion/save.tsx
  • resources/js/visual-editor/blocks/accordions/block.json
  • resources/js/visual-editor/blocks/accordions/edit.tsx
  • resources/js/visual-editor/blocks/accordions/index.ts
  • resources/js/visual-editor/blocks/accordions/save.tsx
  • resources/js/visual-editor/blocks/tab-section/block.json
  • resources/js/visual-editor/blocks/tab-section/edit.tsx
  • resources/js/visual-editor/blocks/tab-section/index.ts
  • resources/js/visual-editor/blocks/tab-section/save.tsx
  • resources/js/visual-editor/blocks/tabs/block.json
  • resources/js/visual-editor/blocks/tabs/edit.tsx
  • resources/js/visual-editor/blocks/tabs/index.ts
  • resources/js/visual-editor/blocks/tabs/save.tsx
  • resources/js/visual-editor/blocks/tabs/tabs.css
  • resources/js/visual-editor/editor/__tests__/canvas-styles.test.ts
  • resources/js/visual-editor/editor/canvas-styles.ts

Comment thread resources/js/visual-editor/blocks/accordion/accordion.css Outdated
- Lowercase the `currentcolor` keyword in accordion + tabs CSS to
  satisfy stylelint's value-keyword-case rule.
- Slugify $tabId in the standalone tab-section partial so emitted
  id / aria-labelledby attributes are always valid HTML ids even
  when the persisted slug carries characters outside [a-z0-9-].
@ViewFromTheBox
ViewFromTheBox merged commit 3d71d66 into feature/495-port-crosswinds-blocks Jun 11, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In Review to Done in ArtisanPack UI Overview Jun 11, 2026
@ViewFromTheBox
ViewFromTheBox deleted the feature/497-cw1-accordion-tabs branch June 11, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

Crosswinds block fork CW1 · Interactive cluster (accordion + tabs families)

1 participant