radiant-docs 0.1.48 → 0.1.50

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.
@@ -60,16 +60,20 @@ const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
60
60
  ---
61
61
 
62
62
  <div
63
- class="rd-prose-block rd-component-preview flex w-full max-w-full min-w-0 flex-col"
63
+ class="rd-prose-block rd-component-preview isolate flex w-full max-w-full min-w-0 flex-col"
64
64
  data-rd-component-preview-root="true"
65
65
  >
66
66
  <div
67
- class="w-full max-w-full min-w-0 overflow-x-auto rounded-t-xl border border-b-0 bg-white dark:bg-neutral-800/15 p-4 xs:p-6 sm:p-12 shadow-xs [&>:first-child]:mt-0! [&>:last-child]:mb-0!"
67
+ class="relative z-10 w-full max-w-full min-w-0 rounded-t-xl border-[0.5px] border-b-0 border-(--rd-code-tab-edge-border) bg-(--rd-code-surface) p-2"
68
68
  >
69
- <slot />
69
+ <div
70
+ class="bg-white dark:bg-neutral-900/70 rounded-xl border-[0.5px] border-neutral-900/10 dark:border-white/8 p-4 xs:p-6 sm:p-9 sm:px-12 *:first:mt-0! *:last:mb-0! shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-8px_rgba(0,0,0,0.08)] dark:shadow-[0_-0.5px_1px_rgba(255,255,255,0.1),0_5px_12px_-4px_rgba(0,0,0,0.15)] overflow-x-auto"
71
+ >
72
+ <slot />
73
+ </div>
70
74
  </div>
71
75
  <div
72
- class="rd-component-preview__code not-prose relative overflow-hidden rounded-b-xl bg-neutral-50 dark:bg-(--rd-code-surface)"
76
+ class="rd-component-preview__code not-prose relative z-0 overflow-hidden rounded-b-xl"
73
77
  data-rd-preview-expanded={isInitiallyExpanded ? "true" : "false"}
74
78
  style={{ "--rd-preview-visible-lines": String(visibleLines) }}
75
79
  >
@@ -82,16 +86,19 @@ const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
82
86
  hideLanguageIcon={hideLanguageIcon}
83
87
  highlightedLines={highlightedLines}
84
88
  collapsedLines={collapsedLines}
89
+ flushTop
85
90
  />
86
91
  <div
87
- class="rd-component-preview__overlay pointer-events-none absolute inset-x-px inset-y-px hidden items-end justify-center rounded-b-xl pb-4"
92
+ class="rd-component-preview__overlay pointer-events-none absolute inset-0 hidden items-end justify-center rounded-b-xl pb-4"
88
93
  data-rd-preview-overlay
89
94
  aria-hidden="true"
90
95
  >
91
- <div class="bg-white dark:bg-neutral-800">
96
+ <div
97
+ class="bg-white dark:bg-neutral-800 rounded-xl [corner-shape:superellipse(1.2)]"
98
+ >
92
99
  <button
93
100
  type="button"
94
- class="pointer-events-auto inline-flex h-8 items-center justify-center rounded-md border border-neutral-200 bg-white px-3 text-sm font-medium text-neutral-700 shadow-xl transition-colors duration-150 hover:bg-neutral-50 cursor-pointer dark:border-neutral-700 dark:bg-neutral-700/30 dark:text-neutral-200 dark:hover:bg-neutral-700/50"
101
+ class="pointer-events-auto inline-flex h-8 items-center justify-center rounded-xl [corner-shape:superellipse(1.2)] border-[0.5px] border-neutral-900/10 dark:border-white/8 bg-linear-to-br from-white via-white/10 to-neutral-900/5 dark:from-white/7 dark:via-white/6 dark:to-white/2 px-3 text-sm font-medium text-neutral-600 hover:text-neutral-900 dark:text-neutral-300/90 hover:dark:text-neutral-200 transition-colors duration-200 hover:bg-neutral-50/80 cursor-pointer dark:hover:bg-neutral-700/30 shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-4px_rgba(0,0,0,0.08)] dark:shadow-[0_-0.5px_0px_rgba(255,255,255,0.15),0_5px_12px_-4px_rgba(0,0,0,0.18)]"
95
102
  data-rd-preview-expand
96
103
  >
97
104
  View code
@@ -174,36 +181,37 @@ const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
174
181
  .rd-component-preview__code[data-rd-preview-expanded="false"]
175
182
  .rd-component-preview__overlay {
176
183
  display: flex;
177
- background: linear-gradient(
178
- to top,
179
- rgb(250 250 250 / 90%),
180
- rgb(250 250 250 / 30%)
181
- );
182
- }
183
-
184
- :global(.dark)
185
- .rd-component-preview__code[data-rd-preview-expanded="false"]
186
- .rd-component-preview__overlay {
187
- background: linear-gradient(
188
- to top,
189
- color-mix(in srgb, var(--rd-code-surface) 90%, transparent),
190
- color-mix(in srgb, var(--rd-code-surface) 35%, transparent)
191
- );
192
184
  }
193
185
 
194
186
  .rd-component-preview__code :global([data-rd-code-scroll-area]) {
195
187
  overflow-y: hidden;
196
- transition: max-height 280ms cubic-bezier(0.22, 1, 0.36, 1);
188
+ transition:
189
+ max-height 280ms cubic-bezier(0.22, 1, 0.36, 1),
190
+ mask-image 180ms ease-out;
197
191
  }
198
192
 
199
193
  .rd-component-preview__code[data-rd-preview-expanded="false"]
200
194
  :global([data-rd-code-scroll-area]) {
201
- max-height: calc(var(--rd-preview-visible-lines, 5) * 1.5rem + 1.25rem);
195
+ max-height: calc(var(--rd-preview-visible-lines, 5) * 1.5rem + 0.75rem);
196
+ -webkit-mask-image: linear-gradient(
197
+ to bottom,
198
+ rgb(0 0 0 / 80%) 0%,
199
+ rgb(0 0 0 / 32%) 82%,
200
+ transparent 110%
201
+ );
202
+ mask-image: linear-gradient(
203
+ to bottom,
204
+ rgb(0 0 0 / 80%) 0%,
205
+ rgb(0 0 0 / 32%) 82%,
206
+ transparent 110%
207
+ );
202
208
  }
203
209
 
204
210
  .rd-component-preview__code[data-rd-preview-expanded="true"]
205
211
  :global([data-rd-code-scroll-area]) {
206
212
  max-height: var(--rd-preview-expanded-height, 200rem);
213
+ -webkit-mask-image: none;
214
+ mask-image: none;
207
215
  }
208
216
  </style>
209
217
 
@@ -1,10 +1,9 @@
1
1
  ---
2
2
  import { validateNoUnknownProps, validateProps } from "../../lib/component-error";
3
- import { renderMarkdown } from "../../lib/utils";
4
3
  import { resolveStaticAssetUrl } from "../../lib/static-asset-url";
5
4
 
6
5
  interface Props {
7
- src: string;
6
+ src: string | { light?: string; dark?: string };
8
7
  alt?: string;
9
8
  title?: string;
10
9
  width?: number | string;
@@ -24,7 +23,7 @@ validateProps(
24
23
  "Image",
25
24
  imageProps,
26
25
  {
27
- src: { required: true, type: "string" },
26
+ src: { required: true, type: ["string", "object"] },
28
27
  alt: { type: "string" },
29
28
  title: { type: "string" },
30
29
  width: { type: ["number", "string"] },
@@ -35,17 +34,51 @@ validateProps(
35
34
 
36
35
  const { src, alt, title, width, zoom = true } = imageProps as Props;
37
36
  const zoomEnabled = zoom !== false;
38
- const resolvedSrc = resolveStaticAssetUrl(src);
39
37
  const rawWidth = width;
40
- const imageAttrs: Record<string, unknown> = { src: resolvedSrc };
41
- if (typeof alt === "string") {
42
- imageAttrs.alt = alt;
38
+
39
+ function normalizeImageSource(value: Props["src"]): {
40
+ light: string;
41
+ dark?: string;
42
+ } {
43
+ if (typeof value === "string") {
44
+ return { light: value };
45
+ }
46
+
47
+ const light = typeof value.light === "string" ? value.light : "";
48
+ const dark = typeof value.dark === "string" ? value.dark : undefined;
49
+
50
+ return { light, dark };
43
51
  }
44
- if (width !== undefined) {
45
- imageAttrs.width = width;
52
+
53
+ const normalizedSource = normalizeImageSource(src);
54
+ if (!normalizedSource.light.trim()) {
55
+ throw new Error(
56
+ `[USER_ERROR]: <Image>: Invalid prop "src": object form requires a non-empty "light" string (in ${Astro.url.pathname})`,
57
+ );
46
58
  }
47
59
 
48
- const zoomAttrs: Record<string, unknown> = { src: resolvedSrc };
60
+ const resolvedLightSrc = resolveStaticAssetUrl(normalizedSource.light);
61
+ const resolvedDarkSrc =
62
+ typeof normalizedSource.dark === "string" && normalizedSource.dark.trim()
63
+ ? resolveStaticAssetUrl(normalizedSource.dark)
64
+ : undefined;
65
+ const hasDarkSource =
66
+ typeof resolvedDarkSrc === "string" && resolvedDarkSrc !== resolvedLightSrc;
67
+
68
+ function createImageAttrs(source: string): Record<string, unknown> {
69
+ const attrs: Record<string, unknown> = { src: source };
70
+ if (typeof alt === "string") {
71
+ attrs.alt = alt;
72
+ }
73
+ if (width !== undefined) {
74
+ attrs.width = width;
75
+ }
76
+ return attrs;
77
+ }
78
+
79
+ const lightImageAttrs = createImageAttrs(resolvedLightSrc);
80
+ const darkImageAttrs = hasDarkSource ? createImageAttrs(resolvedDarkSrc!) : null;
81
+ const zoomAttrs: Record<string, unknown> = { src: resolvedLightSrc };
49
82
  if (typeof alt === "string") {
50
83
  zoomAttrs.alt = alt;
51
84
  }
@@ -81,17 +114,33 @@ function isConstrainedWidthValue(value: unknown): boolean {
81
114
  return true;
82
115
  }
83
116
 
117
+ function toCssWidthValue(value: unknown): string | undefined {
118
+ if (typeof value === "number") {
119
+ if (!Number.isFinite(value) || value <= 0) return undefined;
120
+ return `${value}px`;
121
+ }
122
+
123
+ if (typeof value !== "string") return undefined;
124
+ const normalized = value.trim();
125
+ if (!normalized) return undefined;
126
+
127
+ return /^-?\d*\.?\d+$/.test(normalized) ? `${normalized}px` : normalized;
128
+ }
129
+
84
130
  const hasCustomImageWidth = isConstrainedWidthValue(rawWidth);
131
+ const contentWidthStyle = hasCustomImageWidth
132
+ ? `width: ${toCssWidthValue(rawWidth) ?? "auto"};`
133
+ : undefined;
85
134
 
86
- const captionHtml =
87
- typeof title === "string" && title.trim().length > 0
88
- ? (await renderMarkdown(title)).replace(/^<p>([\s\S]*)<\/p>\n?$/, "$1")
89
- : "";
135
+ const slotCaptionHtml = Astro.slots.has("default")
136
+ ? (await Astro.slots.render("default")).trim()
137
+ : "";
138
+ const hasCaption = slotCaptionHtml.length > 0;
90
139
  ---
91
140
 
92
141
  <figure
93
142
  class:list={[
94
- "rd-prose-block p-1.5 pb-1 xs:pb-1.5 group border border-neutral-200 dark:border-neutral-800 shadow-xs bg-neutral-50 dark:bg-(--rd-code-surface) rounded-2xl",
143
+ "rd-prose-block p-1.5 pb-1 xs:pb-1.5 group border-[0.5px] border-neutral-900/8 dark:border-white/6 bg-(--rd-code-surface) rounded-2xl shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-8px_rgba(0,0,0,0.08)] dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-8px_rgba(0,0,0,0.2)]",
95
144
  hasCustomImageWidth ? "w-fit max-w-full mx-auto" : "w-full",
96
145
  ]}
97
146
  x-data="{
@@ -102,8 +151,16 @@ const captionHtml =
102
151
  style: 'visibility: hidden;',
103
152
  fullShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
104
153
  noShadow: '0 0 0 rgba(0, 0, 0, 0)',
154
+ zoomSrc: '',
155
+ activeImage() {
156
+ const darkImg = this.$refs.darkImg;
157
+ if (darkImg && window.getComputedStyle(darkImg).display !== 'none') {
158
+ return darkImg;
159
+ }
160
+ return this.$refs.lightImg;
161
+ },
105
162
  computeZoomSize() {
106
- const img = this.$refs.img;
163
+ const img = this.activeImage();
107
164
  const zoomContainer = this.$refs.zoomViewport;
108
165
  if (!img || !zoomContainer) {
109
166
  this.zoomSize = `width: min(92vw, ${this.zoomMaxWidth}px);`;
@@ -138,9 +195,12 @@ const captionHtml =
138
195
  return this.zoomSize;
139
196
  },
140
197
  async zoom() {
198
+ const img = this.activeImage();
199
+ if (!img) return;
141
200
  // 1. Lock scroll and measure
142
201
  document.body.style.overflow = 'hidden';
143
- const rect = this.$refs.img.getBoundingClientRect();
202
+ this.zoomSrc = img.currentSrc || img.src;
203
+ const rect = img.getBoundingClientRect();
144
204
 
145
205
  // 2. Prepare the zoomed image (hidden but in DOM)
146
206
  this.style = 'opacity: 0; transition: none;';
@@ -176,7 +236,9 @@ const captionHtml =
176
236
  },
177
237
  close() {
178
238
  document.body.style.overflow = 'auto';
179
- const rect = this.$refs.img.getBoundingClientRect();
239
+ const img = this.activeImage();
240
+ if (!img) return;
241
+ const rect = img.getBoundingClientRect();
180
242
  const zRect = this.$refs.zoomedImg.getBoundingClientRect();
181
243
 
182
244
  const scale = rect.width / zRect.width;
@@ -194,28 +256,46 @@ const captionHtml =
194
256
  }"
195
257
  >
196
258
  <div
197
- class="overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-(--rd-code-surface)"
259
+ class:list={["max-w-full", !hasCustomImageWidth && "w-full"]}
260
+ style={contentWidthStyle}
198
261
  >
199
- <img
200
- {...imageAttrs}
201
- x-ref="img"
202
- title={title}
203
- class:list={[
204
- "h-auto my-0! block transition-opacity",
205
- zoomEnabled && "cursor-zoom-in",
206
- hasCustomImageWidth ? "max-w-full" : "w-full",
207
- ]}
208
- :class="showZoomed ? 'opacity-0 duration-0' : 'opacity-100 duration-300'"
209
- @click={zoomEnabled ? "zoom()" : undefined}
210
- />
262
+ <div class="overflow-hidden rounded-[11px]">
263
+ <img
264
+ {...lightImageAttrs}
265
+ x-ref="lightImg"
266
+ title={title}
267
+ class:list={[
268
+ "h-auto my-0! block w-full transition-opacity",
269
+ hasDarkSource && "dark:hidden",
270
+ zoomEnabled && "cursor-zoom-in",
271
+ ]}
272
+ :class="showZoomed ? 'opacity-0 duration-0' : 'opacity-100 duration-300'"
273
+ @click={zoomEnabled ? "zoom()" : undefined}
274
+ />
275
+ {
276
+ darkImageAttrs && (
277
+ <img
278
+ {...darkImageAttrs}
279
+ x-ref="darkImg"
280
+ title={title}
281
+ class:list={[
282
+ "h-auto my-0! hidden w-full transition-opacity dark:block",
283
+ zoomEnabled && "cursor-zoom-in",
284
+ ]}
285
+ :class="showZoomed ? 'opacity-0 duration-0' : 'opacity-100 duration-300'"
286
+ @click={zoomEnabled ? "zoom()" : undefined}
287
+ />
288
+ )
289
+ }
290
+ </div>
291
+ {
292
+ hasCaption && (
293
+ <figcaption class="prose-rules mt-1! xs:mt-1.5! max-w-none! *:max-w-none! text-center text-xs! xs:text-sm! text-neutral-500 dark:text-neutral-400 leading-relaxed px-2">
294
+ <Fragment set:html={slotCaptionHtml} />
295
+ </figcaption>
296
+ )
297
+ }
211
298
  </div>
212
- {
213
- title && (
214
- <figcaption class="mt-1! xs:mt-1.5! text-center text-xs! xs:text-sm! text-neutral-500 dark:text-neutral-400 font-medium whitespace-pre-line leading-relaxed px-2">
215
- <span set:html={captionHtml} />
216
- </figcaption>
217
- )
218
- }
219
299
 
220
300
  {
221
301
  zoomEnabled && (
@@ -243,6 +323,7 @@ const captionHtml =
243
323
  <img
244
324
  {...zoomAttrs}
245
325
  x-ref="zoomedImg"
326
+ :src="zoomSrc || $el.getAttribute('src')"
246
327
  class="relative z-10 max-w-full max-h-full object-contain rounded-2xl shadow-none will-change-transform pointer-events-none"
247
328
  :style="style"
248
329
  />
@@ -21,14 +21,14 @@ validateProps(
21
21
  <div
22
22
  class:list={[
23
23
  "relative pl-10 step-item pb-4 last:pb-0 space-y-4",
24
- "before:absolute before:left-[10.5px] before:top-8 before:bottom-0 before:w-px before:bg-linear-[transparent,var(--color-neutral-200)_10%,var(--color-neutral-200)_90%,transparent] dark:before:bg-linear-[transparent,var(--color-neutral-700)_10%,var(--color-neutral-700)_90%,transparent]",
24
+ "before:mask-b-from-90% before:absolute before:left-[13.25px] before:top-[28px] before:bottom-0 before:w-[1.5px] before:bg-neutral-900/8 dark:before:bg-neutral-50/8",
25
25
  ]}
26
26
  data-step-panel
27
27
  >
28
28
  <div
29
29
  class:list={[
30
30
  "flex items-center gap-1.5 not-prose",
31
- "step-number before:size-6 before:bg-linear-to-b before:from-neutral-900/80 before:to-neutral-900 dark:before:from-neutral-100 dark:before:to-neutral-200 before:rounded-full before:text-white before:flex before:items-center before:justify-center before:text-xs before:font-bold dark:before:font-extrabold before:absolute before:left-px before:top-[3px] before:shadow-sm dark:before:bg-neutral-200 dark:before:text-neutral-900",
31
+ "step-number before:leading-none before:size-7 before:bg-neutral-900/6 dark:before:bg-neutral-50/6 before:rounded-full before:text-neutral-700 dark:before:text-neutral-100 before:flex before:items-center before:justify-center before:text-[13px] before:font-medium before:absolute before:left-0 before:top-0",
32
32
  ]}
33
33
  >
34
34
  <h3 class="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
@@ -30,12 +30,33 @@ if (labels.length === 0) {
30
30
  previousTab: null,
31
31
  transitionDirection: 1,
32
32
  isTransitioning: false,
33
- transitionDurationMs: 300,
33
+ transitionDurationMs: 400,
34
+ transitionEasing: 'cubic-bezier(0.22, 1, 0.36, 1)',
34
35
  transitionTimeout: null,
35
36
  containerHeight: 'auto',
36
37
  markerStyle: { left: null, width: null },
37
38
  resizeHandler: null,
39
+ readMotionTokens() {
40
+ const styles = window.getComputedStyle(document.documentElement);
41
+ const configuredDurationMs = Number.parseFloat(
42
+ styles.getPropertyValue('--rd-panel-transition-duration-ms'),
43
+ );
44
+ this.transitionDurationMs = Number.isFinite(configuredDurationMs)
45
+ ? configuredDurationMs
46
+ : this.transitionDurationMs;
47
+ this.transitionEasing =
48
+ styles.getPropertyValue('--rd-panel-transition-easing').trim() ||
49
+ this.transitionEasing;
50
+
51
+ if (
52
+ typeof window.matchMedia === 'function' &&
53
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches
54
+ ) {
55
+ this.transitionDurationMs = 0;
56
+ }
57
+ },
38
58
  init() {
59
+ this.readMotionTokens();
39
60
  this.resizeHandler = () => {
40
61
  this.updateMarker(this.activeTab);
41
62
  this.updateHeight(this.isTransitioning);
@@ -68,7 +89,7 @@ if (labels.length === 0) {
68
89
  this.isTransitioning = true;
69
90
  this.activeTab = index;
70
91
  this.updateMarker(this.activeTab);
71
- this.updateHeight(true);
92
+ this.updateHeight();
72
93
 
73
94
  this.transitionTimeout = window.setTimeout(() => {
74
95
  this.isTransitioning = false;
@@ -87,7 +108,7 @@ if (labels.length === 0) {
87
108
  }
88
109
  },
89
110
  getPanelStyle(index) {
90
- const base = 'position: absolute; inset: 0;';
111
+ const base = 'position: absolute; top: 0; left: 0; right: 0; width: 100%;';
91
112
 
92
113
  const isActive = index === this.activeTab;
93
114
  const isPrevious = this.isTransitioning && index === this.previousTab;
@@ -105,29 +126,23 @@ if (labels.length === 0) {
105
126
  this.transitionDirection === 1
106
127
  ? 'rd-tabs-slide-in-from-right'
107
128
  : 'rd-tabs-slide-in-from-left';
108
- return `${base} opacity: 1; pointer-events: auto; visibility: visible; z-index: 2; animation: ${animationName} ${this.transitionDurationMs}ms ease-in-out both;`;
129
+ return `${base} opacity: 1; pointer-events: auto; visibility: visible; z-index: 2; animation: ${animationName} ${this.transitionDurationMs}ms ${this.transitionEasing} both;`;
109
130
  }
110
131
 
111
132
  const animationName =
112
133
  this.transitionDirection === 1
113
134
  ? 'rd-tabs-slide-out-to-left'
114
135
  : 'rd-tabs-slide-out-to-right';
115
- return `${base} opacity: 1; pointer-events: none; visibility: visible; z-index: 1; animation: ${animationName} ${this.transitionDurationMs}ms ease-in-out both;`;
136
+ return `${base} opacity: 1; pointer-events: none; visibility: visible; z-index: 1; animation: ${animationName} ${this.transitionDurationMs}ms ${this.transitionEasing} both;`;
116
137
  },
117
- updateHeight(includePrevious = false) {
138
+ updateHeight() {
118
139
  this.$nextTick(() => {
119
140
  const activeSlide = this.$refs['content-' + this.activeTab];
120
141
  if (!activeSlide) return;
142
+ const activeContent = activeSlide.querySelector('[data-rd-tabs-panel-content]');
143
+ const measuredElement = activeContent || activeSlide;
121
144
 
122
- let nextHeight = activeSlide.scrollHeight;
123
- if (includePrevious && this.previousTab !== null) {
124
- const previousSlide = this.$refs['content-' + this.previousTab];
125
- if (previousSlide) {
126
- nextHeight = Math.max(nextHeight, previousSlide.scrollHeight);
127
- }
128
- }
129
-
130
- this.containerHeight = nextHeight + 'px';
145
+ this.containerHeight = measuredElement.scrollHeight + 'px';
131
146
  });
132
147
  }
133
148
  }"
@@ -167,19 +182,28 @@ class="rd-prose-block">
167
182
  </ul>
168
183
 
169
184
  <div
170
- class="relative mt-4 overflow-hidden transition-[height] duration-300 ease-in-out"
171
- :style="'height: ' + containerHeight"
185
+ class="relative mt-4 overflow-visible transition-[height] motion-reduce:transition-none"
186
+ :style="'height: ' + containerHeight + '; transition-duration: ' + transitionDurationMs + 'ms; transition-timing-function: ' + transitionEasing + ';'"
172
187
  >
173
- { tabContents.map((content, index) => (
174
- <div
175
- {...(index !== 0 ? { "x-cloak": true } : {})}
176
- x-ref={`content-${index}`}
177
- class="prose-rules w-full max-w-none! *:max-w-none!"
178
- :style={`getPanelStyle(${index})`}
179
- style={index === 0 ? 'position: relative;' : ''}
180
- set:html={content}
181
- />
182
- )) }
188
+ <div class="relative -mx-4 h-full overflow-x-clip overflow-y-visible">
189
+ <div class="relative mx-4 h-full">
190
+ { tabContents.map((content, index) => (
191
+ <div
192
+ {...(index !== 0 ? { "x-cloak": true } : {})}
193
+ x-ref={`content-${index}`}
194
+ class="w-full"
195
+ :style={`getPanelStyle(${index})`}
196
+ style={index === 0 ? 'position: relative;' : ''}
197
+ >
198
+ <div
199
+ data-rd-tabs-panel-content
200
+ class="prose-rules w-full max-w-none! *:max-w-none!"
201
+ set:html={content}
202
+ />
203
+ </div>
204
+ )) }
205
+ </div>
206
+ </div>
183
207
  </div>
184
208
  </div>
185
209
 
@@ -187,36 +211,44 @@ class="rd-prose-block">
187
211
  @keyframes rd-tabs-slide-in-from-right {
188
212
  from {
189
213
  transform: translateX(100%);
214
+ opacity: 0;
190
215
  }
191
216
  to {
192
217
  transform: translateX(0);
218
+ opacity: 1;
193
219
  }
194
220
  }
195
221
 
196
222
  @keyframes rd-tabs-slide-in-from-left {
197
223
  from {
198
224
  transform: translateX(-100%);
225
+ opacity: 0;
199
226
  }
200
227
  to {
201
228
  transform: translateX(0);
229
+ opacity: 1;
202
230
  }
203
231
  }
204
232
 
205
233
  @keyframes rd-tabs-slide-out-to-left {
206
234
  from {
207
235
  transform: translateX(0);
236
+ opacity: 1;
208
237
  }
209
238
  to {
210
239
  transform: translateX(-100%);
240
+ opacity: 0;
211
241
  }
212
242
  }
213
243
 
214
244
  @keyframes rd-tabs-slide-out-to-right {
215
245
  from {
216
246
  transform: translateX(0);
247
+ opacity: 1;
217
248
  }
218
249
  to {
219
250
  transform: translateX(100%);
251
+ opacity: 0;
220
252
  }
221
253
  }
222
254
  </style>