From cf20aee0988a70ea0b1c804c5c9d91169901be5f Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 3 Jul 2026 20:23:35 -0700 Subject: [PATCH 1/4] fix(compare): swap LangChain logo, fix comparison-table horizontal scroll Replace LangChainIcon's path with the official brand mark (light-blue link icon) instead of the prior monochrome recreation. The comparison table's grid cells were missing min-w-0, so a grid item without it sizes to its content's max-content width instead of respecting its column's fr track, pushing the whole table wider than its container and forcing a horizontal scrollbar. Add min-w-0 to every grid cell/header, and size the row-label column to minmax(140px, max-content) instead of a guessed fr ratio, so it's exactly as wide as its longest label ("Vetted first-party integrations") needs and no wider, leaving the Sim/competitor value columns their full share. --- .../comparison-table/comparison-table.tsx | 16 +++++++++------- apps/sim/components/icons.tsx | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx index 70a21ae4e24..796c920aff8 100644 --- a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx +++ b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx @@ -49,15 +49,17 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
- Compare - + + Compare + + {sim.name} vs {competitor.name}
@@ -115,7 +117,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
@@ -124,7 +126,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
@@ -133,7 +135,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 9505f29ee94..ea1eeea8a10 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -1565,8 +1565,8 @@ export function LangChainIcon(props: SVGProps) { return ( ) From e4949dd4fd267a068f0ddc3fb6126e5a6d144d03 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 3 Jul 2026 20:30:30 -0700 Subject: [PATCH 2/4] fix(compare): stacked mobile layout for the comparison table Below sm (640px) a 3-column table has no room to be legible even with the sticky label column, so switch to the standard responsive-table pattern instead: each fact stacks as label -> Sim's value -> the competitor's value, each value tagged with its product name since the column headers are no longer directly above. Pure CSS (max-sm:/sm: variants), no JS, keeping this a zero-hydration server component. --- .../comparison-table/comparison-table.tsx | 55 +++++++++++++++++-- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx index 796c920aff8..b04d282399b 100644 --- a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx +++ b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx @@ -10,6 +10,29 @@ export interface ComparisonTableProps { competitor: CompetitorProfile } +/** + * Pins the row-label column during horizontal scroll at tablet width and up + * (the standard pattern for responsive data tables, e.g. Stripe/GitHub/Notion + * comparison tables) so a reader keeps row context while scrolling to see the + * Sim/competitor values. Below `sm` the table switches to a stacked layout + * instead (see `MOBILE_STACK`), so sticky positioning is scoped to `sm:` only. + * The shadow is a permanent CSS-only affordance (no scroll-position JS) so + * this stays a zero-hydration server component. + */ +const STICKY_LABEL_COL = 'sm:sticky sm:left-0 sm:z-10 sm:shadow-[2px_0_4px_-2px_rgba(0,0,0,0.08)]' + +/** + * Below `sm` (640px) a 3-column grid has no room to be legible even with a + * pinned label column, so each fact instead stacks as label -> Sim's value -> + * the competitor's value, with a small name tag on each value since the + * column headers are no longer directly above. Applied to the label cell. + */ +const MOBILE_STACK_LABEL = 'max-sm:border-r-0 max-sm:border-b-0 max-sm:pt-3 max-sm:pb-1' + +/** Applied to a value cell (Sim's or the competitor's) in the stacked mobile layout. */ +const MOBILE_STACK_VALUE = + 'max-sm:flex-col max-sm:items-start max-sm:gap-0.5 max-sm:border-r-0 max-sm:px-4' + function ColumnHeader({ name, iconTile, @@ -49,12 +72,16 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
Compare @@ -91,6 +118,8 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) { role='columnheader' className={cn( 'border-[var(--border)] border-r bg-[var(--surface-1)] px-4 py-2', + STICKY_LABEL_COL, + 'max-sm:border-r-0', sectionIdx > 0 && 'border-[var(--border-1)] border-t' )} > @@ -101,7 +130,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
0 && 'border-[var(--border-1)] border-t' )} /> @@ -118,27 +147,41 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) { role='rowheader' className={cn( 'flex min-w-0 items-center border-[var(--border)] border-r bg-[var(--surface-1)] px-4 py-2.5', + STICKY_LABEL_COL, + MOBILE_STACK_LABEL, isNotLastRow && 'border-[var(--border-1)] border-b' )} > - {row.label} + + {row.label} +
+ + {sim.name} +
+ + {competitor.name} +
From d32aca28be234eb6aa36dffa67ca210572593d11 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 3 Jul 2026 20:37:50 -0700 Subject: [PATCH 3/4] fix(compare): fix SourceLink inline-anchor overflow, align table breakpoint Root cause of the persistent table-overflow/mobile-scroll issues: SourceLink renders a plain with no explicit display, so it defaults to 'display: inline'. min-width and truncate are no-ops on inline elements, so any fact with a source (nearly all of them) ignored its flex/grid parent's width constraint and could force the whole row (and thus the whole table) wider than intended, regardless of any container-level min-w-0/max-content fix. Give the anchor 'block min-w-0' so width constraints and truncation actually cascade down to the wrapped value. Also aligns the table's mobile-stack breakpoint from an ad hoc sm (640px) to lg (1024px), matching this route group's own tablet-and-below convention (.claude/rules for the (landing) group), and fixes the stacked mobile cells to override the cell's base items-center with items-stretch so the name tag and value get a real full-width box to truncate within instead of shrinking to their own content size with no boundary. --- .../comparison-table/comparison-table.tsx | 58 +++++++++++-------- .../components/source-info/source-info.tsx | 4 +- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx index b04d282399b..1f222d0c696 100644 --- a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx +++ b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx @@ -11,27 +11,37 @@ export interface ComparisonTableProps { } /** - * Pins the row-label column during horizontal scroll at tablet width and up - * (the standard pattern for responsive data tables, e.g. Stripe/GitHub/Notion - * comparison tables) so a reader keeps row context while scrolling to see the - * Sim/competitor values. Below `sm` the table switches to a stacked layout - * instead (see `MOBILE_STACK`), so sticky positioning is scoped to `sm:` only. - * The shadow is a permanent CSS-only affordance (no scroll-position JS) so - * this stays a zero-hydration server component. + * Pins the row-label column during horizontal scroll on genuinely spacious + * viewports (the standard pattern for responsive data tables, e.g. + * Stripe/GitHub/Notion comparison tables) so a reader keeps row context while + * scrolling to see the Sim/competitor values. Below `lg` (this page's own + * tablet-and-below tier, per `.claude/rules` for this route group) the table + * switches to a stacked layout instead (see `MOBILE_STACK_*`) rather than + * relying on horizontal scroll at a width too narrow to render a 3-column + * table comfortably, so sticky positioning is scoped to `lg:` only. The + * shadow is a permanent CSS-only affordance (no scroll-position JS) so this + * stays a zero-hydration server component. */ -const STICKY_LABEL_COL = 'sm:sticky sm:left-0 sm:z-10 sm:shadow-[2px_0_4px_-2px_rgba(0,0,0,0.08)]' +const STICKY_LABEL_COL = 'lg:sticky lg:left-0 lg:z-10 lg:shadow-[2px_0_4px_-2px_rgba(0,0,0,0.08)]' /** - * Below `sm` (640px) a 3-column grid has no room to be legible even with a - * pinned label column, so each fact instead stacks as label -> Sim's value -> - * the competitor's value, with a small name tag on each value since the - * column headers are no longer directly above. Applied to the label cell. + * Below `lg` (1024px) a 3-column grid doesn't reliably have room to be + * legible, so each fact instead stacks as label -> Sim's value -> the + * competitor's value, with a small name tag on each value since the column + * headers are no longer directly above. Applied to the label cell. */ -const MOBILE_STACK_LABEL = 'max-sm:border-r-0 max-sm:border-b-0 max-sm:pt-3 max-sm:pb-1' +const MOBILE_STACK_LABEL = 'max-lg:border-r-0 max-lg:border-b-0 max-lg:pt-3 max-lg:pb-1' -/** Applied to a value cell (Sim's or the competitor's) in the stacked mobile layout. */ +/** + * Applied to a value cell (Sim's or the competitor's) in the stacked mobile + * layout. `items-stretch` overrides the cell's base `items-center` (which + * would otherwise shrink-wrap and center each child horizontally in a + * flex-col): stretching gives the name tag and the value their own + * full-width box to left-align and truncate within, instead of a + * content-sized box with no boundary to clip against. + */ const MOBILE_STACK_VALUE = - 'max-sm:flex-col max-sm:items-start max-sm:gap-0.5 max-sm:border-r-0 max-sm:px-4' + 'max-lg:flex-col max-lg:items-stretch max-lg:gap-0.5 max-lg:border-r-0 max-lg:px-4' function ColumnHeader({ name, @@ -72,7 +82,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
@@ -119,7 +129,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) { className={cn( 'border-[var(--border)] border-r bg-[var(--surface-1)] px-4 py-2', STICKY_LABEL_COL, - 'max-sm:border-r-0', + 'max-lg:border-r-0', sectionIdx > 0 && 'border-[var(--border-1)] border-t' )} > @@ -130,7 +140,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
0 && 'border-[var(--border-1)] border-t' )} /> @@ -152,7 +162,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) { isNotLastRow && 'border-[var(--border-1)] border-b' )} > - + {row.label}
@@ -161,11 +171,11 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) { className={cn( 'flex min-w-0 items-center gap-1 border-[var(--border)] border-r bg-[var(--surface-2)] px-3 py-2.5', MOBILE_STACK_VALUE, - 'max-sm:border-b-0 max-sm:pt-1 max-sm:pb-1', + 'max-lg:border-b-0 max-lg:pt-1 max-lg:pb-1', isNotLastRow && 'border-[var(--border-1)] border-b' )} > - + {sim.name} @@ -175,11 +185,11 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) { className={cn( 'flex min-w-0 items-center gap-1 bg-[var(--surface-2)] px-3 py-2.5', MOBILE_STACK_VALUE, - 'max-sm:pt-1 max-sm:pb-3', + 'max-lg:pt-1 max-lg:pb-3', isNotLastRow && 'border-[var(--border-1)] border-b' )} > - + {competitor.name} diff --git a/apps/sim/app/(landing)/comparison/components/source-info/source-info.tsx b/apps/sim/app/(landing)/comparison/components/source-info/source-info.tsx index fba0e4debd8..200d1c35aaf 100644 --- a/apps/sim/app/(landing)/comparison/components/source-info/source-info.tsx +++ b/apps/sim/app/(landing)/comparison/components/source-info/source-info.tsx @@ -1,7 +1,7 @@ 'use client' import type { ReactNode } from 'react' -import { Tooltip } from '@sim/emcn' +import { cn, Tooltip } from '@sim/emcn' import type { FactSource } from '@/lib/compare/data' export interface SourceLinkProps { @@ -29,7 +29,7 @@ export function SourceLink({ source, children, className }: SourceLinkProps) { target='_blank' rel='noopener noreferrer' aria-label={`${source.label} (opens source)`} - className={className} + className={cn('block min-w-0', className)} > {children}
From 8dcad87b682aa4ad6df103658e366405e6fe4e97 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 3 Jul 2026 20:45:22 -0700 Subject: [PATCH 4/4] fix(compare): add min-w-0 to ColumnHeader per Greptile review ColumnHeader (the Sim/competitor logo header cells in the two fr columns) still had default min-width: auto, so an unusually long competitor name could size the header to its min-content width and push the grid wider than its column allows, same root cause as the rows already fixed. --- .../components/comparison-table/comparison-table.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx index 1f222d0c696..2b13db94836 100644 --- a/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx +++ b/apps/sim/app/(landing)/comparison/components/comparison-table/comparison-table.tsx @@ -55,12 +55,14 @@ function ColumnHeader({ return (
{iconTile} - {name} + + {name} +
) }