radiant-docs 0.1.41 → 0.1.42

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 (32) hide show
  1. package/package.json +1 -1
  2. package/template/astro.config.mjs +42 -40
  3. package/template/package-lock.json +7 -0
  4. package/template/package.json +1 -0
  5. package/template/src/components/Header.astro +150 -16
  6. package/template/src/components/MdxPage.astro +76 -22
  7. package/template/src/components/PagePagination.astro +44 -8
  8. package/template/src/components/Sidebar.astro +10 -1
  9. package/template/src/components/TableOfContents.astro +159 -53
  10. package/template/src/components/chat/AssistantDocsWidget.tsx +221 -8
  11. package/template/src/components/chat/AssistantEmbedPanel.tsx +1090 -104
  12. package/template/src/components/user/Accordion.astro +2 -2
  13. package/template/src/components/user/AccordionGroup.astro +1 -1
  14. package/template/src/components/user/Callout.astro +2 -2
  15. package/template/src/components/user/Card.astro +488 -0
  16. package/template/src/components/user/CardGradient.astro +964 -0
  17. package/template/src/components/user/CodeBlock.astro +1 -1
  18. package/template/src/components/user/CodeGroup.astro +1 -1
  19. package/template/src/components/user/Column.astro +25 -0
  20. package/template/src/components/user/Columns.astro +200 -0
  21. package/template/src/components/user/ComponentPreviewBlock.astro +1 -1
  22. package/template/src/components/user/Image.astro +1 -1
  23. package/template/src/components/user/Step.astro +1 -1
  24. package/template/src/components/user/Steps.astro +1 -1
  25. package/template/src/components/user/Tab.astro +1 -3
  26. package/template/src/components/user/Tabs.astro +2 -2
  27. package/template/src/layouts/Layout.astro +2 -4
  28. package/template/src/lib/assistant-chrome-defaults.ts +12 -0
  29. package/template/src/lib/assistant-embed-script.ts +209 -18
  30. package/template/src/lib/validation.ts +325 -75
  31. package/template/src/styles/global.css +81 -4
  32. package/template/src/components/chat/AskAiWidget.tsx +0 -2011
@@ -110,6 +110,9 @@ const AVAILABLE_COMPONENTS = [
110
110
  "Step",
111
111
  "Accordion",
112
112
  "AccordionGroup",
113
+ "Card",
114
+ "Column",
115
+ "Columns",
113
116
  "Image",
114
117
  "CodeGroup",
115
118
  "ComponentPreview",
@@ -166,6 +169,7 @@ export type NavbarItem = {
166
169
  text: string;
167
170
  href: string;
168
171
  icon?: string | null;
172
+ color?: string | ThemeColorByMode;
169
173
  };
170
174
  export type HiddenPageRoute = {
171
175
  filePath: string;
@@ -211,9 +215,21 @@ export type ThemeColorByMode = {
211
215
  light?: string;
212
216
  dark?: string;
213
217
  };
218
+ export type CardCoverTheme = {
219
+ colors?: string[];
220
+ colorSeed?: string;
221
+ };
222
+ export type CardButtonTheme = {
223
+ color?: string | ThemeColorByMode;
224
+ };
225
+ export type CardTheme = {
226
+ cover?: CardCoverTheme;
227
+ button?: CardButtonTheme;
228
+ };
214
229
  export type DocsTheme = {
215
230
  baseColor?: BaseColorOption | BaseColorByMode;
216
231
  themeColor?: string | ThemeColorByMode;
232
+ card?: CardTheme;
217
233
  };
218
234
  export type AssistantIcon = {
219
235
  src?: string;
@@ -222,12 +238,18 @@ export type AssistantIcon = {
222
238
  export type AssistantButtonSize = "small" | "default";
223
239
  export type AssistantButtonConfig = {
224
240
  size?: AssistantButtonSize;
241
+ color?: string | ThemeColorByMode;
242
+ };
243
+ export type AssistantNavbarButtonConfig = {
244
+ enabled?: boolean;
245
+ text?: string;
246
+ color?: string | ThemeColorByMode;
225
247
  };
226
248
  export type AssistantConfig = {
227
249
  button?: AssistantButtonConfig;
250
+ navbarButton?: AssistantNavbarButtonConfig;
228
251
  heading?: string;
229
252
  questions?: string[];
230
- themeColor?: string | ThemeColorByMode;
231
253
  icon?: AssistantIcon;
232
254
  };
233
255
  export type DocsConfig = {
@@ -340,6 +362,93 @@ function normalizeHexColor(
340
362
  return normalizedValue.toLowerCase();
341
363
  }
342
364
 
365
+ function normalizeThemeColorConfig(
366
+ value: unknown,
367
+ currentPath: Path,
368
+ label: string,
369
+ ): string | ThemeColorByMode {
370
+ if (typeof value === "string") {
371
+ return normalizeHexColor(value, currentPath, label);
372
+ }
373
+
374
+ checkType(value, "object", currentPath, label);
375
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
376
+ throwConfigError(
377
+ `${label} must be a string or an object with light/dark values.`,
378
+ currentPath,
379
+ );
380
+ }
381
+
382
+ const colorByMode = value as Record<string, unknown>;
383
+ const allowedKeys = new Set(["light", "dark"]);
384
+ for (const key of Object.keys(colorByMode)) {
385
+ if (!allowedKeys.has(key)) {
386
+ throwConfigError(`${label} object only supports 'light' and 'dark'.`, [
387
+ ...currentPath,
388
+ key,
389
+ ]);
390
+ }
391
+ }
392
+
393
+ const light =
394
+ colorByMode.light !== undefined
395
+ ? normalizeHexColor(colorByMode.light, [...currentPath, "light"], label)
396
+ : undefined;
397
+ const dark =
398
+ colorByMode.dark !== undefined
399
+ ? normalizeHexColor(colorByMode.dark, [...currentPath, "dark"], label)
400
+ : undefined;
401
+
402
+ if (light === undefined && dark === undefined) {
403
+ throwConfigError(
404
+ `${label} object must include 'light', 'dark', or both.`,
405
+ currentPath,
406
+ );
407
+ }
408
+
409
+ return {
410
+ ...(light !== undefined ? { light } : {}),
411
+ ...(dark !== undefined ? { dark } : {}),
412
+ };
413
+ }
414
+
415
+ function normalizeHexColorArray(
416
+ value: unknown,
417
+ currentPath: Path,
418
+ label: string,
419
+ ): string[] {
420
+ checkType(value, "array", currentPath, label);
421
+ if (!Array.isArray(value)) {
422
+ throwConfigError(`${label} must be an array.`, currentPath);
423
+ }
424
+
425
+ if (value.length < 1 || value.length > 4) {
426
+ throwConfigError(`${label} must include 1 to 4 colors.`, currentPath);
427
+ }
428
+
429
+ return value.map((color, index) =>
430
+ normalizeHexColor(color, [...currentPath, index], `${label} ${index + 1}`),
431
+ );
432
+ }
433
+
434
+ function normalizeSeedValue(
435
+ value: unknown,
436
+ currentPath: Path,
437
+ label: string,
438
+ ): string {
439
+ checkType(value, "string", currentPath, label);
440
+ if (typeof value !== "string") {
441
+ throwConfigError(`${label} must be a string.`, currentPath);
442
+ }
443
+
444
+ const trimmedValue = value.trim();
445
+ if (trimmedValue.length === 0) {
446
+ throwConfigError(`${label} cannot be empty.`, currentPath);
447
+ }
448
+
449
+ return trimmedValue;
450
+ }
451
+
343
452
  function validateFileExistence(filePath: string, currentPath: Path): void {
344
453
  // Assuming relative path from DOCS_DIR and .mdx extension
345
454
  const fullPath = path.join(DOCS_DIR, `${filePath}.mdx`);
@@ -1224,6 +1333,21 @@ function validateNavbarItem(
1224
1333
 
1225
1334
  // Optional property
1226
1335
  validateIcon(item.icon, [...currentPath, "icon"]);
1336
+ if (item.color !== undefined) {
1337
+ if (currentPath[0] !== "navbar" || currentPath[1] !== "primary") {
1338
+ throwConfigError(
1339
+ "Navbar item color is only supported on navbar.primary.",
1340
+ [...currentPath, "color"],
1341
+ );
1342
+ }
1343
+
1344
+ item.color = normalizeThemeColorConfig(
1345
+ item.color,
1346
+ [...currentPath, "color"],
1347
+ "Navbar primary color",
1348
+ );
1349
+ }
1350
+
1227
1351
  return hiddenPageRoute;
1228
1352
  }
1229
1353
 
@@ -1594,6 +1718,116 @@ function validateTheme(theme: DocsConfig["theme"]): void {
1594
1718
  }
1595
1719
  }
1596
1720
 
1721
+ if (theme.card !== undefined) {
1722
+ checkType(theme.card, "object", ["theme", "card"], "Theme card");
1723
+ if (
1724
+ typeof theme.card !== "object" ||
1725
+ theme.card === null ||
1726
+ Array.isArray(theme.card)
1727
+ ) {
1728
+ throwConfigError("Theme card must be an object.", ["theme", "card"]);
1729
+ }
1730
+
1731
+ const cardTheme = theme.card as CardTheme & Record<string, unknown>;
1732
+ const allowedCardKeys = new Set(["cover", "button"]);
1733
+ for (const key of Object.keys(cardTheme)) {
1734
+ if (!allowedCardKeys.has(key)) {
1735
+ throwConfigError(
1736
+ "Theme card configuration only supports 'cover' and 'button'.",
1737
+ ["theme", "card", key],
1738
+ );
1739
+ }
1740
+ }
1741
+
1742
+ if (cardTheme.cover !== undefined) {
1743
+ checkType(
1744
+ cardTheme.cover,
1745
+ "object",
1746
+ ["theme", "card", "cover"],
1747
+ "Theme card cover",
1748
+ );
1749
+ if (
1750
+ typeof cardTheme.cover !== "object" ||
1751
+ cardTheme.cover === null ||
1752
+ Array.isArray(cardTheme.cover)
1753
+ ) {
1754
+ throwConfigError("Theme card cover must be an object.", [
1755
+ "theme",
1756
+ "card",
1757
+ "cover",
1758
+ ]);
1759
+ }
1760
+
1761
+ const coverTheme = cardTheme.cover as CardCoverTheme &
1762
+ Record<string, unknown>;
1763
+ const allowedCoverKeys = new Set(["colors", "colorSeed"]);
1764
+ for (const key of Object.keys(coverTheme)) {
1765
+ if (!allowedCoverKeys.has(key)) {
1766
+ throwConfigError(
1767
+ "Theme card cover configuration only supports 'colors' and 'colorSeed'.",
1768
+ ["theme", "card", "cover", key],
1769
+ );
1770
+ }
1771
+ }
1772
+
1773
+ if (coverTheme.colors !== undefined) {
1774
+ coverTheme.colors = normalizeHexColorArray(
1775
+ coverTheme.colors,
1776
+ ["theme", "card", "cover", "colors"],
1777
+ "Theme card cover colors",
1778
+ );
1779
+ }
1780
+
1781
+ if (coverTheme.colorSeed !== undefined) {
1782
+ coverTheme.colorSeed = normalizeSeedValue(
1783
+ coverTheme.colorSeed,
1784
+ ["theme", "card", "cover", "colorSeed"],
1785
+ "Theme card cover color seed",
1786
+ );
1787
+ }
1788
+ }
1789
+
1790
+ if (cardTheme.button !== undefined) {
1791
+ checkType(
1792
+ cardTheme.button,
1793
+ "object",
1794
+ ["theme", "card", "button"],
1795
+ "Theme card button",
1796
+ );
1797
+ if (
1798
+ typeof cardTheme.button !== "object" ||
1799
+ cardTheme.button === null ||
1800
+ Array.isArray(cardTheme.button)
1801
+ ) {
1802
+ throwConfigError("Theme card button must be an object.", [
1803
+ "theme",
1804
+ "card",
1805
+ "button",
1806
+ ]);
1807
+ }
1808
+
1809
+ const buttonTheme = cardTheme.button as CardButtonTheme &
1810
+ Record<string, unknown>;
1811
+ const allowedButtonKeys = new Set(["color"]);
1812
+ for (const key of Object.keys(buttonTheme)) {
1813
+ if (!allowedButtonKeys.has(key)) {
1814
+ throwConfigError(
1815
+ "Theme card button configuration only supports 'color'.",
1816
+ ["theme", "card", "button", key],
1817
+ );
1818
+ }
1819
+ }
1820
+
1821
+ if (buttonTheme.color !== undefined) {
1822
+ buttonTheme.color = normalizeThemeColorConfig(
1823
+ buttonTheme.color,
1824
+ ["theme", "card", "button", "color"],
1825
+ "Theme card button color",
1826
+ );
1827
+ }
1828
+ }
1829
+ }
1830
+
1597
1831
  if (theme.themeColor === undefined) {
1598
1832
  return;
1599
1833
  }
@@ -1677,15 +1911,15 @@ function validateAssistant(assistant: DocsConfig["assistant"]): void {
1677
1911
 
1678
1912
  const allowedAssistantKeys = new Set([
1679
1913
  "button",
1914
+ "navbarButton",
1680
1915
  "heading",
1681
1916
  "questions",
1682
- "themeColor",
1683
1917
  "icon",
1684
1918
  ]);
1685
1919
  for (const key of Object.keys(assistant)) {
1686
1920
  if (!allowedAssistantKeys.has(key)) {
1687
1921
  throwConfigError(
1688
- "Assistant configuration only supports 'button', 'heading', 'questions', 'themeColor', and 'icon'.",
1922
+ "Assistant configuration only supports 'button', 'navbarButton', 'heading', 'questions', and 'icon'.",
1689
1923
  ["assistant", key],
1690
1924
  );
1691
1925
  }
@@ -1709,11 +1943,11 @@ function validateAssistant(assistant: DocsConfig["assistant"]): void {
1709
1943
  ]);
1710
1944
  }
1711
1945
 
1712
- const allowedButtonKeys = new Set(["size"]);
1946
+ const allowedButtonKeys = new Set(["size", "color"]);
1713
1947
  for (const key of Object.keys(assistant.button)) {
1714
1948
  if (!allowedButtonKeys.has(key)) {
1715
1949
  throwConfigError(
1716
- "Assistant button configuration only supports 'size'.",
1950
+ "Assistant button configuration only supports 'size' and 'color'.",
1717
1951
  ["assistant", "button", key],
1718
1952
  );
1719
1953
  }
@@ -1743,6 +1977,92 @@ function validateAssistant(assistant: DocsConfig["assistant"]): void {
1743
1977
  }
1744
1978
  assistant.button.size = trimmedSize;
1745
1979
  }
1980
+
1981
+ if (assistant.button.color !== undefined) {
1982
+ assistant.button.color = normalizeThemeColorConfig(
1983
+ assistant.button.color,
1984
+ ["assistant", "button", "color"],
1985
+ "Assistant button color",
1986
+ );
1987
+ }
1988
+ }
1989
+
1990
+ if (assistant.navbarButton !== undefined) {
1991
+ checkType(
1992
+ assistant.navbarButton,
1993
+ "object",
1994
+ ["assistant", "navbarButton"],
1995
+ "Assistant navbar button configuration",
1996
+ );
1997
+ if (
1998
+ typeof assistant.navbarButton !== "object" ||
1999
+ assistant.navbarButton === null ||
2000
+ Array.isArray(assistant.navbarButton)
2001
+ ) {
2002
+ throwConfigError(
2003
+ "Assistant navbar button configuration must be an object.",
2004
+ ["assistant", "navbarButton"],
2005
+ );
2006
+ }
2007
+
2008
+ const allowedNavbarButtonKeys = new Set(["enabled", "text", "color"]);
2009
+ for (const key of Object.keys(assistant.navbarButton)) {
2010
+ if (!allowedNavbarButtonKeys.has(key)) {
2011
+ throwConfigError(
2012
+ "Assistant navbar button configuration only supports 'enabled', 'text', and 'color'.",
2013
+ ["assistant", "navbarButton", key],
2014
+ );
2015
+ }
2016
+ }
2017
+
2018
+ if (assistant.navbarButton.enabled !== undefined) {
2019
+ checkType(
2020
+ assistant.navbarButton.enabled,
2021
+ "boolean",
2022
+ ["assistant", "navbarButton", "enabled"],
2023
+ "Assistant navbar button enabled",
2024
+ );
2025
+ if (typeof assistant.navbarButton.enabled !== "boolean") {
2026
+ throwConfigError(
2027
+ "Assistant navbar button enabled must be a boolean.",
2028
+ ["assistant", "navbarButton", "enabled"],
2029
+ );
2030
+ }
2031
+ }
2032
+
2033
+ if (assistant.navbarButton.text !== undefined) {
2034
+ checkType(
2035
+ assistant.navbarButton.text,
2036
+ "string",
2037
+ ["assistant", "navbarButton", "text"],
2038
+ "Assistant navbar button text",
2039
+ );
2040
+ if (typeof assistant.navbarButton.text !== "string") {
2041
+ throwConfigError("Assistant navbar button text must be a string.", [
2042
+ "assistant",
2043
+ "navbarButton",
2044
+ "text",
2045
+ ]);
2046
+ }
2047
+
2048
+ const trimmedText = assistant.navbarButton.text.trim();
2049
+ if (trimmedText.length === 0) {
2050
+ throwConfigError("Assistant navbar button text cannot be empty.", [
2051
+ "assistant",
2052
+ "navbarButton",
2053
+ "text",
2054
+ ]);
2055
+ }
2056
+ assistant.navbarButton.text = trimmedText;
2057
+ }
2058
+
2059
+ if (assistant.navbarButton.color !== undefined) {
2060
+ assistant.navbarButton.color = normalizeThemeColorConfig(
2061
+ assistant.navbarButton.color,
2062
+ ["assistant", "navbarButton", "color"],
2063
+ "Assistant navbar button color",
2064
+ );
2065
+ }
1746
2066
  }
1747
2067
 
1748
2068
  if (assistant.heading !== undefined) {
@@ -1817,76 +2137,6 @@ function validateAssistant(assistant: DocsConfig["assistant"]): void {
1817
2137
  });
1818
2138
  }
1819
2139
 
1820
- if (assistant.themeColor !== undefined) {
1821
- if (typeof assistant.themeColor === "string") {
1822
- assistant.themeColor = normalizeHexColor(
1823
- assistant.themeColor,
1824
- ["assistant", "themeColor"],
1825
- "Assistant theme color",
1826
- );
1827
- } else {
1828
- checkType(
1829
- assistant.themeColor,
1830
- "object",
1831
- ["assistant", "themeColor"],
1832
- "Assistant theme color",
1833
- );
1834
- if (
1835
- typeof assistant.themeColor !== "object" ||
1836
- assistant.themeColor === null ||
1837
- Array.isArray(assistant.themeColor)
1838
- ) {
1839
- throwConfigError(
1840
- "Assistant theme color must be a string or an object with light/dark values.",
1841
- ["assistant", "themeColor"],
1842
- );
1843
- }
1844
-
1845
- const assistantThemeColorByMode = assistant.themeColor as Record<
1846
- string,
1847
- unknown
1848
- >;
1849
- const allowedThemeColorKeys = new Set(["light", "dark"]);
1850
- for (const key of Object.keys(assistantThemeColorByMode)) {
1851
- if (!allowedThemeColorKeys.has(key)) {
1852
- throwConfigError(
1853
- "Assistant theme color object only supports 'light' and 'dark'.",
1854
- ["assistant", "themeColor", key],
1855
- );
1856
- }
1857
- }
1858
-
1859
- const light =
1860
- assistantThemeColorByMode.light !== undefined
1861
- ? normalizeHexColor(
1862
- assistantThemeColorByMode.light,
1863
- ["assistant", "themeColor", "light"],
1864
- "Assistant theme color light",
1865
- )
1866
- : undefined;
1867
- const dark =
1868
- assistantThemeColorByMode.dark !== undefined
1869
- ? normalizeHexColor(
1870
- assistantThemeColorByMode.dark,
1871
- ["assistant", "themeColor", "dark"],
1872
- "Assistant theme color dark",
1873
- )
1874
- : undefined;
1875
-
1876
- if (!light && !dark) {
1877
- throwConfigError(
1878
- "Assistant theme color object must include 'light', 'dark', or both.",
1879
- ["assistant", "themeColor"],
1880
- );
1881
- }
1882
-
1883
- assistant.themeColor = {
1884
- ...(light !== undefined ? { light } : {}),
1885
- ...(dark !== undefined ? { dark } : {}),
1886
- };
1887
- }
1888
- }
1889
-
1890
2140
  if (assistant.icon === undefined) return;
1891
2141
 
1892
2142
  checkType(
@@ -111,7 +111,7 @@
111
111
 
112
112
  /* Prose styling */
113
113
  .prose-rules {
114
- @apply *:max-w-2xl prose *:first:mt-0 *:last:mb-0 prose-h2:scroll-mt-28 prose-h3:scroll-mt-24 prose-headings:font-semibold;
114
+ @apply prose max-w-none *:my-6 *:first:mt-0 *:last:mb-0 prose-h2:mt-7 prose-h2:mb-2 prose-h2:scroll-mt-24 prose-h3:mb-2 prose-h3:scroll-mt-24 prose-headings:font-semibold prose-p:mt-0 prose-p:mb-4 prose-ol:mt-0 prose-ol:mb-5 prose-ul:mt-0 prose-ul:mb-5 prose-a:decoration-(--color-theme) prose-a:decoration-from-font prose-blockquote:border-(--color-theme)/30 dark:prose-blockquote:border-(--color-theme)/30;
115
115
  --tw-prose-body: var(--color-neutral-700);
116
116
  --tw-prose-headings: var(--color-neutral-900);
117
117
  --tw-prose-lead: var(--color-neutral-600);
@@ -134,7 +134,7 @@
134
134
  --tw-prose-pre-bg: var(--color-neutral-800);
135
135
  --tw-prose-th-borders: var(--color-neutral-300);
136
136
  --tw-prose-td-borders: var(--color-neutral-200);
137
- --tw-prose-invert-body: var(--color-neutral-300);
137
+ --tw-prose-invert-body: var(--color-neutral-200);
138
138
  --tw-prose-invert-headings: white;
139
139
  --tw-prose-invert-lead: var(--color-neutral-400);
140
140
  --tw-prose-invert-links: white;
@@ -175,7 +175,54 @@
175
175
  --tw-prose-td-borders: var(--tw-prose-invert-td-borders);
176
176
  }
177
177
 
178
- .prose-rules :is(h2, h3, h4, h5, h6) {
178
+ .rd-prose-block {
179
+ margin-block: 1.5rem;
180
+ }
181
+
182
+ .prose-rules > .rd-accordion:has(+ .rd-accordion) {
183
+ margin-block-end: 0 !important;
184
+ margin-bottom: 0 !important;
185
+ }
186
+
187
+ .prose-rules > .rd-accordion + .rd-accordion {
188
+ margin-block-start: 0 !important;
189
+ margin-top: 0 !important;
190
+ }
191
+
192
+ .prose-rules > .react-renderer,
193
+ .prose-rules > [data-node-view-content-react],
194
+ .prose-rules > [data-node-view-content-react] > .react-renderer {
195
+ margin-block: 0 !important;
196
+ max-width: none !important;
197
+ }
198
+
199
+ @layer base {
200
+ .prose-rules > .rd-prose-block:first-child,
201
+ .prose-rules > .react-renderer:first-child > .rd-prose-block,
202
+ .prose-rules > [data-node-view-content-react]:first-child > :first-child,
203
+ .prose-rules > [data-node-view-content-react] > .rd-prose-block:first-child,
204
+ .prose-rules
205
+ > [data-node-view-content-react]
206
+ > .react-renderer:first-child
207
+ > .rd-prose-block {
208
+ margin-block-start: 0 !important;
209
+ margin-top: 0 !important;
210
+ }
211
+
212
+ .prose-rules > .rd-prose-block:last-child,
213
+ .prose-rules > .react-renderer:last-child > .rd-prose-block,
214
+ .prose-rules > [data-node-view-content-react]:last-child > :last-child,
215
+ .prose-rules > [data-node-view-content-react] > .rd-prose-block:last-child,
216
+ .prose-rules
217
+ > [data-node-view-content-react]
218
+ > .react-renderer:last-child
219
+ > .rd-prose-block {
220
+ margin-block-end: 0 !important;
221
+ margin-bottom: 0 !important;
222
+ }
223
+ }
224
+
225
+ .prose-rules :is(h2, h3) {
179
226
  @apply relative;
180
227
  padding-left: 0.375rem;
181
228
  margin-left: -0.375rem;
@@ -197,8 +244,38 @@
197
244
  @apply opacity-100;
198
245
  }
199
246
 
247
+ .prose-rules
248
+ :is(h2, h3, h4, h5, h6)
249
+ > .heading-anchor[data-copy-state="copied"] {
250
+ @apply opacity-100 bg-green-500/5 text-green-700/80 dark:bg-green-900/20 dark:text-green-600/80;
251
+ }
252
+
253
+ .prose-rules .heading-anchor-icons {
254
+ @apply grid size-3.5 place-items-center;
255
+ }
256
+
200
257
  .prose-rules .heading-anchor-icon {
201
- @apply h-3.5 w-3.5;
258
+ @apply col-start-1 row-start-1 h-3.5 w-3.5 origin-center transition-all duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none;
259
+ }
260
+
261
+ .prose-rules .heading-anchor-link-icon {
262
+ @apply scale-100 rotate-0 opacity-100;
263
+ }
264
+
265
+ .prose-rules .heading-anchor-check-icon {
266
+ @apply scale-50 rotate-6 opacity-0;
267
+ }
268
+
269
+ .prose-rules
270
+ .heading-anchor[data-copy-state="copied"]
271
+ .heading-anchor-link-icon {
272
+ @apply scale-50 -rotate-6 opacity-0;
273
+ }
274
+
275
+ .prose-rules
276
+ .heading-anchor[data-copy-state="copied"]
277
+ .heading-anchor-check-icon {
278
+ @apply scale-110 rotate-0 opacity-100;
202
279
  }
203
280
 
204
281
  @media (max-width: 1023px) {