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 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
- ## Setup
42
-
43
- Import the global stylesheet once near the top of your application.
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
- Then import components from the Next build:
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
- Use the Next build for components.
56
-
57
- ```tsx
58
- "use client";
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
- ## Shared Style Props
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,6 @@
1
1
  {
2
2
  "name": "boreal-ui",
3
- "version": "0.0.888",
3
+ "version": "0.0.889",
4
4
  "description": "A modern, customizable React/Next.js component library",
5
5
  "author": "Davin Chiupka",
6
6
  "license": "MIT",
@@ -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
- const plan = createSetupPlan(root, packageJsonPath, packageJson, framework);
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 createSetupPlan(root, packageJsonPath, packageJson, framework) {
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
- addNextPagesRouterChange(changes, pagesAppPath);
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
- default:
71
- if (arg?.startsWith("--")) {
72
- fail(`Unknown option: ${arg}`);
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.888";
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