starlight-theme-nova 0.10.0 → 0.11.1

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 (36) hide show
  1. package/lib/styles.css +1 -2
  2. package/lib/tailwind.css +130 -0
  3. package/lib/tailwind.gen.css +1313 -0
  4. package/package.json +13 -17
  5. package/src/components/CodeCopy.astro +7 -5
  6. package/src/components/CodeTabs.astro +4 -19
  7. package/src/components/Header.astro +19 -7
  8. package/src/components/LinkButton.astro +15 -7
  9. package/src/components/LinkButtonIcon.astro +11 -9
  10. package/src/components/LinkCard.astro +17 -3
  11. package/src/components/MobileMenuFooter.astro +1 -1
  12. package/src/components/MobileMenuToggle.astro +12 -2
  13. package/src/components/MobileTableOfContents.astro +1 -1
  14. package/src/components/PageFrame.astro +18 -21
  15. package/src/components/Pagination.astro +5 -2
  16. package/src/components/PaginationLink.astro +17 -8
  17. package/src/components/Search.astro +3 -2
  18. package/src/components/SiteTitle.astro +5 -1
  19. package/src/components/SocialIcons.astro +5 -1
  20. package/src/components/ThemeSelect.astro +8 -4
  21. package/src/constants.ts +2 -0
  22. package/src/icons/lucide-arrow-left.svg +1 -0
  23. package/src/icons/lucide-arrow-right.svg +1 -0
  24. package/src/icons/lucide-check.svg +1 -0
  25. package/src/icons/lucide-chevron-left.svg +1 -0
  26. package/src/icons/lucide-chevron-right.svg +1 -0
  27. package/src/icons/lucide-clipboard.svg +1 -0
  28. package/src/icons/lucide-menu.svg +1 -0
  29. package/src/icons/lucide-search.svg +1 -0
  30. package/src/icons/lucide-x.svg +1 -0
  31. package/src/icons/tabler-moon-filled.svg +1 -0
  32. package/src/icons/tabler-sun-high-filled.svg +1 -0
  33. package/src/index.ts +43 -2
  34. package/src/shiki-transformer-container.ts +6 -3
  35. package/src/user-options.ts +16 -0
  36. package/lib/styles.gen.css +0 -214
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "starlight-theme-nova",
3
3
  "type": "module",
4
- "version": "0.10.0",
4
+ "version": "0.11.1",
5
5
  "description": "A beautiful theme for Astro Starlight",
6
6
  "author": "ocavue <ocavue@gmail.com>",
7
7
  "license": "MIT",
@@ -28,9 +28,9 @@
28
28
  "dependencies": {
29
29
  "@aria-ui/core": "^0.0.22",
30
30
  "@pagefind/default-ui": "^1.4.0",
31
- "@shikijs/transformers": "^3.15.0",
32
- "@shikijs/twoslash": "^3.15.0",
33
- "@shikijs/types": "^3.15.0",
31
+ "@shikijs/transformers": "^3.21.0",
32
+ "@shikijs/twoslash": "^3.21.0",
33
+ "@shikijs/types": "^3.21.0",
34
34
  "@types/hast": "^3.0.4",
35
35
  "astro-theme-toggle": "^0.8.0",
36
36
  "hast-util-is-element": "^3.0.0",
@@ -47,21 +47,17 @@
47
47
  }
48
48
  },
49
49
  "devDependencies": {
50
- "@astrojs/starlight": "^0.36.2",
51
- "@iconify-json/bxl": "^1.2.2",
52
- "@iconify-json/logos": "^1.2.10",
53
- "@iconify-json/lucide": "^1.2.72",
54
- "@iconify-json/tabler": "^1.2.23",
55
- "@ocavue/tsconfig": "^0.5.0",
50
+ "@astrojs/starlight": "^0.37.2",
51
+ "@ocavue/tsconfig": "^0.6.2",
52
+ "@tailwindcss/cli": "^4.1.18",
53
+ "@tailwindcss/vite": "^4.1.18",
56
54
  "@types/node": "^20.17.30",
57
- "@unocss/cli": "^66.5.5",
58
- "astro": "^5.15.4",
59
- "typescript": "^5.9.3",
60
- "unocss": "^66.5.5",
61
- "unocss-preset-animations": "^1.3.0"
55
+ "astro": "^5.16.8",
56
+ "tailwindcss": "^4.1.18",
57
+ "typescript": "^5.9.3"
62
58
  },
63
59
  "scripts": {
64
- "dev": "unocss -w",
65
- "build": "unocss"
60
+ "build": "pnpm run build:tailwindcss",
61
+ "build:tailwindcss": "tailwindcss -i ./lib/tailwind.css -o ./lib/tailwind.gen.css"
66
62
  }
67
63
  }
@@ -1,6 +1,6 @@
1
1
  <script>
2
- const DEFAULT_HTML = /* html */ `<span class="nova-code-copy-button-icon-clipboard"></span>`
3
- const SUCCESS_HTML = /* html */ `<span class="nova-code-copy-button-icon-check"></span>`
2
+ import ClipboardIcon from '../icons/lucide-clipboard.svg?raw'
3
+ import CheckIcon from '../icons/lucide-check.svg?raw'
4
4
 
5
5
  class CodeCopyButton extends HTMLElement {
6
6
  constructor() {
@@ -8,10 +8,12 @@
8
8
  }
9
9
 
10
10
  connectedCallback() {
11
- this.innerHTML = DEFAULT_HTML
12
-
13
11
  const update = () => {
14
- this.innerHTML = state === 'ready' ? DEFAULT_HTML : SUCCESS_HTML
12
+ const iconHtml = state === 'ready' ? ClipboardIcon : CheckIcon
13
+ this.innerHTML =
14
+ '<span class="w-full h-full block [&_svg]:w-full [&_svg]:h-full">' +
15
+ iconHtml +
16
+ '</span>'
15
17
  }
16
18
 
17
19
  let state: 'ready' | 'success' = 'ready'
@@ -20,10 +20,8 @@ const processedHtml = processCodeTabs(originalHtml)
20
20
  </div>
21
21
 
22
22
  <style is:global>
23
- @layer nova {
23
+ @layer utilities {
24
24
  .sl-markdown-content .code-tabs {
25
- overflow: hidden;
26
-
27
25
  &.code-tabs-border {
28
26
  border-color: var(--sl-color-gray-5);
29
27
  border-width: 1px;
@@ -31,17 +29,11 @@ const processedHtml = processCodeTabs(originalHtml)
31
29
  border-radius: 0.5rem;
32
30
  }
33
31
 
34
- starlight-tabs {
35
- margin-top: 0;
32
+ [role='tablist'] {
33
+ border-bottom-width: 1px;
36
34
  }
37
35
 
38
36
  .tablist-wrapper {
39
- scrollbar-width: none;
40
-
41
- [role='tablist'] {
42
- border-bottom-width: 1px;
43
- }
44
-
45
37
  [role='tab'] {
46
38
  font-family: var(--sl-font-system-mono);
47
39
  padding-top: 0.5rem;
@@ -50,13 +42,6 @@ const processedHtml = processCodeTabs(originalHtml)
50
42
  padding-right: 0.75rem;
51
43
  font-size: 0.875rem;
52
44
  white-space: nowrap;
53
-
54
- border-bottom-style: solid;
55
- border-bottom-width: 0px;
56
-
57
- &[aria-selected='true'] {
58
- border-bottom-width: 3px;
59
- }
60
45
  }
61
46
  }
62
47
 
@@ -68,7 +53,7 @@ const processedHtml = processCodeTabs(originalHtml)
68
53
  border-radius: 0;
69
54
  }
70
55
 
71
- .nova-code-container {
56
+ [data-nova-code-container] {
72
57
  border-radius: 0;
73
58
  border-width: 0;
74
59
  border-color: transparent;
@@ -16,30 +16,42 @@ const nav = options.nav ?? []
16
16
  const route = Astro.locals.starlightRoute
17
17
  ---
18
18
 
19
- <div class="nova-header">
20
- <div class="nova-header-title">
19
+ <div class="box-border flex h-full items-center gap-2">
20
+ <div
21
+ class:list={[
22
+ /* Prevent long titles overflowing and covering the search and menu buttons on narrow viewports. */
23
+ /* Avoid clipping focus ring around link inside title wrapper. */
24
+ '-m-3 overflow-clip p-3',
25
+ 'flex min-w-0',
26
+ ]}
27
+ >
21
28
  <SiteTitle />
22
29
  </div>
23
- <nav class="nova-header-nav">
30
+ <nav
31
+ class="flex flex-1 flex-row gap-4 overflow-x-auto py-3 pr-4 pl-4 text-sm font-medium max-md:**:hidden xl:gap-6 xl:pl-6"
32
+ >
24
33
  {
25
34
  nav.map((item) => (
26
- <a class="nova-header-nav-link" href={getI18nText(item.href, route)}>
35
+ <a
36
+ class="-m-1.5 rounded-md p-1.5 text-(--sl-color-gray-3) no-underline hover:text-(--sl-color-white) focus-visible:outline-offset-0"
37
+ href={getI18nText(item.href, route)}
38
+ >
27
39
  {getI18nText(item.label, route)}
28
40
  </a>
29
41
  ))
30
42
  }
31
43
  </nav>
32
- <div class="nova-header-search">
44
+ <div class="flex md:max-w-60 md:flex-1 print:hidden">
33
45
  <Search />
34
46
  </div>
35
- <div class="nova-header-actions-lg">
47
+ <div class="hidden items-center gap-2 md:flex print:hidden">
36
48
  <SocialIcons />
37
49
  <LanguageSelect />
38
50
  <ThemeSelect />
39
51
  </div>
40
52
  {
41
53
  hasSidebar && (
42
- <div class="nova-header-actions-sm">
54
+ <div class="flex items-center gap-2 md:hidden print:hidden">
43
55
  <MobileMenuToggle />
44
56
  </div>
45
57
  )
@@ -19,15 +19,23 @@ const {
19
19
  } = Astro.props
20
20
  ---
21
21
 
22
- <span class="not-content">
22
+ <span
23
+ class:list={[
24
+ 'not-content',
25
+ 'inline-flex items-center justify-center align-middle',
26
+ ]}
27
+ >
23
28
  <a
24
29
  class:list={[
25
- 'nova-link-button not-content',
26
- {
27
- 'nova-link-button-primary': variant === 'primary',
28
- 'nova-link-button-secondary': variant === 'secondary',
29
- 'nova-link-button-minimal': variant === 'minimal',
30
- },
30
+ 'not-content',
31
+ 'group me-2 mt-2 mb-2 inline-flex items-center justify-between gap-2 rounded-xl border border-solid border-transparent px-6 py-3 font-medium no-underline transition active:scale-97',
32
+ variant === 'primary'
33
+ ? 'bg-black text-white shadow-sm hover:bg-gray-800 hover:shadow-md dark:bg-gray-100 dark:text-gray-900 dark:hover:bg-gray-300'
34
+ : undefined,
35
+ variant === 'secondary'
36
+ ? 'border-(--sl-color-gray-5) bg-white text-gray-700 shadow-sm hover:bg-gray-50 hover:shadow-md dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700'
37
+ : undefined,
38
+ variant === 'minimal' ? 'text-gray-700 dark:text-gray-200' : undefined,
31
39
  className,
32
40
  ]}
33
41
  {...attrs}
@@ -1,20 +1,22 @@
1
1
  ---
2
2
  import { Icon } from '@astrojs/starlight/components'
3
3
  import type { StarlightIcon } from '@astrojs/starlight/types'
4
+ import ArrowRightIcon from '../icons/lucide-arrow-right.svg'
5
+ import ArrowLeftIcon from '../icons/lucide-arrow-left.svg'
4
6
 
5
7
  interface Props {
6
8
  icon: StarlightIcon
7
9
  }
8
10
 
9
11
  const { icon } = Astro.props
10
-
11
- let className = ''
12
-
13
- if (icon === 'right-arrow') {
14
- className = 'nova-link-button-icon-right'
15
- } else if (icon === 'left-arrow') {
16
- className = 'nova-link-button-icon-left'
17
- }
18
12
  ---
19
13
 
20
- {className ? <div class={className} /> : <Icon name={icon} size="1.25rem" />}
14
+ {
15
+ icon === 'right-arrow' ? (
16
+ <ArrowRightIcon class="block size-5 transition group-hover:translate-x-1" />
17
+ ) : icon === 'left-arrow' ? (
18
+ <ArrowLeftIcon class="block size-5 transition group-hover:-translate-x-1" />
19
+ ) : (
20
+ <Icon name={icon} size="1.25rem" />
21
+ )
22
+ }
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from 'astro/types'
3
+ import ArrowRightIcon from '../icons/lucide-arrow-right.svg'
3
4
 
4
5
  interface Props extends Omit<HTMLAttributes<'a'>, 'title'> {
5
6
  title: string
@@ -9,10 +10,23 @@ interface Props extends Omit<HTMLAttributes<'a'>, 'title'> {
9
10
  const { title, description, class: className, ...attributes } = Astro.props
10
11
  ---
11
12
 
12
- <div class:list={['nova-link-card not-content', className]}>
13
- <a class="nova-link-card-link" {...attributes}>
13
+ <div
14
+ class:list={[
15
+ 'not-content',
16
+ 'group relative flex flex-col gap-2 rounded-xl border border-solid border-(--sl-color-gray-5) bg-white px-5 py-4 text-gray-700 shadow-sm transition hover:bg-gray-50 hover:shadow-md active:scale-99 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700',
17
+ className,
18
+ ]}
19
+ >
20
+ <a
21
+ class:list={[
22
+ 'inline-flex items-center justify-between text-lg font-semibold text-gray-700 no-underline dark:text-gray-200',
23
+ /* a11y fix for https://github.com/withastro/starlight/issues/487 */
24
+ "before:absolute before:inset-0 before:content-['']",
25
+ ]}
26
+ {...attributes}
27
+ >
14
28
  <span set:html={title} />
15
- <div class="nova-link-card-icon"></div>
29
+ <ArrowRightIcon class="ml-2 size-5 transition group-hover:translate-x-1" />
16
30
  </a>
17
31
  {description && <span set:html={description} />}
18
32
  </div>
@@ -4,7 +4,7 @@ import SocialIcons from 'virtual:starlight/components/SocialIcons'
4
4
  import ThemeSelect from 'virtual:starlight/components/ThemeSelect'
5
5
  ---
6
6
 
7
- <div class="nova-mobile-menu-footer">
7
+ <div class="flex items-center justify-end gap-2 py-4 print:hidden">
8
8
  <SocialIcons />
9
9
  <LanguageSelect />
10
10
  <ThemeSelect />
@@ -1,9 +1,19 @@
1
+ ---
2
+ import MenuIcon from '../icons/lucide-menu.svg'
3
+ import XIcon from '../icons/lucide-x.svg'
4
+ ---
5
+
1
6
  <nova-mobile-menu-toggle
2
- class="print:hidden nova-icon-button"
7
+ class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90 print:hidden"
3
8
  aria-label={Astro.locals.t('menuButton.accessibleLabel')}
4
9
  aria-controls="starlight__sidebar"
5
10
  >
6
- <div class="nova-mobile-menu-toggle-icon"></div>
11
+ <div
12
+ class="transition-transform [body[data-mobile-menu-expanded]_&]:rotate-90"
13
+ >
14
+ <MenuIcon class="block [body[data-mobile-menu-expanded]_&]:hidden" />
15
+ <XIcon class="hidden [body[data-mobile-menu-expanded]_&]:block" />
16
+ </div>
7
17
  </nova-mobile-menu-toggle>
8
18
 
9
19
  <script>
@@ -6,7 +6,7 @@ const { toc } = Astro.locals.starlightRoute
6
6
 
7
7
  {
8
8
  toc && (
9
- <div class="nova-mobile-table-of-contents">
9
+ <div class="[&_nav]:backdrop-blur [&_summary]:border-b-(--sl-color-hairline)">
10
10
  <Default />
11
11
  </div>
12
12
  )
@@ -2,8 +2,10 @@
2
2
  const { hasSidebar } = Astro.locals.starlightRoute
3
3
  ---
4
4
 
5
- <div class="nova-page-frame">
6
- <header class="nova-page-frame-header">
5
+ <div class="flex min-h-screen flex-col">
6
+ <header
7
+ class="fixed inset-0 z-(--sl-z-index-navbar) box-border h-(--sl-nav-height) w-full border-0 border-b border-solid border-(--sl-color-hairline) bg-(--sl-color-bg-nav) px-(--sl-nav-pad-x) py-(--sl-nav-pad-y) backdrop-blur"
8
+ >
7
9
  <slot name="header" />
8
10
  </header>
9
11
  {
@@ -15,9 +17,21 @@ const { hasSidebar } = Astro.locals.starlightRoute
15
17
  >
16
18
  <div
17
19
  id="starlight__sidebar"
18
- class:list={['sidebar-pane', 'nova-page-frame-sidebar-pane']}
20
+ class:list={[
21
+ 'nova-scroll-container',
22
+
23
+ 'invisible md:visible [body[data-mobile-menu-expanded]_&]:max-md:visible',
24
+
25
+ 'fixed top-(--sl-nav-height) bottom-0 left-0 z-(--sl-z-index-menu) overflow-y-auto border-0 border-solid border-(--sl-color-hairline) bg-(--sl-color-black) max-md:w-full md:w-(--sl-sidebar-width) md:border-r',
26
+ ]}
19
27
  >
20
- <div class="sidebar-content sl-flex">
28
+ <div
29
+ class:list={[
30
+ // The class name `sidebar-content` is used here: https://github.com/withastro/starlight/blob/5387d33b9644900afd61651b8287503698cdb065/packages/starlight/user-components/Badge.astro#L77
31
+ 'sidebar-content',
32
+ 'flex h-full min-h-max flex-col px-(--sl-sidebar-pad-x) py-4',
33
+ ]}
34
+ >
21
35
  <slot name="sidebar" />
22
36
  </div>
23
37
  </div>
@@ -29,23 +43,6 @@ const { hasSidebar } = Astro.locals.starlightRoute
29
43
 
30
44
  <style>
31
45
  @layer nova {
32
- .sidebar-pane {
33
- position: fixed;
34
- z-index: var(--sl-z-index-menu);
35
- inset-block: var(--sl-nav-height) 0;
36
- inset-inline-start: 0;
37
- background-color: var(--sl-color-black);
38
- overflow-y: auto;
39
- }
40
-
41
- .sidebar-content {
42
- height: 100%;
43
- min-height: max-content;
44
- padding: 1rem var(--sl-sidebar-pad-x) 1rem;
45
- flex-direction: column;
46
- gap: 1rem;
47
- }
48
-
49
46
  .main-frame {
50
47
  padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
51
48
  padding-inline-start: var(--sl-content-inline-start);
@@ -6,7 +6,10 @@ const { prev, next } = pagination
6
6
  const isRtl = dir === 'rtl'
7
7
  ---
8
8
 
9
- <div class="print:hidden nova-pagination" dir={dir}>
9
+ <div
10
+ class="flex min-w-full flex-row items-stretch justify-between gap-2 px-1 pt-0 pb-6 print:hidden"
11
+ dir={dir}
12
+ >
10
13
  {
11
14
  prev && (
12
15
  <PaginationLink
@@ -17,7 +20,7 @@ const isRtl = dir === 'rtl'
17
20
  />
18
21
  )
19
22
  }
20
- <div class="nova-pagination-divider"></div>
23
+ <div class="flex-1"></div>
21
24
  {
22
25
  next && (
23
26
  <PaginationLink
@@ -1,4 +1,7 @@
1
1
  ---
2
+ import ChevronRightIcon from '../icons/lucide-chevron-right.svg'
3
+ import ChevronLeftIcon from '../icons/lucide-chevron-left.svg'
4
+
2
5
  interface Props {
3
6
  label: string
4
7
  href: string
@@ -9,13 +12,19 @@ interface Props {
9
12
  const { label, href, rel, side } = Astro.props
10
13
  ---
11
14
 
12
- <a href={href} rel={rel} data-side={side} class:list={['nova-pagination-link']}>
15
+ <a
16
+ href={href}
17
+ rel={rel}
18
+ data-side={side}
19
+ class="group m-0 flex items-center justify-end gap-2 rounded-xl p-2 font-medium text-(--sl-color-gray-2) no-underline transition hover:text-(--sl-color-white) md:text-lg [[rel='next']]:flex-row [[rel='prev']]:flex-row-reverse"
20
+ >
13
21
  <span>{label}</span>
14
- <span
15
- class:list={[
16
- 'nova-pagination-link-icon',
17
- side === 'right'
18
- ? 'nova-pagination-link-icon-right'
19
- : 'nova-pagination-link-icon-left',
20
- ]}></span>
22
+
23
+ {
24
+ side === 'right' ? (
25
+ <ChevronRightIcon class="block min-h-5 min-w-5 transition group-hover:translate-x-1" />
26
+ ) : (
27
+ <ChevronLeftIcon class="block min-h-5 min-w-5 transition group-hover:-translate-x-1" />
28
+ )
29
+ }
21
30
  </a>
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  import project from 'virtual:starlight/project-context'
3
+ import SearchIcon from '../icons/lucide-search.svg'
3
4
 
4
5
  const pagefindTranslations = {
5
6
  placeholder: Astro.locals.t('search.label'),
@@ -19,13 +20,13 @@ if (project.trailingSlash === 'never')
19
20
 
20
21
  <site-search class={Astro.props.class} {...dataAttributes}>
21
22
  <button
22
- class="nova-search-button"
23
+ class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90 md:h-9 md:w-full md:max-w-88 md:rounded-md md:border md:border-solid md:border-(--sl-color-gray-5) md:p-2 md:text-(--sl-color-text) md:transition-colors md:hover:bg-gray-400/10"
23
24
  data-open-modal
24
25
  disabled
25
26
  aria-label={Astro.locals.t('search.label')}
26
27
  aria-keyshortcuts="Control+K"
27
28
  >
28
- <span class="nova-search-button-icon"></span>
29
+ <SearchIcon class="size-4" />
29
30
  <span class="sl-hidden md:sl-block" aria-hidden="true"
30
31
  >{Astro.locals.t('search.label')}</span
31
32
  >
@@ -2,4 +2,8 @@
2
2
  import Default from '@astrojs/starlight/components/SiteTitle.astro'
3
3
  ---
4
4
 
5
- <span class="nova-site-title"><Default /></span>
5
+ <span
6
+ class="-m-1.5 rounded-md p-1.5 **:text-lg **:font-semibold **:text-(--sl-color-text) focus-visible:outline-offset-0"
7
+ >
8
+ <Default />
9
+ </span>
@@ -17,7 +17,11 @@ if (!Array.isArray(links)) {
17
17
  links.length > 0 && (
18
18
  <>
19
19
  {links.map(({ label, href, icon }) => (
20
- <a href={href} rel="me" class="nova-icon-button">
20
+ <a
21
+ href={href}
22
+ rel="me"
23
+ class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90"
24
+ >
21
25
  <span class="sr-only">{label}</span>
22
26
  <Icon name={icon} size="1rem" color="currentColor" />
23
27
  </a>
@@ -1,14 +1,18 @@
1
1
  ---
2
2
  import { Toggle } from 'astro-theme-toggle/components'
3
+ import MoonIcon from '../icons/tabler-moon-filled.svg'
4
+ import SunIcon from '../icons/tabler-sun-high-filled.svg'
3
5
  ---
4
6
 
5
- <div class="nova-theme-select">
6
- <Toggle class="nova-icon-button">
7
+ <div class="size-8 overflow-visible">
8
+ <Toggle
9
+ class="flex size-8 rounded-md p-2 text-(--sl-color-text) transition hover:bg-gray-400/30 focus-visible:outline-offset-1 active:scale-90"
10
+ >
7
11
  <Fragment slot="icon-light">
8
- <div class="nova-theme-select-icon-light"></div>
12
+ <SunIcon class="size-4" />
9
13
  </Fragment>
10
14
  <Fragment slot="icon-dark">
11
- <div class="nova-theme-select-icon-dark"></div>
15
+ <MoonIcon class="size-4" />
12
16
  </Fragment>
13
17
  </Toggle>
14
18
  </div>
package/src/constants.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export const PAGE_TITLE_ID = '_top'
2
2
 
3
+ // data-nova-code-container
3
4
  export const CODE_PROPERTY_CONTAINER = 'dataNovaCodeContainer'
4
5
 
6
+ // data-nova-code-title
5
7
  export const CODE_PROPERTY_TITLE = 'dataNovaCodeTitle'
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7-7l7 7l-7 7"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 6L9 17l-5-5"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m15 18l-6-6l6-6"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m9 18l6-6l-6-6"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5h16M4 12h16M4 19h16"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m21 21l-4.34-4.34"/><circle cx="11" cy="11" r="8"/></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Lucide by Lucide Contributors - https://github.com/lucide-icons/lucide/blob/main/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 6L6 18M6 6l12 12"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Tabler Icons by Paweł Kuna - https://github.com/tabler/tabler-icons/blob/master/LICENSE --><path fill="currentColor" d="M12 1.992a10 10 0 1 0 9.236 13.838c.341-.82-.476-1.644-1.298-1.31a6.5 6.5 0 0 1-6.864-10.787l.077-.08c.551-.63.113-1.653-.758-1.653h-.266l-.068-.006z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><!-- Icon from Tabler Icons by Paweł Kuna - https://github.com/tabler/tabler-icons/blob/master/LICENSE --><path fill="currentColor" d="M12 19a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1m-4.95-2.05a1 1 0 0 1 0 1.414l-1.414 1.414a1 1 0 1 1-1.414-1.414l1.414-1.414a1 1 0 0 1 1.414 0m11.314 0l1.414 1.414a1 1 0 0 1-1.414 1.414l-1.414-1.414a1 1 0 0 1 1.414-1.414m-5.049-9.836a5 5 0 1 1-2.532 9.674a5 5 0 0 1 2.532-9.674M4 11a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2zm18 0a1 1 0 0 1 0 2h-2a1 1 0 0 1 0-2zM5.636 4.222L7.05 5.636A1 1 0 0 1 5.636 7.05L4.222 5.636a1 1 0 0 1 1.414-1.414m14.142 0a1 1 0 0 1 0 1.414L18.364 7.05a1 1 0 0 1-1.414-1.414l1.414-1.414a1 1 0 0 1 1.414 0M12 1a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0V2a1 1 0 0 1 1-1"/></svg>
package/src/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type {
2
+ StarlightConfig,
2
3
  StarlightPlugin,
3
4
  StarlightUserConfig,
4
5
  } from '@astrojs/starlight/types'
6
+ import type { AstroConfig } from 'astro'
5
7
  import remarkCustomHeaderId from 'remark-custom-header-id'
6
8
 
7
9
  import { createShikiConfig } from './shiki-config'
@@ -25,7 +27,7 @@ const components = {
25
27
  'starlight-theme-nova/components/MobileTableOfContents.astro',
26
28
  MobileMenuFooter: 'starlight-theme-nova/components/MobileMenuFooter.astro',
27
29
  LanguageSelect: 'starlight-theme-nova/components/LanguageSelect.astro',
28
- } as const
30
+ } as const satisfies Partial<StarlightConfig['components']>
29
31
 
30
32
  export type { ThemeNovaOptions }
31
33
 
@@ -35,13 +37,29 @@ export default function starlightThemeNova(
35
37
  return {
36
38
  name: 'starlight-theme-nova',
37
39
  hooks: {
38
- setup({ config, updateConfig, addIntegration, astroConfig }) {
40
+ setup: async ({ config, updateConfig, addIntegration, astroConfig }) => {
41
+ let useTailwind: boolean
42
+
43
+ if (options.stylingSystem === 'css') {
44
+ useTailwind = false
45
+ } else if (options.stylingSystem === 'tailwind') {
46
+ useTailwind = true
47
+ } else {
48
+ const hasTailwindcss = await checkHasTailwindcss(
49
+ astroConfig.vite?.plugins,
50
+ )
51
+ useTailwind = hasTailwindcss
52
+ }
53
+
39
54
  const newConfig = {
40
55
  customCss: [
41
56
  ...(config.customCss || []),
42
57
  // Including nova styles *after* any user CSS, so that @layer nova
43
58
  // can have a higher precedence.
44
59
  'starlight-theme-nova/styles.css',
60
+ useTailwind
61
+ ? 'starlight-theme-nova/tailwind.css'
62
+ : 'starlight-theme-nova/tailwind.gen.css',
45
63
  ],
46
64
  components: {
47
65
  // Including any user components *after* our own.
@@ -78,3 +96,26 @@ export default function starlightThemeNova(
78
96
  },
79
97
  }
80
98
  }
99
+
100
+ type ViteUserConfig = AstroConfig['vite']
101
+ type VitePlugin = NonNullable<ViteUserConfig['plugins']>[number]
102
+
103
+ async function checkHasTailwindcss(
104
+ plugin: VitePlugin | Promise<VitePlugin>,
105
+ ): Promise<boolean> {
106
+ const awaited = await plugin
107
+
108
+ if (!awaited) {
109
+ return false
110
+ }
111
+ if (Array.isArray(awaited)) {
112
+ for (const p of awaited) {
113
+ if (await checkHasTailwindcss(p)) {
114
+ return true
115
+ }
116
+ }
117
+ return false
118
+ }
119
+ let name = awaited.name || ''
120
+ return name.includes('tailwind')
121
+ }