diff --git a/src/index.ts b/src/index.ts index 8fc23c6..a2703f5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import shimmerKeyframes from '@/keyframes/shimmer' import skeletonPattern from '@/patterns/skeleton' import alertRecipe from '@/recipes/alert' import buttonRecipe from '@/recipes/button' +import cardRecipe from '@/recipes/card' import chipRecipe from '@/recipes/chip' import detailsRecipe from '@/recipes/details' import iconButtonRecipe from '@/recipes/iconButton' @@ -46,6 +47,7 @@ const srJuggernautPandaPreset = (config?: ThemeConfig) => { recipes: { alert: alertRecipe({ semanticColorNames: semanticColorKeysArray }), button: buttonRecipe({ semanticColorNames: semanticColorKeysArray }), + card: cardRecipe({ semanticColorNames: semanticColorKeysArray }), chip: chipRecipe({ semanticColorNames: semanticColorKeysArray }), details: detailsRecipe({ semanticColorNames: semanticColorKeysArray }), mark: markRecipe({ semanticColorNames: semanticColorKeysArray }), diff --git a/src/recipes/card.ts b/src/recipes/card.ts new file mode 100644 index 0000000..d580144 --- /dev/null +++ b/src/recipes/card.ts @@ -0,0 +1,160 @@ +import { defineSlotRecipe, type SystemStyleObject } from '@pandacss/dev' + +export const cardSlots = ['root', 'header', 'body', 'footer', 'media', 'actions'] as const + +export type CardRecipeArg = { + semanticColorNames: string[] +} + +const cardRecipe = ({ semanticColorNames }: CardRecipeArg) => { + const colors = ['neutral', ...semanticColorNames] + return defineSlotRecipe({ + className: 'card', + slots: cardSlots, + base: { + root: { + border: '1px solid', + borderColor: 'var(--card-border-color)', + borderRadius: 'md', + backgroundColor: 'var(--card-background-color)', + color: 'var(--card-color-text)', + overflow: 'hidden', + '&:is(button, a, [role="button"], [role="link"])': { + all: 'unset', + border: '1px solid', + borderColor: 'var(--card-border-color)', + borderRadius: 'md', + backgroundColor: 'var(--card-background-color)', + color: 'var(--card-color-text)', + overflow: 'hidden', + cursor: 'pointer', + transitionProperty: 'color, background-color, border-color', + transitionDuration: 'normal', + transitionTimingFunction: 'easeOut', + focusRingColor: 'var(--card-border-color-hover)', + _hover: { + borderColor: 'var(--card-border-color-hover)', + backgroundColor: 'var(--card-background-color-hover)', + '& .card__header': { + backgroundColor: 'var(--card-background-color-variant-hover)' + } + }, + _active: { + borderColor: 'var(--card-border-color-active)', + backgroundColor: 'var(--card-background-color-active)' + }, + _focus: { + focusRing: 'outside', + focusRingWidth: '2px', + borderColor: 'var(--card-border-color-hover)', + backgroundColor: 'var(--card-background-color-hover)', + '& .card__header': { + backgroundColor: 'var(--card-background-color-variant-hover)' + } + }, + _disabled: { + borderColor: 'color-mix(in srgb, var(--card-border-color) 40%, transparent)', + backgroundColor: 'color-mix(in srgb, var(--card-background-color) 40%, transparent)', + color: 'color-mix(in srgb, var(--card-color-text) 40%, transparent)', + cursor: 'not-allowed', + _hover: { + borderColor: 'color-mix(in srgb, var(--card-border-color) 40%, transparent)', + backgroundColor: 'color-mix(in srgb, var(--card-background-color) 40%, transparent)' + }, + _active: { + borderColor: 'color-mix(in srgb, var(--card-border-color) 40%, transparent)', + backgroundColor: 'color-mix(in srgb, var(--card-background-color) 40%, transparent)' + }, + '& .card__header': { + backgroundColor: 'color-mix(in srgb, var(--card-background-color-variant) 40%, transparent)' + } + } + } + }, + header: { + width: '100%', + paddingInline: 'calc({spacing.xs} * 1.25)', + paddingBlock: 'calc({spacing.xs} * 0.75)', + backgroundColor: 'var(--card-background-color-variant)', + transitionProperty: 'color, background-color, border-color', + fontWeight: 'semibold', + fontSize: 'lg', + textOverflow: 'ellipsis', + textWrap: 'stable', + wordBreak: 'break-word', + overflow: 'hidden', + whiteSpace: 'nowrap' + }, + body: { + paddingInline: 'calc({spacing.xs} * 1.25)', + paddingBlock: 'calc({spacing.xs} * 0.75)', + textWrap: 'balance', + '@supports (text-wrap: pretty)': { + textWrap: 'pretty' + } + }, + footer: { + paddingInline: 'calc({spacing.xs} * 1.25)', + paddingBlock: 'calc({spacing.xs} * 0.75)', + color: 'var(--card-color-text-subtle)', + fontSize: 'sm', + textWrap: 'balance', + '@supports (text-wrap: pretty)': { + textWrap: 'pretty' + } + }, + media: { + width: '100%', + height: 'auto', + objectFit: 'cover', + objectPosition: 'center' + }, + actions: { + display: 'flex', + justifyContent: 'space-evenly', + + paddingInline: 'calc({spacing.xs} * 1.25)', + paddingBlock: 'calc({spacing.xs} * 0.75)' + } + }, + variants: { + color: { + ...Object.fromEntries( + colors.map((color) => [ + color, + Object.fromEntries( + cardSlots.map((slot) => { + switch (slot) { + case 'root': + return [ + slot, + { + '--card-background-color': `{colors.${color}.2}`, + '--card-background-color-variant': `{colors.${color}.3}`, + '--card-background-color-hover': `{colors.${color}.4}`, + '--card-background-color-active': `{colors.${color}.5}`, + '--card-border-color': `{colors.${color}.6}`, + '--card-border-color-hover': `{colors.${color}.7}`, + '--card-border-color-active': `{colors.${color}.8}`, + '--card-color': `{colors.${color}.9}`, + '--card-color-hover': `{colors.${color}.10}`, + '--card-color-text-subtle': `{colors.${color}.11}`, + '--card-color-text': `{colors.${color}.12}` + } as SystemStyleObject + ] + default: + return [slot, {} as SystemStyleObject] + } + }) + ) + ]) + ) + } + }, + defaultVariants: { + color: 'neutral' + } + }) +} + +export default cardRecipe