ab-nextjs-components 0.1.6 → 0.1.8
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/CHANGELOG.md +9 -0
- package/README.md +13 -0
- package/ab-logo/index.tsx +1 -1
- package/ab-navbar/index.tsx +1 -1
- package/assets/logos/ab-logo.svg +1 -0
- package/assets/spinners/12-dots-scale-rotate.svg +1 -0
- package/assets/spinners/3-dots-scale.svg +1 -0
- package/assets/spinners/LICENSE +20 -0
- package/css/chrome-mq.css +121 -0
- package/css/dialog.css +248 -0
- package/css/doodle.css +41 -0
- package/css/logo.css +52 -0
- package/css/spinner.css +28 -0
- package/css/toast.css +70 -0
- package/package.json +12 -5
- package/server/ab-logo/index.tsx +1 -1
- package/server/ab-navbar/index.tsx +1 -1
- package/styles.css +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## 0.1.8 — 2026-10-05
|
|
2
|
+
|
|
3
|
+
- **Chrome styles:** dialog, toast, logo (`span.app-logo`), spinner, and empty-state doodle CSS + SVG assets now ship here via `@import "ab-nextjs-components/styles.css"` (import after `ab-nextjs-theme/styles.css`)
|
|
4
|
+
- **AbLogo:** comments corrected — mask fallback `--app-logo-url` is owned by this package’s chrome CSS, not the theme
|
|
5
|
+
|
|
6
|
+
## 0.1.7 — 2026-10-05
|
|
7
|
+
|
|
8
|
+
- **AbNavbar:** root `<nav>` sets `data-ab-part="bottomBar"` (client + server)
|
|
9
|
+
|
|
1
10
|
## 0.1.6 — 2026-10-05
|
|
2
11
|
|
|
3
12
|
- **AbSidebar:** renders `<nav data-ab-part="sidebar">` (was `<aside>`) so aside dialogs/menus never hit the sidebar
|
package/README.md
CHANGED
|
@@ -37,6 +37,19 @@
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
|
|
41
|
+
### Component chrome styles
|
|
42
|
+
|
|
43
|
+
Dialog, toast, logo (`span.app-logo`), spinner, and empty-state doodle CSS (plus their SVG assets) live in this package:
|
|
44
|
+
|
|
45
|
+
```css
|
|
46
|
+
@import "ab-nextjs-theme/styles.css"; /* tokens */
|
|
47
|
+
@import "ab-nextjs-components/styles.css"; /* chrome */
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Shipped assets: `assets/logos/ab-logo.svg`, `assets/spinners/3-dots-scale.svg`, `assets/spinners/12-dots-scale-rotate.svg`.
|
|
51
|
+
|
|
52
|
+
|
|
40
53
|
## Tooling (Phase 1)
|
|
41
54
|
|
|
42
55
|
- Next.js **16.3.4** / React **19** hard peers
|
package/ab-logo/index.tsx
CHANGED
|
@@ -73,7 +73,7 @@ const AbLogo = ({
|
|
|
73
73
|
);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// With no `mask`/`src`, leave the mask to `ab-nextjs-
|
|
76
|
+
// With no `mask`/`src`, leave the mask to `ab-nextjs-components` chrome (`--app-logo-url` / `.app-logo`).
|
|
77
77
|
// When `mask` or `src` is set, apply it inline (src becomes a CSS mask url).
|
|
78
78
|
const maskValue = mask ?? (src ? `url('${src}') no-repeat 50% 50%` : undefined);
|
|
79
79
|
return (
|
package/ab-navbar/index.tsx
CHANGED
|
@@ -40,7 +40,7 @@ const AbNavbar = ({
|
|
|
40
40
|
}: AbNavbarProps): ReactElement => {
|
|
41
41
|
const pathname = usePathname() ?? '';
|
|
42
42
|
return (
|
|
43
|
-
<nav data-type={type} className={clsx('AbNavbar', styles.abNavbar, className)} aria-label="Primary">
|
|
43
|
+
<nav data-type={type} data-ab-part="bottomBar" className={clsx('AbNavbar', styles.abNavbar, className)} aria-label="Primary">
|
|
44
44
|
<ul className={styles.abNavbarList}>
|
|
45
45
|
{links.map((link) => {
|
|
46
46
|
const active = page ? page === link.value : pathname === link.href || pathname.startsWith(`${link.href}/`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 24 24"><path fill="#A67C52" d="m1.8 20.5 9.3-17v17zM22.2 7.7l-9.3-4.2v17l9.3-4.2-4.7-4.3z"/><path d="m12.9 20.5 9.3-4.2-9.3-8.6z" opacity=".1"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>@keyframes spinner_grm3{0%,50%{animation-timing-function:cubic-bezier(.27,.42,.37,.99);r:1px}25%{animation-timing-function:cubic-bezier(.53,0,.61,.73);r:2px}}@keyframes spinner_T3O6{0%{transform:rotate(360deg)}}.spinner_EUy1{animation:spinner_grm3 1.2s infinite}.spinner_f6oS{animation-delay:.1s}.spinner_g3nX{animation-delay:.2s}.spinner_nvEs{animation-delay:.3s}.spinner_MaNM{animation-delay:.4s}.spinner_4nle{animation-delay:.5s}.spinner_ZETM{animation-delay:.6s}.spinner_HXuO{animation-delay:.7s}.spinner_YaQo{animation-delay:.8s}.spinner_GOx1{animation-delay:.9s}.spinner_4vv9{animation-delay:1s}.spinner_NTs9{animation-delay:1.1s}.spinner_auJJ{transform-origin:center;animation:spinner_T3O6 6s linear infinite}</style><g class="spinner_auJJ"><circle cx="12" cy="3" r="1" class="spinner_EUy1"/><circle cx="16.5" cy="4.21" r="1" class="spinner_EUy1 spinner_f6oS"/><circle cx="7.5" cy="4.21" r="1" class="spinner_EUy1 spinner_NTs9"/><circle cx="19.79" cy="7.5" r="1" class="spinner_EUy1 spinner_g3nX"/><circle cx="4.21" cy="7.5" r="1" class="spinner_EUy1 spinner_4vv9"/><circle cx="21" cy="12" r="1" class="spinner_EUy1 spinner_nvEs"/><circle cx="3" cy="12" r="1" class="spinner_EUy1 spinner_GOx1"/><circle cx="19.79" cy="16.5" r="1" class="spinner_EUy1 spinner_MaNM"/><circle cx="4.21" cy="16.5" r="1" class="spinner_EUy1 spinner_YaQo"/><circle cx="16.5" cy="19.79" r="1" class="spinner_EUy1 spinner_4nle"/><circle cx="7.5" cy="19.79" r="1" class="spinner_EUy1 spinner_HXuO"/><circle cx="12" cy="21" r="1" class="spinner_EUy1 spinner_ZETM"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>@keyframes spinner_xe7Q{93.75%{r:3px}46.875%{r:.2px}}.spinner_b2T7{animation:spinner_xe7Q .8s linear infinite}.spinner_YRVV{animation-delay:-.65s}.spinner_c9oY{animation-delay:-.5s}</style><circle cx="4" cy="12" r="3" class="spinner_b2T7"/><circle cx="12" cy="12" r="3" class="spinner_b2T7 spinner_YRVV"/><circle cx="20" cy="12" r="3" class="spinner_b2T7 spinner_c9oY"/></svg>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Utkarsh Verma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ab-nextjs-components — chrome styles moved out of ab-nextjs-theme
|
|
3
|
+
* Copyright (c) 2026 Abraham Ukachi. MIT.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
@media (width >= 460px) {
|
|
7
|
+
[data-content] > .container[data-empty="true"] .doodle {
|
|
8
|
+
width: var(--empty-doodle-size-bigger, 256px);
|
|
9
|
+
height: var(--empty-doodle-size-bigger, 256px);
|
|
10
|
+
}
|
|
11
|
+
/* Toasts */
|
|
12
|
+
#toasts {
|
|
13
|
+
padding: 3rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/* Toast Emojis */
|
|
18
|
+
.toast-emoji {
|
|
19
|
+
width: var(--toast-emoji-size, 32px);
|
|
20
|
+
height: var(--toast-emoji-size, 32px);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.toast-emoji:not([data-emoji-hidden], .success) {
|
|
24
|
+
transform: scale(1.2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@media (width >= 768px) {
|
|
29
|
+
/* Toast */
|
|
30
|
+
#toasts .toast {
|
|
31
|
+
padding: 1rem 1.5rem;
|
|
32
|
+
border-radius: 48px;
|
|
33
|
+
font-size: 1.25rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#toasts .toast-emoji:not([data-emoji-hidden], .success) {
|
|
37
|
+
transform: scale(1.5);
|
|
38
|
+
}
|
|
39
|
+
#dialog, .dialog {
|
|
40
|
+
max-width: var(--dialog-max-width, 480px);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@media (width >= 768px) and (width <= 1024px) {
|
|
45
|
+
/* Dialog */
|
|
46
|
+
.dialog {
|
|
47
|
+
text-align: left;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Divider of Dialog */
|
|
51
|
+
.dialog span.divider {
|
|
52
|
+
display: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Dialog Title */
|
|
56
|
+
.dialog-title {
|
|
57
|
+
font-size: 26px;
|
|
58
|
+
line-height: 32px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Dialog Message */
|
|
62
|
+
.dialog-message, .dialog-msg {
|
|
63
|
+
font-size: 20px;
|
|
64
|
+
line-height: 32px;
|
|
65
|
+
margin: 12px 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Dialog Buttons */
|
|
69
|
+
.dialog-buttons {
|
|
70
|
+
flex-direction: row;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@media (width >= 1024px) and (width <= 2560px) {
|
|
75
|
+
/* Dialogs */
|
|
76
|
+
#dialogs, .Dialogs {
|
|
77
|
+
padding: var(--dialogs-container-padding, 24px 48px);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Dialog */
|
|
81
|
+
.dialog {
|
|
82
|
+
padding: 12px 24px;
|
|
83
|
+
text-align: left;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Divider of Dialog */
|
|
87
|
+
.dialog span.divider {
|
|
88
|
+
display: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Dialog Title */
|
|
92
|
+
.dialog-title {
|
|
93
|
+
font-size: 26px;
|
|
94
|
+
line-height: 32px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Dialog Message */
|
|
98
|
+
.dialog-message, .dialog-msg {
|
|
99
|
+
font-size: 20px;
|
|
100
|
+
line-height: 26px;
|
|
101
|
+
margin: 12px 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Dialog Buttons */
|
|
105
|
+
.dialog-buttons {
|
|
106
|
+
flex-direction: row;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
/* Toasts */
|
|
111
|
+
#toasts {
|
|
112
|
+
padding: 5rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Toast Emojis */
|
|
116
|
+
.toast-emoji {
|
|
117
|
+
width: var(--toast-emoji-size, 42px);
|
|
118
|
+
height: var(--toast-emoji-size, 42px);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
package/css/dialog.css
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ab-nextjs-components — chrome styles moved out of ab-nextjs-theme
|
|
3
|
+
* Copyright (c) 2026 Abraham Ukachi. MIT.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* Dialogs */
|
|
7
|
+
#dialogs, .Dialogs {
|
|
8
|
+
display: flex;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
align-items: center;
|
|
11
|
+
z-index: 101;
|
|
12
|
+
pointer-events: none;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
padding: var(--dialogs-padding, 24px);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/* Dialog */
|
|
19
|
+
#dialog, .dialog {
|
|
20
|
+
width: 100%;
|
|
21
|
+
position: relative;
|
|
22
|
+
user-select: none;
|
|
23
|
+
pointer-events: auto;
|
|
24
|
+
background: var(--dialog-color-background, var(--background-color));
|
|
25
|
+
color: var(--dialog-color-on-background, var(--on-background-color));
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
padding: var(--dialog-padding, 8px 16px);
|
|
28
|
+
border-radius: var(--dialog-border-radius, 12px);
|
|
29
|
+
|
|
30
|
+
/* opacity: 0; */
|
|
31
|
+
text-align: center;
|
|
32
|
+
max-width: 512px;
|
|
33
|
+
|
|
34
|
+
--fade-in-animation-delay: var(--dialog-animation-delay, 200ms);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Dialog Title */
|
|
38
|
+
.dialog-title {
|
|
39
|
+
margin: 16px 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Dialog Message */
|
|
43
|
+
.dialog-message, .dialog-msg {
|
|
44
|
+
opacity: var(--opacity-text-inactive);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Dialog Buttons */
|
|
48
|
+
.dialog-buttons {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
justify-content: center;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Each Dialog Button */
|
|
55
|
+
a.dialog-button, a[role="button"].dialog-button {
|
|
56
|
+
width: 100%;
|
|
57
|
+
height: auto;
|
|
58
|
+
border-radius: 8px;
|
|
59
|
+
margin: 8px 0;
|
|
60
|
+
background: var(--dialog-button-background-color, inherit);
|
|
61
|
+
color: var(--dialog-button-text-color, inherit);
|
|
62
|
+
font-size: 20px;
|
|
63
|
+
line-height: 50px;
|
|
64
|
+
letter-spacing: 1px;
|
|
65
|
+
font-weight: bold;
|
|
66
|
+
text-align: center;
|
|
67
|
+
align-items: center;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Dialog Button with default attribute */
|
|
71
|
+
a.dialog-button[data-default], a[role="button"][data-default].dialog-button {
|
|
72
|
+
color: var(--dialog-button-color-default, var(--primary-color));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Hover on Dialog Button */
|
|
76
|
+
a.dialog-button:hover, a[role="button"].dialog-button:hover {
|
|
77
|
+
background: var(--dialog-button-color-hover, var(--surface-variant-color));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
a.dialog-button:focus, a[role="button"].dialog-button:focus {
|
|
81
|
+
outline: 3px solid;
|
|
82
|
+
outline-color: var(--tertiary-color);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
/* dialog-list */
|
|
87
|
+
ul.dialog-list {
|
|
88
|
+
width: var(--dialog-list-width, 100%);
|
|
89
|
+
height: var(--dialog-list-height, auto);
|
|
90
|
+
max-height: var(--dialog-list-max-height, 300px);
|
|
91
|
+
overflow: hidden auto;
|
|
92
|
+
margin: var(--dialog-list-margin, 2rem 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/* dialog-list-item */
|
|
97
|
+
li.dialog-list-item {
|
|
98
|
+
position: relative;
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
width: var(--dialog-list-item-width, 100%);
|
|
101
|
+
height: var(--dialog-list-item-height, auto);
|
|
102
|
+
padding: var(--dialog-list-item-padding, 0.75rem 1rem);
|
|
103
|
+
transform: scale(1);
|
|
104
|
+
transform-origin: left;
|
|
105
|
+
opacity: var(--opacity-dialog-list-item, 0.68);
|
|
106
|
+
user-select: none;
|
|
107
|
+
transition: all 0.2s ease-in-out;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
li.dialog-list-item:hover {
|
|
111
|
+
opacity: var(--opacity-dialog-list-item-hover, 1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
li.dialog-list-item[data-shrinks]:active {
|
|
116
|
+
transform: scale(0.9);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
li.dialog-list-item .checkbox,
|
|
121
|
+
li.dialog-list-item .radio {
|
|
122
|
+
width: var(--dialog-list-item-checkbox-size, 20px);
|
|
123
|
+
height: var(--dialog-list-item-checkbox-size, 20px);
|
|
124
|
+
outline: 2px solid var(--dialog-list-item-checkbox-outline-color, var(--outline-color));
|
|
125
|
+
margin-right: 1.25rem;
|
|
126
|
+
outline-offset: 3px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
li.dialog-list-item .checkbox {
|
|
130
|
+
border-radius: 10%;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
li.dialog-list-item .radio {
|
|
134
|
+
border-radius: 50%;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
/* [data-selected="true"] dialog list item */
|
|
139
|
+
li.dialog-list-item[data-selected="true"] {
|
|
140
|
+
font-weight: bold;
|
|
141
|
+
opacity: var(--opacity-dialog-list-item-selected, 1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
li.dialog-list-item[data-selected="true"] .checkbox,
|
|
146
|
+
li.dialog-list-item[data-selected="true"] .radio {
|
|
147
|
+
outline-color: var(--dialog-list-item-checkbox-selected-border-color, var(--primary-color));
|
|
148
|
+
background: var(--dialog-list-item-checkbox-selected-background-color, var(--primary-color));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* TODO: dialog-list-item-link */
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
/* Dialogs */
|
|
162
|
+
#dialogs, .Dialogs {
|
|
163
|
+
display: flex;
|
|
164
|
+
justify-content: center;
|
|
165
|
+
align-items: center;
|
|
166
|
+
z-index: 101;
|
|
167
|
+
pointer-events: none;
|
|
168
|
+
box-sizing: border-box;
|
|
169
|
+
padding: var(--dialogs-padding, 24px);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Dialog */
|
|
173
|
+
#dialog, .dialog {
|
|
174
|
+
width: var(--dialog-width, 80%);
|
|
175
|
+
position: absolute;
|
|
176
|
+
user-select: none;
|
|
177
|
+
pointer-events: auto;
|
|
178
|
+
background: var(--dialog-color-background, var(--background-color));
|
|
179
|
+
color: var(--dialog-color-on-background, var(--on-background-color));
|
|
180
|
+
box-sizing: border-box;
|
|
181
|
+
padding: var(--dialog-padding, 8px 16px);
|
|
182
|
+
border-radius: var(--dialog-border-radius, 12px);
|
|
183
|
+
|
|
184
|
+
/* opacity: 0; */
|
|
185
|
+
text-align: center;
|
|
186
|
+
max-width: var(--dialog-max-width, 320px);
|
|
187
|
+
|
|
188
|
+
--fade-in-animation-delay: var(--dialog-animation-delay, 200ms);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Dialog Title */
|
|
192
|
+
.dialog-title {
|
|
193
|
+
margin: 16px 0;
|
|
194
|
+
font-size: 24px;
|
|
195
|
+
font-weight: bold;
|
|
196
|
+
line-height: 30px;
|
|
197
|
+
letter-spacing: 0.8px;
|
|
198
|
+
word-spacing: normal;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Dialog Message */
|
|
202
|
+
.dialog-message, .dialog-msg {
|
|
203
|
+
opacity: var(--opacity-text-inactive);
|
|
204
|
+
font-size: 16px;
|
|
205
|
+
line-height: 24px;
|
|
206
|
+
letter-spacing: 0.20px;
|
|
207
|
+
word-spacing: 1.5px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Dialog Buttons */
|
|
211
|
+
.dialog-buttons {
|
|
212
|
+
display: flex;
|
|
213
|
+
flex-direction: column;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Each Dialog Button */
|
|
218
|
+
a.dialog-button, a[role="button"].dialog-button {
|
|
219
|
+
width: 100%;
|
|
220
|
+
height: auto;
|
|
221
|
+
border-radius: 8px;
|
|
222
|
+
margin: 8px 0;
|
|
223
|
+
background: var(--dialog-button-background-color, inherit);
|
|
224
|
+
color: var(--dialog-button-text-color, inherit);
|
|
225
|
+
font-size: 20px;
|
|
226
|
+
line-height: 50px;
|
|
227
|
+
letter-spacing: 1px;
|
|
228
|
+
font-weight: bold;
|
|
229
|
+
text-align: center;
|
|
230
|
+
align-items: center;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Dialog Button with default attribute */
|
|
234
|
+
a.dialog-button[data-default], a[role="button"][data-default].dialog-button {
|
|
235
|
+
color: var(--dialog-button-color-default, var(--primary-color));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* Hover on Dialog Button */
|
|
239
|
+
a.dialog-button:hover, a[role="button"].dialog-button:hover {
|
|
240
|
+
background: var(--dialog-button-color-hover, var(--surface-variant-color));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
a.dialog-button:focus, a[role="button"].dialog-button:focus {
|
|
244
|
+
outline: 3px solid;
|
|
245
|
+
outline-color: var(--tertiary-color);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* === End of Dialog Styles === */
|
package/css/doodle.css
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ab-nextjs-components — chrome styles moved out of ab-nextjs-theme
|
|
3
|
+
* Copyright (c) 2026 Abraham Ukachi. MIT.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
[data-content] > .container[data-empty="true"] .doodle {
|
|
7
|
+
width: var(--empty-doodle-size, 200px);
|
|
8
|
+
height: var(--empty-doodle-size, 200px);
|
|
9
|
+
background-color: inherit;
|
|
10
|
+
margin-top: -48px;
|
|
11
|
+
background-image: var(--empty-doodle-image, none); /* NOTE: no doodle is shipped; set `--empty-doodle-image` in your app */
|
|
12
|
+
background-repeat: no-repeat;
|
|
13
|
+
background-position: center;
|
|
14
|
+
background-size: cover;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
[data-content] > .container[data-empty="true"] .doodle[data-mask] {
|
|
19
|
+
background-image: none;
|
|
20
|
+
background-color: var(--empty-doodle-color, var(--on-background-color));
|
|
21
|
+
opacity: var(--empty-doodle-opacity, var(--opacity-md-icon-inactive));
|
|
22
|
+
mask-image: var(--empty-doodle-image, none);
|
|
23
|
+
mask-repeat: no-repeat;
|
|
24
|
+
mask-position: center;
|
|
25
|
+
mask-size: cover;
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
[data-content] > .container[data-empty="true"] .ddd {
|
|
31
|
+
width: var(--ddd-size, 200px);
|
|
32
|
+
height: var(--ddd-size, 200px);
|
|
33
|
+
background-color: inherit;
|
|
34
|
+
background-image: var(--ddd-image-url, none); /* NOTE: no 3D image is shipped; set `--ddd-image-url` in your app */
|
|
35
|
+
background-repeat: no-repeat;
|
|
36
|
+
background-position: center;
|
|
37
|
+
background-size: cover;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
package/css/logo.css
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ab-nextjs-components — chrome styles moved out of ab-nextjs-theme
|
|
3
|
+
* Copyright (c) 2026 Abraham Ukachi. MIT.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* App Logo of any ASIDE */
|
|
7
|
+
aside > span.app-logo {
|
|
8
|
+
--app-logo-color: var(--outline-color);
|
|
9
|
+
|
|
10
|
+
opacity: var(--opacity-md-icon-disabled);
|
|
11
|
+
|
|
12
|
+
--app-logo-size: 50%;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/* App Logo | SPAN */
|
|
18
|
+
span.app-logo {
|
|
19
|
+
display: block;
|
|
20
|
+
position: relative;
|
|
21
|
+
width: var(--app-logo-size, 32px);
|
|
22
|
+
height: var(--app-logo-size, 32px);
|
|
23
|
+
max-width: var(--app-logo-size-max, 512px);
|
|
24
|
+
max-height: var(--app-logo-size-max, 512px);
|
|
25
|
+
background-color: var(--app-logo-color, var(--on-background-color));
|
|
26
|
+
opacity: var(--app-logo-opacity, inherit);
|
|
27
|
+
mask: var(--app-logo-url, url('../assets/logos/ab-logo.svg')) no-repeat 50% 50%;
|
|
28
|
+
mask-size: cover;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Outlined App Logo | SPAN
|
|
32
|
+
* NOTE: no outlined logo is shipped; set `--app-logo-outline-url` in your app (falls back to the filled logo) */
|
|
33
|
+
span[data-type="outlined"].app-logo {
|
|
34
|
+
mask: var(--app-logo-outline-url, var(--app-logo-url, url('../assets/logos/ab-logo.svg'))) no-repeat 50% 50%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/* App Logo in an Nav Link */
|
|
39
|
+
a.nav-link span.app-logo {
|
|
40
|
+
--app-logo-size: var(--side-bar-logo-size, 48px);
|
|
41
|
+
--app-logo-color: var(--side-bar-logo-color, var(--primary-color));
|
|
42
|
+
--app-logo-opacity: inherit;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
/* App Logo in an active Nav Link */
|
|
48
|
+
a.nav-link[data-active="true"] span.app-logo {
|
|
49
|
+
background-color: var(--app-logo-color-active, var(--primary-color));
|
|
50
|
+
opacity: inherit;
|
|
51
|
+
}
|
|
52
|
+
|
package/css/spinner.css
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ab-nextjs-components — chrome styles moved out of ab-nextjs-theme
|
|
3
|
+
* Copyright (c) 2026 Abraham Ukachi. MIT.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* ==== SPINNERS ==== */
|
|
7
|
+
|
|
8
|
+
span.spinner {
|
|
9
|
+
display: block;
|
|
10
|
+
position: relative;
|
|
11
|
+
width: var(--spinner-size, 48px);
|
|
12
|
+
height: var(--spinner-size, 48px);
|
|
13
|
+
background-color: var(--spinner-color, var(--on-background-color));
|
|
14
|
+
opacity: var(--spinner-opacity, inherit);
|
|
15
|
+
mask: var(--spinner-url, url("../assets/spinners/3-dots-scale.svg")) no-repeat 50% 50%;
|
|
16
|
+
mask-size: cover;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
span.spinner.dots-3 {
|
|
20
|
+
--spinner-url: url("../assets/spinners/3-dots-scale.svg");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
span.spinner.dots-12 {
|
|
24
|
+
--spinner-url: url("../assets/spinners/12-dots-scale-rotate.svg");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/* === END of SPINNERS === */
|
package/css/toast.css
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ab-nextjs-components — chrome styles moved out of ab-nextjs-theme
|
|
3
|
+
* Copyright (c) 2026 Abraham Ukachi. MIT.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* === Toasts Styles === */
|
|
7
|
+
|
|
8
|
+
/* All Toasts */
|
|
9
|
+
#toasts,
|
|
10
|
+
main > .Toasts,
|
|
11
|
+
aside > .Toasts {
|
|
12
|
+
z-index: 100;
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: flex-end; /* b4: end */
|
|
17
|
+
pointer-events: none;
|
|
18
|
+
padding: 3rem 1rem;
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
|
|
21
|
+
--fade-in-animation-duration: 700ms;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/* Toast */
|
|
26
|
+
.toast {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: row;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
align-items: center;
|
|
31
|
+
background-color: var(--on-background-color);
|
|
32
|
+
color: var(--background-color);
|
|
33
|
+
padding: 0.5rem 1.25rem;
|
|
34
|
+
border-radius: 32px;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
font-size: 1rem;
|
|
37
|
+
margin: 0 auto;
|
|
38
|
+
opacity: var(--opacity-md-active);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
/* Toast Emojis */
|
|
43
|
+
.toast-emoji {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: row;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
align-items: center;
|
|
48
|
+
width: 24px;
|
|
49
|
+
height: 24px;
|
|
50
|
+
background-color: transparent;
|
|
51
|
+
margin-right: 0.25rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.toast-emoji:not([data-emoji-hidden]) {
|
|
55
|
+
background-image: var(--toast-emoji-image);
|
|
56
|
+
background-repeat: no-repeat;
|
|
57
|
+
background-position: center center;
|
|
58
|
+
background-size: cover;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/* Success - Toast Emoji
|
|
63
|
+
* NOTE: no image is shipped; set `--toast-emoji-success-image` in your app (e.g. a GIF from your `public/` folder) */
|
|
64
|
+
.toast-emoji.success,
|
|
65
|
+
.toast-emoji.st {
|
|
66
|
+
--toast-emoji-image: var(--toast-emoji-success-image, none);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
/* ==== End of Toasts Styles === */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ab-nextjs-components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "\ud83d\ude80 A lightweight collection of server & client React components created by Abraham Ukachi, and optimized for Next.js applications \ud83d\ude0e.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"lint": "eslint . --fix",
|
|
18
18
|
"test": "vitest run",
|
|
19
|
-
"check:publish": "node scripts/check-publish-deps.mjs",
|
|
20
|
-
"prepublishOnly": "node scripts/check-publish-deps.mjs"
|
|
19
|
+
"check:publish": "node scripts/check-publish-deps.mjs && node scripts/check-css-assets.mjs",
|
|
20
|
+
"prepublishOnly": "node scripts/check-publish-deps.mjs && node scripts/check-css-assets.mjs",
|
|
21
|
+
"check:css-assets": "node scripts/check-css-assets.mjs"
|
|
21
22
|
},
|
|
22
23
|
"keywords": [
|
|
23
24
|
"react",
|
|
@@ -102,7 +103,10 @@
|
|
|
102
103
|
"ab-switch-back",
|
|
103
104
|
"ab-demo-box",
|
|
104
105
|
"ab-demo-code",
|
|
105
|
-
"ab-logo/ab-logo.svg"
|
|
106
|
+
"ab-logo/ab-logo.svg",
|
|
107
|
+
"styles.css",
|
|
108
|
+
"css",
|
|
109
|
+
"assets"
|
|
106
110
|
],
|
|
107
111
|
"exports": {
|
|
108
112
|
".": {
|
|
@@ -163,7 +167,10 @@
|
|
|
163
167
|
"./server/ab-text": "./server/ab-text/index.tsx",
|
|
164
168
|
"./server/ab-test-hello": "./server/ab-test-hello/index.tsx",
|
|
165
169
|
"./server/ab-key-feature": "./server/ab-key-feature/index.tsx",
|
|
166
|
-
"./server/ab-switch-back": "./server/ab-switch-back/index.tsx"
|
|
170
|
+
"./server/ab-switch-back": "./server/ab-switch-back/index.tsx",
|
|
171
|
+
"./styles.css": "./styles.css",
|
|
172
|
+
"./css/*": "./css/*",
|
|
173
|
+
"./assets/*": "./assets/*"
|
|
167
174
|
},
|
|
168
175
|
"peerDependenciesMeta": {
|
|
169
176
|
"ab-nextjs-theme": {
|
package/server/ab-logo/index.tsx
CHANGED
|
@@ -72,7 +72,7 @@ const AbLogo = ({
|
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
// With no `mask`/`src`, leave the mask to `ab-nextjs-
|
|
75
|
+
// With no `mask`/`src`, leave the mask to `ab-nextjs-components` chrome (`--app-logo-url` / `.app-logo`).
|
|
76
76
|
// When `mask` or `src` is set, apply it inline (src becomes a CSS mask url).
|
|
77
77
|
const maskValue = mask ?? (src ? `url('${src}') no-repeat 50% 50%` : undefined);
|
|
78
78
|
return (
|
|
@@ -36,7 +36,7 @@ const AbNavbar = ({
|
|
|
36
36
|
links = [],
|
|
37
37
|
}: AbNavbarProps): ReactElement => {
|
|
38
38
|
return (
|
|
39
|
-
<nav data-type={type} className={clsx('AbNavbar', styles.abNavbar, className)} aria-label="Primary">
|
|
39
|
+
<nav data-type={type} data-ab-part="bottomBar" className={clsx('AbNavbar', styles.abNavbar, className)} aria-label="Primary">
|
|
40
40
|
<ul className={styles.abNavbarList}>
|
|
41
41
|
{links.map((link) => {
|
|
42
42
|
const active = page === link.value;
|
package/styles.css
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ab-nextjs-components — component chrome (dialog, toast, logo, spinner, doodle)
|
|
3
|
+
*
|
|
4
|
+
* Import after ab-nextjs-theme/styles.css:
|
|
5
|
+
* @import "ab-nextjs-theme/styles.css";
|
|
6
|
+
* @import "ab-nextjs-components/styles.css";
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
@import "./css/dialog.css";
|
|
10
|
+
@import "./css/toast.css";
|
|
11
|
+
@import "./css/logo.css";
|
|
12
|
+
@import "./css/spinner.css";
|
|
13
|
+
@import "./css/doodle.css";
|
|
14
|
+
@import "./css/chrome-mq.css";
|