Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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