barua-ui 0.2.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 +107 -0
- package/css/barua.css +28 -0
- package/css/base.css +242 -0
- package/css/components/actions.css +371 -0
- package/css/components/auth.css +237 -0
- package/css/components/charts.css +370 -0
- package/css/components/content.css +802 -0
- package/css/components/control-center.css +370 -0
- package/css/components/feedback.css +364 -0
- package/css/components/forms.css +511 -0
- package/css/components/marketing.css +280 -0
- package/css/components/media.css +321 -0
- package/css/components/mobile.css +177 -0
- package/css/components/nav.css +682 -0
- package/css/components/overlays.css +574 -0
- package/css/components/productivity.css +525 -0
- package/css/components/specialized.css +367 -0
- package/css/liquid.css +213 -0
- package/css/tokens.css +365 -0
- package/css/utilities.css +507 -0
- package/dist/index.cjs +2750 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1106 -0
- package/dist/index.d.ts +1106 -0
- package/dist/index.js +2462 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# barua-ui
|
|
2
|
+
|
|
3
|
+
Barua UI as React components — an Apple-DNA design system (Barua blue, real
|
|
4
|
+
glass materials, HIG type) built on **pure CSS**, with thin typed React
|
|
5
|
+
bindings on top. The CSS is the source of truth; components just compose the
|
|
6
|
+
`.b-*` class system and add the interactive behaviors the React way.
|
|
7
|
+
|
|
8
|
+
## What is in it
|
|
9
|
+
|
|
10
|
+
278 components and hooks — every block in the system has one. The full
|
|
11
|
+
list, grouped, is at <https://ui.barua.tz/docs/react.html>, and every example in
|
|
12
|
+
the documentation carries a **React** tab with the JSX that produces it.
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Card, CardBody, Stat, Button, Icon } from "barua-ui";
|
|
16
|
+
|
|
17
|
+
<Card compact>
|
|
18
|
+
<CardBody>
|
|
19
|
+
<Stat label="Outstanding" value="TZS 4.2M" />
|
|
20
|
+
<Button variant="primary">
|
|
21
|
+
<Icon name="check" /> Mark paid
|
|
22
|
+
</Button>
|
|
23
|
+
</CardBody>
|
|
24
|
+
</Card>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Components render the documented markup and nothing else, so anything you read
|
|
28
|
+
on the docs pages applies unchanged. Each one forwards `className` and its ref,
|
|
29
|
+
so you are never boxed in.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm install barua-ui
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Use
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
// 1. Load the stylesheet once (any bundler handles the @imports)
|
|
41
|
+
import "barua-ui/css";
|
|
42
|
+
|
|
43
|
+
// 2. Mount the provider at your root
|
|
44
|
+
import { BaruaProvider, Button, Card, CardBody, useToast } from "barua-ui";
|
|
45
|
+
|
|
46
|
+
function Root() {
|
|
47
|
+
return (
|
|
48
|
+
<BaruaProvider theme="auto" accent="blue" glass={false}>
|
|
49
|
+
<App />
|
|
50
|
+
</BaruaProvider>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
const toast = useToast();
|
|
56
|
+
return (
|
|
57
|
+
<Card variant="glass" interactive>
|
|
58
|
+
<CardBody>
|
|
59
|
+
<Button
|
|
60
|
+
variant="primary"
|
|
61
|
+
size="lg"
|
|
62
|
+
onClick={() => toast({ title: "Karibu!", variant: "success" })}
|
|
63
|
+
>
|
|
64
|
+
Habari, Barua!
|
|
65
|
+
</Button>
|
|
66
|
+
</CardBody>
|
|
67
|
+
</Card>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
SSR/Next.js: render `<ThemeScript />` in `<head>` so the stored theme applies
|
|
73
|
+
before first paint.
|
|
74
|
+
|
|
75
|
+
## What's in the box
|
|
76
|
+
|
|
77
|
+
- **Provider** — `BaruaProvider` (theme / `accent` re-tint / Liquid Glass
|
|
78
|
+
`glass` mode / Tier-2 `refraction`), `ThemeScript`, `useToast`
|
|
79
|
+
- **Actions** — `Button`, `ButtonLink`, `Fab`, `Toolbar`
|
|
80
|
+
- **Forms** — `Field`, `Input`, `Textarea`, `Select`, `Checkbox`, `Radio`,
|
|
81
|
+
`Switch`, `Slider`, `OtpInput`
|
|
82
|
+
- **Content** — `Card` family (incl. `StoryCard`, flush iOS-Settings groups),
|
|
83
|
+
`List`/`ListItem`, `Badge`, `Tag`, `Chip`, `Avatar`(+`AvatarGroup`),
|
|
84
|
+
`IconTile`, `Stat`, `EmptyState`
|
|
85
|
+
- **Feedback** — `Alert`, `Progress`, `Spinner`, `Skeleton`, `StatusDot`,
|
|
86
|
+
toasts
|
|
87
|
+
- **Overlays** — `Modal`, `AlertDialog`, `Sheet`, `BottomSheet`, `Popover`
|
|
88
|
+
(arrowed/card), `Tooltip` — all on native `<dialog>`, centered by default
|
|
89
|
+
- **Navigation** — `Segmented`, `Tabs`/`TabPanel`, `Breadcrumbs`
|
|
90
|
+
|
|
91
|
+
Anything not wrapped yet is still one `className` away — the entire `.b-*`
|
|
92
|
+
system ships in `barua-ui/css`, documented in the Barua UI docs site.
|
|
93
|
+
|
|
94
|
+
## Theming
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<BaruaProvider theme="dark" accent="purple" glass refraction />
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `theme`: `"auto" | "light" | "dark"` → `data-theme` on `<html>`
|
|
101
|
+
- `accent`: re-tints every component from one token
|
|
102
|
+
- `glass`: Liquid Glass mode — re-skins the whole system
|
|
103
|
+
- `refraction`: SVG-displacement refraction on liquid objects (Blink only,
|
|
104
|
+
graceful elsewhere)
|
|
105
|
+
|
|
106
|
+
Modern evergreen browsers: `light-dark()`, `@layer`, `color-mix()`, Popover
|
|
107
|
+
API, `<dialog>`.
|
package/css/barua.css
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Barua UI — single entry point
|
|
3
|
+
<link rel="stylesheet" href="css/barua.css">
|
|
4
|
+
|
|
5
|
+
Layer order (later wins): reset < tokens < base < utilities < components.
|
|
6
|
+
Your overrides go in unlayered CSS, which always beats layered rules.
|
|
7
|
+
========================================================================== */
|
|
8
|
+
|
|
9
|
+
@layer tokens, base, utilities, components;
|
|
10
|
+
|
|
11
|
+
@import url("tokens.css");
|
|
12
|
+
@import url("base.css");
|
|
13
|
+
@import url("utilities.css");
|
|
14
|
+
@import url("components/actions.css");
|
|
15
|
+
@import url("components/nav.css");
|
|
16
|
+
@import url("components/forms.css");
|
|
17
|
+
@import url("components/content.css");
|
|
18
|
+
@import url("components/feedback.css");
|
|
19
|
+
@import url("components/overlays.css");
|
|
20
|
+
@import url("components/charts.css");
|
|
21
|
+
@import url("components/media.css");
|
|
22
|
+
@import url("components/specialized.css");
|
|
23
|
+
@import url("components/productivity.css");
|
|
24
|
+
@import url("components/mobile.css");
|
|
25
|
+
@import url("components/auth.css");
|
|
26
|
+
@import url("components/control-center.css");
|
|
27
|
+
@import url("components/marketing.css");
|
|
28
|
+
@import url("liquid.css");
|
package/css/base.css
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
Barua UI — Base
|
|
3
|
+
Reset, document defaults, typography elements, focus, selection,
|
|
4
|
+
interaction-state conventions.
|
|
5
|
+
========================================================================== */
|
|
6
|
+
|
|
7
|
+
@layer base {
|
|
8
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
9
|
+
|
|
10
|
+
* { margin: 0; }
|
|
11
|
+
/* The universal reset above would defeat the UA's centering of dialogs
|
|
12
|
+
and popovers (both center via `margin: auto` in the top layer). */
|
|
13
|
+
dialog, [popover] { margin: auto; }
|
|
14
|
+
|
|
15
|
+
html {
|
|
16
|
+
-webkit-text-size-adjust: 100%;
|
|
17
|
+
tab-size: 4;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-family: var(--b-font-sans);
|
|
22
|
+
font-size: var(--b-text-body);
|
|
23
|
+
line-height: var(--b-leading-body);
|
|
24
|
+
letter-spacing: var(--b-tracking-text);
|
|
25
|
+
color: var(--b-text);
|
|
26
|
+
background: var(--b-bg);
|
|
27
|
+
-webkit-font-smoothing: antialiased;
|
|
28
|
+
text-rendering: optimizeLegibility;
|
|
29
|
+
min-height: 100dvh;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
img, picture, video, canvas, svg { display: block; max-width: 100%; }
|
|
33
|
+
input, button, textarea, select { font: inherit; color: inherit; letter-spacing: inherit; }
|
|
34
|
+
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
|
|
35
|
+
|
|
36
|
+
/* The hidden attribute means hidden. A display utility on the same element
|
|
37
|
+
would otherwise win on specificity and quietly keep it on screen. */
|
|
38
|
+
[hidden] { display: none !important; }
|
|
39
|
+
|
|
40
|
+
/* ---- Typographic elements -------------------------------------------- */
|
|
41
|
+
h1, h2, h3, h4, h5, h6 {
|
|
42
|
+
font-weight: var(--b-weight-semibold);
|
|
43
|
+
line-height: var(--b-leading-tight);
|
|
44
|
+
letter-spacing: var(--b-tracking-tight);
|
|
45
|
+
text-wrap: balance;
|
|
46
|
+
}
|
|
47
|
+
h1 { font-size: var(--b-text-large-title); font-weight: var(--b-weight-bold); }
|
|
48
|
+
h2 { font-size: var(--b-text-title1); }
|
|
49
|
+
h3 { font-size: var(--b-text-title2); }
|
|
50
|
+
h4 { font-size: var(--b-text-title3); }
|
|
51
|
+
/* At headline size and below, tight tracking is wrong — these match the
|
|
52
|
+
.b-headline / .b-subheadline roles exactly rather than diverging. */
|
|
53
|
+
h5 { font-size: var(--b-text-headline); line-height: var(--b-leading-snug); letter-spacing: var(--b-tracking-text); }
|
|
54
|
+
h6 { font-size: var(--b-text-subheadline); text-transform: uppercase;
|
|
55
|
+
letter-spacing: var(--b-tracking-wide); color: var(--b-text-secondary); }
|
|
56
|
+
|
|
57
|
+
small { font-size: var(--b-text-footnote); color: var(--b-text-secondary); }
|
|
58
|
+
strong, b { font-weight: var(--b-weight-semibold); }
|
|
59
|
+
|
|
60
|
+
a {
|
|
61
|
+
color: var(--b-color-accent-text);
|
|
62
|
+
text-decoration: none;
|
|
63
|
+
}
|
|
64
|
+
a:hover { text-decoration: underline; text-underline-offset: 0.2em; }
|
|
65
|
+
|
|
66
|
+
code, kbd, samp, pre { font-family: var(--b-font-mono); font-size: 0.875em; }
|
|
67
|
+
code {
|
|
68
|
+
background: var(--b-fill-tertiary);
|
|
69
|
+
border-radius: var(--b-radius-xs);
|
|
70
|
+
padding: 0.125em 0.375em;
|
|
71
|
+
}
|
|
72
|
+
pre {
|
|
73
|
+
overflow: auto;
|
|
74
|
+
padding: var(--b-space-4);
|
|
75
|
+
background: var(--b-surface-2);
|
|
76
|
+
border: 1px solid var(--b-separator);
|
|
77
|
+
border-radius: var(--b-radius-md);
|
|
78
|
+
line-height: var(--b-leading-snug);
|
|
79
|
+
}
|
|
80
|
+
pre code { background: none; padding: 0; border-radius: 0; font-size: var(--b-text-footnote); }
|
|
81
|
+
|
|
82
|
+
hr {
|
|
83
|
+
border: none;
|
|
84
|
+
border-top: 1px solid var(--b-separator);
|
|
85
|
+
margin: var(--b-space-6) 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
blockquote {
|
|
89
|
+
border-inline-start: 3px solid var(--b-color-accent);
|
|
90
|
+
padding: var(--b-space-1) var(--b-space-4);
|
|
91
|
+
color: var(--b-text-secondary);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Lists in prose. The reset strips markers from every list so that navigation
|
|
95
|
+
and component lists are unadorned; these put them back where a list is
|
|
96
|
+
actually a list — rendered markdown, a card body, an article. Component
|
|
97
|
+
lists opt out with .b-list, which is unaffected. */
|
|
98
|
+
:where(ul, ol):not([class]) {
|
|
99
|
+
padding-inline-start: var(--b-space-6);
|
|
100
|
+
list-style-position: outside;
|
|
101
|
+
}
|
|
102
|
+
:where(ul):not([class]) { list-style-type: disc; }
|
|
103
|
+
:where(ol):not([class]) { list-style-type: decimal; }
|
|
104
|
+
:where(ul, ol):not([class]) :where(ul, ol) {
|
|
105
|
+
margin-block-start: var(--b-space-1);
|
|
106
|
+
}
|
|
107
|
+
:where(ul, ol):not([class]) :where(ul):not([class]) { list-style-type: circle; }
|
|
108
|
+
:where(ul, ol):not([class]) > li + li { margin-block-start: var(--b-space-1); }
|
|
109
|
+
:where(ul, ol):not([class]) > li::marker { color: var(--b-text-tertiary); }
|
|
110
|
+
|
|
111
|
+
::selection {
|
|
112
|
+
background: color-mix(in srgb, var(--b-color-accent) 28%, transparent);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ---- Focus ring (Focus Ring component) -------------------------------- */
|
|
116
|
+
:focus-visible {
|
|
117
|
+
outline: none;
|
|
118
|
+
box-shadow: var(--b-focus-ring);
|
|
119
|
+
border-radius: var(--b-radius-xs);
|
|
120
|
+
}
|
|
121
|
+
.b-focus-ring:focus-visible,
|
|
122
|
+
button:focus-visible, [href]:focus-visible,
|
|
123
|
+
input:focus-visible, select:focus-visible, textarea:focus-visible,
|
|
124
|
+
[tabindex]:focus-visible {
|
|
125
|
+
outline: none;
|
|
126
|
+
box-shadow: var(--b-focus-ring);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* ---- Interaction-state conventions ------------------------------------
|
|
130
|
+
Every interactive component honours:
|
|
131
|
+
:hover / .is-hover — hover affordance
|
|
132
|
+
:active / .is-pressed — pressed
|
|
133
|
+
.is-active — current / toggled-on
|
|
134
|
+
.is-selected — selection
|
|
135
|
+
.is-dragging — mid-drag
|
|
136
|
+
:disabled / .is-disabled — non-interactive
|
|
137
|
+
------------------------------------------------------------------------- */
|
|
138
|
+
:disabled, .is-disabled {
|
|
139
|
+
opacity: var(--b-opacity-disabled);
|
|
140
|
+
cursor: not-allowed !important;
|
|
141
|
+
pointer-events: var(--b-disabled-pointer, auto);
|
|
142
|
+
}
|
|
143
|
+
.is-dragging { opacity: 0.55; cursor: grabbing !important; }
|
|
144
|
+
|
|
145
|
+
[draggable="true"] { cursor: grab; }
|
|
146
|
+
|
|
147
|
+
/* ---- Scrollbars (subtle, macOS-like) ---------------------------------- */
|
|
148
|
+
* {
|
|
149
|
+
scrollbar-width: thin;
|
|
150
|
+
scrollbar-color: var(--b-fill) transparent;
|
|
151
|
+
}
|
|
152
|
+
*::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
153
|
+
*::-webkit-scrollbar-thumb {
|
|
154
|
+
background: var(--b-fill);
|
|
155
|
+
border-radius: var(--b-radius-full);
|
|
156
|
+
}
|
|
157
|
+
*::-webkit-scrollbar-track { background: transparent; }
|
|
158
|
+
|
|
159
|
+
/* ---- Reduced motion ---------------------------------------------------- */
|
|
160
|
+
@media (prefers-reduced-motion: reduce) {
|
|
161
|
+
*, *::before, *::after {
|
|
162
|
+
animation-duration: 0.01ms !important;
|
|
163
|
+
animation-iteration-count: 1 !important;
|
|
164
|
+
transition-duration: 0.01ms !important;
|
|
165
|
+
scroll-behavior: auto !important;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* ---- Screen-reader-only ------------------------------------------------ */
|
|
170
|
+
.b-sr-only {
|
|
171
|
+
position: absolute;
|
|
172
|
+
width: 1px; height: 1px;
|
|
173
|
+
padding: 0; margin: -1px;
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
clip-path: inset(50%);
|
|
176
|
+
white-space: nowrap;
|
|
177
|
+
border: 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Skip link — appears on focus */
|
|
181
|
+
.b-skip-link {
|
|
182
|
+
position: fixed;
|
|
183
|
+
inset-block-start: var(--b-space-2);
|
|
184
|
+
inset-inline-start: var(--b-space-2);
|
|
185
|
+
z-index: var(--b-z-tooltip);
|
|
186
|
+
padding: var(--b-space-2) var(--b-space-4);
|
|
187
|
+
background: var(--b-elevated);
|
|
188
|
+
border-radius: var(--b-radius-md);
|
|
189
|
+
box-shadow: var(--b-elevation-4);
|
|
190
|
+
font-weight: var(--b-weight-semibold);
|
|
191
|
+
translate: 0 calc(-100% - var(--b-space-4));
|
|
192
|
+
transition: translate var(--b-duration-fast) var(--b-ease-out);
|
|
193
|
+
}
|
|
194
|
+
.b-skip-link:focus-visible { translate: 0 0; }
|
|
195
|
+
|
|
196
|
+
/* ---- Keyboard shortcut (kbd) ------------------------------------------ */
|
|
197
|
+
kbd, .b-kbd {
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: center;
|
|
201
|
+
min-width: 1.4em;
|
|
202
|
+
padding: 0.1em 0.45em;
|
|
203
|
+
font-family: var(--b-font-sans);
|
|
204
|
+
font-size: var(--b-text-caption);
|
|
205
|
+
font-weight: var(--b-weight-medium);
|
|
206
|
+
color: var(--b-text-secondary);
|
|
207
|
+
background: var(--b-fill-tertiary);
|
|
208
|
+
border: 1px solid var(--b-border);
|
|
209
|
+
border-bottom-width: 2px;
|
|
210
|
+
border-radius: var(--b-radius-sm);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* UNLAYERED on purpose — must beat every layer. Closed dialogs and closed
|
|
215
|
+
popovers stay hidden even when a component class sets `display` (author
|
|
216
|
+
styles otherwise override the UA's hiding rules). `allow-discrete`
|
|
217
|
+
transitions on components still animate the exit before display flips. */
|
|
218
|
+
dialog:not([open]) { display: none; }
|
|
219
|
+
[popover]:not(:popover-open) { display: none; }
|
|
220
|
+
|
|
221
|
+
/* View Transitions (SwiftUI matchedGeometry energy) — OPT-IN: set
|
|
222
|
+
data-vt on <html> and same-origin navigations cross-fade with the
|
|
223
|
+
system's motion tokens. Reduced motion disables it automatically. */
|
|
224
|
+
@view-transition { navigation: auto; }
|
|
225
|
+
html:not([data-vt])::view-transition-old(root),
|
|
226
|
+
html:not([data-vt])::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
|
|
227
|
+
[data-vt]::view-transition-old(root) { animation-duration: 180ms; }
|
|
228
|
+
[data-vt]::view-transition-new(root) { animation-duration: 220ms; }
|
|
229
|
+
/* Matched elements: any two elements sharing a data-vt-name — a thumbnail here
|
|
230
|
+
and the hero it opens into there — are morphed by the browser instead of
|
|
231
|
+
cross-faded. The group carries the position and size change, so it wants the
|
|
232
|
+
system's spring rather than a linear tween. */
|
|
233
|
+
[data-vt]::view-transition-group(*) {
|
|
234
|
+
animation-duration: var(--b-duration-slow, 400ms);
|
|
235
|
+
animation-timing-function: var(--b-ease-spring, cubic-bezier(0.32, 0.72, 0, 1));
|
|
236
|
+
}
|
|
237
|
+
@media (prefers-reduced-motion: reduce) {
|
|
238
|
+
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
|
|
239
|
+
::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
|
|
240
|
+
animation: none;
|
|
241
|
+
}
|
|
242
|
+
}
|