From ba1c9fccfa190dba2e51916d2a2c7655a4da6496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Mon, 29 Jun 2026 10:37:33 +0200 Subject: [PATCH] Fix CardInset padding decoupling and logo a11y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address PR review feedback identified by cubic: - Re-key CardInset off the Card's resolved padding (shared via context) instead of size, so insets line up even when padding is decoupled from size via the padding prop; padding="none" becomes a no-op. - Give ProboLogo a default aria-label so its role="img" SVG has an accessible name when used standalone; callers can still override it. - Make the ProboLogo story render spread args so Storybook controls work. Signed-off-by: Émile Ré --- packages/ui/src/v2/Card/Card.tsx | 2 +- packages/ui/src/v2/Card/CardInset.tsx | 9 +-- packages/ui/src/v2/Card/context.ts | 6 +- packages/ui/src/v2/Card/variants.ts | 59 ++++++++++--------- .../ui/src/v2/ProboLogo/ProboLogo.stories.tsx | 5 +- packages/ui/src/v2/ProboLogo/ProboLogo.tsx | 1 + 6 files changed, 45 insertions(+), 37 deletions(-) diff --git a/packages/ui/src/v2/Card/Card.tsx b/packages/ui/src/v2/Card/Card.tsx index 2c8ac2df5..d394d4782 100644 --- a/packages/ui/src/v2/Card/Card.tsx +++ b/packages/ui/src/v2/Card/Card.tsx @@ -28,7 +28,7 @@ export function Card(props: CardProps) { return (
- {children} + {children}
); } diff --git a/packages/ui/src/v2/Card/CardInset.tsx b/packages/ui/src/v2/Card/CardInset.tsx index f0abc81af..d5d3badbb 100644 --- a/packages/ui/src/v2/Card/CardInset.tsx +++ b/packages/ui/src/v2/Card/CardInset.tsx @@ -23,11 +23,12 @@ export type CardInsetProps = ComponentProps<"div"> & { }; // Content that bleeds past the Card's padding to its edges (Radix "Inset"), -// e.g. a cover image. Negates the padding for the parent Card's size, read from -// context, so it always lines up. +// e.g. a cover image. Negates the parent Card's resolved padding (read from +// context) so it always lines up, even when padding is decoupled from size. With +// `padding="none"` there is nothing to negate, so this is a no-op wrapper. export function CardInset(props: CardInsetProps) { const { side = "all", className, ...rest } = props; - const size = useCardContext(); + const padding = useCardContext(); - return
; + return
; } diff --git a/packages/ui/src/v2/Card/context.ts b/packages/ui/src/v2/Card/context.ts index 0329db513..f468e80de 100644 --- a/packages/ui/src/v2/Card/context.ts +++ b/packages/ui/src/v2/Card/context.ts @@ -15,9 +15,11 @@ import { createContext, useContext } from "react"; export type CardSize = 1 | 2 | 3 | 4 | 5; +export type CardPadding = CardSize | "none"; -// The card's size, shared with CardInset so it can negate the matching padding. -const CardContext = createContext(1); +// The card's resolved padding, shared with CardInset so it negates the actual +// padding (which can be decoupled from `size` via the `padding` prop). +const CardContext = createContext(1); export const CardProvider = CardContext.Provider; diff --git a/packages/ui/src/v2/Card/variants.ts b/packages/ui/src/v2/Card/variants.ts index acd6b4f19..4c220999a 100644 --- a/packages/ui/src/v2/Card/variants.ts +++ b/packages/ui/src/v2/Card/variants.ts @@ -63,43 +63,44 @@ export const card = tv({ }); // Negates the card's padding so content bleeds to its edges. The negative -// margin must match the card's size padding (1→3, 2→4, 3→5, 4→6, 5→8), so the -// matrix is keyed on both size and side. `overflow-hidden` clips bled media. +// margin must match the card's resolved padding (1→3, 2→4, 3→5, 4→6, 5→8), so +// the matrix is keyed on the padding (from context) and side. `padding="none"` +// has nothing to negate. `overflow-hidden` clips bled media. export const cardInset = tv({ base: "overflow-hidden", variants: { - size: { 1: "", 2: "", 3: "", 4: "", 5: "" }, + padding: { 1: "", 2: "", 3: "", 4: "", 5: "", none: "" }, side: { all: "", x: "", y: "", top: "", bottom: "" }, }, compoundVariants: [ - { size: 1, side: "all", class: "-m-3" }, - { size: 1, side: "x", class: "-mx-3" }, - { size: 1, side: "y", class: "-my-3" }, - { size: 1, side: "top", class: "-mx-3 -mt-3 mb-3" }, - { size: 1, side: "bottom", class: "-mx-3 -mb-3 mt-3" }, - { size: 2, side: "all", class: "-m-4" }, - { size: 2, side: "x", class: "-mx-4" }, - { size: 2, side: "y", class: "-my-4" }, - { size: 2, side: "top", class: "-mx-4 -mt-4 mb-4" }, - { size: 2, side: "bottom", class: "-mx-4 -mb-4 mt-4" }, - { size: 3, side: "all", class: "-m-5" }, - { size: 3, side: "x", class: "-mx-5" }, - { size: 3, side: "y", class: "-my-5" }, - { size: 3, side: "top", class: "-mx-5 -mt-5 mb-5" }, - { size: 3, side: "bottom", class: "-mx-5 -mb-5 mt-5" }, - { size: 4, side: "all", class: "-m-6" }, - { size: 4, side: "x", class: "-mx-6" }, - { size: 4, side: "y", class: "-my-6" }, - { size: 4, side: "top", class: "-mx-6 -mt-6 mb-6" }, - { size: 4, side: "bottom", class: "-mx-6 -mb-6 mt-6" }, - { size: 5, side: "all", class: "-m-8" }, - { size: 5, side: "x", class: "-mx-8" }, - { size: 5, side: "y", class: "-my-8" }, - { size: 5, side: "top", class: "-mx-8 -mt-8 mb-8" }, - { size: 5, side: "bottom", class: "-mx-8 -mb-8 mt-8" }, + { padding: 1, side: "all", class: "-m-3" }, + { padding: 1, side: "x", class: "-mx-3" }, + { padding: 1, side: "y", class: "-my-3" }, + { padding: 1, side: "top", class: "-mx-3 -mt-3 mb-3" }, + { padding: 1, side: "bottom", class: "-mx-3 -mb-3 mt-3" }, + { padding: 2, side: "all", class: "-m-4" }, + { padding: 2, side: "x", class: "-mx-4" }, + { padding: 2, side: "y", class: "-my-4" }, + { padding: 2, side: "top", class: "-mx-4 -mt-4 mb-4" }, + { padding: 2, side: "bottom", class: "-mx-4 -mb-4 mt-4" }, + { padding: 3, side: "all", class: "-m-5" }, + { padding: 3, side: "x", class: "-mx-5" }, + { padding: 3, side: "y", class: "-my-5" }, + { padding: 3, side: "top", class: "-mx-5 -mt-5 mb-5" }, + { padding: 3, side: "bottom", class: "-mx-5 -mb-5 mt-5" }, + { padding: 4, side: "all", class: "-m-6" }, + { padding: 4, side: "x", class: "-mx-6" }, + { padding: 4, side: "y", class: "-my-6" }, + { padding: 4, side: "top", class: "-mx-6 -mt-6 mb-6" }, + { padding: 4, side: "bottom", class: "-mx-6 -mb-6 mt-6" }, + { padding: 5, side: "all", class: "-m-8" }, + { padding: 5, side: "x", class: "-mx-8" }, + { padding: 5, side: "y", class: "-my-8" }, + { padding: 5, side: "top", class: "-mx-8 -mt-8 mb-8" }, + { padding: 5, side: "bottom", class: "-mx-8 -mb-8 mt-8" }, ], defaultVariants: { - size: 1, + padding: 1, side: "all", }, }); diff --git a/packages/ui/src/v2/ProboLogo/ProboLogo.stories.tsx b/packages/ui/src/v2/ProboLogo/ProboLogo.stories.tsx index 79b882679..4e4ce3b95 100644 --- a/packages/ui/src/v2/ProboLogo/ProboLogo.stories.tsx +++ b/packages/ui/src/v2/ProboLogo/ProboLogo.stories.tsx @@ -19,7 +19,10 @@ import { ProboLogo } from "./ProboLogo"; export default { title: "v2/ProboLogo", component: ProboLogo, - render: () => , + args: { + className: "h-6 w-auto text-sand-12", + }, + render: args => , } satisfies Meta; type Story = StoryObj; diff --git a/packages/ui/src/v2/ProboLogo/ProboLogo.tsx b/packages/ui/src/v2/ProboLogo/ProboLogo.tsx index 374354493..5a24cd83a 100644 --- a/packages/ui/src/v2/ProboLogo/ProboLogo.tsx +++ b/packages/ui/src/v2/ProboLogo/ProboLogo.tsx @@ -29,6 +29,7 @@ export function ProboLogo(props: ProboLogoProps) { fill="none" viewBox="0 0 90 24" role="img" + aria-label="Probo" {...props} >