radiant-docs 0.1.46 → 0.1.48

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.
@@ -1,112 +1,167 @@
1
1
  ---
2
2
  import Icon from "../ui/Icon.astro";
3
- import warningIcon from "../../assets/icons/warning.svg?url";
4
- import infoIcon from "../../assets/icons/info.svg?url";
5
- import lightbulbIcon from "../../assets/icons/lightbulb.svg?url";
6
- import dangerIcon from "../../assets/icons/danger.svg?url";
7
- import successIcon from "../../assets/icons/check.svg?url";
8
3
  import { validateProps } from "../../lib/component-error";
9
4
 
10
5
  type CalloutType = "warning" | "info" | "tip" | "danger" | "success";
11
6
 
12
7
  interface Props {
13
8
  type?: CalloutType;
14
- title?: string;
15
- icon?: string;
9
+ title?: string | false;
10
+ icon?: string | false;
11
+ accent?: boolean;
16
12
  color?: string;
17
13
  }
18
14
 
19
15
  const typeDefaults: Record<
20
16
  CalloutType,
21
- { icon: string; color: string; iconColor: string; title: string }
17
+ { icon: string; color: string; title: string }
22
18
  > = {
23
19
  warning: {
24
- icon: warningIcon,
25
- color: "bg-amber-500",
26
- iconColor: "text-amber-500 dark:text-amber-400",
20
+ icon: "fluent:warning-16-filled",
21
+ color: "#FF990A",
27
22
  title: "Warning",
28
23
  },
29
24
  info: {
30
- icon: infoIcon,
31
- color: "bg-sky-600/80",
32
- iconColor: "text-sky-600/80 dark:text-sky-400",
25
+ icon: "fluent:info-16-filled",
26
+ color: "#5589C5",
33
27
  title: "Info",
34
28
  },
35
29
  tip: {
36
- icon: lightbulbIcon,
37
- color: "bg-yellow-500",
38
- iconColor: "text-yellow-500 dark:text-yellow-400",
30
+ icon: "fluent:lightbulb-filament-24-filled",
31
+ color: "#FFB400",
39
32
  title: "Tip",
40
33
  },
41
34
  danger: {
42
- icon: dangerIcon,
43
- color: "bg-red-600",
44
- iconColor: "text-red-600 dark:text-red-400",
35
+ icon: "fluent:diamond-dismiss-24-filled",
36
+ color: "#E5484D",
45
37
  title: "Danger",
46
38
  },
47
39
  success: {
48
- icon: successIcon,
49
- color: "bg-green-600",
50
- iconColor: "text-green-600 dark:text-green-400",
40
+ icon: "fluent:checkmark-starburst-24-filled",
41
+ color: "#46A758",
51
42
  title: "Success",
52
43
  },
53
44
  };
54
45
 
55
- const { type = "info", title, icon, color } = Astro.props;
46
+ const rawProps = Astro.props as { icon?: unknown; title?: unknown };
47
+ const rawIcon = rawProps.icon;
48
+ const rawTitle = rawProps.title;
49
+ const { type = "info", title, icon, accent = true, color } = Astro.props;
56
50
 
57
51
  validateProps(
58
52
  "Callout",
59
- { type, title, icon, color },
53
+ { type, title, icon, accent, color },
60
54
  {
61
55
  type: { enum: ["warning", "info", "tip", "danger", "success"] },
62
- title: { type: "string" },
63
- icon: { type: "string" },
56
+ title: { type: ["string", "boolean"] },
57
+ icon: { type: ["string", "boolean"] },
58
+ accent: { type: "boolean" },
64
59
  color: { type: "string" },
65
60
  },
66
61
  Astro.url.pathname,
67
62
  );
68
63
 
64
+ if (rawIcon === true) {
65
+ throw new Error(
66
+ `[USER_ERROR]: <Callout>: Invalid prop "icon": expected string or false, got true (in ${Astro.url.pathname})`,
67
+ );
68
+ }
69
+
70
+ if (rawTitle === true) {
71
+ throw new Error(
72
+ `[USER_ERROR]: <Callout>: Invalid prop "title": expected string or false, got true (in ${Astro.url.pathname})`,
73
+ );
74
+ }
75
+
69
76
  const defaults = typeDefaults[type];
70
- const resolvedTitle = title ?? defaults.title;
77
+ const resolvedTitle = title === false ? null : (title ?? defaults.title);
78
+ const resolvedIcon = icon === false ? null : (icon ?? defaults.icon);
79
+ const resolvedColor = color ?? defaults.color;
80
+ const isIconifyName = (value: string) => {
81
+ const parts = value.split(":");
82
+ return (
83
+ parts.length === 2 &&
84
+ /^[a-z0-9-]+$/i.test(parts[0]) &&
85
+ /^[a-z0-9-]+$/i.test(parts[1])
86
+ );
87
+ };
88
+ const shouldUseIconComponent =
89
+ typeof resolvedIcon === "string" && isIconifyName(resolvedIcon);
90
+ const hasTitle = typeof resolvedTitle === "string" && resolvedTitle.length > 0;
71
91
  ---
72
92
 
73
93
  <aside
74
94
  class:list={[
75
- "rd-prose-block relative space-y-1 rounded-xl border-[0.5px]. border-neutral-900/8 bg-white px-4 py-3.5 pl-6 dark:border-neutral-800 dark:bg-(--rd-code-surface) shadow-[0px_1px_3px_0px_rgba(0,0,0,0.04),0px_0px_0px_0.8px_rgba(0,0,0,0.06)_inset,0px_-1px_0px_0px_rgba(0,0,0,0.06)_inset]",
95
+ "rd-prose-block relative space-y-1 rounded-xl border-[0.5px] bg-neutral-50/70 border-neutral-900/8 dark:border-white/6 px-4 py-3.5 dark:bg-(--rd-code-surface) shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-6px_rgba(0,0,0,0.08)] dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-6px_rgba(0,0,0,0.2)]",
96
+ accent ? "pl-6" : "pl-4",
76
97
  ]}
77
98
  role="note"
78
99
  >
79
- <div
80
- class:list={[
81
- "absolute left-2.5 inset-y-2.5 w-[2px] rounded-full mb-0",
82
- defaults.color,
83
- ]}
84
- style={color && { backgroundColor: color }}
85
- >
86
- </div>
87
- <div class="flex items-center gap-2 not-prose mb-3">
88
- {
89
- icon ? (
90
- <Icon
91
- name={icon}
92
- class={`size-4 ${defaults.iconColor}`}
93
- style={color && { color: color }}
94
- />
95
- ) : (
96
- <img src={defaults.icon} alt="" width="16" height="16" class="" />
97
- )
98
- }
99
- <h6
100
- class:list={[
101
- "font-semibold text-sm text-neutral-900 dark:text-neutral-100",
102
- ]}
103
- >
104
- {resolvedTitle}
105
- </h6>
106
- </div>
107
- <div
108
- class="prose-rules prose-sm! max-w-none! *:max-w-none! text-neutral-700 dark:text-neutral-300"
109
- >
110
- <slot />
111
- </div>
100
+ {
101
+ accent && (
102
+ <div
103
+ class="absolute left-2.5 inset-y-2.5 w-[2px] rounded-full mb-0"
104
+ style={{ backgroundColor: resolvedColor }}
105
+ />
106
+ )
107
+ }
108
+ {
109
+ hasTitle ? (
110
+ <>
111
+ <div
112
+ class:list={[
113
+ "flex items-start gap-2 not-prose",
114
+ resolvedIcon && shouldUseIconComponent ? "mb-2" : "mb-1",
115
+ ]}
116
+ >
117
+ {resolvedIcon && shouldUseIconComponent ? (
118
+ <Icon
119
+ name={resolvedIcon}
120
+ class="size-5 shrink-0"
121
+ style={{ color: resolvedColor }}
122
+ />
123
+ ) : resolvedIcon ? (
124
+ <img
125
+ src={resolvedIcon}
126
+ alt=""
127
+ width="16"
128
+ height="16"
129
+ class="shrink-0 border border-white"
130
+ />
131
+ ) : null}
132
+ <h6
133
+ class:list={[
134
+ "font-semibold text-sm text-neutral-900 dark:text-neutral-100",
135
+ ]}
136
+ >
137
+ {resolvedTitle}
138
+ </h6>
139
+ </div>
140
+ <div class="prose-rules prose-sm! max-w-none! *:max-w-none! text-neutral-700 dark:text-neutral-300">
141
+ <slot />
142
+ </div>
143
+ </>
144
+ ) : (
145
+ <div class="flex items-start gap-2">
146
+ {resolvedIcon && shouldUseIconComponent ? (
147
+ <Icon
148
+ name={resolvedIcon}
149
+ class="mt-0.5 size-5 shrink-0"
150
+ style={{ color: resolvedColor }}
151
+ />
152
+ ) : resolvedIcon ? (
153
+ <img
154
+ src={resolvedIcon}
155
+ alt=""
156
+ width="16"
157
+ height="16"
158
+ class="mt-0.5 shrink-0 border border-white"
159
+ />
160
+ ) : null}
161
+ <div class="prose-rules prose-sm! max-w-none! min-w-0 flex-1 *:max-w-none! text-neutral-700 dark:text-neutral-300">
162
+ <slot />
163
+ </div>
164
+ </div>
165
+ )
166
+ }
112
167
  </aside>
@@ -199,10 +199,14 @@ const displayFilename =
199
199
  ? trimmedFilename
200
200
  : buildDefaultCodeFileName(normalizedLanguage);
201
201
 
202
- const { lines: tokenLines } = await getCodeLineTokens({
202
+ const { lines: tokenLines, themeColors } = await getCodeLineTokens({
203
203
  code: raw,
204
204
  language: normalizedLanguage,
205
205
  });
206
+ const codeThemeStyle = [
207
+ `--rd-code-line-highlight-theme-bg-light:${themeColors.lineHighlightBackground.light}`,
208
+ `--rd-code-line-highlight-theme-bg-dark:${themeColors.lineHighlightBackground.dark}`,
209
+ ].join(";");
206
210
 
207
211
  const rawLines = raw.split("\n");
208
212
  const normalizedRawLines = rawLines.length > 0 ? rawLines : [""];
@@ -244,12 +248,14 @@ const renderedCodeLinesHtml = normalizedTokenLines
244
248
 
245
249
  const lineContentClass = parsedShowLineNumbers
246
250
  ? "flex-1 whitespace-pre pr-4"
247
- : "flex-1 whitespace-pre pr-4 pl-4";
248
- const lineClass = isHighlighted
249
- ? "flex min-w-full bg-neutral-100/80 dark:bg-neutral-800/70"
250
- : "flex min-w-full";
251
+ : !parsedInCodeGroup && !parsedShowFilename
252
+ ? "flex-1 whitespace-pre pl-4 pr-8.5"
253
+ : "flex-1 whitespace-pre pl-4 pr-4";
254
+ const lineHighlightAttribute = isHighlighted
255
+ ? ' data-rd-code-line-highlighted="true"'
256
+ : "";
251
257
 
252
- return `<span class="${lineClass}">${lineNumberHtml}<span class="${lineContentClass}">${tokenHtml}</span></span>`;
258
+ return `<span class="flex min-w-full"${lineHighlightAttribute}>${lineNumberHtml}<span class="${lineContentClass}">${tokenHtml}</span></span>`;
253
259
  })
254
260
  .join("");
255
261
  ---
@@ -257,7 +263,7 @@ const renderedCodeLinesHtml = normalizedTokenLines
257
263
  <div
258
264
  class:list={[
259
265
  "group/prose-code not-prose relative w-full max-w-full min-w-0 rounded-xl",
260
- parsedInCodeGroup ? "my-0" : "rd-prose-block shadow-xs",
266
+ parsedInCodeGroup ? "my-0" : "rd-prose-block",
261
267
  ]}
262
268
  data-rd-code-block-root="true"
263
269
  data-rd-collapsed-lines={collapsedLinesValue}
@@ -268,21 +274,22 @@ const renderedCodeLinesHtml = normalizedTokenLines
268
274
  data-rd-tab-hide-icon={parsedInCodeGroup && parsedHideLanguageIcon
269
275
  ? "true"
270
276
  : undefined}
277
+ style={codeThemeStyle}
271
278
  >
272
279
  <div
273
280
  class:list={[
274
- "w-full max-w-full min-w-0 overflow-hidden border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-(--rd-code-surface)",
275
- parsedInCodeGroup ? "rounded-t-none rounded-b-xl" : "rounded-xl",
281
+ "w-full max-w-full min-w-0 bg-(--rd-code-surface)",
282
+ parsedInCodeGroup ? "rounded-tr-none rounded-xl" : "rounded-xl",
276
283
  ]}
277
284
  >
278
285
  {
279
286
  !parsedInCodeGroup && parsedShowFilename ? (
280
- <div class="flex items-center justify-between gap-2 border-b border-neutral-200 bg-neutral-50 inset-shadow-sm inset-shadow-neutral-100/80 dark:border-neutral-800 dark:bg-neutral-900/60 dark:inset-shadow-neutral-900/80">
287
+ <div class="flex items-center justify-between gap-2 border-b-[0.5px] border-(--rd-code-tab-edge-border) bg-(--rd-code-header-surface)">
281
288
  <div class="min-w-0 flex-1">
282
- <div class="relative h-9 w-fit max-w-full rounded-tl-xl bg-white dark:bg-(--rd-code-surface)">
283
- <div class="absolute inset-x-0 -bottom-px h-px bg-white dark:bg-(--rd-code-surface)" />
284
- <CodeTabEdge className="pointer-events-none absolute -top-px left-full z-10 h-[calc(100%+2px)]" />
285
- <div class="relative z-20 inline-flex h-9 max-w-full items-center gap-2 pl-5 py-1.5 text-xs font-medium text-neutral-700 dark:text-neutral-300">
289
+ <div class="relative h-9 w-fit max-w-full rounded-tl-xl bg-(--rd-code-surface)">
290
+ <div class="absolute inset-x-0 -bottom-px h-px bg-(--rd-code-surface)" />
291
+ <CodeTabEdge className="pointer-events-none absolute -top-[0.5px] left-full z-10 h-[calc(100%+1.5px)]" />
292
+ <div class="relative z-20 inline-flex h-9 max-w-full items-center gap-2 pl-5 py-1.5 text-xs font-medium text-neutral-700 dark:text-neutral-300 rounded-tl-xl border-t-[0.5px] border-l-[0.5px] border-(--rd-code-tab-edge-border)">
286
293
  {shouldRenderLanguageIcon ? (
287
294
  <CodeLanguageIcon
288
295
  language={normalizedLanguage}
@@ -294,8 +301,8 @@ const renderedCodeLinesHtml = normalizedTokenLines
294
301
  </div>
295
302
  </div>
296
303
  </div>
297
- <div class="relative h-9 w-5 shrink-0 rounded-tr-xl bg-white dark:bg-(--rd-code-surface)">
298
- <div class="absolute inset-x-0 -bottom-px h-px bg-white dark:bg-(--rd-code-surface)" />
304
+ <div class="relative h-9 w-5 shrink-0 rounded-tr-xl bg-(--rd-code-surface) border-t-[0.5px] border-r-[0.5px] border-(--rd-code-tab-edge-border)">
305
+ <div class="absolute inset-x-0 -bottom-px h-px bg-(--rd-code-surface)" />
299
306
  <CodeTabEdge className="pointer-events-none absolute -top-px right-full z-10 h-[calc(100%+2px)] rotate-y-180" />
300
307
  <button
301
308
  type="button"
@@ -323,7 +330,11 @@ const renderedCodeLinesHtml = normalizedTokenLines
323
330
  }
324
331
 
325
332
  <div
326
- class="relative min-w-0"
333
+ class:list={[
334
+ "relative min-w-0 border-[0.5px] rounded-xl overflow-hidden border-(--rd-code-tab-edge-border)",
335
+ parsedShowFilename && "border-t-0 rounded-t-none",
336
+ parsedInCodeGroup && "rounded-tl-xl border-0!",
337
+ ]}
327
338
  data-rd-code-group-panel={parsedInCodeGroup ? "true" : undefined}
328
339
  >
329
340
  {
@@ -331,7 +342,7 @@ const renderedCodeLinesHtml = normalizedTokenLines
331
342
  <div class="pointer-events-none absolute right-2 top-2 z-20">
332
343
  <button
333
344
  type="button"
334
- class="pointer-events-auto inline-flex size-7 appearance-none items-center justify-center rounded-md border border-neutral-200/80 text-neutral-500/80 bg-white/80 backdrop-blur-xs outline-none ring-0 transition-colors duration-150 hover:bg-neutral-50 hover:text-neutral-600 focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0 cursor-pointer dark:border-neutral-700/50 dark:bg-(--rd-code-surface) dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
345
+ class="pointer-events-none inline-flex size-7 appearance-none items-center justify-center rounded-md bg-(--rd-code-surface)/40 backdrop-blur-sm text-neutral-500/80 outline-none ring-0 transition-colors duration-150 hover:text-neutral-700 focus:outline-none focus-visible:outline-none focus:ring-0 focus-visible:ring-0 cursor-pointer group-hover/prose-code:pointer-events-auto group-focus-within/prose-code:pointer-events-auto dark:text-neutral-400 dark:hover:text-neutral-200 relative before:absolute before:inset-1 hover:before:inset-0 before:rounded-md hover:before:bg-neutral-900/4 dark:hover:before:bg-white/4 before:duration-150"
335
346
  data-rd-copy-trigger="true"
336
347
  data-rd-copy-content={encodedRaw}
337
348
  aria-label="Copy code"
@@ -344,7 +355,7 @@ const renderedCodeLinesHtml = normalizedTokenLines
344
355
  />
345
356
  <Icon
346
357
  name="lucide:check"
347
- class="absolute size-3.5 stroke-3 origin-center scale-25 rotate-6 opacity-0 text-green-700/80 transition-all duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform motion-reduce:transition-none dark:text-green-400/90"
358
+ class="absolute size-3.5 stroke-3 origin-center scale-25 rotate-6 opacity-0 text-green-800/70 transition-all duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform motion-reduce:transition-none dark:text-green-400/90"
348
359
  data-rd-copy-check
349
360
  aria-hidden="true"
350
361
  />
@@ -372,7 +383,7 @@ const renderedCodeLinesHtml = normalizedTokenLines
372
383
  class="relative overflow-x-auto [scrollbar-width:thin] [scrollbar-color:var(--color-neutral-300)_transparent] [&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-neutral-300/70 hover:[&::-webkit-scrollbar-thumb]:bg-neutral-300/90 dark:[scrollbar-color:var(--color-neutral-700)_transparent] dark:[&::-webkit-scrollbar-thumb]:bg-neutral-700/70 dark:hover:[&::-webkit-scrollbar-thumb]:bg-neutral-700/90"
373
384
  >
374
385
  <pre
375
- class="relative m-0 min-w-full bg-white p-0 text-[13px] leading-6 dark:bg-(--rd-code-surface)"><code data-rd-code-theme class="block min-w-full py-2.5 font-mono text-neutral-800 dark:text-neutral-200"><Fragment set:html={renderedCodeLinesHtml} /></code></pre>
386
+ class="relative m-0 min-w-full bg-(--rd-code-surface) p-0 text-[13px] leading-6"><code data-rd-code-theme class="block min-w-full py-2.5 font-mono text-neutral-800 dark:text-neutral-200"><Fragment set:html={renderedCodeLinesHtml} /></code></pre>
376
387
  </div>
377
388
  </div>
378
389
  </div>
@@ -4,11 +4,11 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
4
4
  ---
5
5
 
6
6
  <div
7
- class="rd-prose-block group/prose-code-group not-prose relative w-full max-w-full min-w-0 shadow-xs rounded-xl"
7
+ class="rd-prose-block group/prose-code-group not-prose relative w-full max-w-full min-w-0 rounded-xl"
8
8
  data-rd-code-group-root="true"
9
9
  >
10
10
  <div
11
- class="relative z-10 overflow-visible rounded-t-xl border border-b-0 border-neutral-200 bg-neutral-50 inset-shadow-sm inset-shadow-neutral-100/80 dark:border-neutral-800 dark:bg-neutral-900/60 dark:inset-shadow-neutral-900/80"
11
+ class="relative z-10 overflow-visible rounded-t-xl bg-(--rd-code-header-surface)"
12
12
  >
13
13
  <div class="flex min-w-0 items-end justify-between gap-2">
14
14
  <div class="min-w-0 flex-1 overflow-hidden rounded-t-xl">
@@ -19,18 +19,16 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
19
19
  <div
20
20
  data-rd-code-group-pill
21
21
  aria-hidden="true"
22
- class="pointer-events-none absolute top-1/2 z-0 h-[28px] -translate-y-1/2 rounded-[9px] border-[0.5px] border-neutral-200 bg-white opacity-0 transition-[left,width,opacity] duration-200 ease-out dark:border-neutral-700/70 dark:bg-(--rd-code-surface)"
22
+ class="pointer-events-none absolute top-1/2 z-0 h-[28px] -translate-y-1/2 rounded-[9px] border-[0.5px] border-(--rd-code-tab-edge-border) bg-(--rd-code-surface) opacity-0 transition-[left,width,opacity] duration-200 ease-out"
23
23
  >
24
24
  </div>
25
25
  </div>
26
26
  </div>
27
27
 
28
28
  <div
29
- class="relative h-9 w-5 shrink-0 rounded-tr-xl bg-white dark:bg-(--rd-code-surface)"
29
+ class="relative h-9 w-5 shrink-0 rounded-tr-xl bg-(--rd-code-surface) border-t-[0.5px] border-r-[0.5px] border-(--rd-code-tab-edge-border)"
30
30
  >
31
- <div
32
- class="absolute inset-x-0 -bottom-px h-px bg-white dark:bg-(--rd-code-surface)"
33
- >
31
+ <div class="absolute inset-x-0 -bottom-px h-px bg-(--rd-code-surface)">
34
32
  </div>
35
33
  <CodeTabEdge
36
34
  className="pointer-events-none absolute -top-px right-full z-10 h-[calc(100%+2px)] rotate-y-180"
@@ -49,7 +47,7 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
49
47
  />
50
48
  <Icon
51
49
  name="lucide:check"
52
- class="absolute size-3.5 stroke-3 origin-center scale-25 rotate-6 opacity-0 text-green-700/80 transition-all duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform motion-reduce:transition-none dark:text-green-400/90"
50
+ class="absolute size-3.5 stroke-3 origin-center scale-25 rotate-6 opacity-0 text-green-800/70 transition-all duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform motion-reduce:transition-none dark:text-green-400/90"
53
51
  data-rd-copy-check
54
52
  aria-hidden="true"
55
53
  />
@@ -60,7 +58,7 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
60
58
 
61
59
  <div
62
60
  data-rd-code-group-content
63
- class="relative min-w-0 overflow-hidden transition-[height] duration-300 ease-in-out"
61
+ class="relative min-w-0 overflow-hidden transition-[height] duration-[360ms] ease-[cubic-bezier(0.22,1,0.36,1)] bg-(--rd-code-surface) rounded-xl rounded-tr-none border-[0.5px] border-(--rd-code-tab-edge-border)"
64
62
  >
65
63
  <slot />
66
64
  </div>
@@ -93,7 +91,8 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
93
91
  const prefersReducedMotion =
94
92
  typeof window.matchMedia === "function" &&
95
93
  window.matchMedia("(prefers-reduced-motion: reduce)").matches;
96
- const TRANSITION_DURATION_MS = prefersReducedMotion ? 0 : 300;
94
+ const TRANSITION_DURATION_MS = prefersReducedMotion ? 0 : 360;
95
+ const TRANSITION_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
97
96
 
98
97
  let activeIndex = 0;
99
98
  let transitionTimeoutId = null;
@@ -139,7 +138,7 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
139
138
 
140
139
  const tabButton = document.createElement("button");
141
140
  tabButton.type = "button";
142
- tabButton.className = `relative inline-flex h-9 items-center border-0 bg-transparent px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors duration-150 focus:outline-none focus-visible:outline-none cursor-pointer ${hasTabIcon ? "gap-2" : ""}`;
141
+ tabButton.className = `relative inline-flex h-9 items-center border-0 bg-transparent px-3 py-1.5 text-xs font-medium transition-colors duration-150 focus:outline-none focus-visible:outline-none cursor-pointer ${hasTabIcon ? "gap-2" : ""}`;
143
142
  tabButton.setAttribute("aria-label", filename);
144
143
  tabButton.setAttribute("data-rd-code-group-tab", String(index));
145
144
 
@@ -205,12 +204,11 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
205
204
  const updateTabButtonStates = () => {
206
205
  tabs.forEach(({ tabButton, iconContainer }, index) => {
207
206
  const isActive = index === activeIndex;
208
- tabButton.classList.toggle("text-foreground", isActive);
209
- tabButton.classList.toggle("text-muted-foreground", !isActive);
210
-
207
+ tabButton.classList.toggle("text-neutral-500/80", !isActive);
208
+ tabButton.classList.toggle("dark:text-neutral-400/80", !isActive);
211
209
  if (!iconContainer) return;
212
- iconContainer.classList.toggle("opacity-100", isActive);
213
210
  iconContainer.classList.toggle("opacity-80", !isActive);
211
+ iconContainer.classList.toggle("grayscale-100", !isActive);
214
212
  });
215
213
  syncPill();
216
214
  };
@@ -269,15 +267,31 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
269
267
  activeItemResizeObserver.observe(activeItem);
270
268
  };
271
269
 
270
+ const setItemToTransitionPosition = (itemElement) => {
271
+ itemElement.style.position = "absolute";
272
+ itemElement.style.inset = "";
273
+ itemElement.style.top = "0";
274
+ itemElement.style.right = "0";
275
+ itemElement.style.bottom = "";
276
+ itemElement.style.left = "0";
277
+ };
278
+
272
279
  const setPanelsToRestState = () => {
273
280
  tabs.forEach(({ itemElement, panelElement, frameElement }, index) => {
274
281
  const isActive = index === activeIndex;
275
282
  itemElement.style.position = isActive ? "relative" : "absolute";
276
- itemElement.style.inset = isActive ? "" : "0";
283
+ itemElement.style.inset = "";
284
+ itemElement.style.top = isActive ? "" : "0";
285
+ itemElement.style.right = isActive ? "" : "0";
286
+ itemElement.style.bottom = isActive ? "" : "0";
287
+ itemElement.style.left = isActive ? "" : "0";
277
288
  itemElement.style.opacity = isActive ? "1" : "0";
278
289
  itemElement.style.visibility = isActive ? "visible" : "hidden";
279
290
  itemElement.style.pointerEvents = isActive ? "auto" : "none";
280
291
  itemElement.style.zIndex = isActive ? "1" : "0";
292
+ itemElement.style.transform = "translateX(0)";
293
+ itemElement.style.animation = "none";
294
+ itemElement.style.willChange = "auto";
281
295
  panelElement.style.transform = "translateX(0)";
282
296
  panelElement.style.animation = "none";
283
297
  panelElement.style.willChange = "auto";
@@ -313,8 +327,6 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
313
327
  const nextItem = nextTab?.itemElement;
314
328
  const previousPanel = previousTab?.panelElement;
315
329
  const nextPanel = nextTab?.panelElement;
316
- const previousFrame = previousTab?.frameElement;
317
- const nextFrame = nextTab?.frameElement;
318
330
  if (!previousItem || !nextItem || !previousPanel || !nextPanel) {
319
331
  normalizeToCurrentState();
320
332
  return;
@@ -331,37 +343,35 @@ import CodeTabEdge from "../ui/CodeTabEdge.astro";
331
343
  });
332
344
  }
333
345
 
334
- previousItem.style.position = "absolute";
335
- previousItem.style.inset = "0";
346
+ setItemToTransitionPosition(previousItem);
336
347
  previousItem.style.opacity = "1";
337
348
  previousItem.style.visibility = "visible";
338
349
  previousItem.style.pointerEvents = "none";
339
350
  previousItem.style.zIndex = "1";
351
+ previousItem.style.transform = "translateX(0)";
352
+ previousItem.style.willChange = "transform, opacity";
340
353
  previousPanel.style.transform = "translateX(0)";
341
- previousPanel.style.willChange = "transform";
342
- if (previousFrame) {
343
- previousFrame.style.height = "100%";
344
- }
345
- previousPanel.style.animation =
354
+ previousPanel.style.animation = "none";
355
+ previousPanel.style.willChange = "auto";
356
+ previousItem.style.animation =
346
357
  direction === 1
347
- ? `rd-code-group-slide-out-to-left ${TRANSITION_DURATION_MS}ms ease-in-out both`
348
- : `rd-code-group-slide-out-to-right ${TRANSITION_DURATION_MS}ms ease-in-out both`;
358
+ ? `rd-code-group-slide-out-to-left ${TRANSITION_DURATION_MS}ms ${TRANSITION_EASING} both`
359
+ : `rd-code-group-slide-out-to-right ${TRANSITION_DURATION_MS}ms ${TRANSITION_EASING} both`;
349
360
 
350
- nextItem.style.position = "absolute";
351
- nextItem.style.inset = "0";
361
+ setItemToTransitionPosition(nextItem);
352
362
  nextItem.style.opacity = "1";
353
363
  nextItem.style.visibility = "visible";
354
364
  nextItem.style.pointerEvents = "auto";
355
365
  nextItem.style.zIndex = "2";
366
+ nextItem.style.transform = "translateX(0)";
367
+ nextItem.style.willChange = "transform, opacity";
356
368
  nextPanel.style.transform = "translateX(0)";
357
- nextPanel.style.willChange = "transform";
358
- if (nextFrame) {
359
- nextFrame.style.height = "100%";
360
- }
361
- nextPanel.style.animation =
369
+ nextPanel.style.animation = "none";
370
+ nextPanel.style.willChange = "auto";
371
+ nextItem.style.animation =
362
372
  direction === 1
363
- ? `rd-code-group-slide-in-from-right ${TRANSITION_DURATION_MS}ms ease-in-out both`
364
- : `rd-code-group-slide-in-from-left ${TRANSITION_DURATION_MS}ms ease-in-out both`;
373
+ ? `rd-code-group-slide-in-from-right ${TRANSITION_DURATION_MS}ms ${TRANSITION_EASING} both`
374
+ : `rd-code-group-slide-in-from-left ${TRANSITION_DURATION_MS}ms ${TRANSITION_EASING} both`;
365
375
 
366
376
  transitionTimeoutId = window.setTimeout(() => {
367
377
  transitionTimeoutId = null;