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
3 changes: 2 additions & 1 deletion bulma-ui/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ A new or changed component is **five artifacts, not one**. Touch all of:
If the change invalidates guidance in `skills/`, update the skill in the same PR.

The full worked walkthrough (including the SCSS side for extras) is
`skills/bestax-custom-component/SKILL.md` — follow it rather than improvising.
`skills/bestax-custom-component/references/library-contributor.md` — follow it rather than
improvising.

## Conventions

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/api/helpers/classnames.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ sidebar_label: classNames
## Import

```tsx
import { classNames } from '@allxsmith/bestax-bulma;
import { classNames } from '@allxsmith/bestax-bulma';
```

---
Expand Down
164 changes: 164 additions & 0 deletions docs/docs/api/helpers/useprefixedclassnames.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
title: usePrefixedClassNames
sidebar_label: usePrefixedClassNames
---

# usePrefixedClassNames

## Overview

`usePrefixedClassNames` builds a component class string that honors the `classPrefix` from `ConfigProvider` — the hook every bestax component uses for its own classes. It accepts the same arguments as [`classNames`](./classnames.md) (strings, numbers, arrays, objects — falsy values ignored, duplicates removed), reads the current `classPrefix` from context, and prepends it to **every** emitted class name. Two non-hook variants, `prefixedClassNames` and `createPrefixedClassNames`, do the same when you already have the prefix in hand or are outside a React component.

---

## Import

```tsx
import {
usePrefixedClassNames,
prefixedClassNames,
createPrefixedClassNames,
} from '@allxsmith/bestax-bulma';
```

---

## API

```ts
// Hook: reads classPrefix from the nearest ConfigProvider
function usePrefixedClassNames(
...args: (
| string
| number
| undefined
| null
| false
| Record<string, unknown>
| unknown[]
)[]
): string;

// Plain function: pass the prefix explicitly (undefined ⇒ plain classNames)
function prefixedClassNames(
prefix: string | undefined,
...args: (
| string
| number
| undefined
| null
| false
| Record<string, unknown>
| unknown[]
)[]
): string;

// Factory: returns a classNames function bound to a fixed prefix
function createPrefixedClassNames(classPrefix: string): (...args) => string; // args: same union as classNames
```

### Parameters

| Function | Parameter | Type | Description |
| -------------------------- | ------------- | --------------------- | -------------------------------------------------------------------------------------------------- |
| `usePrefixedClassNames` | `...args` | same as `classNames` | Class values to join. The `classPrefix` from `ConfigProvider` is applied to every resulting class. |
| `prefixedClassNames` | `prefix` | `string \| undefined` | Prefix to apply. When `undefined` (or empty), behaves exactly like `classNames`. |
| `prefixedClassNames` | `...args` | same as `classNames` | Class values to join. |
| `createPrefixedClassNames` | `classPrefix` | `string` | Prefix baked into the returned function. |

All three return a space-separated string of unique class names. With no `ConfigProvider` (or no `classPrefix` set), `usePrefixedClassNames` produces the same output as `classNames`.
Comment on lines +56 to +69

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the factory signature and return description. createPrefixedClassNames should use a typed rest parameter (...args: ClassValue[]), and the summary should say the hook/plain function return a string while the factory returns a function.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/api/helpers/useprefixedclassnames.md` around lines 56 - 69, Update
the createPrefixedClassNames factory documentation to declare a typed rest
parameter using ClassValue[] instead of an untyped args tuple. Correct the
return descriptions so usePrefixedClassNames and prefixedClassNames return a
string, while createPrefixedClassNames returns a classNames function.


---

## Usage

### In a custom component (honors `ConfigProvider`)

This is the pattern every bestax component follows: build the component's **own** Bulma classes with `usePrefixedClassNames`, then merge in the (already-prefixed) helper classes from `useBulmaClasses` and the consumer's `className` with plain `classNames` — the consumer's `className` must **not** be prefixed.

```tsx
import {
usePrefixedClassNames,
useBulmaClasses,
classNames,
type BulmaClassesProps,
} from '@allxsmith/bestax-bulma';

interface ChipProps
extends React.HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {
color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';
Comment on lines +79 to +89

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,180p' docs/docs/api/helpers/useprefixedclassnames.md

Repository: allxsmith/bestax

Length of output: 5897


Import the React HTML attribute type used by the example. React.HTMLAttributes needs an explicit type import here, otherwise the snippet isn’t self-contained in a standard TypeScript setup.

 import {
   usePrefixedClassNames,
   useBulmaClasses,
   classNames,
   type BulmaClassesProps,
 } from '`@allxsmith/bestax-bulma`';
+import type { HTMLAttributes } from 'react';

 interface ChipProps
-  extends React.HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {
+  extends HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```tsx
import {
usePrefixedClassNames,
useBulmaClasses,
classNames,
type BulmaClassesProps,
} from '@allxsmith/bestax-bulma';
interface ChipProps
extends React.HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {
color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';
import {
usePrefixedClassNames,
useBulmaClasses,
classNames,
type BulmaClassesProps,
} from '`@allxsmith/bestax-bulma`';
import type { HTMLAttributes } from 'react';
interface ChipProps
extends HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {
color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/api/helpers/useprefixedclassnames.md` around lines 79 - 89, Add an
explicit type-only React import for the HTML attribute type used by the
ChipProps interface, and update the interface to reference that imported type
while preserving the existing BulmaClassesProps extension.

Source: Coding guidelines

isRounded?: boolean;
}

function Chip({ color, isRounded, className, children, ...props }: ChipProps) {
const { bulmaHelperClasses, rest } = useBulmaClasses(props);

// 'tag is-rounded is-primary' — or 'bestax-tag bestax-is-rounded …'
// inside <ConfigProvider classPrefix="bestax-">
const chipClasses = usePrefixedClassNames('tag', {
[`is-${color}`]: !!color,
'is-rounded': isRounded,
});

return (
<span
className={classNames(chipClasses, bulmaHelperClasses, className)}
{...rest}
>
{children}
</span>
);
}
```

Wrapped in a provider, the component emits prefixed classes automatically:

```tsx
import { ConfigProvider } from '@allxsmith/bestax-bulma';

<ConfigProvider classPrefix="bestax-">
<Chip color="primary" isRounded>
Prefixed
</Chip>
{/* renders class="bestax-tag bestax-is-primary bestax-is-rounded" */}
</ConfigProvider>;
```

### Outside a component: `prefixedClassNames`

When you already know the prefix (or might not have one), pass it as the first argument:

```ts
prefixedClassNames('bulma-', 'button', { 'is-primary': true });
// => 'bulma-button bulma-is-primary'

prefixedClassNames(undefined, 'button', { 'is-primary': true });
// => 'button is-primary'
```

### Reusable prefixer: `createPrefixedClassNames`

Bind the prefix once and reuse the returned function:

```ts
const cx = createPrefixedClassNames('bulma-');

cx('card', ['has-shadow', { 'is-active': true }]);
// => 'bulma-card bulma-has-shadow bulma-is-active'
```

---

## Tips

- The prefix is applied to **every** class, including modifier classes from object keys (`'is-primary'` → `bestax-is-primary`) — this matches the `bestax-prefixed` CSS flavor.
- Never route a consumer-supplied `className` through these helpers; combine it afterwards with plain `classNames` so user classes stay untouched.
- `usePrefixedClassNames` is a React hook — call it unconditionally at the top level of a component. Use `prefixedClassNames`/`createPrefixedClassNames` everywhere else.

---

## See Also

- [`classNames`](./classnames.md): The unprefixed class-string builder these helpers wrap.
- [`ConfigProvider`](./config.md): Where `classPrefix` comes from.
- [`useBulmaClasses`](./usebulmaclasses.md): Helper-prop classes (already prefix-aware).
88 changes: 88 additions & 0 deletions docs/docs/api/helpers/valid-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: Valid value constants
sidebar_label: Valid value constants
---

# Valid value constants

## Overview

The `valid*` constant arrays enumerate every accepted value for the shared Bulma helper props — public API you can import to build prop types and validation. Each is a readonly `as const` tuple, so `(typeof validColors)[number]` gives you the exact string-literal union. `useBulmaClasses` (and the per-concern hooks) silently ignore values outside these lists, so validating against them tells you exactly what will produce a class.

---

## Import

```tsx
import {
validColors,
validSizes,
validViewports,
} from '@allxsmith/bestax-bulma';
```

All 18 constants are importable the same way.

---

## Constants

| Constant | Values | Used by prop family |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `validColors` | `'primary'`, `'link'`, `'info'`, `'success'`, `'warning'`, `'danger'`, `'black'` … `'white'`, `'light'`, `'dark'` (17 values) | `color`, `backgroundColor` (also component `color` props) |
| `validColorShades` | `'00'`, `'05'` … `'95'`, `'invert'`, `'light'`, `'dark'`, `'soft'`, `'bold'`, `'on-scheme'` | `colorShade`, `backgroundColorShade` |
| `validSizes` | `'0'`–`'6'`, `'auto'` | Spacing: `m`, `mt`, `mr`, `mb`, `ml`, `mx`, `my`, `p`, `pt`, … `px`, `py` |
| `validTextSizes` | `'1'`–`'7'` | `textSize` (+ `textSizeMobile` … `textSizeFullhd`) |
| `validAlignments` | `'centered'`, `'justified'`, `'left'`, `'right'` | `textAlign` (+ viewport variants) |
| `validTextTransforms` | `'capitalized'`, `'lowercase'`, `'uppercase'`, `'italic'` | `textTransform` |
| `validTextWeights` | `'light'`, `'normal'`, `'medium'`, `'semibold'`, `'bold'` | `textWeight` |
| `validFontFamilies` | `'sans-serif'`, `'monospace'`, `'primary'`, `'secondary'`, `'code'` | `fontFamily` |
| `validDisplays` | `'block'`, `'flex'`, `'inline'`, `'inline-block'`, `'inline-flex'` | `display` (+ `displayMobile` … `displayFullhd`) |
| `validVisibilities` | `'hidden'`, `'sr-only'`, `'invisible'` | `visibility` (+ viewport variants) |
| `validFlexDirections` | `'row'`, `'row-reverse'`, `'column'`, `'column-reverse'` | `flexDirection` |
| `validFlexWraps` | `'nowrap'`, `'wrap'`, `'wrap-reverse'` | `flexWrap` |
| `validJustifyContents` | `'flex-start'`, `'flex-end'`, `'center'`, `'space-between'`, `'space-around'`, `'space-evenly'`, `'start'`, `'end'`, `'left'`, `'right'` | `justifyContent` |
| `validAlignContents` | `'flex-start'`, `'flex-end'`, `'center'`, `'space-between'`, `'space-around'`, `'space-evenly'`, `'stretch'` | `alignContent` |
| `validAlignItems` | `'stretch'`, `'flex-start'`, `'flex-end'`, `'center'`, `'baseline'`, `'start'`, `'end'` | `alignItems` |
| `validAlignSelfs` | `'auto'`, `'flex-start'`, `'flex-end'`, `'center'`, `'baseline'`, `'stretch'` | `alignSelf` |
| `validFlexGrowShrink` | `'0'`–`'5'` | `flexGrow`, `flexShrink` |
| `validViewports` | `'mobile'`, `'tablet'`, `'desktop'`, `'widescreen'`, `'fullhd'` | `viewport` (responsive modifier) |

---

## Typing with `(typeof …)[number]`

Because the arrays are `as const`, indexing them with `number` yields the union of their literal values — the same idiom the library uses internally for its prop types:

```ts
import { validColors, validSizes } from '@allxsmith/bestax-bulma';

type Color = (typeof validColors)[number]; // 'primary' | 'link' | … | 'dark'
type Spacing = (typeof validSizes)[number]; // '0' | '1' | … | '6' | 'auto'

interface MyComponentProps {
color?: Color;
m?: Spacing;
}
```

They also work as runtime validators:

```ts
function isColor(value: string): value is (typeof validColors)[number] {
return (validColors as readonly string[]).includes(value);
}
```

:::warning `validSizes` is for spacing, not element sizes

`validSizes` (`'0'`–`'6'` | `'auto'`) enumerates the **spacing helper** scale — the values for `m`/`p` props like `m="4"` (→ `m-4`). Element `size` props are a different axis: `Button`, `Icon`, and friends declare an inline union such as `'small' | 'medium' | 'large'` (Button adds `'normal'`), **not** `validSizes`. Don't use `validSizes` to type a component's `size` prop.

:::

---

## See Also

- [`useBulmaClasses`](./usebulmaclasses.md): The hook that consumes these values and generates the helper classes.
- [Helper guides](../../guides/helpers/color.md): Task-oriented walkthroughs of the helper-prop system (color, spacing, typography, flex, visibility).
6 changes: 4 additions & 2 deletions docs/docs/skills/custom-component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import { ExampleMeta } from '@site/src/components/SkillExamples';

The [`bestax-custom-component`](https://github.com/allxsmith/bestax/blob/main/skills/bestax-custom-component/SKILL.md)
skill teaches an agent to build a new custom component the bestax way — starting by
**checking whether a matching component already exists**, then the helper hooks and the Bulma v1
SCSS pattern, plus stories, tests, and docs.
**checking whether a matching component already exists** — in either of two contexts. In an app
using `@allxsmith/bestax-bulma`, it composes existing components, helper props, the public hooks,
and `--bulma-*` CSS variables; inside the bestax monorepo, it covers the full library pipeline
(helper hooks, the Bulma v1 SCSS pattern, stories, tests, and docs).

## Install

Expand Down
19 changes: 11 additions & 8 deletions skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ directory the agent reads on demand.

## Skills

| Skill | Use it when… |
| --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`bestax-custom-component`](./bestax-custom-component/SKILL.md) | Building a **new custom component** beyond stock Bulma — React + TS, the Bulma v1 SCSS CSS-variable pattern, stories, tests, docs, and export/build wiring. |
| [`bestax-form`](./bestax-form/SKILL.md) | Building **forms** — Field/Control composition, the full input inventory, and the validate-it-yourself error pattern (there is no form library). |
| [`bestax-theming`](./bestax-theming/SKILL.md) | **Theming** — customize colors, branding, fonts/radius tokens, and dark mode by overriding Bulma's `--bulma-*` variables with the `Theme` component. |
| [`bestax-layout-scaffold`](./bestax-layout-scaffold/SKILL.md) | **Layout scaffolding** — turn a high-level request (dashboard, landing page, auth page, catalog) into a complete responsive page from named layout archetypes. |
| Skill | Use it when… |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [`bestax-custom-component`](./bestax-custom-component/SKILL.md) | Building a **custom component** beyond stock Bulma — in an app: composition, helper props, public hooks, `--bulma-*` CSS vars; in the monorepo: the full SCSS/stories/tests/docs pipeline. |
| [`bestax-form`](./bestax-form/SKILL.md) | Building **forms** — Field/Control composition, the full input inventory, and the validate-it-yourself error pattern (there is no form library). |
| [`bestax-theming`](./bestax-theming/SKILL.md) | **Theming** — customize colors, branding, fonts/radius tokens, and dark mode by overriding Bulma's `--bulma-*` variables with the `Theme` component. |
| [`bestax-layout-scaffold`](./bestax-layout-scaffold/SKILL.md) | **Layout scaffolding** — turn a high-level request (dashboard, landing page, auth page, catalog) into a complete responsive page from named layout archetypes. |

## Install

Expand All @@ -32,11 +32,14 @@ npx skills add https://github.com/allxsmith/bestax --skill bestax-layout-scaffol
```
skills/
bestax-custom-component/
SKILL.md
SKILL.md # app context (compose + public hooks + --bulma-* vars)
references/
api.md # helper hooks, classNames, valid-value constants, SCSS utilities
patterns.md # Dialog — the canonical worked example
patterns.md # Dialog — the canonical library-contributor worked example
component-catalog.md # generated: every exported component + one-line purpose
library-contributor.md # monorepo pipeline: SCSS partial, stories, tests, docs, wiring
examples/
stat-card.tsx # app-side worked example (composition + helper props + scoped CSS)
bestax-form/
SKILL.md
references/
Expand Down
Loading
Loading