@baybreezy/docd 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/assets/css/tailwind.css +2 -1
- package/app/components/docs/DocsMobileBar.vue +3 -2
- package/app/components/docs/DocsThemeCustomizer.vue +21 -204
- package/app/components/docs/DocsThemeCustomizerCore.vue +209 -0
- package/app/components/page/PageMobileToc.vue +1 -4
- package/app/layouts/docs.vue +1 -1
- package/modules/css.ts +44 -0
- package/nuxt.config.ts +1 -0
- package/package.json +5 -8
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
>
|
|
6
6
|
<DocsLogo />
|
|
7
7
|
<div class="flex items-center gap-1">
|
|
8
|
+
<DocsThemeCustomizer v-if="!hideThemeCustomizer" />
|
|
8
9
|
<DocsSearchButton v-if="!hideSearch" />
|
|
9
10
|
<UiButton
|
|
10
11
|
variant="ghost"
|
|
11
12
|
size="icon-sm"
|
|
12
|
-
class="size-7
|
|
13
|
+
class="size-7"
|
|
13
14
|
aria-label="Open navigation"
|
|
14
15
|
@click="navOpen = true"
|
|
15
16
|
>
|
|
@@ -21,6 +22,6 @@
|
|
|
21
22
|
</template>
|
|
22
23
|
|
|
23
24
|
<script lang="ts" setup>
|
|
24
|
-
const { isDashed, hideSearch } = useDocd();
|
|
25
|
+
const { isDashed, hideSearch, hideThemeCustomizer } = useDocd();
|
|
25
26
|
const navOpen = ref(false);
|
|
26
27
|
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<UiPopover>
|
|
2
|
+
<UiPopover v-if="isDesktop">
|
|
3
3
|
<UiTooltip>
|
|
4
4
|
<UiTooltipTrigger as-child>
|
|
5
5
|
<UiPopoverTrigger as-child>
|
|
@@ -15,215 +15,32 @@
|
|
|
15
15
|
align="end"
|
|
16
16
|
:side-offset="8"
|
|
17
17
|
>
|
|
18
|
-
<
|
|
19
|
-
<AnimatePresence>
|
|
20
|
-
<Motion
|
|
21
|
-
v-if="active"
|
|
22
|
-
class="absolute inset-0 rounded-md bg-linear-to-b from-foreground/10 via-foreground/5"
|
|
23
|
-
:initial="{ opacity: 0, scaleY: 0, transformOrigin: 'top left', scaleX: 0 }"
|
|
24
|
-
:animate="{ opacity: 1, scaleY: 1, scaleX: 1 }"
|
|
25
|
-
:exit="{ opacity: 0, scaleY: 0, scaleX: 0 }"
|
|
26
|
-
:transition="{ duration: 0.2, ease: 'easeOut' }"
|
|
27
|
-
/>
|
|
28
|
-
</AnimatePresence>
|
|
29
|
-
</DefineBgActive>
|
|
30
|
-
|
|
31
|
-
<!-- Header -->
|
|
32
|
-
<div class="grid space-y-1">
|
|
33
|
-
<h1 class="text-base font-semibold text-foreground">Customize</h1>
|
|
34
|
-
<p class="text-sm text-muted-foreground">Pick a style and color for your components.</p>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<!-- Color -->
|
|
38
|
-
<div class="space-y-1.5 pt-6">
|
|
39
|
-
<UiLabel class="text-sm font-semibold">Color</UiLabel>
|
|
40
|
-
<RovingFocusGroup loop class="grid grid-cols-3 gap-2 py-1.5">
|
|
41
|
-
<RovingFocusItem v-for="t in themes" :key="t.value" as-child>
|
|
42
|
-
<UiButton
|
|
43
|
-
variant="outline"
|
|
44
|
-
size="sm"
|
|
45
|
-
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
46
|
-
:style="{
|
|
47
|
-
borderColor: activeTheme === t.value ? 'var(--primary)' : 'var(--border)',
|
|
48
|
-
}"
|
|
49
|
-
@click="setTheme(t.value)"
|
|
50
|
-
>
|
|
51
|
-
<BgActive :active="activeTheme === t.value" />
|
|
52
|
-
<span class="relative flex items-center gap-2">
|
|
53
|
-
<span
|
|
54
|
-
class="flex size-5 shrink-0 items-center justify-center rounded-full ring-1 ring-border/60 transition-transform duration-200"
|
|
55
|
-
:class="
|
|
56
|
-
activeTheme === t.value ? 'scale-110' : 'scale-100 group-hover:scale-105'
|
|
57
|
-
"
|
|
58
|
-
:style="{ backgroundColor: t.color }"
|
|
59
|
-
>
|
|
60
|
-
<AnimatePresence>
|
|
61
|
-
<Motion
|
|
62
|
-
v-if="activeTheme === t.value"
|
|
63
|
-
:initial="{ scale: 0, opacity: 0 }"
|
|
64
|
-
:animate="{ scale: 1, opacity: 1 }"
|
|
65
|
-
:exit="{ scale: 0, opacity: 0 }"
|
|
66
|
-
:transition="{ duration: 0.2, delay: 0.1, ease: 'backOut' }"
|
|
67
|
-
>
|
|
68
|
-
<Icon name="lucide:check" class="size-3 text-white" />
|
|
69
|
-
</Motion>
|
|
70
|
-
</AnimatePresence>
|
|
71
|
-
</span>
|
|
72
|
-
<span class="text-xs capitalize">{{ t.label }}</span>
|
|
73
|
-
</span>
|
|
74
|
-
</UiButton>
|
|
75
|
-
</RovingFocusItem>
|
|
76
|
-
</RovingFocusGroup>
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
<!-- Radius -->
|
|
80
|
-
<div class="space-y-1.5 pt-4">
|
|
81
|
-
<UiLabel class="text-sm font-semibold">Radius</UiLabel>
|
|
82
|
-
<RovingFocusGroup loop class="grid grid-cols-5 gap-2 py-1.5">
|
|
83
|
-
<RovingFocusItem v-for="r in radii" :key="r.value" as-child>
|
|
84
|
-
<UiButton
|
|
85
|
-
variant="outline"
|
|
86
|
-
size="sm"
|
|
87
|
-
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
88
|
-
:style="{
|
|
89
|
-
borderColor: activeRadius === r.value ? 'var(--primary)' : 'var(--border)',
|
|
90
|
-
}"
|
|
91
|
-
@click="setRadius(r.value)"
|
|
92
|
-
>
|
|
93
|
-
<BgActive :active="activeRadius === r.value" />
|
|
94
|
-
<Motion
|
|
95
|
-
class="relative text-xs"
|
|
96
|
-
:initial="false"
|
|
97
|
-
:animate="{ scale: activeRadius === r.value ? 1.05 : 1 }"
|
|
98
|
-
:transition="{ duration: 0.2, ease: 'easeOut' }"
|
|
99
|
-
>
|
|
100
|
-
{{ r.label }}
|
|
101
|
-
</Motion>
|
|
102
|
-
</UiButton>
|
|
103
|
-
</RovingFocusItem>
|
|
104
|
-
</RovingFocusGroup>
|
|
105
|
-
</div>
|
|
106
|
-
|
|
107
|
-
<!-- Theme / Mode -->
|
|
108
|
-
<div class="space-y-1.5 pt-4">
|
|
109
|
-
<UiLabel class="text-sm font-semibold">Theme</UiLabel>
|
|
110
|
-
<RovingFocusGroup loop class="flex space-x-2 py-1.5">
|
|
111
|
-
<RovingFocusItem as-child>
|
|
112
|
-
<UiButton
|
|
113
|
-
variant="outline"
|
|
114
|
-
size="sm"
|
|
115
|
-
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
116
|
-
:style="{
|
|
117
|
-
borderColor: colorMode.value === 'light' ? 'var(--primary)' : 'var(--border)',
|
|
118
|
-
}"
|
|
119
|
-
@click="setMode('light')"
|
|
120
|
-
>
|
|
121
|
-
<BgActive :active="colorMode.value === 'light'" />
|
|
122
|
-
<span class="relative flex items-center justify-center">
|
|
123
|
-
<Icon name="lucide:sun" class="mr-2 size-4" />
|
|
124
|
-
<span class="text-xs">Light</span>
|
|
125
|
-
</span>
|
|
126
|
-
</UiButton>
|
|
127
|
-
</RovingFocusItem>
|
|
128
|
-
<RovingFocusItem as-child>
|
|
129
|
-
<UiButton
|
|
130
|
-
variant="outline"
|
|
131
|
-
size="sm"
|
|
132
|
-
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
133
|
-
:style="{
|
|
134
|
-
borderColor: colorMode.value === 'dark' ? 'var(--primary)' : 'var(--border)',
|
|
135
|
-
}"
|
|
136
|
-
@click="setMode('dark')"
|
|
137
|
-
>
|
|
138
|
-
<BgActive :active="colorMode.value === 'dark'" />
|
|
139
|
-
<span class="relative flex items-center justify-center">
|
|
140
|
-
<Icon name="lucide:moon" class="mr-2 size-4" />
|
|
141
|
-
<span class="text-xs">Dark</span>
|
|
142
|
-
</span>
|
|
143
|
-
</UiButton>
|
|
144
|
-
</RovingFocusItem>
|
|
145
|
-
</RovingFocusGroup>
|
|
146
|
-
</div>
|
|
18
|
+
<DocsThemeCustomizerCore />
|
|
147
19
|
</UiPopoverContent>
|
|
148
20
|
<UiTooltipContent> Customize theme </UiTooltipContent>
|
|
149
21
|
</UiTooltip>
|
|
150
22
|
</UiPopover>
|
|
23
|
+
<UiDrawer v-else>
|
|
24
|
+
<UiDrawerTrigger as-child>
|
|
25
|
+
<UiButton size="icon-sm" variant="ghost">
|
|
26
|
+
<Icon name="lucide:palette" />
|
|
27
|
+
</UiButton>
|
|
28
|
+
</UiDrawerTrigger>
|
|
29
|
+
<UiDrawerContent class="w-full px-4 pb-6">
|
|
30
|
+
<UiDrawerHeader>
|
|
31
|
+
<UiDrawerTitle class="sr-only">Theme Customizer</UiDrawerTitle>
|
|
32
|
+
<UiDrawerDescription class="sr-only"
|
|
33
|
+
>Pick a style and color for your components.</UiDrawerDescription
|
|
34
|
+
>
|
|
35
|
+
</UiDrawerHeader>
|
|
36
|
+
<DocsThemeCustomizerCore />
|
|
37
|
+
</UiDrawerContent>
|
|
38
|
+
</UiDrawer>
|
|
151
39
|
</template>
|
|
152
40
|
|
|
153
41
|
<script lang="ts" setup>
|
|
154
|
-
import {
|
|
155
|
-
|
|
156
|
-
const [DefineBgActive, BgActive] = createReusableTemplate<{ active: boolean }>({
|
|
157
|
-
inheritAttrs: false,
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
const colorMode = useColorMode();
|
|
161
|
-
|
|
162
|
-
const themes = [
|
|
163
|
-
{ label: "Stone", value: "theme-stone", color: "oklch(0.216 0.006 56.043)" },
|
|
164
|
-
{ label: "Zinc", value: "theme-zinc", color: "oklch(0.21 0.006 285.885)" },
|
|
165
|
-
{ label: "Neutral", value: "theme-neutral", color: "oklch(0.205 0 0)" },
|
|
166
|
-
{ label: "Gray", value: "theme-gray", color: "oklch(0.21 0.034 264.665)" },
|
|
167
|
-
{ label: "Slate", value: "theme-slate", color: "oklch(0.208 0.042 265.755)" },
|
|
168
|
-
{ label: "Red", value: "theme-red", color: "oklch(0.637 0.237 25.331)" },
|
|
169
|
-
{ label: "Rose", value: "theme-rose", color: "oklch(0.645 0.246 16.439)" },
|
|
170
|
-
{ label: "Orange", value: "theme-orange", color: "oklch(0.705 0.213 47.604)" },
|
|
171
|
-
{ label: "Green", value: "theme-green", color: "oklch(0.723 0.219 149.579)" },
|
|
172
|
-
{ label: "Blue", value: "theme-blue", color: "oklch(0.623 0.214 259.815)" },
|
|
173
|
-
{ label: "Yellow", value: "theme-yellow", color: "oklch(0.795 0.184 86.047)" },
|
|
174
|
-
{ label: "Violet", value: "theme-violet", color: "oklch(0.606 0.25 292.717)" },
|
|
175
|
-
{ label: "Nuxt", value: "theme-nuxt", color: "oklch(0.79 0.19 155.63)" },
|
|
176
|
-
{ label: "Mauve", value: "theme-mauve", color: "oklch(0.212 0.019 322.12)" },
|
|
177
|
-
{ label: "Olive", value: "theme-olive", color: "oklch(0.228 0.013 107.4)" },
|
|
178
|
-
{ label: "Mist", value: "theme-mist", color: "oklch(0.218 0.008 223.9)" },
|
|
179
|
-
{ label: "Taupe", value: "theme-taupe", color: "oklch(0.214 0.009 43.1)" },
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
const radii = [
|
|
183
|
-
{ label: "0", value: "0rem" },
|
|
184
|
-
{ label: "0.25", value: "0.25rem" },
|
|
185
|
-
{ label: "0.5", value: "0.5rem" },
|
|
186
|
-
{ label: "0.625", value: "0.625rem" },
|
|
187
|
-
{ label: "0.75", value: "0.75rem" },
|
|
188
|
-
{ label: "1", value: "1rem" },
|
|
189
|
-
];
|
|
190
|
-
|
|
191
|
-
const THEME_KEY = "docs-color-theme";
|
|
192
|
-
const RADIUS_KEY = "docs-radius";
|
|
193
|
-
|
|
194
|
-
const activeTheme = ref("theme-zinc");
|
|
195
|
-
const activeRadius = ref("0.625rem");
|
|
196
|
-
|
|
197
|
-
onMounted(() => {
|
|
198
|
-
const savedTheme = localStorage.getItem(THEME_KEY);
|
|
199
|
-
const savedRadius = localStorage.getItem(RADIUS_KEY);
|
|
200
|
-
if (savedTheme) applyTheme(savedTheme, false);
|
|
201
|
-
if (savedRadius) applyRadius(savedRadius, false);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
function applyTheme(value: string, save = true) {
|
|
205
|
-
themes.forEach((t) => document.documentElement.classList.remove(t.value));
|
|
206
|
-
document.documentElement.classList.add(value);
|
|
207
|
-
activeTheme.value = value;
|
|
208
|
-
if (save) localStorage.setItem(THEME_KEY, value);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function applyRadius(value: string, save = true) {
|
|
212
|
-
document.documentElement.style.setProperty("--radius", value);
|
|
213
|
-
activeRadius.value = value;
|
|
214
|
-
if (save) localStorage.setItem(RADIUS_KEY, value);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function setTheme(value: string) {
|
|
218
|
-
applyTheme(value);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function setRadius(value: string) {
|
|
222
|
-
applyRadius(value);
|
|
223
|
-
}
|
|
42
|
+
import { breakpointsTailwind } from "@vueuse/core";
|
|
224
43
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
colorMode.preference = value;
|
|
228
|
-
}
|
|
44
|
+
const bp = useBreakpoints(breakpointsTailwind);
|
|
45
|
+
const isDesktop = bp.greaterOrEqual("lg");
|
|
229
46
|
</script>
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<DefineBgActive v-slot="{ active }">
|
|
4
|
+
<AnimatePresence>
|
|
5
|
+
<Motion
|
|
6
|
+
v-if="active"
|
|
7
|
+
class="absolute inset-0 rounded-md bg-linear-to-b from-foreground/10 via-foreground/5"
|
|
8
|
+
:initial="{ opacity: 0, scaleY: 0, transformOrigin: 'top left', scaleX: 0 }"
|
|
9
|
+
:animate="{ opacity: 1, scaleY: 1, scaleX: 1 }"
|
|
10
|
+
:exit="{ opacity: 0, scaleY: 0, scaleX: 0 }"
|
|
11
|
+
:transition="{ duration: 0.2, ease: 'easeOut' }"
|
|
12
|
+
/>
|
|
13
|
+
</AnimatePresence>
|
|
14
|
+
</DefineBgActive>
|
|
15
|
+
|
|
16
|
+
<!-- Header -->
|
|
17
|
+
<div class="grid space-y-1">
|
|
18
|
+
<h1 class="text-base font-semibold text-foreground">Customize</h1>
|
|
19
|
+
<p class="text-sm text-muted-foreground">Pick a style and color for your components.</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<!-- Color -->
|
|
23
|
+
<div class="space-y-1.5 pt-6">
|
|
24
|
+
<UiLabel class="text-sm font-semibold">Color</UiLabel>
|
|
25
|
+
<RovingFocusGroup loop class="grid grid-cols-3 gap-2 py-1.5">
|
|
26
|
+
<RovingFocusItem v-for="t in themes" :key="t.value" as-child>
|
|
27
|
+
<UiButton
|
|
28
|
+
variant="outline"
|
|
29
|
+
size="sm"
|
|
30
|
+
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
31
|
+
:style="{
|
|
32
|
+
borderColor: activeTheme === t.value ? 'var(--primary)' : 'var(--border)',
|
|
33
|
+
}"
|
|
34
|
+
@click="setTheme(t.value)"
|
|
35
|
+
>
|
|
36
|
+
<BgActive :active="activeTheme === t.value" />
|
|
37
|
+
<span class="relative flex items-center gap-2">
|
|
38
|
+
<span
|
|
39
|
+
class="flex size-5 shrink-0 items-center justify-center rounded-full ring-1 ring-border/60 transition-transform duration-200"
|
|
40
|
+
:class="activeTheme === t.value ? 'scale-110' : 'scale-100 group-hover:scale-105'"
|
|
41
|
+
:style="{ backgroundColor: t.color }"
|
|
42
|
+
>
|
|
43
|
+
<AnimatePresence>
|
|
44
|
+
<Motion
|
|
45
|
+
v-if="activeTheme === t.value"
|
|
46
|
+
:initial="{ scale: 0, opacity: 0 }"
|
|
47
|
+
:animate="{ scale: 1, opacity: 1 }"
|
|
48
|
+
:exit="{ scale: 0, opacity: 0 }"
|
|
49
|
+
:transition="{ duration: 0.2, delay: 0.1, ease: 'backOut' }"
|
|
50
|
+
>
|
|
51
|
+
<Icon name="lucide:check" class="size-3 text-white" />
|
|
52
|
+
</Motion>
|
|
53
|
+
</AnimatePresence>
|
|
54
|
+
</span>
|
|
55
|
+
<span class="text-xs capitalize">{{ t.label }}</span>
|
|
56
|
+
</span>
|
|
57
|
+
</UiButton>
|
|
58
|
+
</RovingFocusItem>
|
|
59
|
+
</RovingFocusGroup>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Radius -->
|
|
63
|
+
<div class="space-y-1.5 pt-4">
|
|
64
|
+
<UiLabel class="text-sm font-semibold">Radius</UiLabel>
|
|
65
|
+
<RovingFocusGroup loop class="grid grid-cols-5 gap-2 py-1.5">
|
|
66
|
+
<RovingFocusItem v-for="r in radii" :key="r.value" as-child>
|
|
67
|
+
<UiButton
|
|
68
|
+
variant="outline"
|
|
69
|
+
size="sm"
|
|
70
|
+
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
71
|
+
:style="{
|
|
72
|
+
borderColor: activeRadius === r.value ? 'var(--primary)' : 'var(--border)',
|
|
73
|
+
}"
|
|
74
|
+
@click="setRadius(r.value)"
|
|
75
|
+
>
|
|
76
|
+
<BgActive :active="activeRadius === r.value" />
|
|
77
|
+
<Motion
|
|
78
|
+
class="relative text-xs"
|
|
79
|
+
:initial="false"
|
|
80
|
+
:animate="{ scale: activeRadius === r.value ? 1.05 : 1 }"
|
|
81
|
+
:transition="{ duration: 0.2, ease: 'easeOut' }"
|
|
82
|
+
>
|
|
83
|
+
{{ r.label }}
|
|
84
|
+
</Motion>
|
|
85
|
+
</UiButton>
|
|
86
|
+
</RovingFocusItem>
|
|
87
|
+
</RovingFocusGroup>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Theme / Mode -->
|
|
91
|
+
<div class="space-y-1.5 pt-4">
|
|
92
|
+
<UiLabel class="text-sm font-semibold">Theme</UiLabel>
|
|
93
|
+
<RovingFocusGroup loop class="flex space-x-2 py-1.5">
|
|
94
|
+
<RovingFocusItem as-child>
|
|
95
|
+
<UiButton
|
|
96
|
+
variant="outline"
|
|
97
|
+
size="sm"
|
|
98
|
+
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
99
|
+
:style="{
|
|
100
|
+
borderColor: colorMode.value === 'light' ? 'var(--primary)' : 'var(--border)',
|
|
101
|
+
}"
|
|
102
|
+
@click="setMode('light')"
|
|
103
|
+
>
|
|
104
|
+
<BgActive :active="colorMode.value === 'light'" />
|
|
105
|
+
<span class="relative flex items-center justify-center">
|
|
106
|
+
<Icon name="lucide:sun" class="mr-2 size-4" />
|
|
107
|
+
<span class="text-xs">Light</span>
|
|
108
|
+
</span>
|
|
109
|
+
</UiButton>
|
|
110
|
+
</RovingFocusItem>
|
|
111
|
+
<RovingFocusItem as-child>
|
|
112
|
+
<UiButton
|
|
113
|
+
variant="outline"
|
|
114
|
+
size="sm"
|
|
115
|
+
class="group relative justify-start overflow-hidden bg-transparent px-3 transition-all duration-200"
|
|
116
|
+
:style="{
|
|
117
|
+
borderColor: colorMode.value === 'dark' ? 'var(--primary)' : 'var(--border)',
|
|
118
|
+
}"
|
|
119
|
+
@click="setMode('dark')"
|
|
120
|
+
>
|
|
121
|
+
<BgActive :active="colorMode.value === 'dark'" />
|
|
122
|
+
<span class="relative flex items-center justify-center">
|
|
123
|
+
<Icon name="lucide:moon" class="mr-2 size-4" />
|
|
124
|
+
<span class="text-xs">Dark</span>
|
|
125
|
+
</span>
|
|
126
|
+
</UiButton>
|
|
127
|
+
</RovingFocusItem>
|
|
128
|
+
</RovingFocusGroup>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</template>
|
|
132
|
+
|
|
133
|
+
<script lang="ts" setup>
|
|
134
|
+
import { RovingFocusGroup, RovingFocusItem } from "reka-ui";
|
|
135
|
+
|
|
136
|
+
const [DefineBgActive, BgActive] = createReusableTemplate<{ active: boolean }>({
|
|
137
|
+
inheritAttrs: false,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const radii = [
|
|
141
|
+
{ label: "0", value: "0rem" },
|
|
142
|
+
{ label: "0.25", value: "0.25rem" },
|
|
143
|
+
{ label: "0.5", value: "0.5rem" },
|
|
144
|
+
{ label: "0.625", value: "0.625rem" },
|
|
145
|
+
{ label: "0.75", value: "0.75rem" },
|
|
146
|
+
{ label: "1", value: "1rem" },
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
const colorMode = useColorMode();
|
|
150
|
+
|
|
151
|
+
const themes = [
|
|
152
|
+
{ label: "Stone", value: "theme-stone", color: "oklch(0.216 0.006 56.043)" },
|
|
153
|
+
{ label: "Zinc", value: "theme-zinc", color: "oklch(0.21 0.006 285.885)" },
|
|
154
|
+
{ label: "Neutral", value: "theme-neutral", color: "oklch(0.205 0 0)" },
|
|
155
|
+
{ label: "Gray", value: "theme-gray", color: "oklch(0.21 0.034 264.665)" },
|
|
156
|
+
{ label: "Slate", value: "theme-slate", color: "oklch(0.208 0.042 265.755)" },
|
|
157
|
+
{ label: "Red", value: "theme-red", color: "oklch(0.637 0.237 25.331)" },
|
|
158
|
+
{ label: "Rose", value: "theme-rose", color: "oklch(0.645 0.246 16.439)" },
|
|
159
|
+
{ label: "Orange", value: "theme-orange", color: "oklch(0.705 0.213 47.604)" },
|
|
160
|
+
{ label: "Green", value: "theme-green", color: "oklch(0.723 0.219 149.579)" },
|
|
161
|
+
{ label: "Blue", value: "theme-blue", color: "oklch(0.623 0.214 259.815)" },
|
|
162
|
+
{ label: "Yellow", value: "theme-yellow", color: "oklch(0.795 0.184 86.047)" },
|
|
163
|
+
{ label: "Violet", value: "theme-violet", color: "oklch(0.606 0.25 292.717)" },
|
|
164
|
+
{ label: "Nuxt", value: "theme-nuxt", color: "oklch(0.79 0.19 155.63)" },
|
|
165
|
+
{ label: "Mauve", value: "theme-mauve", color: "oklch(0.212 0.019 322.12)" },
|
|
166
|
+
{ label: "Olive", value: "theme-olive", color: "oklch(0.228 0.013 107.4)" },
|
|
167
|
+
{ label: "Mist", value: "theme-mist", color: "oklch(0.218 0.008 223.9)" },
|
|
168
|
+
{ label: "Taupe", value: "theme-taupe", color: "oklch(0.214 0.009 43.1)" },
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const THEME_KEY = "docs-color-theme";
|
|
172
|
+
const RADIUS_KEY = "docs-radius";
|
|
173
|
+
|
|
174
|
+
const activeTheme = ref("theme-zinc");
|
|
175
|
+
const activeRadius = ref("0.625rem");
|
|
176
|
+
|
|
177
|
+
onMounted(() => {
|
|
178
|
+
const savedTheme = localStorage.getItem(THEME_KEY);
|
|
179
|
+
const savedRadius = localStorage.getItem(RADIUS_KEY);
|
|
180
|
+
if (savedTheme) applyTheme(savedTheme, false);
|
|
181
|
+
if (savedRadius) applyRadius(savedRadius, false);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
function applyTheme(value: string, save = true) {
|
|
185
|
+
themes.forEach((t) => document.documentElement.classList.remove(t.value));
|
|
186
|
+
document.documentElement.classList.add(value);
|
|
187
|
+
activeTheme.value = value;
|
|
188
|
+
if (save) localStorage.setItem(THEME_KEY, value);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function applyRadius(value: string, save = true) {
|
|
192
|
+
document.documentElement.style.setProperty("--radius", value);
|
|
193
|
+
activeRadius.value = value;
|
|
194
|
+
if (save) localStorage.setItem(RADIUS_KEY, value);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function setTheme(value: string) {
|
|
198
|
+
applyTheme(value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function setRadius(value: string) {
|
|
202
|
+
applyRadius(value);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function setMode(value: string) {
|
|
206
|
+
document.documentElement.classList.toggle("dark", value === "dark");
|
|
207
|
+
colorMode.preference = value;
|
|
208
|
+
}
|
|
209
|
+
</script>
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
v-if="hasToc"
|
|
4
|
-
class="sticky top-[calc(var(--header-height)*2)] z-40 tablet:top-[calc(var(--header-height))] xl:hidden"
|
|
5
|
-
>
|
|
2
|
+
<div v-if="hasToc" class="sticky top-[calc(var(--header-height))] z-40 xl:hidden">
|
|
6
3
|
<!-- Always-mounted hidden container — keeps scrollspy alive regardless of popover state -->
|
|
7
4
|
<div
|
|
8
5
|
ref="spyRoot"
|
package/app/layouts/docs.vue
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
:class="isDashed ? 'border-dashed' : ''"
|
|
45
45
|
>
|
|
46
46
|
<DocsMobileBar />
|
|
47
|
-
<div class="border-b" :class="isDashed ? 'border-dashed' : ''">
|
|
47
|
+
<div class="hidden border-b tablet:block" :class="isDashed ? 'border-dashed' : ''">
|
|
48
48
|
<UiContainer class="flex h-(--header-height) items-center">
|
|
49
49
|
<DocsHeader />
|
|
50
50
|
</UiContainer>
|
package/modules/css.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { addVitePlugin, createResolver, defineNuxtModule } from "@nuxt/kit";
|
|
2
|
+
import { joinURL } from "ufo";
|
|
3
|
+
|
|
4
|
+
export default defineNuxtModule({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "docd:css",
|
|
7
|
+
},
|
|
8
|
+
setup(_options, nuxt) {
|
|
9
|
+
const resolver = createResolver(import.meta.url);
|
|
10
|
+
|
|
11
|
+
// Absolute path to the layer's tailwind entry — this is the file that owns
|
|
12
|
+
// `@import "tailwindcss"` and therefore controls Tailwind's scanner.
|
|
13
|
+
const tailwindCssPath = resolver.resolve("../app/assets/css/tailwind.css");
|
|
14
|
+
|
|
15
|
+
// The consuming app's content directory. We convert to forward-slashes so
|
|
16
|
+
// the injected @source glob works on Windows too.
|
|
17
|
+
const contentDir = joinURL(nuxt.options.rootDir, "content").replace(/\\/g, "/");
|
|
18
|
+
|
|
19
|
+
// Inject `@source <contentDir>/**/*` into tailwind.css
|
|
20
|
+
addVitePlugin({
|
|
21
|
+
name: "docd:inject-tailwind-sources",
|
|
22
|
+
enforce: "pre",
|
|
23
|
+
transform(code: string, id: string) {
|
|
24
|
+
if (id.split("?")[0] !== tailwindCssPath) return null;
|
|
25
|
+
return {
|
|
26
|
+
code: `${code}\n@source ${JSON.stringify(`${contentDir}/**/*`)};`,
|
|
27
|
+
map: null,
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Suppress noisy Vite warnings produced during Tailwind's build pass.
|
|
33
|
+
nuxt.hook("vite:extendConfig", (config) => {
|
|
34
|
+
const logger = config.customLogger;
|
|
35
|
+
if (!logger) return;
|
|
36
|
+
const ignore = ["@tailwindcss/vite:generate:build", "nuxt:module-preload-polyfill"];
|
|
37
|
+
const originalWarn = logger.warn.bind(logger);
|
|
38
|
+
logger.warn = (msg, options) => {
|
|
39
|
+
if (ignore.some((p) => msg.includes(p))) return;
|
|
40
|
+
originalWarn(msg, options);
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
});
|
package/nuxt.config.ts
CHANGED
|
@@ -50,6 +50,7 @@ export default defineNuxtConfig({
|
|
|
50
50
|
"@morev/vue-transitions/nuxt",
|
|
51
51
|
"vue-sonner/nuxt",
|
|
52
52
|
resolver.resolve("./modules/routing"),
|
|
53
|
+
resolver.resolve("./modules/css"),
|
|
53
54
|
resolver.resolve("./modules/config"),
|
|
54
55
|
resolver.resolve("./modules/custom-icons"),
|
|
55
56
|
resolver.resolve("./modules/prose-component-meta"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@baybreezy/docd",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "A Nuxt layer for building documentation sites with UI Thing.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@baybreezy/file-extension-icon": "^0.0.4",
|
|
34
|
-
"@iconify-json/lucide": "^1.2.
|
|
34
|
+
"@iconify-json/lucide": "^1.2.103",
|
|
35
35
|
"@iconify-json/simple-icons": "^1.2.79",
|
|
36
36
|
"@iconify/utils": "^3.1.0",
|
|
37
37
|
"@morev/vue-transitions": "^3.0.5",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"@nuxtjs/color-mode": "^4.0.0",
|
|
44
44
|
"@nuxtjs/mcp-toolkit": "^0.14.0",
|
|
45
45
|
"@nuxtjs/mdc": "^0.21.1",
|
|
46
|
-
"@nuxtjs/robots": "^6.0.
|
|
46
|
+
"@nuxtjs/robots": "^6.0.8",
|
|
47
47
|
"@tailwindcss/forms": "^0.5.11",
|
|
48
48
|
"@tailwindcss/typography": "^0.5.19",
|
|
49
49
|
"@tailwindcss/vite": "^4.2.4",
|
|
50
|
-
"@takumi-rs/core": "^1.
|
|
50
|
+
"@takumi-rs/core": "^1.1.2",
|
|
51
51
|
"@vueuse/core": "^14.2.1",
|
|
52
52
|
"@vueuse/nuxt": "^14.2.1",
|
|
53
53
|
"better-sqlite3": "^12.9.0",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"nuxt-component-meta": "^0.17.2",
|
|
60
60
|
"nuxt-gtag": "4.1.0",
|
|
61
61
|
"nuxt-llms": "^0.2.0",
|
|
62
|
-
"nuxt-og-image": "^6.4.
|
|
62
|
+
"nuxt-og-image": "^6.4.8",
|
|
63
63
|
"pkg-types": "^2.3.0",
|
|
64
64
|
"reka-ui": "^2.9.6",
|
|
65
65
|
"shiki": "^4.0.2",
|
|
@@ -80,9 +80,6 @@
|
|
|
80
80
|
"vue": "latest",
|
|
81
81
|
"vue-router": "^5.0.6"
|
|
82
82
|
},
|
|
83
|
-
"overrides": {
|
|
84
|
-
"@takumi-rs/core": "^1.0.16"
|
|
85
|
-
},
|
|
86
83
|
"packageManager": "bun@1.3.13",
|
|
87
84
|
"trustedDependencies": [
|
|
88
85
|
"@morev/vue-transitions",
|