@moldea.ai/website-ui 1.2.1 → 1.6.0
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/README.md +163 -2
- package/dist/markdown/index.d.ts +7 -0
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown.js +33 -6
- package/dist/markdown.js.map +1 -1
- package/package.json +22 -1
- package/src/components/accordion/accordion.component.astro +103 -0
- package/src/components/action-link/action-link.component.astro +6 -1
- package/src/components/breadcrumbs/breadcrumbs.component.astro +1 -1
- package/src/components/code-block/code-block.component.astro +22 -0
- package/src/components/connection-label/connection-label.component.astro +26 -0
- package/src/components/dialog/dialog.component.astro +266 -0
- package/src/components/documentation-shell/documentation-outline.component.astro +1 -4
- package/src/components/documentation-shell/documentation-shell.component.astro +2 -12
- package/src/components/evaluation-replay/evaluation-replay-markdown.component.astro +1 -1
- package/src/components/file-preview/file-preview.component.astro +55 -0
- package/src/components/hero-backdrop/hero-backdrop.component.astro +9 -0
- package/src/components/inline-brand-text/inline-brand-text.component.astro +1 -1
- package/src/components/local-search/local-search.component.astro +37 -17
- package/src/components/result-summary/result-summary.component.astro +64 -0
- package/src/components/site-header/site-header.component.astro +39 -32
- package/src/components/status-badge/status-badge.component.astro +8 -2
- package/src/styles.css +54 -8
- package/src/tokens.css +2 -2
|
@@ -14,6 +14,7 @@ export interface Props {
|
|
|
14
14
|
noResultsMessage?: string;
|
|
15
15
|
placeholder: string;
|
|
16
16
|
searchIndexUrl: string;
|
|
17
|
+
shouldFocusOnLoad?: boolean;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const {
|
|
@@ -27,6 +28,7 @@ const {
|
|
|
27
28
|
noResultsMessage = 'No matching documentation was found.',
|
|
28
29
|
placeholder,
|
|
29
30
|
searchIndexUrl,
|
|
31
|
+
shouldFocusOnLoad = false,
|
|
30
32
|
} = Astro.props;
|
|
31
33
|
---
|
|
32
34
|
|
|
@@ -36,6 +38,7 @@ const {
|
|
|
36
38
|
data-initial-prompt={initialPrompt}
|
|
37
39
|
data-loading-message={loadingMessage}
|
|
38
40
|
data-no-results-message={noResultsMessage}
|
|
41
|
+
data-search-focus-on-load={shouldFocusOnLoad ? 'true' : undefined}
|
|
39
42
|
>
|
|
40
43
|
<form
|
|
41
44
|
class="mt-10"
|
|
@@ -116,6 +119,20 @@ const {
|
|
|
116
119
|
return item;
|
|
117
120
|
};
|
|
118
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Loads and validates the shared index on the first submitted query.
|
|
124
|
+
* @returns The public search documents, or null when the index is unavailable.
|
|
125
|
+
*/
|
|
126
|
+
const loadSearchDocuments = async (url: string): Promise<ISearchDocument[] | null> => {
|
|
127
|
+
try {
|
|
128
|
+
const response = await fetch(url);
|
|
129
|
+
if (!response.ok) return null;
|
|
130
|
+
return parseSearchDocuments(await response.json());
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
119
136
|
const initializeSearch = (root: HTMLElement): void => {
|
|
120
137
|
const form = root.querySelector<HTMLFormElement>('[data-search-form]');
|
|
121
138
|
const input = root.querySelector<HTMLInputElement>('[data-search-input]');
|
|
@@ -131,15 +148,12 @@ const {
|
|
|
131
148
|
if (!searchIndexUrl) return;
|
|
132
149
|
|
|
133
150
|
root.dataset.searchInitialized = 'true';
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
throw new Error('The documentation search index could not be loaded.');
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return parseSearchDocuments(await response.json());
|
|
140
|
-
});
|
|
151
|
+
let documentsPromise: Promise<ISearchDocument[] | null> | undefined;
|
|
152
|
+
let latestRequest: symbol;
|
|
141
153
|
|
|
142
154
|
const runSearch = async (query: string): Promise<void> => {
|
|
155
|
+
const request = Symbol();
|
|
156
|
+
latestRequest = request;
|
|
143
157
|
results.replaceChildren();
|
|
144
158
|
|
|
145
159
|
if (!query.trim()) {
|
|
@@ -149,19 +163,22 @@ const {
|
|
|
149
163
|
|
|
150
164
|
status.textContent = root.dataset.loadingMessage ?? '';
|
|
151
165
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
166
|
+
const pendingDocuments = (documentsPromise ??= loadSearchDocuments(searchIndexUrl));
|
|
167
|
+
const documents = await pendingDocuments;
|
|
168
|
+
if (documents === null && documentsPromise === pendingDocuments) documentsPromise = undefined;
|
|
169
|
+
// A replacement or cleared query owns the UI even while the index is still loading.
|
|
170
|
+
if (latestRequest !== request || !root.isConnected) return;
|
|
158
171
|
|
|
159
|
-
|
|
160
|
-
? `${matches.length} result${matches.length === 1 ? '' : 's'} for “${query}”.`
|
|
161
|
-
: (root.dataset.noResultsMessage ?? '');
|
|
162
|
-
} catch {
|
|
172
|
+
if (documents === null) {
|
|
163
173
|
status.textContent = root.dataset.failureMessage ?? '';
|
|
174
|
+
return;
|
|
164
175
|
}
|
|
176
|
+
|
|
177
|
+
const matches = searchDocuments(query, documents);
|
|
178
|
+
results.replaceChildren(...matches.map(createSearchResult));
|
|
179
|
+
status.textContent = matches.length
|
|
180
|
+
? `${matches.length} result${matches.length === 1 ? '' : 's'} for “${query}”.`
|
|
181
|
+
: (root.dataset.noResultsMessage ?? '');
|
|
165
182
|
};
|
|
166
183
|
|
|
167
184
|
form.addEventListener('submit', (event) => {
|
|
@@ -180,6 +197,9 @@ const {
|
|
|
180
197
|
const initializeLocalSearch = (): void => {
|
|
181
198
|
for (const root of document.querySelectorAll<HTMLElement>('[data-local-search]')) {
|
|
182
199
|
initializeSearch(root);
|
|
200
|
+
if (root.dataset.searchFocusOnLoad === 'true') {
|
|
201
|
+
root.querySelector<HTMLInputElement>('[data-search-input]')?.focus({ preventScroll: true });
|
|
202
|
+
}
|
|
183
203
|
}
|
|
184
204
|
};
|
|
185
205
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
import InlineBrandText from '../inline-brand-text/inline-brand-text.component.astro';
|
|
3
|
+
import type { IStatusBadgeTone } from '../status-badge/status-badge.component.astro';
|
|
4
|
+
|
|
5
|
+
// balanced presentation shared by outcome cards and slotted dialog headings
|
|
6
|
+
export interface Props {
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
tone?: IStatusBadgeTone;
|
|
10
|
+
headingId?: string;
|
|
11
|
+
as?: 'p' | 'h2' | 'h3';
|
|
12
|
+
hideIconOnMobile?: boolean;
|
|
13
|
+
ariaLabel?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
title,
|
|
18
|
+
description,
|
|
19
|
+
tone = 'neutral',
|
|
20
|
+
headingId,
|
|
21
|
+
as: Title = 'p',
|
|
22
|
+
hideIconOnMobile = false,
|
|
23
|
+
ariaLabel = 'Result',
|
|
24
|
+
} = Astro.props;
|
|
25
|
+
const toneClasses = {
|
|
26
|
+
danger: 'border-destructive/40 bg-destructive/10',
|
|
27
|
+
info: 'border-info/40 bg-info/10',
|
|
28
|
+
neutral: 'border-border bg-muted',
|
|
29
|
+
success: 'border-border bg-secondary',
|
|
30
|
+
warning: 'border-warning/60 bg-warning/15',
|
|
31
|
+
} as const;
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
<div
|
|
35
|
+
class="flex min-w-0 items-center gap-3"
|
|
36
|
+
role="group"
|
|
37
|
+
aria-label={ariaLabel}
|
|
38
|
+
>
|
|
39
|
+
{
|
|
40
|
+
Astro.slots.has('icon') && (
|
|
41
|
+
<span
|
|
42
|
+
class:list={[
|
|
43
|
+
'size-10 shrink-0 items-center justify-center rounded-lg border',
|
|
44
|
+
hideIconOnMobile ? 'hidden sm:flex' : 'flex',
|
|
45
|
+
toneClasses[tone],
|
|
46
|
+
]}
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
>
|
|
49
|
+
<slot name="icon" />
|
|
50
|
+
</span>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
<div class="min-w-0 flex-1">
|
|
54
|
+
<div class="flex flex-wrap items-center gap-x-2 gap-y-1">
|
|
55
|
+
<Title
|
|
56
|
+
id={headingId}
|
|
57
|
+
class="text-sm leading-5 font-semibold"
|
|
58
|
+
><InlineBrandText text={title} /></Title
|
|
59
|
+
>
|
|
60
|
+
<slot name="status" />
|
|
61
|
+
</div>
|
|
62
|
+
<p class="text-xs leading-5 text-muted-foreground"><InlineBrandText text={description} /></p>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { GitFork, Menu, Search } from '@lucide/astro';
|
|
3
3
|
|
|
4
|
+
import ActionLink from '../action-link/action-link.component.astro';
|
|
4
5
|
import ThemeControl from '../theme-control/theme-control.component.astro';
|
|
5
6
|
|
|
6
7
|
export interface ISiteHeaderNavigationItem {
|
|
8
|
+
compactLabel?: string;
|
|
7
9
|
href: string;
|
|
8
10
|
isActive: boolean;
|
|
9
11
|
label: string;
|
|
@@ -51,7 +53,9 @@ const breakpointClasses = {
|
|
|
51
53
|
const responsiveClasses = breakpointClasses[breakpoint];
|
|
52
54
|
---
|
|
53
55
|
|
|
54
|
-
<header
|
|
56
|
+
<header
|
|
57
|
+
class="sticky top-0 z-50 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/85"
|
|
58
|
+
>
|
|
55
59
|
<div class="page-shell flex min-h-16 items-center justify-between gap-4">
|
|
56
60
|
<slot name="brand" />
|
|
57
61
|
|
|
@@ -60,29 +64,41 @@ const responsiveClasses = breakpointClasses[breakpoint];
|
|
|
60
64
|
aria-label={navigationAriaLabel}
|
|
61
65
|
>
|
|
62
66
|
{
|
|
63
|
-
navigationItems.map(({ href, isActive, label }) => (
|
|
67
|
+
navigationItems.map(({ href, isActive, label, compactLabel }) => (
|
|
64
68
|
<a
|
|
65
|
-
class
|
|
66
|
-
'rounded-lg px-3 py-2 text-sm transition-colors',
|
|
67
|
-
isActive
|
|
68
|
-
? 'bg-secondary/70 font-bold text-foreground dark:bg-secondary/50'
|
|
69
|
-
: 'font-semibold text-muted-foreground hover:bg-muted hover:text-foreground',
|
|
70
|
-
]}
|
|
69
|
+
class="rounded-lg navigation-link px-3 py-2 text-sm whitespace-nowrap"
|
|
71
70
|
href={href}
|
|
71
|
+
aria-label={compactLabel ? label : undefined}
|
|
72
72
|
aria-current={isActive ? 'page' : undefined}
|
|
73
73
|
>
|
|
74
|
-
{
|
|
74
|
+
{compactLabel ? (
|
|
75
|
+
<>
|
|
76
|
+
<span
|
|
77
|
+
class="xl:hidden"
|
|
78
|
+
aria-hidden="true"
|
|
79
|
+
>
|
|
80
|
+
{compactLabel}
|
|
81
|
+
</span>
|
|
82
|
+
<span
|
|
83
|
+
class="hidden xl:inline"
|
|
84
|
+
aria-hidden="true"
|
|
85
|
+
>
|
|
86
|
+
{label}
|
|
87
|
+
</span>
|
|
88
|
+
</>
|
|
89
|
+
) : (
|
|
90
|
+
label
|
|
91
|
+
)}
|
|
75
92
|
</a>
|
|
76
93
|
))
|
|
77
94
|
}
|
|
78
95
|
</nav>
|
|
79
96
|
|
|
80
97
|
<div class:list={['items-center gap-2', responsiveClasses.desktop]}>
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
]}
|
|
98
|
+
<ActionLink
|
|
99
|
+
size="icon"
|
|
100
|
+
variant="ghost"
|
|
101
|
+
class="aria-[current=page]:bg-secondary"
|
|
86
102
|
href={searchHref}
|
|
87
103
|
aria-label={searchAriaLabel}
|
|
88
104
|
aria-current={searchIsActive ? 'page' : undefined}
|
|
@@ -91,9 +107,10 @@ const responsiveClasses = breakpointClasses[breakpoint];
|
|
|
91
107
|
class="size-4"
|
|
92
108
|
aria-hidden="true"
|
|
93
109
|
/>
|
|
94
|
-
</
|
|
95
|
-
<
|
|
96
|
-
|
|
110
|
+
</ActionLink>
|
|
111
|
+
<ActionLink
|
|
112
|
+
size="icon"
|
|
113
|
+
variant="ghost"
|
|
97
114
|
href={sourceHref}
|
|
98
115
|
target="_blank"
|
|
99
116
|
rel="noopener noreferrer"
|
|
@@ -103,14 +120,14 @@ const responsiveClasses = breakpointClasses[breakpoint];
|
|
|
103
120
|
class="size-4"
|
|
104
121
|
aria-hidden="true"
|
|
105
122
|
/>
|
|
106
|
-
</
|
|
123
|
+
</ActionLink>
|
|
107
124
|
<ThemeControl storageKey={themeStorageKey} />
|
|
108
125
|
<slot name="desktop-actions" />
|
|
109
126
|
</div>
|
|
110
127
|
|
|
111
128
|
<details class:list={['group relative', responsiveClasses.mobile]}>
|
|
112
129
|
<summary
|
|
113
|
-
class="flex size-11 list-none items-center justify-center rounded-lg border border-input
|
|
130
|
+
class="flex size-11 list-none items-center justify-center rounded-lg border border-input surface-link bg-background [&::-webkit-details-marker]:hidden"
|
|
114
131
|
aria-label={mobileMenuLabel}
|
|
115
132
|
>
|
|
116
133
|
<Menu
|
|
@@ -129,12 +146,7 @@ const responsiveClasses = breakpointClasses[breakpoint];
|
|
|
129
146
|
{
|
|
130
147
|
navigationItems.map(({ href, isActive, label }) => (
|
|
131
148
|
<a
|
|
132
|
-
class
|
|
133
|
-
'rounded-lg px-3 py-3 transition-colors',
|
|
134
|
-
isActive
|
|
135
|
-
? 'bg-secondary/70 font-bold text-foreground dark:bg-secondary/50'
|
|
136
|
-
: 'font-semibold text-muted-foreground hover:bg-muted hover:text-foreground',
|
|
137
|
-
]}
|
|
149
|
+
class="rounded-lg navigation-link px-3 py-3"
|
|
138
150
|
href={href}
|
|
139
151
|
aria-current={isActive ? 'page' : undefined}
|
|
140
152
|
>
|
|
@@ -143,19 +155,14 @@ const responsiveClasses = breakpointClasses[breakpoint];
|
|
|
143
155
|
))
|
|
144
156
|
}
|
|
145
157
|
<a
|
|
146
|
-
class
|
|
147
|
-
'rounded-lg px-3 py-3 transition-colors',
|
|
148
|
-
searchIsActive
|
|
149
|
-
? 'bg-secondary/70 font-bold text-foreground dark:bg-secondary/50'
|
|
150
|
-
: 'font-semibold text-muted-foreground hover:bg-muted hover:text-foreground',
|
|
151
|
-
]}
|
|
158
|
+
class="rounded-lg navigation-link px-3 py-3"
|
|
152
159
|
href={searchHref}
|
|
153
160
|
aria-current={searchIsActive ? 'page' : undefined}
|
|
154
161
|
>
|
|
155
162
|
Search
|
|
156
163
|
</a>
|
|
157
164
|
<a
|
|
158
|
-
class="rounded-lg px-3 py-3
|
|
165
|
+
class="rounded-lg navigation-link px-3 py-3"
|
|
159
166
|
href={sourceHref}
|
|
160
167
|
target="_blank"
|
|
161
168
|
rel="noopener noreferrer"
|
|
@@ -4,11 +4,16 @@ export type IStatusBadgeTone = 'danger' | 'info' | 'neutral' | 'success' | 'warn
|
|
|
4
4
|
export interface Props {
|
|
5
5
|
borderStyle?: 'dashed' | 'dotted' | 'solid';
|
|
6
6
|
label: string;
|
|
7
|
+
size?: 'md' | 'sm';
|
|
7
8
|
tone: IStatusBadgeTone;
|
|
8
9
|
truncate?: boolean;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
const { borderStyle = 'solid', label, tone, truncate = false } = Astro.props;
|
|
12
|
+
const { borderStyle = 'solid', label, size = 'md', tone, truncate = false } = Astro.props;
|
|
13
|
+
const sizeClasses = {
|
|
14
|
+
md: 'min-h-6 px-2.5 py-1 text-[0.68rem] font-bold tracking-[0.08em]',
|
|
15
|
+
sm: 'min-h-5 px-1.5 py-0 text-[0.625rem] leading-4 font-semibold tracking-wide',
|
|
16
|
+
} as const;
|
|
12
17
|
const borderStyleClasses = {
|
|
13
18
|
dashed: 'border-dashed',
|
|
14
19
|
dotted: 'border-dotted',
|
|
@@ -25,9 +30,10 @@ const toneClasses = {
|
|
|
25
30
|
|
|
26
31
|
<span
|
|
27
32
|
class:list={[
|
|
28
|
-
'
|
|
33
|
+
'max-w-full rounded-full border text-start uppercase',
|
|
29
34
|
truncate ? 'block min-w-0 truncate' : 'inline-flex items-center break-words whitespace-normal',
|
|
30
35
|
borderStyleClasses[borderStyle],
|
|
36
|
+
sizeClasses[size],
|
|
31
37
|
toneClasses[tone],
|
|
32
38
|
]}
|
|
33
39
|
data-status-badge={tone}
|
package/src/styles.css
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
body {
|
|
24
|
-
@apply
|
|
24
|
+
@apply bg-background text-foreground;
|
|
25
25
|
margin: 0;
|
|
26
26
|
overflow-wrap: break-word;
|
|
27
27
|
}
|
|
@@ -83,13 +83,13 @@
|
|
|
83
83
|
|
|
84
84
|
.display-title {
|
|
85
85
|
font-size: clamp(2.65rem, 9vw, 6.6rem);
|
|
86
|
-
line-height:
|
|
86
|
+
line-height: 1.08;
|
|
87
87
|
letter-spacing: -0.065em;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
.section-title {
|
|
91
91
|
font-size: clamp(2rem, 5vw, 4.25rem);
|
|
92
|
-
line-height:
|
|
92
|
+
line-height: 1.08;
|
|
93
93
|
letter-spacing: -0.045em;
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
.interactive-card {
|
|
101
|
-
@apply transition duration-200 ease-out hover:-translate-y-0.5 hover:border-foreground/25 active:translate-y-px motion-reduce:transform-none motion-reduce:transition-none;
|
|
101
|
+
@apply transition duration-200 ease-out hover:-translate-y-0.5 hover:border-foreground/25 focus-visible:ring-offset-0 active:translate-y-px motion-reduce:translate-none motion-reduce:transform-none motion-reduce:transition-none motion-reduce:hover:translate-none motion-reduce:active:translate-none;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
.action-control {
|
|
@@ -125,6 +125,10 @@
|
|
|
125
125
|
@apply border-transparent bg-transparent px-0! text-primary underline-offset-4 not-disabled:not-aria-disabled:hover:underline dark:text-primary-foreground dark:not-disabled:not-aria-disabled:hover:text-primary-foreground/80;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
.text-action-link {
|
|
129
|
+
@apply border-transparent bg-transparent px-0! font-semibold text-foreground link-feedback not-disabled:not-aria-disabled:active:not-aria-[haspopup]:translate-none;
|
|
130
|
+
}
|
|
131
|
+
|
|
128
132
|
.action-size-sm {
|
|
129
133
|
@apply min-h-8 gap-1.5 px-3 py-1.5 text-sm;
|
|
130
134
|
}
|
|
@@ -185,11 +189,11 @@
|
|
|
185
189
|
list-style: decimal;
|
|
186
190
|
}
|
|
187
191
|
|
|
188
|
-
.prose-moldea a
|
|
189
|
-
|
|
190
|
-
@apply font-medium text-link underline decoration-link/40 underline-offset-4 hover:decoration-current;
|
|
192
|
+
.prose-moldea a {
|
|
193
|
+
@apply text-link;
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
.inline-code,
|
|
193
197
|
.prose-moldea code:not(pre code) {
|
|
194
198
|
@apply rounded-md bg-code px-1.5 py-1 font-mono text-[0.86em] font-semibold text-code-foreground;
|
|
195
199
|
}
|
|
@@ -198,7 +202,18 @@
|
|
|
198
202
|
@apply max-w-full overflow-x-auto rounded-xl border bg-code p-5 text-sm leading-6 text-code-foreground shadow-inset;
|
|
199
203
|
}
|
|
200
204
|
|
|
201
|
-
|
|
205
|
+
/* Preserve code, structured output, and file-tree alignment by default. */
|
|
206
|
+
.code-block,
|
|
207
|
+
.prose-moldea pre {
|
|
208
|
+
@apply max-w-full overflow-x-auto outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background;
|
|
209
|
+
white-space: pre;
|
|
210
|
+
overflow-wrap: normal;
|
|
211
|
+
word-break: normal;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Only explicitly identified plain text wraps; unlabelled fences remain lossless. */
|
|
215
|
+
.code-block[data-code-language='text'],
|
|
216
|
+
.prose-moldea pre:has(> code:is(.language-text, .language-txt, .language-plaintext)) {
|
|
202
217
|
white-space: pre-wrap;
|
|
203
218
|
overflow-wrap: anywhere;
|
|
204
219
|
}
|
|
@@ -295,6 +310,37 @@
|
|
|
295
310
|
}
|
|
296
311
|
}
|
|
297
312
|
|
|
313
|
+
/* platform TextLink feedback belongs to high-contrast text, not every linked surface */
|
|
314
|
+
@utility link-feedback {
|
|
315
|
+
@apply not-disabled:not-aria-disabled:hover:opacity-70 focus-visible:ring-current focus-visible:ring-offset-0 not-disabled:not-aria-disabled:active:opacity-60;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@utility text-link {
|
|
319
|
+
/* keep the foreground opaque: a second dark-mode fade fails contrast when pressed */
|
|
320
|
+
color: var(--link);
|
|
321
|
+
@apply rounded-sm font-medium underline decoration-current underline-offset-4 link-feedback aria-[current=page]:font-semibold;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
@utility plain-link {
|
|
325
|
+
@apply rounded-sm text-muted-foreground not-disabled:not-aria-disabled:hover:text-foreground focus-visible:ring-foreground focus-visible:ring-offset-0 not-disabled:not-aria-disabled:active:text-foreground;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@utility surface-link {
|
|
329
|
+
@apply not-disabled:not-aria-disabled:hover:bg-secondary not-disabled:not-aria-disabled:active:bg-secondary/80 aria-[current=page]:bg-secondary aria-[current=page]:font-semibold;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
@utility surface-link-warning {
|
|
333
|
+
@apply border-warning/60 bg-warning/15 text-warning-foreground not-disabled:not-aria-disabled:hover:bg-warning/20 not-disabled:not-aria-disabled:active:bg-warning/25 dark:text-foreground;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
@utility surface-link-danger {
|
|
337
|
+
@apply border-destructive/50 bg-destructive/10 not-disabled:not-aria-disabled:hover:bg-destructive/20 not-disabled:not-aria-disabled:active:bg-destructive/25 dark:bg-destructive/20 dark:not-disabled:not-aria-disabled:hover:bg-destructive/30 dark:not-disabled:not-aria-disabled:active:bg-destructive/25;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@utility navigation-link {
|
|
341
|
+
@apply font-semibold text-muted-foreground not-aria-[current=page]:hover:bg-secondary not-aria-[current=page]:hover:text-foreground not-aria-[current=page]:active:bg-secondary/80 aria-[current=page]:bg-secondary/70 aria-[current=page]:font-bold aria-[current=page]:text-foreground dark:aria-[current=page]:bg-secondary/50;
|
|
342
|
+
}
|
|
343
|
+
|
|
298
344
|
@media (prefers-color-scheme: dark) {
|
|
299
345
|
html:not(.light):not(.dark) {
|
|
300
346
|
color-scheme: dark;
|
package/src/tokens.css
CHANGED
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
--info-foreground: oklch(99% 0.005 255);
|
|
131
131
|
--sidebar: oklch(97.8% 0.005 198);
|
|
132
132
|
--sidebar-foreground: var(--foreground);
|
|
133
|
-
--link:
|
|
133
|
+
--link: var(--primary);
|
|
134
134
|
--code-background: oklch(95.1% 0.007 226);
|
|
135
135
|
--code-foreground: oklch(20% 0.026 232);
|
|
136
136
|
--syntax-comment: oklch(51% 0.024 220);
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
--info-foreground: oklch(20% 0.05 255);
|
|
175
175
|
--sidebar: oklch(25.3% 0.031 258);
|
|
176
176
|
--sidebar-foreground: var(--foreground);
|
|
177
|
-
--link:
|
|
177
|
+
--link: var(--primary-foreground);
|
|
178
178
|
--code-background: oklch(23.8% 0.027 258);
|
|
179
179
|
--code-foreground: oklch(94% 0.01 215);
|
|
180
180
|
--syntax-comment: oklch(72% 0.025 220);
|