Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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/standardize-small-controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/kumo": patch
---

Standardize small interactive controls at 30px high with the base text size, including medium corner radii for small toolbars, and restore `Toolbar size="sm"` as a supported API while keeping `xs` and `lg` deprecated.
4 changes: 2 additions & 2 deletions packages/kumo-docs-astro/src/components/demos/ToolbarDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export function ToolbarDemo() {
);
}

/** @deprecated Toolbar size customization remains for compatibility. */
/** Supported compact and default Toolbar sizes. */
export function ToolbarSizesDemo() {
return (
<div className="grid gap-3">
{(["xs", "sm", "base", "lg"] as const).map((size) => (
{(["sm", "base"] as const).map((size) => (
<div key={size} className="flex items-center gap-3">
<span className="w-10 text-sm text-kumo-subtle">{size}</span>
<Toolbar size={size} className="w-fit">
Expand Down
14 changes: 7 additions & 7 deletions packages/kumo-docs-astro/src/pages/components/toolbar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function Example() {
Toolbar item components intentionally own grouped control presentation:

- Every `Toolbar.*` control uses the `base` size by default.
- The Toolbar `size` prop remains available for compatibility but is deprecated; omit it for the base size.
- Set `size="sm"` for a compact 30px-high toolbar with medium corner radii.
- `Toolbar.Button` always renders with quiet toolbar button styling.
- `Toolbar.InputGroup` passes props directly to `InputGroup` with the resolved toolbar size.
- Give Select `render={<Toolbar.Button />}` to compose its trigger into the toolbar.
Expand Down Expand Up @@ -133,11 +133,11 @@ inline addon or suffix.
<ToolbarInputGroupDemo client:visible />
</ComponentExample>

### Deprecated sizing
### Sizing

The `size` prop still supports `xs`, `sm`, `base`, and `lg` for compatibility,
but it is deprecated and will be removed in a future major release. Omit it to
use the default `base` size.
Use `size="sm"` for compact toolbars or omit `size` to use the default `base`
size. The legacy `xs` and `lg` values are deprecated and remain available only
for backwards compatibility.

<ComponentExample demo="ToolbarSizesDemo">
<ToolbarSizesDemo client:visible />
Expand Down Expand Up @@ -187,10 +187,10 @@ options instead.
<td>Toolbar controls rendered as one grouped card.</td>
</tr>
<tr>
<td><code>size</code> <strong>Deprecated</strong></td>
<td><code>size</code></td>
<td><code>&quot;xs&quot; | &quot;sm&quot; | &quot;base&quot; | &quot;lg&quot;</code></td>
<td><code>&quot;base&quot;</code></td>
<td>Sets every supported item size. Omit this deprecated prop to use the default base size.</td>
<td>Sets every supported item size. Use <code>&quot;sm&quot;</code> for compact toolbars; <code>&quot;xs&quot;</code> and <code>&quot;lg&quot;</code> are deprecated compatibility values.</td>
</tr>
<tr>
<td><code>className</code></td>
Expand Down
4 changes: 2 additions & 2 deletions packages/kumo-figma/src/generators/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export function getButtonParsedShapeStyles(shape: string) {
*
* Source: button.tsx KUMO_BUTTON_VARIANTS.compactSize
* - xs: size-3.5 = 14px
* - sm: size-6.5 = 26px
* - sm: size-7.5 = 30px
* - base: size-9 = 36px
* - lg: size-10 = 40px
*
Expand All @@ -179,7 +179,7 @@ export function getCompactSizeMap(): Record<string, number> {
// Compact size classes from KUMO_BUTTON_VARIANTS.compactSize in button.tsx
const compactSizeClasses: Record<string, string> = {
xs: "size-3.5",
sm: "size-6.5",
sm: "size-7.5",
base: "size-9",
lg: "size-10",
};
Expand Down
8 changes: 4 additions & 4 deletions packages/kumo-figma/src/generators/drift-detection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ describe("Figma Plugin - Registry Sync Validation", () => {
// Expected compact sizes from Button COMPACT_SIZE_MAP (derived from registry)
const expectedSizes: Record<string, number> = {
xs: 14, // size-3.5 = 3.5 * 4 = 14px
sm: 26, // size-6.5 = 6.5 * 4 = 26px
sm: 30, // size-7.5 = 7.5 * 4 = 30px
base: 36, // size-9 = 9 * 4 = 36px
lg: 40, // size-10 = 10 * 4 = 40px
};
Expand Down Expand Up @@ -817,7 +817,7 @@ describe("Figma Plugin - Phase 6 Magic Number Enforcement", () => {
content,
);

// Check for explicit hardcoded COMPACT_SIZE_MAP definition: { xs: 14, sm: 26, base: 36, lg: 40 }
// Check for explicit hardcoded COMPACT_SIZE_MAP definition: { xs: 14, sm: 30, base: 36, lg: 40 }
// This is the most drift-prone pattern - explicit recreation of button compact sizes
const hasHardcodedCompactMap =
/(?:const|let|var)\s+COMPACT_SIZE_MAP[^=]*=\s*\{[^}]*xs:\s*14[^}]*sm:\s*26/.test(
Expand All @@ -841,7 +841,7 @@ describe("Figma Plugin - Phase 6 Magic Number Enforcement", () => {
` - const COMPACT_SIZE_MAP = FALLBACK_VALUES.buttonCompactSize;\n` +
` Or reference values directly:\n` +
` - FALLBACK_VALUES.buttonCompactSize.xs (14px)\n` +
` - FALLBACK_VALUES.buttonCompactSize.sm (26px)\n` +
` - FALLBACK_VALUES.buttonCompactSize.sm (30px)\n` +
` - FALLBACK_VALUES.buttonCompactSize.base (36px)\n` +
` - FALLBACK_VALUES.buttonCompactSize.lg (40px)\n`,
);
Expand Down Expand Up @@ -1223,7 +1223,7 @@ describe("Figma Plugin - CSS Theme Sync Validation", () => {

// Verify expected values
expect(themeData.kumo.buttonCompactSize.xs).toBe(14); // size-3.5 = 14px
expect(themeData.kumo.buttonCompactSize.sm).toBe(26); // size-6.5 = 26px
expect(themeData.kumo.buttonCompactSize.sm).toBe(30); // size-7.5 = 30px
expect(themeData.kumo.buttonCompactSize.base).toBe(36); // size-9 = 36px
expect(themeData.kumo.buttonCompactSize.lg).toBe(40); // size-10 = 40px
});
Expand Down
2 changes: 1 addition & 1 deletion packages/kumo-figma/src/generators/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function getSizeConfigFromRegistry(size: string) {
{ height: number; paddingX: number; fontSize: number; borderRadius: number }
> = {
xs: { height: 20, paddingX: 6, fontSize: 12, borderRadius: 2 },
sm: { height: 26, paddingX: 8, fontSize: 12, borderRadius: 6 },
sm: { height: 30, paddingX: 8, fontSize: 14, borderRadius: 6 },
base: { height: 36, paddingX: 12, fontSize: 16, borderRadius: 8 },
lg: { height: 40, paddingX: 16, fontSize: 16, borderRadius: 8 },
};
Expand Down
22 changes: 8 additions & 14 deletions packages/kumo-figma/src/generators/refresh-button.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,12 @@ describe("RefreshButton Generator - Icon Size Configuration", () => {
lg: 20,
};

const COMPACT_SIZE_MAP: Record<string, number> = {
xs: 14,
sm: 26,
base: 36,
lg: 40,
};
const COMPACT_SIZE_MAP = FALLBACK_VALUES.buttonCompactSize;

for (const size of sizeProp.values) {
expect(REFRESH_ICON_SIZE[size]).toBeLessThan(COMPACT_SIZE_MAP[size]);
expect(REFRESH_ICON_SIZE[size]).toBeLessThan(
COMPACT_SIZE_MAP[size as keyof typeof COMPACT_SIZE_MAP],
);
}
});

Expand Down Expand Up @@ -318,12 +315,7 @@ describe("RefreshButton Generator - Complete Variant Data", () => {
});

it("should have all required configuration maps", () => {
const COMPACT_SIZE_MAP: Record<string, number> = {
xs: 14,
sm: 26,
base: 36,
lg: 40,
};
const COMPACT_SIZE_MAP = FALLBACK_VALUES.buttonCompactSize;

const REFRESH_ICON_SIZE: Record<string, number> = {
xs: 12,
Expand All @@ -337,7 +329,9 @@ describe("RefreshButton Generator - Complete Variant Data", () => {

// Verify all size values have mappings
for (const size of sizeProp.values) {
expect(COMPACT_SIZE_MAP[size]).toBeDefined();
expect(
COMPACT_SIZE_MAP[size as keyof typeof COMPACT_SIZE_MAP],
).toBeDefined();
expect(REFRESH_ICON_SIZE[size]).toBeDefined();
}
});
Expand Down
4 changes: 2 additions & 2 deletions packages/kumo-figma/src/generators/sensitive-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ function getSizeConfigFromRegistry(size: string): {
borderRadius: BORDER_RADIUS.xs,
},
sm: {
height: 26,
height: 30,
paddingX: 8,
fontSize: 12,
fontSize: 14,
borderRadius: BORDER_RADIUS.md,
},
base: {
Expand Down
2 changes: 1 addition & 1 deletion packages/kumo-figma/src/generators/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ export const FALLBACK_VALUES = {
buttonCompactSize: {
/** Extra small compact button (size-3.5) */
xs: themeData.computed.buttonCompactSize.xs,
/** Small compact button (size-6.5) */
/** Small compact button (size-7.5) */
sm: themeData.computed.buttonCompactSize.sm,
/** Base compact button (size-9) */
base: themeData.computed.buttonCompactSize.base,
Expand Down
4 changes: 2 additions & 2 deletions packages/kumo-figma/src/parsers/tailwind-to-figma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ export function parseTailwindClasses(classes: string): ParsedStyles {
continue;
}

// Height: h-5, h-6.5, h-9, h-10
// Height: h-5, h-7.5, h-9, h-10
const heightMatch = cls.match(/^h-(\d+\.?\d*)$/);
if (heightMatch) {
result.height = getOrDefault(
Expand All @@ -266,7 +266,7 @@ export function parseTailwindClasses(classes: string): ParsedStyles {
continue;
}

// Size (width and height): size-3.5, size-6.5, size-9, size-10
// Size (width and height): size-3.5, size-7.5, size-9, size-10
const sizeMatch = cls.match(/^size-(\d+\.?\d*)$/);
if (sizeMatch) {
const size = getOrDefault(
Expand Down
14 changes: 7 additions & 7 deletions packages/kumo/scripts/component-registry/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -630,21 +630,21 @@ export const COMPONENT_STYLING_METADATA: Record<string, ComponentStyling> = {
base: "bg-kumo-control text-kumo-default ring ring-kumo-line",
sizes: {
xs: "h-5 gap-1 rounded-sm px-1.5 text-xs",
sm: "h-6.5 gap-1 rounded-md px-2 text-xs",
sm: "h-7.5 gap-1 rounded-md px-2 text-base",
base: "h-9 gap-1.5 rounded-lg px-3 text-base",
lg: "h-10 gap-2 rounded-lg px-4 text-base",
},
},
sizeVariants: {
sm: {
height: 26,
classes: "text-xs",
height: 30,
classes: "text-base",
buttonSize: "sm",
dimensions: {
paddingX: 8,
gap: 1,
borderRadius: 6,
fontSize: 12,
fontSize: 14,
},
},
base: {
Expand Down Expand Up @@ -722,11 +722,11 @@ export const COMPONENT_STYLING_METADATA: Record<string, ComponentStyling> = {
},
},
sm: {
height: 26,
classes: "h-6.5 gap-1 rounded-md px-2 text-xs",
height: 30,
classes: "h-7.5 gap-1 rounded-md px-2 text-base",
dimensions: {
paddingX: 8,
fontSize: 12,
fontSize: 14,
borderRadius: 6,
},
},
Expand Down
2 changes: 1 addition & 1 deletion packages/kumo/src/components/autocomplete/autocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface KumoAutocompleteVariantsProps {
/**
* Size of the autocomplete input. Matches Input component sizes.
* - `"xs"` — Extra small for compact UIs (h-5 / 20px)
* - `"sm"` — Small for secondary fields (h-6.5 / 26px)
* - `"sm"` — Small for secondary fields (h-7.5 / 30px)
* - `"base"` — Default size (h-9 / 36px)
* - `"lg"` — Large for prominent fields (h-10 / 40px)
* @default "base"
Expand Down
8 changes: 4 additions & 4 deletions packages/kumo/src/components/banner/banner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe("Banner", () => {
);

const cta = screen.getByTestId("cta");
expect(cta.className).toContain("h-6.5");
expect(cta.className).toContain("h-7.5");
expect(cta.className).toContain("px-2");
expect(cta.getAttribute("aria-label")).toBe("Dismiss");
expect(screen.getByTestId("icon")).toBeTruthy();
Expand All @@ -128,9 +128,9 @@ describe("Banner", () => {
);

const cta = screen.getByTestId("cta");
expect(cta.className).toContain("h-6.5");
expect(cta.className).toContain("h-7.5");
expect(cta.className).toContain("px-2");
expect(cta.className).toContain("text-xs");
expect(cta.className).toContain("text-base");
});

it("applies compact spacing for the sm banner size", () => {
Expand All @@ -157,7 +157,7 @@ describe("Banner", () => {
);

const cta = screen.getByTestId("cta");
// Inherits the banner's size => xs (h-5), not the standalone sm default (h-6.5).
// Inherits the banner's size => xs (h-5), not the standalone sm default (h-7.5).
expect(cta.className).toContain("h-5");
expect(cta.className).toContain("px-1.5");
});
Expand Down
9 changes: 9 additions & 0 deletions packages/kumo/src/components/button/button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ describe("Button", () => {
expect(button.classList.contains("select-text")).toBe(false);
});

it("uses the standardized small control size", () => {
const classes = buttonVariants({ size: "sm" });
const compactClasses = buttonVariants({ size: "sm", shape: "square" });

expect(classes).toContain("h-7.5");
expect(classes).toContain("text-base");
expect(compactClasses).toContain("size-7.5");
});

it("forwards ref to the <button> DOM node", () => {
const ref = React.createRef<HTMLButtonElement>();
render(<Button ref={ref}>Click</Button>);
Expand Down
4 changes: 2 additions & 2 deletions packages/kumo/src/components/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const KUMO_BUTTON_VARIANTS = {
description: "Extra small button for compact UIs",
},
sm: {
classes: "h-6.5 gap-1 rounded-md px-2 text-xs",
classes: "h-7.5 gap-1 rounded-md px-2 text-base",
description: "Small button for secondary actions",
},
base: {
Expand All @@ -42,7 +42,7 @@ export const KUMO_BUTTON_VARIANTS = {
},
compactSize: {
xs: { classes: "size-3.5" },
sm: { classes: "size-6.5" },
sm: { classes: "size-7.5" },
base: { classes: "size-9" },
lg: { classes: "size-10" },
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ describe("ClipboardText", () => {
).toBeTruthy();
});

it("uses the standardized small control size", () => {
render(<ClipboardText text="sk_live_abc123" size="sm" />);
const clipboardText = screen.getByText("sk_live_abc123").parentElement;

expect(clipboardText?.className).toContain("h-7.5");
expect(clipboardText?.className).toContain("text-base");
});

it("copies text and announces copied state without tooltip", async () => {
render(<ClipboardText text="token-value" />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const COPIED_FEEDBACK_MS = 1500;
export const KUMO_CLIPBOARD_TEXT_VARIANTS = {
size: {
sm: {
classes: "text-xs",
classes: "text-base",
buttonSize: "sm" as const,
description: "Small clipboard text for compact UIs",
},
Expand Down
4 changes: 2 additions & 2 deletions packages/kumo/src/components/combobox/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export interface KumoComboboxVariantsProps {
/**
* Size of the combobox trigger. Matches Input component sizes.
* - `"xs"` — Extra small for compact UIs (h-5 / 20px)
* - `"sm"` — Small for secondary fields (h-6.5 / 26px)
* - `"sm"` — Small for secondary fields (h-7.5 / 30px)
* - `"base"` — Default size (h-9 / 36px)
* - `"lg"` — Large for prominent fields (h-10 / 40px)
* @default "base"
Expand Down Expand Up @@ -544,7 +544,7 @@ function Chip({
// Map size to min-height class for TriggerMultipleWithInput
const sizeToMinHeight: Record<KumoComboboxSize, string> = {
xs: "min-h-5",
sm: "min-h-6.5",
sm: "min-h-7.5",
base: "min-h-9",
lg: "min-h-10",
};
Expand Down
2 changes: 1 addition & 1 deletion packages/kumo/src/components/input-group/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export const INPUT_GROUP_SIZE: Record<KumoInputSize, InputGroupSizeTokens> = {
addonButtonOuterStart: "pl-1",
addonButtonOuterEnd: "pr-1",
suffixPad: "pr-2",
fontSize: "text-xs",
fontSize: "text-base",
iconSize: 13,
},
base: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Icon, IconProps } from "@phosphor-icons/react";
import { describe, expect, it, vi } from "vite-plus/test";
import { fireEvent, render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { InputGroup } from "./input-group";
import { InputGroup, KUMO_INPUT_GROUP_VARIANTS } from "./input-group";
import { INPUT_GROUP_SIZE, detectFocusMode } from "./context";
import type { KumoInputSize } from "../input/input";

Expand Down Expand Up @@ -322,6 +322,12 @@ describe("InputGroup", () => {
});

describe("size variants", () => {
it("uses the standardized small control size", () => {
expect(KUMO_INPUT_GROUP_VARIANTS.size.sm.classes).toContain("h-7.5");
expect(KUMO_INPUT_GROUP_VARIANTS.size.sm.classes).toContain("text-base");
expect(INPUT_GROUP_SIZE.sm.fontSize).toBe("text-base");
});

it("applies size to input", () => {
const { rerender } = render(
<InputGroup size="sm" label="Small">
Expand Down
Loading
Loading