Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8d2417d
feat(Tabs): styling
tenphi Jan 30, 2026
a8713da
fix(Tabs): selection indicator
tenphi Jan 30, 2026
e845206
fix(Tabs): selection indicator * 2
tenphi Jan 30, 2026
7ae22a1
fix(Tabs): selection indicator * 3
tenphi Jan 30, 2026
daa00f7
fix(Tabs): selection indicator * 4
tenphi Jan 30, 2026
018f641
fix(Tabs): selection indicator * 5
tenphi Jan 30, 2026
1c25942
fix(Tabs): selection indicator * 6
tenphi Jan 30, 2026
5957754
fix(Item): actions padding
tenphi Jan 30, 2026
dd6806f
fix(Item): actions padding * 2
tenphi Jan 30, 2026
273b8da
fix(Item): actions padding * 3
tenphi Jan 30, 2026
e23b360
fix(Layout): scrollbar selector
tenphi Jan 30, 2026
2267850
fix(LayoutPanel): zIndex
tenphi Feb 2, 2026
b3cd2c2
fix(ItemButton): mods
tenphi Feb 2, 2026
79aa239
feat(Item): actions refactoring
tenphi Feb 2, 2026
87b9bcc
feat(Item): actions refactoring * 2
tenphi Feb 2, 2026
f431520
feat(Item): actions refactoring * 3
tenphi Feb 2, 2026
ed425e9
feat(Item): actions refactoring * 4
tenphi Feb 2, 2026
c045f9f
feat(Item): actions refactoring * 56
tenphi Feb 2, 2026
81a0e8c
chore: increase size limit
tenphi Feb 2, 2026
d09e09f
feat(Item): actions refactoring * 6
tenphi Feb 2, 2026
51e436f
feat(Item): actions refactoring * 7
tenphi Feb 2, 2026
b97226a
fix(Tabs): close icon for deletion
tenphi Feb 2, 2026
8bd40a6
feat(Item): actions refactoring * 8
tenphi Feb 2, 2026
4acde51
fix(LayoutPane): sub element selector
tenphi Feb 2, 2026
507cf90
fix(LayoutPane): sub element selector * 2
tenphi Feb 2, 2026
df7ea6e
fix(LayoutPane): sub element selector * 2
tenphi Feb 2, 2026
d26f5ef
feat(Item): actions refactoring * 9
tenphi Feb 2, 2026
69f4779
feat(Layout): zIndex
tenphi Feb 2, 2026
7aacf64
fix(tasty): boolean tokens
tenphi Feb 2, 2026
e0840c1
feat(Tabs): styling
tenphi Feb 3, 2026
8da0748
feat(Tabs): styling * 2
tenphi Feb 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tabs-styling-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cube-dev/ui-kit": minor
---

Add styling props to Tabs component: `tabListStyles`, `tabStyles`, and `tabPanelStyles` for customizing the tab list container, individual tabs, and tab panels respectively. Per-tab and per-panel styles can override these defaults.
13 changes: 11 additions & 2 deletions src/components/navigation/Tabs/TabButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export function TabButton({ item, tabData, isLastTab }: TabButtonProps) {
onDelete,
dragState,
dropState,
tabStyles: parentTabStyles,
editingKey,
editValue,
setEditValue,
Expand Down Expand Up @@ -497,10 +498,18 @@ export function TabButton({ item, tabData, isLastTab }: TabButtonProps) {
onAction: _onAction,
qa,
qaVal,
styles,
styles: tabSpecificStyles,
...itemStyleProps
} = tabData;

// Merge parent tabStyles with tab-specific styles (tab-specific overrides parent)
const effectiveStyles = useMemo(() => {
if (!parentTabStyles && !tabSpecificStyles) return undefined;
if (!parentTabStyles) return tabSpecificStyles;
if (!tabSpecificStyles) return parentTabStyles;
return { ...parentTabStyles, ...tabSpecificStyles };
}, [parentTabStyles, tabSpecificStyles]);

// Use the hook's targetRef when context menu is enabled
const effectiveContainerRef =
effectiveContextMenu && processedMenu
Expand Down Expand Up @@ -544,7 +553,7 @@ export function TabButton({ item, tabData, isLastTab }: TabButtonProps) {
ref={ref}
qa={qa ?? `Tab-${String(item.key)}`}
qaVal={qaVal}
styles={styles}
styles={effectiveStyles}
mods={mods}
isSelected={isActive}
isDisabled={isDisabled}
Expand Down
15 changes: 14 additions & 1 deletion src/components/navigation/Tabs/TabPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface TabPanelRendererProps {
tabPrerender?: boolean;
tabKeepMounted?: boolean;
visitedKeys: Set<string>;
defaultPanelStyles?: Styles;
panelStyles?: Styles;
qa?: string;
qaVal?: string;
Expand All @@ -57,6 +58,7 @@ export function TabPanelRenderer({
tabPrerender,
tabKeepMounted,
visitedKeys,
defaultPanelStyles,
panelStyles,
qa,
qaVal,
Expand All @@ -74,6 +76,14 @@ export function TabPanelRenderer({
const effectivePrerender = tabPrerender ?? prerender;
const effectiveKeepMounted = tabKeepMounted ?? keepMounted;

// Merge default panel styles with panel-specific styles (panel-specific overrides default)
const effectiveStyles = useMemo(() => {
if (!defaultPanelStyles && !panelStyles) return undefined;
if (!defaultPanelStyles) return panelStyles;
if (!panelStyles) return defaultPanelStyles;
return { ...defaultPanelStyles, ...panelStyles };
}, [defaultPanelStyles, panelStyles]);

// Determine if panel should render
if (
!shouldRenderPanel(
Expand All @@ -92,7 +102,7 @@ export function TabPanelRenderer({
ref={ref}
qa={qa ?? 'TabPanel'}
qaVal={qaVal ?? String(tabKey)}
styles={panelStyles}
styles={effectiveStyles}
style={{
display: isActive ? 'contents' : 'none',
}}
Expand All @@ -115,6 +125,7 @@ export interface CachedPanelRendererProps {
prerender: boolean;
keepMounted: boolean;
visitedKeys: Set<string>;
defaultPanelStyles?: Styles;
}

/**
Expand All @@ -138,6 +149,7 @@ export function CachedPanelRenderer({
prerender,
keepMounted,
visitedKeys,
defaultPanelStyles,
}: CachedPanelRendererProps) {
// Cache for rendered content - stores { content, cacheKey } per panel
const contentCacheRef = useRef<
Expand Down Expand Up @@ -254,6 +266,7 @@ export function CachedPanelRenderer({
tabPrerender={tabPrerender}
tabKeepMounted={tabKeepMounted}
visitedKeys={visitedKeys}
defaultPanelStyles={defaultPanelStyles}
panelStyles={explicitPanel?.styles}
qa={explicitPanel?.qa}
qaVal={explicitPanel?.qaVal}
Expand Down
18 changes: 16 additions & 2 deletions src/components/navigation/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,21 @@ function TabsComponent(
showScrollArrows = false,
tabPickerPosition = 'suffix',
scrollArrowsPosition = 'suffix',
tabListStyles,
tabStyles,
tabPanelStyles,
...otherProps
} = props;

// Extract outer styles
const combinedStyles = extractStyles(otherProps, OUTER_STYLES);
// Extract outer styles and merge with tabListStyles for Container sub-element
const baseStyles = extractStyles(otherProps, OUTER_STYLES);
const combinedStyles = useMemo(() => {
if (!tabListStyles) return baseStyles;
return {
...baseStyles,
Container: tabListStyles,
};
}, [baseStyles, tabListStyles]);
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated

// DOM element refs
const listRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -458,6 +468,7 @@ function TabsComponent(
contextMenu: parentContextMenu,
onDelete,
onAction: parentOnAction,
tabStyles,
editingKey,
editValue,
setEditValue,
Expand All @@ -477,6 +488,7 @@ function TabsComponent(
parentContextMenu,
onDelete,
parentOnAction,
tabStyles,
editingKey,
editValue,
setEditValue,
Expand Down Expand Up @@ -653,6 +665,7 @@ function TabsComponent(
prerender={prerender}
keepMounted={keepMounted}
visitedKeys={visitedKeysRef.current}
defaultPanelStyles={tabPanelStyles}
/>
)}

Expand All @@ -676,6 +689,7 @@ function TabsComponent(
tabPrerender={explicitPanel?.prerender ?? tab.prerender}
tabKeepMounted={explicitPanel?.keepMounted ?? tab.keepMounted}
visitedKeys={visitedKeysRef.current}
defaultPanelStyles={tabPanelStyles}
panelStyles={explicitPanel?.styles}
qa={explicitPanel?.qa}
qaVal={explicitPanel?.qaVal}
Expand Down
3 changes: 3 additions & 0 deletions src/components/navigation/Tabs/TabsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
DroppableCollectionState,
TabListState,
} from 'react-stately';
import type { Styles } from '../../../tasty';
import type { CubeItemActionProps } from '../../actions/ItemAction';
import type { CubeMenuProps } from '../../actions/Menu';
import type { TabSize, TabType } from './types';
Expand Down Expand Up @@ -40,6 +41,8 @@ export interface TabsContextValue {
dragState?: DraggableCollectionState;
/** Drop state for reorderable tabs (undefined if not reorderable) */
dropState?: DroppableCollectionState;
/** Default styles for all tabs */
tabStyles?: Styles;

// Editing callbacks
/** Current tab being edited (null if none) */
Expand Down
16 changes: 11 additions & 5 deletions src/components/navigation/Tabs/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,21 +192,27 @@ export const TabElement = tasty(Item, {
'(type=file | type=radio) & selected': '#white',
},
border: {
'': '0 #clear',
'type=file & selected': '$tab-indicator-size #purple bottom',
'': '#clear',
'type=file': '0 #clear',
},
preset: {
'': 't3m',
'size=xsmall': 't4',
},
shadow: {
'': 'none',
'focused & focus-visible': 'inset 0 0 0 1bw #purple-text',
editing: 'inset 0 0 0 1bw #purple-text',
'': '$selection-shadow',
'focused & focus-visible':
'inset 0 0 0 1bw #purple-text, $selection-shadow',
editing: 'inset 0 0 0 1bw #purple-text, $selection-shadow',
'type=radio & selected': '$item-shadow',
'type=radio & selected & focused & focus-visible':
'$item-shadow, inset 0 0 0 1bw #purple-text',
},
'$selection-shadow': {
'': 'inset 0 0 0 0 #purple',
'type=file & selected': 'inset 0 (-1 * $tab-indicator-size) 0 0 #purple',
'!type=file': 'inset 0 0 0 0 #purple.0',
},
Comment thread
cursor[bot] marked this conversation as resolved.
// Collapse horizontal padding for narrow type
'$label-padding-left': {
'': '$inline-padding',
Expand Down
14 changes: 14 additions & 0 deletions src/components/navigation/Tabs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,20 @@ export interface CubeTabsProps
* @default 'suffix'
*/
scrollArrowsPosition?: TabsActionPosition;
/**
* Custom styles for the tab list container (the scrollable area containing tabs).
*/
tabListStyles?: Styles;
/**
* Default styles applied to all tab buttons.
* Can be overridden per-tab via Tab's styles prop.
*/
tabStyles?: Styles;
/**
* Default styles applied to all tab panels.
* Can be overridden per-panel via Panel's styles prop.
*/
tabPanelStyles?: Styles;
}

export interface CubeTabProps extends TabStyleProps, PanelBehaviorProps {
Expand Down
Loading