The table refused to become cards
Turning a product comparison into stacked cards made the narrow layout prettier and the comparison task dramatically worse.
The comparison table was the part of C62Y that would not become responsive politely.
Each row was a specification. Each column was a product. A person could hold a property steady and scan across alternatives. The structure was dense because the task was dense.
On a narrow screen, all columns could not remain visible at a readable size. I tried the solution that appeared in many responsive examples: transform each product column into a card, repeat the specification labels, and stack the cards vertically.
The result fit perfectly. It also stopped being a comparison.
A reader could understand one product at a time. To compare weight across four models, they had to find the weight row in one card, remember it, scroll to the next card, find the same row, and repeat. The design preserved every value and broke the relationship that made the values useful.
The table did not resist responsiveness. It resisted being redesigned as a different task.
The shape carried meaning
A table is not merely a visual grid. Row and column relationships are part of its content.
In C62Y, rows held comparable properties. Columns held products. Headers named both axes. The intersection carried one value whose meaning depended on those headers.
The card layout preserved the product grouping and weakened the property grouping. Every card repeated labels, so it remained semantically readable in isolation. The cross-product scan disappeared.
That loss was easy to miss in a design review because the cards looked spacious and complete. It became obvious when I used a task:
Find the lightest product that supports the required capacity and fits within the available width.
In the table, the eye could move across three rows while keeping products aligned. In cards, the task became repeated search plus memory.
The lesson was not that cards are bad. A card is useful when one item is the unit of reading. A comparison table is useful when relationships across items are the unit of reading. Responsive design should not choose the first shape because it stacks easily.
My next approach showed only the “most important” columns at narrow widths. A control allowed the reader to reveal the rest.
This kept the grid and created a different problem. Importance belonged to the person’s decision, not the viewport. The two products I selected as defaults were not necessarily the ones being compared. Hiding columns also changed the apparent best choice.
I experimented with a product picker that let the reader choose two visible columns. It supported focused comparison and added setup cost. Switching a column made the spatial position of every value change, which made repeated exploration harder.
The picker remained useful as an optional narrowing tool for large selections. It did not become the only narrow-screen representation.
The full comparison needed to remain available, even if the viewport could reveal only part at once.
Local horizontal movement was the honest compromise
I had treated horizontal scrolling as evidence that a responsive design had failed. For the table, bounded horizontal movement preserved more of the task than the “responsive” transformations.
The comparison entered its own overflow region. The page itself never became wider than the viewport. Headings, instructions, and surrounding content remained stable while the two-dimensional object could be explored on its own axis.
The region received a concise instruction before the table, not a decorative hint after the user became stuck. Its boundary and slight edge treatment made additional columns discoverable without relying on a fake gradient that disappeared in high-contrast settings.
Keyboard users could focus the region and scroll it. Touch users could pan directly. Wheel and trackpad behavior remained ordinary. I avoided building custom drag behavior that would compete with selection and platform scrolling.
This was localized compromise: horizontal movement where the content was inherently two-dimensional, not a general excuse for an overflowing layout.
The first column was context
Once the table could move horizontally, the row labels disappeared while inspecting later product columns. A value such as 42 kg had little meaning without its property.
Modern sticky positioning was not yet a dependable universal answer in my 2012 target environment. I used a layered approach. Capable layouts kept the first column visually stable with the available positioning techniques. The baseline allowed it to move and repeated enough context in grouped views to remain usable.
I also tested a two-row compact header that placed abbreviated property context with each value. It prevented loss of meaning and made the cells noisy. The final baseline favored clear row headers and a small number of selected products by default, with the complete table available.
No option erased the spatial constraint. The design documented which support level received a fixed label column and which received an ordinary scrollable table.
Progressive enhancement meant preserving the comparison before improving its convenience.
Headers needed real associations
The first markup used styled div elements because they were easier to rearrange into cards. That decision made the two-dimensional relationships visual only.
I returned to a real table with column and row headers. Header scope and document structure allowed nonvisual navigation to announce which property and product framed a value.
The product heading included model and manufacturer without repeating a long marketing name in every announcement. Units lived with values or in the property label according to whether they were consistent across the row.
Empty cells carried an explicit reason rather than a dash whose meaning had to be guessed. A screen reader could distinguish not applicable from not supplied, just as the visible legend did.
Responsive enhancement operated around the table rather than rewriting it into unrelated markup. One semantic structure supported the complete baseline and the bounded overflow presentation.
This reduced another form of duplication: I no longer needed separate desktop-table and mobile-card HTML with two accessibility models and two chances to drift.
Numbers aligned cleanly and encouraged comparison even when their units or measurement conditions differed.
I normalized units for display where conversion was defensible and preserved the source measurement. A converted value used appropriate precision. Values measured under different conditions received a visible marker and nearby explanation.
The table could now say that two values occupied the same row while their test conditions limited direct comparison. Without that context, visual alignment would make a stronger claim than the data.
Decimal alignment helped scanning for some numeric properties. It did not justify formatting every value as a bare number. A reader still needed units, ranges, qualifiers, and absence states.
The design of a comparison surface includes designing where comparison should slow down.
Row order was a product decision
The source spreadsheet supplied a property order inherited from data entry. The table reproduced it and forced readers through manufacturer, internal code, and reference dimensions before the few properties that usually shaped a choice.
The content model introduced category-specific decision fields. The comparison led with those, followed by compatibility constraints and then reference details.
I kept a stable order within a category so repeated use built familiarity. Alphabetical sorting looked neutral and scattered related properties. Sorting by how complete the data was rewarded the catalogue rather than the decision.
Some properties formed groups with headings. Those group rows were marked as structure and not fake product values. Visually, they provided breathing room. Semantically, they made the table navigable by sections.
The row order improved the narrow experience without changing width at all. A reader reached useful relationships sooner, which reduced unnecessary horizontal and vertical exploration.
The selection state needed an address
The first comparison existed only after selecting products in JavaScript. Refreshing lost the selection. Sharing the page could not reproduce it.
I encoded product identifiers in the URL. The server rendered the selected comparison directly, and the enhancement layer allowed adding or removing products without losing the address.
The URL used stable product IDs rather than display names. It imposed a maximum selection count that matched the table’s useful range and the server’s response limits. An invalid or retired ID produced a visible message without removing valid selections.
This made comparison a real page rather than a transient client state. A person could bookmark it, send it, print it, or open it with scripts disabled.
The same principle had improved navigation: durable location and selection belonged in URLs, while transient disclosure and scroll position did not.
With no products selected, the early page displayed an empty table shell. It made the implementation visible and gave the reader no clear next step.
The empty state became a product selector organized by category and recent catalogue context. It explained that comparison worked best among products with shared decision fields. Adding the first product established the category; incompatible choices remained possible but carried a warning.
The selector did not preload the entire catalogue into the page. It used server-rendered results and a small enhancement for filtering. Selected items remained ordinary removable entries.
This state was not part of the desktop comp and became one of the most used entry points during testing. Designing it clarified the comparison’s scope before any values appeared.
Empty state design exposed another hidden assumption: I had thought of the table as a destination and not the process by which a person built a meaningful comparison.
Printing exposed a different width
The comparison was likely to be printed or saved, so I could not treat paper as a screenshot of the scroll position.
The print stylesheet removed interactive controls, included all selected columns, repeated table headers across pages where supported, and used landscape orientation as a suggestion rather than a guarantee.
For too many columns, the print view offered a focused selection before printing. It did not scale text to illegibility to preserve one page.
URLs for manuals and sources appeared in a reference section instead of expanding every link inline inside the table. Color-dependent differences gained text labels.
Printing reinforced the central lesson. A responsive table does not have one perfect transformation. It needs a stable semantic core and presentations that preserve the comparison under different constraints.
Performance belonged to the table too
The comparison page initially loaded full-size product images above every column. Four images dominated the transfer before the user reached the specifications.
I replaced them with small identification thumbnails and made the detailed image a link. The table did not need photographic inspection to support numeric comparison.
The page rendered only selected products and their comparison fields. The selector fetched results separately rather than shipping the complete catalogue as a client-side dataset.
JavaScript enhanced selection and did not calculate layout for every cell. Native table layout and overflow did most of the work. A clever script that measured columns on resize caused visible delay and disagreement after fonts loaded, so I removed it.
The simpler implementation was faster and more resilient. The browser already knew how to lay out a table; my job was to give it usable content and boundaries.
I tested the comparison with a screen reader after the semantic table returned. Correct elements improved the experience immediately and did not make the result automatically concise.
Long product headers repeated with every value. Unit text repeated inconsistently. Group rows were announced as data because I had used an ordinary cell with bold styling. A visually empty placeholder had no explanation.
I shortened accessible product headers to the stable model identity and left additional marketing text in the visible summary. Row headers included the unit only when it was consistent for the whole property. Section rows used header relationships appropriate to their group. Explicit absence text replaced decorative dashes.
The comparison page included a heading naming the selected products before the table. A caption explained the table’s purpose without duplicating the visible page title. The overflow wrapper did not interfere with table navigation and received a label only for the scrolling region itself.
I navigated by row and column, changed the selection, and returned to the table. After adding a product, focus initially jumped to the top of the page because the server response replaced the region. The enhanced path moved focus to a short update message and offered a direct link back to the comparison heading.
The test reminded me that semantic correctness is a starting condition. Density, repetition, updates, and recovery still need product judgment. A card version might have produced a simpler linear reading and would have sacrificed cross-column navigation that assistive technology can support when the table is marked up well.
Different reading modes did not require different data. They required the shared relationships to be exposed clearly enough that each mode could navigate them.
Comparison history was deliberately small
I considered saving every selected set in local storage. The feature made the demo feel personalized and created unexplained stale state across visits.
Because selections already lived in URLs, bookmarks and browser history provided durable recovery. I added a small recent-comparisons list only after giving each entry a visible date and selected-product summary, with a clear removal action.
The list stored URLs, not copied product data. Opening an old comparison loaded current catalogue values and identified a retired product if necessary. It did not present an old cached number as current simply because the selection was remembered.
This kept selection history separate from data history. A later audit-grade tool might need an immutable snapshot of the values compared at a decision moment. C62Y did not claim that contract. Its recent list helped navigation and remained honest about current data.
The card version was not wasted. It made product-by-product reading pleasant and became a useful summary view before comparison.
The mistake was making it replace the table. The catalogue could offer cards for discovery and a table for cross-product decisions. One representation did not need to win every context.
I reused the card layout for selected-product summaries above the comparison on wide screens and as the empty-state picker. Once the card stopped pretending to preserve row comparison, its strengths became clear.
This changed how I evaluated responsive transformations. A different visual shape may support a different task. If so, it should be introduced as that task, not described as the same information made mobile-friendly.
The first overflow region had no visible boundary, so touch users sometimes moved the page and did not discover later columns. The next version used a heavy inner shadow that looked like disabled content. A quieter border, instruction, and partially visible next column proved more understandable.
The fixed first column once sat above links in scrolled cells because its background was transparent. It also consumed too much width when property labels were long. I set a readable maximum, allowed labels to wrap, and tested focus outlines where sticky and moving cells overlapped.
The product picker allowed removing the last product and left an empty table with stale row headings. It returned to the intentional empty state instead.
One unit conversion rounded two distinct values to the same display. The comparison retained source precision rules and exposed the original measurement in details.
Every failure came from a relationship: between viewport and table, label and value, normalized and source data, selection and URL, or visible region and hidden columns. Fixing individual pixels without naming the relationship would have produced another fragile exception.
The compromise remained visible
The final table still required horizontal movement on a phone. I did not describe it as a seamless solution.
The page explained the interaction, kept row context where capability allowed, offered focused selection, preserved semantic headers, supported keyboard scrolling, and provided complete print output. It made the constrained behavior usable and honest.
Responsive design is sometimes presented as the elimination of scrolling, overflow, and compromise. A real interface has content whose dimensionality does not fit every viewport. The responsible design can localize that conflict, preserve meaning, and reveal the cost.
The table refused to become cards because the table’s value was not the number of cells it contained. Its value was the spatial relationship among them.
That realization carried beyond tables. Later dashboards, incident timelines, evidence graphs, and agent traces would all tempt me to replace dense relational views with isolated cards. Sometimes cards improved reading. Sometimes they dissolved the exact relationship the interface existed to show.
The next C62Y investigation moved from layout cost to delivery cost. Product images could shrink visually while remaining enormous over the network. CSS could make the box responsive and leave the transfer untouched.