Skip to content
Merged
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
2 changes: 1 addition & 1 deletion bestax-mcp/data/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,7 @@
"import": "import { useBulmaClasses } from '@allxsmith/bestax-bulma';",
"compound": false,
"propCount": 0,
"exampleCount": 45
"exampleCount": 46
},
{
"name": "useFocusTrap",
Expand Down
2 changes: 1 addition & 1 deletion bestax-mcp/data/components/Columns.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
"name": "gap",
"type": "0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'",
"default": null,
"description": "Gap size for all breakpoints. Same scale as `Grid`'s `gap` prop; wins over `gapSize` if both are set.",
"description": "Gap size for all breakpoints, the columns gutter (`is-<value>`). Whole steps of the same scale as `Grid`'s `gap` prop; wins over `gapSize` if both are set. This is not the `gap` helper prop other components take, which renders `is-gap-<value>`: on `Columns` the gutter is padding inside each column, so the helper's CSS `gap` would add to it rather than replace it.",
"required": false,
"inherited": false,
"deprecated": false
Expand Down
17 changes: 11 additions & 6 deletions bestax-mcp/data/components/Grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,27 @@
},
{
"name": "gap",
"type": "0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'",
"type": "BulmaGapStep",
"default": null,
"description": "Main gap for grid (Bulma `is-gap-X`).",
"description": "Main gap for grid (Bulma `is-gap-X`), a whole or half step of Bulma's gap scale. The same values and class as the `gap` helper prop on every other component.",
"required": false,
"inherited": false,
"deprecated": false
},
{
"name": "columnGap",
"type": "0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'",
"type": "BulmaGapStep",
"default": null,
"description": "Column gap for grid (`is-column-gap-X`).",
"description": "Column gap for grid (`is-column-gap-X`), on the same scale as `gap`.",
"required": false,
"inherited": false,
"deprecated": false
},
{
"name": "rowGap",
"type": "0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'",
"type": "BulmaGapStep",
"default": null,
"description": "Row gap for grid (`is-row-gap-X`).",
"description": "Row gap for grid (`is-row-gap-X`), on the same scale as `gap`.",
"required": false,
"inherited": false,
"deprecated": false
Expand Down Expand Up @@ -209,6 +209,11 @@
"extraProps": [],
"catchAll": "All standard <div> attributes and Bulma helper props",
"types": [
{
"name": "BulmaGapStep",
"expansion": "'0' | '0.5' | '1' | '1.5' | '2' | '2.5' | '3' | '3.5' | '4' | '4.5' | '5' | '5.5' | '6' | '6.5' | '7' | '7.5' | '8' | 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 3.5 | 4 | 4.5 | 5 | 5.5 | 6 | 6.5 | 7 | 7.5 | 8",
"summary": "A gap step as `gap`, `columnGap` and `rowGap` take it: a value from `validGaps`, or the same step written as a number (`gap={2}`, `gap={1.5}`). `Grid` took its gaps as numbers before the gap helpers were shared, so every gap prop still does."
},
{
"name": "BulmaMinColValue",
"expansion": "1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32",
Expand Down
2 changes: 1 addition & 1 deletion bestax-mcp/data/components/Valid value constants.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions bestax-mcp/data/components/useBulmaClasses.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions bestax-mcp/data/skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"promptName": "custom-component",
"dir": "bestax-custom-component",
"references": [
{ "id": "api", "file": "references/api.md", "bytes": 10708 },
{ "id": "api", "file": "references/api.md", "bytes": 10721 },
{
"id": "component-catalog",
"file": "references/component-catalog.md",
Expand All @@ -20,7 +20,7 @@
{ "id": "patterns", "file": "references/patterns.md", "bytes": 4601 }
],
"examples": [
{ "id": "stat-card", "file": "examples/stat-card.tsx", "bytes": 3828 }
{ "id": "stat-card", "file": "examples/stat-card.tsx", "bytes": 3739 }
]
},
{
Expand Down Expand Up @@ -64,7 +64,7 @@
{
"id": "layout-components",
"file": "references/layout-components.md",
"bytes": 15323
"bytes": 15557
}
],
"examples": [
Expand Down Expand Up @@ -108,7 +108,7 @@
{
"id": "bulma-classes-prop-map",
"file": "references/bulma-classes/prop-map.md",
"bytes": 32690
"bytes": 32658
},
{
"id": "bulma-classes-unmappables",
Expand Down
3 changes: 3 additions & 0 deletions bestax-mcp/src/__tests__/helper-props.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ describe('resolveGroup', () => {
['margin', 'spacing'],
['padding', 'spacing'],
['mt', 'spacing'],
['gap', 'spacing'],
['rowGap', 'spacing'],
['gapless', 'spacing'],
['colour', 'color'],
['background', 'color'],
['text', 'typography'],
Expand Down
2 changes: 1 addition & 1 deletion bestax-mcp/src/__tests__/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ describe('get_helper_props', () => {
it('keeps the default under 10k while naming every prop', async () => {
const out = text(await call('get_helper_props'));
expect(out.length).toBeLessThan(10_000);
// Spot-check across all seven groups; helper-props.test.ts checks all 46 exhaustively.
// Spot-check across all seven groups; helper-props.test.ts checks every prop exhaustively.
for (const prop of [
'mt',
'px',
Expand Down
10 changes: 7 additions & 3 deletions bestax-mcp/src/helper-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
* heading at all and would answer nothing.
*
* So the groups are curated, and each one carries its own slice of the prop table. The
* governing rule is that **no call ever loses a prop name**: the default lists all 46 with
* their accepted values, because a builder has to be able to look up a valid value in one
* governing rule is that **no call ever loses a prop name**: the default lists every prop with
* its accepted values, because a builder has to be able to look up a valid value in one
* call. What moves behind `group` is the prose and the live examples, which are re-fetchable
* and which nothing breaks without.
*/
Expand Down Expand Up @@ -71,8 +71,12 @@ export interface HelperGroup {
*/
export const HELPER_GROUPS: Record<string, HelperGroup> = {
spacing: {
headings: ['### Margin'],
headings: ['### Margin', '### Gap'],
props: [
'gap',
'columnGap',
'rowGap',
'gapless',
'm',
'mt',
'mr',
Expand Down
2 changes: 1 addition & 1 deletion bestax-migrate/src/sources/bulma-classes/class-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ export const ROOTS: Readonly<Record<string, RootEntry>> = {
],
},
// Grid's gaps take a numeric string, but its column minimum and every Cell
// placement are typed as numbers. The half steps (`is-gap-0.5`) have no prop.
// placement are typed as numbers.
grid: {
...BASE,
target: 'Grid',
Expand Down
2 changes: 1 addition & 1 deletion bulma-ui/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ These keep it working, and each of them failed once:
(IntersectionObserver/matchMedia mocks, SSR via `renderToStaticMarkup`).
- Stories: types from `@storybook/react-vite`; `tags: ['autodocs']`; every argType gets a
`description` (meta-test enforced). No inline `style={{}}` in stories/docs examples — helper
props (no `gap` helper — space with `m*`/`p*`); legacy inline styles exist, don't copy them.
props; legacy inline styles exist, don't copy them.
- Must build and pass tests on **React 18 and 19** (CI matrix) — avoid single-major APIs.
- Bundle size is marketing-visible (the READMEs link the live bundlephobia badge) — check `pnpm bundle:stats`
(writes `dist/stats.html`) when adding anything with real runtime weight.
Expand Down
1 change: 1 addition & 0 deletions bulma-ui/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ const PUBLIC_EXPORTS = [
'validFlexWraps',
'validFloats',
'validFontFamilies',
'validGaps',
'validInteractions',
'validJustifyContents',
'validOverflows',
Expand Down
28 changes: 25 additions & 3 deletions bulma-ui/src/columns/Columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ export type BulmaGapSize = BulmaGapValue;
export interface ColumnsProps
extends
React.HTMLAttributes<HTMLDivElement>,
Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
// The column and row gap helpers are left out, as the `gap` helper is: the
// gutter is padding inside each column, so CSS `gap` would add to it, and
// only from the tablet width up, where `.columns` is flex.
Omit<
BulmaClassesProps,
'color' | 'backgroundColor' | 'columnGap' | 'rowGap'
> {
/** Additional CSS classes for the columns container. */
className?: string;
/** Text color. */
Expand All @@ -44,7 +50,15 @@ export interface ColumnsProps
/** Apply columns layout on desktop and up. */
isDesktop?: boolean;

/** Gap size for all breakpoints. Same scale as `Grid`'s `gap` prop; wins over `gapSize` if both are set. */
/**
* Gap size for all breakpoints, the columns gutter (`is-<value>`). Whole
* steps of the same scale as `Grid`'s `gap` prop; wins over `gapSize` if
* both are set.
*
* This is not the `gap` helper prop other components take, which renders
* `is-gap-<value>`: on `Columns` the gutter is padding inside each column,
* so the helper's CSS `gap` would add to it rather than replace it.
*/
gap?: BulmaGapValue;
Comment thread
claude[bot] marked this conversation as resolved.
/** Gap size for mobile. Wins over `gapSizeMobile` if both are set. */
gapMobile?: BulmaGapValue;
Expand Down Expand Up @@ -115,10 +129,18 @@ const ColumnsComponent: React.FC<ColumnsProps> = ({
const resolvedGapWidescreen = gapWidescreen ?? gapSizeWidescreen;
const resolvedGapFullhd = gapFullhd ?? gapSizeFullhd;

// Untyped code can still pass the gap helpers ColumnsProps leaves out. They
// are dropped here, so they render no class and never reach the DOM.
const {
columnGap: _columnGap,
rowGap: _rowGap,
...helperProps
} = props as typeof props & { columnGap?: unknown; rowGap?: unknown };

const { bulmaHelperClasses, rest } = useBulmaClasses({
color: textColor,
backgroundColor: bgColor,
...props,
...helperProps,
});

const mainClass = usePrefixedClassNames('columns');
Expand Down
46 changes: 45 additions & 1 deletion bulma-ui/src/columns/__tests__/Columns.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render } from '@testing-library/react';
import { Columns } from '../Columns';
import { Columns, type ColumnsProps } from '../Columns';
import { Column } from '../Column';
import { ConfigProvider } from '../../helpers/Config';

Expand Down Expand Up @@ -98,6 +98,50 @@ describe('Columns', () => {
expect(container.firstChild).toHaveClass('is-4', 'is-1-mobile');
});

// Columns' `gap` is the columns gutter, so the shared gap helper's
// `is-gap-*` never renders for it, while the helper props Columns does not
// declare reach the helper as on any other component.
it('keeps gap as the columns gutter rather than the gap helper', () => {
const { container } = render(<Columns gap={3} />);
expect(container.firstChild).toHaveClass('is-3');
expect(container.firstChild).not.toHaveClass('is-gap-3');
});

// The column and row gap helpers are left out of Columns, as `gap` is:
// CSS `gap` would add to the gutter rather than replace it. Typed code
// cannot pass them, and untyped code gets no class and no DOM attribute.
it('leaves out the column and row gap helpers, and takes gapless', () => {
const { container } = render(
<>
{/* @ts-expect-error columnGap is left out of ColumnsProps */}
<Columns columnGap="2" />
<Columns
isMultiline
gapless
{...({ columnGap: '2', rowGap: 3 } as unknown as ColumnsProps)}
/>
</>
);

const [typed, untyped] = Array.from(container.children);
expect(typed.className).toBe('columns');
expect(untyped.className).toBe('columns is-multiline is-gapless');
for (const el of [typed, untyped]) {
expect(el).not.toHaveAttribute('columngap');
expect(el).not.toHaveAttribute('rowgap');
}
});

// Bulma's `is-gapless` is both the columns modifier and the gap helper, so
// `gapless` beside `isGapless` renders the class once.
it('renders is-gapless once when gapless and isGapless are both set', () => {
const { container } = render(<Columns isGapless gapless />);
expect((container.firstChild as HTMLElement).className.split(' ')).toEqual([
'columns',
'is-gapless',
]);
});

it('prefers gap* props over the deprecated gapSize* props when both are set', () => {
const { container } = render(
<Columns
Expand Down
2 changes: 1 addition & 1 deletion bulma-ui/src/form/Autocomplete.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ export const Controlled: Story = {

return (
<ResponsiveWrapper>
<Block display="flex" alignItems="center" mb="4" className="is-gap-2">
<Block display="flex" alignItems="center" mb="4" gap="2">
<Span>Value: &quot;{value}&quot;</Span>
<Button size="small" onClick={handleClear}>
Clear
Expand Down
24 changes: 16 additions & 8 deletions bulma-ui/src/grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import {
BulmaClassesProps,
validColors,
} from '../helpers/useBulmaClasses';
import type { BulmaGapStep } from '../helpers/bulmaClassHelpers';

/**
* 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.
* The whole steps of Bulma's 0-8 gap scale, as `Columns` takes them. Accepts
* the value as a number or a numeric string.
*
* `Grid`'s gap props and the shared `gap`, `columnGap` and `rowGap` helper
* props take the half steps as well, as `BulmaGapStep`.
*/
export type BulmaGapValue =
| 0
Expand Down Expand Up @@ -86,12 +90,16 @@ export interface GridProps
Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {
/** Use a fixed grid layout (`.fixed-grid > .grid`). */
isFixed?: boolean;
/** Main gap for grid (Bulma `is-gap-X`). */
gap?: BulmaGapValue;
/** Column gap for grid (`is-column-gap-X`). */
columnGap?: BulmaGapValue;
/** Row gap for grid (`is-row-gap-X`). */
rowGap?: BulmaGapValue;
/**
* Main gap for grid (Bulma `is-gap-X`), a whole or half step of Bulma's gap
* scale. The same values and class as the `gap` helper prop on every other
* component.
*/
gap?: BulmaGapStep;
Comment thread
claude[bot] marked this conversation as resolved.
/** Column gap for grid (`is-column-gap-X`), on the same scale as `gap`. */
columnGap?: BulmaGapStep;
/** Row gap for grid (`is-row-gap-X`), on the same scale as `gap`. */
rowGap?: BulmaGapStep;
/** Minimum column width for the grid (`is-col-min-X`). */
minCol?: BulmaMinColValue;
/** For fixed grids: explicit column count (`has-X-cols`), or `'auto'` for auto-count. */
Expand Down
37 changes: 37 additions & 0 deletions bulma-ui/src/grid/__tests__/Grid.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,43 @@ describe('Grid', () => {
expect(grid).toHaveClass('is-row-gap-1');
});

// Grid's gap props render Bulma's gap helpers, the same classes the shared
// `gap`, `columnGap` and `rowGap` helper props render, so they take the
// half steps too.
it('accepts the half steps, as numbers or strings', () => {
const { container } = render(
<Grid gap={0.5} columnGap="1.5" rowGap={7.5}>
<TestCell />
</Grid>
);
const grid = container.querySelector('.grid')!;
expect(grid).toHaveClass('is-gap-0.5');
expect(grid).toHaveClass('is-column-gap-1.5');
expect(grid).toHaveClass('is-row-gap-7.5');
});

// Grid renders its own gap props, so they never reach the helper as well
// and each class appears once, in the place it always had.
it('renders each gap class once, before the helper classes', () => {
const { container } = render(
<Grid gap={2} columnGap={1} m="2">
<TestCell />
</Grid>
);
expect(container.querySelector('.grid')?.className).toBe(
'grid is-gap-2 is-column-gap-1 m-2'
);
});

it('takes the gapless helper', () => {
const { container } = render(
<Grid gapless>
<TestCell />
</Grid>
);
expect(container.querySelector('.grid')).toHaveClass('is-gapless');
});

it('renders as fixed-grid wrapper with grid inside', () => {
const { container } = render(
<Grid isFixed>
Expand Down
6 changes: 5 additions & 1 deletion bulma-ui/src/helpers/Theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,11 @@ const setRootThemeRules = (order: number, rules: string): void => {
*/
export interface ThemeProps extends Omit<
BulmaClassesProps,
'color' | 'backgroundColor'
// On Theme, `columnGap` is the camelCase prop `bulmaVarPropMap` mints for
// `--bulma-column-gap`, so the column gap helper does not reach it and is
// left out of the type. `gap`, `rowGap` and `gapless` mint no variable and
// are the gap helpers here, as on every other component.
'color' | 'backgroundColor' | 'columnGap'
> {
children: ReactNode;
className?: string;
Expand Down
Loading
Loading