Skip to content

feat(dashboard): one-time Stack Auth → Hexclave rebrand modal#1493

Merged
BilalG1 merged 5 commits into
cl/romantic-mendel-5a2c25from
cl/hexclave-rebrand-modal
May 27, 2026
Merged

feat(dashboard): one-time Stack Auth → Hexclave rebrand modal#1493
BilalG1 merged 5 commits into
cl/romantic-mendel-5a2c25from
cl/hexclave-rebrand-modal

Conversation

@BilalG1
Copy link
Copy Markdown
Collaborator

@BilalG1 BilalG1 commented May 26, 2026

Summary

Adds a one-time informational modal that announces the Stack Auth → Hexclave rebrand to existing logged-in dashboard users. Brand-new users signing up after the cutoff already land on a fully Hexclave-branded experience and don't see the modal — they have no "Stack Auth" mental model to update.

Stacked on top of PR #1481 (the visible-rebrand flip).

Hexclave rebrand modal

What's in here

  • Component: apps/dashboard/src/components/hexclave-rebrand-modal.tsx — the modal itself plus the static RebrandIllustration (Stack Auth mark → arrow → Hexclave benzene mark).
  • Mount: apps/dashboard/src/app/(main)/(protected)/layout-client.tsx — single <HexclaveRebrandModal /> inside ConfigUpdateDialogProvider so it covers every authenticated dashboard route.
  • Asset: apps/dashboard/public/hexclave-icon.svg — the benzene-ring mark pulled from https://hexclave.com/icon.svg (the canonical Hexclave brand mark). Stack Auth side uses the existing /logo.svg + /logo-bright.svg (dark mode variant).

Targeting

Three independent conditions must all hold for the modal to render:

  1. Logged-in useruseUser({ or: "return-null" }) opts out of the sign-in redirect so guests are silently skipped.
  2. user.signedUpAt < REBRAND_CUTOFF where REBRAND_CUTOFF = 2026-05-27T00:00:00Z. This is the cleanest "pre-rebrand user" signal — more accurate than cookie inspection (the existing stack-is-https hint cookie gets dual-written for new and returning users alike since packages/template/src/lib/cookie.ts:201, so it can't distinguish), and it follows the same user across browsers/devices.
  3. No hexclave-rebrand-modal-dismissed=true in localStorage. Any dismissal path (Got it button, X, overlay click, Escape) routes through onOpenChange and writes this flag, so the modal never re-opens for that browser.

Behavior verification

End-to-end checked against a live dev dashboard:

  • ✅ Modal opens once on first authenticated dashboard view for a pre-cutoff user.
  • localStorage["hexclave-rebrand-modal-dismissed"] flips to "true" on any dismissal path.
  • ✅ Reload after dismissal: zero [role=dialog] elements rendered, localStorage flag persists.
  • ✅ Not rendered for guests (no useUser → early return null, no auth-redirect).
  • ✅ Not rendered for users with signedUpAt >= REBRAND_CUTOFF.
  • ✅ SSR-safe: hooks into useEffect to read storage post-hydration; try/catch around storage access so private-mode / sandboxed iframes degrade silently.
  • pnpm --filter @stackframe/dashboard lint clean.

Notes


Summary by cubic

Adds a one-time modal in the dashboard to announce the Stack Auth → Hexclave rebrand for pre-rebrand users. It shows once per account per browser, inlines the import-rename step, links the migration guide, and doesn’t show for new signups or in dev/preview environments.

  • New Features
    • Shows only for logged-in users with signedUpAt < '2026-05-27T00:00:00Z'; guests, post-cutoff signups, and local emulator/remote dev/preview never see it.
    • Dismissal persists via localStorage["hexclave-rebrand-modal-dismissed:<user.id>"]="true"; all close paths set the flag.
    • Mounted in the protected dashboard layout so it covers all authenticated routes once per account per browser.
    • Includes illustration (/logo.svg → arrow → /hexclave-icon.svg) and links to app.hexclave.com and the migration guide; copy tells users to rename @stackframe/* to @hexclave/* (@stackframe/stack@hexclave/next).

Written for commit 92c07f2. Summary will update on new commits. Review in cubic

Announces the rebrand to authenticated dashboard users whose
signedUpAt predates 2026-05-27 UTC (the rebrand cutoff). Brand-new
users signing up after the cutoff already land on Hexclave-branded
surfaces and don't see the modal.

- Modal lives in the protected dashboard layout via
  apps/dashboard/src/app/(main)/(protected)/layout-client.tsx so it
  surfaces on any logged-in route once per browser.
- Gated on useUser({ or: "return-null" }) + signedUpAt < cutoff, so
  it never triggers a sign-in redirect for guests and never shows for
  post-rebrand signups.
- Dismissal (button, X, overlay, Escape) routes through onOpenChange
  and writes "hexclave-rebrand-modal-dismissed" to localStorage so it
  never re-appears for that browser.
- Illustration uses the existing Stack Auth logo (logo.svg /
  logo-bright.svg for dark mode) and the Hexclave benzene mark saved
  to public/hexclave-icon.svg.
- Copy links app.hexclave.com and docs.hexclave.com/migration.

Screenshot: .github/assets/hexclave-rebrand-modal.png
@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

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

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment May 27, 2026 1:05am
stack-auth-mcp Ready Ready Preview, Comment May 27, 2026 1:05am
stack-auth-skills Ready Ready Preview, Comment May 27, 2026 1:05am
stack-backend Ready Ready Preview, Comment May 27, 2026 1:05am
stack-dashboard Ready Ready Preview, Comment May 27, 2026 1:05am
stack-demo Ready Ready Preview, Comment May 27, 2026 1:05am
stack-docs Ready Ready Preview, Comment May 27, 2026 1:05am
stack-preview-backend Ready Ready Preview, Comment May 27, 2026 1:05am
stack-preview-dashboard Ready Ready Preview, Comment May 27, 2026 1:05am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1a857f8f-c077-46db-8c9d-6c1cf194f405

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cl/hexclave-rebrand-modal

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

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 4 files

Tip: cubic could auto-approve low-risk PRs like this, if it thinks it's safe to merge. Learn more

Re-trigger cubic

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 26, 2026

Greptile Summary

Adds a one-time informational modal that announces the Stack Auth → Hexclave rebrand to dashboard users who signed up before the 2026-05-27T00:00:00Z cutoff. The component is mounted in the protected layout's ConfigUpdateDialogProvider and guards itself with an SSR-safe useEffect/localStorage pattern so the modal never re-appears after any dismissal path.

  • hexclave-rebrand-modal.tsx: New client component; derives isPreRebrandUser from user.signedUpAt, reads a localStorage flag post-hydration to avoid SSR mismatch, and writes the flag on any close event (button, X, overlay, Escape).
  • layout-client.tsx: Minimal one-line mount of <HexclaveRebrandModal /> inside the existing ConfigUpdateDialogProvider wrapper.
  • hexclave-icon.svg: New brand asset served from /public, consistent with how the existing logo.svg is handled.

Confidence Score: 4/5

Safe to merge; the modal is purely informational and the changes are well-isolated to a new component and a one-line layout mount.

The modal logic is correct and SSR-safe, but the localStorage key is not scoped to the logged-in user — on a shared device the first dismissal silences the announcement for everyone else who uses that browser. The cutoff timing also means auto-created preview users will see the modal for the next ~24 hours. Both are non-blocking but worth addressing before wide production traffic.

apps/dashboard/src/components/hexclave-rebrand-modal.tsx — the dismissal key and preview-environment behaviour warrant a second look.

Important Files Changed

Filename Overview
apps/dashboard/src/components/hexclave-rebrand-modal.tsx New one-time rebrand announcement modal; SSR-safe localStorage pattern is sound but the dismissal key is not user-scoped, which causes the modal to be silently suppressed for other users sharing the same browser.
apps/dashboard/src/app/(main)/(protected)/layout-client.tsx Single-line mount of HexclaveRebrandModal inside ConfigUpdateDialogProvider; no logic changes, safe addition.
apps/dashboard/public/hexclave-icon.svg New brand asset SVG; filter/gradient IDs are scoped to their own document context when served via next/image, no conflicts expected.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[LayoutClient mounts] --> B[HexclaveRebrandModal renders]
    B --> C{useUser returns user?}
    C -- "null (loading/guest)" --> D[return null — nothing rendered]
    C -- "user loaded" --> E{signedUpAt < REBRAND_CUTOFF?}
    E -- No --> F[return null — post-rebrand user]
    E -- Yes --> G[isPreRebrandUser = true, open = false]
    G --> H[useEffect fires, read localStorage]
    H --> I{dismissed === 'true'?}
    I -- Yes --> J[stay closed — already seen]
    I -- No --> K[setOpen true — Dialog shown]
    K --> L{User dismisses}
    L -- "Got it / X / Escape / Overlay" --> M[dismiss: write localStorage, setOpen false]
    M --> N[Modal closed permanently for this browser]
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
apps/dashboard/src/components/hexclave-rebrand-modal.tsx:16
The dismissal flag is not scoped to the logged-in user. On a shared device (or a browser where multiple users take turns logging in), the first person to dismiss the modal writes `"true"` to the same key for every subsequent user. Pre-rebrand teammates sharing a machine would never receive the announcement.

```suggestion
const storageKey = (userId: string) => `hexclave-rebrand-modal-dismissed:${userId}`;
```

### Issue 2 of 3
apps/dashboard/src/components/hexclave-rebrand-modal.tsx:42-54
If the `STORAGE_KEY` becomes user-scoped (see the other comment), the `useEffect` and the `dismiss` function both need `user.id` to construct the correct key. Both the read in `useEffect` and the write in `dismiss` reference the module-level `STORAGE_KEY` constant; once that's replaced with `storageKey(user.id)` the two call-sites must be updated consistently, otherwise the effect reads one key and the writer writes another, causing the modal to reopen on every page load.

### Issue 3 of 3
apps/dashboard/src/components/hexclave-rebrand-modal.tsx:37
Preview / local-emulator users created before `REBRAND_CUTOFF` (today is 2026-05-26, the cutoff is 2026-05-27) will have a `signedUpAt` that satisfies `< REBRAND_CUTOFF`. Because `layout-client.tsx` auto-signs-up fresh preview accounts each session, each new preview load shows the rebrand modal until the cutoff date passes tomorrow. A simple guard checking the preview/emulator env vars inside the modal would suppress noise in non-production environments.

Reviews (1): Last reviewed commit: "feat(dashboard): add one-time Stack Auth..." | Re-trigger Greptile

Comment thread apps/dashboard/src/components/hexclave-rebrand-modal.tsx Outdated
Comment thread apps/dashboard/src/components/hexclave-rebrand-modal.tsx Outdated
Comment thread apps/dashboard/src/components/hexclave-rebrand-modal.tsx Outdated
@BilalG1 BilalG1 requested a review from N2D4 May 26, 2026 22:43
Adds an explicit short-circuit at the top of HexclaveRebrandModal that
returns no modal when any of the three NEXT_PUBLIC_STACK_IS_* dev flags
is true — local emulator, remote development environment, or preview.

These surfaces either auto-create throwaway users (preview) or seed a
fixture admin (emulator/RDE). The rebrand notice would be friction for
developers logging into local dev and meaningless for preview visitors
who never used "Stack Auth" in the first place. Coincidentally these
users are also filtered out today by signedUpAt < REBRAND_CUTOFF (fresh
preview signups happen after the cutoff; a freshly-seeded emulator admin
also signs up post-cutoff), but the explicit env-flag guard is more
defensive — it stays correct if the cutoff is ever bumped forward for a
test.

Same three flags the protected layout already gates on
(apps/dashboard/src/app/(main)/(protected)/layout-client.tsx:15-17).
Comment thread apps/dashboard/src/components/hexclave-rebrand-modal.tsx Outdated
Switches the localStorage key from the global
"hexclave-rebrand-modal-dismissed" to a per-user
"hexclave-rebrand-modal-dismissed:<user.id>".

Rationale: a shared browser (e.g. a work machine where two teammates
each log into their own dashboard account) was previously hiding the
announcement from teammate B as soon as teammate A dismissed it. Now
each account tracks its own dismissal independently. The trade-off —
the same human switching between their personal and work accounts will
see the modal once per account — is acceptable: each account is a
distinct identity, and a one-time brand notice per account is fine.

Storage key is computed as `${STORAGE_KEY_PREFIX}${user.id}` only when
`user` is non-null; both the useEffect read path and the dismiss write
path null-check `storageKey` defensively even though the existing gates
(`isPreRebrandUser`, `if (!isPreRebrandUser) return null`) already
ensure neither runs without a user.

No migration shim — the old global key was only ever written in local
dev/test before this PR ships, so no production data is being orphaned.
Tells users up-front to rename @stackframe/* imports to @hexclave/* (with
the @stackframe/stack → @hexclave/next exception) so the basic upgrade
path doesn't require a click through to the docs. Migration guide link
stays but is de-emphasized.
@BilalG1 BilalG1 merged commit 4011916 into cl/romantic-mendel-5a2c25 May 27, 2026
25 of 26 checks passed
@BilalG1 BilalG1 deleted the cl/hexclave-rebrand-modal branch May 27, 2026 01:16
BilalG1 added a commit that referenced this pull request May 27, 2026
The rebrand modal was added on the rebrand-modal branch (PR #1493) and
still imported useUser from the pre-rename package name @stackframe/stack.
After this branch's source rename to @hexclave/*, that package no longer
exists in the workspace, causing the dashboard Turbopack build to fail
with 'Module not found: Can't resolve @stackframe/stack' and aborting the
E2E job.
BilalG1 added a commit that referenced this pull request May 27, 2026
## Summary

The Stack Auth → Hexclave rebrand modal added in
[#1493](#1493) rendered the
**Hexclave** logo on both sides of its illustration instead of *Stack
Auth → Hexclave*.

## Root cause


[\`hexclave-rebrand-modal.tsx\`](https://github.com/hexclave/stack-auth/blob/dev/apps/dashboard/src/components/hexclave-rebrand-modal.tsx)
referenced \`/logo.svg\` and \`/logo-bright.svg\` for the left-hand
"Stack Auth" mark. But the visible-rebrand flip in
[#1481](#1481) replaced those
files in \`apps/dashboard/public/\` with the Hexclave benzene mark — so
post-merge, both \`<Image>\` slots resolved to the Hexclave logo.

## Fix

- Restored the **pre-rebrand Stack Auth SVGs** (lifted from
\`57ff5d3ce~1\`, the commit before the brand-flip) under dedicated
filenames so they can't get clobbered the next time \`/logo.svg\` is
updated:
- \`apps/dashboard/public/stack-auth-logo.svg\` — black mark, light
theme
- \`apps/dashboard/public/stack-auth-logo-bright.svg\` — white mark,
dark theme
- Pointed the modal's left-hand \`<Image>\` pair at the new paths and
left a comment explaining why we don't share \`/logo.svg\` (so the next
person doesn't try to consolidate it back).

The originals on \`origin/dev\` (\`/logo.svg\`, \`/logo-bright.svg\` =
Hexclave) are untouched.

## Verification

- \`pnpm --filter @stackframe/dashboard lint\` — clean.
- Diff of
[hexclave-rebrand-modal.tsx](https://github.com/hexclave/stack-auth/blob/fix-stack-logo-in-modal/apps/dashboard/src/components/hexclave-rebrand-modal.tsx)
is two \`src=\` swaps plus a comment; the dismissal logic, gates, and
storage key are untouched.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes the rebrand modal to show the Stack Auth logo on the left instead
of rendering Hexclave on both sides. Restores pre-rebrand assets as
`stack-auth-logo.svg` and `stack-auth-logo-bright.svg` and updates the
modal to use them for light/dark themes.

<sup>Written for commit f4ed262.
Summary will update on new commits. <a
href="https://cubic.dev/pr/hexclave/stack-auth/pull/1495?utm_source=github">Review
in cubic</a></sup>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Style**
* Updated the rebrand modal illustration assets to display Stack Auth
branding correctly.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1495?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

1 participant