Skip to content

feat: improve badge customization with dynamic text based on contrast#1979

Merged
danielroe merged 12 commits intonpmx-dev:mainfrom
sandros94:feat/improve-badge-customization
Mar 9, 2026
Merged

feat: improve badge customization with dynamic text based on contrast#1979
danielroe merged 12 commits intonpmx-dev:mainfrom
sandros94:feat/improve-badge-customization

Conversation

@sandros94
Copy link
Contributor

🔗 Linked issue

Followup from working on the unjs/automd#137 integration

Reproduction:

https://npmx.dev/api/registry/badge/downloads/unjwt?color=FFDC3B&labelColor=CCC

Broken rendering

📚 Description

Currently the badges have the text hardcoded to white, we can simply add a WCAG luminance evaluation to properly decide if the text should be white or black depending on the color input

Also added proper hex color validation for badges.

@vercel
Copy link

vercel bot commented Mar 7, 2026

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

Project Deployment Actions Updated (UTC)
docs.npmx.dev Ready Ready Preview, Comment Mar 8, 2026 11:03pm
npmx.dev Ready Ready Preview, Comment Mar 8, 2026 11:03pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
npmx-lunaria Ignored Ignored Mar 8, 2026 11:03pm

Request Review

@sandros94 sandros94 changed the title feat(api): improve badge customization with dynamic text based on contrast feat: improve badge customization with dynamic text based on contrast Mar 7, 2026
@codecov
Copy link

codecov bot commented Mar 7, 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!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds automatic selection of readable text colours for badge label and value using WCAG contrast calculations. Introduces SafeColorSchema to validate and normalise colour query parameters. Adds color utilities (toLinear, getContrastTextColor) and computes labelTextColor/valueTextColor from finalLabelColor/finalColor. Extends renderDefaultBadgeSvg and renderShieldsBadgeSvg signatures to accept labelTextColor and valueTextColor and updates SVG output to use them. Updates docs/examples to use color (replacing colorB) and adds e2e tests covering contrast behaviour and 3‑character hex handling.

Possibly related PRs

Suggested labels

front, a11y

Suggested reviewers

  • danielroe
  • ghostdevv
  • wojtekmaj
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly explains the issue (hardcoded white text causing legibility problems), the solution (WCAG luminance evaluation for dynamic text color), and references a specific reproduction case.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
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

🧹 Nitpick comments (1)
docs/content/2.guide/1.features.md (1)

162-162: Minor grammar improvement: add comma before 'so' in compound sentences.

Static analysis flags that a comma should precede 'so' when it connects two independent clauses.

📝 Suggested fix
-Overrides the default label color. You can pass a standard hex code (with or without the `#` prefix). The label text color is automatically chosen (black or white) based on WCAG contrast ratio so the badge remains readable.
+Overrides the default label color. You can pass a standard hex code (with or without the `#` prefix). The label text colour is automatically chosen (black or white) based on WCAG contrast ratio, so the badge remains readable.
-Overrides the default strategy color. You can pass a standard hex code (with or without the `#` prefix). The text color is automatically chosen (black or white) based on WCAG contrast ratio so the badge remains readable.
+Overrides the default strategy color. You can pass a standard hex code (with or without the `#` prefix). The text colour is automatically chosen (black or white) based on WCAG contrast ratio, so the badge remains readable.

Also applies to: 176-176


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ac835d6d-5831-4cab-b294-b020da103551

📥 Commits

Reviewing files that changed from the base of the PR and between 0d952fa and 5b2011b.

📒 Files selected for processing (3)
  • docs/content/2.guide/1.features.md
  • server/api/registry/badge/[type]/[...pkg].get.ts
  • test/e2e/badge.spec.ts

Copy link
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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2a1b170f-b278-41c8-8748-5125366965cb

📥 Commits

Reviewing files that changed from the base of the PR and between 40f2aa8 and 0b0d806.

📒 Files selected for processing (1)
  • test/e2e/badge.spec.ts

Copy link
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)
test/e2e/badge.spec.ts (1)

121-155: Cover the remaining badge rendering paths in this new contrast suite.

These cases only exercise valid colours on the default renderer. The server change also threads the new text colours through style=shieldsio and adds hex validation, so regressions in either path would still pass here. Please add at least one contrast assertion for style=shieldsio and one invalid color/labelColor case.

Example follow-up coverage
+  test('light colour produces dark text for contrast in shieldsio style', async ({ page, baseURL }) => {
+    const url = toLocalUrl(baseURL, '/api/registry/badge/version/nuxt?style=shieldsio&color=FFDC3B')
+    const { body } = await fetchBadge(page, url)
+
+    expect(body).toContain('fill="#ffffff">version')
+    expect(body).toMatch(/fill="#000000">v\d/)
+  })
+
+  test('invalid colour input falls back consistently', async ({ page, baseURL }) => {
+    const url = toLocalUrl(baseURL, '/api/registry/badge/version/nuxt?color=not-a-hex&labelColor=xyz')
+    const { response, body } = await fetchBadge(page, url)
+
+    expect(response.status()).toBe(200)
+    expect(body).not.toContain('fill="#not-a-hex"')
+    expect(body).not.toContain('fill="#xyz"')
+  })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0efd9ba9-f3d3-410e-8c8e-4033c97cdb18

📥 Commits

Reviewing files that changed from the base of the PR and between 0b0d806 and 6dae6a6.

📒 Files selected for processing (1)
  • test/e2e/badge.spec.ts

@sandros94
Copy link
Contributor Author

Sorry for the CI spam, my local playwrite is constantly crashing (and I do not have enough experience in debugging it, finding where the issue is and fixing it)

Copy link
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.

Caution

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

⚠️ Outside diff range comments (1)
docs/content/2.guide/1.features.md (1)

162-176: ⚠️ Potential issue | 🟡 Minor

Clarify that # must be URL-encoded in badge links.

Lines 162 and 176 say the value may include the # prefix, but in a query string a literal # starts the fragment and never reaches the server. I’d either recommend bare hex only in the docs, or explicitly call out %23ff69b4 / %23ffffff for prefixed examples.

🧹 Nitpick comments (2)
test/e2e/badge.spec.ts (2)

139-145: Decouple the labelColor case from the default strategy colour.

Line 145 is asserting the value-side text colour, but this test only controls labelColor. Per server/api/registry/badge/[type]/[...pkg].get.ts:466-484, the value text still comes from the default version badge colour, so a future palette tweak would fail this case for the wrong reason. Either drop the value assertion or pin color= in the URL as well.


121-166: Add one invalid-hex e2e case beside these happy paths.

These assertions cover the contrast logic nicely, but the new colour-validation branch is still unexercised here. One bad color/labelColor request would make the SafeColorSchema behaviour much harder to regress.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9c6358c8-aaf5-4d80-ac0f-0545beb1e42d

📥 Commits

Reviewing files that changed from the base of the PR and between 6dae6a6 and be08424.

📒 Files selected for processing (2)
  • docs/content/2.guide/1.features.md
  • test/e2e/badge.spec.ts

@danielroe danielroe added this pull request to the merge queue Mar 9, 2026
Merged via the queue into npmx-dev:main with commit 36bebce Mar 9, 2026
20 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.

3 participants