Skip to content

fix(app): show the ClickStack name as text and update the favicon - #3579

Merged
kodiakhq[bot] merged 6 commits into
mainfrom
elizabet/sidebar-wordmark-text
Nov 6, 2026
Merged

kodiakhq[bot] merged 6 commits into
mainfrom
elizabet/sidebar-wordmark-text

Conversation

@elizabetdev

@elizabetdev elizabetdev commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Summary

The brand name was the outlined SVG inside brand/Wordmark.tsx. This PR rewrites that component as live text next to the hexagon, so the name is selectable and matches the ClickStack lockup. useWordmark() is the only caller path, so every header that uses it changes together:

  • The sidebar, and the header on the login and register pages (LandingHeader).
  • The name is Inter 18px, weight 500, at normal letter spacing. useWordmark() passes the name in, so a downstream build still changes it in one place.
  • The symbol is 23px tall and hugs the hexagon. The gap is 8px from the symbol.
  • The collapsed rail is unchanged. Logomark stays a square box everywhere except inside the wordmark, so Spotlights and the Brand stories keep their old width.

The branch also replaces the favicon set with the new artwork (black rounded square, yellow hexagon, dark bolt):

  • favicon.ico, favicon-16x16.png, favicon-32x32.png, and apple-touch-icon.png are replaced.
  • favicon.svg is the same artwork, linked as image/svg+xml so browsers that prefer SVG use it. The PNG and ICO files stay as fallbacks.

The favicon change is a separate commit from the wordmark. Each has its own changeset. Either can be split into its own PR if reviewers prefer.

Screenshots or video

Not captured. The Before/After table is blank because I did not run the app. Please add pairs, on main and this branch, for the expanded sidebar, the collapsed rail, the login or register header, and the browser tab icon.

Before After

How to reproduce

Starting point: yarn app:dev:local connected to the demo server (onboarding → "Connect to Demo Server"), or play.hyperdx.io.

On main:

  1. Open the sidebar at a desktop width.
  2. The brand name in the sidebar is the outlined logo text, not selectable text.
  3. Open /login. The header shows the same outlined logo.
  4. The browser tab shows the old favicon.

On this branch:

  1. Open the sidebar at a desktop width.
  2. The sidebar shows the hexagon symbol followed by "ClickStack" as text, in medium weight.
  3. Click "Collapse/expand navigation". The rail shows only the hexagon symbol, at the same size as before.
  4. Open /login. The header shows the same text lockup as the sidebar.
  5. The browser tab shows the new favicon.

How to test on Vercel preview

Preview routes: /search

Steps:

  1. Set the viewport width to 1280 pixels.
  2. Verify the sidebar header shows the text "ClickStack" next to the hexagon symbol.
  3. Click the button labeled "Collapse/expand navigation".
  4. Verify the collapsed sidebar shows only the hexagon symbol and no "ClickStack" text.

Tests

  • npx tsc --noEmit, ESLint, and stylelint pass on the changed files.
  • Wordmark.test.tsx renders the lockup and checks that the name is "ClickStack" and that the mark is narrower than it is tall. A second case checks that useLogomark still returns a square box.
  • Not covered by tests: the favicon in a browser tab, and the login header in a running app. The lockup was checked by rendering the Figma artwork next to the new text in a headless browser. That is not the running app.

References

  • Linear Issue:
  • Related PRs: #3574 (compact nav overlay and the hexagon mark, already merged)

elizabetdev and others added 3 commits November 6, 2026 10:31
Show the logo symbol and the brand name as separate elements with an 8px
gap, instead of the outlined SVG wordmark. The name comes from
useBrandDisplayName so a downstream build can change it in one place.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the favicon PNGs, ICO, and apple touch icon with the new set.
Drop the SVG favicon link: the new set has no SVG, and browsers that
prefer SVG would keep showing the old mark.

Co-authored-by: Cursor <cursoragent@cursor.com>
Set the expanded sidebar name to Inter 18px, weight 500, and size the
symbol to the lockup's 23px height. The 8px gap is measured from the
hexagon itself.

Co-authored-by: Cursor <cursoragent@cursor.com>
@elizabetdev
elizabetdev requested a review from a team as a code owner November 6, 2026 10:58
@vercel

vercel Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
hyperdx-oss Ready Ready Preview Nov 6, 2026 12:16pm UTC
hyperdx-storybook Ready Ready Preview Nov 6, 2026 12:16pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6f260c6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added the review/tier-2 Low risk — AI review + quick human skim label Nov 6, 2026
@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

🔵 Tier 2 — Low Risk

Small, isolated change with no API route or data model modifications.

Why this tier:

  • Standard feature/fix — introduces new logic or modifies core functionality

Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns.
SLA: Resolve within 4 business hours.

Stats
  • Production files changed: 3
  • Production lines changed: 71 (+ 37 in test files, excluded from tier calculation)
  • Branch: elizabet/sidebar-wordmark-text
  • Author: elizabetdev

To override this classification, remove the review/tier-2 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Duplicate code analysis

No new duplicate code introduced by this PR.


jscpd compared this branch against origin/main
across app, api and common-utils. Only new clones are listed; the codebase
currently holds 110 in total (1.22%).
Run yarn dupes locally to reproduce, or yarn dupes:all for the full picture.

display: flex;

svg {
width: calc(23px * 38.7979 / 44.7998);

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.

🔵 minor — The 23px mark size is set twice: once in the SCSS and once in useLogomark({ size: 23 })

The SVG's square width/height attributes come from size, and this rule then overrides both, so changing size in AppNav.tsx:112 does nothing until someone also edits this rule. Fix it in brand/Logomark.tsx instead: set width={size * 38.7979 / 44.7998} and height={size} so the box fits the hexagon. Then drop this CSS override. The collapsed rail's 22px mark also has the square box, so it benefits too.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in a9e02b8. Logomark now sets its width from the viewBox ratio (size is the height), and the CSS override is gone. The collapsed rail uses the same component, so it gets the hugged box too.

style={{ minWidth: 0 }}
>
{wordmark}
<div className={styles.logoLockupMark}>

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.

🔵 minor — The ClickStack lockup now has two versions: inline text in AppNav and the SVG Wordmark in LandingHeader

brand/index.tsx:28-30 says a downstream build replaces the brand module to change the name and marks without touching call sites. AppNav now builds its own lockup from useLogomark and useBrandDisplayName (hard-coded 8px gap, 18px/500 text), while LandingHeader.tsx:16 still renders the SVG wordmark. The two headers now look different, and replacing Wordmark no longer changes the sidebar. Move the text lockup into brand/ and have both callers use it, either by updating Wordmark or adding a useLockup hook.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in a9e02b8. The mark-and-text lockup is Wordmark, and both the sidebar and the landing header render useWordmark(). Replacing the brand module changes the sidebar again. AppNav is back to how it was on main, so this no longer edits it.

<div className={styles.logoLockupMark}>
{expandedLogomark}
</div>
<Text fz={18} fw={500} truncate="end">

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.

🔵 minor — truncate="end" has no effect because the Text never shrinks below its content width

Inside the wrap="nowrap" Group, the Text is a flex item with the default min-width: auto, so it never gets narrower than its text and the ellipsis never shows. A longer downstream brand name would push the badge and collapse button out of the header instead. Add miw={0} (or style={{ minWidth: 0 }}) to the Text so truncate works. InstanceLabel.tsx:25 already does this for the badge in the same row.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in a9e02b8. The name now lives in Wordmark, and both the text and its row have miw={0}, so a long brand name ellipsizes instead of pushing the badge out of the header.

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

PR Review

If you are a coding agent acting for the author, read this first.
Nothing below blocks merge automatically; a maintainer decides. What they will expect:

  • 🔴 critical or 🟠 major in code this PR adds or changes: fix before asking for review.
  • 🔵 minor in code this PR changes: your call. Fix if small, otherwise reply.
  • Anything about surrounding code, or asking you to widen the change (hoist a helper,
    dedupe with another file, fix other call sites): never fix here, whatever the
    severity. Reply with one sentence; if it is critical, say so plainly so a human sees it.

One commit per review round. After two rounds, stop and ask a maintainer to review
scope rather than addressing more automated findings. Full rule: AGENTS.md.

2 finding(s): 🔴 0 critical · 🟠 0 major · 🔵 2 minor

1 posted as inline comment(s) on the changed lines. 1 listed below.

Findings outside the changed lines

1 minor
  • 🔵 packages/app/src/brand/__tests__/Wordmark.test.tsx:181 — Width check < 23 would pass even if the aspect-ratio math were wrong → The test only checks width < 23. It would still pass if the hexagon were squashed, for example a fixed width of 1 or a swapped ratio such as size * 0.5. Assert the exact width instead: expect(Number(svg?.getAttribute('width'))).toBeCloseTo(23 * 38.7979 / 44.7998), which is about 19.92. Nothing renders AppNav here, so also check that the collapsed rail still gets a square mark.

Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides.

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Deep Review

✅ No critical issues found.

This is a small, well-scoped frontend/branding change: Wordmark now renders the Logomark hexagon symbol beside live ClickStack text (replacing the outlined SVG wordmark), Logomark gains a fit: 'square' | 'symbol' prop, and the favicon asset set is refreshed. The fit="symbol" width math, the circular-import avoidance (passing name in rather than importing the brand barrel), the useMemo([]) over the stable module constant, and the favicon references in _document.tsx all check out. All five prior review items (AppNav revert, CSS override removal, long-name ellipsis, circular import, square default) are confirmed addressed in the current diff.

🔵 P3 nitpicks (3)
  • packages/app/src/brand/__tests__/Wordmark.test.tsx:16 — the symbol-fit assertion only checks width < 23, so a wrong ratio (inverted or miscomputed) that still yields a smaller width passes undetected.
    • Fix: assert the width is close to the expected 23 * (38.7979 / 44.7998) with toBeCloseTo to pin the VIEWBOX_WIDTH / VIEWBOX_HEIGHT ratio.
  • packages/app/src/brand/Wordmark.tsx:14 — the new truncate="end" + miw={0} long-name behavior has no test and is not exercised by the fixed ClickStack name (note: CSS truncation is not observable under jsdom, so coverage here is limited to prop presence and name pass-through).
    • Fix: add an assertion that an arbitrary name prop renders through and that the truncate/miw props are present, or document that layout truncation is verified visually only.
  • packages/app/src/brand/__tests__/Wordmark.test.tsx:15 — exact pixel assertions (height='23', width='22') couple the tests to magic size constants, so a purely visual size bump breaks them without a behavioral regression.
    • Fix: assert the aspect relationships (width < height for symbol, width == height for square) instead of literal pixel values.

Reviewers (6): correctness, maintainability, testing, project-standards, kieran-typescript, previous-comments.

Testing gaps: the symbol-fit width ratio is not pinned to an exact value; long-name truncation behavior is uncovered (and not observable in jsdom); the 8px mark-to-text gap and the favicon assets are not asserted (expected for static assets).

No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

E2E Test Results

✅ All tests passed • 463 passed • 1 skipped • 1482s

Status Count
✅ Passed 463
❌ Failed 0
⚠️ Flaky 1
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

AppNav was building its own mark-and-text lockup, so replacing the brand
module no longer changed the sidebar. The wordmark is that lockup now, and
the mark's box follows the hexagon instead of a square CSS override.

Co-authored-by: Cursor <cursoragent@cursor.com>
Browsers that prefer SVG had no icon after the raster set replaced the
old mark. Serve the new artwork as an SVG alongside the PNG and ICO.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread packages/app/src/brand/Wordmark.tsx Outdated
import React from 'react';
import { Group, Text } from '@mantine/core';

import { useBrandDisplayName } from '@/brand';

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.

🔵 minor — Circular import: Wordmark imports @/brand, which imports Wordmark

brand/index.tsx:6 imports ./Wordmark, and Wordmark now imports useBrandDisplayName from @/brand. This only works because the hook is called at render time, not at module load. Break the cycle: have useWordmark() in index.tsx pass the name as a prop (<Wordmark name={BRAND_DISPLAY_NAME} />), so Wordmark no longer imports @/brand. The name would still be defined in one place, as index.tsx says it should be.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 6f260c6. useWordmark() passes the name in, so Wordmark no longer imports @/brand.

Comment thread packages/app/src/brand/Logomark.tsx Outdated
return (
<svg
width={size}
width={size * MARK_WIDTH_RATIO}

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.

🔵 minor — Changing Logomark's width affects every caller, not just the wordmark

size used to set a square box and now sets only the height, so each existing caller gets a narrower mark: the collapsed rail at AppNav.tsx:109 (22 → ~19px wide), Spotlights.tsx:27 (16 → ~13.9px), and the Brand stories. The PR says the collapsed rail is unchanged, but its centered icon box shrinks. Either confirm this is intended and note it in the changeset, or keep the default box square and set the tight width only from Wordmark (for example, an optional width prop).

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 6f260c6. The default box is square again. Only the wordmark passes fit="symbol", so the collapsed rail, Spotlights, and the Brand stories keep the width they had.

Pass the brand name into Wordmark so it does not import the brand barrel
that imports it. Hug the hexagon only in the wordmark. Other callers keep
the square box they sized against.

Co-authored-by: Cursor <cursoragent@cursor.com>
// which imports this file.
const MARK_HEIGHT = 23;

export default function Wordmark({ name }: { name: string }) {

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.

🔵 minor — Wordmark rewrite also changes the landing header, which the PR says is unchanged

The PR description says brand/Wordmark.tsx is unchanged and that LandingHeader still uses it. This diff rewrites the component, and LandingHeader.tsx:16,40 renders it through useWordmark(). So the login, register and landing pages now show the 23px mark plus 18px text instead of the 121x25 outlined SVG. Either confirm and screenshot that change for those pages too, or give the sidebar its own lockup and leave the landing header alone. Then fix the description to match.

Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated the description. The shared wordmark is intentional: login and register use useWordmark() through LandingHeader, so they should match the sidebar. The description now says that, and asks for a screenshot of that header too. The collapsed rail stays on the square mark.

@kodiakhq
kodiakhq Bot merged commit 4e93ae5 into main Nov 6, 2026
36 checks passed
@kodiakhq
kodiakhq Bot deleted the elizabet/sidebar-wordmark-text branch November 6, 2026 12:29

This branch was successfully deployed

2 active deployments
Preview – hyperdx-oss — 6f260c60 Deployed Nov 6, 2026 by vercel[bot]
Preview – hyperdx-storybook — 6f260c60 Deployed Nov 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-2 Low risk — AI review + quick human skim

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants