@lexibase/rdx-ui 3.5.3 → 3.5.5

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 CHANGED
@@ -102,7 +102,7 @@ automatically registered in MDX. Import them from
102
102
  `@lexibase/rdx-ui/components/tabs` in React files. Use an explicit `defaultValue`,
103
103
  matching string values and an `aria-label` on each list. Arrow keys move focus;
104
104
  Enter or Space activates a tab. The list scrolls on small screens. See
105
- the repository's `MDX.md` and `docs/examples/tabs.mdx` for complete examples.
105
+ the [Tabs guide](https://github.com/lexibase/rdx/blob/main/apps/web/docs/03-writing-documentation/tabs.mdx) for complete examples.
106
106
 
107
107
  ## Documentation accordion
108
108
 
@@ -111,4 +111,4 @@ Base UI Accordion and are registered in MDX. Import them from
111
111
  `@lexibase/rdx-ui/components/accordion` in React files. One item opens at a time
112
112
  by default; `multiple` allows independent expansion. Triggers use native buttons
113
113
  inside `h3` headings with normal Tab navigation and Enter/Space activation.
114
- See `MDX.md` and `docs/examples/accordion.mdx` for examples and React options.
114
+ See the [Accordion guide](https://github.com/lexibase/rdx/blob/main/apps/web/docs/03-writing-documentation/accordion.mdx) for examples.
@@ -2,12 +2,13 @@
2
2
 
3
3
  ## shadcn/ui
4
4
 
5
- Table, Kbd and Sheet use the official base-nova registry components. Dialog, Code Block,
5
+ Table, Kbd, Badge and Sheet use the official base-nova registry components. Dialog, Code Block,
6
6
  useHighlightedCode, and highlighter are installed in full from that registry,
7
7
  with project imports, icons, localization and clipboard failure feedback wired
8
8
  to RDX. The official shadcn 4.22.1 tailwind.css is included as styles/shadcn.css.
9
9
  https://ui.shadcn.com/docs/components/base/table and
10
10
  https://ui.shadcn.com/docs/components/base/kbd and
11
+ https://ui.shadcn.com/docs/components/base/badge and
11
12
  https://ui.shadcn.com/docs/components/base/code-block and
12
13
  https://ui.shadcn.com/docs/components/base/dialog and
13
14
  https://ui.shadcn.com/docs/components/base/sheet.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexibase/rdx-ui",
3
- "version": "3.5.3",
3
+ "version": "3.5.5",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^22.18.0 || ^24.11.0"
@@ -39,7 +39,7 @@
39
39
  "tailwind-merge": "^3.7.0",
40
40
  "tailwindcss": "^4.3.3",
41
41
  "tw-animate-css": "^1.4.0",
42
- "@lexibase/rdx-types": "2.4.0"
42
+ "@lexibase/rdx-types": "2.6.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "next": "^16.4.2",
@@ -59,8 +59,8 @@
59
59
  "react-dom": "19.3.1",
60
60
  "tailwindcss": "^4.3.3",
61
61
  "typescript": "6.0.3",
62
- "@rdx/eslint-config": "1.0.0",
63
- "@rdx/typescript-config": "1.0.0"
62
+ "@rdx/typescript-config": "1.0.0",
63
+ "@rdx/eslint-config": "1.0.0"
64
64
  },
65
65
  "exports": {
66
66
  "./globals.css": {
@@ -0,0 +1,51 @@
1
+ import { mergeProps } from '@base-ui/react/merge-props'
2
+ import { useRender } from '@base-ui/react/use-render'
3
+ import { cva, type VariantProps } from 'class-variance-authority'
4
+ import { cn } from '@lexibase/rdx-ui/lib/utils'
5
+
6
+ const badgeVariants = cva(
7
+ 'group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
12
+ secondary:
13
+ 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
14
+ destructive:
15
+ 'bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20',
16
+ outline:
17
+ 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
18
+ ghost:
19
+ 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
20
+ link: 'text-primary underline-offset-4 hover:underline',
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: 'default',
25
+ },
26
+ }
27
+ )
28
+
29
+ function Badge({
30
+ className,
31
+ variant = 'default',
32
+ render,
33
+ ...props
34
+ }: useRender.ComponentProps<'span'> & VariantProps<typeof badgeVariants>) {
35
+ return useRender({
36
+ defaultTagName: 'span',
37
+ props: mergeProps<'span'>(
38
+ {
39
+ className: cn(badgeVariants({ variant }), className),
40
+ },
41
+ props
42
+ ),
43
+ render,
44
+ state: {
45
+ slot: 'badge',
46
+ variant,
47
+ },
48
+ })
49
+ }
50
+
51
+ export { Badge, badgeVariants }
@@ -90,6 +90,7 @@ function CodeBlockCopyButton({
90
90
  value,
91
91
  variant = 'ghost',
92
92
  size = 'icon-xs',
93
+ children,
93
94
  className,
94
95
  onClick,
95
96
  ...props
@@ -160,6 +161,7 @@ function CodeBlockCopyButton({
160
161
  {...props}
161
162
  >
162
163
  {copied ? <CheckIcon /> : <CopyIcon />}
164
+ {children}
163
165
  </Button>
164
166
  <span
165
167
  role="status"
@@ -8,6 +8,7 @@ import { AnimatePresence, motion, useReducedMotion } from 'motion/react'
8
8
  import type { RdxPageHighlight } from '@lexibase/rdx-types'
9
9
 
10
10
  import { Button } from './button'
11
+ import { HighlightDot } from './highlight-dot'
11
12
  import { Separator } from './separator'
12
13
  import {
13
14
  Sheet,
@@ -95,13 +96,11 @@ export function DocsSidebar({
95
96
  >
96
97
  {link.label}
97
98
  {link.highlighting && (
98
- <span
99
- aria-hidden="true"
99
+ <HighlightDot
100
+ kind={link.highlighting}
100
101
  data-rdx-sidebar-highlight={link.highlighting}
101
- className={`relative ms-auto size-2 shrink-0 rounded-full bg-current ${link.highlighting === 'important' ? 'text-amber-600 dark:text-amber-400' : 'text-blue-600 dark:text-blue-400'}`}
102
- >
103
- <span className="absolute inset-0 rounded-full bg-current opacity-0 motion-safe:animate-[rdx-sidebar-highlight_2.8s_ease-out_infinite]" />
104
- </span>
102
+ className="ms-auto"
103
+ />
105
104
  )}
106
105
  </Button>
107
106
  ))}
@@ -0,0 +1,27 @@
1
+ import type { ComponentPropsWithoutRef } from 'react'
2
+ import type { RdxPageHighlight } from '@lexibase/rdx-types'
3
+
4
+ import { cn } from '../lib/utils'
5
+
6
+ export function HighlightDot({
7
+ kind,
8
+ className,
9
+ ...props
10
+ }: ComponentPropsWithoutRef<'span'> & { kind: RdxPageHighlight }) {
11
+ return (
12
+ <span
13
+ {...props}
14
+ aria-hidden="true"
15
+ data-rdx-highlight-dot={kind}
16
+ className={cn(
17
+ 'relative size-2 shrink-0 rounded-full bg-current',
18
+ kind === 'important'
19
+ ? 'text-amber-600 dark:text-amber-400'
20
+ : 'text-blue-600 dark:text-blue-400',
21
+ className
22
+ )}
23
+ >
24
+ <span className="absolute inset-0 rounded-full bg-current opacity-0 motion-safe:animate-[rdx-sidebar-highlight_2.8s_ease-out_infinite]" />
25
+ </span>
26
+ )
27
+ }
@@ -1,47 +1,186 @@
1
- import type { ReactNode } from 'react'
1
+ import type { ComponentPropsWithoutRef, ReactNode } from 'react'
2
+ import { ArrowRight } from 'lucide-react'
2
3
 
4
+ import { cn } from '../lib/utils'
3
5
  import { Button } from './button'
4
6
  import { Logo } from './logo'
5
7
 
6
- /** The default landing page from apps/web, not a separate marketing theme. */
8
+ /** Gives each HomeSection the height of the visible homepage area. */
9
+ export function HomeSections({ children }: { children: ReactNode }) {
10
+ return <div className="relative h-full">{children}</div>
11
+ }
12
+
13
+ /** A homepage section that fills the viewport and grows with its content. */
14
+ export function HomeSection({
15
+ children,
16
+ className,
17
+ ...props
18
+ }: ComponentPropsWithoutRef<'section'>) {
19
+ return (
20
+ <section
21
+ className={cn('grid min-h-full content-center px-6 py-12', className)}
22
+ {...props}
23
+ >
24
+ {children}
25
+ </section>
26
+ )
27
+ }
28
+
29
+ /** The responsive hero layout used by the official site and project homepages. */
30
+ export function HomeHeroLayout({
31
+ intro,
32
+ preview,
33
+ details,
34
+ previewInert,
35
+ previewProps,
36
+ ...props
37
+ }: Omit<ComponentPropsWithoutRef<'section'>, 'children' | 'className'> & {
38
+ intro: ReactNode
39
+ preview: ReactNode
40
+ details?: ReactNode
41
+ previewInert?: boolean
42
+ previewProps?: Pick<
43
+ ComponentPropsWithoutRef<'div'>,
44
+ 'className' | 'onPointerMove' | 'onPointerLeave' | 'onPointerCancel'
45
+ >
46
+ }) {
47
+ const { className: previewClassName, ...previewEvents } = previewProps ?? {}
48
+
49
+ return (
50
+ <section
51
+ className="rdx-home-stage mx-auto grid w-full max-w-[1440px] content-center items-center gap-8 px-6 py-10 md:px-10 md:py-20 lg:min-h-[calc(100svh-3rem)] lg:grid-cols-[0.8fr_1.2fr] lg:gap-x-12 lg:gap-y-5"
52
+ {...props}
53
+ >
54
+ <div className="rdx-home-copy min-w-0">{intro}</div>
55
+ <div
56
+ className={cn(
57
+ 'rdx-home-preview-shell min-w-0 lg:col-start-2 lg:row-start-1',
58
+ details && 'lg:row-span-2',
59
+ previewClassName
60
+ )}
61
+ inert={previewInert}
62
+ aria-hidden={previewInert || undefined}
63
+ {...previewEvents}
64
+ >
65
+ {preview}
66
+ </div>
67
+ {details && (
68
+ <div className="rdx-home-css min-w-0 lg:col-start-1 lg:row-start-2">
69
+ {details}
70
+ </div>
71
+ )}
72
+ </section>
73
+ )
74
+ }
75
+
76
+ export function HomePreviewFrame({
77
+ caption,
78
+ children,
79
+ className,
80
+ ...props
81
+ }: ComponentPropsWithoutRef<'div'> & { caption: string }) {
82
+ return (
83
+ <div
84
+ className={cn(
85
+ 'overflow-hidden rounded-xl border border-border bg-background text-foreground shadow-2xl shadow-black/10',
86
+ className
87
+ )}
88
+ {...props}
89
+ >
90
+ <div className="flex h-10 items-center gap-1.5 border-b border-border bg-muted/30 px-4">
91
+ <span className="size-2 rounded-full bg-muted-foreground/30" />
92
+ <span className="size-2 rounded-full bg-muted-foreground/30" />
93
+ <span className="size-2 rounded-full bg-muted-foreground/30" />
94
+ <span className="mx-auto truncate pr-8 text-[10px] text-muted-foreground">
95
+ {caption}
96
+ </span>
97
+ </div>
98
+ {children}
99
+ </div>
100
+ )
101
+ }
102
+
103
+ /** The built-in homepage with project branding. */
7
104
  export function Home({
8
105
  title = 'RDX',
9
106
  logo,
10
107
  description,
11
108
  buttons = [],
109
+ preview,
110
+ previewTitle = title,
111
+ previewDescription,
112
+ children,
12
113
  }: {
13
114
  title?: string
14
115
  logo?: ReactNode
15
116
  description?: string
16
- buttons?: { label: string; href: string }[]
117
+ buttons?: { label: string; href: string; className?: string }[]
118
+ preview?: ReactNode
119
+ previewTitle?: string
120
+ previewDescription?: string
121
+ children?: ReactNode
17
122
  }) {
18
123
  return (
19
- <section
20
- data-rdx-home
21
- className="w-full h-full flex flex-col items-center justify-center"
22
- >
23
- {logo ?? (
24
- <Logo className="size-52 hover:scale-105 hover:rotate-1 transition-[scale,rotation] duration-1000" />
25
- )}
26
- <span className="text-3xl font-mono uppercase">{title}</span>
27
- {description && (
28
- <p className="text-sm text-muted-foreground text-center max-w-xl mt-4 px-6">
29
- {description}
30
- </p>
31
- )}
32
- {buttons.length > 0 && (
33
- <div className="flex flex-wrap justify-center gap-3 mt-6 px-6">
34
- {buttons.map((button, index) => (
35
- <Button
36
- key={`${index}:${button.href}`}
37
- variant={index === 0 ? 'default' : 'outline'}
38
- href={button.href}
39
- >
40
- {button.label}
41
- </Button>
42
- ))}
43
- </div>
44
- )}
45
- </section>
124
+ <HomeSections>
125
+ <HomeSection data-rdx-home className="min-h-full overflow-x-clip p-0">
126
+ <HomeHeroLayout
127
+ previewInert
128
+ intro={
129
+ <>
130
+ <h1 className="text-3xl font-semibold leading-tight tracking-tight wrap-break-word sm:text-4xl lg:text-6xl lg:leading-none">
131
+ {title}
132
+ </h1>
133
+ {description && (
134
+ <p className="mt-3 max-w-md text-sm leading-6 text-muted-foreground lg:mt-6 lg:text-base lg:leading-7">
135
+ {description}
136
+ </p>
137
+ )}
138
+ {buttons.length > 0 && (
139
+ <div className="mt-4 flex flex-wrap gap-3 lg:mt-7">
140
+ {buttons.map((button, index) => (
141
+ <Button
142
+ key={`${index}:${button.href}`}
143
+ variant={index === 0 ? 'default' : 'outline'}
144
+ href={button.href}
145
+ className={button.className}
146
+ >
147
+ {button.label} {index === 0 && <ArrowRight />}
148
+ </Button>
149
+ ))}
150
+ </div>
151
+ )}
152
+ </>
153
+ }
154
+ preview={
155
+ preview ?? (
156
+ <HomePreviewFrame caption={title}>
157
+ <div className="flex items-center gap-2 border-b border-border px-5 py-3 text-sm font-semibold">
158
+ {logo ?? <Logo className="size-5" />}
159
+ {title}
160
+ </div>
161
+ <div className="flex min-h-72">
162
+ <div className="w-36 shrink-0 border-r border-border p-3">
163
+ <p className="rounded-md bg-accent px-2 py-1.5 text-xs font-medium text-accent-foreground">
164
+ {previewTitle}
165
+ </p>
166
+ </div>
167
+ <div className="min-w-0 flex-1 wrap-break-word p-7">
168
+ <p className="text-2xl font-semibold tracking-tight">
169
+ {previewTitle}
170
+ </p>
171
+ {previewDescription && (
172
+ <p className="mt-4 text-sm leading-6 text-muted-foreground">
173
+ {previewDescription}
174
+ </p>
175
+ )}
176
+ </div>
177
+ </div>
178
+ </HomePreviewFrame>
179
+ )
180
+ }
181
+ />
182
+ </HomeSection>
183
+ {children}
184
+ </HomeSections>
46
185
  )
47
186
  }
@@ -14,6 +14,7 @@ export function Navbar({
14
14
  siteTitle = 'RDX',
15
15
  logo = <Logo className="size-5" />,
16
16
  homeHref = '/',
17
+ leadingLinks,
17
18
  docsHref = '/docs',
18
19
  docsLink,
19
20
  versionMenu,
@@ -27,6 +28,7 @@ export function Navbar({
27
28
  siteTitle?: string
28
29
  logo?: ReactNode
29
30
  homeHref?: string
31
+ leadingLinks?: ReactNode
30
32
  docsHref?: string
31
33
  docsLink?: ReactNode
32
34
  versionMenu?: ReactNode
@@ -42,7 +44,7 @@ export function Navbar({
42
44
  aria-label={messages.mainNavigation}
43
45
  className="w-full flex flex-wrap shrink-0 items-center justify-between px-6 py-2 border-b border-border gap-y-2"
44
46
  >
45
- <div className="flex items-center gap-1">
47
+ <div className="flex items-center gap-3 sm:gap-5">
46
48
  <Button
47
49
  variant="ghost"
48
50
  size={title === '' ? 'icon' : 'default'}
@@ -53,6 +55,7 @@ export function Navbar({
53
55
  {logo}
54
56
  {title !== '' && <span>{title}</span>}
55
57
  </Button>
58
+ {leadingLinks}
56
59
  </div>
57
60
  <div className="flex items-center gap-x-1 md:gap-x-2 h-4">
58
61
  {search}
@@ -268,3 +268,67 @@
268
268
  transform: translateX(var(--rdx-page-offset));
269
269
  }
270
270
  }
271
+
272
+ .rdx-home-stage {
273
+ position: relative;
274
+ isolation: isolate;
275
+ }
276
+
277
+ .rdx-home-copy,
278
+ .rdx-home-css {
279
+ position: relative;
280
+ z-index: 1;
281
+ }
282
+
283
+ .rdx-home-preview-shell {
284
+ width: calc(100% + 2rem);
285
+ transform: perspective(1400px) rotateY(-12deg) rotateX(4deg) rotateZ(1deg);
286
+ transform-origin: center;
287
+ }
288
+
289
+ @media (max-width: 1023px) {
290
+ .rdx-home-stage {
291
+ overflow: clip;
292
+ row-gap: 1.25rem;
293
+ padding-top: 1.5rem;
294
+ padding-bottom: 1.5rem;
295
+ }
296
+
297
+ .rdx-home-copy {
298
+ display: flex;
299
+ min-height: 0;
300
+ flex-direction: column;
301
+ justify-content: flex-start;
302
+ }
303
+
304
+ .rdx-home-preview-shell {
305
+ position: absolute;
306
+ z-index: 0;
307
+ top: 1.5rem;
308
+ left: max(13rem, calc(100% - 8.5rem));
309
+ width: 40rem;
310
+ height: 33rem;
311
+ overflow: hidden;
312
+ pointer-events: none;
313
+ opacity: 0.95;
314
+ transform: perspective(1400px) rotateY(-12deg) rotateX(4deg) rotateZ(1deg)
315
+ scale(0.56);
316
+ transform-origin: top left;
317
+ mask-image: linear-gradient(to bottom, black, black 65%, transparent);
318
+ }
319
+
320
+ .rdx-home-preview-shell > p {
321
+ display: none;
322
+ }
323
+
324
+ .rdx-home-preview-sidebar {
325
+ display: block;
326
+ }
327
+ }
328
+
329
+ @media (max-width: 374px), (max-width: 1023px) and (max-height: 740px) {
330
+ .rdx-home-preview-shell {
331
+ transform: perspective(1400px) rotateY(-12deg) rotateX(4deg) rotateZ(1deg)
332
+ scale(0.48);
333
+ }
334
+ }