From 944ef344e6dd882a96cef6ce289bc7cb1b3c6263 Mon Sep 17 00:00:00 2001 From: SrJuggernaut Date: Thu, 30 Apr 2026 21:48:06 -0600 Subject: [PATCH] feat: add tabs slot recipe for tab component styling --- src/index.ts | 2 + src/recipes/tabs.ts | 179 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/recipes/tabs.ts diff --git a/src/index.ts b/src/index.ts index 4fd7ff7..ab8a4b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ import markRecipe from '@/recipes/mark' import menuRecipe from '@/recipes/menu' import progressBarRecipe from '@/recipes/progressBar' import selectRecipe from '@/recipes/select' +import tabsRecipe from '@/recipes/tabs' import { type BrandColor, type Color, type ColorVariation, color, type NeutralColor } from '@/types' export type ThemeConfig = { @@ -78,6 +79,7 @@ const srJuggernautPandaPreset = (config?: ThemeConfig) => { input: inputRecipe({ semanticColorNames: semanticColorKeysArray }), iconButton: iconButtonRecipe({ semanticColorNames: semanticColorKeysArray }), progressBar: progressBarRecipe({ semanticColorNames: semanticColorKeysArray }), + tabs: tabsRecipe({ semanticColorNames: semanticColorKeysArray }), select: selectRecipe({ semanticColorNames: semanticColorKeysArray }) }, breakpoints: Object.fromEntries(breakpointEntries.map(([key, value]) => [key, `${value}px`])), diff --git a/src/recipes/tabs.ts b/src/recipes/tabs.ts new file mode 100644 index 0000000..a265217 --- /dev/null +++ b/src/recipes/tabs.ts @@ -0,0 +1,179 @@ +import { defineSlotRecipe, type SystemStyleObject } from '@pandacss/dev' + +export const tabsSlots = ['container', 'list', 'tab', 'content'] as const + +export interface TabsRecipeArg { + semanticColorNames: string[] +} + +const tabsRecipe = ({ semanticColorNames }: TabsRecipeArg) => { + const colorVariants = ['neutral', ...semanticColorNames] + return defineSlotRecipe({ + className: 'tabs', + slots: tabsSlots, + base: { + container: { + borderColor: 'var(--tabs-colors-6, var(--colors-neutral-6))', + backgroundColor: 'var(--tabs-colors-1, var(--colors-neutral-1))', + color: 'var(--tabs-colors-12, var(--colors-neutral-12))', + border: '1px solid', + borderRadius: 'md', + overflow: 'hidden' + }, + list: { + display: 'flex', + flexWrap: 'nowrap', + width: '100%', + margin: 0, + padding: 0, + backgroundColor: 'var(--tabs-colors-list-1, var(--tabs-colors-1))', + color: 'var(--tabs-colors-list-12, var(--tabs-colors-12))', + overflowX: 'auto' + }, + tab: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: 'xs', + flex: 1, + minWidth: 'max-content', + paddingBlock: 'xs', + paddingInline: 'sm', + backgroundColor: 'var(--tabs-colors-tab-3, var(--tabs-colors-list-3, var(--tabs-colors-3)))', + color: 'var(--tabs-colors-tab-12, var(--tabs-colors-list-12, var(--tabs-colors-12)))', + transitionProperty: 'color, background-color, border-color', + transitionDuration: 'normal', + transitionTimingFunction: 'easeOut', + fontWeight: 'semibold', + focusVisibleRing: 'outside', + focusRingWidth: '2px', + focusRingOffset: '0px', + cursor: 'pointer', + _hover: { + backgroundColor: 'var(--tabs-colors-tab-4, var(--tabs-colors-list-4, var(--tabs-colors-4)))' + }, + _active: { + backgroundColor: 'var(--tabs-colors-tab-5, var(--tabs-colors-list-5, var(--tabs-colors-5)))', + _hover: { + backgroundColor: 'var(--tabs-colors-tab-4, var(--tabs-colors-list-4, var(--tabs-colors-4)))' + } + }, + _disabled: { + cursor: 'not-allowed', + backgroundColor: + 'color-mix(in srgb, var(--tabs-colors-tab-3, var(--tabs-colors-list-3, var(--tabs-colors-3))) 40%, transparent)', + color: + 'color-mix(in srgb, var(--tabs-colors-tab-11, var(--tabs-colors-list-11, var(--tabs-colors-11))) 40%, transparent)', + _hover: { + backgroundColor: + 'color-mix(in srgb, var(--tabs-colors-tab-3, var(--tabs-colors-list-3, var(--tabs-colors-3))) 40%, transparent)', + color: + 'color-mix(in srgb, var(--tabs-colors-tab-11, var(--tabs-colors-list-11, var(--tabs-colors-11))) 40%, transparent)', + _active: { + backgroundColor: + 'color-mix(in srgb, var(--tabs-colors-tab-3, var(--tabs-colors-list-3, var(--tabs-colors-3))) 40%, transparent)', + color: + 'color-mix(in srgb, var(--tabs-colors-tab-11, var(--tabs-colors-list-11, var(--tabs-colors-11))) 40%, transparent)' + } + }, + _active: { + backgroundColor: + 'color-mix(in srgb, var(--tabs-colors-tab-3, var(--tabs-colors-list-3, var(--tabs-colors-3))) 40%, transparent)', + color: + 'color-mix(in srgb, var(--tabs-colors-tab-11, var(--tabs-colors-list-11, var(--tabs-colors-11))) 40%, transparent)' + } + } + }, + content: { + display: 'block', + opacity: 1, + padding: 'sm', + backgroundColor: 'var(--tabs-colors-content-1, var(--tabs-colors-1))', + color: 'var(--tabs-colors-content-12, var(--tabs-colors-12))', + transitionProperty: 'display, opacity, transform', + transitionDuration: '{durations.normal}, {durations.normal}, {durations.normal}', + transitionTimingFunction: '{easings.easeOut}, {easings.easeOutQuint}, {easings.easeOutQuint}', + transitionBehavior: 'allow-discrete', + transform: 'translateY(0)', + _hidden: { + display: 'none' + }, + '&[data-starting-style]': { + opacity: 0, + '&[data-activation-direction="right"]': { + transform: 'translateX(50%)' + }, + '&[data-activation-direction="left"]': { + transform: 'translateX(-50%)' + } + }, + '&[data-ending-style]': { + opacity: 0, + transitionDuration: '0ms' + } + } + }, + variants: { + color: { + ...Object.fromEntries( + colorVariants.map((color) => [ + color, + { + ...Object.fromEntries( + tabsSlots.map((slot) => { + switch (slot) { + case 'container': + return [ + slot, + { + '--tabs-colors-1': `{colors.${color}.1}`, + '--tabs-colors-2': `{colors.${color}.2}`, + '--tabs-colors-3': `{colors.${color}.3}`, + '--tabs-colors-4': `{colors.${color}.4}`, + '--tabs-colors-5': `{colors.${color}.5}`, + '--tabs-colors-6': `{colors.${color}.6}`, + '--tabs-colors-7': `{colors.${color}.7}`, + '--tabs-colors-8': `{colors.${color}.8}`, + '--tabs-colors-9': `{colors.${color}.9}`, + '--tabs-colors-10': `{colors.${color}.10}`, + '--tabs-colors-11': `{colors.${color}.11}`, + '--tabs-colors-12': `{colors.${color}.12}`, + '--tabs-colors-foreground': `{colors.${color}.foreground}` + } as SystemStyleObject + ] + case 'list': + case 'tab': + case 'content': + return [ + slot, + { + [`--tabs-colors-${slot}-1`]: `{colors.${color}.1}`, + [`--tabs-colors-${slot}-2`]: `{colors.${color}.2}`, + [`--tabs-colors-${slot}-3`]: `{colors.${color}.3}`, + [`--tabs-colors-${slot}-4`]: `{colors.${color}.4}`, + [`--tabs-colors-${slot}-5`]: `{colors.${color}.5}`, + [`--tabs-colors-${slot}-6`]: `{colors.${color}.6}`, + [`--tabs-colors-${slot}-7`]: `{colors.${color}.7}`, + [`--tabs-colors-${slot}-8`]: `{colors.${color}.8}`, + [`--tabs-colors-${slot}-9`]: `{colors.${color}.9}`, + [`--tabs-colors-${slot}-10`]: `{colors.${color}.10}`, + [`--tabs-colors-${slot}-11`]: `{colors.${color}.11}`, + [`--tabs-colors-${slot}-12`]: `{colors.${color}.12}`, + [`--tabs-colors-${slot}-foreground`]: `{colors.${color}.foreground}` + } as SystemStyleObject + ] + default: + return [slot, {} as SystemStyleObject] + } + }) + ) + } + ]) + ) + } + }, + defaultVariants: {} + }) +} + +export default tabsRecipe