create-bestax 2.2.0 → 3.1.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +27 -6
- package/dist/project-creator.d.ts +2 -0
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +27 -3
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +9 -0
- package/package.json +4 -3
- package/templates/skills/bestax-custom-component/SKILL.md +389 -0
- package/templates/skills/bestax-custom-component/references/api.md +77 -0
- package/templates/skills/bestax-custom-component/references/patterns.md +133 -0
- package/templates/skills/bestax-form/SKILL.md +209 -0
- package/templates/skills/bestax-form/references/api.md +102 -0
- package/templates/skills/bestax-form/references/patterns.md +210 -0
- package/templates/skills/bestax-layout-scaffold/SKILL.md +66 -0
- package/templates/skills/bestax-layout-scaffold/examples/app-shell.tsx +80 -0
- package/templates/skills/bestax-layout-scaffold/examples/card-grid.tsx +98 -0
- package/templates/skills/bestax-layout-scaffold/examples/centered.tsx +56 -0
- package/templates/skills/bestax-layout-scaffold/examples/landing.tsx +77 -0
- package/templates/skills/bestax-layout-scaffold/references/archetypes.md +183 -0
- package/templates/skills/bestax-layout-scaffold/references/layout-components.md +181 -0
- package/templates/skills/bestax-theming/SKILL.md +73 -0
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +38 -0
- package/templates/skills/bestax-theming/examples/theme-config.tsx +58 -0
- package/templates/skills/bestax-theming/references/css-variables.md +130 -0
- package/templates/skills/bestax-theming/references/themeable-components.md +74 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Layout archetypes
|
|
2
|
+
|
|
3
|
+
Four named, composable page patterns. For each: **when to pick it**, the **JSX skeleton**, and its
|
|
4
|
+
**responsive behavior**. Map the request to one archetype and build it — do not ask the user layout
|
|
5
|
+
questions. Full runnable versions live in `examples/`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. App shell with sidebar
|
|
10
|
+
|
|
11
|
+
**Pick when:** the request is an internal/authenticated tool — dashboard, admin, console, back
|
|
12
|
+
office, or anything that mentions a persistent sidebar/navigation. The default for "build me an
|
|
13
|
+
app".
|
|
14
|
+
|
|
15
|
+
**Skeleton:**
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<>
|
|
19
|
+
<Navbar fixed="top" color="dark">
|
|
20
|
+
<Navbar.Brand>
|
|
21
|
+
<Navbar.Item href="#">Brand</Navbar.Item>
|
|
22
|
+
<Navbar.Burger
|
|
23
|
+
active={open}
|
|
24
|
+
onClick={() => setOpen(o => !o)}
|
|
25
|
+
aria-label="menu"
|
|
26
|
+
/>
|
|
27
|
+
</Navbar.Brand>
|
|
28
|
+
<Navbar.Menu active={open}>
|
|
29
|
+
<Navbar.End>
|
|
30
|
+
<Navbar.Item href="#">Account</Navbar.Item>
|
|
31
|
+
</Navbar.End>
|
|
32
|
+
</Navbar.Menu>
|
|
33
|
+
</Navbar>
|
|
34
|
+
|
|
35
|
+
<Container fluid>
|
|
36
|
+
<Columns>
|
|
37
|
+
<Column size={3} sizeWidescreen={2}>
|
|
38
|
+
<Menu>
|
|
39
|
+
<Menu.Label>General</Menu.Label>
|
|
40
|
+
<Menu.List>
|
|
41
|
+
<Menu.Item active href="#">
|
|
42
|
+
Dashboard
|
|
43
|
+
</Menu.Item>
|
|
44
|
+
<Menu.Item href="#">Customers</Menu.Item>
|
|
45
|
+
</Menu.List>
|
|
46
|
+
</Menu>
|
|
47
|
+
</Column>
|
|
48
|
+
<Column>
|
|
49
|
+
<Section>{/* main content */}</Section>
|
|
50
|
+
</Column>
|
|
51
|
+
</Columns>
|
|
52
|
+
</Container>
|
|
53
|
+
</>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Required:** add `has-navbar-fixed-top` to `<html>` (see `examples/app-shell.tsx`) so content is
|
|
57
|
+
not hidden behind the fixed navbar.
|
|
58
|
+
|
|
59
|
+
**Responsive:** the navbar collapses to a burger on mobile (`Navbar.Burger` + `Navbar.Menu active`).
|
|
60
|
+
The sidebar and content columns sit side by side on tablet and up, and stack (menu above content)
|
|
61
|
+
on mobile.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 2. Marketing / landing
|
|
66
|
+
|
|
67
|
+
**Pick when:** the request is a public-facing page — landing, homepage, marketing, product or
|
|
68
|
+
pricing page. The default for "build me a site/page".
|
|
69
|
+
|
|
70
|
+
**Skeleton:**
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
<>
|
|
74
|
+
<Hero color="primary" size="medium">
|
|
75
|
+
<Hero.Body>
|
|
76
|
+
<Container textAlign="centered">
|
|
77
|
+
<Title size="1">Headline</Title>
|
|
78
|
+
<SubTitle size="3">Supporting line.</SubTitle>
|
|
79
|
+
<Buttons isCentered>
|
|
80
|
+
<Button color="light" size="large">
|
|
81
|
+
Primary CTA
|
|
82
|
+
</Button>
|
|
83
|
+
</Buttons>
|
|
84
|
+
</Container>
|
|
85
|
+
</Hero.Body>
|
|
86
|
+
</Hero>
|
|
87
|
+
|
|
88
|
+
<Section size="large">
|
|
89
|
+
<Container>
|
|
90
|
+
<Columns>
|
|
91
|
+
<Column>
|
|
92
|
+
<Box>{/* feature */}</Box>
|
|
93
|
+
</Column>
|
|
94
|
+
<Column>
|
|
95
|
+
<Box>{/* feature */}</Box>
|
|
96
|
+
</Column>
|
|
97
|
+
<Column>
|
|
98
|
+
<Box>{/* feature */}</Box>
|
|
99
|
+
</Column>
|
|
100
|
+
</Columns>
|
|
101
|
+
</Container>
|
|
102
|
+
</Section>
|
|
103
|
+
|
|
104
|
+
<Footer>
|
|
105
|
+
<Container>
|
|
106
|
+
<Content textAlign="centered">{/* footer */}</Content>
|
|
107
|
+
</Container>
|
|
108
|
+
</Footer>
|
|
109
|
+
</>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Responsive:** `Section`s already stack vertically. The feature `Columns` collapse to one feature
|
|
113
|
+
per row on mobile. Use `Hero size="large"` / `"fullheight"` for a taller hero.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 3. Centered single-column
|
|
118
|
+
|
|
119
|
+
**Pick when:** the request is one focused task — login, sign up, auth, settings, checkout, or a
|
|
120
|
+
single standalone form/panel.
|
|
121
|
+
|
|
122
|
+
**Skeleton:**
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
<Section>
|
|
126
|
+
<Container>
|
|
127
|
+
<Columns isCentered>
|
|
128
|
+
<Column size="half" sizeWidescreen="one-third">
|
|
129
|
+
<Box>{/* form / panel */}</Box>
|
|
130
|
+
</Column>
|
|
131
|
+
</Columns>
|
|
132
|
+
</Container>
|
|
133
|
+
</Section>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Responsive:** the centered column is narrow on desktop and becomes full width on mobile. Tighten
|
|
137
|
+
or widen via the `size*` breakpoint props (`one-third` desktop, `half` tablet, full mobile).
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 4. Card grid / catalog
|
|
142
|
+
|
|
143
|
+
**Pick when:** the request is a collection of similar items — catalog, gallery, products, listing,
|
|
144
|
+
search results, "a grid of cards".
|
|
145
|
+
|
|
146
|
+
**Skeleton:**
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<Section>
|
|
150
|
+
<Container>
|
|
151
|
+
<Columns isMultiline>
|
|
152
|
+
{items.map(item => (
|
|
153
|
+
<Column
|
|
154
|
+
key={item.id}
|
|
155
|
+
sizeMobile="full"
|
|
156
|
+
sizeTablet="half"
|
|
157
|
+
sizeDesktop="one-third"
|
|
158
|
+
>
|
|
159
|
+
<Card
|
|
160
|
+
image={item.image}
|
|
161
|
+
header={item.name}
|
|
162
|
+
footer={<span className="card-footer-item">{item.price}</span>}
|
|
163
|
+
>
|
|
164
|
+
{item.blurb}
|
|
165
|
+
</Card>
|
|
166
|
+
</Column>
|
|
167
|
+
))}
|
|
168
|
+
</Columns>
|
|
169
|
+
</Container>
|
|
170
|
+
</Section>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Responsive:** `isMultiline` wraps cards onto new rows; the `size*` props set the per-row count —
|
|
174
|
+
1 on mobile, 2 on tablet, 3 on desktop here. Change the fractions to change the column count.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Combining archetypes
|
|
179
|
+
|
|
180
|
+
Archetypes nest. An "admin dashboard with a product list" is **App shell** whose main `Column`
|
|
181
|
+
holds a **Card grid**. A "settings page inside the app" is **App shell** whose main column holds a
|
|
182
|
+
**Centered** panel. Pick the outer archetype from the dominant intent, then place another inside
|
|
183
|
+
its content area.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Layout component inventory
|
|
2
|
+
|
|
3
|
+
Self-contained reference for the `@allxsmith/bestax-bulma` layout components. All values are
|
|
4
|
+
source-verified. Import everything from the package root:
|
|
5
|
+
|
|
6
|
+
```tsx
|
|
7
|
+
import {
|
|
8
|
+
Container,
|
|
9
|
+
Section,
|
|
10
|
+
Hero,
|
|
11
|
+
Footer,
|
|
12
|
+
Level,
|
|
13
|
+
Columns,
|
|
14
|
+
Column,
|
|
15
|
+
Navbar,
|
|
16
|
+
Menu,
|
|
17
|
+
Card,
|
|
18
|
+
} from '@allxsmith/bestax-bulma';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `textAlign`,
|
|
22
|
+
`textColor`, `bgColor`, etc.).
|
|
23
|
+
|
|
24
|
+
> **There is no `Tile` component.** Build grids and nested layouts with `Columns` / `Column`.
|
|
25
|
+
|
|
26
|
+
## Container
|
|
27
|
+
|
|
28
|
+
`<Container>` centers and constrains page content.
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Notes |
|
|
31
|
+
| --------------------------------- | --------------------------------------- | ----------------------------------------- |
|
|
32
|
+
| `fluid` | `boolean` | Full width with a small gutter. |
|
|
33
|
+
| `widescreen` / `fullhd` | `boolean` | Only constrain at that breakpoint and up. |
|
|
34
|
+
| `breakpoint` | `'tablet' \| 'desktop' \| 'widescreen'` | Max-width breakpoint. |
|
|
35
|
+
| `isMax` | `boolean` | Use the `is-max-*` width. |
|
|
36
|
+
| `color` / `textColor` / `bgColor` | Bulma color | — |
|
|
37
|
+
|
|
38
|
+
## Section
|
|
39
|
+
|
|
40
|
+
`<Section>` adds vertical page rhythm (padding).
|
|
41
|
+
|
|
42
|
+
| Prop | Type |
|
|
43
|
+
| --------------------------------- | ---------------------------------------- |
|
|
44
|
+
| `size` | `'medium' \| 'large'` (omit for default) |
|
|
45
|
+
| `color` / `textColor` / `bgColor` | Bulma color |
|
|
46
|
+
|
|
47
|
+
## Hero
|
|
48
|
+
|
|
49
|
+
`<Hero>` is a full-width banner. Subcomponents: `Hero.Head`, `Hero.Body`, `Hero.Foot`.
|
|
50
|
+
|
|
51
|
+
| Prop | Type |
|
|
52
|
+
| ---------------------- | ---------------------------------------------------------------------------- |
|
|
53
|
+
| `color` | Bulma color |
|
|
54
|
+
| `size` | `'small' \| 'medium' \| 'large' \| 'fullheight' \| 'fullheight-with-navbar'` |
|
|
55
|
+
| `fullheightWithNavbar` | `boolean` |
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Hero color="primary" size="medium">
|
|
59
|
+
<Hero.Body>…</Hero.Body>
|
|
60
|
+
</Hero>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Footer
|
|
64
|
+
|
|
65
|
+
`<Footer>` is the page footer.
|
|
66
|
+
|
|
67
|
+
| Prop | Type |
|
|
68
|
+
| --------------------------------- | ------------------- |
|
|
69
|
+
| `as` | `'footer' \| 'div'` |
|
|
70
|
+
| `color` / `textColor` / `bgColor` | Bulma color |
|
|
71
|
+
|
|
72
|
+
## Level
|
|
73
|
+
|
|
74
|
+
`<Level>` is a horizontal toolbar (left/right groups). Subcomponents: `Level.Left`, `Level.Right`,
|
|
75
|
+
`Level.Item`.
|
|
76
|
+
|
|
77
|
+
| Prop | Type |
|
|
78
|
+
| ---------------------------- | ------------------------------------- |
|
|
79
|
+
| `Level.isMobile` | `boolean` (keep horizontal on mobile) |
|
|
80
|
+
| `Level.Item.as` | `'div' \| 'p' \| 'a'` |
|
|
81
|
+
| `Level.Item.hasTextCentered` | `boolean` |
|
|
82
|
+
|
|
83
|
+
## Columns / Column
|
|
84
|
+
|
|
85
|
+
The responsive grid. `Columns` is the row; `Column` is a cell.
|
|
86
|
+
|
|
87
|
+
**Columns**
|
|
88
|
+
|
|
89
|
+
| Prop | Type |
|
|
90
|
+
| -------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
|
|
91
|
+
| `isMultiline` | `boolean` (wrap cells onto new rows) |
|
|
92
|
+
| `isCentered` | `boolean` (center the row) |
|
|
93
|
+
| `isVCentered` | `boolean` (vertical centering — capital V) |
|
|
94
|
+
| `isGapless` | `boolean` |
|
|
95
|
+
| `isMobile` | `boolean` (stay side-by-side on mobile) |
|
|
96
|
+
| `isDesktop` | `boolean` |
|
|
97
|
+
| `gapSize` / `gapSizeMobile` / `gapSizeTablet` / `gapSizeDesktop` / `gapSizeWidescreen` / `gapSizeFullhd` | `0`–`8` (number or string) |
|
|
98
|
+
|
|
99
|
+
**Column**
|
|
100
|
+
|
|
101
|
+
| Prop | Type |
|
|
102
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
|
|
103
|
+
| `size` (+ `sizeMobile` / `sizeTablet` / `sizeDesktop` / `sizeWidescreen` / `sizeFullhd`) | `BulmaColumnSize` |
|
|
104
|
+
| `offset` (+ per-breakpoint) | `BulmaColumnSize` |
|
|
105
|
+
| `isNarrow` (+ per-breakpoint: `isNarrowMobile` / `isNarrowTablet` / `isNarrowTouch` / `isNarrowDesktop` / `isNarrowWidescreen` / `isNarrowFullhd`) | `boolean` |
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
type BulmaColumnSize =
|
|
109
|
+
| number // 1–12
|
|
110
|
+
| 'full'
|
|
111
|
+
| 'half'
|
|
112
|
+
| 'one-third'
|
|
113
|
+
| 'two-thirds'
|
|
114
|
+
| 'one-quarter'
|
|
115
|
+
| 'three-quarters'
|
|
116
|
+
| 'one-fifth'
|
|
117
|
+
| 'two-fifths'
|
|
118
|
+
| 'three-fifths'
|
|
119
|
+
| 'four-fifths';
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
> Columns **stack on mobile** by default and go side-by-side at the tablet breakpoint and up.
|
|
123
|
+
> Use the per-breakpoint `size*` props to control how many cells share a row at each width.
|
|
124
|
+
|
|
125
|
+
## Navbar
|
|
126
|
+
|
|
127
|
+
`<Navbar>` is the top bar. Subcomponents: `Navbar.Brand`, `Navbar.Item`, `Navbar.Link`,
|
|
128
|
+
`Navbar.Burger`, `Navbar.Menu`, `Navbar.Start`, `Navbar.End`, `Navbar.Dropdown`,
|
|
129
|
+
`Navbar.DropdownMenu`, `Navbar.Divider`.
|
|
130
|
+
|
|
131
|
+
| Prop | Type |
|
|
132
|
+
| --------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `fixed` | `'top' \| 'bottom'` |
|
|
134
|
+
| `transparent` | `boolean` |
|
|
135
|
+
| `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger' \| 'black' \| 'dark' \| 'light' \| 'white'` |
|
|
136
|
+
| `Navbar.Item` | `as?`, `active?`, `href` |
|
|
137
|
+
| `Navbar.Burger` | `active?`, `onClick` |
|
|
138
|
+
| `Navbar.Menu` | `active?` (toggled open on mobile) |
|
|
139
|
+
|
|
140
|
+
The burger/menu is controlled state — toggle `Navbar.Burger active`/`onClick` and pass the same
|
|
141
|
+
flag to `Navbar.Menu active`. **A `fixed="top"` navbar requires `has-navbar-fixed-top` on `<html>`**
|
|
142
|
+
(Bulma offsets the page from it); the library adds no helper, so set it yourself:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
document.documentElement.classList.add('has-navbar-fixed-top');
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Menu
|
|
149
|
+
|
|
150
|
+
`<Menu>` is a vertical sidebar menu. Subcomponents: `Menu.Label`, `Menu.List`, `Menu.Item`.
|
|
151
|
+
|
|
152
|
+
| Prop | Type |
|
|
153
|
+
| ----------- | ------------------------------------------------------------------------------------------------- |
|
|
154
|
+
| `Menu.Item` | `active?`, `href?`, `as?` (custom link component); nest a `Menu.List` inside an item for submenus |
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<Menu>
|
|
158
|
+
<Menu.Label>General</Menu.Label>
|
|
159
|
+
<Menu.List>
|
|
160
|
+
<Menu.Item active href="#">
|
|
161
|
+
Dashboard
|
|
162
|
+
</Menu.Item>
|
|
163
|
+
<Menu.Item href="#">Customers</Menu.Item>
|
|
164
|
+
</Menu.List>
|
|
165
|
+
</Menu>
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Card
|
|
169
|
+
|
|
170
|
+
`<Card>` for catalog/grid items. Use the props form (`header`, `image`, `footer`) or the compound
|
|
171
|
+
subcomponents (`Card.Header` + `.Title`/`.Icon`, `Card.Image`, `Card.Content`, `Card.Footer`,
|
|
172
|
+
`Card.FooterItem`).
|
|
173
|
+
|
|
174
|
+
| Prop | Type |
|
|
175
|
+
| ------------------------------- | ----------------------------- |
|
|
176
|
+
| `header` | `ReactNode` |
|
|
177
|
+
| `image` | `string` (URL) or `ReactNode` |
|
|
178
|
+
| `imageAlt` | `string` |
|
|
179
|
+
| `footer` | `ReactNode \| ReactNode[]` |
|
|
180
|
+
| `hasShadow` | `boolean` (default `true`) |
|
|
181
|
+
| `headerCentered` / `headerIcon` | `boolean` / `ReactNode` |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bestax-theming
|
|
3
|
+
description: Customize colors, branding, dark mode, and visual tokens of an app built with @allxsmith/bestax-bulma. Use when changing the primary/brand color, recoloring components, overriding Bulma --bulma-* CSS variables, setting fonts/radius/spacing tokens, or adding light/dark mode.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Theming @allxsmith/bestax-bulma
|
|
8
|
+
|
|
9
|
+
`@allxsmith/bestax-bulma` wraps Bulma 1.x, which is themed through `--bulma-*` CSS custom
|
|
10
|
+
properties. Theme an app by overriding the right variables — no component re-styling required.
|
|
11
|
+
|
|
12
|
+
## Use when
|
|
13
|
+
|
|
14
|
+
- Setting a brand/primary color or recoloring `link`/`info`/`success`/`warning`/`danger`.
|
|
15
|
+
- Adjusting global tokens — radius, fonts, sizes, weights.
|
|
16
|
+
- Adding light/dark mode.
|
|
17
|
+
|
|
18
|
+
## Approach
|
|
19
|
+
|
|
20
|
+
Recolor a brand color by overriding its **hue/saturation/lightness trio** — Bulma derives every
|
|
21
|
+
shade, light/dark, and invert variant from `--bulma-<color>-h` / `-s` / `-l`. Override the trio and
|
|
22
|
+
the whole palette follows.
|
|
23
|
+
|
|
24
|
+
Choose an override path:
|
|
25
|
+
|
|
26
|
+
- **`Theme` component (runtime, preferred).** Exported from the package. Pass named HSL props
|
|
27
|
+
(`primaryH`, `primaryS`, `primaryL`, …) and/or `bulmaVars={{ '--bulma-*': '…' }}` for everything
|
|
28
|
+
else. Add `isRoot` to inject the variables globally at `:root` (once, at the app root); omit it to
|
|
29
|
+
scope the variables to the wrapped subtree.
|
|
30
|
+
- **Plain CSS.** Set `:root { --bulma-primary-h: …; }` (or any selector) directly.
|
|
31
|
+
- **Build-time Sass.** `@use 'bulma/sass' with ($primary: #1e6b99)` when compiling Bulma's Sass.
|
|
32
|
+
|
|
33
|
+
For **dark mode**, pass `colorMode` to `Theme` (`'light' | 'dark' | 'system'`). It writes Bulma's
|
|
34
|
+
`data-theme` attribute on `<html>`, flipping the light/dark scheme — global, even on a scoped
|
|
35
|
+
`Theme`; `'system'` follows the OS `prefers-color-scheme`. Drive it from state on the app-root
|
|
36
|
+
`Theme`: `<Theme isRoot colorMode={mode}>`.
|
|
37
|
+
|
|
38
|
+
Reach for the helper props (`color` / `textColor` / `bgColor` / `colorShade`, `textSize`,
|
|
39
|
+
`textWeight`, `fontFamily`) to apply themed colors and type to individual components.
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { Theme, Button } from '@allxsmith/bestax-bulma';
|
|
45
|
+
|
|
46
|
+
// Global brand theme at the app root.
|
|
47
|
+
<Theme isRoot primaryH="265" primaryS="65%" primaryL="55%">
|
|
48
|
+
<App />
|
|
49
|
+
</Theme>;
|
|
50
|
+
|
|
51
|
+
// Themed components recolor automatically.
|
|
52
|
+
<Button color="primary">Save</Button>;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## References
|
|
56
|
+
|
|
57
|
+
- `references/css-variables.md` — the `--bulma-*` variable map (colors, scheme/text/border, radius,
|
|
58
|
+
fonts, sizes, weights, dark mode) and all three override mechanisms.
|
|
59
|
+
- `references/themeable-components.md` — which components take `color`/`size` props, the real
|
|
60
|
+
accepted values, and the shared helper props.
|
|
61
|
+
|
|
62
|
+
## Examples
|
|
63
|
+
|
|
64
|
+
- `examples/theme-config.tsx` — a custom brand theme at the app root, plus a scoped override.
|
|
65
|
+
- `examples/dark-mode.tsx` — a light/dark toggle using Bulma's `data-theme`.
|
|
66
|
+
|
|
67
|
+
## Checklist
|
|
68
|
+
|
|
69
|
+
- [ ] Recolor brand colors via the HSL trio (`*-h` / `*-s` / `*-l`), not by hard-coding hex on components.
|
|
70
|
+
- [ ] Apply a global theme once with `<Theme isRoot>` (or `:root`); use scoped `<Theme>` for one-off sections.
|
|
71
|
+
- [ ] Set non-color tokens (radius, fonts, sizes) through `bulmaVars` or `:root`.
|
|
72
|
+
- [ ] Implement dark mode with `data-theme` on `<html>`; do not expect a shipped dark-mode component.
|
|
73
|
+
- [ ] Pass `color`/`textColor`/`bgColor` (not custom CSS) to color individual components.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Dark mode for an app built on @allxsmith/bestax-bulma.
|
|
2
|
+
//
|
|
3
|
+
// The Theme component drives the light/dark scheme: pass `colorMode` and it
|
|
4
|
+
// writes Bulma's `data-theme` attribute on <html>. This is global (even on a
|
|
5
|
+
// scoped Theme); `'system'` removes the attribute so Bulma follows the OS
|
|
6
|
+
// `prefers-color-scheme`. Wrap the app once at the root.
|
|
7
|
+
import React, { useState } from 'react';
|
|
8
|
+
import {
|
|
9
|
+
Theme,
|
|
10
|
+
Box,
|
|
11
|
+
Button,
|
|
12
|
+
Title,
|
|
13
|
+
Notification,
|
|
14
|
+
} from '@allxsmith/bestax-bulma';
|
|
15
|
+
|
|
16
|
+
type Mode = 'light' | 'dark' | 'system';
|
|
17
|
+
|
|
18
|
+
export function DarkModeToggle() {
|
|
19
|
+
const [mode, setMode] = useState<Mode>('system');
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Theme isRoot colorMode={mode}>
|
|
23
|
+
<Box>
|
|
24
|
+
<Title size="5">Color mode: {mode}</Title>
|
|
25
|
+
<Notification color="info">
|
|
26
|
+
Components below follow the current Bulma color scheme.
|
|
27
|
+
</Notification>
|
|
28
|
+
<Button color="primary" onClick={() => setMode('light')}>
|
|
29
|
+
Light
|
|
30
|
+
</Button>
|
|
31
|
+
<Button color="primary" onClick={() => setMode('dark')}>
|
|
32
|
+
Dark
|
|
33
|
+
</Button>
|
|
34
|
+
<Button onClick={() => setMode('system')}>System</Button>
|
|
35
|
+
</Box>
|
|
36
|
+
</Theme>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Custom brand theme for an app built on @allxsmith/bestax-bulma.
|
|
2
|
+
// Copy this to your app and adjust the HSL values to your brand.
|
|
3
|
+
//
|
|
4
|
+
// Bulma 1.x derives every shade of a color from its hue/saturation/lightness
|
|
5
|
+
// trio (--bulma-<color>-h / -s / -l), so overriding the trio recolors the whole
|
|
6
|
+
// palette. The <Theme> component writes those CSS variables for you:
|
|
7
|
+
// - isRoot -> inject the variables globally at :root (use once, at the app root)
|
|
8
|
+
// - without isRoot -> wrap children in a <div> and scope the variables to it
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import {
|
|
11
|
+
Theme,
|
|
12
|
+
Button,
|
|
13
|
+
Notification,
|
|
14
|
+
Box,
|
|
15
|
+
Title,
|
|
16
|
+
} from '@allxsmith/bestax-bulma';
|
|
17
|
+
|
|
18
|
+
// 1) Global brand theme at the app root.
|
|
19
|
+
// Hue is unitless; saturation and lightness are percentages.
|
|
20
|
+
export function ThemedApp({ children }: { children: React.ReactNode }) {
|
|
21
|
+
return (
|
|
22
|
+
<Theme
|
|
23
|
+
isRoot
|
|
24
|
+
// Brand primary (HSL trio) — recolors every `is-primary` / `has-text-primary`.
|
|
25
|
+
primaryH="265"
|
|
26
|
+
primaryS="65%"
|
|
27
|
+
primaryL="55%"
|
|
28
|
+
// A matching link color.
|
|
29
|
+
linkH="200"
|
|
30
|
+
linkS="80%"
|
|
31
|
+
linkL="45%"
|
|
32
|
+
// Anything without a named prop goes through `bulmaVars`, keyed by the real
|
|
33
|
+
// --bulma-* variable names (string values).
|
|
34
|
+
bulmaVars={{
|
|
35
|
+
'--bulma-radius': '0.75rem',
|
|
36
|
+
'--bulma-radius-large': '1.25rem',
|
|
37
|
+
'--bulma-family-primary': "'Inter', system-ui, sans-serif",
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{children}
|
|
41
|
+
</Theme>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2) Scoped override — theme just one section; the rest of the app is untouched.
|
|
46
|
+
export function PromoPanel() {
|
|
47
|
+
return (
|
|
48
|
+
<Theme primaryH="16" primaryS="85%" primaryL="55%">
|
|
49
|
+
<Box>
|
|
50
|
+
<Title size="4">Limited offer</Title>
|
|
51
|
+
<Notification color="primary">
|
|
52
|
+
This panel uses a warm primary; the rest of the app is unaffected.
|
|
53
|
+
</Notification>
|
|
54
|
+
<Button color="primary">Claim it</Button>
|
|
55
|
+
</Box>
|
|
56
|
+
</Theme>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Bulma CSS variables & override mechanisms
|
|
2
|
+
|
|
3
|
+
`@allxsmith/bestax-bulma` wraps **Bulma 1.x**, which is themed entirely through `--bulma-*` CSS
|
|
4
|
+
custom properties. Every color, font, size, and radius resolves from one of these variables, so
|
|
5
|
+
theming means overriding the right `--bulma-*` values.
|
|
6
|
+
|
|
7
|
+
## How to override (three paths)
|
|
8
|
+
|
|
9
|
+
### 1. The `Theme` component (runtime, recommended)
|
|
10
|
+
|
|
11
|
+
`Theme` is exported from the package and writes `--bulma-*` variables for you.
|
|
12
|
+
|
|
13
|
+
- `isRoot` → injects the variables globally at `:root` (use once, at the app root).
|
|
14
|
+
- without `isRoot` → wraps children in a `<div>` and scopes the variables to that subtree.
|
|
15
|
+
- Named props set the color/scheme HSL channels and common values (see "Theme props" below).
|
|
16
|
+
- `bulmaVars` sets any other variable, keyed by its real `--bulma-*` name:
|
|
17
|
+
|
|
18
|
+
```tsx
|
|
19
|
+
<Theme
|
|
20
|
+
isRoot
|
|
21
|
+
primaryH="265"
|
|
22
|
+
primaryS="65%"
|
|
23
|
+
primaryL="55%"
|
|
24
|
+
bulmaVars={{
|
|
25
|
+
'--bulma-radius': '0.75rem',
|
|
26
|
+
'--bulma-family-primary': "'Inter', sans-serif",
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
<App />
|
|
30
|
+
</Theme>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> Note: the type of the `bulmaVars` keys is not exported — pass it as an object literal (TypeScript
|
|
34
|
+
> still checks the keys against the allowed `--bulma-*` names).
|
|
35
|
+
|
|
36
|
+
### 2. Plain CSS
|
|
37
|
+
|
|
38
|
+
Set the variables yourself on any selector. `:root` themes the whole document; a class scopes it.
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
:root {
|
|
42
|
+
--bulma-primary-h: 265;
|
|
43
|
+
--bulma-primary-s: 65%;
|
|
44
|
+
--bulma-primary-l: 55%;
|
|
45
|
+
--bulma-radius: 0.75rem;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Build-time Sass
|
|
50
|
+
|
|
51
|
+
If compiling Bulma's Sass yourself, set the SCSS variables before the CSS variables are generated:
|
|
52
|
+
|
|
53
|
+
```scss
|
|
54
|
+
@use 'bulma/sass' with (
|
|
55
|
+
$primary: #1e6b99,
|
|
56
|
+
$link: #485fc7
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
(The library itself ships `$primary: #1e6b99` — HSL `202 / 67% / 36%` — this way.)
|
|
61
|
+
|
|
62
|
+
## Colors — override the H/S/L trio to recolor
|
|
63
|
+
|
|
64
|
+
Bulma derives a color's entire palette (shades, light/dark/invert variants) from three channels.
|
|
65
|
+
Override the trio; the rest follows automatically. For each color `<c>` ∈ `primary`, `link`,
|
|
66
|
+
`info`, `success`, `warning`, `danger`:
|
|
67
|
+
|
|
68
|
+
| Variable | Meaning | `Theme` prop |
|
|
69
|
+
| --------------- | -------------------------- | ------------------------ |
|
|
70
|
+
| `--bulma-<c>-h` | hue (unitless, e.g. `265`) | `<c>H` (e.g. `primaryH`) |
|
|
71
|
+
| `--bulma-<c>-s` | saturation (`%`) | `<c>S` |
|
|
72
|
+
| `--bulma-<c>-l` | lightness (`%`) | `<c>L` |
|
|
73
|
+
|
|
74
|
+
Read-only derived values (set automatically; reference if you need them): `--bulma-<c>` (the
|
|
75
|
+
resolved color), `--bulma-<c>-invert`, `--bulma-<c>-light`, `--bulma-<c>-dark`, `--bulma-<c>-rgb`,
|
|
76
|
+
and numeric shades `--bulma-<c>-00` … `--bulma-<c>-95`.
|
|
77
|
+
|
|
78
|
+
## Scheme, text, background, border (light/dark surfaces)
|
|
79
|
+
|
|
80
|
+
| Variable | Role |
|
|
81
|
+
| --------------------------------------------------------------------------- | ----------------------------------------------------- |
|
|
82
|
+
| `--bulma-scheme-h`, `--bulma-scheme-s` | scheme hue/saturation (`Theme`: `schemeH`, `schemeS`) |
|
|
83
|
+
| `--bulma-scheme-main`, `--bulma-scheme-main-bis`, `--bulma-scheme-main-ter` | page/surface backgrounds (main + subtle steps) |
|
|
84
|
+
| `--bulma-background` | secondary background |
|
|
85
|
+
| `--bulma-text`, `--bulma-text-strong`, `--bulma-text-weak` | body / emphasized / muted text |
|
|
86
|
+
| `--bulma-border`, `--bulma-border-weak` | borders |
|
|
87
|
+
|
|
88
|
+
## Radius, typography
|
|
89
|
+
|
|
90
|
+
| Variable | Default | `Theme` prop |
|
|
91
|
+
| ----------------------------------------------------------------------------- | --------------------------------- | --------------- |
|
|
92
|
+
| `--bulma-radius-small` / `--bulma-radius` / `--bulma-radius-large` | 0.25 / 0.375 / 0.75rem | via `bulmaVars` |
|
|
93
|
+
| `--bulma-radius-rounded` | 9999px | via `bulmaVars` |
|
|
94
|
+
| `--bulma-family-primary` / `--bulma-family-secondary` / `--bulma-family-code` | sans / sans / mono | via `bulmaVars` |
|
|
95
|
+
| `--bulma-size-1` … `--bulma-size-7` | 3rem … 0.75rem | via `bulmaVars` |
|
|
96
|
+
| `--bulma-size-small` / `-normal` / `-medium` / `-large` | 0.75 / 1 / 1.25 / 1.5rem | via `bulmaVars` |
|
|
97
|
+
| `--bulma-weight-light/normal/medium/semibold/bold/extrabold` | 300 / 400 / 500 / 600 / 700 / 800 | via `bulmaVars` |
|
|
98
|
+
|
|
99
|
+
## Dark mode (`Theme colorMode`)
|
|
100
|
+
|
|
101
|
+
Drive the light/dark scheme with the `Theme` component's **`colorMode`** prop —
|
|
102
|
+
`'light' | 'dark' | 'system'`:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<Theme isRoot colorMode={mode}>
|
|
106
|
+
<App />
|
|
107
|
+
</Theme>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`colorMode` writes Bulma's **`data-theme`** attribute on `<html>`, so it is always **global** (even
|
|
111
|
+
on a scoped `Theme`). `'system'` removes the attribute, so Bulma falls back to the OS
|
|
112
|
+
`@media (prefers-color-scheme: dark)`. Omitting `colorMode` leaves the current setting untouched.
|
|
113
|
+
|
|
114
|
+
Under the hood this is Bulma 1.x's own mechanism — `[data-theme="dark"]` / `.theme-dark` selectors
|
|
115
|
+
(plus the `prefers-color-scheme` media query). Setting the attribute by hand still works
|
|
116
|
+
(`document.documentElement.setAttribute('data-theme', 'dark')`); `colorMode` just does it for you.
|
|
117
|
+
|
|
118
|
+
Under dark mode Bulma flips the scheme/text/border/background lightness variables (e.g.
|
|
119
|
+
`--bulma-scheme-main-l` 100% → 9%, `--bulma-text-l` 29% → 71%). Your brand color overrides from
|
|
120
|
+
`Theme isRoot` or `:root` still apply on top, because they set the hue/saturation/lightness
|
|
121
|
+
channels directly.
|
|
122
|
+
|
|
123
|
+
## `Theme` props (named)
|
|
124
|
+
|
|
125
|
+
Color trios: `primaryH/primaryS/primaryL`, `linkH/linkS/linkL`, `infoH/S/L`, `successH/S/L`,
|
|
126
|
+
`warningH/S/L`, `dangerH/S/L`. Scheme: `schemeH`, `schemeS`, `lightL`, `darkL`, `lightInvertL`,
|
|
127
|
+
`darkInvertL`, `softL`, `boldL`, `softInvertL`, `boldInvertL`. Interaction deltas:
|
|
128
|
+
`hoverBackgroundLDelta`, `activeBackgroundLDelta`, `hoverBorderLDelta`, `activeBorderLDelta`,
|
|
129
|
+
`hoverColorLDelta`, `activeColorLDelta`, `hoverShadowADelta`, `activeShadowADelta`. Everything else:
|
|
130
|
+
`bulmaVars={{ '--bulma-…': '…' }}`. All values are strings.
|