feat: add card recipe

This commit is contained in:
2025-10-08 14:09:05 -06:00
parent 42d5fc940a
commit 396f8d2fde
2 changed files with 162 additions and 0 deletions

View File

@@ -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 }),

160
src/recipes/card.ts Normal file
View File

@@ -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