Skip to content

Conversation

@JoPrignol
Copy link
Contributor

Added a link obfuscation prop.

This adds a new optional 'isObfuscated' prop to obfuscate links in the SocialShare component. This props takes a boolean.
When a link is obfuscated, it is rendered with a tag that triggers the 'openLink' function that opens the link in a new tab. When it's not obfuscated, it is rendered with a tag, as usual.
This is useful for SEO purposes in order to optimize the site's internal linking.

Resolves: #434 --> [Feature]: Add a props for link obfuscation

This is my first GitHub contribution, so please don't hesitate to give me feedback :)

This adds a new prop to obfuscate links in the SocialShare component
@gemini-code-assist
Copy link

Summary of Changes

Hello @JoPrignol, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the SocialShare component by adding a new isObfuscated property. This property enables developers to control the rendering of social share links, allowing them to appear as either standard anchor tags or interactive buttons that open links programmatically. The primary goal of this addition is to offer a tool for optimizing a site's internal linking strategy for better search engine optimization.

Highlights

  • New isObfuscated Prop: Introduced an optional isObfuscated boolean prop to the SocialShare component, allowing control over how social share links are rendered.
  • Dynamic Link Rendering: The component now dynamically renders either an <a> tag or a <button> tag based on the isObfuscated prop. When obfuscated, a button is used which programmatically opens the link.
  • SEO Optimization: This feature is designed to assist with SEO optimization by providing a mechanism to obfuscate internal links, which can be beneficial for managing crawl budget and link equity.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

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

Choose a reason for hiding this comment

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

Code Review

Welcome, and thank you for your first contribution! This is a great feature addition for improving SEO. The implementation is well on its way. I've left a couple of suggestions to enhance security and robustness, particularly around opening links in new tabs. Great job on identifying the need to add border: none for the button version to maintain visual consistency. Looking forward to your next contributions!

Comment on lines +2 to 12
<component
:is="isObfuscated ? 'button' : 'a'"
v-if="selectedNetwork"
:href="!isObfuscated ? selectedNetwork.shareUrl : null"
:aria-label="`Share with ${selectedNetwork.name}`"
target="_blank"
class="social-share-button"
:class="[`social-share-button--${network}`, { 'social-share-button--styled': isStyled }]"
:href="selectedNetwork.shareUrl"
:style="`--color-brand:${selectedNetwork.color}`"
:aria-label="`Share with ${selectedNetwork.name}`"
target="_blank"
@click="isObfuscated ? openLink(selectedNetwork.shareUrl) : null"
>

Choose a reason for hiding this comment

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

high

To improve security and HTML validity, I suggest a few changes:

  • Security: When using target="_blank", it's a best practice to also add rel="noopener noreferrer" to prevent potential security vulnerabilities (tabnabbing).
  • HTML Validity: The target attribute is not valid on a <button> element. It's better to apply it conditionally, only when rendering an <a> tag. The same applies to the rel attribute.

The suggestion below applies these changes.

  <component
    :is="isObfuscated ? 'button' : 'a'"
    v-if="selectedNetwork"
    :href="!isObfuscated ? selectedNetwork.shareUrl : null"
    :aria-label="`Share with ${selectedNetwork.name}`"
    :target="!isObfuscated ? '_blank' : null"
    :rel="!isObfuscated ? 'noopener noreferrer' : null"
    class="social-share-button"
    :class="[`social-share-button--${network}`, { 'social-share-button--styled': isStyled }]"
    :style="`--color-brand:${selectedNetwork.color}`"
    @click="isObfuscated ? openLink(selectedNetwork.shareUrl) : null"
  >

Comment on lines +61 to +64
function openLink(url) {
const newWindow = window.open(url, '_blank')
newWindow.focus()
}

Choose a reason for hiding this comment

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

high

The openLink function can be made more robust and secure:

  1. If a user has a popup blocker, window.open() might return null. Calling .focus() on null will cause a runtime error. It's good practice to check if the window was successfully opened before trying to focus it.
  2. For security, it's best to use the noopener,noreferrer options with window.open(), similar to the rel attribute on an <a> tag.
function openLink(url) {
  const newWindow = window.open(url, '_blank', 'noopener,noreferrer')
  if (newWindow) newWindow.focus()
}

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a link obfuscation feature to the SocialShare component through a new isObfuscated prop. When enabled, social share links are rendered as buttons that programmatically open URLs instead of standard anchor tags, which can help with SEO and internal linking optimization.

Key Changes:

  • Added isObfuscated boolean prop to toggle between button and anchor tag rendering
  • Implemented openLink function to handle programmatic link opening
  • Updated styles by removing @layer components wrapper and adding button reset styles

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

function openLink(url) {
const newWindow = window.open(url, '_blank')
newWindow.focus()
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

The openLink function doesn't handle the case where window.open() returns null (when popup is blocked). Calling .focus() on a null reference will throw a runtime error. Add a null check before calling focus:

function openLink(url) {
  const newWindow = window.open(url, '_blank')
  if (newWindow) {
    newWindow.focus()
  }
}
Suggested change
newWindow.focus()
if (newWindow) {
newWindow.focus()
}

Copilot uses AI. Check for mistakes.
:style="`--color-brand:${selectedNetwork.color}`"
:aria-label="`Share with ${selectedNetwork.name}`"
target="_blank"
@click="isObfuscated ? openLink(selectedNetwork.shareUrl) : null"
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

The @click handler should prevent the default action for the button element and should use a proper function reference. The current implementation @click="isObfuscated ? openLink(selectedNetwork.shareUrl) : null" will execute on every click even when isObfuscated is false. Consider:

@click="isObfuscated && openLink(selectedNetwork.shareUrl)"

Or better yet, use a method:

@click="handleClick"

And in the script:

function handleClick(event) {
  if (isObfuscated) {
    event.preventDefault()
    openLink(selectedNetwork.shareUrl)
  }
}

Copilot uses AI. Check for mistakes.
v-if="selectedNetwork"
:href="!isObfuscated ? selectedNetwork.shareUrl : null"
:aria-label="`Share with ${selectedNetwork.name}`"
target="_blank"
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

When rendered as a <button>, the target="_blank" attribute is invalid and has no effect on buttons. This attribute should be conditionally applied only when the component is rendered as an <a> tag:

:target="!isObfuscated ? '_blank' : null"

Or use v-bind conditionally to avoid adding invalid attributes to buttons.

Suggested change
target="_blank"
:target="!isObfuscated ? '_blank' : null"

Copilot uses AI. Check for mistakes.
gap: 0.5em;
align-items: center;
text-decoration: none;
width: min-content;
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

The border: none; style was added to .social-share-button--styled, but buttons rendered without this class will still have default browser button styles (borders, background, etc.) when isObfuscated is true. Consider adding button reset styles to .social-share-button to ensure consistent appearance:

.social-share-button {
  display: flex;
  gap: 0.5em;
  align-items: center;
  text-decoration: none;
  width: min-content;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
}
Suggested change
width: min-content;
width: min-content;
border: none;
background: none;
padding: 0;
cursor: pointer;
font: inherit;

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +64
function openLink(url) {
const newWindow = window.open(url, '_blank')
newWindow.focus()
}
Copy link

Copilot AI Nov 25, 2025

Choose a reason for hiding this comment

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

Opening a link with window.open() and target="_blank" without specifying rel="noopener noreferrer" attributes (for anchor tags) or equivalent security measures poses a security risk. The opened page can access the window.opener property and potentially redirect the parent page.

For the anchor tag case, add:

:rel="!isObfuscated ? 'noopener noreferrer' : null"

For the button case with window.open(), while the security risk is lower in modern browsers, it's still good practice to set opener to null or use the third parameter:

function openLink(url) {
  const newWindow = window.open(url, '_blank', 'noopener,noreferrer')
  if (newWindow) {
    newWindow.focus()
  }
}

Copilot uses AI. Check for mistakes.
@stefanobartoletti
Copy link
Owner

stefanobartoletti commented Nov 25, 2025

@JoPrignol Thanks for your contribution. For a general consideration of the proposed solution, I have already answered you on the linked issue #434

Since I guess that opening this PR was also for educational purposes on your side, I added some extra considerations about this PR quality:

  • There are some issues that the AI review tools have already highlighted:
    • mismatched attributes when switching from a and button elements (rel, target are not valid for button, for example
    • not handled errors in the onclick function.
    • attributes on the .social-share-button element, were not only edited but also reordered. There is no a real reason to do that, and it is better to stick to existing code if edits are not serving a clear purpose (generally speaking, only edit as few as possible).
    • having different tags means also that there will be different default browser styling (a browser renders a a with different styles from a button by default, which has not been addressed in the CSS.
  • generally speaking, if JavaScript is disabled by the final user or if there are some JS problems somehow, the share functionality will be broken without a proper fallback or any feedback to the user that something went wrong.
  • there is also a removal of the @layer components from the CSS, which is not only an unwanted edit (there are reasons, also explained in the docs, why this specific layer is used), but also breaks correct functionality for custom CSS styling in some cases.

Before continuing to work on this PR (that, for reasons explained on the #434 issue, I'm not inclined to merge), let's continue the discussion on the issue.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@stefanobartoletti
Copy link
Owner

closed in favor of #436

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.

[Feature]: Add a props for link obfuscation

2 participants