Skip to content

✨ Add option to enable custom branding#2321

Merged
lukevella merged 4 commits intomainfrom
luke/ral-1128-branding-settings
Apr 9, 2026
Merged

✨ Add option to enable custom branding#2321
lukevella merged 4 commits intomainfrom
luke/ral-1128-branding-settings

Conversation

@lukevella
Copy link
Copy Markdown
Owner

@lukevella lukevella commented Apr 9, 2026

Summary by CodeRabbit

  • New Features
    • Custom branding for Pro: set a primary color to brand your space across the app, public pages, and emails.
    • New toggle to enable/disable branded elements and a dedicated Branding section in Space settings (admin controls).
    • Save flow shows progress and success/error toasts; free users are guided to upgrade via the paywall when attempting to enable branding.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 9, 2026

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

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Apr 9, 2026 11:27am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
landing Skipped Skipped Apr 9, 2026 11:27am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Walkthrough

Adds a custom branding feature: new UI to toggle and save a space primary color, TRPC changes to accept/clear primaryColor and update showBranding with PostHog groupIdentify, locale strings, paywall gating for free users, a toast-progress hook, and conditional application of branding CSS vars.

Changes

Cohort / File(s) Summary
Localization
apps/web/public/locales/en/app.json
Removed image; added branding-related translation keys (showBrandingDescription, useCustomBranding, savingBranding, brandingSaved, brandingSaveError, customBranding, customBrandingDescription).
Settings UI
apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx
New exported CustomBrandingSection component: color picker, enable/disable toggle, optimistic TRPC mutations, paywall gating, disabled UI states.
Settings form & page
apps/web/src/app/[locale]/(space)/settings/general/components/space-settings-form.tsx, apps/web/src/app/[locale]/(space)/settings/general/page-client.tsx
Refactored form primitives from @rallly/ui/form@rallly/ui/field with Controller wiring; integrated CustomBrandingSection into general settings (disabled for non-admins).
Billing / Paywall
apps/web/src/features/billing/components/pay-wall-dialog.tsx
Added Pro plan benefit item for custom branding (PaletteIcon + localized title/description).
Space client & analytics
apps/web/src/features/space/client.tsx
SpaceProvider now includes custom_branding_enabled in PostHog group props; CSS primary color vars are applied only when space.showBranding is true.
Toast utility
apps/web/src/lib/use-toast-progress.ts
New useToastProgress hook returning a wrapper to register promises with toast.promise and localized messages.
TRPC router
apps/web/src/trpc/routers/spaces.ts
spaces.update input extended to optional name and optional/nullable validated primaryColor; removed spaces.updatePrimaryColor mutation; spaces.updateShowBranding now calls PostHog groupIdentify to set custom_branding_enabled.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as CustomBrandingSection
    participant Billing as useBilling
    participant Paywall as PayWallDialog
    participant TRPC as TRPC (spaces)
    participant DB as Database
    participant PostHog
    participant Cache as Query Cache

    User->>UI: Toggle "Enable Custom Branding"
    UI->>Billing: check isFree
    alt Free user
        Billing-->>UI: isFree = true
        UI->>PostHog: capture event (attempt_enable_branding)
        UI->>Paywall: show paywall
        Paywall-->>User: display Pro benefits (includes custom branding)
    else Paid user
        Billing-->>UI: isFree = false
        UI->>TRPC: mutate spaces.updateShowBranding(newValue)
        TRPC->>DB: update showBranding
        DB-->>TRPC: confirm
        TRPC->>PostHog: groupIdentify(custom_branding_enabled)
        TRPC->>Cache: update spaces.getCurrent (optimistic)
        Cache-->>UI: reflect new state
        UI->>User: toast "Branding saved"
    end

    User->>UI: Pick color & click Save
    UI->>TRPC: mutate spaces.update({ primaryColor })
    TRPC->>DB: update primaryColor (nullable)
    DB-->>TRPC: confirm
    TRPC->>Cache: update spaces.getCurrent
    Cache-->>UI: reflect new color
    UI->>User: toast "Branding saved"
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I nibble hues and pick a tone so bright,
I toggle branding on beneath the night,
Toasts sing "saved" like carrots in a row,
A paywall hums when free folks try to show,
PostHog notes our colorful rabbit glow.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a custom branding option. It directly relates to the core feature implemented across multiple files (new CustomBrandingSection component, translation keys, TRPC mutations, and UI integrations).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch luke/ral-1128-branding-settings

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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/trpc/routers/spaces.ts (1)

269-299: ⚠️ Potential issue | 🟠 Major

Avoid clobbering name during branding-only updates.

Because name is required and always written (Line 271 and Line 295), a color-only save from stale client data can overwrite a newer space rename.

💡 Suggested fix
 update: spaceProcedure
   .input(
-    z.object({
-      name: z.string().min(1).max(100),
+    z
+      .object({
+        name: z.string().min(1).max(100).optional(),
         primaryColor: z
           .string()
           .regex(/^#[0-9a-fA-F]{6}$/, "Invalid hex color")
           .nullable()
           .optional(),
-      }),
+      })
+      .refine(
+        (data) => data.name !== undefined || data.primaryColor !== undefined,
+        "At least one field must be provided",
+      ),
   )
   .mutation(async ({ ctx, input }) => {
@@
       await prisma.space.update({
         where: { id: ctx.space.id },
         data: {
-          name: input.name,
+          ...(input.name !== undefined && { name: input.name }),
           ...(input.primaryColor !== undefined && {
             primaryColor: input.primaryColor,
           }),
         },
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/trpc/routers/spaces.ts` around lines 269 - 299, The mutation
currently requires and always writes name which causes color-only updates to
clobber newer names; make the name field optional in the Zod input (change
z.string().min(1).max(100) to optional()) and mirror the primaryColor pattern in
the prisma.space.update call so name is only included in the data object when
input.name !== undefined; update the input schema and the update payload in the
mutation that uses defineAbilityForMember / memberAbility and
prisma.space.update accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx:
- Around line 54-60: The free-tier guard in handleToggle currently blocks all
toggles; change it so it only blocks attempts to turn branding off. Update the
condition in handleToggle (referencing isFree, newChecked, and showPayWall) to
call showPayWall() and return only when isFree && newChecked === false,
otherwise proceed to call toastProgress with updateShowBranding.mutateAsync({
showBranding: newChecked }).
- Around line 62-66: The handleSave flow calls updateSpace.mutateAsync but
doesn't refresh the cached space data, so UI consumers of spaces.getCurrent
won't see the new primaryColor; after the mutation completes in handleSave,
trigger a refresh/invalidation of the spaces.getCurrent cache (via your
trpc/react-query utils or equivalent) so that spaces.getCurrent returns the
updated space and theming updates immediately.

---

Outside diff comments:
In `@apps/web/src/trpc/routers/spaces.ts`:
- Around line 269-299: The mutation currently requires and always writes name
which causes color-only updates to clobber newer names; make the name field
optional in the Zod input (change z.string().min(1).max(100) to optional()) and
mirror the primaryColor pattern in the prisma.space.update call so name is only
included in the data object when input.name !== undefined; update the input
schema and the update payload in the mutation that uses defineAbilityForMember /
memberAbility and prisma.space.update accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fdb432f5-b895-4463-8a53-9b6d27d1d1b9

📥 Commits

Reviewing files that changed from the base of the PR and between 287c62c and 3668ac1.

📒 Files selected for processing (9)
  • apps/web/public/locales/en/app.json
  • apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx
  • apps/web/src/app/[locale]/(space)/settings/general/components/space-branding-form.tsx
  • apps/web/src/app/[locale]/(space)/settings/general/components/space-settings-form.tsx
  • apps/web/src/app/[locale]/(space)/settings/general/page-client.tsx
  • apps/web/src/features/billing/components/pay-wall-dialog.tsx
  • apps/web/src/features/space/client.tsx
  • apps/web/src/lib/use-toast-progress.ts
  • apps/web/src/trpc/routers/spaces.ts

Comment on lines +54 to +60
const handleToggle = (newChecked: boolean) => {
if (isFree) {
showPayWall();
return;
}
toastProgress(updateShowBranding.mutateAsync({ showBranding: newChecked }));
};
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 9, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Free-tier guard currently blocks turning branding off.

Line 55 applies the paywall guard regardless of target state, so free users with showBranding=true cannot disable it.

💡 Suggested fix
   const handleToggle = (newChecked: boolean) => {
-    if (isFree) {
+    if (newChecked && isFree) {
       showPayWall();
       return;
     }
     toastProgress(updateShowBranding.mutateAsync({ showBranding: newChecked }));
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleToggle = (newChecked: boolean) => {
if (isFree) {
showPayWall();
return;
}
toastProgress(updateShowBranding.mutateAsync({ showBranding: newChecked }));
};
const handleToggle = (newChecked: boolean) => {
if (newChecked && isFree) {
showPayWall();
return;
}
toastProgress(updateShowBranding.mutateAsync({ showBranding: newChecked }));
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx
around lines 54 - 60, The free-tier guard in handleToggle currently blocks all
toggles; change it so it only blocks attempts to turn branding off. Update the
condition in handleToggle (referencing isFree, newChecked, and showPayWall) to
call showPayWall() and return only when isFree && newChecked === false,
otherwise proceed to call toastProgress with updateShowBranding.mutateAsync({
showBranding: newChecked }).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

When a space gets downgraded we disable show branding so this shouldn't happen

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.

@lukevella, that makes sense — since downgrading automatically disables showBranding, a free-tier space can never have it enabled in the first place, so the guard is correctly placed. Thanks for the clarification!


✏️ Learnings added
Learnt from: lukevella
Repo: lukevella/rallly PR: 2321
File: apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx:54-60
Timestamp: 2026-04-09T10:38:54.240Z
Learning: In the Rallly codebase (`apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx`), when a space is downgraded from a paid tier to free, `showBranding` is automatically set to `false`. Therefore, a free-tier space can never have `showBranding=true`, and the paywall guard in `handleToggle` (blocking all toggles for `isFree` users) is intentional and correct — free users will never need to turn branding off.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +62 to +66
const handleSave = async () => {
const value = hexColor === DEFAULT_PRIMARY_COLOR ? null : hexColor;
toastProgress(
updateSpace.mutateAsync({ name: space.name, primaryColor: value }),
);
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 9, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Refresh spaces.getCurrent after saving color.

The save mutation doesn’t update or invalidate the space cache, so branding changes may not apply immediately in UI that reads spaces.getCurrent (e.g., theming in apps/web/src/features/space/client.tsx).

💡 Suggested fix
   const handleSave = async () => {
     const value = hexColor === DEFAULT_PRIMARY_COLOR ? null : hexColor;
     toastProgress(
-      updateSpace.mutateAsync({ name: space.name, primaryColor: value }),
+      updateSpace
+        .mutateAsync({ name: space.name, primaryColor: value })
+        .then(() => utils.spaces.getCurrent.invalidate()),
     );
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleSave = async () => {
const value = hexColor === DEFAULT_PRIMARY_COLOR ? null : hexColor;
toastProgress(
updateSpace.mutateAsync({ name: space.name, primaryColor: value }),
);
const handleSave = async () => {
const value = hexColor === DEFAULT_PRIMARY_COLOR ? null : hexColor;
toastProgress(
updateSpace
.mutateAsync({ name: space.name, primaryColor: value })
.then(() => utils.spaces.getCurrent.invalidate()),
);
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx
around lines 62 - 66, The handleSave flow calls updateSpace.mutateAsync but
doesn't refresh the cached space data, so UI consumers of spaces.getCurrent
won't see the new primaryColor; after the mutation completes in handleSave,
trigger a refresh/invalidation of the spaces.getCurrent cache (via your
trpc/react-query utils or equivalent) so that spaces.getCurrent returns the
updated space and theming updates immediately.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Any mutation invalidates all queries

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for global mutation invalidation configuration in TRPC or React Query setup
rg -n "invalidate" apps/web/src/trpc/ -A3 -B3
rg -n "onSuccess|onSettled|onMutate" apps/web/src/trpc/ -A3 -B3 | head -80
# Also check for queryClient or ReactQueryProvider config
rg -rn "invalidateQueries\|onSuccess.*invalidate" apps/web/src/ -A3 -B3 | head -60

Length of output: 787


@lukevella, confirmed — apps/web/src/trpc/client.ts has a global override that calls opts.queryClient.invalidateQueries() (with no filter) in the onSuccess of every mutation, so spaces.getCurrent will already be refreshed automatically. My suggestion was unnecessary. Apologies for the noise!


✏️ Learnings added
Learnt from: lukevella
Repo: lukevella/rallly PR: 2321
File: apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx:62-66
Timestamp: 2026-04-09T10:39:30.660Z
Learning: In the Rallly codebase (`apps/web/src/trpc/client.ts`), the TRPC client is configured with a global `useMutation` override that calls `opts.queryClient.invalidateQueries()` (no arguments) inside `onSuccess`. This means every tRPC mutation automatically invalidates all React Query caches, so explicit per-mutation cache invalidation is never needed.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/trpc/routers/spaces.ts`:
- Around line 269-278: The input schema currently requires name and always
writes it back, causing color-only branding saves to overwrite newer names;
update the z.object in the mutation (the .input schema defining name and
primaryColor) to make name optional (and/or nullable) and then change the update
logic (the handler that calls prisma/ctx update — e.g., the space update
mutation around the code that writes name on save) to only include name in the
update payload when it is present in the input. Ensure primaryColor can still be
saved independently and that the update call merges fields conditionally so a
missing name won’t overwrite the DB.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e14f9498-0d9e-4fc9-9896-13f5ef342851

📥 Commits

Reviewing files that changed from the base of the PR and between 3668ac1 and 54e54d2.

📒 Files selected for processing (3)
  • apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx
  • apps/web/src/features/space/client.tsx
  • apps/web/src/trpc/routers/spaces.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx (2)

99-104: Use cn() for conditional class composition.

Prefer cn() over ternary class strings for consistency with the design-system utility pattern.

Suggested change
+import { cn } from "@rallly/ui";
@@
-          <div
-            className={
-              !space.showBranding || disabled
-                ? "pointer-events-none opacity-50"
-                : undefined
-            }
-          >
+          <div
+            className={cn(
+              !space.showBranding || disabled && "pointer-events-none opacity-50",
+            )}
+          >

As per coding guidelines, "Use cn() from @rallly/ui to compose classes."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx
around lines 99 - 104, Replace the ternary className in the
CustomBrandingSection JSX with the design-system cn() utility: import cn from
"@rallly/ui" (or ensure it's already imported) and change the div's className to
use cn() to conditionally apply "pointer-events-none opacity-50" when
!space.showBranding || disabled; keep undefined/no class when false (e.g.,
className={cn({ "pointer-events-none opacity-50": !space.showBranding ||
disabled })} or cn(!space.showBranding || disabled && "pointer-events-none
opacity-50")). Ensure you update the component named CustomBrandingSection
accordingly.

58-58: Align PostHog event action naming to present tense.

Use a present-tense action in the event key to match the repository convention.

Suggested change
-      posthog?.capture("branding_settings:custom_branding_paywall_shown");
+      posthog?.capture("branding_settings:custom_branding_paywall_show");

As per coding guidelines, "Use the category:object_action pattern for PostHog event naming with lowercase, snake_case, and present-tense verbs..."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx
at line 58, The PostHog event name uses past tense
("branding_settings:custom_branding_paywall_shown"); update the string in the
posthog?.capture call to use present-tense and follow the category:object_action
pattern and snake_case (for example
"branding_settings:custom_branding_paywall_show" or
"branding_settings:custom_branding_paywall_view") so replace the existing event
key in the posthog?.capture invocation accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx:
- Around line 99-104: Replace the ternary className in the CustomBrandingSection
JSX with the design-system cn() utility: import cn from "@rallly/ui" (or ensure
it's already imported) and change the div's className to use cn() to
conditionally apply "pointer-events-none opacity-50" when !space.showBranding ||
disabled; keep undefined/no class when false (e.g., className={cn({
"pointer-events-none opacity-50": !space.showBranding || disabled })} or
cn(!space.showBranding || disabled && "pointer-events-none opacity-50")). Ensure
you update the component named CustomBrandingSection accordingly.
- Line 58: The PostHog event name uses past tense
("branding_settings:custom_branding_paywall_shown"); update the string in the
posthog?.capture call to use present-tense and follow the category:object_action
pattern and snake_case (for example
"branding_settings:custom_branding_paywall_show" or
"branding_settings:custom_branding_paywall_view") so replace the existing event
key in the posthog?.capture invocation accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a035780b-5d06-4cc7-9aa4-8ca556f87eff

📥 Commits

Reviewing files that changed from the base of the PR and between 54e54d2 and f03fa1b.

📒 Files selected for processing (2)
  • apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx
  • apps/web/src/trpc/routers/spaces.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/trpc/routers/spaces.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx (1)

99-104: Use cn() for conditional Tailwind class composition.

This conditional className should use cn() instead of a ternary string for consistency with project conventions.

♻️ Proposed refactor
+import { cn } from "@rallly/ui";
...
-          <div
-            className={
-              !space.showBranding || disabled
-                ? "pointer-events-none opacity-50"
-                : undefined
-            }
-          >
+          <div
+            className={cn(
+              (!space.showBranding || disabled) &&
+                "pointer-events-none opacity-50",
+            )}
+          >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx
around lines 99 - 104, The div's conditional className currently uses a ternary
string; replace it with the project's cn() helper to compose Tailwind classes
consistently: change the div's className to something like cn({
"pointer-events-none opacity-50": !space.showBranding || disabled }) (or include
other static classes as first args) and ensure the cn helper is imported (the
project's cn utility) so the conditional class is applied via cn instead of the
ternary; update the JSX in the custom-branding-section component where the div
uses space.showBranding and disabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/web/src/app/`[locale]/(space)/settings/general/components/custom-branding-section.tsx:
- Around line 99-104: The div's conditional className currently uses a ternary
string; replace it with the project's cn() helper to compose Tailwind classes
consistently: change the div's className to something like cn({
"pointer-events-none opacity-50": !space.showBranding || disabled }) (or include
other static classes as first args) and ensure the cn helper is imported (the
project's cn utility) so the conditional class is applied via cn instead of the
ternary; update the JSX in the custom-branding-section component where the div
uses space.showBranding and disabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2b136dbd-2096-4c83-b5e1-0edcf9456937

📥 Commits

Reviewing files that changed from the base of the PR and between f03fa1b and 7180d3e.

📒 Files selected for processing (1)
  • apps/web/src/app/[locale]/(space)/settings/general/components/custom-branding-section.tsx

@lukevella lukevella merged commit ca01712 into main Apr 9, 2026
13 checks passed
@lukevella lukevella deleted the luke/ral-1128-branding-settings branch April 9, 2026 11:30
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