neba 0.0.1 → 1.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/README.md +177 -2
- package/dist/components/accordion/Accordion.d.ts +97 -0
- package/dist/components/accordion/Accordion.js +1 -0
- package/dist/components/accordion/index.d.ts +2 -0
- package/dist/components/accordion/index.js +1 -0
- package/dist/components/alert/Alert.d.ts +47 -0
- package/dist/components/alert/Alert.js +1 -0
- package/dist/components/alert/index.d.ts +2 -0
- package/dist/components/alert/index.js +1 -0
- package/dist/components/badge/Badge.d.ts +80 -0
- package/dist/components/badge/Badge.js +1 -0
- package/dist/components/badge/index.d.ts +2 -0
- package/dist/components/badge/index.js +1 -0
- package/dist/components/blockquote/Blockquote.d.ts +65 -0
- package/dist/components/blockquote/Blockquote.js +1 -0
- package/dist/components/blockquote/index.d.ts +2 -0
- package/dist/components/blockquote/index.js +1 -0
- package/dist/components/box/Box.d.ts +50 -0
- package/dist/components/box/Box.js +1 -0
- package/dist/components/box/index.d.ts +2 -0
- package/dist/components/box/index.js +1 -0
- package/dist/components/button/Button.d.ts +25 -3
- package/dist/components/button/Button.js +1 -1
- package/dist/components/button/index.d.ts +1 -0
- package/dist/components/button-group/ButtonGroup.d.ts +31 -0
- package/dist/components/button-group/ButtonGroup.js +1 -0
- package/dist/components/button-group/index.d.ts +2 -0
- package/dist/components/button-group/index.js +1 -0
- package/dist/components/card/Card.d.ts +45 -0
- package/dist/components/card/Card.js +1 -0
- package/dist/components/card/index.d.ts +2 -0
- package/dist/components/card/index.js +1 -0
- package/dist/components/carousel/Carousel.d.ts +74 -0
- package/dist/components/carousel/Carousel.js +1 -0
- package/dist/components/carousel/index.d.ts +2 -0
- package/dist/components/carousel/index.js +1 -0
- package/dist/components/checkbox/Checkbox.d.ts +39 -0
- package/dist/components/checkbox/Checkbox.js +1 -0
- package/dist/components/checkbox/index.d.ts +2 -0
- package/dist/components/checkbox/index.js +1 -0
- package/dist/components/chip/Chip.d.ts +50 -0
- package/dist/components/chip/Chip.js +1 -0
- package/dist/components/chip/index.d.ts +2 -0
- package/dist/components/chip/index.js +1 -0
- package/dist/components/combobox/Combobox.d.ts +131 -0
- package/dist/components/combobox/Combobox.js +1 -0
- package/dist/components/combobox/index.d.ts +2 -0
- package/dist/components/combobox/index.js +1 -0
- package/dist/components/container/Container.d.ts +55 -0
- package/dist/components/container/Container.js +1 -0
- package/dist/components/container/index.d.ts +2 -0
- package/dist/components/container/index.js +1 -0
- package/dist/components/date-picker/DatePicker.d.ts +75 -0
- package/dist/components/date-picker/DatePicker.js +1 -0
- package/dist/components/date-picker/index.d.ts +2 -0
- package/dist/components/date-picker/index.js +1 -0
- package/dist/components/date-range-picker/DateRangePicker.d.ts +81 -0
- package/dist/components/date-range-picker/DateRangePicker.js +1 -0
- package/dist/components/date-range-picker/index.d.ts +2 -0
- package/dist/components/date-range-picker/index.js +1 -0
- package/dist/components/date-time-picker/DateTimePicker.d.ts +68 -0
- package/dist/components/date-time-picker/DateTimePicker.js +1 -0
- package/dist/components/date-time-picker/index.d.ts +2 -0
- package/dist/components/date-time-picker/index.js +1 -0
- package/dist/components/dialog/Dialog.d.ts +111 -0
- package/dist/components/dialog/Dialog.js +1 -0
- package/dist/components/dialog/index.d.ts +2 -0
- package/dist/components/dialog/index.js +1 -0
- package/dist/components/divider/Divider.d.ts +67 -0
- package/dist/components/divider/Divider.js +1 -0
- package/dist/components/divider/index.d.ts +2 -0
- package/dist/components/divider/index.js +1 -0
- package/dist/components/file-picker/FilePicker.d.ts +103 -0
- package/dist/components/file-picker/FilePicker.js +1 -0
- package/dist/components/file-picker/index.d.ts +2 -0
- package/dist/components/file-picker/index.js +1 -0
- package/dist/components/grid/Grid.d.ts +50 -0
- package/dist/components/grid/Grid.js +1 -0
- package/dist/components/grid/index.d.ts +2 -0
- package/dist/components/grid/index.js +1 -0
- package/dist/components/grid-container/GridContainer.d.ts +79 -0
- package/dist/components/grid-container/GridContainer.js +1 -0
- package/dist/components/grid-container/index.d.ts +2 -0
- package/dist/components/grid-container/index.js +1 -0
- package/dist/components/highlight/Highlight.d.ts +84 -0
- package/dist/components/highlight/Highlight.js +1 -0
- package/dist/components/highlight/index.d.ts +2 -0
- package/dist/components/highlight/index.js +1 -0
- package/dist/components/icon/Icon.d.ts +58 -0
- package/dist/components/icon/Icon.js +1 -0
- package/dist/components/icon/index.d.ts +2 -0
- package/dist/components/icon/index.js +1 -0
- package/dist/components/icon-button/IconButton.d.ts +39 -0
- package/dist/components/icon-button/IconButton.js +1 -0
- package/dist/components/icon-button/index.d.ts +2 -0
- package/dist/components/icon-button/index.js +1 -0
- package/dist/components/list/List.d.ts +81 -0
- package/dist/components/list/List.js +1 -0
- package/dist/components/list/index.d.ts +2 -0
- package/dist/components/list/index.js +1 -0
- package/dist/components/menu/Menu.d.ts +216 -0
- package/dist/components/menu/Menu.js +1 -0
- package/dist/components/menu/index.d.ts +2 -0
- package/dist/components/menu/index.js +1 -0
- package/dist/components/number-field/NumberField.d.ts +113 -0
- package/dist/components/number-field/NumberField.js +1 -0
- package/dist/components/number-field/index.d.ts +2 -0
- package/dist/components/number-field/index.js +1 -0
- package/dist/components/overlay/Overlay.d.ts +85 -0
- package/dist/components/overlay/Overlay.js +1 -0
- package/dist/components/overlay/index.d.ts +2 -0
- package/dist/components/overlay/index.js +1 -0
- package/dist/components/pagination/Pagination.d.ts +65 -0
- package/dist/components/pagination/Pagination.js +1 -0
- package/dist/components/pagination/index.d.ts +2 -0
- package/dist/components/pagination/index.js +1 -0
- package/dist/components/pill/Pill.d.ts +80 -0
- package/dist/components/pill/Pill.js +1 -0
- package/dist/components/pill/index.d.ts +2 -0
- package/dist/components/pill/index.js +1 -0
- package/dist/components/progress-box/ProgressBox.d.ts +33 -0
- package/dist/components/progress-box/ProgressBox.js +1 -0
- package/dist/components/progress-box/index.d.ts +2 -0
- package/dist/components/progress-box/index.js +1 -0
- package/dist/components/progress-circular/ProgressCircular.d.ts +19 -0
- package/dist/components/progress-circular/ProgressCircular.js +1 -0
- package/dist/components/progress-circular/index.d.ts +2 -0
- package/dist/components/progress-circular/index.js +1 -0
- package/dist/components/progress-linear/ProgressLinear.d.ts +20 -0
- package/dist/components/progress-linear/ProgressLinear.js +1 -0
- package/dist/components/progress-linear/index.d.ts +2 -0
- package/dist/components/progress-linear/index.js +1 -0
- package/dist/components/radio-group/RadioGroup.d.ts +51 -0
- package/dist/components/radio-group/RadioGroup.js +1 -0
- package/dist/components/radio-group/index.d.ts +2 -0
- package/dist/components/radio-group/index.js +1 -0
- package/dist/components/segmented-button/SegmentedButton.d.ts +77 -0
- package/dist/components/segmented-button/SegmentedButton.js +1 -0
- package/dist/components/segmented-button/index.d.ts +2 -0
- package/dist/components/segmented-button/index.js +1 -0
- package/dist/components/select/Select.d.ts +76 -0
- package/dist/components/select/Select.js +1 -0
- package/dist/components/select/index.d.ts +2 -0
- package/dist/components/select/index.js +1 -0
- package/dist/components/shortcut/Shortcut.d.ts +60 -0
- package/dist/components/shortcut/Shortcut.js +1 -0
- package/dist/components/shortcut/index.d.ts +2 -0
- package/dist/components/shortcut/index.js +1 -0
- package/dist/components/slider/Slider.d.ts +38 -0
- package/dist/components/slider/Slider.js +1 -0
- package/dist/components/slider/index.d.ts +2 -0
- package/dist/components/slider/index.js +1 -0
- package/dist/components/statistic/Statistic.d.ts +91 -0
- package/dist/components/statistic/Statistic.js +1 -0
- package/dist/components/statistic/index.d.ts +2 -0
- package/dist/components/statistic/index.js +1 -0
- package/dist/components/switch/Switch.d.ts +39 -0
- package/dist/components/switch/Switch.js +1 -0
- package/dist/components/switch/index.d.ts +2 -0
- package/dist/components/switch/index.js +1 -0
- package/dist/components/table/Table.d.ts +80 -0
- package/dist/components/table/Table.js +1 -0
- package/dist/components/table/index.d.ts +2 -0
- package/dist/components/table/index.js +1 -0
- package/dist/components/tabs/Tabs.d.ts +92 -0
- package/dist/components/tabs/Tabs.js +1 -0
- package/dist/components/tabs/index.d.ts +2 -0
- package/dist/components/tabs/index.js +1 -0
- package/dist/components/text-field/TextField.d.ts +63 -0
- package/dist/components/text-field/TextField.js +1 -0
- package/dist/components/text-field/index.d.ts +2 -0
- package/dist/components/text-field/index.js +1 -0
- package/dist/components/time-picker/TimePicker.d.ts +76 -0
- package/dist/components/time-picker/TimePicker.js +1 -0
- package/dist/components/time-picker/index.d.ts +2 -0
- package/dist/components/time-picker/index.js +1 -0
- package/dist/components/timeline/Timeline.d.ts +98 -0
- package/dist/components/timeline/Timeline.js +1 -0
- package/dist/components/timeline/index.d.ts +2 -0
- package/dist/components/timeline/index.js +1 -0
- package/dist/components/toast/Toast.d.ts +116 -0
- package/dist/components/toast/Toast.js +1 -0
- package/dist/components/toast/index.d.ts +2 -0
- package/dist/components/toast/index.js +1 -0
- package/dist/components/toolbar/Toolbar.d.ts +67 -0
- package/dist/components/toolbar/Toolbar.js +1 -0
- package/dist/components/toolbar/index.d.ts +2 -0
- package/dist/components/toolbar/index.js +1 -0
- package/dist/components/tooltip/Tooltip.d.ts +81 -0
- package/dist/components/tooltip/Tooltip.js +1 -0
- package/dist/components/tooltip/index.d.ts +2 -0
- package/dist/components/tooltip/index.js +1 -0
- package/dist/components/typography/Typography.d.ts +62 -0
- package/dist/components/typography/Typography.js +1 -0
- package/dist/components/typography/index.d.ts +2 -0
- package/dist/components/typography/index.js +1 -0
- package/dist/index.d.ts +50 -1
- package/dist/index.js +1 -1
- package/dist/internal/button-group.d.ts +25 -0
- package/dist/internal/button-group.js +1 -0
- package/dist/internal/calendar.d.ts +149 -0
- package/dist/internal/calendar.js +1 -0
- package/dist/internal/date.d.ts +170 -0
- package/dist/internal/date.js +1 -0
- package/dist/internal/grid.d.ts +69 -0
- package/dist/internal/grid.js +1 -0
- package/dist/internal/icons.d.ts +79 -0
- package/dist/internal/icons.js +1 -0
- package/dist/internal/menu.d.ts +20 -0
- package/dist/internal/menu.js +1 -0
- package/dist/internal/picker.d.ts +111 -0
- package/dist/internal/picker.js +1 -0
- package/dist/internal/progress.d.ts +122 -0
- package/dist/internal/progress.js +1 -0
- package/dist/internal/sizer.d.ts +33 -0
- package/dist/internal/sizer.js +1 -0
- package/dist/internal/styles.d.ts +257 -0
- package/dist/internal/styles.js +1 -0
- package/dist/styles.css +2 -0
- package/dist/tailwind.css +892 -0
- package/dist/types.d.ts +142 -0
- package/dist/types.js +8 -0
- package/package.json +90 -21
- package/pnpm-workspace.yaml +0 -2
package/README.md
CHANGED
|
@@ -1,9 +1,184 @@
|
|
|
1
|
-
|
|
1
|
+
<img src=".github/128x128.png" alt="Neba UI" width="128" height="128" />
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Neba UI
|
|
4
4
|
|
|
5
5
|
[](https://github.com/jooy2/neba/blob/main/LICENSE)   [](https://www.npmjs.com/package/neba) [](https://www.npmjs.com/package/neba)  [](https://github.com/jooy2) 
|
|
6
6
|
|
|
7
|
+
### 📘 [**neba.cdget.com**](https://neba.cdget.com)
|
|
8
|
+
|
|
9
|
+
[](https://neba.cdget.com/examples/)
|
|
10
|
+
|
|
11
|
+
Live previews and full props for every component. This README is just the quick start.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
**Neba UI is a React component library for building application interfaces.** It gives you the pieces a real product needs — buttons and form fields, menus and dialogs, tables and tabs, progress and notifications — already styled, already accessible, and already agreeing with each other.
|
|
16
|
+
|
|
17
|
+
You install one package, add one line to your CSS, and import components. There is nothing to configure, no theme object to assemble, and no per-component styling decisions to make before you can ship a screen.
|
|
18
|
+
|
|
19
|
+
- **A broad component set, still growing** — enough to build a whole screen without reaching elsewhere.
|
|
20
|
+
- **One shared vocabulary** — `size`, `color`, `variant`, `density`, `elevation`. An `md` is the same height on every control; `primary` is the same colour everywhere.
|
|
21
|
+
- **Accessible by construction** — real roles, labels, focus management and keyboard support, not `div`s with click handlers.
|
|
22
|
+
- **Dark mode with no work** — follows the system, and can be forced either way per subtree.
|
|
23
|
+
- **A design language, not a theme file** — a translucent acrylic surface with a hairline edge, one deliberate motion signature, and shadows that are opt-in.
|
|
24
|
+
- **ESM only**, TypeScript declarations included, tree-shakeable — every component compiles to its own module.
|
|
25
|
+
- **One runtime dependency.** React 18 or 19, Node.js 18 or later.
|
|
26
|
+
|
|
27
|
+
## Documentation
|
|
28
|
+
|
|
29
|
+
Everything is documented at **[neba.cdget.com](https://neba.cdget.com)**, where the previews are not screenshots — they are the components, running in the page.
|
|
30
|
+
|
|
31
|
+
| Page | What you will find |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| [**Getting started**](https://neba.cdget.com/guide/getting-started) | Install and setup, end to end. |
|
|
34
|
+
| [**All components**](https://neba.cdget.com/components/) | Every component, one page each: live previews and the full props table. |
|
|
35
|
+
| [**Examples**](https://neba.cdget.com/examples/) | A whole sample screen, explained block by block. |
|
|
36
|
+
| [**Design language**](https://neba.cdget.com/design/design-language) | Why a Neba surface looks and behaves the way it does. |
|
|
37
|
+
| [**Prop conventions**](https://neba.cdget.com/design/prop-conventions) | The shared vocabulary every component draws from. |
|
|
38
|
+
| [**Color**](https://neba.cdget.com/design/color) | The token families, and how to theme them. |
|
|
39
|
+
|
|
40
|
+
Also available in Korean / 한국어 문서: **[neba.cdget.com/ko/](https://neba.cdget.com/ko/)**
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install neba
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pnpm add neba
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`react` and `react-dom` are peer dependencies — React 18 or 19. Neba uses the copy your project already has, and npm 7 and later will install them alongside it if it has none.
|
|
53
|
+
|
|
54
|
+
### Setup
|
|
55
|
+
|
|
56
|
+
Add one line to your app's CSS entry point:
|
|
57
|
+
|
|
58
|
+
```css
|
|
59
|
+
@import 'neba/styles.css';
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`neba/styles.css` is finished CSS — the design tokens, the compiled rules for every utility class the components use, and a small reset whose every rule is specificity 0 so your own styles always win. [Tailwind CSS](https://tailwindcss.com) v4 builds this package; it does not have to be installed in yours.
|
|
63
|
+
|
|
64
|
+
That is the whole setup. No provider is required at the root, no theme object, no config file.
|
|
65
|
+
|
|
66
|
+
If your project already runs Tailwind v4, import `neba/tailwind.css` instead — the token sheet, which registers the package as a style source so your own build generates the utilities in the same pass as your app's:
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
@import 'tailwindcss';
|
|
70
|
+
@import 'neba/tailwind.css';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
Import components from the package root:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { Button, Card, TextField } from 'neba';
|
|
79
|
+
|
|
80
|
+
export default function SignIn() {
|
|
81
|
+
return (
|
|
82
|
+
<Card>
|
|
83
|
+
<TextField label="Email" type="email" />
|
|
84
|
+
<Button onClick={submit}>Sign in</Button>
|
|
85
|
+
</Card>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
A few components provide context and are mounted once, near the root, only if you use them — `ToastProvider` (paired with the `useToast()` hook) and `TooltipProvider`.
|
|
91
|
+
|
|
92
|
+
### The shared prop vocabulary
|
|
93
|
+
|
|
94
|
+
The reason a Neba screen looks composed rather than assembled is that the props mean one thing across the library. They live in [`src/types.ts`](src/types.ts) and every component draws from the same list:
|
|
95
|
+
|
|
96
|
+
| Prop | Values | What it changes |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| `size` | `xs` `sm` `md` `lg` `xl` | The control's scale. `md` is the desktop default. |
|
|
99
|
+
| `color` | `primary` `secondary` `success` `warning` `danger` `info` | The semantic colour family. |
|
|
100
|
+
| `variant` | `solid` `outline` `text` | How much visual weight the surface carries. |
|
|
101
|
+
| `density` | `default` `compact` | Padding only — never the height, never the type scale, so a compact control still lines up with a default one. |
|
|
102
|
+
| `elevation` | `0` `1` `2` `3` | How far a surface floats off the page. `0` is the default and means no shadow at all. |
|
|
103
|
+
|
|
104
|
+
Placement props are logical, not physical — `start`/`end` rather than `left`/`right` — so layouts flip correctly under RTL. The full rules are in [**Prop conventions**](https://neba.cdget.com/design/prop-conventions).
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<Button size="sm" color="danger" variant="outline">Delete</Button>
|
|
108
|
+
<Chip size="sm" color="danger" variant="outline">Overdue</Chip>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Components
|
|
112
|
+
|
|
113
|
+
**Inputs** — Button, IconButton, ButtonGroup, SegmentedButton, TextField, NumberField, Select, Combobox, Checkbox, RadioGroup, Switch, Slider, Menu (with submenus, checkbox and radio items), ContextMenu, FilePicker, Pagination, DatePicker, TimePicker, DateTimePicker, DateRangePicker
|
|
114
|
+
|
|
115
|
+
**Surfaces** — Box, Card, Accordion, Tabs, Carousel, Toolbar, Pill
|
|
116
|
+
|
|
117
|
+
**Display** — Typography, Blockquote, Highlight, Divider, Chip, Badge, Icon, Shortcut, Statistic, List, Table, Timeline
|
|
118
|
+
|
|
119
|
+
**Feedback** — Alert, Dialog, Toast, Tooltip, Overlay, ProgressLinear, ProgressCircular, ProgressBox
|
|
120
|
+
|
|
121
|
+
Recent additions: **Blockquote** (somebody else's words, with the `figure`/`figcaption` markup the spec actually asks for), **Shortcut** (a keyboard key or combination, where `Mod` resolves to Command on a Mac and Control everywhere else, and every glyph carries its name for a screen reader), **Highlight** (marks the words a reader searched for, walking into elements rather than demanding a plain string), **SegmentedButton** (exactly one of a set, in one pill, with a tile that slides between the choices and transforms nothing), and **Timeline** (a sequence of steps in the order they happen, vertical or as a horizontal stepper); before those, the four pickers — **DatePicker**, **TimePicker**, **DateTimePicker** and **DateRangePicker** — which take and return a plain `Date` and add no date library to your bundle.
|
|
122
|
+
|
|
123
|
+
Each one has its own page — live previews, every prop, and the variations worth seeing — under [**All components**](https://neba.cdget.com/components/).
|
|
124
|
+
|
|
125
|
+
### Theming and dark mode
|
|
126
|
+
|
|
127
|
+
Colours, radii, and surface strengths are CSS custom properties declared in the stylesheet you imported. Override any of them in your own CSS and the whole library follows:
|
|
128
|
+
|
|
129
|
+
```css
|
|
130
|
+
:root {
|
|
131
|
+
--neba-primary-fill: oklch(0.62 0.19 265);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Dark mode responds to `prefers-color-scheme` on its own. To force it, put `.dark` or `[data-theme='dark']` (or `'light'`) on any ancestor — it applies to that subtree, so a dark panel on a light page is one attribute.
|
|
136
|
+
|
|
137
|
+
Adding a whole new colour family is two edits: an entry in `NebaColor` and five tokens; everything else derives from them.
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
Clone the repository and install dependencies, then:
|
|
142
|
+
|
|
143
|
+
| Command | What it does |
|
|
144
|
+
| --- | --- |
|
|
145
|
+
| `npm run docs:dev` | Starts the documentation site (`docs/`) locally — every component, rendered live, with HMR. |
|
|
146
|
+
| `npm test` | Runs the test suite once. |
|
|
147
|
+
| `npm run test:watch` | Runs the test suite in watch mode. |
|
|
148
|
+
| `npm run typecheck` | Type-checks the library, the tests, and the docs. |
|
|
149
|
+
| `npm run build` | Formats, compiles, and minifies the library into `dist/`. |
|
|
150
|
+
| `npm run lint:fix` | Runs ESLint with autofix. |
|
|
151
|
+
| `npm run format:fix` | Runs Prettier over the repository. |
|
|
152
|
+
|
|
153
|
+
All commands are run from the repository root.
|
|
154
|
+
|
|
155
|
+
### Project structure
|
|
156
|
+
|
|
157
|
+
- `src/` — the library source. Each component lives in `src/components/{name}/` and is re-exported from `src/index.ts`.
|
|
158
|
+
- `src/internal/` — what the library shares with itself: the size and spacing tables, the surface and focus-ring generators, the icons more than one component draws. Shipped, but not part of the public API.
|
|
159
|
+
- `test/` — the test suite, mirroring the `src/` tree.
|
|
160
|
+
- `docs/` — the VitePress documentation site, in English and Korean. It renders the real components from `src/`, so it is also where components are developed and eyeballed.
|
|
161
|
+
|
|
162
|
+
There is no separate demo app: `npm run docs:dev` is the develop-and-eyeball loop.
|
|
163
|
+
|
|
164
|
+
### Tests
|
|
165
|
+
|
|
166
|
+
Tests run with [Vitest](https://vitest.dev) in browser mode, against a real headless browser driven by Playwright — the components depend on browser APIs (`ResizeObserver`, the popover API, `dialog.showModal()`) that jsdom does not implement. Install the browser once before your first run:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npx playwright install chromium
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`npm test` uses Chromium. To run against another engine, set `VITEST_BROWSER` to `chromium`, `firefox`, or `webkit` (or a comma-separated list), having installed those browsers first:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
VITEST_BROWSER=firefox npm test
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
CI runs the suite across Linux, Windows, and macOS in all three browser engines.
|
|
179
|
+
|
|
180
|
+
Tests ship with the component they cover, in the same commit. Add a test at the path mirroring its source — `src/components/button/Button.tsx` is covered by `test/components/button/Button.test.tsx`.
|
|
181
|
+
|
|
7
182
|
## Contributing
|
|
8
183
|
|
|
9
184
|
Anyone can contribute to the project by reporting new issues or submitting a pull request. For more information, please see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
export interface AccordionProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'defaultValue' | 'onChange'> {
|
|
4
|
+
/**
|
|
5
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
6
|
+
* @default 0
|
|
7
|
+
*/
|
|
8
|
+
elevation?: NebaElevation;
|
|
9
|
+
/**
|
|
10
|
+
* Whether more than one section may be open at once.
|
|
11
|
+
*
|
|
12
|
+
* `false` by default, which is the whole reason an accordion is not just a
|
|
13
|
+
* stack of collapsibles: closing the last one as you open the next is what
|
|
14
|
+
* keeps the page from growing under the reader.
|
|
15
|
+
* @default false
|
|
16
|
+
*/
|
|
17
|
+
multiple?: boolean;
|
|
18
|
+
/** Which sections are open. Use with `onValueChange` for a controlled accordion. */
|
|
19
|
+
value?: (string | number)[];
|
|
20
|
+
/** Which start open, for an uncontrolled one. */
|
|
21
|
+
defaultValue?: (string | number)[];
|
|
22
|
+
onValueChange?: (value: (string | number)[]) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Separates the sections with a hairline rather than with space.
|
|
25
|
+
*
|
|
26
|
+
* On by default, which is the other way round from List. A list of tiles is a
|
|
27
|
+
* list; an accordion of tiles is a stack of cards that happen to fold, and the
|
|
28
|
+
* rule is what says the sections are parts of one thing.
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
dividers?: boolean;
|
|
32
|
+
/** Unavailable. Every section stops answering. */
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Keeps closed panels in the DOM so the browser's own page search can find and
|
|
36
|
+
* open them. Overrides `keepMounted`.
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
hiddenUntilFound?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Keeps closed panels in the DOM. For content that is expensive to build, or
|
|
42
|
+
* that holds form state which should survive being folded away.
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
keepMounted?: boolean;
|
|
46
|
+
children?: React.ReactNode;
|
|
47
|
+
}
|
|
48
|
+
export interface AccordionItemProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'title' | 'onChange'> {
|
|
49
|
+
/**
|
|
50
|
+
* Identifies the section to `value` / `defaultValue`. Base UI generates one
|
|
51
|
+
* when it is left out, which is fine for an accordion nobody drives from code.
|
|
52
|
+
*/
|
|
53
|
+
value?: string | number;
|
|
54
|
+
/** The heading on the fold. */
|
|
55
|
+
title?: React.ReactNode;
|
|
56
|
+
/** A second line under the title, one step down the type scale and muted. */
|
|
57
|
+
subtitle?: React.ReactNode;
|
|
58
|
+
/** Content before the title — an icon, a status dot, a count. */
|
|
59
|
+
startIcon?: React.ReactNode;
|
|
60
|
+
/**
|
|
61
|
+
* A control pinned to the end of the header, before the chevron.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately outside the trigger: a header that both folds and holds a
|
|
64
|
+
* switch has two things to press, and one of them cannot be nested inside the
|
|
65
|
+
* other. The same shape ListItem uses.
|
|
66
|
+
*/
|
|
67
|
+
action?: React.ReactNode;
|
|
68
|
+
/** Unavailable. This section stops folding; the rest keep working. */
|
|
69
|
+
disabled?: boolean;
|
|
70
|
+
/** The body. */
|
|
71
|
+
children?: React.ReactNode;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A stack of sections, one of which is open.
|
|
75
|
+
*
|
|
76
|
+
* Base UI owns the parts that are genuinely hard: the `button` / `region`
|
|
77
|
+
* pairing and the `aria-controls` / `aria-expanded` wiring between them, the
|
|
78
|
+
* open set, and measuring the panel so it has a height to animate from.
|
|
79
|
+
*
|
|
80
|
+
* What is here is the surface, the ladders and the one motion decision. The
|
|
81
|
+
* panel's height *is* animated, which looks like an exception to the rule
|
|
82
|
+
* against moving things and is not: nothing is transformed, no text is resampled,
|
|
83
|
+
* and the content does not shift relative to the panel it is in — the panel is
|
|
84
|
+
* a window opening onto it. An accordion whose sections appear instantly is a
|
|
85
|
+
* page that jumps, which is the failure the rule exists to prevent.
|
|
86
|
+
*/
|
|
87
|
+
export declare const Accordion: React.ForwardRefExoticComponent<AccordionProps & React.RefAttributes<HTMLDivElement>>;
|
|
88
|
+
/**
|
|
89
|
+
* One section.
|
|
90
|
+
*
|
|
91
|
+
* The header is always a row; what is inside it is a real `<button>` covering
|
|
92
|
+
* the title and the chevron, with `action` sitting outside that button as a
|
|
93
|
+
* separate control. The same shape Chip and ListItem use, for the same two
|
|
94
|
+
* reasons: a `<div>` carrying a click handler is invisible to a keyboard, and a
|
|
95
|
+
* `<button>` inside a `<button>` is markup the browser rewrites on parse.
|
|
96
|
+
*/
|
|
97
|
+
export declare const AccordionItem: React.ForwardRefExoticComponent<AccordionItemProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as n}from"react/jsx-runtime";import*as a from"react";import{Accordion as t}from"@base-ui/react/accordion";import{boxPaddingXClasses as l,boxPaddingYClasses as r}from"../box/Box";import{ChevronIcon as i}from"../../internal/icons";import{focusRingClasses as s,gapClasses as o,hasContent as d,iconClasses as c,metaTextClasses as m,radiusClasses as f,sheetBodyClasses as u,sheetHeaderGapClasses as h,sheetTitleClasses as b,surfaceClasses as x,surfaceSlots as p,transitionClasses as v}from"../../internal/styles";const g=a.createContext({size:"md",density:"default",dividers:!0}),N={solid:[x,"text-(--neba-fg) bg-(--n-panel-hover)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[x,"border text-(--neba-fg) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"text-(--neba-fg) bg-transparent"},j={xs:f.xs,sm:f.xs,md:f.sm,lg:f.sm,xl:f.md};export const Accordion=a.forwardRef(function({variant:n="outline",size:l="md",color:r="primary",density:i="default",elevation:s=0,multiple:o=!1,value:d,defaultValue:c,onValueChange:m,dividers:u=!0,disabled:h=!1,hiddenUntilFound:b=!1,keepMounted:x=!1,className:j,style:w,children:y,...k},z){const B=a.useMemo(()=>({size:l,density:i,dividers:u}),[l,i,u]),C=["flex flex-col",f[l],N[n],v,u?"overflow-hidden [&>div+div]:border-t [&>div+div]:[border-color:var(--n-line)]":"p-1",j??""].filter(Boolean).join(" ");return e(g.Provider,{value:B,children:e(t.Root,{ref:z,multiple:o,value:d,defaultValue:c,onValueChange:e=>m?.(e),disabled:h,hiddenUntilFound:b,keepMounted:x,className:C,style:{...p(r,s),...w},...k,children:y})})});export const AccordionItem=a.forwardRef(function({value:f,title:x,subtitle:p,startIcon:N,action:w,disabled:y=!1,className:k,children:z,...B},C){const{size:V,density:$,dividers:_}=a.useContext(g),A=l[$][V],I=r[$][V];return n(t.Item,{ref:C,value:f,disabled:y,className:["flex flex-col",k??""].filter(Boolean).join(" "),...B,children:[n(t.Header,{className:"m-0 flex w-full items-center [font:inherit]",children:[n(t.Trigger,{className:["flex min-w-0 flex-1 cursor-pointer items-center text-start",A,I,o[V],v,c,s,_?"":j[V],"hover:bg-(--n-soft)","data-[panel-open]:text-(--n-accent)","disabled:cursor-not-allowed disabled:bg-transparent disabled:text-(--neba-disabled-fg)"].filter(Boolean).join(" "),children:[d(N)?e("span",{className:"flex h-[1lh] shrink-0 items-center text-(--neba-muted-fg)",children:N}):null,n("span",{className:`flex min-w-0 flex-1 flex-col ${h[V]}`,children:[d(x)?e("span",{className:`truncate font-semibold ${b[V]}`,children:x}):null,d(p)?e("span",{className:`truncate text-(--neba-muted-fg) ${m[V]}`,children:p}):null]}),e("span",{className:["flex h-[1lh] shrink-0 items-center text-(--neba-muted-fg)","[transition:rotate_var(--neba-duration)_var(--neba-ease)]","data-[panel-open]:rotate-180"].join(" "),children:e(i,{})})]}),d(w)?e("span",{className:`flex shrink-0 items-center ${A}`,children:w}):null]}),e(t.Panel,{className:["h-(--accordion-panel-height) overflow-hidden","[transition:height_var(--neba-duration)_var(--neba-ease)]","data-[starting-style]:h-0 data-[ending-style]:h-0"].join(" "),children:e("div",{className:["text-(--neba-muted-fg)",u[V],A,"compact"===$?"pb-2":"pb-4"].join(" "),children:z})})]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Accordion,AccordionItem}from"./Accordion";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
export interface AlertProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'title'> {
|
|
4
|
+
/**
|
|
5
|
+
* Drop shadow depth. `0` (the default) is flat. An alert belongs to the flow
|
|
6
|
+
* of the page it interrupts; the one that floats above it is a Toast.
|
|
7
|
+
* @default 0
|
|
8
|
+
*/
|
|
9
|
+
elevation?: NebaElevation;
|
|
10
|
+
/**
|
|
11
|
+
* The heading line. With it the alert is two-part — a headline and the detail
|
|
12
|
+
* under it; without it the whole thing is one line.
|
|
13
|
+
*/
|
|
14
|
+
title?: React.ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* The glyph at the start. Defaults to the one that goes with `color`; pass
|
|
17
|
+
* `false` to drop it, or a node to replace it. A node is sized in `em`, so it
|
|
18
|
+
* tracks whichever line it sits on.
|
|
19
|
+
*/
|
|
20
|
+
icon?: React.ReactNode | false;
|
|
21
|
+
/**
|
|
22
|
+
* Content pinned to the end of the row — a "Retry" button, a link. Kept out
|
|
23
|
+
* of `children` so it stays on the first line while the message wraps.
|
|
24
|
+
*/
|
|
25
|
+
action?: React.ReactNode;
|
|
26
|
+
/** Passing it is what makes the dismiss button appear. */
|
|
27
|
+
onClose?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
28
|
+
/** Accessible name of the dismiss button. */
|
|
29
|
+
closeLabel?: string;
|
|
30
|
+
/** The message. */
|
|
31
|
+
children?: React.ReactNode;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A message about something that happened, set into the page it is about.
|
|
35
|
+
*
|
|
36
|
+
* The three shapes people mean by "an alert" are one component with different
|
|
37
|
+
* slots filled rather than three components: a bare line
|
|
38
|
+
* (`<Alert icon={false}>`), a line with a glyph (the default), and a glyph with
|
|
39
|
+
* a headline and the detail under it (`title` plus `children`). Nothing about
|
|
40
|
+
* the surface changes between them — only how much of it is used.
|
|
41
|
+
*
|
|
42
|
+
* There is no Base UI primitive under this, and there should not be: an alert
|
|
43
|
+
* has no interaction to delegate. It is a live region with a layout, and the
|
|
44
|
+
* only interactive parts it can grow — the action and the dismiss button — are
|
|
45
|
+
* real buttons that the caller either passes in or gets by passing `onClose`.
|
|
46
|
+
*/
|
|
47
|
+
export declare const Alert: React.ForwardRefExoticComponent<AlertProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as n from"react";import{boxPaddingClasses as l}from"../box/Box";import{CloseIcon as i,severityIcons as s}from"../../internal/icons";import{controlSlots as a,hasContent as o,iconClasses as r,radiusClasses as c,sheetBodyClasses as d,sheetHeaderGapClasses as f,sheetSectionGapClasses as m,sheetTitleClasses as u,surfaceClasses as x,transitionClasses as b}from"../../internal/styles";const v={solid:[x,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[x,"border text-(--neba-fg) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"text-(--neba-fg) bg-(--n-soft)"},h={solid:"",outline:"text-(--n-accent)",text:"text-(--n-accent)"},p={solid:"",outline:"text-(--neba-muted-fg)",text:"text-(--neba-muted-fg)"},y={primary:"status",secondary:"status",info:"status",success:"status",warning:"alert",danger:"alert"};export const Alert=n.forwardRef(function({variant:n="outline",size:x="md",color:g="info",density:N="default",elevation:j=0,title:w,icon:k,action:$,onClose:_,closeLabel:z="Dismiss",className:B,style:C,children:A,...D},L){const R=void 0===k?s[g]:k,q=h[n],E=o(w);return t("div",{ref:L,role:y[g],className:["flex w-full items-start",l[N][x],c[x],m[x],d[x],b,v[n],r,B??""].filter(Boolean).join(" "),style:{...a(g,j,n),...C},...D,children:[o(R)?e("span",{className:`flex h-[1lh] shrink-0 items-center ${q}`,children:R}):null,t("div",{className:`flex min-w-0 flex-1 flex-col ${f[x]}`,children:[E?e("div",{className:`neba-title font-semibold ${u[x]} ${q}`,children:w}):null,o(A)?e("div",{className:E?p[n]:void 0,children:A}):null]}),o($)?e("div",{className:"flex h-[1lh] shrink-0 items-center",children:$}):null,_?e("span",{className:"flex h-[1lh] shrink-0 items-center",children:e("button",{type:"button","aria-label":z,onClick:_,className:["inline-flex size-[1.15em] cursor-pointer items-center justify-center rounded-full","opacity-70 [transition:opacity_var(--neba-duration)_var(--neba-ease)]","hover:opacity-100 focus-visible:opacity-100","focus-visible:[outline:2px_solid_var(--n-ring)] focus-visible:outline-offset-2"].join(" "),children:e(i,{})})}):null]})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Alert}from"./Alert";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaCorner, NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
export interface BadgeProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'span'>, 'color' | 'content'> {
|
|
4
|
+
/**
|
|
5
|
+
* What the badge says — usually a count, sometimes a word.
|
|
6
|
+
*
|
|
7
|
+
* Omit it and the badge draws a dot instead, which is the honest thing when
|
|
8
|
+
* there is something to report but nothing to count.
|
|
9
|
+
*/
|
|
10
|
+
content?: React.ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Caps a numeric `content` and adds a `+`. Only applies when the content is
|
|
13
|
+
* actually a number: a badge cannot know how to truncate a word.
|
|
14
|
+
* @default 99
|
|
15
|
+
*/
|
|
16
|
+
max?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Draws the marker as a dot even when there is content, keeping the content
|
|
19
|
+
* for screen readers only. For the corner that has to stay quiet.
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
dot?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Whether a `content` of `0` is shown. Off by default — zero unread messages
|
|
25
|
+
* is not news, and a badge that never goes away stops meaning anything.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
showZero?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Hides the marker without unmounting the anchor. The badge keeps its place
|
|
31
|
+
* in the DOM, so showing it again does not relayout what it sits on.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
invisible?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Which corner of the anchor it sits on.
|
|
37
|
+
* @default 'top-end'
|
|
38
|
+
*/
|
|
39
|
+
placement?: NebaCorner;
|
|
40
|
+
/**
|
|
41
|
+
* The shape of the thing underneath, which is what decides how far the marker
|
|
42
|
+
* tucks in: a circle's corner is further from its centre than a square's, so
|
|
43
|
+
* a badge that looks right on an avatar hangs off an icon button.
|
|
44
|
+
* @default 'square'
|
|
45
|
+
*/
|
|
46
|
+
overlap?: 'square' | 'circle';
|
|
47
|
+
/**
|
|
48
|
+
* Drop shadow depth. `0` (the default) is flat.
|
|
49
|
+
* @default 0
|
|
50
|
+
*/
|
|
51
|
+
elevation?: NebaElevation;
|
|
52
|
+
/**
|
|
53
|
+
* What a screen reader hears instead of the raw content. `content={3}` on a
|
|
54
|
+
* bell is "3" to a reader and means nothing; `label="3 unread notifications"`
|
|
55
|
+
* is the sentence.
|
|
56
|
+
*/
|
|
57
|
+
label?: string;
|
|
58
|
+
/**
|
|
59
|
+
* What the badge is pinned to. Without it the badge is a standalone marker
|
|
60
|
+
* that lays out inline, which is what a status pill in a table cell is.
|
|
61
|
+
*/
|
|
62
|
+
children?: React.ReactNode;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A small mark in the corner of something else: unread mail on an inbox icon,
|
|
66
|
+
* a status dot on an avatar, a count on a tab.
|
|
67
|
+
*
|
|
68
|
+
* The shell is a `<span>` that wraps the anchor and does nothing but establish a
|
|
69
|
+
* positioning context — no width, no padding, `align-middle` — so a badged icon
|
|
70
|
+
* button still measures and lines up exactly like a bare one. With no children
|
|
71
|
+
* the marker lays out inline instead, which is what a standalone status pill is.
|
|
72
|
+
*
|
|
73
|
+
* There is no Base UI primitive under this, and there should not be: a badge has
|
|
74
|
+
* no interaction, no state and no keyboard contract. It is a mark. Wiring it to
|
|
75
|
+
* a widget primitive would hand every decorative dot a role it cannot honour.
|
|
76
|
+
*
|
|
77
|
+
* What it does owe a screen reader is a sentence rather than a number, which is
|
|
78
|
+
* what `label` is for — `content={3}` beside a bell reads out as "3".
|
|
79
|
+
*/
|
|
80
|
+
export declare const Badge: React.ForwardRefExoticComponent<BadgeProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,Fragment as t,jsxs as m}from"react/jsx-runtime";import*as n from"react";import{controlSlots as s,hasContent as o,srOnlyClasses as a,surfaceClasses as l,transitionClasses as i}from"../../internal/styles";const r={xs:"h-3.5 min-w-3.5",sm:"h-4 min-w-4",md:"h-4.5 min-w-4.5",lg:"h-5 min-w-5",xl:"h-6 min-w-6"},d={xs:"size-1.5",sm:"size-2",md:"size-2.5",lg:"size-2.5",xl:"size-3"},b={xs:"text-[0.5625rem]",sm:"text-[0.625rem]",md:"text-[0.6875rem]",lg:"text-[0.75rem]",xl:"text-[0.8125rem]"},x={default:{xs:"px-1",sm:"px-1",md:"px-1.5",lg:"px-1.5",xl:"px-2"},compact:{xs:"px-0.5",sm:"px-0.5",md:"px-1",lg:"px-1",xl:"px-1.5"}},p="rounded-full",c={xs:{badge:"-mt-1.5 -mb-1.5 -ms-1.5 -me-1.5",dot:"-mt-0.5 -mb-0.5 -ms-0.5 -me-0.5"},sm:{badge:"-mt-2 -mb-2 -ms-2 -me-2",dot:"-mt-1 -mb-1 -ms-1 -me-1"},md:{badge:"-mt-2 -mb-2 -ms-2 -me-2",dot:"-mt-1 -mb-1 -ms-1 -me-1"},lg:{badge:"-mt-2.5 -mb-2.5 -ms-2.5 -me-2.5",dot:"-mt-1 -mb-1 -ms-1 -me-1"},xl:{badge:"-mt-3 -mb-3 -ms-3 -me-3",dot:"-mt-1.5 -mb-1.5 -ms-1.5 -me-1.5"}},g={"top-start":"top-0 start-0","top-end":"top-0 end-0","bottom-start":"bottom-0 start-0","bottom-end":"bottom-0 end-0"},f={"top-start":"mt-[7%] ms-[7%]","top-end":"mt-[7%] me-[7%]","bottom-start":"mb-[7%] ms-[7%]","bottom-end":"mb-[7%] me-[7%]"},h={solid:[l,"text-(--n-on-solid) bg-(--n-fill)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[l,"border text-(--n-accent) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"text-(--n-accent) bg-(--n-soft-press)"};export const Badge=n.forwardRef(function({variant:n="solid",size:l="md",color:u="primary",density:v="default",elevation:w=0,content:j,max:y=99,dot:z=!1,showZero:N=!1,invisible:k=!1,placement:B="top-end",overlap:$="square",label:q,className:F,style:R,children:Z,...A},C){const D=o(Z),E=!o(j)||0===j&&!N,G=z||E,H=k||E&&!z,I=["pointer-events-none z-10 inline-flex shrink-0 items-center justify-center","font-semibold tabular-nums whitespace-nowrap",p,i,h[n],G?d[l]:[r[l],b[l],x[v][l]].join(" "),D?`absolute ${g[B]}`:"relative align-middle",D?G?c[l].dot:c[l].badge:"",D&&"circle"===$?f[B]:"",H?"invisible":"",F??""].filter(Boolean).join(" "),J=function(e,t){return"number"==typeof e&&e>t?`${t}+`:e}(j,y),K=e("span",{ref:C,className:I,style:{...s(u,w,n),...R},"aria-hidden":!!H||void 0,...A,children:H?null:m(t,{children:[G||q?e("span",{className:a,children:q??J}):null,G?null:e("span",{"aria-hidden":!!q||void 0,children:J})]})});return D?m("span",{className:"relative inline-flex shrink-0 align-middle",children:[Z,K]}):K});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Badge}from"./Badge";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* The props are a `<figure>`'s rather than a `<blockquote>`'s, which is a
|
|
5
|
+
* consequence of where the drawing happens: everything a caller passes lands on
|
|
6
|
+
* the wrapper, and the wrapper is a figure or a div. Both are `HTMLElement`, so
|
|
7
|
+
* an event handler written against one works on the other.
|
|
8
|
+
*/
|
|
9
|
+
export interface BlockquoteProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'figure'>, 'color'> {
|
|
10
|
+
/**
|
|
11
|
+
* Drop shadow depth. `0` (the default) is flat — a quote is set *into* a page
|
|
12
|
+
* rather than floating over it, so this is raised even less often than on a
|
|
13
|
+
* Card.
|
|
14
|
+
* @default 0
|
|
15
|
+
*/
|
|
16
|
+
elevation?: NebaElevation;
|
|
17
|
+
/**
|
|
18
|
+
* Who said it. Its presence is what turns the quote into a `<figure>` with a
|
|
19
|
+
* `<figcaption>`, which is the markup the HTML spec asks for: an attribution
|
|
20
|
+
* is *about* the quote and is not part of what was said.
|
|
21
|
+
*/
|
|
22
|
+
author?: React.ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Where it is from — a book, a talk, a page. Rendered inside a `<cite>`, which
|
|
25
|
+
* is the element for the title of a work and, per the spec, never for the name
|
|
26
|
+
* of a person. That is what `author` is.
|
|
27
|
+
*/
|
|
28
|
+
source?: React.ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* URL of the document the quote was taken from. Lands on the `<blockquote>`'s
|
|
31
|
+
* own `cite` attribute, which is machine-readable and shown to nobody — use
|
|
32
|
+
* `source` for the part a reader should see.
|
|
33
|
+
*/
|
|
34
|
+
cite?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The mark drawn before the quote. Omit it for the house glyph, pass a node to
|
|
37
|
+
* replace it, pass `false` to take it away — the same three-way spelling Alert
|
|
38
|
+
* uses for the same idea.
|
|
39
|
+
*/
|
|
40
|
+
icon?: React.ReactNode | false;
|
|
41
|
+
/** What was said. */
|
|
42
|
+
children?: React.ReactNode;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Somebody else's words, set apart from your own.
|
|
46
|
+
*
|
|
47
|
+
* There is no Base UI primitive under this and there should not be: a quote has
|
|
48
|
+
* no state, no keyboard contract and nothing to interact with. What it has is
|
|
49
|
+
* markup that is easy to get wrong, and getting it right is most of the point.
|
|
50
|
+
*
|
|
51
|
+
* **Nothing is drawn on the `<blockquote>` itself.** The surface, the rule and
|
|
52
|
+
* the padding all belong to the element around it, and that is not tidiness —
|
|
53
|
+
* `blockquote` is one of the handful of tags a host stylesheet still styles by
|
|
54
|
+
* name. VitePress's `.vp-doc blockquote` sets a grey `border-left`, a
|
|
55
|
+
* `padding-left` and a `color`, all at a specificity a one-class utility cannot
|
|
56
|
+
* outrank, so a rule drawn on the quote itself would silently come out grey and
|
|
57
|
+
* a pixel too thin. Moving the drawing onto a wrapper is what lets the docs undo
|
|
58
|
+
* VitePress's version in `scope.css` without also undoing ours.
|
|
59
|
+
*
|
|
60
|
+
* The wrapper is a `<figure>` when there is an attribution and a `<div>` when
|
|
61
|
+
* there is not, because the HTML spec is explicit that the attribution goes
|
|
62
|
+
* *outside* the blockquote — a name inside it claims the speaker said their own
|
|
63
|
+
* name — and a `<figure>` with no `<figcaption>` in it is a figure of nothing.
|
|
64
|
+
*/
|
|
65
|
+
export declare const Blockquote: React.ForwardRefExoticComponent<BlockquoteProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import*as t from"react";import{boxPaddingClasses as n}from"../box/Box";import{hasContent as l,metaTextClasses as a,radiusClasses as o,surfaceClasses as i,surfaceSlots as s,transitionClasses as c}from"../../internal/styles";const m={xs:"text-[0.75rem]/[1.25rem]",sm:"text-[0.8125rem]/[1.375rem]",md:"text-[0.9375rem]/[1.625rem]",lg:"text-[1.0625rem]/[1.875rem]",xl:"text-[1.25rem]/[2.125rem]"},d="border-s-2 [border-inline-start-color:var(--n-accent)]",x={solid:[i,"bg-(--n-panel-hover)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[i,"border border-s-2 bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"bg-transparent"};function f(){return e("svg",{viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:e("path",{d:"M6.4 3.6c-2.3.9-3.7 2.8-3.7 5.1 0 2 1.2 3.3 2.8 3.3 1.4 0 2.5-1 2.5-2.4 0-1.3-.9-2.2-2.1-2.2-.2 0-.4 0-.6.1.3-1 1.1-1.8 2.2-2.3l-1.1-1.6ZM13.3 3.6c-2.3.9-3.7 2.8-3.7 5.1 0 2 1.2 3.3 2.8 3.3 1.4 0 2.5-1 2.5-2.4 0-1.3-.9-2.2-2.1-2.2-.2 0-.4 0-.6.1.3-1 1.1-1.8 2.2-2.3l-1.1-1.6Z",fill:"currentColor"})})}export const Blockquote=t.forwardRef(function({variant:t="text",size:i="md",color:u="primary",density:b="default",elevation:p=0,author:h,source:v,cite:g,icon:N,className:j,style:y,children:w,...B},k){const z=l(h)||l(v),q=void 0===N?e(f,{}):N,M=["flex flex-col text-(--neba-fg)",d,"text"===t?"":`${o[i]} rounded-s-none`,x[t],n[b][i],c,j??""].filter(Boolean).join(" "),Z=r("blockquote",{cite:g,className:m[i],children:[l(q)?e("span",{"aria-hidden":"true",className:"mb-1 block size-[2em] text-(--n-soft-press) [&>svg]:size-full",children:q}):null,w]}),C={...s(u,p),...y};return z?r("figure",{ref:k,className:M,style:C,...B,children:[Z,r("figcaption",{className:["mt-2 flex flex-wrap items-baseline gap-x-1.5 text-(--neba-muted-fg)",a[i]].join(" "),children:[l(h)?r("span",{className:"font-medium text-(--neba-fg)",children:[e("span",{"aria-hidden":"true",children:"— "}),h]}):null,l(v)?e("cite",{className:"not-italic",children:v}):null]})]}):e("div",{ref:k,className:M,style:C,...B,children:Z})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Blockquote}from"./Blockquote";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { NebaDensity, NebaElevation, NebaSize, NebaStyleProps } from '../../types';
|
|
4
|
+
export interface BoxProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
5
|
+
/**
|
|
6
|
+
* Drop shadow depth. `0` (the default) is flat — the acrylic edge is what
|
|
7
|
+
* separates the box from the page. Raise it only for a surface that genuinely
|
|
8
|
+
* floats above the content around it.
|
|
9
|
+
* @default 0
|
|
10
|
+
*/
|
|
11
|
+
elevation?: NebaElevation;
|
|
12
|
+
/**
|
|
13
|
+
* Inner padding, on the `size`/`density` scale below. Turn it off for
|
|
14
|
+
* full-bleed content — an image, a table, a list that draws its own rows.
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
padded?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Renders something other than a `<div>`: `render={<section />}`,
|
|
20
|
+
* `render={<li />}`, or a function for full control. Base UI's own escape
|
|
21
|
+
* hatch, so it behaves the same here as on every Base UI primitive.
|
|
22
|
+
*/
|
|
23
|
+
render?: useRender.RenderProp;
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The same two density tracks as Button, applied on both axes rather than
|
|
28
|
+
* horizontally only: a control has a fixed height that its vertical padding
|
|
29
|
+
* would fight, and a box does not.
|
|
30
|
+
*
|
|
31
|
+
* The per-axis tables are the same numbers split apart, for Card — which turns
|
|
32
|
+
* this padding off and re-applies it section by section so its dividers can run
|
|
33
|
+
* the full width of the sheet. They live here so the two can never drift.
|
|
34
|
+
*/
|
|
35
|
+
export declare const boxPaddingClasses: Record<NebaDensity, Record<NebaSize, string>>;
|
|
36
|
+
export declare const boxPaddingXClasses: Record<NebaDensity, Record<NebaSize, string>>;
|
|
37
|
+
export declare const boxPaddingYClasses: Record<NebaDensity, Record<NebaSize, string>>;
|
|
38
|
+
/**
|
|
39
|
+
* A sheet of acrylic with content on it. The plainest surface in the library:
|
|
40
|
+
* it groups things, and that is all it does.
|
|
41
|
+
*
|
|
42
|
+
* Everything structural — a title, a footer, dividers — belongs to Card, which
|
|
43
|
+
* is a Box with those sections laid out on it.
|
|
44
|
+
*
|
|
45
|
+
* `surfaceSlots` is the undyed slot set: the panel ladder points at the neutral
|
|
46
|
+
* `--neba-panel*` steps rather than at the family's own, which is what keeps a
|
|
47
|
+
* container's surface white. `color` reaches the hairline and the accent, and
|
|
48
|
+
* stops there.
|
|
49
|
+
*/
|
|
50
|
+
export declare const Box: React.ForwardRefExoticComponent<BoxProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"react";import{useRender as s}from"@base-ui/react/use-render";import{radiusClasses as o,surfaceClasses as a,surfaceSlots as p,transitionClasses as t}from"../../internal/styles";const l=o;export const boxPaddingClasses={default:{xs:"p-2",sm:"p-3",md:"p-4",lg:"p-5",xl:"p-6"},compact:{xs:"p-1.5",sm:"p-2",md:"p-2.5",lg:"p-3",xl:"p-4"}};export const boxPaddingXClasses={default:{xs:"px-2",sm:"px-3",md:"px-4",lg:"px-5",xl:"px-6"},compact:{xs:"px-1.5",sm:"px-2",md:"px-2.5",lg:"px-3",xl:"px-4"}};export const boxPaddingYClasses={default:{xs:"py-2",sm:"py-3",md:"py-4",lg:"py-5",xl:"py-6"},compact:{xs:"py-1.5",sm:"py-2",md:"py-2.5",lg:"py-3",xl:"py-4"}};const n=["block",t].join(" "),r={solid:[a,"text-(--neba-fg) bg-(--n-panel-hover)","[box-shadow:var(--n-elev),var(--neba-plate-solid)]"].join(" "),outline:[a,"border text-(--neba-fg) bg-(--n-panel)","[border-color:var(--n-line)]","[box-shadow:var(--n-elev),var(--neba-plate-glass)]"].join(" "),text:"text-(--neba-fg) bg-transparent"};export const Box=e.forwardRef(function({variant:e="outline",size:o="md",color:a="primary",density:t="default",elevation:x=0,padded:d=!0,render:i,className:m,style:c,children:b,...g},f){const y=[n,l[o],d?boxPaddingClasses[t][o]:"",r[e],m??""].filter(Boolean).join(" ");return s({render:i,ref:f,props:{className:y,style:{...p(a,x),...c},children:b,...g}})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{Box}from"./Box";
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { NebaElevation, NebaStyleProps } from '../../types';
|
|
3
|
+
export interface ButtonProps extends NebaStyleProps, Omit<React.ComponentPropsWithoutRef<'button'>, 'color'> {
|
|
4
|
+
/**
|
|
5
|
+
* Drop shadow depth. `0` (the default) is flat — the acrylic edge is what
|
|
6
|
+
* separates the button from the page. Hover adds a level, press removes one.
|
|
7
|
+
* @default 0
|
|
8
|
+
*/
|
|
9
|
+
elevation?: NebaElevation;
|
|
10
|
+
/** Content placed before the label. Sized in `em`, so it tracks the label. */
|
|
11
|
+
startIcon?: React.ReactNode;
|
|
12
|
+
/** Content placed after the label. */
|
|
13
|
+
endIcon?: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Shows a spinner in place of `startIcon` and stops the button from
|
|
16
|
+
* activating, while keeping it focusable and visually unchanged otherwise.
|
|
17
|
+
*/
|
|
18
|
+
loading?: boolean;
|
|
19
|
+
/** Inert but not dimmed — the action exists, it just is not available here. */
|
|
20
|
+
readOnly?: boolean;
|
|
21
|
+
/** Stretches to the width of the container. */
|
|
22
|
+
fullWidth?: boolean;
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|