bestax-mcp 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/data/catalog.json +1869 -0
- package/data/components/Autocomplete.json +604 -0
- package/data/components/Avatar.json +220 -0
- package/data/components/Avatars.json +294 -0
- package/data/components/Badge.json +261 -0
- package/data/components/Block.json +105 -0
- package/data/components/Box.json +157 -0
- package/data/components/Breadcrumb.json +155 -0
- package/data/components/Button.json +599 -0
- package/data/components/Buttons.json +533 -0
- package/data/components/Card.json +506 -0
- package/data/components/Carousel.json +512 -0
- package/data/components/Cell.json +154 -0
- package/data/components/Checkbox.json +176 -0
- package/data/components/Checkboxes.json +240 -0
- package/data/components/Code.json +128 -0
- package/data/components/Collapse.json +218 -0
- package/data/components/Column.json +284 -0
- package/data/components/Columns.json +542 -0
- package/data/components/ConfigProvider.json +16 -0
- package/data/components/Container.json +149 -0
- package/data/components/Content.json +228 -0
- package/data/components/Control.json +484 -0
- package/data/components/DateInput.json +872 -0
- package/data/components/DateTimeInput.json +952 -0
- package/data/components/Delete.json +153 -0
- package/data/components/Dialog.json +285 -0
- package/data/components/Divider.json +68 -0
- package/data/components/Dropdown.json +353 -0
- package/data/components/Emphasis.json +99 -0
- package/data/components/Field.json +539 -0
- package/data/components/Figure.json +143 -0
- package/data/components/File.json +403 -0
- package/data/components/Footer.json +109 -0
- package/data/components/Grid.json +357 -0
- package/data/components/Hero.json +340 -0
- package/data/components/Icon.json +232 -0
- package/data/components/IconText.json +307 -0
- package/data/components/Image.json +166 -0
- package/data/components/Input.json +583 -0
- package/data/components/Level.json +332 -0
- package/data/components/Link.json +154 -0
- package/data/components/LinkButton.json +247 -0
- package/data/components/ListItem.json +101 -0
- package/data/components/Loading.json +323 -0
- package/data/components/Media.json +345 -0
- package/data/components/Menu.json +317 -0
- package/data/components/Message.json +303 -0
- package/data/components/Modal.json +444 -0
- package/data/components/Navbar.json +996 -0
- package/data/components/Notification.json +165 -0
- package/data/components/Numberinput.json +364 -0
- package/data/components/OrderedList.json +189 -0
- package/data/components/Pagination.json +619 -0
- package/data/components/Panel.json +582 -0
- package/data/components/Paragraph.json +107 -0
- package/data/components/Pre.json +114 -0
- package/data/components/Progress.json +137 -0
- package/data/components/Radio.json +164 -0
- package/data/components/Radios.json +240 -0
- package/data/components/Rate.json +466 -0
- package/data/components/Reveal.json +177 -0
- package/data/components/Section.json +123 -0
- package/data/components/Select.json +526 -0
- package/data/components/Sidebar.json +488 -0
- package/data/components/Skeleton.json +162 -0
- package/data/components/Slider.json +534 -0
- package/data/components/Span.json +100 -0
- package/data/components/Steps.json +452 -0
- package/data/components/Strong.json +105 -0
- package/data/components/SubTitle.json +233 -0
- package/data/components/Switch.json +291 -0
- package/data/components/Table.json +490 -0
- package/data/components/Tabs.json +592 -0
- package/data/components/Tag.json +197 -0
- package/data/components/Taginput.json +587 -0
- package/data/components/Tags.json +181 -0
- package/data/components/TextArea.json +483 -0
- package/data/components/Theme.json +65 -0
- package/data/components/TimeInput.json +906 -0
- package/data/components/Title.json +240 -0
- package/data/components/Toast.json +308 -0
- package/data/components/Tooltip.json +212 -0
- package/data/components/UnorderedList.json +149 -0
- package/data/components/Valid value constants.json +16 -0
- package/data/components/classNames.json +65 -0
- package/data/components/useBulmaClasses.json +169 -0
- package/data/components/usePrefixedClassNames.json +25 -0
- package/data/skills/bestax-custom-component/SKILL.md +196 -0
- package/data/skills/bestax-custom-component/examples/stat-card.tsx +105 -0
- package/data/skills/bestax-custom-component/references/api.md +90 -0
- package/data/skills/bestax-custom-component/references/component-catalog.md +149 -0
- package/data/skills/bestax-custom-component/references/library-contributor.md +395 -0
- package/data/skills/bestax-custom-component/references/patterns.md +136 -0
- package/data/skills/bestax-form/SKILL.md +254 -0
- package/data/skills/bestax-form/references/api.md +103 -0
- package/data/skills/bestax-form/references/patterns.md +210 -0
- package/data/skills/bestax-icons/SKILL.md +80 -0
- package/data/skills/bestax-icons/examples/icon-usage.tsx +80 -0
- package/data/skills/bestax-icons/references/icon-libraries.md +131 -0
- package/data/skills/bestax-layout-scaffold/SKILL.md +196 -0
- package/data/skills/bestax-layout-scaffold/examples/app-shell.tsx +86 -0
- package/data/skills/bestax-layout-scaffold/examples/card-grid.tsx +106 -0
- package/data/skills/bestax-layout-scaffold/examples/centered.tsx +56 -0
- package/data/skills/bestax-layout-scaffold/examples/content-page.tsx +90 -0
- package/data/skills/bestax-layout-scaffold/examples/landing.tsx +122 -0
- package/data/skills/bestax-layout-scaffold/references/archetypes.md +246 -0
- package/data/skills/bestax-layout-scaffold/references/layout-components.md +269 -0
- package/data/skills/bestax-migrate/SKILL.md +78 -0
- package/data/skills/bestax-migrate/references/component-map.md +91 -0
- package/data/skills/bestax-migrate/references/css-migration.md +100 -0
- package/data/skills/bestax-migrate/references/prop-map.md +48 -0
- package/data/skills/bestax-migrate/references/unmappables.md +124 -0
- package/data/skills/bestax-optimize/SKILL.md +131 -0
- package/data/skills/bestax-optimize/references/modular-build.md +173 -0
- package/data/skills/bestax-theming/SKILL.md +117 -0
- package/data/skills/bestax-theming/examples/dark-mode.tsx +38 -0
- package/data/skills/bestax-theming/examples/theme-config.tsx +60 -0
- package/data/skills/bestax-theming/references/css-variables.md +466 -0
- package/data/skills/bestax-theming/references/themeable-components.md +119 -0
- package/data/skills.json +148 -0
- package/dist/data.d.ts +131 -0
- package/dist/data.d.ts.map +1 -0
- package/dist/data.js +111 -0
- package/dist/format.d.ts +37 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +178 -0
- package/dist/helper-props.d.ts +57 -0
- package/dist/helper-props.d.ts.map +1 -0
- package/dist/helper-props.js +249 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/search.d.ts +32 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +194 -0
- package/dist/server.d.ts +28 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +616 -0
- package/dist/version.d.ts +29 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +147 -0
- package/package.json +70 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "useBulmaClasses",
|
|
3
|
+
"kind": "helper",
|
|
4
|
+
"category": "helpers",
|
|
5
|
+
"slug": "helpers/usebulmaclasses",
|
|
6
|
+
"docsUrl": "https://bestax.io/docs/api/helpers/usebulmaclasses",
|
|
7
|
+
"examples": [
|
|
8
|
+
{
|
|
9
|
+
"title": "Basic Example",
|
|
10
|
+
"code": "function example() {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: 'link',\n backgroundColor: 'primary',\n p: '4',\n textAlign: 'centered',\n id: 'card1',\n });\n\n return JSON.stringify({ bulmaHelperClasses, rest });\n // bulmaHelperClasses: 'has-text-link has-background-primary p-4 has-text-centered'\n // rest: { id: 'card1' }\n}"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"title": "Basic Example",
|
|
14
|
+
"code": "function example() {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: 'link',\n backgroundColor: 'primary',\n p: '4',\n textAlign: 'centered',\n id: 'card1',\n });\n\n return (\n <div className={classNames('custom-card', bulmaHelperClasses)} {...rest}>\n Card content\n </div>\n );\n}"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"title": "Responsive Props",
|
|
18
|
+
"code": "function example() {\n const { bulmaHelperClasses } = useBulmaClasses({\n color: 'link',\n backgroundColor: 'primary',\n m: '2',\n textAlign: 'centered',\n viewport: 'mobile',\n });\n return JSON.stringify({ bulmaHelperClasses });\n // bulmaHelperClasses: 'has-text-link-mobile m-2-mobile has-text-centered-mobile'\n}"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"title": "With Flexbox Helpers",
|
|
22
|
+
"code": "function example() {\n const { bulmaHelperClasses } = useBulmaClasses({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4',\n });\n return JSON.stringify({ bulmaHelperClasses });\n // bulmaHelperClasses: 'is-flex is-flex-direction-column is-align-items-center is-justify-content-center'\n}"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"title": "With Flexbox Helpers",
|
|
26
|
+
"code": "// Container with display flex\n<Box display=\"flex\" alignItems=\"center\">\n {/* Items don't need display flex to use alignSelf, flexGrow, flexShrink */}\n <Button alignSelf=\"flex-start\">Start</Button>\n <Button alignSelf=\"center\" flexGrow=\"1\">\n Center & Grow\n </Button>\n <Button alignSelf=\"flex-end\" flexShrink=\"0\">\n End & No Shrink\n </Button>\n</Box>"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"title": "Using with Components",
|
|
30
|
+
"code": "function example() {\n function MyColumns({ children, ...props }) {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n return (\n <div className={classNames('columns', bulmaHelperClasses)} {...rest}>\n {children}\n </div>\n );\n }\n\n return (\n <MyColumns textAlign=\"centered\" textTransform=\"uppercase\">\n <div className=\"column\">\n <Notification color=\"primary\">Left</Notification>\n </div>\n <div className=\"column\">\n <Notification color=\"info\">Right</Notification>\n </div>\n </MyColumns>\n );\n}"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"title": "Using with Components",
|
|
34
|
+
"code": "function example() {\n const props = {};\n\n const textColor = 'white';\n const bgColor = 'primary';\n const hasShadow = true;\n const className = 'custom-card';\n const children = <p>Making a custom card</p>;\n const p = '4';\n\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n p,\n ...props,\n });\n const cardClasses = classNames('card', className, bulmaHelperClasses, {\n 'is-shadowless': !hasShadow,\n });\n return (\n <div className={cardClasses} {...rest}>\n {children}\n </div>\n );\n}"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"title": "Colors",
|
|
38
|
+
"code": "<Buttons>\n <Button color=\"primary\">Primary</Button>\n <Button color=\"link\">Link</Button>\n <Button color=\"info\">Info</Button>\n <Button color=\"success\">Success</Button>\n <Button color=\"warning\">Warning</Button>\n <Button color=\"danger\">Danger</Button>\n <Button color=\"black\">Black</Button>\n <Button color=\"white\">White</Button>\n</Buttons>"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"title": "Background Color",
|
|
42
|
+
"code": "<Columns isMultiline>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" textColor=\"white\">\n Primary\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"link\" textColor=\"white\">\n Link\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"info\" textColor=\"white\">\n Info\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"success\" textColor=\"white\">\n Success\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"warning\" textColor=\"black\">\n Warning\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"danger\" textColor=\"white\">\n Danger\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"black\" textColor=\"white\">\n Black\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"white\" textColor=\"black\">\n White\n </Box>\n </Column>\n</Columns>"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"title": "Color Shade",
|
|
46
|
+
"code": "<>\n <Paragraph textColor=\"primary\" colorShade=\"10\" textWeight=\"semibold\">\n Primary 10\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"30\" textWeight=\"semibold\">\n Primary 30\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"60\" textWeight=\"semibold\">\n Primary 60\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"90\" textWeight=\"semibold\">\n Primary 90\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"invert\" textWeight=\"semibold\">\n Primary Invert\n </Paragraph>\n</>"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"title": "Background Color Shade",
|
|
50
|
+
"code": "<Columns isMultiline>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"10\" textColor=\"white\">\n Primary 10\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"30\" textColor=\"white\">\n Primary 30\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"60\" textColor=\"black\">\n Primary 60\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"90\" textColor=\"black\">\n Primary 90\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box\n backgroundColor=\"primary\"\n backgroundColorShade=\"invert\"\n textColor=\"white\"\n >\n Primary Invert\n </Box>\n </Column>\n</Columns>"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"title": "Background Color Shade",
|
|
54
|
+
"code": "<Box\n backgroundColor=\"primary\"\n backgroundColorShade=\"15\"\n textColor=\"primary\"\n colorShade=\"85\"\n>\n Primary text shade 85 on a primary background shade 15\n</Box>"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"title": "Margin",
|
|
58
|
+
"code": "<>\n <Buttons>\n <Button>Left</Button>\n <Button m=\"4\">Margin 4</Button>\n <Button>Right</Button>\n </Buttons>\n <Buttons mt=\"5\" mb=\"5\">\n <Button>Top/Bottom Margin</Button>\n <Button>Row 2</Button>\n </Buttons>\n</>"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"title": "Text Size",
|
|
62
|
+
"code": "<>\n <Notification textSize=\"1\">Size 1</Notification>\n <Notification textSize=\"3\">Size 3</Notification>\n <Notification textSize=\"5\">Size 5</Notification>\n <Notification textSize=\"7\">Size 7</Notification>\n</>"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"title": "Text Align",
|
|
66
|
+
"code": "<>\n <Box textAlign=\"centered\">Centered text</Box>\n <Box textAlign=\"right\">Right aligned text</Box>\n <Box textAlign=\"left\">Left aligned text</Box>\n</>"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"title": "Text Transform",
|
|
70
|
+
"code": "<>\n <Box>\n <Content textTransform=\"uppercase\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textTransform=\"lowercase\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textTransform=\"capitalized\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textTransform=\"italic\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n</>"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"title": "Text Weight",
|
|
74
|
+
"code": "<>\n <Box>\n <Content textWeight=\"light\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textWeight=\"normal\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textWeight=\"medium\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textWeight=\"bold\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n</>"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"title": "Font Family",
|
|
78
|
+
"code": "<>\n <Box>\n <Content fontFamily=\"sans-serif\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"monospace\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"primary\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"secondary\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"code\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n</>"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"title": "Visibility",
|
|
82
|
+
"code": "<Buttons>\n <Button visibility=\"hidden\">Hidden</Button>\n <Button visibility=\"sr-only\">Screen Reader Only</Button>\n <Button>Visible</Button>\n</Buttons>"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"title": "Overflow",
|
|
86
|
+
"code": "<Box overflow=\"clipped\" style={{ width: 200, height: 50 }}>\n This is a very long line of text that will be clipped and not overflow the\n box.\n</Box>"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"title": "Overlay",
|
|
90
|
+
"code": "function OverlayExample() {\n const [show, setShow] = useState(false);\n return (\n <Box overlay={show}>\n <Button onClick={() => setShow(s => !s)}>\n {show ? 'Hide Overlay' : 'Show Overlay'}\n </Button>\n </Box>\n );\n}"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"title": "Interaction",
|
|
94
|
+
"code": "<>\n <Box interaction=\"unselectable\">This text cannot be selected.</Box>\n <Box interaction=\"clickable\">This box is clickable.</Box>\n</>"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"title": "Cursor",
|
|
98
|
+
"code": "<>\n <Box cursor=\"pointer\">Cursor: pointer (uses is-clickable)</Box>\n <Box cursor=\"help\">Cursor: help</Box>\n</>"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"title": "Radius",
|
|
102
|
+
"code": "<Buttons>\n <Button radius=\"radiusless\">Radiusless</Button>\n <Button>Normal</Button>\n</Buttons>"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"title": "Shadowless",
|
|
106
|
+
"code": "<Box shadow=\"shadowless\">This box has no shadow.</Box>"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"title": "ClassName",
|
|
110
|
+
"code": "<Message className=\"custom-message\">\n This message uses a custom className.\n</Message>"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"title": "Clearfix",
|
|
114
|
+
"code": "<>\n <Box>\n <Title size=\"6\">Without clearfix (container collapses)</Title>\n <Notification color=\"danger\">\n <Button float=\"left\" color=\"primary\">\n Left\n </Button>\n <Button float=\"right\" color=\"danger\">\n Right\n </Button>\n </Notification>\n </Box>\n\n <Box>\n <Title size=\"6\">With clearfix (container wraps its floats)</Title>\n <Notification color=\"success\" clearfix>\n <Button float=\"left\" color=\"primary\">\n Left\n </Button>\n <Button float=\"right\" color=\"danger\">\n Right\n </Button>\n </Notification>\n </Box>\n</>"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"title": "Relative Position",
|
|
118
|
+
"code": "<Notification relative pb=\"6\" color=\"info\">\n <Span>This container has position: relative</Span>\n <Tag\n color=\"primary\"\n size=\"medium\"\n style={{ position: 'absolute', top: '1rem', right: '1rem' }}\n >\n Absolutely positioned child\n </Tag>\n</Notification>"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"title": "Skeleton Examples",
|
|
122
|
+
"code": "<Button skeleton>Skeleton Button</Button>"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"title": "Skeleton Examples",
|
|
126
|
+
"code": "<Buttons>\n <Button skeleton>Skeleton</Button>\n <Button skeleton>Skeleton</Button>\n <Button skeleton>Skeleton</Button>\n</Buttons>"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"title": "Skeleton Examples",
|
|
130
|
+
"code": "<Icon name=\"star\" skeleton ariaLabel=\"Star icon skeleton\" />"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"title": "Skeleton Examples",
|
|
134
|
+
"code": "<Image skeleton src=\"/img/react-logo.png\" alt=\"Skeleton image\" size=\"128x128\" />"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"title": "Skeleton Examples",
|
|
138
|
+
"code": "<Notification skeleton>Skeleton notification message.</Notification>"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"title": "Skeleton Examples",
|
|
142
|
+
"code": "<Tag skeleton>Skeleton Tag</Tag>"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"title": "Skeleton Examples",
|
|
146
|
+
"code": "<Title skeleton size=\"2\">\n Skeleton Title\n</Title>"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"title": "Skeleton Examples",
|
|
150
|
+
"code": "<SubTitle skeleton size=\"4\">\n Skeleton SubTitle\n</SubTitle>"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"title": "Skeleton Examples",
|
|
154
|
+
"code": "<Input skeleton placeholder=\"Skeleton Input\" />"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"title": "Skeleton Examples",
|
|
158
|
+
"code": "<TextArea skeleton placeholder=\"Skeleton TextArea\" rows={3} />"
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
"accessibility": null,
|
|
162
|
+
"related": [],
|
|
163
|
+
"storybook": null,
|
|
164
|
+
"summary": "`useBulmaClasses` is a custom React hook that generates Bulma helper class strings from a set of props.",
|
|
165
|
+
"import": "import { useBulmaClasses } from '@allxsmith/bestax-bulma';",
|
|
166
|
+
"doc": "# useBulmaClasses\n\n## Overview\n\n`useBulmaClasses` is a custom React hook that generates Bulma helper class strings from a set of props. It makes it easy to apply color, spacing, alignment, typography, flexbox, and other Bulma utility classes to your components in a type-safe, composable way. It also separates out any non-helper props for direct use on elements.\n\n---\n\n## Import\n\n```tsx\nimport { useBulmaClasses } from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Usage\n\n:::info Container components pad themselves\nContainer components like `Box`, `Notification`, `Message`, and `Card` ship with Bulma's standard, pleasant padding and stack with sensible default spacing. The examples below only add margin or padding props when spacing itself is what's being demonstrated.\n:::\n\n### Basic Example\n\n```tsx live\nfunction example() {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: 'link',\n backgroundColor: 'primary',\n p: '4',\n textAlign: 'centered',\n id: 'card1',\n });\n\n return JSON.stringify({ bulmaHelperClasses, rest });\n // bulmaHelperClasses: 'has-text-link has-background-primary p-4 has-text-centered'\n // rest: { id: 'card1' }\n}\n```\n\nApply to your component:\n\n```tsx live\nfunction example() {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: 'link',\n backgroundColor: 'primary',\n p: '4',\n textAlign: 'centered',\n id: 'card1',\n });\n\n return (\n <div className={classNames('custom-card', bulmaHelperClasses)} {...rest}>\n Card content\n </div>\n );\n}\n```\n\n---\n\n### Responsive Props\n\n```tsx live\nfunction example() {\n const { bulmaHelperClasses } = useBulmaClasses({\n color: 'link',\n backgroundColor: 'primary',\n m: '2',\n textAlign: 'centered',\n viewport: 'mobile',\n });\n return JSON.stringify({ bulmaHelperClasses });\n // bulmaHelperClasses: 'has-text-link-mobile m-2-mobile has-text-centered-mobile'\n}\n```\n\n---\n\n### With Flexbox Helpers\n\nFlexbox properties are divided into two categories:\n\n#### Container Properties\n\nThese properties apply to elements with `display: flex` or `display: inline-flex`:\n\n- `flexDirection` - Direction of flex items\n- `flexWrap` - Whether items wrap\n- `justifyContent` - Alignment along main axis\n- `alignContent` - Alignment of wrapped lines\n- `alignItems` - Alignment along cross axis\n\n#### Item Properties\n\nThese properties apply to flex items (children of flex containers) and **do not require** the element itself to have `display: flex`:\n\n- `alignSelf` - Override container's alignItems for this item\n- `flexGrow` - How much the item should grow\n- `flexShrink` - How much the item should shrink\n\n```tsx live\nfunction example() {\n const { bulmaHelperClasses } = useBulmaClasses({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4',\n });\n return JSON.stringify({ bulmaHelperClasses });\n // bulmaHelperClasses: 'is-flex is-flex-direction-column is-align-items-center is-justify-content-center'\n}\n```\n\nExample with flex items:\n\n```tsx\n// Container with display flex\n<Box display=\"flex\" alignItems=\"center\">\n {/* Items don't need display flex to use alignSelf, flexGrow, flexShrink */}\n <Button alignSelf=\"flex-start\">Start</Button>\n <Button alignSelf=\"center\" flexGrow=\"1\">\n Center & Grow\n </Button>\n <Button alignSelf=\"flex-end\" flexShrink=\"0\">\n End & No Shrink\n </Button>\n</Box>\n```\n\n---\n\n### Using with Components\n\n:::note These are illustrations, not replacements\nThe snippets below rebuild simplified components by hand purely to show how `useBulmaClasses` fits inside one. In a real app, use the library's own [Columns / Column](../columns/columns.md) and [Card](../components/card.md) components — they already wire up `useBulmaClasses` (plus prefixes, modifiers, and more) for you.\n:::\n\n#### Columns\n\n`MyColumns` forwards all of its props to `useBulmaClasses` — the `textAlign` and `textTransform` helper props below flow through `props` into the hook (producing `has-text-centered is-uppercase`), while anything the hook doesn't recognize comes back in `rest` and lands on the `<div>`:\n\n```tsx live\nfunction example() {\n function MyColumns({ children, ...props }) {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n return (\n <div className={classNames('columns', bulmaHelperClasses)} {...rest}>\n {children}\n </div>\n );\n }\n\n return (\n <MyColumns textAlign=\"centered\" textTransform=\"uppercase\">\n <div className=\"column\">\n <Notification color=\"primary\">Left</Notification>\n </div>\n <div className=\"column\">\n <Notification color=\"info\">Right</Notification>\n </div>\n </MyColumns>\n );\n}\n```\n\n#### Card\n\n```tsx live\nfunction example() {\n const props = {};\n\n const textColor = 'white';\n const bgColor = 'primary';\n const hasShadow = true;\n const className = 'custom-card';\n const children = <p>Making a custom card</p>;\n const p = '4';\n\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n p,\n ...props,\n });\n const cardClasses = classNames('card', className, bulmaHelperClasses, {\n 'is-shadowless': !hasShadow,\n });\n return (\n <div className={cardClasses} {...rest}>\n {children}\n </div>\n );\n}\n```\n\n---\n\n### Colors\n\nUse the `color` prop to apply Bulma color classes. Here, each button demonstrates a different color:\n\n```tsx live\n<Buttons>\n <Button color=\"primary\">Primary</Button>\n <Button color=\"link\">Link</Button>\n <Button color=\"info\">Info</Button>\n <Button color=\"success\">Success</Button>\n <Button color=\"warning\">Warning</Button>\n <Button color=\"danger\">Danger</Button>\n <Button color=\"black\">Black</Button>\n <Button color=\"white\">White</Button>\n</Buttons>\n```\n\n### Background Color\n\nUse the `backgroundColor` prop to set the background color. Pair it with a text color that keeps the label readable on each background:\n\n```tsx live\n<Columns isMultiline>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" textColor=\"white\">\n Primary\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"link\" textColor=\"white\">\n Link\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"info\" textColor=\"white\">\n Info\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"success\" textColor=\"white\">\n Success\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"warning\" textColor=\"black\">\n Warning\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"danger\" textColor=\"white\">\n Danger\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"black\" textColor=\"white\">\n Black\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"white\" textColor=\"black\">\n White\n </Box>\n </Column>\n</Columns>\n```\n\n### Color Shade\n\n`colorShade` refines the **text color** — it pairs with the hook's `color` prop (exposed as `textColor` on components, where `color` is the component's own modifier). Here, each line steps through the primary palette:\n\n```tsx live\n<>\n <Paragraph textColor=\"primary\" colorShade=\"10\" textWeight=\"semibold\">\n Primary 10\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"30\" textWeight=\"semibold\">\n Primary 30\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"60\" textWeight=\"semibold\">\n Primary 60\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"90\" textWeight=\"semibold\">\n Primary 90\n </Paragraph>\n <Paragraph textColor=\"primary\" colorShade=\"invert\" textWeight=\"semibold\">\n Primary Invert\n </Paragraph>\n</>\n```\n\n### Background Color Shade\n\nBackground shades use their own prop: `backgroundColorShade` pairs with `backgroundColor`, independently of `colorShade` (which pairs with the text color). Low shade numbers are dark and high numbers are light, so dark shades get light text and light shades get dark text to keep each label readable:\n\n```tsx live\n<Columns isMultiline>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"10\" textColor=\"white\">\n Primary 10\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"30\" textColor=\"white\">\n Primary 30\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"60\" textColor=\"black\">\n Primary 60\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box backgroundColor=\"primary\" backgroundColorShade=\"90\" textColor=\"black\">\n Primary 90\n </Box>\n </Column>\n <Column size=\"one-quarter\">\n <Box\n backgroundColor=\"primary\"\n backgroundColorShade=\"invert\"\n textColor=\"white\"\n >\n Primary Invert\n </Box>\n </Column>\n</Columns>\n```\n\nBecause the two shade props are independent, you can shade the text and the background at the same time — a dark background shade with a light text shade of the same color reads well in both themes:\n\n```tsx live\n<Box\n backgroundColor=\"primary\"\n backgroundColorShade=\"15\"\n textColor=\"primary\"\n colorShade=\"85\"\n>\n Primary text shade 85 on a primary background shade 15\n</Box>\n```\n\n### Margin\n\nUse margin props to add spacing. Here, margin is applied to buttons, and to a row of buttons using the `Buttons` component:\n\n```tsx live\n<>\n <Buttons>\n <Button>Left</Button>\n <Button m=\"4\">Margin 4</Button>\n <Button>Right</Button>\n </Buttons>\n <Buttons mt=\"5\" mb=\"5\">\n <Button>Top/Bottom Margin</Button>\n <Button>Row 2</Button>\n </Buttons>\n</>\n```\n\n### Text Size\n\nApply `textSize` to Notifications to illustrate different sizes:\n\n```tsx live\n<>\n <Notification textSize=\"1\">Size 1</Notification>\n <Notification textSize=\"3\">Size 3</Notification>\n <Notification textSize=\"5\">Size 5</Notification>\n <Notification textSize=\"7\">Size 7</Notification>\n</>\n```\n\n### Text Align\n\nApply `textAlign` to a Box to illustrate text alignment:\n\n```tsx live\n<>\n <Box textAlign=\"centered\">Centered text</Box>\n <Box textAlign=\"right\">Right aligned text</Box>\n <Box textAlign=\"left\">Left aligned text</Box>\n</>\n```\n\n### Text Transform\n\nApply `textTransform` to a Content tag with a paragraph of the Declaration of Independence:\n\n```tsx live\n<>\n <Box>\n <Content textTransform=\"uppercase\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textTransform=\"lowercase\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textTransform=\"capitalized\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textTransform=\"italic\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n</>\n```\n\n### Text Weight\n\nApply `textWeight` to Content tags to illustrate different font weights:\n\n```tsx live\n<>\n <Box>\n <Content textWeight=\"light\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textWeight=\"normal\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textWeight=\"medium\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content textWeight=\"bold\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n</>\n```\n\n### Font Family\n\nUse Content blocks with different font families:\n\n```tsx live\n<>\n <Box>\n <Content fontFamily=\"sans-serif\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"monospace\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"primary\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"secondary\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n <Box>\n <Content fontFamily=\"code\">\n <p>\n We hold these truths to be self-evident, that all men are created\n equal...\n </p>\n </Content>\n </Box>\n</>\n```\n\n### Visibility\n\nShow three buttons, one hidden, one screen-reader only:\n\n```tsx live\n<Buttons>\n <Button visibility=\"hidden\">Hidden</Button>\n <Button visibility=\"sr-only\">Screen Reader Only</Button>\n <Button>Visible</Button>\n</Buttons>\n```\n\n### Overflow\n\nShow a Box with clipped overflow:\n\n```tsx live\n<Box overflow=\"clipped\" style={{ width: 200, height: 50 }}>\n This is a very long line of text that will be clipped and not overflow the\n box.\n</Box>\n```\n\n### Overlay\n\nShow an overlay (toggle with button click):\n\n```tsx\nfunction OverlayExample() {\n const [show, setShow] = useState(false);\n return (\n <Box overlay={show}>\n <Button onClick={() => setShow(s => !s)}>\n {show ? 'Hide Overlay' : 'Show Overlay'}\n </Button>\n </Box>\n );\n}\n```\n\n### Interaction\n\nShow text in a Box with `unselectable`, and another Box with `clickable`:\n\n```tsx live\n<>\n <Box interaction=\"unselectable\">This text cannot be selected.</Box>\n <Box interaction=\"clickable\">This box is clickable.</Box>\n</>\n```\n\n### Cursor\n\nUse the `cursor` prop to set the cursor style. `'pointer'` reuses Bulma's `is-clickable` class, while `'help'` uses the project-level `is-cursor-help` class:\n\n```tsx live\n<>\n <Box cursor=\"pointer\">Cursor: pointer (uses is-clickable)</Box>\n <Box cursor=\"help\">Cursor: help</Box>\n</>\n```\n\n### Radius\n\nShow buttons with `radiusless`:\n\n```tsx live\n<Buttons>\n <Button radius=\"radiusless\">Radiusless</Button>\n <Button>Normal</Button>\n</Buttons>\n```\n\n### Shadowless\n\nShow a Box with `shadowless`:\n\n```tsx live\n<Box shadow=\"shadowless\">This box has no shadow.</Box>\n```\n\n### ClassName\n\nShow a Message with a custom className:\n\n```tsx live\n<Message className=\"custom-message\">\n This message uses a custom className.\n</Message>\n```\n\n### Clearfix\n\nShow containers with and without clearfix to demonstrate the importance of clearing floated elements:\n\n```tsx live\n<>\n <Box>\n <Title size=\"6\">Without clearfix (container collapses)</Title>\n <Notification color=\"danger\">\n <Button float=\"left\" color=\"primary\">\n Left\n </Button>\n <Button float=\"right\" color=\"danger\">\n Right\n </Button>\n </Notification>\n </Box>\n\n <Box>\n <Title size=\"6\">With clearfix (container wraps its floats)</Title>\n <Notification color=\"success\" clearfix>\n <Button float=\"left\" color=\"primary\">\n Left\n </Button>\n <Button float=\"right\" color=\"danger\">\n Right\n </Button>\n </Notification>\n </Box>\n</>\n```\n\n### Relative Position\n\nShow a container with relative positioning that provides context for absolutely positioned children:\n\nBulma only ships an `is-relative` helper — absolute positioning itself has no helper class, so the child keeps a minimal `position/top/right` inline style while everything visual comes from props:\n\n```tsx live\n<Notification relative pb=\"6\" color=\"info\">\n <Span>This container has position: relative</Span>\n <Tag\n color=\"primary\"\n size=\"medium\"\n style={{ position: 'absolute', top: '1rem', right: '1rem' }}\n >\n Absolutely positioned child\n </Tag>\n</Notification>\n```\n\n---\n\n### Skeleton Examples\n\nThe `skeleton` prop applies Bulma's skeleton loading effect. Here are examples for each component:\n\n#### Skeleton Button\n\n```tsx live\n<Button skeleton>Skeleton Button</Button>\n```\n\n#### Skeleton Buttons Group\n\n```tsx live\n<Buttons>\n <Button skeleton>Skeleton</Button>\n <Button skeleton>Skeleton</Button>\n <Button skeleton>Skeleton</Button>\n</Buttons>\n```\n\n#### Skeleton Icon\n\n```tsx live\n<Icon name=\"star\" skeleton ariaLabel=\"Star icon skeleton\" />\n```\n\n#### Skeleton Image\n\n```tsx live\n<Image skeleton src=\"/img/react-logo.png\" alt=\"Skeleton image\" size=\"128x128\" />\n```\n\n#### Skeleton Notification\n\n```tsx live\n<Notification skeleton>Skeleton notification message.</Notification>\n```\n\n#### Skeleton Tag\n\n```tsx live\n<Tag skeleton>Skeleton Tag</Tag>\n```\n\n#### Skeleton Title\n\n```tsx live\n<Title skeleton size=\"2\">\n Skeleton Title\n</Title>\n```\n\n#### Skeleton SubTitle\n\n```tsx live\n<SubTitle skeleton size=\"4\">\n Skeleton SubTitle\n</SubTitle>\n```\n\n#### Skeleton Input\n\n```tsx live\n<Input skeleton placeholder=\"Skeleton Input\" />\n```\n\n#### Skeleton TextArea\n\n```tsx live\n<TextArea skeleton placeholder=\"Skeleton TextArea\" rows={3} />\n```\n\n---\n\n## API\n\n```tsx\nconst { bulmaHelperClasses, rest } = useBulmaClasses(props);\n```\n\n- **props**: An object supporting all Bulma helper class props (see table below), plus any additional props.\n- **bulmaHelperClasses**: A string of Bulma utility classes to be applied to your element.\n- **rest**: An object of all remaining props (with helper props stripped out), suitable for spreading onto your component.\n\n---\n\n## Composable Mini-Hooks\n\n`useBulmaClasses` remains the everything-hook — it is what every bestax-bulma component uses internally, and it covers the full helper prop surface with zero behavior change. If you are building a custom component that only needs a slice of that surface, you can reach for one of six composable mini hooks instead. Each mini hook accepts just its own group of props and returns a plain class string, which you can combine with the [`classNames`](./classnames.md) utility.\n\n| Hook | Props Covered | Returns |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `useColorClasses` | `color`, `colorShade`, `backgroundColor`, `backgroundColorShade` | Text and background color classes, including palette shades |\n| `useSpacingClasses` | `m`, `mt`, `mr`, `mb`, `ml`, `mx`, `my`, `p`, `pt`, `pr`, `pb`, `pl`, `px`, `py` | Margin and padding classes |\n| `useTypographyClasses` | `textSize`, `textAlign`, `textTransform`, `textWeight`, `fontFamily`, `viewport`, plus `textSize{Mobile..Fullhd}`, `textAlign{Mobile..Fullhd}` | Typography classes, including responsive size/alignment variants |\n| `useVisibilityClasses` | `display`, `visibility`, `viewport`, plus `display{Mobile..Fullhd}`, `visibility{Mobile..Fullhd}` | Display and visibility classes (this hook owns all display emission) |\n| `useFlexboxClasses` | `flexDirection`, `flexWrap`, `justifyContent`, `alignContent`, `alignItems`, `alignSelf`, `flexGrow`, `flexShrink` | Flexbox classes; container classes are only emitted when a `display`/`display{Viewport}` prop is `flex`/`inline-flex`, item classes (`alignSelf`, `flexGrow`, `flexShrink`) always emit |\n| `useOtherClasses` | `float`, `overflow`, `overlay`, `interaction`, `cursor`, `radius`, `shadow`, `responsive`, `skeleton`, `clearfix`, `relative`, `fullHeight` | Miscellaneous utility classes |\n\nAll six hooks (and `classNames`) are exported from the package root:\n\n```tsx\nimport {\n useColorClasses,\n useSpacingClasses,\n classNames,\n} from '@allxsmith/bestax-bulma';\n```\n\nHere is a small custom component that only needs color and spacing helpers:\n\n```tsx live\nfunction example() {\n function PriceTag({\n color,\n backgroundColor,\n backgroundColorShade,\n m,\n children,\n }) {\n const colorClasses = useColorClasses({\n color,\n backgroundColor,\n backgroundColorShade,\n });\n const spacingClasses = useSpacingClasses({ m });\n return (\n <span className={classNames('tag', colorClasses, spacingClasses)}>\n {children}\n </span>\n );\n }\n\n return (\n <div>\n <PriceTag color=\"white\" backgroundColor=\"success\" m=\"2\">\n $19.99\n </PriceTag>\n <PriceTag\n color=\"white\"\n backgroundColor=\"danger\"\n backgroundColorShade=\"dark\"\n m=\"2\"\n >\n $49.99\n </PriceTag>\n </div>\n );\n}\n```\n\n:::note useFlexboxClasses and display\n\n`useFlexboxClasses` only emits container classes (`flexDirection`, `flexWrap`, `justifyContent`, `alignContent`, `alignItems`) when a `display` or `display{Viewport}` prop is `flex` or `inline-flex` — it reads those display props for gating but never emits the display class itself. Pair it with `useVisibilityClasses` (which owns display emission), or just use `useBulmaClasses`.\n\n:::\n\n---\n\n## Supported Props\n\nBelow is the full list of supported props, derived from the `BulmaClassesProps` TypeScript definition:\n\n| Prop | Type / Example Value | Effect / Example Class |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| `color` | `'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'` | `has-text-primary`, `has-text-grey-dark`, `has-text-inherit` |\n| `backgroundColor` | Same as `color` | `has-background-warning` |\n| `colorShade` | `'00'`, `'05'`, `'10'`, `'15'`, `'20'`, `'25'`, `'30'`, `'35'`, `'40'`, `'45'`, `'50'`, `'55'`, `'60'`, `'65'`, `'70'`, `'75'`, `'80'`, `'85'`, `'90'`, `'95'`, `'invert'`, `'light'`, `'dark'`, `'soft'`, `'bold'`, `'on-scheme'` | `has-text-primary-25` |\n| `backgroundColorShade` | `'00'`, `'05'`, `'10'`, `'15'`, `'20'`, `'25'`, `'30'`, `'35'`, `'40'`, `'45'`, `'50'`, `'55'`, `'60'`, `'65'`, `'70'`, `'75'`, `'80'`, `'85'`, `'90'`, `'95'`, `'invert'`, `'light'`, `'dark'`, `'soft'`, `'bold'`, `'on-scheme'` | `has-background-info-dark` |\n| `m` | `'0'..'6'`, `'auto'` | `m-4`, `m-auto` |\n| `mt` | `'0'..'6'`, `'auto'` | `mt-2` |\n| `mr` | `'0'..'6'`, `'auto'` | `mr-6` |\n| `mb` | `'0'..'6'`, `'auto'` | `mb-0` |\n| `ml` | `'0'..'6'`, `'auto'` | `ml-auto` |\n| `mx` | `'0'..'6'`, `'auto'` | `mx-2` |\n| `my` | `'0'..'6'`, `'auto'` | `my-5` |\n| `p` | `'0'..'6'`, `'auto'` | `p-3`, `p-auto` |\n| `pt` | `'0'..'6'`, `'auto'` | `pt-1` |\n| `pr` | `'0'..'6'`, `'auto'` | `pr-6` |\n| `pb` | `'0'..'6'`, `'auto'` | `pb-0` |\n| `pl` | `'0'..'6'`, `'auto'` | `pl-4` |\n| `px` | `'0'..'6'`, `'auto'` | `px-1` |\n| `py` | `'0'..'6'`, `'auto'` | `py-6` |\n| `textSize` | `'1'..'7'` | `is-size-3` |\n| `textAlign` | `'centered'`, `'justified'`, `'left'`, `'right'` | `has-text-centered` |\n| `textTransform` | `'capitalized'`, `'lowercase'`, `'uppercase'`, `'italic'` | `is-uppercase`, `is-italic` |\n| `textWeight` | `'light'`, `'normal'`, `'medium'`, `'semibold'`, `'bold'` | `has-text-weight-bold` |\n| `fontFamily` | `'sans-serif'`, `'monospace'`, `'primary'`, `'secondary'`, `'code'` | `is-family-monospace` |\n| `display` | `'block'`, `'flex'`, `'inline'`, `'inline-block'`, `'inline-flex'`, `'none'` | `is-flex`, `is-inline-block`, `is-hidden` |\n| `visibility` | `'hidden'`, `'sr-only'`, `'invisible'` | `is-hidden`, `is-sr-only`, `is-invisible` |\n| `flexDirection` | `'row'`, `'row-reverse'`, `'column'`, `'column-reverse'` | `is-flex-direction-row` |\n| `flexWrap` | `'nowrap'`, `'wrap'`, `'wrap-reverse'` | `is-flex-wrap-nowrap` |\n| `justifyContent` | `'flex-start'`, `'flex-end'`, `'center'`, `'space-between'`, `'space-around'`, `'space-evenly'`, `'start'`, `'end'`, `'left'`, `'right'` | `is-justify-content-center` |\n| `alignContent` | `'flex-start'`, `'flex-end'`, `'center'`, `'space-between'`, `'space-around'`, `'space-evenly'`, `'stretch'` | `is-align-content-stretch` |\n| `alignItems` | `'stretch'`, `'flex-start'`, `'flex-end'`, `'center'`, `'baseline'`, `'start'`, `'end'` | `is-align-items-center` |\n| `alignSelf` | `'auto'`, `'flex-start'`, `'flex-end'`, `'center'`, `'baseline'`, `'stretch'` | `is-align-self-center` |\n| `flexGrow` | `'0'`, `'1'`, `'2'`, `'3'`, `'4'`, `'5'` | `is-flex-grow-1` |\n| `flexShrink` | `'0'`, `'1'`, `'2'`, `'3'`, `'4'`, `'5'` | `is-flex-shrink-0` |\n| `float` | `'left'`, `'right'` | `is-pulled-left`, `is-pulled-right` |\n| `overflow` | `'clipped'` | `is-clipped` |\n| `overlay` | `true` | `is-overlay` |\n| `interaction` | `'unselectable'`, `'clickable'` | `is-unselectable` |\n| `cursor` | `'pointer'`, `'help'` | `is-clickable`, `is-cursor-help` |\n| `radius` | `'radiusless'` | `is-radiusless` |\n| `shadow` | `'shadowless'` | `is-shadowless` |\n| `responsive` | `'mobile'`, `'narrow'` | `is-mobile`, `is-narrow` |\n| `viewport` | `'mobile'`, `'tablet'`, `'desktop'`, `'widescreen'`, `'fullhd'` | Adds viewport suffix to supported properties |\n| `skeleton` | `true` | `is-skeleton` |\n| `clearfix` | `true` | `is-clearfix` |\n| `relative` | `true` | `is-relative` |\n| `fullHeight` | `true` | `is-full-height` |\n\n### Viewport-Specific Properties\n\nThe following properties allow you to set different values for each viewport breakpoint:\n\n| Prop | Type / Example Value | Effect / Example Class |\n| ---------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------- |\n| `displayMobile` | `'block'`, `'flex'`, `'inline'`, `'none'`, etc. | `is-block-mobile`, `is-flex-mobile`, `is-hidden-mobile` |\n| `displayTablet` | Same as above | `is-block-tablet`, `is-flex-tablet`, `is-hidden-tablet` |\n| `displayDesktop` | Same as above | `is-block-desktop`, `is-flex-desktop`, `is-hidden-desktop` |\n| `displayWidescreen` | Same as above | `is-block-widescreen`, `is-flex-widescreen` |\n| `displayFullhd` | Same as above | `is-block-fullhd`, `is-flex-fullhd` |\n| `textSizeMobile` | `'1'..'7'` | `is-size-3-mobile`, `is-size-5-mobile` |\n| `textSizeTablet` | `'1'..'7'` | `is-size-3-tablet`, `is-size-5-tablet` |\n| `textSizeDesktop` | `'1'..'7'` | `is-size-3-desktop`, `is-size-5-desktop` |\n| `textSizeWidescreen` | `'1'..'7'` | `is-size-3-widescreen`, `is-size-5-widescreen` |\n| `textSizeFullhd` | `'1'..'7'` | `is-size-3-fullhd`, `is-size-5-fullhd` |\n| `textAlignMobile` | `'centered'`, `'left'`, `'right'`, `'justified'` | `has-text-centered-mobile`, `has-text-left-mobile` |\n| `textAlignTablet` | Same as above | `has-text-centered-tablet`, `has-text-left-tablet` |\n| `textAlignDesktop` | Same as above | `has-text-centered-desktop`, `has-text-left-desktop` |\n| `textAlignWidescreen` | Same as above | `has-text-centered-widescreen`, `has-text-left-widescreen` |\n| `textAlignFullhd` | Same as above | `has-text-centered-fullhd`, `has-text-left-fullhd` |\n| `visibilityMobile` | `'hidden'`, `'sr-only'`, `'invisible'` | `is-hidden-mobile`, `is-sr-only-mobile`, `is-invisible-mobile` |\n| `visibilityTablet` | Same as above | `is-hidden-tablet`, `is-sr-only-tablet`, `is-invisible-tablet` |\n| `visibilityDesktop` | Same as above | `is-hidden-desktop`, `is-sr-only-desktop`, `is-invisible-desktop` |\n| `visibilityWidescreen` | Same as above | `is-hidden-widescreen`, `is-sr-only-widescreen`, `is-invisible-widescreen` |\n| `visibilityFullhd` | Same as above | `is-hidden-fullhd`, `is-sr-only-fullhd`, `is-invisible-fullhd` |\n| `responsive` | `'mobile'`, `'narrow'` | `is-mobile`, `is-narrow` |\n| `clearfix` | `true` | `is-clearfix` |\n| `relative` | `true` | `is-relative` |\n| `viewport` | `'mobile'`, `'tablet'`, `'desktop'`, `'widescreen'`, `'fullhd'` | Adds suffix, e.g. `-mobile`, `-desktop` |\n\n---\n\n### Full TypeScript Definition\n\n`BulmaClassesProps` is composed from the six group interfaces, each of which is also exported (along with the shared `BulmaViewportProps` and `BulmaDisplayProps` types) and accepted by the corresponding [mini hook](#composable-mini-hooks):\n\n```ts\nexport interface BulmaClassesProps\n extends\n BulmaColorProps,\n BulmaSpacingProps,\n BulmaTypographyProps,\n BulmaVisibilityProps,\n BulmaFlexboxProps,\n BulmaOtherProps {}\n\nexport type BulmaViewportProps =\n 'mobile' | 'tablet' | 'desktop' | 'widescreen' | 'fullhd';\n\nexport type BulmaDisplayProps =\n 'block' | 'flex' | 'inline' | 'inline-block' | 'inline-flex' | 'none';\n\nexport interface BulmaColorProps {\n color?:\n | 'primary'\n | 'link'\n | 'info'\n | 'success'\n | 'warning'\n | 'danger'\n | 'black'\n | 'black-bis'\n | 'black-ter'\n | 'grey-darker'\n | 'grey-dark'\n | 'grey'\n | 'grey-light'\n | 'grey-lighter'\n | 'white'\n | 'light'\n | 'dark'\n | 'inherit'\n | 'current';\n colorShade?:\n | '00'\n | '05'\n | '10'\n | '15'\n | '20'\n | '25'\n | '30'\n | '35'\n | '40'\n | '45'\n | '50'\n | '55'\n | '60'\n | '65'\n | '70'\n | '75'\n | '80'\n | '85'\n | '90'\n | '95'\n | 'invert'\n | 'light'\n | 'dark'\n | 'soft'\n | 'bold'\n | 'on-scheme';\n backgroundColor?: BulmaColorProps['color'];\n backgroundColorShade?: BulmaColorProps['colorShade'];\n}\n\nexport interface BulmaSpacingProps {\n m?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n mt?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n mr?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n mb?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n ml?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n mx?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n my?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n p?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n pt?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n pr?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n pb?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n pl?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n px?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n py?: '0' | '1' | '2' | '3' | '4' | '5' | '6' | 'auto';\n}\n\nexport interface BulmaTypographyProps {\n textSize?: '1' | '2' | '3' | '4' | '5' | '6' | '7';\n textAlign?: 'centered' | 'justified' | 'left' | 'right';\n textTransform?: 'capitalized' | 'lowercase' | 'uppercase' | 'italic';\n textWeight?: 'light' | 'normal' | 'medium' | 'semibold' | 'bold';\n fontFamily?: 'sans-serif' | 'monospace' | 'primary' | 'secondary' | 'code';\n viewport?: BulmaViewportProps;\n textSizeMobile?: '1' | '2' | '3' | '4' | '5' | '6' | '7';\n textSizeTablet?: '1' | '2' | '3' | '4' | '5' | '6' | '7';\n textSizeDesktop?: '1' | '2' | '3' | '4' | '5' | '6' | '7';\n textSizeWidescreen?: '1' | '2' | '3' | '4' | '5' | '6' | '7';\n textSizeFullhd?: '1' | '2' | '3' | '4' | '5' | '6' | '7';\n textAlignMobile?: 'centered' | 'justified' | 'left' | 'right';\n textAlignTablet?: 'centered' | 'justified' | 'left' | 'right';\n textAlignDesktop?: 'centered' | 'justified' | 'left' | 'right';\n textAlignWidescreen?: 'centered' | 'justified' | 'left' | 'right';\n textAlignFullhd?: 'centered' | 'justified' | 'left' | 'right';\n}\n\nexport interface BulmaVisibilityProps {\n display?: BulmaDisplayProps;\n visibility?: 'hidden' | 'sr-only' | 'invisible';\n viewport?: BulmaViewportProps;\n displayMobile?: BulmaDisplayProps;\n displayTablet?: BulmaDisplayProps;\n displayDesktop?: BulmaDisplayProps;\n displayWidescreen?: BulmaDisplayProps;\n displayFullhd?: BulmaDisplayProps;\n visibilityMobile?: 'hidden' | 'sr-only' | 'invisible';\n visibilityTablet?: 'hidden' | 'sr-only' | 'invisible';\n visibilityDesktop?: 'hidden' | 'sr-only' | 'invisible';\n visibilityWidescreen?: 'hidden' | 'sr-only' | 'invisible';\n visibilityFullhd?: 'hidden' | 'sr-only' | 'invisible';\n}\n\nexport interface BulmaFlexboxProps {\n flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';\n flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';\n justifyContent?:\n | 'flex-start'\n | 'flex-end'\n | 'center'\n | 'space-between'\n | 'space-around'\n | 'space-evenly'\n | 'start'\n | 'end'\n | 'left'\n | 'right';\n alignContent?:\n | 'flex-start'\n | 'flex-end'\n | 'center'\n | 'space-between'\n | 'space-around'\n | 'space-evenly'\n | 'stretch';\n alignItems?:\n | 'stretch'\n | 'flex-start'\n | 'flex-end'\n | 'center'\n | 'baseline'\n | 'start'\n | 'end';\n alignSelf?:\n 'auto' | 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';\n flexGrow?: '0' | '1' | '2' | '3' | '4' | '5';\n flexShrink?: '0' | '1' | '2' | '3' | '4' | '5';\n}\n\nexport interface BulmaOtherProps {\n float?: 'left' | 'right';\n overflow?: 'clipped';\n overlay?: boolean;\n interaction?: 'unselectable' | 'clickable';\n cursor?: 'pointer' | 'help';\n radius?: 'radiusless';\n shadow?: 'shadowless';\n responsive?: 'mobile' | 'narrow';\n skeleton?: boolean;\n clearfix?: boolean;\n relative?: boolean;\n fullHeight?: boolean;\n}\n```\n\n---\n\n## Responsive Design\n\n### Global Viewport Property (Legacy)\n\nThe `viewport` prop applies a single viewport modifier to ALL properties that support it:\n\n```tsx\nconst { bulmaHelperClasses } = useBulmaClasses({\n color: 'primary',\n textAlign: 'centered',\n textSize: '4',\n viewport: 'mobile', // Applies -mobile to ALL properties\n});\n// Result: has-text-primary-mobile has-text-centered-mobile is-size-4-mobile\n```\n\n### Viewport-Specific Properties (Recommended)\n\nViewport-specific properties allow you to set **different values for each viewport**:\n\n```tsx\nconst { bulmaHelperClasses } = useBulmaClasses({\n // Different text sizes for each viewport\n textSizeMobile: '6', // Small on mobile\n textSizeTablet: '5', // Medium on tablet\n textSizeDesktop: '4', // Large on desktop\n\n // Different text alignment for each viewport\n textAlignMobile: 'centered', // Center on mobile\n textAlignDesktop: 'left', // Left align on desktop\n\n // Different visibility for each viewport\n visibilityMobile: 'hidden', // Hidden on mobile\n visibilityTablet: 'invisible', // Invisible on tablet\n});\n// Result: is-size-6-mobile is-size-5-tablet is-size-4-desktop\n// has-text-centered-mobile has-text-left-desktop is-hidden-mobile is-invisible-tablet\n```\n\n### Breakpoint Reference\n\nBulma's responsive breakpoints:\n\n- **mobile**: up to 768px\n- **tablet**: 769px - 1023px\n- **desktop**: 1024px - 1215px\n- **widescreen**: 1216px - 1407px\n- **fullhd**: 1408px and above\n\n### Properties That Support Viewport Modifiers\n\nOnly certain properties support responsive variants:\n\n**✅ Support Viewport Modifiers:**\n\n- Text size (`textSize`, `textSizeMobile`, `textSizeTablet`, etc.)\n- Text alignment (`textAlign`, `textAlignMobile`, `textAlignTablet`, etc.)\n- Display (`display`, `displayMobile`, `displayTablet`, etc.)\n- Visibility (`visibility`, `visibilityMobile`, `visibilityTablet`, etc.)\n\n**❌ Do NOT Support Viewport Modifiers:**\n\n- Spacing (`m`, `mt`, `mr`, `mb`, `ml`, `mx`, `my`, `p`, `pt`, `pr`, `pb`, `pl`, `px`, `py`)\n- Background colors (`backgroundColor`)\n- Flexbox properties (`flexDirection`, `flexWrap`, `justifyContent`, etc.)\n- Other helpers (`float`, `clearfix`, `relative`, `borderRadius`, `shadow`, etc.)\n\n---\n\n## Notes\n\n- Any prop not recognized as a Bulma helper prop is passed through in `rest`.\n- Responsive helper props (`viewport`) suffix classes with the breakpoint (e.g., `is-size-3-mobile`).\n- You can use in combination with [`classNames`](./classnames.md) for more advanced use.\n\n---\n\n## See Also\n\n- [classNames helper](./classnames.md)\n- [Bulma Classes Reference](https://bulma.io/documentation/helpers/)\n- [Storybook: useBulmaClasses Demo](https://bestax.io/storybook/?path=/story/helpers-usebulmaclasses--default)",
|
|
167
|
+
"parts": [],
|
|
168
|
+
"cssVars": []
|
|
169
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "usePrefixedClassNames",
|
|
3
|
+
"kind": "helper",
|
|
4
|
+
"category": "helpers",
|
|
5
|
+
"slug": "helpers/useprefixedclassnames",
|
|
6
|
+
"docsUrl": "https://bestax.io/docs/api/helpers/useprefixedclassnames",
|
|
7
|
+
"examples": [
|
|
8
|
+
{
|
|
9
|
+
"title": "In a custom component (honors `ConfigProvider`)",
|
|
10
|
+
"code": "import {\n usePrefixedClassNames,\n useBulmaClasses,\n classNames,\n type BulmaClassesProps,\n} from '@allxsmith/bestax-bulma';\n\ninterface ChipProps\n extends React.HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n isRounded?: boolean;\n}\n\nfunction Chip({ color, isRounded, className, children, ...props }: ChipProps) {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n\n // 'tag is-rounded is-primary' — or 'bestax-tag bestax-is-rounded …'\n // inside <ConfigProvider classPrefix=\"bestax-\">\n const chipClasses = usePrefixedClassNames('tag', {\n [`is-${color}`]: !!color,\n 'is-rounded': isRounded,\n });\n\n return (\n <span\n className={classNames(chipClasses, bulmaHelperClasses, className)}\n {...rest}\n >\n {children}\n </span>\n );\n}"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"title": "In a custom component (honors `ConfigProvider`)",
|
|
14
|
+
"code": "import { ConfigProvider } from '@allxsmith/bestax-bulma';\n\n<ConfigProvider classPrefix=\"bestax-\">\n <Chip color=\"primary\" isRounded>\n Prefixed\n </Chip>\n {/* renders class=\"bestax-tag bestax-is-primary bestax-is-rounded\" */}\n</ConfigProvider>;"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"accessibility": null,
|
|
18
|
+
"related": [],
|
|
19
|
+
"storybook": null,
|
|
20
|
+
"summary": "`usePrefixedClassNames` builds a component class string that honors the `classPrefix` from `ConfigProvider` — the hook every bestax component uses for its own…",
|
|
21
|
+
"import": "import { usePrefixedClassNames } from '@allxsmith/bestax-bulma';",
|
|
22
|
+
"doc": "# usePrefixedClassNames\n\n## Overview\n\n`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.\n\n---\n\n## Import\n\n```tsx\nimport {\n usePrefixedClassNames,\n prefixedClassNames,\n createPrefixedClassNames,\n} from '@allxsmith/bestax-bulma';\n```\n\n---\n\n## Usage\n\n### In a custom component (honors `ConfigProvider`)\n\nThis 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.\n\n```tsx\nimport {\n usePrefixedClassNames,\n useBulmaClasses,\n classNames,\n type BulmaClassesProps,\n} from '@allxsmith/bestax-bulma';\n\ninterface ChipProps\n extends React.HTMLAttributes<HTMLSpanElement>, BulmaClassesProps {\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n isRounded?: boolean;\n}\n\nfunction Chip({ color, isRounded, className, children, ...props }: ChipProps) {\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n\n // 'tag is-rounded is-primary' — or 'bestax-tag bestax-is-rounded …'\n // inside <ConfigProvider classPrefix=\"bestax-\">\n const chipClasses = usePrefixedClassNames('tag', {\n [`is-${color}`]: !!color,\n 'is-rounded': isRounded,\n });\n\n return (\n <span\n className={classNames(chipClasses, bulmaHelperClasses, className)}\n {...rest}\n >\n {children}\n </span>\n );\n}\n```\n\nWrapped in a provider, the component emits prefixed classes automatically:\n\n```tsx\nimport { ConfigProvider } from '@allxsmith/bestax-bulma';\n\n<ConfigProvider classPrefix=\"bestax-\">\n <Chip color=\"primary\" isRounded>\n Prefixed\n </Chip>\n {/* renders class=\"bestax-tag bestax-is-primary bestax-is-rounded\" */}\n</ConfigProvider>;\n```\n\n### Outside a component: `prefixedClassNames`\n\nWhen you already know the prefix (or might not have one), pass it as the first argument:\n\n```ts\nprefixedClassNames('bulma-', 'button', { 'is-primary': true });\n// => 'bulma-button bulma-is-primary'\n\nprefixedClassNames(undefined, 'button', { 'is-primary': true });\n// => 'button is-primary'\n```\n\n### Reusable prefixer: `createPrefixedClassNames`\n\nBind the prefix once and reuse the returned function:\n\n```ts\nconst cx = createPrefixedClassNames('bulma-');\n\ncx('card', ['has-shadow', { 'is-active': true }]);\n// => 'bulma-card bulma-has-shadow bulma-is-active'\n```\n\n---\n\n## API\n\n```ts\n// Hook: reads classPrefix from the nearest ConfigProvider\nfunction usePrefixedClassNames(\n ...args: (\n | string\n | number\n | undefined\n | null\n | false\n | Record<string, unknown>\n | unknown[]\n )[]\n): string;\n\n// Plain function: pass the prefix explicitly (undefined ⇒ plain classNames)\nfunction prefixedClassNames(\n prefix: string | undefined,\n ...args: (\n | string\n | number\n | undefined\n | null\n | false\n | Record<string, unknown>\n | unknown[]\n )[]\n): string;\n\n// Factory: returns a classNames function bound to a fixed prefix\nfunction createPrefixedClassNames(classPrefix: string): (...args) => string; // args: same union as classNames\n```\n\n### Parameters\n\n| Function | Parameter | Type | Description |\n| -------------------------- | ------------- | --------------------- | -------------------------------------------------------------------------------------------------- |\n| `usePrefixedClassNames` | `...args` | same as `classNames` | Class values to join. The `classPrefix` from `ConfigProvider` is applied to every resulting class. |\n| `prefixedClassNames` | `prefix` | `string \\| undefined` | Prefix to apply. When `undefined` (or empty), behaves exactly like `classNames`. |\n| `prefixedClassNames` | `...args` | same as `classNames` | Class values to join. |\n| `createPrefixedClassNames` | `classPrefix` | `string` | Prefix baked into the returned function. |\n\nAll three return a space-separated string of unique class names. With no `ConfigProvider` (or no `classPrefix` set), `usePrefixedClassNames` produces the same output as `classNames`.\n\n---\n\n## Tips\n\n- 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.\n- Never route a consumer-supplied `className` through these helpers; combine it afterwards with plain `classNames` so user classes stay untouched.\n- `usePrefixedClassNames` is a React hook — call it unconditionally at the top level of a component. Use `prefixedClassNames`/`createPrefixedClassNames` everywhere else.\n\n---\n\n## See Also\n\n- [`classNames`](./classnames.md): The unprefixed class-string builder these helpers wrap.\n- [`ConfigProvider`](./config.md): Where `classPrefix` comes from.\n- [`useBulmaClasses`](./usebulmaclasses.md): Helper-prop classes (already prefix-aware).",
|
|
23
|
+
"parts": [],
|
|
24
|
+
"cssVars": []
|
|
25
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bestax-custom-component
|
|
3
|
+
description: Build a custom React component in the bestax/Bulma style. In an app using @allxsmith/bestax-bulma — compose existing components, helper props, public hooks (useBulmaClasses, usePrefixedClassNames), and --bulma-* CSS variables. In the bestax monorepo — the full component pipeline (SCSS partial, stories, tests, docs, wiring). Use when creating a component beyond stock Bulma or extending one.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Building a custom component the bestax way
|
|
8
|
+
|
|
9
|
+
This skill teaches how to build a component that isn't in the library — composed from bestax
|
|
10
|
+
pieces in an app, or as a full library "extra" inside the bestax monorepo.
|
|
11
|
+
|
|
12
|
+
## Which context are you in?
|
|
13
|
+
|
|
14
|
+
- **The bestax monorepo** (the repo contains `bulma-ui/src/`) → follow
|
|
15
|
+
`references/library-contributor.md` instead of this file: five-file layout, SCSS partial,
|
|
16
|
+
stories, jest tests, docs page, wiring.
|
|
17
|
+
- **An app depending on `@allxsmith/bestax-bulma`** (e.g. scaffolded by `npm create bestax`) →
|
|
18
|
+
continue here. Everything below assumes public package imports and a plain Vite app.
|
|
19
|
+
|
|
20
|
+
For **form** components (Field/Control/Input/etc.) use the `bestax-form` skill instead.
|
|
21
|
+
|
|
22
|
+
## Check for an existing component first
|
|
23
|
+
|
|
24
|
+
Before building anything, **search the library for a component that already does this — or a
|
|
25
|
+
close synonym** — and tell the user what you found. Many requests are covered by an existing
|
|
26
|
+
element, or are best built by composing existing ones. Building a near-duplicate (a "label" when
|
|
27
|
+
`Tag` exists, a "banner" when `Notification` exists) fragments the API and is usually the wrong
|
|
28
|
+
call.
|
|
29
|
+
|
|
30
|
+
Where to look:
|
|
31
|
+
|
|
32
|
+
- `references/component-catalog.md` — **start here.** Every documented component with a one-line
|
|
33
|
+
purpose, grouped by category. Scan it for the name and its synonyms before anything else.
|
|
34
|
+
- https://bestax.io/docs/api — one doc page per shipped component (full props).
|
|
35
|
+
|
|
36
|
+
Then decide, and **surface the decision to the user**:
|
|
37
|
+
|
|
38
|
+
- **Exact / synonym match exists** → recommend using it. Don't build a duplicate. (E.g. a small
|
|
39
|
+
colored label/badge/chip → `Tag` / `Tags` already exist.)
|
|
40
|
+
- **Partial overlap** → prefer **composing** the existing pieces inside your new component
|
|
41
|
+
rather than re-implementing them. (E.g. a "profile card" → there's no `ProfileCard`, but
|
|
42
|
+
`Card`, `Image`, `Title`, `SubTitle`, and `Content` exist; build `ProfileCard` to compose them.)
|
|
43
|
+
- **Genuine gap** → build the new component using the pattern below.
|
|
44
|
+
|
|
45
|
+
State plainly which case applies before writing code, e.g. _"`Tag` already covers a colored
|
|
46
|
+
label — use that instead"_ or _"No `ProfileCard` exists; I'll build one composing the existing
|
|
47
|
+
`Card`/`Image`/`Title` elements."_
|
|
48
|
+
|
|
49
|
+
## Composition first
|
|
50
|
+
|
|
51
|
+
Build from existing components before writing any CSS: `Box`, `Card`, `Title`, `SubTitle`,
|
|
52
|
+
`Icon`, `Block`, `Content`, `Tag`, plus the shared Bulma helper props (spacing, color,
|
|
53
|
+
typography, flexbox). Compound sub-parts are the exception — `Card.Content`, `Modal.Card`,
|
|
54
|
+
`Tabs.Tab`, `Message.Body` take **no Bulma helper props**, just `className` + HTML attributes
|
|
55
|
+
plus their own few (`Tabs.Tab` requires `index={i}` and has built-in `disabled` and
|
|
56
|
+
`icon`/`iconLibrary`/`iconVariant`/`iconSize`/`iconFeatures` — don't nest an `<Icon>` there) —
|
|
57
|
+
so put helper props on the parent or on an element inside them, never invent them there. Most "custom components" are a composition function — zero new styles.
|
|
58
|
+
See `examples/stat-card.tsx` for a complete worked example.
|
|
59
|
+
|
|
60
|
+
## The component spine
|
|
61
|
+
|
|
62
|
+
Same shape the library itself uses, with all imports from the package. Every reusable
|
|
63
|
+
component gets it — including pure compositions with zero CSS (a heading block, a labeled
|
|
64
|
+
wrapper): extend `BulmaClassesProps`, run your props through `useBulmaClasses`, merge its
|
|
65
|
+
`bulmaHelperClasses` into `className`, and spread the `rest` **it** returns — spreading the raw
|
|
66
|
+
props instead leaks helper props onto the DOM and emits none of their classes. The
|
|
67
|
+
`usePrefixedClassNames` root class is needed only when component-scoped CSS (or a variant
|
|
68
|
+
class) targets it — a zero-CSS composition may omit that call. File at
|
|
69
|
+
`src/components/MyComponent.tsx`:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import type React from 'react';
|
|
73
|
+
import {
|
|
74
|
+
classNames,
|
|
75
|
+
usePrefixedClassNames,
|
|
76
|
+
useBulmaClasses,
|
|
77
|
+
type BulmaClassesProps,
|
|
78
|
+
} from '@allxsmith/bestax-bulma';
|
|
79
|
+
|
|
80
|
+
export interface MyComponentProps
|
|
81
|
+
extends
|
|
82
|
+
Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,
|
|
83
|
+
Omit<BulmaClassesProps, 'color'> {
|
|
84
|
+
color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function MyComponent({
|
|
88
|
+
color,
|
|
89
|
+
className,
|
|
90
|
+
children,
|
|
91
|
+
...props
|
|
92
|
+
}: MyComponentProps) {
|
|
93
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
94
|
+
const mainClasses = usePrefixedClassNames('mycomponent', {
|
|
95
|
+
[`is-${color}`]: !!color,
|
|
96
|
+
});
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
className={classNames(mainClasses, bulmaHelperClasses, className)}
|
|
100
|
+
{...rest}
|
|
101
|
+
>
|
|
102
|
+
{children}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This gives your component the full Bulma helper-prop surface (`m`, `p`, `textAlign`, …) for
|
|
109
|
+
free. `references/api.md` documents the helpers.
|
|
110
|
+
|
|
111
|
+
## Styling ladder — use the lowest rung that works
|
|
112
|
+
|
|
113
|
+
**Rung 1 — helper props only (default).** House rules: never `style={{}}`. Layout with
|
|
114
|
+
`Block`/`Box` and `display="flex"`, `flexDirection`, `alignItems`, `justifyContent`. Flex
|
|
115
|
+
layouts have **no `gap` helper** — space children with `m*`/`p*` margins instead (`Grid` and
|
|
116
|
+
`Columns` take a `gap` prop). Before writing `style={{ … }}` anywhere, translate each
|
|
117
|
+
declaration:
|
|
118
|
+
|
|
119
|
+
| Inline style you're about to write | Helper props instead |
|
|
120
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `marginTop: '1rem'` (any margin/padding) | `mt="4"` — `m`/`mt`/`mx`/`p`/`py`/… scale: `1`=0.25rem, `2`=0.5rem, `3`=0.75rem, `4`=1rem, `5`=1.5rem, `6`=3rem (nearest step) |
|
|
122
|
+
| `textAlign: 'center'` | `textAlign="centered"` (also `left`, `right`, `justified`) |
|
|
123
|
+
| `color: '#…'` | `textColor` with the nearest Bulma color: `primary`, `link`, `info`, `success`, `warning`, `danger`, `white`, `black`, `grey` (+ `grey-light`, `grey-dark`, …) |
|
|
124
|
+
| `backgroundColor: '#…'` | `bgColor` (same palette) |
|
|
125
|
+
| `fontSize: …` | `textSize="1"`…`"7"` (`1` largest) — for headings use `Title`/`SubTitle` `size` |
|
|
126
|
+
| `fontWeight: …` | `textWeight`: `light`, `normal`, `medium`, `semibold`, `bold` |
|
|
127
|
+
| `textTransform`, italics | `textTransform`: `uppercase`, `lowercase`, `capitalized`, `italic` |
|
|
128
|
+
| `display: 'flex'` + flex properties | same-named props: `display="flex"`, `flexDirection`, `justifyContent`, `alignItems`, `flexWrap` |
|
|
129
|
+
| `height: '100%'` on a flex child | `flexGrow="1"` |
|
|
130
|
+
| `display: 'none'` | `visibility="hidden"`, or responsive `display*` props (`displayMobile`, `displayTablet`, …) |
|
|
131
|
+
|
|
132
|
+
Spacing, typography, and flex helpers are on every component; `textColor`/`bgColor` are on
|
|
133
|
+
the content components you'll compose with (`Box`, `Block`, `Title`, `Content`, `Card`, …) —
|
|
134
|
+
the ones with a semantic `color` variant (`Tag`, `Tabs`, `Panel`) take `color` instead.
|
|
135
|
+
`Notification` is the mixed case: it takes `textColor`, but its background comes from the
|
|
136
|
+
semantic `color` prop, not `bgColor`.
|
|
137
|
+
|
|
138
|
+
A value with no helper equivalent (`maxWidth: 720`, a one-off gradient) moves you to rung 2 —
|
|
139
|
+
a named class in a stylesheet — never to inline `style`.
|
|
140
|
+
|
|
141
|
+
**Rung 2 — a plain CSS file**, scoped under the component's class, consuming `--bulma-*`
|
|
142
|
+
variables — never literal colors, so `Theme` and dark mode keep working:
|
|
143
|
+
|
|
144
|
+
```css
|
|
145
|
+
/* src/components/MyComponent.css — import from the .tsx file */
|
|
146
|
+
.mycomponent {
|
|
147
|
+
/* component-scoped custom props, initialized from Bulma tokens:
|
|
148
|
+
any ancestor (or Theme) can re-theme by overriding them */
|
|
149
|
+
--mycomponent-radius: var(--bulma-radius);
|
|
150
|
+
--mycomponent-accent: var(--bulma-primary);
|
|
151
|
+
border-radius: var(--mycomponent-radius);
|
|
152
|
+
border: 1px solid var(--bulma-border);
|
|
153
|
+
background: var(--bulma-scheme-main);
|
|
154
|
+
color: var(--bulma-text);
|
|
155
|
+
}
|
|
156
|
+
.mycomponent .mycomponent-value {
|
|
157
|
+
color: var(--mycomponent-accent);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Caveat: with the prefixed CSS flavor / `ConfigProvider classPrefix`, `usePrefixedClassNames`
|
|
162
|
+
prefixes your classes too — your CSS selectors must match (or build them with plain
|
|
163
|
+
`classNames` instead).
|
|
164
|
+
|
|
165
|
+
**Rung 3 — real Sass (optional).** `npm i -D sass` — nothing else; Vite compiles imported
|
|
166
|
+
`.scss` zero-config, and `bulma` is resolvable because it's a runtime dependency of
|
|
167
|
+
bestax-bulma. Then the full `register-vars`/`getVar` pattern from
|
|
168
|
+
`references/library-contributor.md` works in-app. Prefixed flavor:
|
|
169
|
+
`@use 'bulma/sass/utilities/initial-variables' with ($class-prefix: 'bestax-')`.
|
|
170
|
+
|
|
171
|
+
## Verify in the browser
|
|
172
|
+
|
|
173
|
+
Types don't see layout. Run `npm run dev`, render the component, and actually look at it:
|
|
174
|
+
vertical centering of inline text (use `display="flex" alignItems="center"`, not line-height
|
|
175
|
+
hacks), balanced padding, nothing clipping, every color/size variant, and **dark mode**
|
|
176
|
+
legibility. Fix what you see, then re-check. No browser available (headless)? Fall back to
|
|
177
|
+
`npm run build` plus a Node `renderToString` smoke render, grep the emitted HTML for the
|
|
178
|
+
expected classes, and flag the visual pass as not done.
|
|
179
|
+
|
|
180
|
+
## Tests and stories in an app
|
|
181
|
+
|
|
182
|
+
The scaffolded app has **no test runner and no Storybook** — do not install or scaffold them
|
|
183
|
+
unasked. If the app already has vitest/jest + Testing Library, write the four test shapes:
|
|
184
|
+
render, prop→class mapping, helper-prop passthrough (`m="3"` → `m-3`), and the
|
|
185
|
+
`ConfigProvider classPrefix` case if the app uses a prefix.
|
|
186
|
+
|
|
187
|
+
## Checklist
|
|
188
|
+
|
|
189
|
+
- [ ] Inventory checked (catalog + bestax.io/docs/api) and the decision surfaced to the user.
|
|
190
|
+
- [ ] All imports from `@allxsmith/bestax-bulma` (no deep/internal paths).
|
|
191
|
+
- [ ] Composition first — existing components + helper props before any CSS; every reusable component gets the spine.
|
|
192
|
+
- [ ] No inline `style={{}}` anywhere — translate via the rung-1 mapping table; values with
|
|
193
|
+
no helper equivalent get a named class (rung 2).
|
|
194
|
+
- [ ] Lowest sufficient ladder rung (helper props → scoped CSS vars → Sass).
|
|
195
|
+
- [ ] All colors/radii derived from `--bulma-*` variables — no literals.
|
|
196
|
+
- [ ] Renders correctly via `npm run dev`, including dark mode.
|