From 139ac0e3a486b3a2a75f820807542f93f15d5488 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 3 Apr 2026 10:47:57 +0000 Subject: [PATCH 1/5] fix(Tabs,RadioGroup): align radio/tabs size mapping - RADIO_SIZE_MAP: medium -> small (28px), large -> medium (32px) - Radio.tsx tabs default size: large instead of small - Radio.tsx tabs mapping: medium -> small, large -> medium - Updated JSDoc comments and docs Co-authored-by: Andrey Yamanov --- src/components/fields/RadioGroup/Radio.tsx | 15 +++++++-------- src/components/navigation/Tabs/Tabs.docs.mdx | 4 ++-- src/components/navigation/Tabs/types.ts | 9 +++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/fields/RadioGroup/Radio.tsx b/src/components/fields/RadioGroup/Radio.tsx index f8792d0c6..b61dac93b 100644 --- a/src/components/fields/RadioGroup/Radio.tsx +++ b/src/components/fields/RadioGroup/Radio.tsx @@ -213,18 +213,17 @@ function Radio(props: CubeRadioProps, ref) { // Determine effective size with priority: prop > context > default let effectiveSize: CubeItemProps['size'] = (size ?? contextSize ?? - (effectiveType === 'tabs' ? 'small' : 'medium')) as CubeItemProps['size']; + (effectiveType === 'tabs' ? 'large' : 'medium')) as CubeItemProps['size']; - // Apply size mapping for tabs mode button radios + // Apply size mapping for tabs mode button radios. + // Tabs mode supports 'medium' and 'large' (default) API sizes, + // mapped to smaller Item sizes: medium -> small, large -> medium. if (effectiveType === 'tabs' && isButton) { - if (effectiveSize === 'small' || effectiveSize === 'medium') { - effectiveSize = 'xsmall'; - } else if (effectiveSize === 'large') { + if (effectiveSize === 'medium') { + effectiveSize = 'small'; + } else { effectiveSize = 'medium'; - } else if (effectiveSize === 'xlarge') { - effectiveSize = 'large'; } - // 'xsmall' stays 'xsmall' } // Determine effective button type diff --git a/src/components/navigation/Tabs/Tabs.docs.mdx b/src/components/navigation/Tabs/Tabs.docs.mdx index af4421608..165955e6d 100644 --- a/src/components/navigation/Tabs/Tabs.docs.mdx +++ b/src/components/navigation/Tabs/Tabs.docs.mdx @@ -126,12 +126,12 @@ For individual tabs: ### Sizes -- `xsmall` - Extra small size with t4 typography (radio type only) +- `xsmall` - Extra small size with t4 typography - `small` - Small size with t3m typography (default) - `medium` - Default size with t3m typography - `large` - Larger tabs with t3m typography -**Note:** Radio type only supports `medium` and `large` sizes, which are mapped to smaller Item sizes internally. +**Note:** Radio type supports `large` (default) and `medium` sizes, mapped to Item sizes `medium` (32px) and `small` (28px) respectively. diff --git a/src/components/navigation/Tabs/types.ts b/src/components/navigation/Tabs/types.ts index 55a024a71..35f9cd75f 100644 --- a/src/components/navigation/Tabs/types.ts +++ b/src/components/navigation/Tabs/types.ts @@ -22,10 +22,11 @@ export type TabSize = 'xsmall' | 'small' | 'medium' | 'large'; /** * Size mapping for radio type tabs. - * Radio type uses smaller sizes similar to RadioGroup tabs mode. + * Radio type supports 'large' (default) and 'medium' API sizes, + * mapped to Item sizes: large -> medium (32px), medium -> small (28px). */ export const RADIO_SIZE_MAP: Record<'medium' | 'large', TabSize> = { - medium: 'xsmall', + medium: 'small', large: 'medium', }; @@ -88,7 +89,7 @@ type OmittedItemProps = export interface TabStyleProps extends Omit { /** * Tab size. Supports 'xsmall', 'small', 'medium', 'large'. - * Radio type only supports 'medium' | 'large' (mapped to smaller Item sizes). + * Radio type supports 'large' (default) and 'medium', mapped to Item sizes medium/small. */ size?: TabSize; /** Visual appearance type. */ @@ -123,7 +124,7 @@ export interface CubeTabsProps type?: TabType; /** * Tab size. Supports 'xsmall', 'small', 'medium', 'large'. - * Radio type only supports 'medium' | 'large' (mapped to smaller Item sizes). + * Radio type supports 'large' (default) and 'medium', mapped to Item sizes medium/small. * @default 'medium' */ size?: TabSize; From d1eb29c797c7ff8a226fddefdb84b4afd0ef2e56 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 3 Apr 2026 12:06:01 +0000 Subject: [PATCH 2/5] fix(Tabs,RadioGroup): correct radio/tabs size mapping - RADIO_SIZE_MAP: medium -> xsmall (24px button, 32px total), large -> medium (32px button, 40px total) - Radio.tsx tabs default: large (was small) - Radio.tsx tabs mapping: medium -> xsmall, large -> medium - Simplified stories to show only large/medium sizes - Updated docs and JSDoc comments Co-authored-by: Andrey Yamanov --- AGENTS.md | 53 ++++++++++--------- src/components/fields/RadioGroup/Radio.tsx | 7 +-- .../fields/RadioGroup/RadioGroup.stories.tsx | 39 +------------- src/components/navigation/Tabs/Tabs.docs.mdx | 2 +- .../navigation/Tabs/Tabs.stories.tsx | 20 +++++++ src/components/navigation/Tabs/types.ts | 11 ++-- 6 files changed, 63 insertions(+), 69 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0a6a8d5bb..34211a2bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,22 +7,24 @@ This document is a comprehensive reference for AI agents maintaining or extendin ## Package Overview - **Package:** `@cube-dev/ui-kit` -- **Repository:** https://github.com/cube-js/cube-ui-kit -- **Storybook:** https://cube-ui-kit.vercel.app/ +- **Repository:** [https://github.com/cube-js/cube-ui-kit](https://github.com/cube-js/cube-ui-kit) +- **Storybook:** [https://cube-ui-kit.vercel.app/](https://cube-ui-kit.vercel.app/) - **Styling engine:** [Tasty](https://github.com/tenphi/tasty) (`@tenphi/tasty`) ## Tasty Documentation Tasty documentation is bundled with this package in `docs/tasty/` (symlinked to `node_modules/@tenphi/tasty/docs` during development; copied at pack time by `scripts/prepare-docs.mjs`). -| File | Description | -|------|-------------| -| [usage.md](./docs/tasty/usage.md) | Core API: `tasty()` component creation, state mappings, sub-elements (`data-element`), variants, extending components, and React hooks (`useStyles`, `useGlobalStyles`, `useRawCSS`). | -| [configuration.md](./docs/tasty/configuration.md) | `configure()` options: tokens, recipes, custom units, style handlers, `@keyframes`, `@property`, parser cache, and TypeScript module augmentation. | -| [styles.md](./docs/tasty/styles.md) | Complete reference for all enhanced style properties: `fill`, `flow`, `preset`, `border`, `radius`, `transition`, `scrollbar`, shorthand syntax, and recommended alternatives to raw CSS properties. | -| [debug.md](./docs/tasty/debug.md) | `tastyDebug` runtime API: inspect injected CSS, view cache metrics, chunk breakdowns, and troubleshoot style issues. | -| [injector.md](./docs/tasty/injector.md) | Low-level style injector: `inject()`, `injectGlobal()`, `injectRawCSS()`, `keyframes()`, hash deduplication, reference counting, SSR, and Shadow DOM support. | -| [tasty-static.md](./docs/tasty/tasty-static.md) | Zero-runtime mode: `tastyStatic()`, Babel plugin setup, Next.js/Vite integration, and static extraction limitations. | + +| File | Description | +| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [usage.md](./docs/tasty/usage.md) | Core API: `tasty()` component creation, state mappings, sub-elements (`data-element`), variants, extending components, and React hooks (`useStyles`, `useGlobalStyles`, `useRawCSS`). | +| [configuration.md](./docs/tasty/configuration.md) | `configure()` options: tokens, recipes, custom units, style handlers, `@keyframes`, `@property`, parser cache, and TypeScript module augmentation. | +| [styles.md](./docs/tasty/styles.md) | Complete reference for all enhanced style properties: `fill`, `flow`, `preset`, `border`, `radius`, `transition`, `scrollbar`, shorthand syntax, and recommended alternatives to raw CSS properties. | +| [debug.md](./docs/tasty/debug.md) | `tastyDebug` runtime API: inspect injected CSS, view cache metrics, chunk breakdowns, and troubleshoot style issues. | +| [injector.md](./docs/tasty/injector.md) | Low-level style injector: `inject()`, `injectGlobal()`, `injectRawCSS()`, `keyframes()`, hash deduplication, reference counting, SSR, and Shadow DOM support. | +| [tasty-static.md](./docs/tasty/tasty-static.md) | Zero-runtime mode: `tastyStatic()`, Babel plugin setup, Next.js/Vite integration, and static extraction limitations. | + ## Project Structure @@ -66,18 +68,20 @@ ComponentName/ ## Commands -| Command | Description | -|---------|-------------| -| `pnpm storybook` | Start Storybook on port 6060 | -| `pnpm build` | Build library (tsdown, unbundled ESM) | -| `pnpm test` | Run all tests (Vitest) | -| `pnpm test -- ComponentName` | Run tests matching a pattern | -| `pnpm test -u -- ComponentName` | Update snapshots for a component | -| `pnpm fix` | Lint + format (ESLint + Prettier) | -| `pnpm size` | Check bundle size limits | -| `pnpm chromatic` | Visual regression tests | -| `pnpm add-icons` | Add new icons from tabler | -| `pnpm audit-docs` | Audit component API ↔ docs ↔ argTypes sync. Uses TS Compiler API for full type resolution. Options: `--component=Name` (single component), `--fix-stories` (auto-add/remove argTypes in `.stories.tsx`), `--fix-docs` (auto-update `### Style Properties` sections in `.docs.mdx`), `--json`, `--verbose`, `--all-props`. **Run after changing a component's API or adding a new component.** | + +| Command | Description | +| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `pnpm storybook` | Start Storybook on port 6060 | +| `pnpm build` | Build library (tsdown, unbundled ESM) | +| `pnpm test` | Run all tests (Vitest) | +| `pnpm test -- ComponentName` | Run tests matching a pattern | +| `pnpm test -u -- ComponentName` | Update snapshots for a component | +| `pnpm fix` | Lint + format (ESLint + Prettier) | +| `pnpm size` | Check bundle size limits | +| `pnpm chromatic` | Visual regression tests | +| `pnpm add-icons` | Add new icons from tabler | +| `pnpm audit-docs` | Audit component API ↔ docs ↔ argTypes sync. Uses TS Compiler API for full type resolution. Options: `--component=Name` (single component), `--fix-stories` (auto-add/remove argTypes in `.stories.tsx`), `--fix-docs` (auto-update `### Style Properties` sections in `.docs.mdx`), `--json`, `--verbose`, `--all-props`. **Run after changing a component's API or adding a new component.** | + ## Stack @@ -91,11 +95,11 @@ ComponentName/ ## Creating Components -The full guide for creating components — including style props (`styleProps` vs `extractStyles`), `filterBaseProps`, modifiers, sub-elements, React Aria integration, variants, `useEvent`, and complete examples — is maintained in **`src/stories/CreateComponent.docs.mdx`** (rendered in Storybook under **Getting Started / Create Component**). +The full guide for creating components — including style props (`styleProps` vs `extractStyles`), `filterBaseProps`, modifiers, sub-elements, React Aria integration, variants, `useEvent`, and complete examples — is maintained in `**src/stories/CreateComponent.docs.mdx`** (rendered in Storybook under **Getting Started / Create Component**). ## Design System Reference -The full reference for tokens, presets, colors, modifiers, state syntax, recipes, icons, and the form system is maintained in **`src/stories/Usage.docs.mdx`** (rendered in Storybook under **Getting Started / Usage**). +The full reference for tokens, presets, colors, modifiers, state syntax, recipes, icons, and the form system is maintained in `**src/stories/Usage.docs.mdx`** (rendered in Storybook under **Getting Started / Usage**). Refer to that file for: @@ -133,3 +137,4 @@ Refer to that file for: - **Barrel exports:** each category has `index.ts` files; everything re-exports through `src/index.ts`. - **Compound components:** `Object.assign(Button, { Group: ButtonGroup, Split: ButtonSplit })`. - **Tasty re-exports:** Only types are re-exported from `@tenphi/tasty`. Runtime imports (`tasty`, `extractStyles`, `filterBaseProps`) come directly from `@tenphi/tasty`. + diff --git a/src/components/fields/RadioGroup/Radio.tsx b/src/components/fields/RadioGroup/Radio.tsx index b61dac93b..3a49cfc1f 100644 --- a/src/components/fields/RadioGroup/Radio.tsx +++ b/src/components/fields/RadioGroup/Radio.tsx @@ -216,11 +216,12 @@ function Radio(props: CubeRadioProps, ref) { (effectiveType === 'tabs' ? 'large' : 'medium')) as CubeItemProps['size']; // Apply size mapping for tabs mode button radios. - // Tabs mode supports 'medium' and 'large' (default) API sizes, - // mapped to smaller Item sizes: medium -> small, large -> medium. + // Tabs mode supports 'large' (default) and 'medium' API sizes, + // mapped to Item button sizes: large -> medium (32px), medium -> xsmall (24px). + // Total height includes container padding (2*4px): large=40px, medium=32px. if (effectiveType === 'tabs' && isButton) { if (effectiveSize === 'medium') { - effectiveSize = 'small'; + effectiveSize = 'xsmall'; } else { effectiveSize = 'medium'; } diff --git a/src/components/fields/RadioGroup/RadioGroup.stories.tsx b/src/components/fields/RadioGroup/RadioGroup.stories.tsx index 895ad9547..fa4ca8901 100644 --- a/src/components/fields/RadioGroup/RadioGroup.stories.tsx +++ b/src/components/fields/RadioGroup/RadioGroup.stories.tsx @@ -236,47 +236,12 @@ export const ButtonGroupSizes: StoryFn = () => ( export const TabsGroupSizes: StoryFn = () => ( <> - - Yes - No - Maybe - - - Yes - No - Maybe - - + Yes No Maybe - - Yes - No - Maybe - - + Yes No Maybe diff --git a/src/components/navigation/Tabs/Tabs.docs.mdx b/src/components/navigation/Tabs/Tabs.docs.mdx index 165955e6d..dc0f50ebe 100644 --- a/src/components/navigation/Tabs/Tabs.docs.mdx +++ b/src/components/navigation/Tabs/Tabs.docs.mdx @@ -131,7 +131,7 @@ For individual tabs: - `medium` - Default size with t3m typography - `large` - Larger tabs with t3m typography -**Note:** Radio type supports `large` (default) and `medium` sizes, mapped to Item sizes `medium` (32px) and `small` (28px) respectively. +**Note:** Radio type supports `large` (default, 40px total) and `medium` (32px total) sizes, mapped to Item button sizes `medium` (32px) and `xsmall` (24px) respectively. diff --git a/src/components/navigation/Tabs/Tabs.stories.tsx b/src/components/navigation/Tabs/Tabs.stories.tsx index 228a8bcfd..087d54e59 100644 --- a/src/components/navigation/Tabs/Tabs.stories.tsx +++ b/src/components/navigation/Tabs/Tabs.stories.tsx @@ -387,6 +387,26 @@ export const RadioTypeWithPanels: Story = { ), }; +/** + * Radio type sizes — large (default, 40px total) and medium (32px total) + */ +export const RadioTypeSizes: Story = { + render: (args) => ( + + + + + + + + + + + + + ), +}; + /** * Large size tabs for emphasis */ diff --git a/src/components/navigation/Tabs/types.ts b/src/components/navigation/Tabs/types.ts index 35f9cd75f..8e8db711a 100644 --- a/src/components/navigation/Tabs/types.ts +++ b/src/components/navigation/Tabs/types.ts @@ -23,10 +23,11 @@ export type TabSize = 'xsmall' | 'small' | 'medium' | 'large'; /** * Size mapping for radio type tabs. * Radio type supports 'large' (default) and 'medium' API sizes, - * mapped to Item sizes: large -> medium (32px), medium -> small (28px). + * mapped to Item button sizes: large -> medium (32px), medium -> xsmall (24px). + * Total height includes container padding (2*4px): large=40px, medium=32px. */ export const RADIO_SIZE_MAP: Record<'medium' | 'large', TabSize> = { - medium: 'small', + medium: 'xsmall', large: 'medium', }; @@ -89,7 +90,8 @@ type OmittedItemProps = export interface TabStyleProps extends Omit { /** * Tab size. Supports 'xsmall', 'small', 'medium', 'large'. - * Radio type supports 'large' (default) and 'medium', mapped to Item sizes medium/small. + * Radio type supports 'large' (default, 40px) and 'medium' (32px), + * mapped to Item button sizes medium (32px) and xsmall (24px). */ size?: TabSize; /** Visual appearance type. */ @@ -124,7 +126,8 @@ export interface CubeTabsProps type?: TabType; /** * Tab size. Supports 'xsmall', 'small', 'medium', 'large'. - * Radio type supports 'large' (default) and 'medium', mapped to Item sizes medium/small. + * Radio type supports 'large' (default, 40px) and 'medium' (32px), + * mapped to Item button sizes medium (32px) and xsmall (24px). * @default 'medium' */ size?: TabSize; From 8ec98f2ab8938aa06dc119339c26277361d0fc70 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 3 Apr 2026 12:14:22 +0000 Subject: [PATCH 3/5] chore: add changeset for tabs/radio size alignment Co-authored-by: Andrey Yamanov --- .changeset/tabs-radio-size-alignment.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/tabs-radio-size-alignment.md diff --git a/.changeset/tabs-radio-size-alignment.md b/.changeset/tabs-radio-size-alignment.md new file mode 100644 index 000000000..f98d5335e --- /dev/null +++ b/.changeset/tabs-radio-size-alignment.md @@ -0,0 +1,9 @@ +--- +"@cube-dev/ui-kit": patch +--- + +fix(Tabs, RadioGroup): align radio/tabs size mapping + +Both `Tabs type="radio"` and `Radio.Tabs` now use the same two API sizes with consistent Item button mappings: +- `large` (default): medium button (32px), 40px total +- `medium`: xsmall button (24px), 32px total From 9ef5e8a09ab7532f2abd6fc1eef356f0ae5caaa0 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Fri, 3 Apr 2026 14:27:22 +0200 Subject: [PATCH 4/5] fix(Tabs): default radio type size to 'large' to match Radio.Tabs TabButton fell back to 'medium' regardless of type, producing 32px tabs vs 40px from Radio.Tabs. Use the same conditional default. Made-with: Cursor --- src/components/navigation/Tabs/TabButton.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/navigation/Tabs/TabButton.tsx b/src/components/navigation/Tabs/TabButton.tsx index 3b21c077f..301c1706c 100644 --- a/src/components/navigation/Tabs/TabButton.tsx +++ b/src/components/navigation/Tabs/TabButton.tsx @@ -447,7 +447,9 @@ export function TabButton({ item, tabData, isLastTab }: TabButtonProps) { }, [actions]); // Determine effective size - const effectiveSize = tabData.size ?? size ?? 'medium'; + // For radio type, default to 'large' to match Radio.Tabs behavior (40px total) + const effectiveSize = + tabData.size ?? size ?? (effectiveType === 'radio' ? 'large' : 'medium'); const itemSize = effectiveType === 'radio' ? RADIO_SIZE_MAP[effectiveSize === 'large' ? 'large' : 'medium'] From 43d586a57df31c3bff16dcf28693137f9ed70378 Mon Sep 17 00:00:00 2001 From: Andrey Yamanov Date: Fri, 3 Apr 2026 16:43:09 +0200 Subject: [PATCH 5/5] fix(Tabs,RadioGroup): default radio/tabs size to 'medium' and use RADIO_SIZE_MAP consistently Both components now default to 'medium' and use the shared RADIO_SIZE_MAP for size remapping, eliminating divergent fallback behavior for unsupported sizes. Made-with: Cursor --- src/components/fields/RadioGroup/Radio.tsx | 14 +++++--------- src/components/navigation/Tabs/TabButton.tsx | 4 +--- src/components/navigation/Tabs/types.ts | 5 ++--- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/components/fields/RadioGroup/Radio.tsx b/src/components/fields/RadioGroup/Radio.tsx index 3a49cfc1f..365bca27d 100644 --- a/src/components/fields/RadioGroup/Radio.tsx +++ b/src/components/fields/RadioGroup/Radio.tsx @@ -17,6 +17,7 @@ import { extractStyles } from '../../../utils/styles'; import { CubeItemProps, Item } from '../../content/Item/Item'; import { INLINE_LABEL_STYLES, useFieldProps, useFormProps } from '../../form'; import { HiddenInput } from '../../HiddenInput'; +import { RADIO_SIZE_MAP } from '../../navigation/Tabs/types'; import { useRadioProvider } from './context'; import { RadioGroup } from './RadioGroup'; @@ -213,18 +214,13 @@ function Radio(props: CubeRadioProps, ref) { // Determine effective size with priority: prop > context > default let effectiveSize: CubeItemProps['size'] = (size ?? contextSize ?? - (effectiveType === 'tabs' ? 'large' : 'medium')) as CubeItemProps['size']; + 'medium') as CubeItemProps['size']; // Apply size mapping for tabs mode button radios. - // Tabs mode supports 'large' (default) and 'medium' API sizes, - // mapped to Item button sizes: large -> medium (32px), medium -> xsmall (24px). - // Total height includes container padding (2*4px): large=40px, medium=32px. + // API sizes mapped to Item button sizes: large -> medium (40px), medium -> xsmall (32px). if (effectiveType === 'tabs' && isButton) { - if (effectiveSize === 'medium') { - effectiveSize = 'xsmall'; - } else { - effectiveSize = 'medium'; - } + effectiveSize = + RADIO_SIZE_MAP[effectiveSize === 'large' ? 'large' : 'medium']; } // Determine effective button type diff --git a/src/components/navigation/Tabs/TabButton.tsx b/src/components/navigation/Tabs/TabButton.tsx index 301c1706c..3b21c077f 100644 --- a/src/components/navigation/Tabs/TabButton.tsx +++ b/src/components/navigation/Tabs/TabButton.tsx @@ -447,9 +447,7 @@ export function TabButton({ item, tabData, isLastTab }: TabButtonProps) { }, [actions]); // Determine effective size - // For radio type, default to 'large' to match Radio.Tabs behavior (40px total) - const effectiveSize = - tabData.size ?? size ?? (effectiveType === 'radio' ? 'large' : 'medium'); + const effectiveSize = tabData.size ?? size ?? 'medium'; const itemSize = effectiveType === 'radio' ? RADIO_SIZE_MAP[effectiveSize === 'large' ? 'large' : 'medium'] diff --git a/src/components/navigation/Tabs/types.ts b/src/components/navigation/Tabs/types.ts index 8e8db711a..da7bec613 100644 --- a/src/components/navigation/Tabs/types.ts +++ b/src/components/navigation/Tabs/types.ts @@ -22,9 +22,8 @@ export type TabSize = 'xsmall' | 'small' | 'medium' | 'large'; /** * Size mapping for radio type tabs. - * Radio type supports 'large' (default) and 'medium' API sizes, - * mapped to Item button sizes: large -> medium (32px), medium -> xsmall (24px). - * Total height includes container padding (2*4px): large=40px, medium=32px. + * API sizes mapped to Item button sizes: + * medium (default) -> xsmall (32px total), large -> medium (40px total). */ export const RADIO_SIZE_MAP: Record<'medium' | 'large', TabSize> = { medium: 'xsmall',