Skip to content

fix: download correct variant and fix layout shift on brand page #2595

Merged
ghostdevv merged 4 commits intonpmx-dev:mainfrom
Adebesin-Cell:fix/brand-png-light-variant
Apr 20, 2026
Merged

fix: download correct variant and fix layout shift on brand page #2595
ghostdevv merged 4 commits intonpmx-dev:mainfrom
Adebesin-Cell:fix/brand-png-light-variant

Conversation

@Adebesin-Cell
Copy link
Copy Markdown
Contributor

Summary

  • Fix on-light PNG download on /brand returning the on-dark asset — the click handler always used logo.src regardless of which button was pressed
  • Fix Customize PNG export failing under CSP — swap the blob: URL (not allowed by img-src) for a data: URL
  • Remove <ClientOnly> around BrandCustomize so the section renders during SSR (no layout gap / hydration flash); canvas + download logic only runs in click handlers, so it's safe on the server

Closes #2565

Test plan

  • /brand → wordmark on-light → Download PNG → file is the light variant
  • /brand → logo mark on-light → Download PNG → file is the light variant
  • On-dark PNG downloads still work for both
  • Customize section → toggle On light → Download PNG succeeds (no CSP error)
  • Customize section renders in SSR (view source / disable JS) — no pop-in on hydrate

- Fix on-light PNG downloading the on-dark asset (handler always used logo.src)
- Fix Customize PNG export blocked by CSP — use data URL instead of blob URL
- Render BrandCustomize in SSR (remove ClientOnly); canvas/download only runs on click

Closes npmx-dev#2565

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Apr 20, 2026 7:05am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Apr 20, 2026 7:05am
npmx-lunaria Ignored Ignored Apr 20, 2026 7:05am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

Adds a variant parameter to the PNG download handler to select logo.srcLight ?? logo.src for light variants, renders BrandCustomize directly, changes SVG rasterisation to use inline data URLs, and replaces custom radio widgets with native radio inputs in the Brand Customize component.

Changes

Cohort / File(s) Summary
Brand page & PNG handler
app/pages/brand.vue
Extended `handlePngDownload(logo, variant: 'dark'
Customize component — SVG → inline data URL
app/components/Brand/Customize.vue
downloadCustomPng() switched from creating an SVG Blob + URL.createObjectURL to using an inline data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)} URL; removed URL.revokeObjectURL(...).
Customize component — radio inputs & accessibility wiring
app/components/Brand/Customize.vue
Replaced custom ButtonBase radio widgets with native <label><input type="radio"> controls for accent-color and background dark/light selection; switched event handling from @click assignments to @change/v-model, removed explicit role="radio"/aria-* attributes and custom click handlers.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description provides clear context about the three main fixes: PNG variant selection, CSP compliance, and SSR rendering.
Linked Issues check ✅ Passed The PR addresses issue #2565 by fixing the on-light PNG download to return the correct light variant instead of the dark variant.
Out of Scope Changes check ✅ Passed All changes are within scope: PNG variant selection fix, CSP compliance fix via data URLs, and SSR rendering via ClientOnly removal.
Title check ✅ Passed The title accurately summarizes the two main changes: fixing PNG variant downloads and addressing the layout shift issue caused by ClientOnly wrapper removal.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@Adebesin-Cell Adebesin-Cell changed the title fix(brand): download correct PNG variant and remove hydration gap fix: download correct PNG variant and remove hydration gap Apr 20, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Resolves html-validate errors that surfaced once BrandCustomize began rendering in SSR: replace ButtonBase with role="radio" with native <input type="radio"> in labels, and drop class duplicates already provided by ButtonBase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/Brand/Customize.vue`:
- Around line 147-167: The visible swatch selection (computed/prop
activeAccentId) can be out of sync with the native radio because customAccent
starts null and the aria-label is on the label; update the radio input binding
so the native control always reflects the visible state: either initialize
customAccent to activeAccentId (or set customAccent when activeAccentId changes)
and/or bind the input's checked state to compare customAccent === color.id, and
move the accessible name (aria-label / aria-labelledby) from the label onto the
input element so assistive tech reads the radio itself; adjust code locations
using pickerColors, activeAccentId, and customAccent to implement this.
🪄 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: 83f269f4-c11b-4b1b-bb40-eed1a84a6315

📥 Commits

Reviewing files that changed from the base of the PR and between a7dc645 and 6332984.

📒 Files selected for processing (1)
  • app/components/Brand/Customize.vue

Comment thread app/components/Brand/Customize.vue
Replace ButtonBase with role="radio" with native <input type="radio"> in labels to satisfy html-validate prefer-native-element, and drop duplicate utility classes already provided by ButtonBase. Add focus-within rings for keyboard visibility. Bind :checked from activeAccentId so the native radio matches the visible swatch even when customAccent is null.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/components/Brand/Customize.vue (1)

174-207: Group the background radios with <fieldset>/<legend> for parity with the accent picker.

The two on-dark/on-light radios share name="brand-customize-bg" but aren’t wrapped in a <fieldset>, and the visible <span> at Lines 176-178 isn’t programmatically associated with the group. The accent picker just above uses <fieldset> + <legend class="sr-only">; mirroring that here keeps the a11y model consistent and lets screen readers announce the group name when focus enters the radios.

As per coding guidelines (**/*.vue: Composition API is already used; this is a template-semantics nit, not a refactor of reactivity).

♿ Suggested fix
-          <div class="flex items-center gap-3">
-            <span class="text-sm font-mono text-fg-muted">{{
-              $t('brand.customize.bg_label')
-            }}</span>
-            <div class="flex items-center border border-border rounded-md overflow-hidden">
+          <fieldset class="flex items-center gap-3 border-none p-0 m-0">
+            <legend class="text-sm font-mono text-fg-muted float-none contents">
+              {{ $t('brand.customize.bg_label') }}
+            </legend>
+            <div class="flex items-center border border-border rounded-md overflow-hidden">
               <label ...>...</label>
               <label ...>...</label>
-            </div>
-          </div>
+            </div>
+          </fieldset>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/Brand/Customize.vue` around lines 174 - 207, Wrap the two
background radio inputs (the elements using v-model="customBgDark" and
name="brand-customize-bg") in a <fieldset> and add a <legend class="sr-only">
that contains {{$t('brand.customize.bg_label')}} to match the accent picker
pattern; also mark the visible span that currently shows
$t('brand.customize.bg_label') as aria-hidden (or remove it) to avoid duplicate
announcements so screen readers use the legend for the group label.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/components/Brand/Customize.vue`:
- Around line 174-207: Wrap the two background radio inputs (the elements using
v-model="customBgDark" and name="brand-customize-bg") in a <fieldset> and add a
<legend class="sr-only"> that contains {{$t('brand.customize.bg_label')}} to
match the accent picker pattern; also mark the visible span that currently shows
$t('brand.customize.bg_label') as aria-hidden (or remove it) to avoid duplicate
announcements so screen readers use the legend for the group label.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b0e41369-1589-44a6-9c03-ae0f1d065b39

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb88dd and 60c8e0b.

📒 Files selected for processing (1)
  • app/components/Brand/Customize.vue

@Adebesin-Cell
Copy link
Copy Markdown
Contributor Author

@ghostdevv Feel free to check it out!

@ghostdevv ghostdevv changed the title fix: download correct PNG variant and remove hydration gap fix: download correct variant and fix layout shift on brand page Apr 20, 2026
@ghostdevv ghostdevv added this pull request to the merge queue Apr 20, 2026
Merged via the queue into npmx-dev:main with commit 2dcc3de Apr 20, 2026
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Downloading on-light variant logo as png on /brand-page downloads on-dark variant

2 participants