Repository navigation
docs: dual-audience custom-component skill, helper API pages, drift fixes #268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
059d358
aa10095
2ddb39b
d97de20
3674be4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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`. | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| ## 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.mdRepository: allxsmith/bestax Length of output: 5897 Import the React HTML attribute type used by the example. 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
Suggested change
🤖 Prompt for AI AgentsSource: 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). | ||||||||||||||||||||||||||||||||||||||||||||||
| 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). |
There was a problem hiding this comment.
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.
createPrefixedClassNamesshould 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