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 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 f8792d0c6..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' ? 'small' : 'medium')) as CubeItemProps['size']; + 'medium') as CubeItemProps['size']; - // Apply size mapping for tabs mode button radios + // Apply size mapping for tabs mode button radios. + // API sizes mapped to Item button sizes: large -> medium (40px), medium -> xsmall (32px). if (effectiveType === 'tabs' && isButton) { - if (effectiveSize === 'small' || effectiveSize === 'medium') { - effectiveSize = 'xsmall'; - } else if (effectiveSize === 'large') { - effectiveSize = 'medium'; - } else if (effectiveSize === 'xlarge') { - effectiveSize = 'large'; - } - // 'xsmall' stays 'xsmall' + effectiveSize = + RADIO_SIZE_MAP[effectiveSize === 'large' ? 'large' : 'medium']; } // Determine effective button type 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 af4421608..dc0f50ebe 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, 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 55a024a71..da7bec613 100644 --- a/src/components/navigation/Tabs/types.ts +++ b/src/components/navigation/Tabs/types.ts @@ -22,7 +22,8 @@ export type TabSize = 'xsmall' | 'small' | 'medium' | 'large'; /** * Size mapping for radio type tabs. - * Radio type uses smaller sizes similar to RadioGroup tabs mode. + * 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', @@ -88,7 +89,8 @@ 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, 40px) and 'medium' (32px), + * mapped to Item button sizes medium (32px) and xsmall (24px). */ size?: TabSize; /** Visual appearance type. */ @@ -123,7 +125,8 @@ 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, 40px) and 'medium' (32px), + * mapped to Item button sizes medium (32px) and xsmall (24px). * @default 'medium' */ size?: TabSize;