boreal-ui 0.0.888 → 0.0.889
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 +101 -62
- package/README.npm.md +448 -0
- package/docs/installation-and-imports.md +48 -10
- package/docs/styling-and-theming.md +47 -9
- package/package.json +1 -1
- package/packages/cli/src/commands/init.js +186 -8
- package/packages/cli/src/utils/args.js +13 -4
- package/packages/cli/src/utils/constants.js +4 -4
- package/packages/cli/src/utils/help.js +2 -0
package/README.md
CHANGED
|
@@ -4,12 +4,14 @@ Boreal UI is a customizable, accessible React and Next.js component library with
|
|
|
4
4
|
|
|
5
5
|
Use it when you want production-ready UI primitives that can be themed globally, customized per component, tested predictably, and imported in either standard React apps or Next.js app-router projects.
|
|
6
6
|
|
|
7
|
+
[View the Boreal UI docs](https://www.borealui.ca)
|
|
8
|
+
|
|
7
9
|
## Highlights
|
|
8
10
|
|
|
9
11
|
- **React and Next.js builds:** import from `boreal-ui/core` for React apps or `boreal-ui/next` for Next.js apps.
|
|
10
12
|
- **Deep component set:** buttons, forms, navigation, data display, feedback, overlays, layout primitives, and utility components.
|
|
11
13
|
- **Theme system:** curated color schemes, custom schemes, runtime theme selection, CSS variables, and `ThemeSelect`.
|
|
12
|
-
- **Global defaults:** configure default theme, size, rounding, shadow, border width, glass, outline, and color scheme once with `borealConfig` or `setBorealStyleConfig`.
|
|
14
|
+
- **Global defaults:** configure default theme, size, rounding, shadow, border width, glass, outline, and color scheme once with `borealConfig` or `setBorealStyleConfig`.
|
|
13
15
|
- **Accessible by default:** semantic markup, ARIA support, keyboard behavior, visible focus states, disabled states, live announcements where useful, and predictable test IDs.
|
|
14
16
|
- **Styling flexibility:** theme, state, size, rounding, shadow, outline, glass, custom class names, SCSS variables, and consumer CSS overrides.
|
|
15
17
|
- **Typed public API:** TypeScript component props, shared type exports, and generated prop documentation objects for docs tooling.
|
|
@@ -17,30 +19,31 @@ Use it when you want production-ready UI primitives that can be themed globally,
|
|
|
17
19
|
|
|
18
20
|
## Installation
|
|
19
21
|
|
|
20
|
-
```bash
|
|
21
|
-
npm install boreal-ui
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Boreal UI expects React and React DOM in the consuming app. Next.js users should also have Next installed. `marked` and `uuid` are peer dependencies because some components and utilities rely on them.
|
|
25
|
-
|
|
26
|
-
## CLI Setup
|
|
27
|
-
|
|
28
|
-
Use the CLI inside an existing React or Next.js project to add only the file changes Boreal UI needs: the package dependency, the global stylesheet import, `ThemeProvider`, and default style config.
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npx boreal-ui@latest init
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
You can preview changes or run non-interactively:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npx boreal-ui init --dry-run
|
|
38
|
-
npx boreal-ui init --framework next --yes
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
22
|
+
```bash
|
|
23
|
+
npm install boreal-ui
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Boreal UI expects React and React DOM in the consuming app. Next.js users should also have Next installed. `marked` and `uuid` are peer dependencies because some components and utilities rely on them.
|
|
27
|
+
|
|
28
|
+
## CLI Setup
|
|
29
|
+
|
|
30
|
+
Use the CLI inside an existing React or Next.js project to add only the file changes Boreal UI needs: the package dependency, the global stylesheet import, `ThemeProvider`, and default style config.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx boreal-ui@latest init
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
You can preview changes or run non-interactively:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx boreal-ui init --dry-run
|
|
40
|
+
npx boreal-ui init --framework next --yes
|
|
41
|
+
npx boreal-ui init --framework next --recommended-globals
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Setup
|
|
45
|
+
|
|
46
|
+
Import the global stylesheet once near the top of your application.
|
|
44
47
|
|
|
45
48
|
### React
|
|
46
49
|
|
|
@@ -73,7 +76,43 @@ Import the Next stylesheet once from `app/layout.tsx`, `pages/_app.tsx`, or your
|
|
|
73
76
|
import "boreal-ui/next/globals.css";
|
|
74
77
|
```
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
If your Next.js app still has the starter `globals.css` reset below, avoid loading it after Boreal styles:
|
|
80
|
+
|
|
81
|
+
```css
|
|
82
|
+
* {
|
|
83
|
+
box-sizing: border-box;
|
|
84
|
+
padding: 0;
|
|
85
|
+
margin: 0;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The universal `padding` and `margin` reset can override spacing that Boreal components and nested content rely on. Prefer a narrower baseline:
|
|
90
|
+
|
|
91
|
+
```css
|
|
92
|
+
html {
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
*,
|
|
97
|
+
*::before,
|
|
98
|
+
*::after {
|
|
99
|
+
box-sizing: inherit;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
body {
|
|
103
|
+
margin: 0;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The CLI can create or repair that safer baseline for Next.js apps:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npx boreal-ui init --framework next --recommended-globals
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Interactive Next.js setup prompts for this by default. Use `--recommended-globals` to apply it without the prompt, or `--no-recommended-globals` to skip it.
|
|
114
|
+
|
|
115
|
+
Then import components from the Next build:
|
|
77
116
|
|
|
78
117
|
```tsx
|
|
79
118
|
"use client";
|
|
@@ -101,7 +140,7 @@ import Card from "boreal-ui/next/Card";
|
|
|
101
140
|
|
|
102
141
|
## Components
|
|
103
142
|
|
|
104
|
-
For deeper consumer API examples, see the [Boreal UI consumer API guides](./docs/README.md). They cover import paths, styling and theming, common component patterns, generated prop docs, public TypeScript types, and contributor workflow.
|
|
143
|
+
For deeper consumer API examples, see the [Boreal UI consumer API guides](./docs/README.md). They cover import paths, styling and theming, common component patterns, generated prop docs, public TypeScript types, and contributor workflow.
|
|
105
144
|
|
|
106
145
|
### Actions
|
|
107
146
|
|
|
@@ -113,7 +152,7 @@ For deeper consumer API examples, see the [Boreal UI consumer API guides](./docs
|
|
|
113
152
|
|
|
114
153
|
- `TextInput` and `TextArea` support labels, helper/error text, validation state, disabled state, sizing, theming, and accessible descriptions.
|
|
115
154
|
- `Select` and `ThemeSelect` cover option selection and color-scheme switching.
|
|
116
|
-
- `Checkbox`, `RadioButton`, `RadioGroup`, `Toggle`, and `Slider` provide common controlled input patterns.
|
|
155
|
+
- `Checkbox`, `RadioButton`, `RadioGroup`, `Toggle`, and `Slider` provide common controlled input patterns.
|
|
117
156
|
- `ColorPicker` supports color selection flows.
|
|
118
157
|
- `DateTimePicker` handles date and time input.
|
|
119
158
|
- `FileUpload` supports file selection UI.
|
|
@@ -161,28 +200,28 @@ Exact props vary by component. TypeScript and the generated prop docs are the so
|
|
|
161
200
|
|
|
162
201
|
## Global Style Defaults
|
|
163
202
|
|
|
164
|
-
Call `borealConfig` once before rendering your app to set project-wide defaults. `setBorealStyleConfig` remains available as the explicit API name.
|
|
165
|
-
|
|
166
|
-
```tsx
|
|
167
|
-
import { borealConfig } from "boreal-ui/core";
|
|
168
|
-
|
|
169
|
-
borealConfig({
|
|
170
|
-
defaultTheme: "secondary",
|
|
171
|
-
defaultSize: "medium",
|
|
172
|
-
defaultRounding: "medium",
|
|
173
|
-
defaultShadow: "light",
|
|
174
|
-
defaultBorderWidth: "none",
|
|
175
|
-
defaultGlass: false,
|
|
176
|
-
defaultOutline: false,
|
|
177
|
-
defaultColorSchemeName: "Forest Dusk",
|
|
178
|
-
});
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
For Next.js, import the same API from `boreal-ui/next`:
|
|
182
|
-
|
|
183
|
-
```tsx
|
|
184
|
-
import { borealConfig } from "boreal-ui/next";
|
|
185
|
-
```
|
|
203
|
+
Call `borealConfig` once before rendering your app to set project-wide defaults. `setBorealStyleConfig` remains available as the explicit API name.
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { borealConfig } from "boreal-ui/core";
|
|
207
|
+
|
|
208
|
+
borealConfig({
|
|
209
|
+
defaultTheme: "secondary",
|
|
210
|
+
defaultSize: "medium",
|
|
211
|
+
defaultRounding: "medium",
|
|
212
|
+
defaultShadow: "light",
|
|
213
|
+
defaultBorderWidth: "none",
|
|
214
|
+
defaultGlass: false,
|
|
215
|
+
defaultOutline: false,
|
|
216
|
+
defaultColorSchemeName: "Forest Dusk",
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
For Next.js, import the same API from `boreal-ui/next`:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
import { borealConfig } from "boreal-ui/next";
|
|
224
|
+
```
|
|
186
225
|
|
|
187
226
|
Component props still win over global defaults:
|
|
188
227
|
|
|
@@ -269,20 +308,20 @@ registerColorScheme({
|
|
|
269
308
|
console.log(defaultColorSchemes.map((scheme) => scheme.name));
|
|
270
309
|
```
|
|
271
310
|
|
|
272
|
-
## Generated Prop Docs
|
|
273
|
-
|
|
274
|
-
Boreal UI exports generated prop metadata for documentation sites, playgrounds, or prop tables.
|
|
311
|
+
## Generated Prop Docs
|
|
312
|
+
|
|
313
|
+
Boreal UI exports generated prop metadata for documentation sites, playgrounds, or prop tables.
|
|
275
314
|
|
|
276
315
|
```tsx
|
|
277
316
|
import { buttonPropDocs, dataTablePropDocs } from "boreal-ui/core";
|
|
278
317
|
|
|
279
318
|
console.log(buttonPropDocs.name);
|
|
280
|
-
console.log(dataTablePropDocs.props);
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
The docs export includes `GeneratedComponentDoc` and `GeneratedPropDoc` types, plus one prop-doc object per documented component. Prop docs include `defaultValue` when the component implementation sets a readable default.
|
|
284
|
-
|
|
285
|
-
For the complete generated prop-doc export list, see [Public API Reference](./docs/public-api-reference.md).
|
|
319
|
+
console.log(dataTablePropDocs.props);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The docs export includes `GeneratedComponentDoc` and `GeneratedPropDoc` types, plus one prop-doc object per documented component. Prop docs include `defaultValue` when the component implementation sets a readable default.
|
|
323
|
+
|
|
324
|
+
For the complete generated prop-doc export list, see [Public API Reference](./docs/public-api-reference.md).
|
|
286
325
|
|
|
287
326
|
## Type Exports
|
|
288
327
|
|
|
@@ -372,9 +411,9 @@ Useful scripts:
|
|
|
372
411
|
| `npm run audit` | Run type, lint, style, test, build, and package checks. |
|
|
373
412
|
| `npm run generate:docs` | Regenerate component prop docs. |
|
|
374
413
|
| `npm run generate:entrypoints` | Regenerate component entry points. |
|
|
375
|
-
| `npm run generate:exports` | Regenerate package exports. |
|
|
376
|
-
|
|
377
|
-
Contributor documentation for component structure, generated docs, package output, and release checks lives in [Development Workflow](./docs/development-workflow.md).
|
|
414
|
+
| `npm run generate:exports` | Regenerate package exports. |
|
|
415
|
+
|
|
416
|
+
Contributor documentation for component structure, generated docs, package output, and release checks lives in [Development Workflow](./docs/development-workflow.md).
|
|
378
417
|
|
|
379
418
|
## Package Entry Points
|
|
380
419
|
|
package/README.npm.md
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
# Boreal UI
|
|
2
|
+
|
|
3
|
+
Boreal UI is a customizable, accessible React and Next.js component library with SCSS-powered theming, TypeScript types, generated prop metadata, and parallel `core` and `next` package outputs.
|
|
4
|
+
|
|
5
|
+
Use it when you want production-ready UI primitives that can be themed globally, customized per component, tested predictably, and imported in either standard React apps or Next.js app-router projects.
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- **React and Next.js builds:** import from `boreal-ui/core` for React apps or `boreal-ui/next` for Next.js apps.
|
|
10
|
+
- **Deep component set:** buttons, forms, navigation, data display, feedback, overlays, layout primitives, and utility components.
|
|
11
|
+
- **Theme system:** curated color schemes, custom schemes, runtime theme selection, CSS variables, and `ThemeSelect`.
|
|
12
|
+
- **Global defaults:** configure default theme, size, rounding, shadow, border width, glass, outline, and color scheme once with `borealConfig`.
|
|
13
|
+
- **Accessible by default:** semantic markup, ARIA support, keyboard behavior, visible focus states, disabled states, live announcements where useful, and predictable test IDs.
|
|
14
|
+
- **Styling flexibility:** theme, state, size, rounding, shadow, outline, glass, custom class names, SCSS variables, and consumer CSS overrides.
|
|
15
|
+
- **Typed public API:** TypeScript component props, shared type exports, and generated prop documentation objects for docs tooling.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install boreal-ui
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Boreal UI expects React and React DOM in the consuming app. Next.js users should also have Next installed.
|
|
24
|
+
|
|
25
|
+
Some components and utilities rely on `marked` and `uuid`, so make sure they are available if your package manager does not install peer dependencies automatically.
|
|
26
|
+
|
|
27
|
+
## CLI Setup
|
|
28
|
+
|
|
29
|
+
Use the CLI inside an existing React or Next.js project to add the package dependency, global stylesheet import, `ThemeProvider`, and default style config.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx boreal-ui@latest init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Preview changes before writing files:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx boreal-ui init --dry-run
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Run non-interactively:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx boreal-ui init --framework next --yes
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For Next.js projects, you can also apply Boreal’s recommended global CSS baseline:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx boreal-ui init --framework next --recommended-globals
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Setup
|
|
54
|
+
|
|
55
|
+
Import the global stylesheet once near the top of your application.
|
|
56
|
+
|
|
57
|
+
## React
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import "boreal-ui/core/globals.css";
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then import components from the core build:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { Button, Card, TextInput } from "boreal-ui/core";
|
|
67
|
+
|
|
68
|
+
export function Example() {
|
|
69
|
+
return (
|
|
70
|
+
<Card title="Welcome" theme="primary" shadow="medium">
|
|
71
|
+
<TextInput label="Project name" placeholder="Aurora dashboard" />
|
|
72
|
+
<Button theme="secondary" size="large">
|
|
73
|
+
Continue
|
|
74
|
+
</Button>
|
|
75
|
+
</Card>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Next.js
|
|
81
|
+
|
|
82
|
+
Import the Next stylesheet once from `app/layout.tsx`, `pages/_app.tsx`, or your global stylesheet.
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import "boreal-ui/next/globals.css";
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then import components from the Next build:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
"use client";
|
|
92
|
+
|
|
93
|
+
import { Button, Card, TextInput } from "boreal-ui/next";
|
|
94
|
+
|
|
95
|
+
export default function Example() {
|
|
96
|
+
return (
|
|
97
|
+
<Card title="Welcome" theme="primary" shadow="medium">
|
|
98
|
+
<TextInput label="Project name" placeholder="Aurora dashboard" />
|
|
99
|
+
<Button theme="secondary" size="large">
|
|
100
|
+
Continue
|
|
101
|
+
</Button>
|
|
102
|
+
</Card>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Next.js Global CSS Note
|
|
108
|
+
|
|
109
|
+
If your Next.js app still has the default starter reset below, avoid loading it after Boreal styles:
|
|
110
|
+
|
|
111
|
+
```css
|
|
112
|
+
* {
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
padding: 0;
|
|
115
|
+
margin: 0;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The universal `padding` and `margin` reset can override spacing that Boreal components and nested content rely on.
|
|
120
|
+
|
|
121
|
+
Prefer this safer baseline:
|
|
122
|
+
|
|
123
|
+
```css
|
|
124
|
+
html {
|
|
125
|
+
box-sizing: border-box;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
*,
|
|
129
|
+
*::before,
|
|
130
|
+
*::after {
|
|
131
|
+
box-sizing: inherit;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
body {
|
|
135
|
+
margin: 0;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Standalone Component Imports
|
|
140
|
+
|
|
141
|
+
You can import individual components directly:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import Button from "boreal-ui/core/Button";
|
|
145
|
+
import Card from "boreal-ui/next/Card";
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Package Entry Points
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { Button } from "boreal-ui/core";
|
|
152
|
+
import Button from "boreal-ui/core/Button";
|
|
153
|
+
import "boreal-ui/core/globals.css";
|
|
154
|
+
|
|
155
|
+
import { Button as NextButton } from "boreal-ui/next";
|
|
156
|
+
import NextCard from "boreal-ui/next/Card";
|
|
157
|
+
import "boreal-ui/next/globals.css";
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The root `boreal-ui` entry currently points to the core build. For Next.js apps, prefer `boreal-ui/next` so the Next wrappers and client directives are used.
|
|
161
|
+
|
|
162
|
+
## Components
|
|
163
|
+
|
|
164
|
+
### Actions
|
|
165
|
+
|
|
166
|
+
- `Button`
|
|
167
|
+
- `IconButton`
|
|
168
|
+
- `ScrollToTop`
|
|
169
|
+
|
|
170
|
+
### Forms and Inputs
|
|
171
|
+
|
|
172
|
+
- `TextInput`
|
|
173
|
+
- `TextArea`
|
|
174
|
+
- `Select`
|
|
175
|
+
- `ThemeSelect`
|
|
176
|
+
- `Checkbox`
|
|
177
|
+
- `RadioButton`
|
|
178
|
+
- `RadioGroup`
|
|
179
|
+
- `Toggle`
|
|
180
|
+
- `Slider`
|
|
181
|
+
- `ColorPicker`
|
|
182
|
+
- `DateTimePicker`
|
|
183
|
+
- `FileUpload`
|
|
184
|
+
- `TagInput`
|
|
185
|
+
- `FormGroup`
|
|
186
|
+
|
|
187
|
+
### Data and Content
|
|
188
|
+
|
|
189
|
+
- `DataTable`
|
|
190
|
+
- `MarkdownRenderer`
|
|
191
|
+
- `Typography`
|
|
192
|
+
- `MetricBox`
|
|
193
|
+
|
|
194
|
+
### Feedback and Status
|
|
195
|
+
|
|
196
|
+
- `Badge`
|
|
197
|
+
- `Chip`
|
|
198
|
+
- `ChipGroup`
|
|
199
|
+
- `Progressbar`
|
|
200
|
+
- `CircularProgress`
|
|
201
|
+
- `Spinner`
|
|
202
|
+
- `Skeleton`
|
|
203
|
+
- `Rating`
|
|
204
|
+
- `Tooltip`
|
|
205
|
+
- `MessagePopup`
|
|
206
|
+
- `PopOver`
|
|
207
|
+
- `Modal`
|
|
208
|
+
- `NotificationCenter`
|
|
209
|
+
- `EmptyState`
|
|
210
|
+
|
|
211
|
+
### Navigation and Layout
|
|
212
|
+
|
|
213
|
+
- `Navbar`
|
|
214
|
+
- `Sidebar`
|
|
215
|
+
- `Footer`
|
|
216
|
+
- `Breadcrumbs`
|
|
217
|
+
- `Tabs`
|
|
218
|
+
- `Stepper`
|
|
219
|
+
- `Timeline`
|
|
220
|
+
- `Accordion`
|
|
221
|
+
- `Pager`
|
|
222
|
+
- `Toolbar`
|
|
223
|
+
- `Dropdown`
|
|
224
|
+
- `Divider`
|
|
225
|
+
- `Card`
|
|
226
|
+
- `Avatar`
|
|
227
|
+
|
|
228
|
+
## Common Styling Props
|
|
229
|
+
|
|
230
|
+
Many components share the same styling vocabulary:
|
|
231
|
+
|
|
232
|
+
| Prop | Values |
|
|
233
|
+
| ------------- | --------------------------------------------------------- |
|
|
234
|
+
| `theme` | `primary`, `secondary`, `tertiary`, `quaternary`, `clear` |
|
|
235
|
+
| `state` | `success`, `error`, `warning`, `disabled`, empty string |
|
|
236
|
+
| `size` | `xs`, `small`, `medium`, `large`, `xl` |
|
|
237
|
+
| `rounding` | `none`, `small`, `medium`, `large`, `full` |
|
|
238
|
+
| `shadow` | `none`, `light`, `medium`, `strong`, `intense` |
|
|
239
|
+
| `borderWidth` | `none`, `xs`, `small`, `medium`, `large`, `xl` |
|
|
240
|
+
| `outline` | outline treatment where supported |
|
|
241
|
+
| `glass` | translucent theme-aware surface where supported |
|
|
242
|
+
| `className` | consumer styling hook |
|
|
243
|
+
| `data-testid` | stable test selector |
|
|
244
|
+
|
|
245
|
+
Exact props vary by component. TypeScript and the generated prop docs are the source of truth for each component.
|
|
246
|
+
|
|
247
|
+
## Global Style Defaults
|
|
248
|
+
|
|
249
|
+
Call `borealConfig` once before rendering your app to set project-wide defaults.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
import { borealConfig } from "boreal-ui/core";
|
|
253
|
+
|
|
254
|
+
borealConfig({
|
|
255
|
+
defaultTheme: "secondary",
|
|
256
|
+
defaultSize: "medium",
|
|
257
|
+
defaultRounding: "medium",
|
|
258
|
+
defaultShadow: "light",
|
|
259
|
+
defaultBorderWidth: "none",
|
|
260
|
+
defaultGlass: false,
|
|
261
|
+
defaultOutline: false,
|
|
262
|
+
defaultColorSchemeName: "Forest Dusk",
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
For Next.js, import the same API from `boreal-ui/next`:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { borealConfig } from "boreal-ui/next";
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Component props still win over global defaults:
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
<Button theme="primary" size="large" shadow="strong">
|
|
276
|
+
Save changes
|
|
277
|
+
</Button>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Theme Provider and Color Schemes
|
|
281
|
+
|
|
282
|
+
`ThemeProvider` manages the active color scheme and writes the scheme into CSS variables used by Boreal UI components.
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
"use client";
|
|
286
|
+
|
|
287
|
+
import { ThemeProvider } from "boreal-ui/next";
|
|
288
|
+
|
|
289
|
+
const customSchemes = [
|
|
290
|
+
{
|
|
291
|
+
name: "Cyberpunk Pulse",
|
|
292
|
+
primaryColor: "#ff006e",
|
|
293
|
+
secondaryColor: "#8338ec",
|
|
294
|
+
tertiaryColor: "#3a0ca3",
|
|
295
|
+
quaternaryColor: "#fb5607",
|
|
296
|
+
backgroundColor: "#0f0f0f",
|
|
297
|
+
forceTextColor: "#ffffff",
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
302
|
+
return (
|
|
303
|
+
<ThemeProvider
|
|
304
|
+
customSchemes={customSchemes}
|
|
305
|
+
initialSchemeName="Cyberpunk Pulse"
|
|
306
|
+
>
|
|
307
|
+
{children}
|
|
308
|
+
</ThemeProvider>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### ThemeProvider Props
|
|
314
|
+
|
|
315
|
+
| Prop | Description |
|
|
316
|
+
| ---------------------- | ----------------------------------------------------- |
|
|
317
|
+
| `customSchemes` | Register additional color schemes at runtime. |
|
|
318
|
+
| `initialSchemeName` | Select an initial scheme by name. |
|
|
319
|
+
| `useOnlyCustomSchemes` | Use only custom schemes instead of the built-in list. |
|
|
320
|
+
|
|
321
|
+
### Color Scheme Shape
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
type ColorScheme = {
|
|
325
|
+
name: string;
|
|
326
|
+
primaryColor: string;
|
|
327
|
+
secondaryColor: string;
|
|
328
|
+
tertiaryColor: string;
|
|
329
|
+
quaternaryColor: string;
|
|
330
|
+
backgroundColor: string;
|
|
331
|
+
forceTextColor?: string;
|
|
332
|
+
};
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
You can also register schemes manually:
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
import {
|
|
339
|
+
defaultColorSchemes,
|
|
340
|
+
registerColorScheme,
|
|
341
|
+
ThemeSelect,
|
|
342
|
+
} from "boreal-ui/core";
|
|
343
|
+
|
|
344
|
+
registerColorScheme({
|
|
345
|
+
name: "Brand Night",
|
|
346
|
+
primaryColor: "#4f46e5",
|
|
347
|
+
secondaryColor: "#06b6d4",
|
|
348
|
+
tertiaryColor: "#a855f7",
|
|
349
|
+
quaternaryColor: "#22c55e",
|
|
350
|
+
backgroundColor: "#0f172a",
|
|
351
|
+
forceTextColor: "#ffffff",
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
console.log(defaultColorSchemes.map((scheme) => scheme.name));
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Type Exports
|
|
358
|
+
|
|
359
|
+
Shared public types are exported from both builds:
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
import type {
|
|
363
|
+
BorderType,
|
|
364
|
+
ColorScheme,
|
|
365
|
+
RoundingType,
|
|
366
|
+
ShadowType,
|
|
367
|
+
SizeType,
|
|
368
|
+
ThemeType,
|
|
369
|
+
} from "boreal-ui/core";
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Standalone type entry points are also available:
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
import type { ThemeType } from "boreal-ui/core/types";
|
|
376
|
+
import type { SizeType } from "boreal-ui/next/types";
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Generated Prop Docs
|
|
380
|
+
|
|
381
|
+
Boreal UI exports generated prop metadata for documentation sites, playgrounds, and prop tables.
|
|
382
|
+
|
|
383
|
+
```tsx
|
|
384
|
+
import { buttonPropDocs, dataTablePropDocs } from "boreal-ui/core";
|
|
385
|
+
|
|
386
|
+
console.log(buttonPropDocs.name);
|
|
387
|
+
console.log(dataTablePropDocs.props);
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
The docs export includes `GeneratedComponentDoc` and `GeneratedPropDoc` types, plus one prop-doc object per documented component.
|
|
391
|
+
|
|
392
|
+
## CSS Customization
|
|
393
|
+
|
|
394
|
+
Boreal UI styles are built on CSS variables and SCSS. You can override variables globally or scope them to a subtree:
|
|
395
|
+
|
|
396
|
+
```css
|
|
397
|
+
:root {
|
|
398
|
+
--font-family-ui: Inter, system-ui, sans-serif;
|
|
399
|
+
--border-radius-md: 0.5rem;
|
|
400
|
+
--transition-default: 160ms ease;
|
|
401
|
+
--focus-outline-color: #2563eb;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.admin-shell {
|
|
405
|
+
--background-color: #0f172a;
|
|
406
|
+
--text-color: #f8fafc;
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Most components also accept `className`, and larger components expose section-level class props such as:
|
|
411
|
+
|
|
412
|
+
```tsx
|
|
413
|
+
<Card
|
|
414
|
+
title="Custom Card"
|
|
415
|
+
className="dashboard-card"
|
|
416
|
+
headerClassName="dashboard-card__header"
|
|
417
|
+
contentClassName="dashboard-card__content"
|
|
418
|
+
footerClassName="dashboard-card__footer"
|
|
419
|
+
/>
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Accessibility and Testing
|
|
423
|
+
|
|
424
|
+
Boreal UI is designed for Testing Library, Jest, jest-axe, Cypress, and Storybook workflows.
|
|
425
|
+
|
|
426
|
+
- Prefer roles and accessible names in tests.
|
|
427
|
+
- Use `data-testid` when a stable selector is needed.
|
|
428
|
+
- Icon-only controls should receive an accessible label.
|
|
429
|
+
- Helper and error text are connected with ARIA where components support those states.
|
|
430
|
+
- Interactive components are built with keyboard behavior and visible focus states in mind.
|
|
431
|
+
|
|
432
|
+
```tsx
|
|
433
|
+
import { render, screen } from "@testing-library/react";
|
|
434
|
+
import { Button } from "boreal-ui/core";
|
|
435
|
+
|
|
436
|
+
it("renders an accessible button", () => {
|
|
437
|
+
render(<Button>Submit</Button>);
|
|
438
|
+
expect(screen.getByRole("button", { name: /submit/i })).toBeInTheDocument();
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Documentation
|
|
443
|
+
|
|
444
|
+
For full documentation, examples, and API guides, [View the Boreal UI docs](https://www.borealui.ca)
|
|
445
|
+
|
|
446
|
+
## License
|
|
447
|
+
|
|
448
|
+
MIT © Davin Chiupka
|
|
@@ -46,16 +46,54 @@ export function ProjectForm() {
|
|
|
46
46
|
|
|
47
47
|
## Next.js Setup
|
|
48
48
|
|
|
49
|
-
Import the Next stylesheet once from `app/layout.tsx`, `pages/_app.tsx`, or another global stylesheet loaded by the app.
|
|
50
|
-
|
|
51
|
-
```tsx
|
|
52
|
-
import "boreal-ui/next/globals.css";
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
|
|
49
|
+
Import the Next stylesheet once from `app/layout.tsx`, `pages/_app.tsx`, or another global stylesheet loaded by the app.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import "boreal-ui/next/globals.css";
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Next.js starter projects often include this broad reset in the app's default `globals.css`:
|
|
56
|
+
|
|
57
|
+
```css
|
|
58
|
+
* {
|
|
59
|
+
box-sizing: border-box;
|
|
60
|
+
padding: 0;
|
|
61
|
+
margin: 0;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Avoid loading that reset after Boreal styles. The universal `padding` and `margin` rules can override spacing that Boreal components and nested content rely on. Prefer a narrower global override that keeps box sizing predictable without removing spacing from every element:
|
|
66
|
+
|
|
67
|
+
```css
|
|
68
|
+
html {
|
|
69
|
+
box-sizing: border-box;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
*,
|
|
73
|
+
*::before,
|
|
74
|
+
*::after {
|
|
75
|
+
box-sizing: inherit;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
body {
|
|
79
|
+
margin: 0;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If your app needs additional layout resets, scope them to your own shell classes instead of applying them to every element globally.
|
|
84
|
+
|
|
85
|
+
The CLI can create or repair that safer baseline for Next.js apps:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx boreal-ui init --framework next --recommended-globals
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Interactive Next.js setup prompts for this by default. Use `--recommended-globals` to apply it without the prompt, or `--no-recommended-globals` to skip it.
|
|
92
|
+
|
|
93
|
+
Use the Next build for components.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
"use client";
|
|
59
97
|
|
|
60
98
|
import { Button, Card, TextInput } from "boreal-ui/next";
|
|
61
99
|
|
|
@@ -10,15 +10,53 @@ Import the global stylesheet once.
|
|
|
10
10
|
import "boreal-ui/core/globals.css";
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
For Next.js:
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
import "boreal-ui/next/globals.css";
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
The global stylesheet provides CSS variables, resets, theme values, animations, and shared utility styles used by components.
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
For Next.js:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import "boreal-ui/next/globals.css";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The global stylesheet provides CSS variables, resets, theme values, animations, and shared utility styles used by components.
|
|
20
|
+
|
|
21
|
+
Be careful with the default `globals.css` created by many Next.js starters:
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
* {
|
|
25
|
+
box-sizing: border-box;
|
|
26
|
+
padding: 0;
|
|
27
|
+
margin: 0;
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
When that reset is loaded after Boreal, the universal `padding` and `margin` declarations can override spacing used by Boreal components and nested content. A safer app-level baseline is:
|
|
32
|
+
|
|
33
|
+
```css
|
|
34
|
+
html {
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
*,
|
|
39
|
+
*::before,
|
|
40
|
+
*::after {
|
|
41
|
+
box-sizing: inherit;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
body {
|
|
45
|
+
margin: 0;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Keep broader spacing rules scoped to your app shell, page layouts, or utility classes so they do not erase component-level padding and margins.
|
|
50
|
+
|
|
51
|
+
The CLI can create or repair that safer baseline for Next.js apps:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx boreal-ui init --framework next --recommended-globals
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Interactive Next.js setup prompts for this by default. Use `--recommended-globals` to apply it without the prompt, or `--no-recommended-globals` to skip it.
|
|
58
|
+
|
|
59
|
+
## Shared Style Props
|
|
22
60
|
|
|
23
61
|
Many components support a common styling vocabulary.
|
|
24
62
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-undef */
|
|
2
2
|
import {
|
|
3
3
|
existsSync,
|
|
4
|
+
mkdirSync,
|
|
4
5
|
readFileSync,
|
|
5
6
|
statSync,
|
|
6
7
|
writeFileSync,
|
|
@@ -23,6 +24,20 @@ const BOREAL_CONFIG_CALL = `setBorealStyleConfig({
|
|
|
23
24
|
defaultColorSchemeName: "Forest Dusk",
|
|
24
25
|
});
|
|
25
26
|
`;
|
|
27
|
+
const NEXT_RECOMMENDED_GLOBALS = `html {
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
*,
|
|
32
|
+
*::before,
|
|
33
|
+
*::after {
|
|
34
|
+
box-sizing: inherit;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
body {
|
|
38
|
+
margin: 0;
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
26
41
|
|
|
27
42
|
export async function initCommand(rawOptions) {
|
|
28
43
|
const options = await promptForOptions(rawOptions);
|
|
@@ -41,7 +56,8 @@ export async function initCommand(rawOptions) {
|
|
|
41
56
|
const packageJson = readPackageJson(packageJsonPath);
|
|
42
57
|
const framework = resolveFramework(root, packageJson, options.framework);
|
|
43
58
|
const packageManager = resolvePackageManager(root, options.packageManager);
|
|
44
|
-
|
|
59
|
+
options.recommendedGlobals = await resolveRecommendedGlobalsOption(options, framework);
|
|
60
|
+
const plan = createSetupPlan(root, packageJsonPath, packageJson, framework, options);
|
|
45
61
|
|
|
46
62
|
if (plan.length === 0) {
|
|
47
63
|
console.log("Boreal UI already looks configured for this project.");
|
|
@@ -60,6 +76,7 @@ export async function initCommand(rawOptions) {
|
|
|
60
76
|
try {
|
|
61
77
|
for (const change of plan) {
|
|
62
78
|
if (await shouldApplyChange(rl, change, options.yes)) {
|
|
79
|
+
mkdirSync(dirname(change.path), { recursive: true });
|
|
63
80
|
writeFileSync(change.path, change.nextContents, "utf8");
|
|
64
81
|
appliedCount += 1;
|
|
65
82
|
console.log(`Updated ${relative(root, change.path) || basename(change.path)}`);
|
|
@@ -114,13 +131,32 @@ function resolvePackageManager(root, requestedPackageManager) {
|
|
|
114
131
|
return "npm";
|
|
115
132
|
}
|
|
116
133
|
|
|
117
|
-
function
|
|
134
|
+
async function resolveRecommendedGlobalsOption(options, framework) {
|
|
135
|
+
if (framework !== "next") return false;
|
|
136
|
+
if (typeof options.recommendedGlobals === "boolean") return options.recommendedGlobals;
|
|
137
|
+
if (options.yes) return true;
|
|
138
|
+
if (options.dryRun) return false;
|
|
139
|
+
|
|
140
|
+
const rl = createInterface({ input, output });
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
return await promptBoolean(
|
|
144
|
+
rl,
|
|
145
|
+
"Create or repair a Boreal-safe Next globals.css baseline?",
|
|
146
|
+
true,
|
|
147
|
+
);
|
|
148
|
+
} finally {
|
|
149
|
+
rl.close();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function createSetupPlan(root, packageJsonPath, packageJson, framework, options) {
|
|
118
154
|
const changes = [];
|
|
119
155
|
|
|
120
156
|
addPackageJsonChange(changes, packageJsonPath, packageJson);
|
|
121
157
|
|
|
122
158
|
if (framework === "next") {
|
|
123
|
-
addNextChanges(changes, root);
|
|
159
|
+
addNextChanges(changes, root, options);
|
|
124
160
|
return changes;
|
|
125
161
|
}
|
|
126
162
|
|
|
@@ -179,11 +215,19 @@ function addReactChanges(changes, root) {
|
|
|
179
215
|
}
|
|
180
216
|
}
|
|
181
217
|
|
|
182
|
-
function addNextChanges(changes, root) {
|
|
218
|
+
function addNextChanges(changes, root, options) {
|
|
183
219
|
const pagesAppPath = findFirst(root, nextPagesAppCandidates());
|
|
184
220
|
|
|
185
221
|
if (pagesAppPath) {
|
|
186
|
-
|
|
222
|
+
const globalsPath = resolveNextGlobalsPath(root, pagesAppPath, "pages", options);
|
|
223
|
+
|
|
224
|
+
addNextPagesRouterChange(changes, pagesAppPath, globalsPath);
|
|
225
|
+
addNextRecommendedGlobalsChange(changes, root, {
|
|
226
|
+
enabled: options.recommendedGlobals,
|
|
227
|
+
globalsPath,
|
|
228
|
+
routerEntryPath: pagesAppPath,
|
|
229
|
+
routerType: "pages",
|
|
230
|
+
});
|
|
187
231
|
return;
|
|
188
232
|
}
|
|
189
233
|
|
|
@@ -197,16 +241,30 @@ function addNextChanges(changes, root) {
|
|
|
197
241
|
const resolvedProviderPath =
|
|
198
242
|
providerPath ?? join(dirname(layoutPath), `boreal-provider.${extensionFor(layoutPath)}`);
|
|
199
243
|
const providerImportPath = `./${basename(resolvedProviderPath).replace(/\.[^.]+$/, "")}`;
|
|
244
|
+
const globalsPath = resolveNextGlobalsPath(root, layoutPath, "app", options);
|
|
200
245
|
|
|
201
246
|
addNextProviderChange(changes, resolvedProviderPath);
|
|
202
|
-
addNextLayoutChange(changes, layoutPath, providerImportPath);
|
|
247
|
+
addNextLayoutChange(changes, layoutPath, providerImportPath, globalsPath);
|
|
248
|
+
addNextRecommendedGlobalsChange(changes, root, {
|
|
249
|
+
enabled: options.recommendedGlobals,
|
|
250
|
+
globalsPath,
|
|
251
|
+
routerEntryPath: layoutPath,
|
|
252
|
+
routerType: "app",
|
|
253
|
+
});
|
|
203
254
|
}
|
|
204
255
|
|
|
205
|
-
function addNextPagesRouterChange(changes, pagesAppPath) {
|
|
256
|
+
function addNextPagesRouterChange(changes, pagesAppPath, globalsPath) {
|
|
206
257
|
const source = readFileSync(pagesAppPath, "utf8");
|
|
207
258
|
let nextSource = source;
|
|
208
259
|
|
|
209
260
|
nextSource = ensureSideEffectImport(nextSource, "boreal-ui/next/globals.css");
|
|
261
|
+
if (globalsPath) {
|
|
262
|
+
nextSource = ensureSideEffectImportAfter(
|
|
263
|
+
nextSource,
|
|
264
|
+
toImportSpecifier(pagesAppPath, globalsPath),
|
|
265
|
+
"boreal-ui/next/globals.css",
|
|
266
|
+
);
|
|
267
|
+
}
|
|
210
268
|
nextSource = ensureNamedImport(nextSource, "boreal-ui/next", [
|
|
211
269
|
"ThemeProvider",
|
|
212
270
|
"setBorealStyleConfig",
|
|
@@ -224,13 +282,20 @@ function addNextPagesRouterChange(changes, pagesAppPath) {
|
|
|
224
282
|
}
|
|
225
283
|
}
|
|
226
284
|
|
|
227
|
-
function addNextLayoutChange(changes, layoutPath, providerImportPath) {
|
|
285
|
+
function addNextLayoutChange(changes, layoutPath, providerImportPath, globalsPath) {
|
|
228
286
|
const source = readFileSync(layoutPath, "utf8");
|
|
229
287
|
let nextSource = source;
|
|
230
288
|
const providerImportName =
|
|
231
289
|
getDefaultImportName(source, providerImportPath) ?? "BorealProvider";
|
|
232
290
|
|
|
233
291
|
nextSource = ensureSideEffectImport(nextSource, "boreal-ui/next/globals.css");
|
|
292
|
+
if (globalsPath) {
|
|
293
|
+
nextSource = ensureSideEffectImportAfter(
|
|
294
|
+
nextSource,
|
|
295
|
+
toImportSpecifier(layoutPath, globalsPath),
|
|
296
|
+
"boreal-ui/next/globals.css",
|
|
297
|
+
);
|
|
298
|
+
}
|
|
234
299
|
nextSource = ensureDefaultImport(nextSource, providerImportName, providerImportPath);
|
|
235
300
|
nextSource = ensureNextLayoutProvider(nextSource, providerImportName);
|
|
236
301
|
|
|
@@ -272,6 +337,49 @@ function addNextProviderChange(changes, providerPath) {
|
|
|
272
337
|
}
|
|
273
338
|
}
|
|
274
339
|
|
|
340
|
+
function addNextRecommendedGlobalsChange(
|
|
341
|
+
changes,
|
|
342
|
+
root,
|
|
343
|
+
{ enabled, globalsPath, routerEntryPath, routerType },
|
|
344
|
+
) {
|
|
345
|
+
if (!enabled) return;
|
|
346
|
+
|
|
347
|
+
const resolvedGlobalsPath =
|
|
348
|
+
globalsPath ?? defaultNextGlobalsPath(root, routerEntryPath, routerType);
|
|
349
|
+
const exists = existsSync(resolvedGlobalsPath);
|
|
350
|
+
const source = exists ? readFileSync(resolvedGlobalsPath, "utf8") : "";
|
|
351
|
+
const nextSource = ensureRecommendedNextGlobals(source);
|
|
352
|
+
|
|
353
|
+
if (nextSource !== source) {
|
|
354
|
+
changes.push({
|
|
355
|
+
path: resolvedGlobalsPath,
|
|
356
|
+
summary: exists
|
|
357
|
+
? "Replace broad Next.js global spacing resets with a Boreal-safe globals.css baseline."
|
|
358
|
+
: "Create a Boreal-safe Next.js globals.css baseline.",
|
|
359
|
+
nextContents: nextSource,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function resolveNextGlobalsPath(root, routerEntryPath, routerType, options) {
|
|
365
|
+
if (!options.recommendedGlobals) return undefined;
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
findFirst(root, nextGlobalsCandidates()) ??
|
|
369
|
+
defaultNextGlobalsPath(root, routerEntryPath, routerType)
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function toImportSpecifier(fromPath, targetPath) {
|
|
374
|
+
let specifier = relative(dirname(fromPath), targetPath).replace(/\\/g, "/");
|
|
375
|
+
|
|
376
|
+
if (!specifier.startsWith(".")) {
|
|
377
|
+
specifier = `./${specifier}`;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return specifier;
|
|
381
|
+
}
|
|
382
|
+
|
|
275
383
|
function ensureSideEffectImport(source, specifier) {
|
|
276
384
|
if (source.includes(`"${specifier}"`) || source.includes(`'${specifier}'`)) {
|
|
277
385
|
return source;
|
|
@@ -280,6 +388,22 @@ function ensureSideEffectImport(source, specifier) {
|
|
|
280
388
|
return insertAfterUseClient(source, `import "${specifier}";\n`);
|
|
281
389
|
}
|
|
282
390
|
|
|
391
|
+
function ensureSideEffectImportAfter(source, specifier, afterSpecifier) {
|
|
392
|
+
if (source.includes(`"${specifier}"`) || source.includes(`'${specifier}'`)) {
|
|
393
|
+
return source;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const afterImportRegex = new RegExp(
|
|
397
|
+
`(import\\s*["']${escapeRegExp(afterSpecifier)}["'];?\\s*)`,
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
if (afterImportRegex.test(source)) {
|
|
401
|
+
return source.replace(afterImportRegex, `$1import "${specifier}";\n`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return ensureSideEffectImport(source, specifier);
|
|
405
|
+
}
|
|
406
|
+
|
|
283
407
|
function ensureNamedImport(source, specifier, names) {
|
|
284
408
|
const importRegex = new RegExp(
|
|
285
409
|
`import\\s*\\{([^}]+)\\}\\s*from\\s*["']${escapeRegExp(specifier)}["'];?`,
|
|
@@ -420,6 +544,29 @@ function ensureUseClient(source) {
|
|
|
420
544
|
return `"use client";\n\n${source}`;
|
|
421
545
|
}
|
|
422
546
|
|
|
547
|
+
function ensureRecommendedNextGlobals(source) {
|
|
548
|
+
if (!source.trim()) return NEXT_RECOMMENDED_GLOBALS;
|
|
549
|
+
|
|
550
|
+
const starterResetRegex = /\*\s*\{(?=[^}]*box-sizing\s*:\s*border-box\s*;?)(?=[^}]*padding\s*:\s*0\s*;?)(?=[^}]*margin\s*:\s*0\s*;?)[^}]*\}/m;
|
|
551
|
+
const hasRecommendedBoxSizing =
|
|
552
|
+
/html\s*\{[^}]*box-sizing\s*:\s*border-box\s*;?[^}]*\}/m.test(source) &&
|
|
553
|
+
/\*\s*,\s*\*::before\s*,\s*\*::after\s*\{[^}]*box-sizing\s*:\s*inherit\s*;?[^}]*\}/m.test(source);
|
|
554
|
+
|
|
555
|
+
let nextSource = source;
|
|
556
|
+
|
|
557
|
+
if (starterResetRegex.test(nextSource)) {
|
|
558
|
+
nextSource = nextSource.replace(starterResetRegex, NEXT_RECOMMENDED_GLOBALS.trimEnd());
|
|
559
|
+
} else if (!hasRecommendedBoxSizing) {
|
|
560
|
+
nextSource = `${NEXT_RECOMMENDED_GLOBALS}\n${nextSource}`;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (!/body\s*\{[^}]*margin\s*:\s*0\s*;?[^}]*\}/m.test(nextSource)) {
|
|
564
|
+
nextSource = `${nextSource.trimEnd()}\n\nbody {\n margin: 0;\n}\n`;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return nextSource.endsWith("\n") ? nextSource : `${nextSource}\n`;
|
|
568
|
+
}
|
|
569
|
+
|
|
423
570
|
function insertAfterUseClient(source, text) {
|
|
424
571
|
const useClientRegex = /^(\s*["']use client["'];?\s*)/;
|
|
425
572
|
const match = source.match(useClientRegex);
|
|
@@ -501,6 +648,26 @@ function nextPagesAppCandidates() {
|
|
|
501
648
|
]);
|
|
502
649
|
}
|
|
503
650
|
|
|
651
|
+
function nextGlobalsCandidates() {
|
|
652
|
+
return [
|
|
653
|
+
"app/globals.css",
|
|
654
|
+
"src/app/globals.css",
|
|
655
|
+
"styles/globals.css",
|
|
656
|
+
"src/styles/globals.css",
|
|
657
|
+
];
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function defaultNextGlobalsPath(root, routerEntryPath, routerType) {
|
|
661
|
+
if (routerType === "app") {
|
|
662
|
+
return join(dirname(routerEntryPath), "globals.css");
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const pagesDirectory = dirname(routerEntryPath);
|
|
666
|
+
const sourceDirectory = basename(pagesDirectory) === "pages" ? dirname(pagesDirectory) : root;
|
|
667
|
+
|
|
668
|
+
return join(sourceDirectory, "styles", "globals.css");
|
|
669
|
+
}
|
|
670
|
+
|
|
504
671
|
function nextProviderCandidates(layoutDirectory) {
|
|
505
672
|
return SOURCE_EXTENSIONS.flatMap((extension) => [
|
|
506
673
|
join(layoutDirectory, `providers.${extension}`),
|
|
@@ -535,6 +702,17 @@ async function shouldApplyChange(rl, change, yes) {
|
|
|
535
702
|
return !answer || ["y", "yes", "true", "1"].includes(answer);
|
|
536
703
|
}
|
|
537
704
|
|
|
705
|
+
async function promptBoolean(rl, question, defaultValue) {
|
|
706
|
+
const suffix = defaultValue ? "Y/n" : "y/N";
|
|
707
|
+
const answer = (await rl.question(`${question} (${suffix}): `))
|
|
708
|
+
.trim()
|
|
709
|
+
.toLowerCase();
|
|
710
|
+
|
|
711
|
+
if (!answer) return defaultValue;
|
|
712
|
+
|
|
713
|
+
return ["y", "yes", "true", "1"].includes(answer);
|
|
714
|
+
}
|
|
715
|
+
|
|
538
716
|
function printPlan(root, framework, plan, dryRun) {
|
|
539
717
|
console.log(`Boreal UI setup for ${framework === "next" ? "Next.js" : "React"} at ${root}`);
|
|
540
718
|
console.log(dryRun ? "Planned changes:" : "Proposed changes:");
|
|
@@ -8,6 +8,7 @@ export async function parseArgs(argv) {
|
|
|
8
8
|
framework: undefined,
|
|
9
9
|
install: undefined,
|
|
10
10
|
packageManager: undefined,
|
|
11
|
+
recommendedGlobals: undefined,
|
|
11
12
|
dryRun: false,
|
|
12
13
|
yes: false,
|
|
13
14
|
};
|
|
@@ -66,10 +67,18 @@ export async function parseArgs(argv) {
|
|
|
66
67
|
case "--package-manager":
|
|
67
68
|
options.packageManager = rest.shift();
|
|
68
69
|
break;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
|
|
71
|
+
case "--recommended-globals":
|
|
72
|
+
options.recommendedGlobals = true;
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
case "--no-recommended-globals":
|
|
76
|
+
options.recommendedGlobals = false;
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
default:
|
|
80
|
+
if (arg?.startsWith("--")) {
|
|
81
|
+
fail(`Unknown option: ${arg}`);
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
if (!options.cwd) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export const VERSION = "0.0.
|
|
2
|
-
|
|
3
|
-
export const FRAMEWORKS = new Set(["react", "next"]);
|
|
4
|
-
|
|
1
|
+
export const VERSION = "0.0.889";
|
|
2
|
+
|
|
3
|
+
export const FRAMEWORKS = new Set(["react", "next"]);
|
|
4
|
+
|
|
5
5
|
export const PACKAGE_MANAGERS = new Set(["npm", "pnpm", "yarn"]);
|
|
6
6
|
|
|
7
7
|
export const DEFAULT_OPTIONS = {
|
|
@@ -15,6 +15,8 @@ Options:
|
|
|
15
15
|
--dry-run, --check Show planned edits without writing files
|
|
16
16
|
--install Run dependency install after edits
|
|
17
17
|
--package-manager <name> npm, pnpm, or yarn
|
|
18
|
+
--recommended-globals Add a Boreal-safe Next globals.css baseline without prompting
|
|
19
|
+
--no-recommended-globals Skip the recommended Next globals.css prompt/change
|
|
18
20
|
--no-install Skip dependency installation
|
|
19
21
|
--yes, -y Apply recommended edits without prompts
|
|
20
22
|
--help, -h Show this help message
|