From 9f2ccc6df4df4b776bb34f45ac3494579c073cd3 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Sun, 5 Apr 2026 14:11:55 +0700 Subject: [PATCH 1/2] refactor: Remove outline card variant The outline variant (border-only, no background) doesn't make sense for a card component. Removes it from all 4 card recipes, tests, storybook stories, Vue components, and documentation. --- .../06.components/02.composables/05.card.md | 45 +-- .../src/components/components/card/Card.vue | 2 +- .../components/components/card/CardBody.vue | 2 +- .../components/components/card/CardFooter.vue | 2 +- .../components/components/card/CardHeader.vue | 2 +- .../components/card/preview/CardGrid.vue | 2 +- .../stories/components/card.stories.ts | 8 +- .../recipes/card/useCardBodyRecipe.test.ts | 59 +--- theme/src/recipes/card/useCardBodyRecipe.ts | 245 +++----------- .../recipes/card/useCardFooterRecipe.test.ts | 4 +- theme/src/recipes/card/useCardFooterRecipe.ts | 303 ++++++++---------- .../recipes/card/useCardHeaderRecipe.test.ts | 4 +- theme/src/recipes/card/useCardHeaderRecipe.ts | 68 +--- theme/src/recipes/card/useCardRecipe.test.ts | 30 +- theme/src/recipes/card/useCardRecipe.ts | 52 +-- 15 files changed, 246 insertions(+), 582 deletions(-) diff --git a/apps/docs/content/docs/06.components/02.composables/05.card.md b/apps/docs/content/docs/06.components/02.composables/05.card.md index 99ff3252..35a31be3 100644 --- a/apps/docs/content/docs/06.components/02.composables/05.card.md +++ b/apps/docs/content/docs/06.components/02.composables/05.card.md @@ -13,7 +13,7 @@ The Card recipes integrate directly with the default [design tokens preset](/doc The Card recipe helps you: -- **Ship faster with sensible defaults**: Get 3 colors, 4 visual styles, and 3 sizes out of the box with a single set of composable calls. +- **Ship faster with sensible defaults**: Get 3 colors, 3 visual styles, and 3 sizes out of the box with a single set of composable calls. - **Compose structured layouts**: Four coordinated recipes (container, header, body, footer) share the same variant axes, so your cards stay internally consistent. - **Maintain consistency**: Compound variants ensure every color-variant combination follows the same design rules, including separator colors and dark mode overrides. - **Customize without forking**: Override base styles, default variants, or filter out options you don't need — all through the options API. @@ -75,7 +75,7 @@ import { card, cardHeader, cardBody, cardFooter } from "virtual:styleframe"; interface CardProps { color?: "light" | "dark" | "neutral"; - variant?: "solid" | "outline" | "soft" | "subtle"; + variant?: "solid" | "soft" | "subtle"; size?: "sm" | "md" | "lg"; title?: string; description?: string; @@ -126,7 +126,7 @@ const { size = "md", } = defineProps<{ color?: "light" | "dark" | "neutral"; - variant?: "solid" | "outline" | "soft" | "subtle"; + variant?: "solid" | "soft" | "subtle"; size?: "sm" | "md" | "lg"; }>(); @@ -194,7 +194,7 @@ height: 600 ## Variants -Four visual style variants control how the card is rendered. Each variant is combined with the selected color through [compound variants](/docs/api/recipes#compound-variants), so you always get the correct background, text, border, and separator colors for your chosen color. +Three visual style variants control how the card is rendered. Each variant is combined with the selected color through [compound variants](/docs/api/recipes#compound-variants), so you always get the correct background, text, border, and separator colors for your chosen color. ### Solid @@ -207,17 +207,6 @@ panel: true --- :: -### Outline - -Transparent background with a colored border. Useful for secondary content groups that shouldn't dominate the visual hierarchy. - -::story-preview ---- -story: theme-recipes-card--outline -panel: true ---- -:: - ### Soft Light tinted background with no visible border. A gentle, borderless style that works well for grouped content in dense layouts. @@ -231,7 +220,7 @@ panel: true ### Subtle -Light tinted background with a matching border. Combines the softness of the `soft` variant with the definition of `outline`. +Light tinted background with a matching border. Combines the softness of the `soft` variant with added visual definition from a border. ::story-preview --- @@ -334,7 +323,7 @@ const card = useCardRecipe(s, { }, defaultVariants: { color: 'neutral', - variant: 'outline', + variant: 'subtle', size: 'lg', }, }); @@ -342,7 +331,7 @@ const card = useCardRecipe(s, { const cardHeader = useCardHeaderRecipe(s, { defaultVariants: { color: 'neutral', - variant: 'outline', + variant: 'subtle', size: 'lg', }, }); @@ -356,7 +345,7 @@ const cardBody = useCardBodyRecipe(s, { const cardFooter = useCardFooterRecipe(s, { defaultVariants: { color: 'neutral', - variant: 'outline', + variant: 'subtle', size: 'lg', }, }); @@ -374,11 +363,11 @@ import { useCardRecipe } from '@styleframe/theme'; const s = styleframe(); -// Only generate neutral color with solid and outline styles +// Only generate neutral color with solid and subtle styles const card = useCardRecipe(s, { filter: { color: ['neutral'], - variant: ['solid', 'outline'], + variant: ['solid', 'subtle'], }, }); @@ -412,7 +401,7 @@ Creates the card container recipe with background, border, border radius, and sh | Variant | Options | Default | |---------|---------|---------| | `color` | `light`, `dark`, `neutral` | `neutral` | -| `variant` | `solid`, `outline`, `soft`, `subtle` | `solid` | +| `variant` | `solid`, `soft`, `subtle` | `solid` | | `size` | `sm`, `md`, `lg` | `md` | ### `useCardHeaderRecipe(s, options?)` @@ -424,7 +413,7 @@ Creates the card header recipe with a bottom separator border. Accepts the same | Variant | Options | Default | |---------|---------|---------| | `color` | `light`, `dark`, `neutral` | `neutral` | -| `variant` | `solid`, `outline`, `soft`, `subtle` | `solid` | +| `variant` | `solid`, `soft`, `subtle` | `solid` | | `size` | `sm`, `md`, `lg` | `md` | ### `useCardBodyRecipe(s, options?)` @@ -436,7 +425,7 @@ Creates the card body recipe for the main content area. Accepts the same paramet | Variant | Options | Default | |---------|---------|---------| | `color` | `light`, `dark`, `neutral` | `neutral` | -| `variant` | `solid`, `outline`, `soft`, `subtle` | `solid` | +| `variant` | `solid`, `soft`, `subtle` | `solid` | | `size` | `sm`, `md`, `lg` | `md` | ### `useCardFooterRecipe(s, options?)` @@ -448,7 +437,7 @@ Creates the card footer recipe with a top separator border. Accepts the same par | Variant | Options | Default | |---------|---------|---------| | `color` | `light`, `dark`, `neutral` | `neutral` | -| `variant` | `solid`, `outline`, `soft`, `subtle` | `solid` | +| `variant` | `solid`, `soft`, `subtle` | `solid` | | `size` | `sm`, `md`, `lg` | `md` | [Learn more about recipes →](/docs/api/recipes) @@ -458,7 +447,7 @@ Creates the card footer recipe with a top separator border. Accepts the same par - **Pass `color` and `variant` consistently**: The container, header, and footer all need the same `color` and `variant` values so that separator borders match the card's visual style. - **Pass `size` to each sub-recipe**: The card container controls the border radius, but each section (header, body, footer) manages its own padding and gap based on the `size` prop. - **Use `neutral` for general-purpose cards**: The neutral color adapts to light and dark mode automatically, making it the safest default. -- **Prefer `solid` or `outline` for primary content**: Reserve `soft` and `subtle` for secondary or nested cards to create visual hierarchy. +- **Prefer `solid` for primary content**: Reserve `soft` and `subtle` for secondary or nested cards to create visual hierarchy. - **Don't use all sections if you don't need them**: A card with only a body is perfectly valid. Add headers and footers only when your content has distinct sections. - **Filter what you don't need**: If your component only uses one color, pass a `filter` option to reduce generated CSS. - **Override defaults at the recipe level**: Set your most common variant combination as `defaultVariants` so component consumers write less code. @@ -488,13 +477,13 @@ Both use a light tinted background. The difference is that `subtle` also adds a ::: :::accordion-item{label="How do compound variants work in the Card recipe?" icon="i-lucide-circle-help"} -The Card recipe uses compound variants to map each color-variant combination to specific styles. For example, when `color` is `neutral` and `variant` is `solid`, the compound variant applies `background: @color.white`, `color: @color.text`, and `borderColor: @color.gray-200`, along with dark mode overrides. The header and footer recipes use compound variants to set separator border colors that match the card's visual style. This approach keeps the individual `color` and `variant` definitions clean while handling all 12 combinations (3 colors × 4 variants) automatically. +The Card recipe uses compound variants to map each color-variant combination to specific styles. For example, when `color` is `neutral` and `variant` is `solid`, the compound variant applies `background: @color.white`, `color: @color.text`, and `borderColor: @color.gray-200`, along with dark mode overrides. The header and footer recipes use compound variants to set separator border colors that match the card's visual style. This approach keeps the individual `color` and `variant` definitions clean while handling all 9 combinations (3 colors × 3 variants) automatically. [Learn more about compound variants →](/docs/api/recipes#compound-variants) ::: :::accordion-item{label="How does filtering affect compound variants?" icon="i-lucide-circle-help"} -When you use the `filter` option, compound variants that reference filtered-out values are automatically removed. For example, if you filter `variant` to only `['solid', 'outline']`, all compound variants matching `soft` or `subtle` are excluded from the generated output. Default variants are also adjusted if they reference a removed value. +When you use the `filter` option, compound variants that reference filtered-out values are automatically removed. For example, if you filter `variant` to only `['solid', 'subtle']`, all compound variants matching `soft` are excluded from the generated output. Default variants are also adjusted if they reference a removed value. ::: :::accordion-item{label="Can I use the Card recipe without the design tokens preset?" icon="i-lucide-circle-help"} diff --git a/apps/storybook/src/components/components/card/Card.vue b/apps/storybook/src/components/components/card/Card.vue index 142341db..5dd77953 100644 --- a/apps/storybook/src/components/components/card/Card.vue +++ b/apps/storybook/src/components/components/card/Card.vue @@ -5,7 +5,7 @@ import { card } from "virtual:styleframe"; const props = withDefaults( defineProps<{ color?: "light" | "dark" | "neutral"; - variant?: "solid" | "outline" | "soft" | "subtle"; + variant?: "solid" | "soft" | "subtle"; size?: "sm" | "md" | "lg"; }>(), {}, diff --git a/apps/storybook/src/components/components/card/CardBody.vue b/apps/storybook/src/components/components/card/CardBody.vue index 544922e3..0b0b4082 100644 --- a/apps/storybook/src/components/components/card/CardBody.vue +++ b/apps/storybook/src/components/components/card/CardBody.vue @@ -5,7 +5,7 @@ import { cardBody } from "virtual:styleframe"; const props = withDefaults( defineProps<{ color?: "light" | "dark" | "neutral"; - variant?: "solid" | "outline" | "soft" | "subtle"; + variant?: "solid" | "soft" | "subtle"; size?: "sm" | "md" | "lg"; }>(), {}, diff --git a/apps/storybook/src/components/components/card/CardFooter.vue b/apps/storybook/src/components/components/card/CardFooter.vue index f98819e8..98765031 100644 --- a/apps/storybook/src/components/components/card/CardFooter.vue +++ b/apps/storybook/src/components/components/card/CardFooter.vue @@ -5,7 +5,7 @@ import { cardFooter } from "virtual:styleframe"; const props = withDefaults( defineProps<{ color?: "light" | "dark" | "neutral"; - variant?: "solid" | "outline" | "soft" | "subtle"; + variant?: "solid" | "soft" | "subtle"; size?: "sm" | "md" | "lg"; }>(), {}, diff --git a/apps/storybook/src/components/components/card/CardHeader.vue b/apps/storybook/src/components/components/card/CardHeader.vue index e86f6e2e..02f5de82 100644 --- a/apps/storybook/src/components/components/card/CardHeader.vue +++ b/apps/storybook/src/components/components/card/CardHeader.vue @@ -5,7 +5,7 @@ import { cardHeader } from "virtual:styleframe"; const props = withDefaults( defineProps<{ color?: "light" | "dark" | "neutral"; - variant?: "solid" | "outline" | "soft" | "subtle"; + variant?: "solid" | "soft" | "subtle"; size?: "sm" | "md" | "lg"; }>(), {}, diff --git a/apps/storybook/src/components/components/card/preview/CardGrid.vue b/apps/storybook/src/components/components/card/preview/CardGrid.vue index 61186a2a..df13c004 100644 --- a/apps/storybook/src/components/components/card/preview/CardGrid.vue +++ b/apps/storybook/src/components/components/card/preview/CardGrid.vue @@ -7,7 +7,7 @@ import CardTitle from "../CardTitle.vue"; import CardDescription from "../CardDescription.vue"; const colors = ["neutral", "light", "dark"] as const; -const variants = ["solid", "outline", "soft", "subtle"] as const; +const variants = ["solid", "soft", "subtle"] as const;