@@ -11,27 +11,37 @@ export interface ComparisonTableProps {
1111}
1212
1313/**
14- * Pins the row-label column during horizontal scroll at tablet width and up
15- * (the standard pattern for responsive data tables, e.g. Stripe/GitHub/Notion
16- * comparison tables) so a reader keeps row context while scrolling to see the
17- * Sim/competitor values. Below `sm` the table switches to a stacked layout
18- * instead (see `MOBILE_STACK`), so sticky positioning is scoped to `sm:` only.
19- * The shadow is a permanent CSS-only affordance (no scroll-position JS) so
20- * this stays a zero-hydration server component.
14+ * Pins the row-label column during horizontal scroll on genuinely spacious
15+ * viewports (the standard pattern for responsive data tables, e.g.
16+ * Stripe/GitHub/Notion comparison tables) so a reader keeps row context while
17+ * scrolling to see the Sim/competitor values. Below `lg` (this page's own
18+ * tablet-and-below tier, per `.claude/rules` for this route group) the table
19+ * switches to a stacked layout instead (see `MOBILE_STACK_*`) rather than
20+ * relying on horizontal scroll at a width too narrow to render a 3-column
21+ * table comfortably, so sticky positioning is scoped to `lg:` only. The
22+ * shadow is a permanent CSS-only affordance (no scroll-position JS) so this
23+ * stays a zero-hydration server component.
2124 */
22- const STICKY_LABEL_COL = 'sm :sticky sm :left-0 sm :z-10 sm :shadow-[2px_0_4px_-2px_rgba(0,0,0,0.08)]'
25+ const STICKY_LABEL_COL = 'lg :sticky lg :left-0 lg :z-10 lg :shadow-[2px_0_4px_-2px_rgba(0,0,0,0.08)]'
2326
2427/**
25- * Below `sm ` (640px ) a 3-column grid has no room to be legible even with a
26- * pinned label column , so each fact instead stacks as label -> Sim's value ->
27- * the competitor's value, with a small name tag on each value since the
28- * column headers are no longer directly above. Applied to the label cell.
28+ * Below `lg ` (1024px ) a 3-column grid doesn't reliably have room to be
29+ * legible , so each fact instead stacks as label -> Sim's value -> the
30+ * competitor's value, with a small name tag on each value since the column
31+ * headers are no longer directly above. Applied to the label cell.
2932 */
30- const MOBILE_STACK_LABEL = 'max-sm :border-r-0 max-sm :border-b-0 max-sm :pt-3 max-sm :pb-1'
33+ const MOBILE_STACK_LABEL = 'max-lg :border-r-0 max-lg :border-b-0 max-lg :pt-3 max-lg :pb-1'
3134
32- /** Applied to a value cell (Sim's or the competitor's) in the stacked mobile layout. */
35+ /**
36+ * Applied to a value cell (Sim's or the competitor's) in the stacked mobile
37+ * layout. `items-stretch` overrides the cell's base `items-center` (which
38+ * would otherwise shrink-wrap and center each child horizontally in a
39+ * flex-col): stretching gives the name tag and the value their own
40+ * full-width box to left-align and truncate within, instead of a
41+ * content-sized box with no boundary to clip against.
42+ */
3343const MOBILE_STACK_VALUE =
34- 'max-sm :flex-col max-sm :items-start max-sm :gap-0.5 max-sm :border-r-0 max-sm :px-4'
44+ 'max-lg :flex-col max-lg :items-stretch max-lg :gap-0.5 max-lg :border-r-0 max-lg :px-4'
3545
3646function ColumnHeader ( {
3747 name,
@@ -72,15 +82,15 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
7282 < div
7383 role = 'table'
7484 aria-label = { `Sim vs ${ competitor . name } feature comparison` }
75- className = 'grid grid-cols-1 sm :min-w-[560px] sm :grid-cols-[minmax(140px,max-content)_1fr_1fr]'
85+ className = 'grid grid-cols-1 lg :min-w-[560px] lg :grid-cols-[minmax(140px,max-content)_1fr_1fr]'
7686 >
7787 < div className = 'contents' role = 'row' >
7888 < div
7989 role = 'columnheader'
8090 className = { cn (
8191 'flex min-w-0 flex-col justify-center border-[var(--border)] border-r border-b bg-[var(--surface-1)] px-4 py-4' ,
8292 STICKY_LABEL_COL ,
83- 'max-sm :border-r-0'
93+ 'max-lg :border-r-0'
8494 ) }
8595 >
8696 < span className = 'truncate font-medium text-[var(--text-primary)] text-base' >
@@ -119,7 +129,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
119129 className = { cn (
120130 'border-[var(--border)] border-r bg-[var(--surface-1)] px-4 py-2' ,
121131 STICKY_LABEL_COL ,
122- 'max-sm :border-r-0' ,
132+ 'max-lg :border-r-0' ,
123133 sectionIdx > 0 && 'border-[var(--border-1)] border-t'
124134 ) }
125135 >
@@ -130,7 +140,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
130140 < div
131141 role = 'presentation'
132142 className = { cn (
133- 'col-span-2 bg-[var(--surface-1)] max-sm :hidden' ,
143+ 'col-span-2 bg-[var(--surface-1)] max-lg :hidden' ,
134144 sectionIdx > 0 && 'border-[var(--border-1)] border-t'
135145 ) }
136146 />
@@ -152,7 +162,7 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
152162 isNotLastRow && 'border-[var(--border-1)] border-b'
153163 ) }
154164 >
155- < span className = 'text-[var(--text-body)] text-small max-sm :font-medium max-sm :text-[var(--text-primary)]' >
165+ < span className = 'text-[var(--text-body)] text-small max-lg :font-medium max-lg :text-[var(--text-primary)]' >
156166 { row . label }
157167 </ span >
158168 </ div >
@@ -161,11 +171,11 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
161171 className = { cn (
162172 'flex min-w-0 items-center gap-1 border-[var(--border)] border-r bg-[var(--surface-2)] px-3 py-2.5' ,
163173 MOBILE_STACK_VALUE ,
164- 'max-sm :border-b-0 max-sm :pt-1 max-sm :pb-1' ,
174+ 'max-lg :border-b-0 max-lg :pt-1 max-lg :pb-1' ,
165175 isNotLastRow && 'border-[var(--border-1)] border-b'
166176 ) }
167177 >
168- < span className = 'font-medium text-[var(--text-muted)] text-caption sm :hidden' >
178+ < span className = 'font-medium text-[var(--text-muted)] text-caption lg :hidden' >
169179 { sim . name }
170180 </ span >
171181 < FactValue fact = { simFact } />
@@ -175,11 +185,11 @@ export function ComparisonTable({ sim, competitor }: ComparisonTableProps) {
175185 className = { cn (
176186 'flex min-w-0 items-center gap-1 bg-[var(--surface-2)] px-3 py-2.5' ,
177187 MOBILE_STACK_VALUE ,
178- 'max-sm :pt-1 max-sm :pb-3' ,
188+ 'max-lg :pt-1 max-lg :pb-3' ,
179189 isNotLastRow && 'border-[var(--border-1)] border-b'
180190 ) }
181191 >
182- < span className = 'font-medium text-[var(--text-muted)] text-caption sm :hidden' >
192+ < span className = 'font-medium text-[var(--text-muted)] text-caption lg :hidden' >
183193 { competitor . name }
184194 </ span >
185195 < FactValue fact = { competitorFact } />
0 commit comments