ab-nextjs-components 0.1.1 → 0.1.3

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/LICENSE +1 -1
  3. package/README.md +91 -15
  4. package/ab-avatar/index.tsx +90 -0
  5. package/ab-avatar/styles.module.css +62 -0
  6. package/ab-balloon/index.tsx +55 -0
  7. package/ab-balloon/styles.module.css +12 -0
  8. package/ab-brand/index.tsx +88 -0
  9. package/ab-brand/styles.module.css +54 -0
  10. package/ab-button/index.tsx +164 -0
  11. package/ab-button/styles.module.css +63 -0
  12. package/ab-collapsible/index.tsx +188 -0
  13. package/ab-collapsible/styles.module.css +98 -0
  14. package/ab-color/index.tsx +84 -0
  15. package/ab-color/styles.module.css +51 -0
  16. package/ab-demo-box/index.tsx +38 -0
  17. package/ab-demo-box/styles.module.css +13 -0
  18. package/ab-demo-code/index.tsx +62 -0
  19. package/ab-demo-code/styles.module.css +41 -0
  20. package/ab-icon/index.tsx +63 -0
  21. package/ab-icon/styles.module.css +27 -0
  22. package/ab-icon-button/index.tsx +107 -0
  23. package/ab-icon-button/styles.module.css +64 -0
  24. package/ab-image/index.tsx +83 -0
  25. package/ab-image/styles.module.css +27 -0
  26. package/ab-info/index.tsx +89 -0
  27. package/ab-info/styles.module.css +87 -0
  28. package/ab-input/index.tsx +277 -0
  29. package/ab-input/styles.module.css +101 -0
  30. package/ab-key-feature/index.tsx +60 -0
  31. package/ab-key-feature/styles.module.css +47 -0
  32. package/ab-like-button/index.tsx +116 -0
  33. package/ab-like-button/styles.module.css +60 -0
  34. package/ab-logo/index.tsx +94 -0
  35. package/ab-logo/styles.module.css +30 -0
  36. package/ab-main-headline/index.tsx +57 -0
  37. package/ab-main-headline/styles.module.css +24 -0
  38. package/ab-menu/index.tsx +119 -0
  39. package/ab-menu/styles.module.css +99 -0
  40. package/ab-name/index.tsx +49 -0
  41. package/ab-name/styles.module.css +14 -0
  42. package/ab-navbar/index.tsx +68 -0
  43. package/ab-navbar/styles.module.css +35 -0
  44. package/ab-page-paginator/index.tsx +130 -0
  45. package/ab-page-paginator/styles.module.css +47 -0
  46. package/ab-page-switcher/index.tsx +121 -0
  47. package/ab-page-switcher/styles.module.css +37 -0
  48. package/ab-preview/index.tsx +78 -0
  49. package/ab-preview/styles.module.css +26 -0
  50. package/ab-price/index.tsx +109 -0
  51. package/ab-price/styles.module.css +36 -0
  52. package/ab-price-tag/index.tsx +70 -0
  53. package/ab-price-tag/styles.module.css +81 -0
  54. package/ab-product/index.tsx +128 -0
  55. package/ab-product/styles.module.css +44 -0
  56. package/ab-product-item/index.tsx +78 -0
  57. package/ab-product-item/styles.module.css +21 -0
  58. package/ab-progress-chips/index.tsx +105 -0
  59. package/ab-progress-chips/styles.module.css +43 -0
  60. package/ab-search-result/index.tsx +79 -0
  61. package/ab-search-result/styles.module.css +48 -0
  62. package/ab-searchbar/index.tsx +150 -0
  63. package/ab-searchbar/styles.module.css +77 -0
  64. package/ab-sidebar/index.tsx +72 -0
  65. package/ab-sidebar/styles.module.css +41 -0
  66. package/ab-switch-back/index.tsx +72 -0
  67. package/ab-switch-back/styles.module.css +45 -0
  68. package/ab-tab/index.tsx +101 -0
  69. package/ab-tab/styles.module.css +65 -0
  70. package/ab-tabs/index.tsx +45 -0
  71. package/ab-tabs/styles.module.css +17 -0
  72. package/ab-test-hello/index.tsx +37 -0
  73. package/ab-test-hello/styles.module.css +25 -0
  74. package/ab-text/index.tsx +36 -0
  75. package/ab-text/styles.module.css +3 -0
  76. package/index.ts +117 -0
  77. package/package.json +152 -7
  78. package/server/ab-avatar/index.tsx +85 -0
  79. package/server/ab-avatar/styles.module.css +62 -0
  80. package/server/ab-badge/index.tsx +66 -0
  81. package/server/ab-badge/styles.module.css +39 -0
  82. package/server/ab-brand/index.tsx +87 -0
  83. package/server/ab-brand/styles.module.css +54 -0
  84. package/server/ab-button/index.tsx +144 -0
  85. package/server/ab-button/styles.module.css +63 -0
  86. package/server/ab-icon/index.tsx +61 -0
  87. package/server/ab-icon/styles.module.css +27 -0
  88. package/server/ab-key-feature/index.tsx +59 -0
  89. package/server/ab-key-feature/styles.module.css +47 -0
  90. package/server/ab-logo/index.tsx +93 -0
  91. package/server/ab-logo/styles.module.css +30 -0
  92. package/server/ab-name/index.tsx +48 -0
  93. package/server/ab-name/styles.module.css +14 -0
  94. package/server/ab-nav-link/index.tsx +60 -0
  95. package/server/ab-nav-link/styles.module.css +32 -0
  96. package/server/ab-navbar/index.tsx +64 -0
  97. package/server/ab-navbar/styles.module.css +35 -0
  98. package/server/ab-polygon/index.tsx +45 -0
  99. package/server/ab-polygon/styles.module.css +11 -0
  100. package/server/ab-sidebar/index.tsx +68 -0
  101. package/server/ab-sidebar/styles.module.css +41 -0
  102. package/server/ab-switch-back/index.tsx +71 -0
  103. package/server/ab-switch-back/styles.module.css +45 -0
  104. package/server/ab-test-hello/index.tsx +35 -0
  105. package/server/ab-test-hello/styles.module.css +22 -0
  106. package/server/ab-text/index.tsx +33 -0
  107. package/server/ab-text/styles.module.css +3 -0
  108. package/server/index.ts +27 -0
  109. package/types/index.d.ts +54 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ ### Unreleased (chore/upgrade-next-16 — Skip-override / Phase 5 components)
2
+
3
+ * **Port (client):** AbBrand, AbName, AbNavbar, AbSidebar, AbText, AbTestHello, AbKeyFeature, AbMainHeadline, AbPreview, AbProduct, AbProductItem, AbSwitchBack, AbDemoBox, AbDemoCode
4
+ * **Port (server):** AbBrand, AbName, AbNavbar, AbSidebar, AbNavLink, AbText, AbTestHello, AbKeyFeature, AbSwitchBack
5
+ * Select/MenuItem skipped — empty on Projects (GitHub-only historically)
6
+ * Product/Preview adapted (Dexie/providers/i18n stripped); usable props API kept
7
+ * Catalog **51 Done** (36 client / 15 server). Local only — no push.
8
+
9
+ # Changelog
10
+
11
+ ### Unreleased (chore/upgrade-next-16 — Phase 4 Port)
12
+
13
+ * **Port:** AbSearchbar, AbSearchResult, AbPagePaginator, AbPageSwitcher, AbProgressChips, AbPrice, AbPriceTag
14
+ * **Adapt:** AbImage (Dexie / offline / background-removal stripped; next/image + optional fallback)
15
+ * **Port (server):** AbPolygon
16
+ * Catalog 28 Done (22 client / 6 server). Plain CSS modules. Local only — no push.
17
+
18
+ ### Unreleased (chore/upgrade-next-16 — Phase 2 Adapt + Phase 3 Port)
19
+
20
+ * **Adapt:** **AbBalloon** (`title=` locked) — Input now uses real AbBalloon
21
+ * **Port:** AbAvatar (client + server), AbBadge (server), AbTab, AbTabs, AbMenu, AbCollapsible, AbColor, AbInfo, AbLikeButton
22
+ * Plain CSS modules; LYD brand/nav/i18n stripped; Select/MenuItem skipped (not on Projects)
23
+
24
+ ### Unreleased (chore/upgrade-next-16 — Phase 2)
25
+
26
+ * Ported **AbIcon** (client + server) — Material Symbols ligatures
27
+ * Ported **AbLogo** (client + server) — contained | outlined | hollow | naked; defaults from `ab-nextjs-icons` logos (`#a67c52`), optional `src` / `mask`
28
+ * Ported **AbButton** client from lyd-button; invented server (href → `<a>`, else submit/button; no onClick)
29
+ * Added minimal **AbIconButton** (Input dependency)
30
+ * Ported **AbInput** client — `useAbToggle` from `ab-nextjs-hooks/helpers/useAbToggle`; Balloon stub via `title=`; no console.logs
31
+ * Hard peers: next 16.3.4, react^19, clsx; soft optional peers: theme / icons / hooks / animations
32
+ * Plain CSS modules (no `@apply`); theme `data-*` attrs preserved on buttons
33
+
34
+ ### Unreleased (Phase 1)
35
+
36
+ * Next.js **16.3.4** / React 19 tooling scaffold + Pending catalog
37
+
38
+ ### 0.1.2
39
+
40
+ * Stub package version bump
package/LICENSE CHANGED
@@ -2,7 +2,7 @@ MIT License
2
2
  ~~~~~~~~~~~
3
3
  ab-nextjs-components
4
4
  ~~~~~~~~~~~
5
- Copyright (c) 2024 Abraham Ukachi. Ab UI / NextJS Components.
5
+ Copyright (c) 2024 Abraham Ukachi. The abElements Project.
6
6
 
7
7
  Permission is hereby granted, free of charge, to any person obtaining a copy
8
8
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,10 +1,49 @@
1
- `ab-nextjs-components`
1
+ <p align="center">
2
+ <!-- Ab - Logo - Light Mode -->
3
+ <a href="https://abraham-ukachi.vercel.app/#gh-light-mode-only" target="_blank">
4
+ <img src="./.github/ab-logo-light.svg" alt="Ab Logo on Light" width="64" height="64" />
5
+ </a>
6
+
7
+ <!-- Ab - Logo - Dark Mode -->
8
+ <a href="https://abraham-ukachi.vercel.app/#gh-dark-mode-only" target="_blank">
9
+ <img src="./.github/ab-logo-dark.svg" alt="Ab Logo on Dark" width="64" height="64" />
10
+ </a>
11
+
12
+ <!-- Next.js - Logo Name - Light Mode -->
13
+ <a href="https://nextjs.org/#gh-light-mode-only" target="_blank">
14
+ <img src="./.github/nextjs-logoname-light.svg" alt="Next.js LogoName on Light" width="192" height="64" />
15
+ </a>
16
+
17
+ <!-- Next.js - Logo Name - Dark Mode -->
18
+ <a href="https://nextjs.org/#gh-dark-mode-only" target="_blank">
19
+ <img src="./.github/nextjs-logoname-dark.svg" alt="Next.js LogoName on Dark" width="192" height="64" />
20
+ </a>
21
+
22
+ </p>
23
+
24
+
25
+ <p align="center">
26
+ <a href="https://ab-elements.vercel.app" target="_blank"><b>Checkout abElements &rarr;</b></a>
27
+ </p>
28
+
29
+
30
+ # `ab-nextjs-components`
2
31
 
3
32
  > IMPORTANT: This is a work in progress and subject to major changes until version 1.0.
4
33
 
34
+
5
35
  🚀 This is a lightweight collection of server & client React components created by [Abraham Ukachi](https://github.com/abraham-ukachi), and optimized for [Next.js](https://nextjs.org/docs) applications 😎.
6
36
 
7
37
 
38
+
39
+
40
+ ## Tooling (Phase 1)
41
+
42
+ - Next.js **16.3.4** / React **19** hard peers
43
+ - ESLint 9 flat + Vitest
44
+ - Package ships as TypeScript (`type: module`) with a `supportedComponents` catalog
45
+ - Phase 2 Adapt complete; Phase 4 + Skip-override (Phase 5 components) complete (local)
46
+
8
47
  ## Getting Started
9
48
 
10
49
  ### Installation
@@ -39,14 +78,42 @@ A list of all the **client components** and their current status:
39
78
 
40
79
  | No. | Name | File | Status |
41
80
  |:----|:-----|:-----|:-------|
42
- | 1 | *`AbLogo`* | **ab-logo/index.tsx** | Pending |
43
- | 2 | *`AbIcon`* | **ab-icon/index.tsx** | Pending |
44
- | 3 | *`AbButton`* | **ab-button/index.tsx** | Pending |
45
- | 4 | *`AbInput`* | **ab-input/index.tsx** | Pending |
46
-
47
- > NOTE:
48
-
49
-
81
+ | 1 | *`AbIcon`* | **ab-icon/index.tsx** | Done |
82
+ | 2 | *`AbLogo`* | **ab-logo/index.tsx** | Done |
83
+ | 3 | *`AbButton`* | **ab-button/index.tsx** | Done |
84
+ | 4 | *`AbIconButton`* | **ab-icon-button/index.tsx** | Done |
85
+ | 5 | *`AbInput`* | **ab-input/index.tsx** | Done |
86
+ | 6 | *`AbBalloon`* | **ab-balloon/index.tsx** | Done |
87
+ | 7 | *`AbAvatar`* | **ab-avatar/index.tsx** | Done |
88
+ | 8 | *`AbTab`* | **ab-tab/index.tsx** | Done |
89
+ | 9 | *`AbTabs`* | **ab-tabs/index.tsx** | Done |
90
+ | 10 | *`AbMenu`* | **ab-menu/index.tsx** | Done |
91
+ | 11 | *`AbCollapsible`* | **ab-collapsible/index.tsx** | Done |
92
+ | 12 | *`AbColor`* | **ab-color/index.tsx** | Done |
93
+ | 13 | *`AbInfo`* | **ab-info/index.tsx** | Done |
94
+ | 14 | *`AbLikeButton`* | **ab-like-button/index.tsx** | Done |
95
+ | 15 | *`AbSearchbar`* | **ab-searchbar/index.tsx** | Done |
96
+ | 16 | *`AbSearchResult`* | **ab-search-result/index.tsx** | Done |
97
+ | 17 | *`AbPagePaginator`* | **ab-page-paginator/index.tsx** | Done |
98
+ | 18 | *`AbPageSwitcher`* | **ab-page-switcher/index.tsx** | Done |
99
+ | 19 | *`AbProgressChips`* | **ab-progress-chips/index.tsx** | Done |
100
+ | 20 | *`AbPrice`* | **ab-price/index.tsx** | Done |
101
+ | 21 | *`AbPriceTag`* | **ab-price-tag/index.tsx** | Done |
102
+ | 22 | *`AbImage`* | **ab-image/index.tsx** | Done |
103
+ | 23 | *`AbBrand`* | **ab-brand/index.tsx** | Done |
104
+ | 24 | *`AbName`* | **ab-name/index.tsx** | Done |
105
+ | 25 | *`AbNavbar`* | **ab-navbar/index.tsx** | Done |
106
+ | 26 | *`AbSidebar`* | **ab-sidebar/index.tsx** | Done |
107
+ | 27 | *`AbText`* | **ab-text/index.tsx** | Done |
108
+ | 28 | *`AbTestHello`* | **ab-test-hello/index.tsx** | Done |
109
+ | 29 | *`AbKeyFeature`* | **ab-key-feature/index.tsx** | Done |
110
+ | 30 | *`AbMainHeadline`* | **ab-main-headline/index.tsx** | Done |
111
+ | 31 | *`AbPreview`* | **ab-preview/index.tsx** | Done |
112
+ | 32 | *`AbProduct`* | **ab-product/index.tsx** | Done |
113
+ | 33 | *`AbProductItem`* | **ab-product-item/index.tsx** | Done |
114
+ | 34 | *`AbSwitchBack`* | **ab-switch-back/index.tsx** | Done |
115
+ | 35 | *`AbDemoBox`* | **ab-demo-box/index.tsx** | Done |
116
+ | 36 | *`AbDemoCode`* | **ab-demo-code/index.tsx** | Done |
50
117
 
51
118
  ## Server Components
52
119
 
@@ -54,12 +121,21 @@ A list of all the **server components** and their current status:
54
121
 
55
122
  | No. | Name | File | Status |
56
123
  |:----|:-----|:-----|:-------|
57
- | 1 | *`AbLogo`* | **server/ab-logo/index.tsx** | Pending |
58
- | 2 | *`AbIcon`* | **server/ab-icon/index.tsx** | Pending |
59
- | 3 | *`AbButton`* | **server/ab-button/index.tsx** | Pending |
60
-
61
- > NOTE:
62
-
124
+ | 1 | *`AbIcon`* | **server/ab-icon/index.tsx** | Done |
125
+ | 2 | *`AbLogo`* | **server/ab-logo/index.tsx** | Done |
126
+ | 3 | *`AbButton`* | **server/ab-button/index.tsx** | Done |
127
+ | 4 | *`AbAvatar`* | **server/ab-avatar/index.tsx** | Done |
128
+ | 5 | *`AbBadge`* | **server/ab-badge/index.tsx** | Done |
129
+ | 6 | *`AbPolygon`* | **server/ab-polygon/index.tsx** | Done |
130
+ | 7 | *`AbBrand`* | **server/ab-brand/index.tsx** | Done |
131
+ | 8 | *`AbName`* | **server/ab-name/index.tsx** | Done |
132
+ | 9 | *`AbNavbar`* | **server/ab-navbar/index.tsx** | Done |
133
+ | 10 | *`AbSidebar`* | **server/ab-sidebar/index.tsx** | Done |
134
+ | 11 | *`AbNavLink`* | **server/ab-nav-link/index.tsx** | Done |
135
+ | 12 | *`AbText`* | **server/ab-text/index.tsx** | Done |
136
+ | 13 | *`AbTestHello`* | **server/ab-test-hello/index.tsx** | Done |
137
+ | 14 | *`AbKeyFeature`* | **server/ab-key-feature/index.tsx** | Done |
138
+ | 15 | *`AbSwitchBack`* | **server/ab-switch-back/index.tsx** | Done |
63
139
 
64
140
 
65
141
  ## Learn More abElements
@@ -0,0 +1,90 @@
1
+ /*
2
+ * @license MIT
3
+ * ~~~~~~~~~~~~
4
+ * ab-nextjs-components
5
+ * ~~~~~~~~~~~~
6
+ * Copyright (c) 2026 Abraham Ukachi. The abElements Project.
7
+ *
8
+ * @project: ab-nextjs-components
9
+ * @name: AbAvatar - Client (port of lyd-avatar)
10
+ * @file: ab-avatar/index.tsx
11
+ */
12
+
13
+ 'use client';
14
+
15
+ import type { ReactElement } from 'react';
16
+ import Image from 'next/image';
17
+ import Link from 'next/link';
18
+ import clsx from 'clsx';
19
+ import styles from './styles.module.css';
20
+
21
+ export interface AbAvatarProps {
22
+ isConnected: boolean;
23
+ isOnline: boolean;
24
+ icon?: 'person' | 'group';
25
+ version?: number;
26
+ image?: string;
27
+ label?: string;
28
+ className?: string;
29
+ width?: number;
30
+ height?: number;
31
+ /** Connected href (default `/`). Offline href (default `/`). No app-brand routes. */
32
+ href?: string;
33
+ offlineHref?: string;
34
+ isActive?: boolean;
35
+ noIndicator?: boolean;
36
+ onClick?: () => void;
37
+ }
38
+
39
+ const AbAvatar = ({
40
+ isConnected,
41
+ isOnline,
42
+ icon = 'person',
43
+ version = 1,
44
+ image,
45
+ label = 'Avatar',
46
+ className,
47
+ width = 48,
48
+ height = 48,
49
+ href = '/',
50
+ offlineHref = '/',
51
+ isActive = false,
52
+ noIndicator = false,
53
+ onClick,
54
+ }: AbAvatarProps): ReactElement => {
55
+ const src = image ?? `/assets/images/avatars/avatar_${version}.png`;
56
+
57
+ return (
58
+ <div className={clsx('AbAvatar', styles.abAvatar, className)} onClick={onClick}>
59
+ {isConnected ? (
60
+ <Link href={href} className={clsx(styles.abAvatarLink, isActive && styles.isActive)}>
61
+ <span className={styles.abAvatarImageWrap}>
62
+ <Image
63
+ src={src}
64
+ className={styles.abAvatarImage}
65
+ width={width}
66
+ height={height}
67
+ alt={label}
68
+ />
69
+ {!noIndicator ? (
70
+ <span
71
+ className={clsx(styles.abAvatarIndicator, !isOnline && styles.isOffline)}
72
+ aria-hidden
73
+ />
74
+ ) : null}
75
+ </span>
76
+ </Link>
77
+ ) : (
78
+ <Link href={offlineHref} className={styles.abAvatarOffline}>
79
+ <span className={clsx('material-symbols-rounded', styles.abAvatarIcon)} aria-hidden>
80
+ {icon}
81
+ </span>
82
+ {label ? <span className={styles.abAvatarLabel}>{label}</span> : null}
83
+ </Link>
84
+ )}
85
+ </div>
86
+ );
87
+ };
88
+
89
+ export default AbAvatar;
90
+ export { AbAvatar };
@@ -0,0 +1,62 @@
1
+ /* AbAvatar — plain CSS (no Tailwind apply) */
2
+ .abAvatar {
3
+ display: flex;
4
+ flex-direction: column;
5
+ width: auto;
6
+ height: auto;
7
+ pointer-events: auto;
8
+ }
9
+
10
+ .abAvatarLink {
11
+ position: relative;
12
+ display: inline-flex;
13
+ text-decoration: none;
14
+ opacity: 1;
15
+ }
16
+
17
+ .isActive {
18
+ opacity: 0.9;
19
+ }
20
+
21
+ .abAvatarImageWrap {
22
+ position: relative;
23
+ display: inline-flex;
24
+ }
25
+
26
+ .abAvatarImage {
27
+ border-radius: 9999px;
28
+ object-fit: cover;
29
+ }
30
+
31
+ .abAvatarIndicator {
32
+ position: absolute;
33
+ right: 0;
34
+ bottom: 0;
35
+ width: 1rem;
36
+ height: 1rem;
37
+ border-radius: 9999px;
38
+ border: 2px solid var(--ab-surface, #fff);
39
+ background: #22c55e;
40
+ }
41
+
42
+ .abAvatarIndicator.isOffline {
43
+ background: #ef4444;
44
+ }
45
+
46
+ .abAvatarOffline {
47
+ display: inline-flex;
48
+ flex-direction: column;
49
+ align-items: center;
50
+ gap: 0.25rem;
51
+ text-decoration: none;
52
+ color: inherit;
53
+ }
54
+
55
+ .abAvatarIcon {
56
+ font-size: 1.75rem;
57
+ line-height: 1;
58
+ }
59
+
60
+ .abAvatarLabel {
61
+ font-size: 0.75rem;
62
+ }
@@ -0,0 +1,55 @@
1
+ /*
2
+ * @license MIT
3
+ * ~~~~~~~~~~~~
4
+ * ab-nextjs-components
5
+ * ~~~~~~~~~~~~
6
+ * Copyright (c) 2026 Abraham Ukachi. The abElements Project.
7
+ *
8
+ * @project: ab-nextjs-components
9
+ * @name: AbBalloon - Client (Adapt — title= locked)
10
+ * @file: ab-balloon/index.tsx
11
+ */
12
+
13
+ 'use client';
14
+
15
+ import type { ReactElement, ReactNode } from 'react';
16
+ import clsx from 'clsx';
17
+ import styles from './styles.module.css';
18
+
19
+ export interface AbBalloonProps {
20
+ /** Native tooltip text — locked API (`title=`). */
21
+ title?: string;
22
+ className?: string;
23
+ hidden?: boolean;
24
+ disabled?: boolean;
25
+ children: ReactNode;
26
+ }
27
+
28
+ /**
29
+ * AbBalloon — Phase 2 Adapt.
30
+ * Uses the native `title=` tooltip (locked). Not a port of the LYD hover balloon.
31
+ */
32
+ const AbBalloon = ({
33
+ title,
34
+ className,
35
+ hidden = false,
36
+ disabled = false,
37
+ children,
38
+ }: AbBalloonProps): ReactElement => {
39
+ if (hidden) {
40
+ return <>{children}</>;
41
+ }
42
+
43
+ return (
44
+ <span
45
+ className={clsx('AbBalloon', styles.abBalloon, className)}
46
+ title={disabled ? undefined : title}
47
+ data-disabled={disabled ? 'true' : 'false'}
48
+ >
49
+ {children}
50
+ </span>
51
+ );
52
+ };
53
+
54
+ export default AbBalloon;
55
+ export { AbBalloon };
@@ -0,0 +1,12 @@
1
+ /* AbBalloon — title= Adapt (no hover chrome; native tooltip only) */
2
+ .abBalloon {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ box-sizing: border-box;
7
+ max-width: 100%;
8
+ }
9
+
10
+ .abBalloon[data-disabled='true'] {
11
+ pointer-events: none;
12
+ }
@@ -0,0 +1,88 @@
1
+ /*
2
+ * @license MIT
3
+ * ~~~~~~~~~~~~
4
+ * ab-nextjs-components
5
+ * ~~~~~~~~~~~~
6
+ * Copyright (c) 2026 Abraham Ukachi. The abElements Project.
7
+ *
8
+ * @project: ab-nextjs-components
9
+ * @name: AbBrand - Client (port of lyd-brand)
10
+ * @file: ab-brand/index.tsx
11
+ */
12
+
13
+ 'use client';
14
+
15
+ import type { CSSProperties, ReactElement } from 'react';
16
+ import Link from 'next/link';
17
+ import Image from 'next/image';
18
+ import clsx from 'clsx';
19
+ import styles from './styles.module.css';
20
+
21
+ export interface AbBrandProps {
22
+ className?: string;
23
+ type?: 'default' | 'blurred';
24
+ effect?: 'normal' | 'zoom';
25
+ hidden?: boolean;
26
+ image: string;
27
+ imageAlt?: string;
28
+ logo: string;
29
+ logoAlt?: string;
30
+ name: string;
31
+ shortName: string;
32
+ description: string;
33
+ autohideDescription?: boolean;
34
+ color?: string;
35
+ accentColor?: string;
36
+ link: string;
37
+ ctaLabel: string;
38
+ }
39
+
40
+ const AbBrand = ({
41
+ className,
42
+ type = 'default',
43
+ effect = 'normal',
44
+ hidden = false,
45
+ image,
46
+ imageAlt,
47
+ logo,
48
+ logoAlt,
49
+ name,
50
+ shortName,
51
+ description,
52
+ autohideDescription = false,
53
+ color,
54
+ accentColor,
55
+ link,
56
+ ctaLabel,
57
+ }: AbBrandProps): ReactElement => {
58
+ const style = {
59
+ '--ab-brand-color': color,
60
+ '--ab-brand-accent': accentColor,
61
+ } as CSSProperties;
62
+
63
+ return (
64
+ <Link
65
+ href={link}
66
+ hidden={hidden}
67
+ data-type={type}
68
+ data-effect={effect}
69
+ data-autohide-desc={autohideDescription ? 'true' : 'false'}
70
+ className={clsx('AbBrand', styles.abBrand, className)}
71
+ style={style}
72
+ >
73
+ <div className={styles.abBrandImageWrap}>
74
+ <Image className={styles.abBrandImage} src={image} alt={imageAlt ?? name} width={640} height={360} unoptimized />
75
+ </div>
76
+ <div className={styles.abBrandMeta}>
77
+ <Image className={styles.abBrandLogo} src={logo} alt={logoAlt ?? shortName} width={40} height={40} unoptimized />
78
+ <span className={styles.abBrandShort}>{shortName}</span>
79
+ <h3 className={styles.abBrandName}>{name}</h3>
80
+ <p className={styles.abBrandDesc}>{description}</p>
81
+ <span className={styles.abBrandCta}>{ctaLabel}</span>
82
+ </div>
83
+ </Link>
84
+ );
85
+ };
86
+
87
+ export default AbBrand;
88
+ export { AbBrand };
@@ -0,0 +1,54 @@
1
+ .abBrand {
2
+ position: relative;
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 0.75rem;
6
+ padding: 1rem;
7
+ border-radius: 1rem;
8
+ overflow: hidden;
9
+ text-decoration: none;
10
+ color: inherit;
11
+ background: color-mix(in srgb, var(--ab-surface-container, #f3edf7) 90%, transparent);
12
+ }
13
+ .abBrand[data-type='blurred'] {
14
+ backdrop-filter: blur(12px);
15
+ }
16
+ .abBrand[data-effect='zoom']:hover .abBrandImage {
17
+ transform: scale(1.05);
18
+ }
19
+ .abBrandImageWrap {
20
+ position: relative;
21
+ width: 100%;
22
+ aspect-ratio: 16 / 9;
23
+ border-radius: 0.75rem;
24
+ overflow: hidden;
25
+ background: #ddd;
26
+ }
27
+ .abBrandImage {
28
+ object-fit: cover;
29
+ width: 100%;
30
+ height: 100%;
31
+ transition: transform 0.35s ease;
32
+ }
33
+ .abBrandLogo {
34
+ width: 2.5rem;
35
+ height: 2.5rem;
36
+ object-fit: contain;
37
+ }
38
+ .abBrandMeta { display: flex; flex-direction: column; gap: 0.25rem; }
39
+ .abBrandName { margin: 0; font-size: 1.125rem; font-weight: 700; }
40
+ .abBrandShort { font-size: 0.75rem; font-weight: 600; opacity: 0.7; text-transform: uppercase; }
41
+ .abBrandDesc { margin: 0; font-size: 0.875rem; opacity: 0.8; }
42
+ .abBrand[data-autohide-desc='true'] .abBrandDesc { display: none; }
43
+ .abBrandCta {
44
+ align-self: flex-start;
45
+ border: none;
46
+ border-radius: 9999px;
47
+ padding: 0.4rem 0.9rem;
48
+ background: var(--ab-brand-accent, var(--ab-primary, #6750a4));
49
+ color: #fff;
50
+ font: inherit;
51
+ font-weight: 600;
52
+ cursor: pointer;
53
+ text-decoration: none;
54
+ }