diff --git a/bulma-ui/src/columns/Columns.stories.tsx b/bulma-ui/src/columns/Columns.stories.tsx index 62cd665ec..bc0b1db0a 100644 --- a/bulma-ui/src/columns/Columns.stories.tsx +++ b/bulma-ui/src/columns/Columns.stories.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { BulmaGapSize, Columns } from './Columns'; +import { Columns } from './Columns'; +import type { BulmaGapValue } from '../grid/Grid'; import { Column } from './Column'; import { Notification } from '../elements/Notification'; @@ -158,34 +159,35 @@ export const GapSizes: StoryObj = { render: () => ( <>

- You can control the gap between columns using gapSize and - responsive gap props. + You can control the gap between columns using gap and + responsive gap props (the same 0-8 scale as Grid's{' '} + gap prop).

- + - gapSize=0 + gap=0 - gapSize=0 + gap=0 - + - gapSize=3 + gap=3 - gapSize=3 + gap=3 - + - gapSizeMobile=1 gapSizeTablet=3 gapSizeDesktop=6 + gapMobile=1 gapTablet=3 gapDesktop=6 - gapSizeMobile=1 gapSizeTablet=3 gapSizeDesktop=6 + gapMobile=1 gapTablet=3 gapDesktop=6 @@ -323,24 +325,24 @@ export const MultilineGapless: StoryObj = { }; // --- VARIABLE GAP STORY --- -export const VariableGap: StoryObj<{ gapSize: number }> = { +export const VariableGap: StoryObj<{ gap: number }> = { args: { - gapSize: 2, + gap: 2, }, argTypes: { - gapSize: { + gap: { control: { type: 'number', min: 0, max: 8, step: 1 }, description: 'Bulma gap size (0-8)', }, }, - render: ({ gapSize }) => ( + render: ({ gap }) => ( <>

- The gapSize property controls the variable gap between - columns. Change the value of the storybook control for{' '} - gapSize to adjust spacing. + The gap property controls the variable gap between columns. + Change the value of the storybook control for gap to adjust + spacing.

- + Side @@ -352,7 +354,7 @@ export const VariableGap: StoryObj<{ gapSize: number }> = { - + Three columns @@ -369,7 +371,7 @@ export const VariableGap: StoryObj<{ gapSize: number }> = { - + {Array.from({ length: 12 }).map((_, i) => ( @@ -391,16 +393,16 @@ export const BreakpointBasedColumnGaps: StoryObj = { to see this in action
- gapSizeMobile={1} gapSizeTablet={4} gapSizeDesktop={3}{' '} - gapSizeWidescreen={8} gapSizeFullhd={2} + gapMobile={1} gapTablet={4} gapDesktop={3} gapWidescreen={8}{' '} + gapFullhd={2}

{[...Array(6)].map((_, idx) => ( diff --git a/bulma-ui/src/columns/Columns.tsx b/bulma-ui/src/columns/Columns.tsx index 8e2f50225..4aaea8562 100644 --- a/bulma-ui/src/columns/Columns.tsx +++ b/bulma-ui/src/columns/Columns.tsx @@ -5,29 +5,14 @@ import { BulmaClassesProps, validColors, } from '../helpers/useBulmaClasses'; +import type { BulmaGapValue } from '../grid/Grid'; /** * Possible values for the Bulma columns gap size. + * @deprecated Use {@link BulmaGapValue} instead — `gapSize*` and `gap*` share + * the same 0-8 scale. */ -export type BulmaGapSize = - | 0 - | 1 - | 2 - | 3 - | 4 - | 5 - | 6 - | 7 - | 8 - | '0' - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8'; +export type BulmaGapSize = BulmaGapValue; /** * Props for the Columns component. @@ -42,12 +27,18 @@ export type BulmaGapSize = * @property {boolean} [isVCentered] - Vertically center columns. * @property {boolean} [isMobile] - Only apply columns styles on mobile. * @property {boolean} [isDesktop] - Only apply columns styles on desktop. - * @property {BulmaGapSize} [gapSize] - Gap size for all breakpoints. - * @property {BulmaGapSize} [gapSizeMobile] - Gap size for mobile. - * @property {BulmaGapSize} [gapSizeTablet] - Gap size for tablet. - * @property {BulmaGapSize} [gapSizeDesktop] - Gap size for desktop. - * @property {BulmaGapSize} [gapSizeWidescreen] - Gap size for widescreen. - * @property {BulmaGapSize} [gapSizeFullhd] - Gap size for fullhd. + * @property {BulmaGapValue} [gap] - Gap size for all breakpoints. Wins over `gapSize` if both are set. + * @property {BulmaGapValue} [gapMobile] - Gap size for mobile. Wins over `gapSizeMobile` if both are set. + * @property {BulmaGapValue} [gapTablet] - Gap size for tablet. Wins over `gapSizeTablet` if both are set. + * @property {BulmaGapValue} [gapDesktop] - Gap size for desktop. Wins over `gapSizeDesktop` if both are set. + * @property {BulmaGapValue} [gapWidescreen] - Gap size for widescreen. Wins over `gapSizeWidescreen` if both are set. + * @property {BulmaGapValue} [gapFullhd] - Gap size for fullhd. Wins over `gapSizeFullhd` if both are set. + * @property {BulmaGapSize} [gapSize] - Gap size for all breakpoints. @deprecated Use `gap` instead. + * @property {BulmaGapSize} [gapSizeMobile] - Gap size for mobile. @deprecated Use `gapMobile` instead. + * @property {BulmaGapSize} [gapSizeTablet] - Gap size for tablet. @deprecated Use `gapTablet` instead. + * @property {BulmaGapSize} [gapSizeDesktop] - Gap size for desktop. @deprecated Use `gapDesktop` instead. + * @property {BulmaGapSize} [gapSizeWidescreen] - Gap size for widescreen. @deprecated Use `gapWidescreen` instead. + * @property {BulmaGapSize} [gapSizeFullhd] - Gap size for fullhd. @deprecated Use `gapFullhd` instead. * @property {React.ReactNode} [children] - Columns to render within the container. */ export interface ColumnsProps @@ -65,11 +56,24 @@ export interface ColumnsProps isMobile?: boolean; isDesktop?: boolean; + gap?: BulmaGapValue; + gapMobile?: BulmaGapValue; + gapTablet?: BulmaGapValue; + gapDesktop?: BulmaGapValue; + gapWidescreen?: BulmaGapValue; + gapFullhd?: BulmaGapValue; + + /** @deprecated Use `gap` instead — `gap` wins if both are set. */ gapSize?: BulmaGapSize; + /** @deprecated Use `gapMobile` instead — `gapMobile` wins if both are set. */ gapSizeMobile?: BulmaGapSize; + /** @deprecated Use `gapTablet` instead — `gapTablet` wins if both are set. */ gapSizeTablet?: BulmaGapSize; + /** @deprecated Use `gapDesktop` instead — `gapDesktop` wins if both are set. */ gapSizeDesktop?: BulmaGapSize; + /** @deprecated Use `gapWidescreen` instead — `gapWidescreen` wins if both are set. */ gapSizeWidescreen?: BulmaGapSize; + /** @deprecated Use `gapFullhd` instead — `gapFullhd` wins if both are set. */ gapSizeFullhd?: BulmaGapSize; children?: React.ReactNode; @@ -94,6 +98,12 @@ export const Columns: React.FC = ({ isVCentered, isMobile, isDesktop, + gap, + gapMobile, + gapTablet, + gapDesktop, + gapWidescreen, + gapFullhd, gapSize, gapSizeMobile, gapSizeTablet, @@ -103,6 +113,13 @@ export const Columns: React.FC = ({ children, ...props }) => { + const resolvedGap = gap ?? gapSize; + const resolvedGapMobile = gapMobile ?? gapSizeMobile; + const resolvedGapTablet = gapTablet ?? gapSizeTablet; + const resolvedGapDesktop = gapDesktop ?? gapSizeDesktop; + const resolvedGapWidescreen = gapWidescreen ?? gapSizeWidescreen; + const resolvedGapFullhd = gapFullhd ?? gapSizeFullhd; + const { bulmaHelperClasses, rest } = useBulmaClasses({ color: textColor, backgroundColor: bgColor, @@ -113,17 +130,17 @@ export const Columns: React.FC = ({ // Build gap classes with prefixes const gapClasses = usePrefixedClassNames('', { - [`is-${gapSize}`]: gapSize !== undefined && gapSize !== null, - [`is-${gapSizeMobile}-mobile`]: - gapSizeMobile !== undefined && gapSizeMobile !== null, - [`is-${gapSizeTablet}-tablet`]: - gapSizeTablet !== undefined && gapSizeTablet !== null, - [`is-${gapSizeDesktop}-desktop`]: - gapSizeDesktop !== undefined && gapSizeDesktop !== null, - [`is-${gapSizeWidescreen}-widescreen`]: - gapSizeWidescreen !== undefined && gapSizeWidescreen !== null, - [`is-${gapSizeFullhd}-fullhd`]: - gapSizeFullhd !== undefined && gapSizeFullhd !== null, + [`is-${resolvedGap}`]: resolvedGap !== undefined && resolvedGap !== null, + [`is-${resolvedGapMobile}-mobile`]: + resolvedGapMobile !== undefined && resolvedGapMobile !== null, + [`is-${resolvedGapTablet}-tablet`]: + resolvedGapTablet !== undefined && resolvedGapTablet !== null, + [`is-${resolvedGapDesktop}-desktop`]: + resolvedGapDesktop !== undefined && resolvedGapDesktop !== null, + [`is-${resolvedGapWidescreen}-widescreen`]: + resolvedGapWidescreen !== undefined && resolvedGapWidescreen !== null, + [`is-${resolvedGapFullhd}-fullhd`]: + resolvedGapFullhd !== undefined && resolvedGapFullhd !== null, 'is-centered': !!isCentered, 'is-gapless': !!isGapless, 'is-multiline': !!isMultiline, diff --git a/bulma-ui/src/columns/__tests__/Columns.test.tsx b/bulma-ui/src/columns/__tests__/Columns.test.tsx index f47bd3037..ffcc11757 100644 --- a/bulma-ui/src/columns/__tests__/Columns.test.tsx +++ b/bulma-ui/src/columns/__tests__/Columns.test.tsx @@ -71,6 +71,88 @@ describe('Columns', () => { ); }); + it('applies custom gap classes via the preferred gap* props', () => { + const { container } = render( + + ); + expect(container.firstChild).toHaveClass( + 'is-2', + 'is-1-mobile', + 'is-0-tablet', + 'is-3-desktop', + 'is-8-widescreen', + 'is-2-fullhd' + ); + }); + + it('accepts gap values as strings, matching BulmaGapValue', () => { + const { container } = render(); + expect(container.firstChild).toHaveClass('is-4', 'is-1-mobile'); + }); + + it('prefers gap* props over the deprecated gapSize* props when both are set', () => { + const { container } = render( + + ); + expect(container.firstChild).toHaveClass( + 'is-5', + 'is-6-mobile', + 'is-7-tablet', + 'is-8-desktop', + 'is-0-widescreen', + 'is-1-fullhd' + ); + expect(container.firstChild).not.toHaveClass( + 'is-1', + 'is-2-mobile', + 'is-3-tablet', + 'is-4-desktop', + 'is-5-widescreen', + 'is-6-fullhd' + ); + }); + + it('falls back to the deprecated gapSize* props when gap* is not set', () => { + const { container } = render( + + ); + expect(container.firstChild).toHaveClass( + 'is-4', + 'is-3-mobile', + 'is-2-tablet', + 'is-1-desktop', + 'is-0-widescreen', + 'is-4-fullhd' + ); + }); + it('applies custom className', () => { const { container } = render(); expect(container.firstChild).toHaveClass('custom-class'); diff --git a/bulma-ui/src/grid/Grid.tsx b/bulma-ui/src/grid/Grid.tsx index aef3bc414..56193fb7e 100644 --- a/bulma-ui/src/grid/Grid.tsx +++ b/bulma-ui/src/grid/Grid.tsx @@ -7,9 +7,28 @@ import { } from '../helpers/useBulmaClasses'; /** - * Allowed gap values for Bulma grid. + * Allowed gap values for Bulma's 0-8 spacing scale, shared by `Grid` and + * `Columns`. Accepts the value as a number or a numeric string. */ -export type BulmaGapValue = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; +export type BulmaGapValue = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8'; /** * Allowed minimum column values for Bulma grid. */ diff --git a/bulma-ui/src/grid/__tests__/Grid.test.tsx b/bulma-ui/src/grid/__tests__/Grid.test.tsx index f8c494862..208032aad 100644 --- a/bulma-ui/src/grid/__tests__/Grid.test.tsx +++ b/bulma-ui/src/grid/__tests__/Grid.test.tsx @@ -33,6 +33,18 @@ describe('Grid', () => { expect(grid).toHaveClass('is-col-min-4'); }); + it('accepts gap, columnGap, rowGap as strings, matching BulmaGapValue', () => { + const { container } = render( + + + + ); + const grid = container.querySelector('.grid')!; + expect(grid).toHaveClass('is-gap-3'); + expect(grid).toHaveClass('is-column-gap-2'); + expect(grid).toHaveClass('is-row-gap-1'); + }); + it('renders as fixed-grid wrapper with grid inside', () => { const { container } = render( diff --git a/create-bestax/src/constants.ts b/create-bestax/src/constants.ts index aa12051cc..b4c8ae8a9 100644 --- a/create-bestax/src/constants.ts +++ b/create-bestax/src/constants.ts @@ -109,8 +109,8 @@ ${setupLines.join('\n')} - Never inline \`style={{}}\` — use the helper props every component accepts (\`m*\`/\`p*\` spacing, \`textColor\`/\`bgColor\`, \`display="flex"\`, \`flexDirection\`, \`alignItems\`). - Flex layouts have no \`gap\` helper — space children with margins (\`Grid\` has a real - \`gap\` prop and \`Columns\` has \`gapSize\`, so prefer those there). + Flex layouts have no \`gap\` helper — space children with margins (\`Grid\` and \`Columns\` + take a \`gap\` prop, so prefer that there). - Compose existing components before writing custom CSS; theme via \`Theme\` and \`--bulma-*\` variables, never hardcoded colors. - There is no test runner or Storybook in this app — don't assume one. diff --git a/docs/docs/api/columns/columns.md b/docs/docs/api/columns/columns.md index c3475b37b..4375563ff 100644 --- a/docs/docs/api/columns/columns.md +++ b/docs/docs/api/columns/columns.md @@ -22,26 +22,32 @@ import { Columns, Column } from '@allxsmith/bestax-bulma'; ## Props -| Prop | Type | Description | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -| `className` | `string` | Additional CSS classes for the columns container. | -| `textColor` | `'primary'` \| `'link'` \| `'info'` \| `'success'` \| `'warning'` \| `'danger'` \| `'black'` \| `'black-bis'` \| `'black-ter'` \| `'grey-darker'` \| `'grey-dark'` \| `'grey'` \| `'grey-light'` \| `'grey-lighter'` \| `'white'` \| `'light'` \| `'dark'` \| `'inherit'` \| `'current'` | Text color. | -| `color` | `'primary'` \| `'link'` \| `'info'` \| `'success'` \| `'warning'` \| `'danger'` | Bulma color modifier for all columns. | -| `bgColor` | `'primary'` \| `'link'` \| `'info'` \| `'success'` \| `'warning'` \| `'danger'` \| `'black'` \| `'black-bis'` \| `'black-ter'` \| `'grey-darker'` \| `'grey-dark'` \| `'grey'` \| `'grey-light'` \| `'grey-lighter'` \| `'white'` \| `'light'` \| `'dark'` \| `'inherit'` \| `'current'` | Background color for all columns. | -| `isCentered` | `boolean` | Horizontally center columns within the container. | -| `isGapless` | `boolean` | Remove gap between columns. | -| `isMultiline` | `boolean` | Allow columns to wrap to multiple lines. | -| `isVCentered` | `boolean` | Vertically center columns within the container. | -| `isMobile` | `boolean` | Apply columns layout on mobile and up. | -| `isDesktop` | `boolean` | Apply columns layout on desktop and up. | -| `gapSize` | number \| string (0-8) | Gap size for all breakpoints. | -| `gapSizeMobile` | number \| string (0-8) | Gap size for mobile. | -| `gapSizeTablet` | number \| string (0-8) | Gap size for tablet. | -| `gapSizeDesktop` | number \| string (0-8) | Gap size for desktop. | -| `gapSizeWidescreen` | number \| string (0-8) | Gap size for widescreen. | -| `gapSizeFullhd` | number \| string (0-8) | Gap size for fullhd. | -| `children` | `React.ReactNode` | Columns to render within the container. | -| ... | All Bulma helper and HTML props | (See [Helper Props](../api/helpers/usebulmaclasses)) | +| Prop | Type | Description | +| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| `className` | `string` | Additional CSS classes for the columns container. | +| `textColor` | `'primary'` \| `'link'` \| `'info'` \| `'success'` \| `'warning'` \| `'danger'` \| `'black'` \| `'black-bis'` \| `'black-ter'` \| `'grey-darker'` \| `'grey-dark'` \| `'grey'` \| `'grey-light'` \| `'grey-lighter'` \| `'white'` \| `'light'` \| `'dark'` \| `'inherit'` \| `'current'` | Text color. | +| `color` | `'primary'` \| `'link'` \| `'info'` \| `'success'` \| `'warning'` \| `'danger'` | Bulma color modifier for all columns. | +| `bgColor` | `'primary'` \| `'link'` \| `'info'` \| `'success'` \| `'warning'` \| `'danger'` \| `'black'` \| `'black-bis'` \| `'black-ter'` \| `'grey-darker'` \| `'grey-dark'` \| `'grey'` \| `'grey-light'` \| `'grey-lighter'` \| `'white'` \| `'light'` \| `'dark'` \| `'inherit'` \| `'current'` | Background color for all columns. | +| `isCentered` | `boolean` | Horizontally center columns within the container. | +| `isGapless` | `boolean` | Remove gap between columns. | +| `isMultiline` | `boolean` | Allow columns to wrap to multiple lines. | +| `isVCentered` | `boolean` | Vertically center columns within the container. | +| `isMobile` | `boolean` | Apply columns layout on mobile and up. | +| `isDesktop` | `boolean` | Apply columns layout on desktop and up. | +| `gap` | number \| string (0-8) | Gap size for all breakpoints. Same scale as `Grid`'s `gap` prop; wins over `gapSize` if both are set. | +| `gapMobile` | number \| string (0-8) | Gap size for mobile. Wins over `gapSizeMobile` if both are set. | +| `gapTablet` | number \| string (0-8) | Gap size for tablet. Wins over `gapSizeTablet` if both are set. | +| `gapDesktop` | number \| string (0-8) | Gap size for desktop. Wins over `gapSizeDesktop` if both are set. | +| `gapWidescreen` | number \| string (0-8) | Gap size for widescreen. Wins over `gapSizeWidescreen` if both are set. | +| `gapFullhd` | number \| string (0-8) | Gap size for fullhd. Wins over `gapSizeFullhd` if both are set. | +| `gapSize` | number \| string (0-8) | **Deprecated.** Use `gap` instead. | +| `gapSizeMobile` | number \| string (0-8) | **Deprecated.** Use `gapMobile` instead. | +| `gapSizeTablet` | number \| string (0-8) | **Deprecated.** Use `gapTablet` instead. | +| `gapSizeDesktop` | number \| string (0-8) | **Deprecated.** Use `gapDesktop` instead. | +| `gapSizeWidescreen` | number \| string (0-8) | **Deprecated.** Use `gapWidescreen` instead. | +| `gapSizeFullhd` | number \| string (0-8) | **Deprecated.** Use `gapFullhd` instead. | +| `children` | `React.ReactNode` | Columns to render within the container. | +| ... | All Bulma helper and HTML props | (See [Helper Props](../api/helpers/usebulmaclasses)) | --- @@ -202,39 +208,39 @@ This example shows how columns can be nested within each other. Nested columns i ### Gap Sizes & Responsive Gaps -This example demonstrates the use of the `gapSize` prop to control the space between columns. Responsive gap sizes can also be set for different screen sizes. +This example demonstrates the use of the `gap` prop to control the space between columns (the same 0-8 scale as `Grid`'s `gap` prop). Responsive gap sizes can also be set for different screen sizes. ```tsx live <> - + - gapSize=0 + gap=0 - gapSize=0 + gap=0 - + - gapSize=3 + gap=3 - gapSize=3 + gap=3 - + - gapSizeMobile=1 gapSizeTablet=3 gapSizeDesktop=6 + gapMobile=1 gapTablet=3 gapDesktop=6 - gapSizeMobile=1 gapSizeTablet=3 gapSizeDesktop=6 + gapMobile=1 gapTablet=3 gapDesktop=6 @@ -398,11 +404,11 @@ This example demonstrates using the `isMultiline` and `isGapless` props together ### Variable Gap -This example demonstrates how to use the `gapSize` prop to create columns with variable widths. This can be useful for creating layouts with sidebars or varying content widths. +This example demonstrates how to use the `gap` prop to create columns with variable widths. This can be useful for creating layouts with sidebars or varying content widths. ```tsx live <> - + Side @@ -414,7 +420,7 @@ This example demonstrates how to use the `gapSize` prop to create columns with v - + Three columns @@ -431,7 +437,7 @@ This example demonstrates how to use the `gapSize` prop to create columns with v - + {Array.from({ length: 6 }).map((_, i) => ( @@ -451,11 +457,11 @@ This example shows how to set different gap sizes for different breakpoints. Thi ```tsx live {[...Array(3)].map((_, idx) => ( @@ -608,6 +614,9 @@ This example shows how to combine the `isCentered` prop with multiline columns. - Use `Column` as a direct child of `Columns` for proper grid behavior. - All gap, centering, and alignment props support responsive variants. +- The `gapSize*` props are deprecated aliases for `gap*` and are kept for backwards + compatibility; if both are set on the same breakpoint, `gap*` wins. `Grid` uses the same + `gap` prop name and 0-8 value scale. - Combine with [Bulma helper props](../api/helpers/usebulmaclasses) for utility-first styling. - All standard `
` HTML props are supported. diff --git a/skills/bestax-layout-scaffold/references/layout-components.md b/skills/bestax-layout-scaffold/references/layout-components.md index 4cf314e21..1dc45b2ea 100644 --- a/skills/bestax-layout-scaffold/references/layout-components.md +++ b/skills/bestax-layout-scaffold/references/layout-components.md @@ -91,15 +91,15 @@ The responsive grid. `Columns` is the row; `Column` is a cell. **Columns** -| Prop | Type | -| -------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -| `isMultiline` | `boolean` (wrap cells onto new rows) | -| `isCentered` | `boolean` (center the row) | -| `isVCentered` | `boolean` (vertical centering — capital V) | -| `isGapless` | `boolean` | -| `isMobile` | `boolean` (stay side-by-side on mobile) | -| `isDesktop` | `boolean` | -| `gapSize` / `gapSizeMobile` / `gapSizeTablet` / `gapSizeDesktop` / `gapSizeWidescreen` / `gapSizeFullhd` | `0`–`8` (number or string) | +| Prop | Type | +| -------------------------------------------------------------------------------- | -------------------------------------------------------- | +| `isMultiline` | `boolean` (wrap cells onto new rows) | +| `isCentered` | `boolean` (center the row) | +| `isVCentered` | `boolean` (vertical centering — capital V) | +| `isGapless` | `boolean` | +| `isMobile` | `boolean` (stay side-by-side on mobile) | +| `isDesktop` | `boolean` | +| `gap` / `gapMobile` / `gapTablet` / `gapDesktop` / `gapWidescreen` / `gapFullhd` | `0`–`8` (number or string, same scale as `Grid`'s `gap`) | **Column**