Skip to main content

September 2, 2026

Centra size guide transposes the chart

Fashion size guides store measurements as rows. Shoppers need size rows on the product page. Flip the sheet once so the numbers match the layout.

Sander Korf3 min read

A Centra fashion PDP needs a size guide the shopper can open beside the chips: size labels down the left, Chest, Hip, Dimension across the top. That is what Figma handed me. Centra Measurement Charts store the opposite: Repeater X is sizes (horizontalLabels), Repeater Y is measurements (verticalLabels), and cell values live at values[measurementIndex][sizeIndex].

CSS grid hacks look right in Chrome. Safari put the Hip numbers under the wrong size. I do not want a size guide whose honesty depends on the browser.

The first ProductSizeGuideSheet was a raw Vaul Drawer: right edge on md+, bottom sheet on mobile, sticky header, scrollable table body, reduced-motion overlay. It worked. Then every other overlay on the storefront invented its own padding, width, and scroll region. The size guide was the canary.

Use the demo toggle Centra storage versus transposed UI. The cell numbers stay identical; only the axis contract changes. If Hip values jump columns when you flip modes, the mapper is wrong, not the browser.

Centra chart axes vs Figma size guide

Centra stores sizes on X and measurements on Y. Design wants size rows. Flip once in domain, not in CSS.

SizeChestHipDimension
XS84 cm90 cm62 cm
S88 cm94 cm64 cm
M92 cm98 cm66 cm
L98 cm104 cm68 cm

mapMeasurementTable built this from values[measurementIndex][sizeIndex]. Ready for ProductSizeGuideSheet.

Domain walks sizes, then fills cells

I moved the axis flip into mapMeasurementTable. It walks each size index and builds a row whose cells are verticalLabels.map((_, measurementIndex) => values[measurementIndex][sizeIndex]). Empty tables get skipped, and unit comes from displayUnit. A GraphQL fragment on product details feeds the mapper. Optional sizeGuide on the page view keeps the sheet off products without chart data, so the trigger beside the chips has nothing to open when the mapper skipped an empty table.

function mapMeasurementTable(chart: CentraMeasurementChart) {
	return chart.horizontalLabels.map((size, sizeIndex) => ({
		size,
		cells: chart.verticalLabels.map((_, measurementIndex) => chart.values[measurementIndex]?.[sizeIndex] ?? ''),
		unit: chart.displayUnit,
	}));
}

After that I render a table. The numbers already sit in the axes Figma asked for, so the sheet is not doing a grid trick that still reads the PIM the wrong way around.

Known keys localize without a deploy

The shop mapper translates known column keys to i18n (Chest, Hip, Dimension) and passes unknown Centra labels through unchanged. Designers can add a new measurement in Centra without a deploy, and the column header still reads human.

A typo in the PIM still shows up as a typo in the sheet. I am not going to invent a second mapping layer that pretends every Centra string is in the dictionary. Known keys localize, unknown keys stay as authored, and empty tables still get skipped so a product with a broken chart does not open an empty drawer.

Shared Panel after the table was honest

Later the same sheet moved onto a shared Panel molecule: Sheet on desktop, Drawer on mobile, so every overlay shared header padding and scroll regions. Size selector gained a size-guide trigger beside the chips. Header and scroll body stayed the same because the table was already in design axes.

The raw Vaul drawer was fine for one overlay. The second overlay copied padding by eye. The third invented a width token. I moved the table into design axes first, then hung the sheet on the shared shell.

Shoppers compare their body to a row, not to a measurement index buried in Centra's storage. Transpose once in domain, render a normal table, and Safari stops being the QA department.

I still flinch when a screenshot of a CSS-rotated chart looks done. Safari will tell on you.


Happy coding! Sander