ab-nextjs-components 0.1.2 → 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 (112) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +60 -14
  3. package/ab-avatar/index.tsx +90 -0
  4. package/ab-avatar/styles.module.css +62 -0
  5. package/ab-balloon/index.tsx +55 -0
  6. package/ab-balloon/styles.module.css +12 -0
  7. package/ab-brand/index.tsx +88 -0
  8. package/ab-brand/styles.module.css +54 -0
  9. package/ab-button/index.tsx +164 -0
  10. package/ab-button/styles.module.css +63 -0
  11. package/ab-collapsible/index.tsx +188 -0
  12. package/ab-collapsible/styles.module.css +98 -0
  13. package/ab-color/index.tsx +84 -0
  14. package/ab-color/styles.module.css +51 -0
  15. package/ab-demo-box/index.tsx +38 -0
  16. package/ab-demo-box/styles.module.css +13 -0
  17. package/ab-demo-code/index.tsx +62 -0
  18. package/ab-demo-code/styles.module.css +41 -0
  19. package/ab-icon/index.tsx +63 -0
  20. package/ab-icon/styles.module.css +27 -0
  21. package/ab-icon-button/index.tsx +107 -0
  22. package/ab-icon-button/styles.module.css +64 -0
  23. package/ab-image/index.tsx +83 -0
  24. package/ab-image/styles.module.css +27 -0
  25. package/ab-info/index.tsx +89 -0
  26. package/ab-info/styles.module.css +87 -0
  27. package/ab-input/index.tsx +277 -0
  28. package/ab-input/styles.module.css +101 -0
  29. package/ab-key-feature/index.tsx +60 -0
  30. package/ab-key-feature/styles.module.css +47 -0
  31. package/ab-like-button/index.tsx +116 -0
  32. package/ab-like-button/styles.module.css +60 -0
  33. package/ab-logo/index.tsx +94 -0
  34. package/ab-logo/styles.module.css +30 -0
  35. package/ab-main-headline/index.tsx +57 -0
  36. package/ab-main-headline/styles.module.css +24 -0
  37. package/ab-menu/index.tsx +119 -0
  38. package/ab-menu/styles.module.css +99 -0
  39. package/ab-name/index.tsx +49 -0
  40. package/ab-name/styles.module.css +14 -0
  41. package/ab-navbar/index.tsx +68 -0
  42. package/ab-navbar/styles.module.css +35 -0
  43. package/ab-page-paginator/index.tsx +130 -0
  44. package/ab-page-paginator/styles.module.css +47 -0
  45. package/ab-page-switcher/index.tsx +121 -0
  46. package/ab-page-switcher/styles.module.css +37 -0
  47. package/ab-preview/index.tsx +78 -0
  48. package/ab-preview/styles.module.css +26 -0
  49. package/ab-price/index.tsx +109 -0
  50. package/ab-price/styles.module.css +36 -0
  51. package/ab-price-tag/index.tsx +70 -0
  52. package/ab-price-tag/styles.module.css +81 -0
  53. package/ab-product/index.tsx +128 -0
  54. package/ab-product/styles.module.css +44 -0
  55. package/ab-product-item/index.tsx +78 -0
  56. package/ab-product-item/styles.module.css +21 -0
  57. package/ab-progress-chips/index.tsx +105 -0
  58. package/ab-progress-chips/styles.module.css +43 -0
  59. package/ab-search-result/index.tsx +79 -0
  60. package/ab-search-result/styles.module.css +48 -0
  61. package/ab-searchbar/index.tsx +150 -0
  62. package/ab-searchbar/styles.module.css +77 -0
  63. package/ab-sidebar/index.tsx +72 -0
  64. package/ab-sidebar/styles.module.css +41 -0
  65. package/ab-switch-back/index.tsx +72 -0
  66. package/ab-switch-back/styles.module.css +45 -0
  67. package/ab-tab/index.tsx +101 -0
  68. package/ab-tab/styles.module.css +65 -0
  69. package/ab-tabs/index.tsx +45 -0
  70. package/ab-tabs/styles.module.css +17 -0
  71. package/ab-test-hello/index.tsx +37 -0
  72. package/ab-test-hello/styles.module.css +25 -0
  73. package/ab-text/index.tsx +36 -0
  74. package/ab-text/styles.module.css +3 -0
  75. package/index.ts +117 -0
  76. package/package.json +152 -7
  77. package/server/ab-avatar/index.tsx +85 -0
  78. package/server/ab-avatar/styles.module.css +62 -0
  79. package/server/ab-badge/index.tsx +66 -0
  80. package/server/ab-badge/styles.module.css +39 -0
  81. package/server/ab-brand/index.tsx +87 -0
  82. package/server/ab-brand/styles.module.css +54 -0
  83. package/server/ab-button/index.tsx +144 -0
  84. package/server/ab-button/styles.module.css +63 -0
  85. package/server/ab-icon/index.tsx +61 -0
  86. package/server/ab-icon/styles.module.css +27 -0
  87. package/server/ab-key-feature/index.tsx +59 -0
  88. package/server/ab-key-feature/styles.module.css +47 -0
  89. package/server/ab-logo/index.tsx +93 -0
  90. package/server/ab-logo/styles.module.css +30 -0
  91. package/server/ab-name/index.tsx +48 -0
  92. package/server/ab-name/styles.module.css +14 -0
  93. package/server/ab-nav-link/index.tsx +60 -0
  94. package/server/ab-nav-link/styles.module.css +32 -0
  95. package/server/ab-navbar/index.tsx +64 -0
  96. package/server/ab-navbar/styles.module.css +35 -0
  97. package/server/ab-polygon/index.tsx +45 -0
  98. package/server/ab-polygon/styles.module.css +11 -0
  99. package/server/ab-sidebar/index.tsx +68 -0
  100. package/server/ab-sidebar/styles.module.css +41 -0
  101. package/server/ab-switch-back/index.tsx +71 -0
  102. package/server/ab-switch-back/styles.module.css +45 -0
  103. package/server/ab-test-hello/index.tsx +35 -0
  104. package/server/ab-test-hello/styles.module.css +22 -0
  105. package/server/ab-text/index.tsx +33 -0
  106. package/server/ab-text/styles.module.css +3 -0
  107. package/server/index.ts +27 -0
  108. package/types/index.d.ts +54 -0
  109. package/.github/ab-logo-dark.svg +0 -12
  110. package/.github/ab-logo-light.svg +0 -9
  111. package/.github/nextjs-logoname-dark.svg +0 -28
  112. package/.github/nextjs-logoname-light.svg +0 -25
@@ -0,0 +1,48 @@
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: AbName - Server (port of lyd-name)
10
+ * @file: server/ab-name/index.tsx
11
+ */
12
+
13
+
14
+ import type { ReactElement } from 'react';
15
+ import clsx from 'clsx';
16
+ import styles from './styles.module.css';
17
+
18
+ export interface AbNameProps {
19
+ type?: 'light' | 'regular' | 'bold' | 'black';
20
+ className?: string;
21
+ label?: string;
22
+ onClick?: () => void;
23
+ }
24
+
25
+ const AbName = ({
26
+ type = 'regular',
27
+ className,
28
+ label = 'abElements',
29
+ onClick,
30
+ }: AbNameProps): ReactElement => {
31
+ const first = label.slice(0, 2);
32
+ const rest = label.slice(2);
33
+ return (
34
+ <span
35
+ data-type={type}
36
+ className={clsx('AbName', styles.abName, className)}
37
+ onClick={onClick}
38
+ role={onClick ? 'button' : undefined}
39
+ tabIndex={onClick ? 0 : undefined}
40
+ >
41
+ <span className={styles.abNameAb}>{first}</span>
42
+ <span className={styles.abNameRest}>{rest}</span>
43
+ </span>
44
+ );
45
+ };
46
+
47
+ export default AbName;
48
+ export { AbName };
@@ -0,0 +1,14 @@
1
+ .abName {
2
+ display: inline-flex;
3
+ align-items: baseline;
4
+ font-family: ui-serif, Georgia, serif;
5
+ letter-spacing: -0.02em;
6
+ cursor: default;
7
+ user-select: none;
8
+ }
9
+ .abName[data-type='light'] { font-weight: 300; }
10
+ .abName[data-type='regular'] { font-weight: 400; }
11
+ .abName[data-type='bold'] { font-weight: 700; }
12
+ .abName[data-type='black'] { font-weight: 900; }
13
+ .abNameAb { color: var(--ab-primary, #6750a4); }
14
+ .abNameRest { opacity: 0.85; }
@@ -0,0 +1,60 @@
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: AbNavLink - Server (port of lyd-nav-link)
10
+ * @file: server/ab-nav-link/index.tsx
11
+ */
12
+
13
+ import type { ReactElement } from 'react';
14
+ import Link from 'next/link';
15
+ import clsx from 'clsx';
16
+ import styles from './styles.module.css';
17
+
18
+ export interface AbNavLinkProps {
19
+ className?: string;
20
+ title?: string;
21
+ href: string;
22
+ icon: string;
23
+ label: string;
24
+ active?: boolean;
25
+ noBadge?: boolean;
26
+ badgeCount?: number;
27
+ }
28
+
29
+ const AbNavLink = ({
30
+ className,
31
+ title,
32
+ href,
33
+ icon,
34
+ label,
35
+ active = false,
36
+ noBadge = true,
37
+ badgeCount = 0,
38
+ }: AbNavLinkProps): ReactElement => {
39
+ return (
40
+ <Link
41
+ href={href}
42
+ title={title ?? label}
43
+ data-active={active ? 'true' : 'false'}
44
+ className={clsx('AbNavLink', styles.abNavLink, className)}
45
+ >
46
+ <span className={clsx('material-symbols-rounded', styles.abNavLinkIcon)} aria-hidden>
47
+ {icon}
48
+ </span>
49
+ <span className={styles.abNavLinkLabel}>{label}</span>
50
+ {!noBadge && badgeCount > 0 ? (
51
+ <span className={styles.abNavLinkBadge} aria-label={`${badgeCount} notifications`}>
52
+ {badgeCount}
53
+ </span>
54
+ ) : null}
55
+ </Link>
56
+ );
57
+ };
58
+
59
+ export default AbNavLink;
60
+ export { AbNavLink };
@@ -0,0 +1,32 @@
1
+ .abNavLink {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ gap: 0.5rem;
5
+ text-decoration: none;
6
+ color: inherit;
7
+ opacity: 0.7;
8
+ padding: 0.5rem 0.75rem;
9
+ border-radius: 9999px;
10
+ position: relative;
11
+ }
12
+ .abNavLink[data-active='true'] {
13
+ opacity: 1;
14
+ background: color-mix(in srgb, var(--ab-primary, #6750a4) 12%, transparent);
15
+ }
16
+ .abNavLinkIcon { font-size: 1.25rem; }
17
+ .abNavLinkLabel { font-size: 0.875rem; font-weight: 600; }
18
+ .abNavLinkBadge {
19
+ position: absolute;
20
+ top: 0.15rem;
21
+ right: 0.15rem;
22
+ min-width: 1.1rem;
23
+ height: 1.1rem;
24
+ padding: 0 0.25rem;
25
+ border-radius: 9999px;
26
+ background: var(--ab-error, #b3261e);
27
+ color: #fff;
28
+ font-size: 0.65rem;
29
+ display: inline-flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ }
@@ -0,0 +1,64 @@
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: AbNavbar - Server (port of lyd-navbar)
10
+ * @file: server/ab-navbar/index.tsx
11
+ */
12
+
13
+ import type { ReactElement } from 'react';
14
+ import Link from 'next/link';
15
+ import clsx from 'clsx';
16
+ import styles from './styles.module.css';
17
+
18
+ export type AbNavbarLink = {
19
+ href: string;
20
+ icon: string;
21
+ value: string;
22
+ label: string;
23
+ };
24
+
25
+ export interface AbNavbarProps {
26
+ className?: string;
27
+ type?: 'customer' | 'admin';
28
+ page?: string;
29
+ links?: AbNavbarLink[];
30
+ }
31
+
32
+ const AbNavbar = ({
33
+ className,
34
+ type = 'customer',
35
+ page,
36
+ links = [],
37
+ }: AbNavbarProps): ReactElement => {
38
+ return (
39
+ <nav data-type={type} className={clsx('AbNavbar', styles.abNavbar, className)} aria-label="Primary">
40
+ <ul className={styles.abNavbarList}>
41
+ {links.map((link) => {
42
+ const active = page === link.value;
43
+ return (
44
+ <li key={link.value}>
45
+ <Link
46
+ href={link.href}
47
+ data-active={active ? 'true' : 'false'}
48
+ className={styles.abNavbarLink}
49
+ >
50
+ <span className={clsx('material-symbols-rounded', styles.abNavbarIcon)} aria-hidden>
51
+ {link.icon}
52
+ </span>
53
+ <span>{link.label}</span>
54
+ </Link>
55
+ </li>
56
+ );
57
+ })}
58
+ </ul>
59
+ </nav>
60
+ );
61
+ };
62
+
63
+ export default AbNavbar;
64
+ export { AbNavbar };
@@ -0,0 +1,35 @@
1
+ .abNavbar {
2
+ display: flex;
3
+ align-items: center;
4
+ gap: 0.5rem;
5
+ width: 100%;
6
+ padding: 0.5rem 0.75rem;
7
+ box-sizing: border-box;
8
+ }
9
+ .abNavbarList {
10
+ display: flex;
11
+ align-items: center;
12
+ gap: 0.25rem;
13
+ list-style: none;
14
+ margin: 0;
15
+ padding: 0;
16
+ overflow-x: auto;
17
+ }
18
+ .abNavbarLink {
19
+ display: inline-flex;
20
+ align-items: center;
21
+ gap: 0.35rem;
22
+ padding: 0.45rem 0.75rem;
23
+ border-radius: 9999px;
24
+ text-decoration: none;
25
+ color: inherit;
26
+ opacity: 0.7;
27
+ white-space: nowrap;
28
+ font-size: 0.875rem;
29
+ font-weight: 600;
30
+ }
31
+ .abNavbarLink[data-active='true'] {
32
+ opacity: 1;
33
+ background: color-mix(in srgb, var(--ab-primary, #6750a4) 14%, transparent);
34
+ }
35
+ .abNavbarIcon { font-size: 1.15rem; }
@@ -0,0 +1,45 @@
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: AbPolygon - Server (port of lyd-polygon)
10
+ * @file: server/ab-polygon/index.tsx
11
+ */
12
+
13
+ import type { ReactElement } from 'react';
14
+ import clsx from 'clsx';
15
+ import styles from './styles.module.css';
16
+
17
+ export interface AbPolygonProps {
18
+ className?: string;
19
+ hidden?: boolean;
20
+ fill?: string;
21
+ opacity?: number;
22
+ }
23
+
24
+ const AbPolygon = ({
25
+ className,
26
+ hidden = false,
27
+ fill = '#778D79',
28
+ opacity = 0.36,
29
+ }: AbPolygonProps): ReactElement => {
30
+ return (
31
+ <div hidden={hidden} className={clsx('AbPolygon', styles.abPolygon, className)} aria-hidden>
32
+ <svg width="761" height="603" viewBox="0 0 761 603" fill="none" role="presentation">
33
+ <path
34
+ opacity={opacity}
35
+ d="M647.5 -242.933C708.264 -265.049 770.182 -213.093 758.954 -149.412L638.552 533.421C627.323 597.102 551.369 624.747 501.834 583.183L-29.316 137.495C-78.8509 95.9301 -64.8151 16.3291 -4.05151 -5.78704L647.5 -242.933Z"
36
+ fill={fill}
37
+ />
38
+ </svg>
39
+ </div>
40
+ );
41
+ };
42
+
43
+ export default AbPolygon;
44
+ export { AbPolygon };
45
+
@@ -0,0 +1,11 @@
1
+ .abPolygon {
2
+ display: block;
3
+ line-height: 0;
4
+ pointer-events: none;
5
+ }
6
+ .abPolygon svg {
7
+ display: block;
8
+ width: 100%;
9
+ height: auto;
10
+ }
11
+
@@ -0,0 +1,68 @@
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: AbSidebar - Server (port of lyd-sidebar)
10
+ * @file: server/ab-sidebar/index.tsx
11
+ */
12
+
13
+ import type { ReactElement } from 'react';
14
+ import Link from 'next/link';
15
+ import clsx from 'clsx';
16
+ import styles from './styles.module.css';
17
+
18
+ export type AbSidebarLink = {
19
+ href: string;
20
+ icon: string;
21
+ value: string;
22
+ label: string;
23
+ };
24
+
25
+ export interface AbSidebarProps {
26
+ className?: string;
27
+ type?: 'customer' | 'admin';
28
+ page?: string;
29
+ links?: AbSidebarLink[];
30
+ brandHref?: string;
31
+ brandLabel?: string;
32
+ }
33
+
34
+ const AbSidebar = ({
35
+ className,
36
+ type = 'customer',
37
+ page,
38
+ links = [],
39
+ brandHref = '/',
40
+ brandLabel = 'abElements',
41
+ }: AbSidebarProps): ReactElement => {
42
+ return (
43
+ <aside data-type={type} className={clsx('AbSidebar', styles.abSidebar, className)} aria-label="Sidebar">
44
+ <Link href={brandHref} className={styles.abSidebarBrand}>
45
+ <span className="material-symbols-rounded" aria-hidden>apps</span>
46
+ <span>{brandLabel}</span>
47
+ </Link>
48
+ <ul className={styles.abSidebarList}>
49
+ {links.map((link) => {
50
+ const active = page === link.value;
51
+ return (
52
+ <li key={link.value}>
53
+ <Link href={link.href} data-active={active ? 'true' : 'false'} className={styles.abSidebarLink}>
54
+ <span className={clsx('material-symbols-rounded', styles.abSidebarIcon)} aria-hidden>
55
+ {link.icon}
56
+ </span>
57
+ <span>{link.label}</span>
58
+ </Link>
59
+ </li>
60
+ );
61
+ })}
62
+ </ul>
63
+ </aside>
64
+ );
65
+ };
66
+
67
+ export default AbSidebar;
68
+ export { AbSidebar };
@@ -0,0 +1,41 @@
1
+ .abSidebar {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1rem;
5
+ width: 100%;
6
+ max-width: 16rem;
7
+ padding: 1rem 0.75rem;
8
+ box-sizing: border-box;
9
+ }
10
+ .abSidebarBrand {
11
+ display: flex;
12
+ align-items: center;
13
+ gap: 0.5rem;
14
+ font-weight: 700;
15
+ text-decoration: none;
16
+ color: inherit;
17
+ }
18
+ .abSidebarList {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: 0.25rem;
22
+ list-style: none;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+ .abSidebarLink {
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 0.6rem;
30
+ padding: 0.55rem 0.75rem;
31
+ border-radius: 0.75rem;
32
+ text-decoration: none;
33
+ color: inherit;
34
+ opacity: 0.75;
35
+ font-weight: 600;
36
+ }
37
+ .abSidebarLink[data-active='true'] {
38
+ opacity: 1;
39
+ background: color-mix(in srgb, var(--ab-primary, #6750a4) 14%, transparent);
40
+ }
41
+ .abSidebarIcon { font-size: 1.25rem; }
@@ -0,0 +1,71 @@
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: AbSwitchBack - Server (port of lyd-switch-back)
10
+ * @file: server/ab-switch-back/index.tsx
11
+ */
12
+
13
+
14
+ import type { ReactElement, ReactNode } from 'react';
15
+ import Link from 'next/link';
16
+ import Image from 'next/image';
17
+ import clsx from 'clsx';
18
+ import styles from './styles.module.css';
19
+
20
+ export interface AbSwitchBackProps {
21
+ className?: string;
22
+ hidden?: boolean;
23
+ switched?: boolean;
24
+ image: string;
25
+ imageAlt: string;
26
+ title?: ReactNode;
27
+ description?: ReactNode;
28
+ cta1Label: string;
29
+ cta1Link: string;
30
+ cta2Label: string;
31
+ cta2Link: string;
32
+ }
33
+
34
+ const AbSwitchBack = ({
35
+ className,
36
+ hidden = false,
37
+ switched = false,
38
+ image,
39
+ imageAlt,
40
+ title,
41
+ description,
42
+ cta1Label,
43
+ cta1Link,
44
+ cta2Label,
45
+ cta2Link,
46
+ }: AbSwitchBackProps): ReactElement => {
47
+ return (
48
+ <section
49
+ hidden={hidden}
50
+ data-switched={switched ? 'true' : 'false'}
51
+ className={clsx('AbSwitchBack', styles.abSwitchBack, className)}
52
+ >
53
+ <div className={styles.abSwitchBackMedia}>
54
+ <Image className={styles.abSwitchBackImage} src={image} alt={imageAlt} width={800} height={600} unoptimized />
55
+ </div>
56
+ <div className={styles.abSwitchBackCopy}>
57
+ {title ? <h2 className={styles.abSwitchBackTitle}>{title}</h2> : null}
58
+ {description ? <div className={styles.abSwitchBackDesc}>{description}</div> : null}
59
+ <div className={styles.abSwitchBackActions}>
60
+ <Link href={cta1Link} className={styles.abSwitchBackCta}>{cta1Label}</Link>
61
+ <Link href={cta2Link} className={clsx(styles.abSwitchBackCta, styles.abSwitchBackCtaSecondary)}>
62
+ {cta2Label}
63
+ </Link>
64
+ </div>
65
+ </div>
66
+ </section>
67
+ );
68
+ };
69
+
70
+ export default AbSwitchBack;
71
+ export { AbSwitchBack };
@@ -0,0 +1,45 @@
1
+ .abSwitchBack {
2
+ display: grid;
3
+ grid-template-columns: 1fr;
4
+ gap: 1.25rem;
5
+ align-items: center;
6
+ }
7
+ @media (min-width: 768px) {
8
+ .abSwitchBack {
9
+ grid-template-columns: 1fr 1fr;
10
+ }
11
+ .abSwitchBack[data-switched='true'] .abSwitchBackMedia { order: 2; }
12
+ .abSwitchBack[data-switched='true'] .abSwitchBackCopy { order: 1; }
13
+ }
14
+ .abSwitchBackMedia {
15
+ position: relative;
16
+ width: 100%;
17
+ aspect-ratio: 4 / 3;
18
+ border-radius: 1rem;
19
+ overflow: hidden;
20
+ background: #ddd;
21
+ }
22
+ .abSwitchBackImage {
23
+ object-fit: cover;
24
+ width: 100%;
25
+ height: 100%;
26
+ }
27
+ .abSwitchBackCopy { display: flex; flex-direction: column; gap: 0.75rem; }
28
+ .abSwitchBackTitle { margin: 0; font-size: 1.5rem; font-weight: 700; }
29
+ .abSwitchBackDesc { margin: 0; opacity: 0.8; }
30
+ .abSwitchBackActions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
31
+ .abSwitchBackCta {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ padding: 0.5rem 1rem;
35
+ border-radius: 9999px;
36
+ text-decoration: none;
37
+ font-weight: 600;
38
+ background: var(--ab-primary, #6750a4);
39
+ color: #fff;
40
+ }
41
+ .abSwitchBackCtaSecondary {
42
+ background: transparent;
43
+ color: var(--ab-primary, #6750a4);
44
+ border: 1px solid color-mix(in srgb, var(--ab-primary, #6750a4) 40%, transparent);
45
+ }
@@ -0,0 +1,35 @@
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: AbTestHello - Server (port of lyd-test-hello)
10
+ * @file: server/ab-test-hello/index.tsx
11
+ */
12
+
13
+ import type { ReactElement } from 'react';
14
+ import clsx from 'clsx';
15
+ import styles from './styles.module.css';
16
+
17
+ export interface AbTestHelloProps {
18
+ name: string;
19
+ src?: string;
20
+ className?: string;
21
+ }
22
+
23
+ const AbTestHello = ({ name, src, className }: AbTestHelloProps): ReactElement => {
24
+ return (
25
+ <div id="abTestHello" className={clsx('AbTestHello', styles.abTestHello, className)}>
26
+ <h2 className={styles.abTestHelloTitle}>
27
+ Hello from <span className={styles.abTestHelloName}>{name}</span>
28
+ </h2>
29
+ {src ? <span className={styles.abTestHelloSrc}>{src}</span> : null}
30
+ </div>
31
+ );
32
+ };
33
+
34
+ export default AbTestHello;
35
+ export { AbTestHello };
@@ -0,0 +1,22 @@
1
+ .abTestHello {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: flex-start;
5
+ text-align: left;
6
+ padding: 1.5rem;
7
+ }
8
+ .abTestHelloTitle {
9
+ margin: 0;
10
+ padding: 0.75rem 0 0.25rem;
11
+ font-size: 1rem;
12
+ }
13
+ .abTestHelloName {
14
+ font-family: ui-serif, Georgia, serif;
15
+ font-weight: 700;
16
+ color: var(--ab-primary, #6750a4);
17
+ }
18
+ .abTestHelloSrc {
19
+ font-family: ui-monospace, monospace;
20
+ font-size: 0.875rem;
21
+ opacity: 0.5;
22
+ }
@@ -0,0 +1,33 @@
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: AbText - Server (port of lyd-text; i18n stripped)
10
+ * @file: server/ab-text/index.tsx
11
+ */
12
+
13
+ import type { ReactElement } from 'react';
14
+ import clsx from 'clsx';
15
+ import styles from './styles.module.css';
16
+
17
+ export interface AbTextProps {
18
+ className?: string;
19
+ id?: string;
20
+ value?: string;
21
+ origin?: string;
22
+ }
23
+
24
+ const AbText = ({ className, id, value }: AbTextProps): ReactElement => {
25
+ return (
26
+ <span id={id} className={clsx('AbText', styles.abText, className)}>
27
+ {value ?? id ?? ''}
28
+ </span>
29
+ );
30
+ };
31
+
32
+ export default AbText;
33
+ export { AbText };
@@ -0,0 +1,3 @@
1
+ .abText {
2
+ display: inline;
3
+ }
@@ -0,0 +1,27 @@
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: Server Components - Barrel
10
+ * @file: server/index.ts
11
+ */
12
+
13
+ export { default as AbIcon } from './ab-icon/index.tsx'
14
+ export { default as AbLogo } from './ab-logo/index.tsx'
15
+ export { default as AbButton } from './ab-button/index.tsx'
16
+ export { default as AbAvatar } from './ab-avatar/index.tsx'
17
+ export { default as AbBadge } from './ab-badge/index.tsx'
18
+ export { default as AbPolygon } from './ab-polygon/index.tsx'
19
+ export { default as AbBrand } from './ab-brand/index.tsx'
20
+ export { default as AbName } from './ab-name/index.tsx'
21
+ export { default as AbNavbar } from './ab-navbar/index.tsx'
22
+ export { default as AbSidebar } from './ab-sidebar/index.tsx'
23
+ export { default as AbNavLink } from './ab-nav-link/index.tsx'
24
+ export { default as AbText } from './ab-text/index.tsx'
25
+ export { default as AbTestHello } from './ab-test-hello/index.tsx'
26
+ export { default as AbKeyFeature } from './ab-key-feature/index.tsx'
27
+ export { default as AbSwitchBack } from './ab-switch-back/index.tsx'