Skip to content

feat(ds): add Link for navigation - #8179

Merged
talissoncosta merged 9 commits into
mainfrom
feat/ds-link
Aug 3, 2026
Merged

feat(ds): add Link for navigation#8179
talissoncosta merged 9 commits into
mainfrom
feat/ds-link

Conversation

@talissoncosta

@talissoncosta talissoncosta commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Thanks for submitting a PR! Please check the boxes below:

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Closes #8178 (epic #5746)

The goal is that in-app navigation goes through react-router's Link wherever it can, so the app does not reload, and that one component decides that rather than every call site choosing for itself. Today Button renders an <a> whenever it is given href, which it is at 30 sites, so the name tells you nothing about what you get and each one is a full page load whether it needs to be or not.

  • to is the default, rendering a router link. href is for leaving the app. The type permits exactly one, so a link cannot render without a destination and passing both is a compile error rather than one silently winning.
  • Owns its styling in Link.scss and imports nothing from Button. In light mode it is the same #6837fc as btn-link ($link-color resolves to $primary), so those call sites will not shift when they move over.
  • Dark mode deliberately differs. .dark .btn-link keeps that same purple; this takes --purple-400, which is lighter and holds up better on a dark background. Moving a link over is therefore a visible change in dark mode, not a no-op.
  • .link repeats itself under .dark. body.dark a in styles.scss colours every anchor at higher specificity than a bare .link, so the token never applied and links rendered as body text. .dark .btn-link already exists for the same reason. Both disappear if that global rule moves to a token.
  • Six stories rather than a controls playground, so each variant snapshots. In-prose and with-icon earn their place: .link is inline-flex with a gap, so baseline alignment mid-sentence and icon spacing are what would break unnoticed.

Not in scope here: nothing uses it yet, deliberately, so this lands on its own. Adoption of the 30 Button href sites follows in batches under #8178, after which Button can drop its anchor branch and only ever render <button>. Roughly 20 of those are external, a handful are internal routes that become to, and about 6 are href='#' with an onClick, which are not navigation and want a real button.

How did you test this code?

  • tsc: 0 new errors vs main.
  • eslint: clean on all changed files.
  • Storybook: six stories added under Components/Link.

Nobody has looked at it rendered, hence draft. Smoke test (light + dark):

  • InSentence beside a btn-link elsewhere in the app: colour and weight match, baseline sits right mid-paragraph
  • WithIcon: gap: 0.25rem spacing between icon and text
  • Dark theme toggle: the one place behaviour differs from btn-link
  • InApp: clicking navigates without a page reload
  • NewTab: opens a new tab and rel="noreferrer" is set
  • AlongsideAButton: the link and the button read as different things

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview Aug 3, 2026 11:50am
flagsmith-frontend-staging Ready Ready Preview Aug 3, 2026 11:50am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Aug 3, 2026 11:50am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

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

The change adds a typed Link component for router and external navigation. It adds automatic noreferrer handling for external new-tab links. It adds light and dark mode styles with hover and focus states. It exposes the component and LinkProps through the index module. It adds six Storybook stories for common Link usage patterns.

Estimated code review effort: 3 (Moderate) | ~20 minutes


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.

@github-actions github-actions Bot added the front-end Issue related to the React Front End Dashboard label Jul 31, 2026
@github-actions github-actions Bot added the feature New feature or request label Jul 31, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 31, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 31, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 31, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 31, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Jul 31, 2026
@talissoncosta
talissoncosta marked this pull request as ready for review July 31, 2026 13:00
@talissoncosta
talissoncosta requested a review from a team as a code owner July 31, 2026 13:00
@talissoncosta
talissoncosta requested review from kyle-ssg and removed request for a team July 31, 2026 13:00

@Zaimwa9 Zaimwa9 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requesting changes for a quick one, sorry 🙏

Comment thread frontend/web/components/base/link/Link.tsx Outdated
talissoncosta and others added 8 commits August 3, 2026 08:48
Button renders an <a> whenever it is given href, across 30 call sites, so the
name tells you nothing about what you get. btn-link also gets pasted onto bare
<a> elements in a couple of places, and <Link><Button> nests a button inside an
anchor, which is invalid HTML.

Link does the other job. `to` renders a router link, `href` a plain anchor, and
the type permits exactly one so a link cannot render without a destination. It
owns its styling and imports nothing from Button.

Link.scss reproduces what btn-link renders today so the call sites that move
across later do not shift. --color-text-action is the same purple in light mode
and a lighter one in dark, which btn-link had no treatment for.

Six stories rather than a controls playground, so each one snapshots. The prose
and icon ones earn their place: .link is inline-flex with a gap, so baseline
alignment mid-sentence and icon spacing are what would break unnoticed.

No adoption in this PR. Nothing uses it yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
React 19 passes ref as a normal prop, and Input and InputGroup already take it
that way. Also drops the named export, since the folder barrel and every
consumer use the default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
body.dark a in styles.scss colours every anchor at higher specificity than
.link, so the token never applied and links rendered as body text. Dark is
repeated to beat it, the same way .dark .btn-link already does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
btn-link does have a dark treatment, .dark .btn-link keeps the same purple, so
moving a link over is a visible change in dark mode rather than parity.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A string cannot express router state. ReleaseManagerPage passes
state: { searchQuery } to the flag page, which reads it back via
location.state and promotes it into ?search= on the way back, so the object
form is load-bearing rather than stylistic.

Uses RouterLinkProps['to'] instead of restating the shape, so it cannot drift
from react-router. Both destinations at once, and neither, are still type
errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
target was destructured out and only re-applied on the anchor branch, so
<Link to='/x' target='_blank'> silently opened in the same tab. rel had the
same hole. Both branches now share one safeRel.

Also extracts the destination union into a named LinkDestination, drops an
empty comment line stylelint flags, and points the story types at
@storybook/react-webpack5, which is where Storybook 10 keeps them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Truthiness meant an empty to fell through to the anchor branch and rendered an
<a> with no href, and the type guarantees there is no href to fall back to.
The router decides what '' resolves to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 3, 2026
btn-link now reads the same token, so dark no longer differs and .dark .btn-link
is gone. The .dark .link selector stays: body.dark a is untouched and still
outranks a bare .link.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d12e3c80-84ce-4e81-bbba-36e233f54ef3

📥 Commits

Reviewing files that changed from the base of the PR and between 2c86cb2 and 7d16a2a.

📒 Files selected for processing (1)
  • frontend/web/components/base/link/Link.tsx

Comment thread frontend/web/components/base/link/Link.tsx

@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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5b25c3b5-935b-4aff-bb6a-db9206b26d8e

📥 Commits

Reviewing files that changed from the base of the PR and between 7d16a2a and c59b6ee.

📒 Files selected for processing (4)
  • frontend/documentation/components/Link.stories.tsx
  • frontend/web/components/base/link/Link.scss
  • frontend/web/components/base/link/Link.tsx
  • frontend/web/components/base/link/index.ts

Comment thread frontend/web/components/base/link/Link.tsx

@Zaimwa9 Zaimwa9 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving as I think we can close our eyes on the relative import if you deem to

@talissoncosta
talissoncosta merged commit dc7f354 into main Aug 3, 2026
29 checks passed
@talissoncosta
talissoncosta deleted the feat/ds-link branch August 3, 2026 13:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Standardise Link on project Link: standardise colour and introduce the component

3 participants