Repository: freeCodeCamp/freeCodeCamp
File: client/src/components/profile/components/portfolio-projects.tsx (TypeScript)

Code before:

interface PortfolioProjectsProps {
  portfolioProjects: PortfolioProjectData[];
}

export const PortfolioProjects = ({
  portfolioProjects
}: PortfolioProjectsProps): JSX.Element | null => {
  const { t } = useTranslation();
  if (!portfolioProjects.length) {
    return null;
  }
  return (
    <FullWidthRow>
      <h2 className='text-center'>{t('profile.projects')}</h2>
      {portfolioProjects.map(({ title, url, image, description, id }) => (
        <a
          href={url}
          rel='nofollow noopener noreferrer'
          target='_blank'
          className='portfolio-card'
          key={id}
        >
          <span className='sr-only'>
            {title}, {t('aria.opens-new-window')}
          </span>
          {image && (
            <img
              alt=''
              className='portfolio-image'
              src={image}
              onError={({ currentTarget }) => {
                currentTarget.src =
                  'https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png';
              }}
            />
          )}
          <div className='portfolio-card-description'>
            <div className='portfolio-card-text'>
              <h3>{title}</h3>
              <p>{description}</p>
            </div>
          </div>
        </a>
      ))}
      <hr />
    </FullWidthRow>
  );
};


Review comment (refactor):
I think we should add the `aria.opens-new-window` string to the `h3`, otherwise, screen reader users would hear the `title` twice.

That means we remove this part
```suggestion
```

Then update the `h3` as follows:
```tsx
<h3>
  {title}
  <span className='sr-only>, {t('aria.opens-new-window')}</span>
</h3>
```

Code after:

interface PortfolioProjectsProps {
  portfolioProjects: PortfolioProjectData[];
}

export const PortfolioProjects = ({
  portfolioProjects
}: PortfolioProjectsProps): JSX.Element | null => {
  const { t } = useTranslation();
  if (!portfolioProjects.length) {
    return null;
  }
  return (
    <FullWidthRow>
      <h2 className='text-center'>{t('profile.projects')}</h2>
      {portfolioProjects.map(({ title, url, image, description, id }) => (
        <a
          href={url}
          rel='nofollow noopener noreferrer'
          target='_blank'
          className='portfolio-card'
          key={id}
        >
          {image && (
            <img
              alt=''
              className='portfolio-image'
              src={image}
              onError={({ currentTarget }) => {
                currentTarget.src =
                  'https://cdn.freecodecamp.org/platform/universal/fcc_meta_1920X1080-indigo.png';
              }}
            />
          )}
          <div className='portfolio-card-description'>
            <div className='portfolio-card-text'>
              <h3>
                {title}
                <span className='sr-only'>, {t('aria.opens-new-window')}</span>
              </h3>
              <p>{description}</p>
            </div>
          </div>
        </a>
      ))}
      <hr />
    </FullWidthRow>
  );
};