@f0rbit/ui 0.1.2 → 0.1.7
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/dist/components.css +4 -0
- package/dist/index.d.ts +80 -5
- package/dist/index.js +103 -80
- package/dist/index.jsx +80 -61
- package/dist/server.js +41 -61
- package/dist/server.jsx +80 -61
- package/dist/starlight.css +243 -0
- package/dist/styles.css +4 -0
- package/package.json +3 -2
- package/src/components/Stepper.tsx +11 -3
- package/src/components/Tabs.tsx +155 -69
- package/src/styles/components.css +4 -0
- package/src/styles/starlight.css +243 -0
|
@@ -18,6 +18,11 @@ export interface StepProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
|
18
18
|
description?: string;
|
|
19
19
|
icon?: JSX.Element;
|
|
20
20
|
status?: StepStatus;
|
|
21
|
+
children?: JSX.Element;
|
|
22
|
+
/** Explicit step number (optional, auto-increments if not provided) */
|
|
23
|
+
number?: number;
|
|
24
|
+
/** Explicit orientation (optional, uses context if not provided) */
|
|
25
|
+
orientation?: "horizontal" | "vertical";
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
const statusClasses: Record<StepStatus, string> = {
|
|
@@ -41,12 +46,12 @@ type StepContextValue = {
|
|
|
41
46
|
const StepContext = createContext<StepContextValue>();
|
|
42
47
|
|
|
43
48
|
export function Step(props: StepProps) {
|
|
44
|
-
const [local, rest] = splitProps(props, ["title", "description", "icon", "status", "class"]);
|
|
49
|
+
const [local, rest] = splitProps(props, ["title", "description", "icon", "status", "class", "children", "number", "orientation"]);
|
|
45
50
|
const stepperCtx = useContext(StepperContext);
|
|
46
51
|
const stepCtx = useContext(StepContext);
|
|
47
52
|
|
|
48
|
-
const stepNumber = stepperCtx?.registerStep() ?? 1;
|
|
49
|
-
const orientation = () => stepCtx?.orientation() ?? "
|
|
53
|
+
const stepNumber = local.number ?? stepperCtx?.registerStep() ?? 1;
|
|
54
|
+
const orientation = () => local.orientation ?? stepCtx?.orientation() ?? "vertical";
|
|
50
55
|
|
|
51
56
|
const status = () => local.status ?? "upcoming";
|
|
52
57
|
|
|
@@ -105,6 +110,9 @@ export function Step(props: StepProps) {
|
|
|
105
110
|
<Show when={local.description}>
|
|
106
111
|
<div class="step-description">{local.description}</div>
|
|
107
112
|
</Show>
|
|
113
|
+
<Show when={local.children}>
|
|
114
|
+
<div class="step-body">{local.children}</div>
|
|
115
|
+
</Show>
|
|
108
116
|
</div>
|
|
109
117
|
<div class={connectorClasses()} />
|
|
110
118
|
</div>
|
package/src/components/Tabs.tsx
CHANGED
|
@@ -1,62 +1,146 @@
|
|
|
1
|
-
import { type JSX, splitProps,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { type JSX, splitProps, onMount, onCleanup, createSignal } from "solid-js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A tab container component that manages tab selection and panel visibility.
|
|
5
|
+
*
|
|
6
|
+
* Uses DOM-based discovery via data attributes, making it compatible with
|
|
7
|
+
* Astro's island architecture where each child component hydrates independently.
|
|
8
|
+
*
|
|
9
|
+
* @example Basic usage in SolidJS
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <Tabs defaultValue="tab1">
|
|
12
|
+
* <TabList>
|
|
13
|
+
* <Tab value="tab1">First Tab</Tab>
|
|
14
|
+
* <Tab value="tab2">Second Tab</Tab>
|
|
15
|
+
* </TabList>
|
|
16
|
+
* <TabPanel value="tab1">First panel content</TabPanel>
|
|
17
|
+
* <TabPanel value="tab2">Second panel content</TabPanel>
|
|
18
|
+
* </Tabs>
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example Usage in Astro MDX (each component needs client:load)
|
|
22
|
+
* ```mdx
|
|
23
|
+
* <Tabs defaultValue="tab1" client:load>
|
|
24
|
+
* <TabList client:load>
|
|
25
|
+
* <Tab value="tab1" client:load>First Tab</Tab>
|
|
26
|
+
* <Tab value="tab2" client:load>Second Tab</Tab>
|
|
27
|
+
* </TabList>
|
|
28
|
+
* <TabPanel value="tab1" client:load>First panel content</TabPanel>
|
|
29
|
+
* <TabPanel value="tab2" client:load>Second panel content</TabPanel>
|
|
30
|
+
* </Tabs>
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example Creating a wrapper to avoid multiple client:load directives
|
|
34
|
+
* ```tsx
|
|
35
|
+
* // MyTabs.tsx - your custom wrapper
|
|
36
|
+
* import { Tabs, TabList, Tab, TabPanel } from '@f0rbit/ui';
|
|
37
|
+
*
|
|
38
|
+
* export function MyTabs() {
|
|
39
|
+
* return (
|
|
40
|
+
* <Tabs defaultValue="tab1">
|
|
41
|
+
* <TabList>
|
|
42
|
+
* <Tab value="tab1">First</Tab>
|
|
43
|
+
* <Tab value="tab2">Second</Tab>
|
|
44
|
+
* </TabList>
|
|
45
|
+
* <TabPanel value="tab1">Content 1</TabPanel>
|
|
46
|
+
* <TabPanel value="tab2">Content 2</TabPanel>
|
|
47
|
+
* </Tabs>
|
|
48
|
+
* );
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* // Then in Astro: <MyTabs client:load />
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
10
54
|
export interface TabsProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
55
|
+
/** The value of the initially selected tab */
|
|
11
56
|
defaultValue?: string;
|
|
12
|
-
value?: string;
|
|
13
|
-
onValueChange?: (value: string) => void;
|
|
14
57
|
children: JSX.Element;
|
|
15
58
|
}
|
|
16
59
|
|
|
17
|
-
export
|
|
18
|
-
children
|
|
19
|
-
|
|
60
|
+
export function Tabs(props: TabsProps) {
|
|
61
|
+
const [local, rest] = splitProps(props, ["defaultValue", "children", "class"]);
|
|
62
|
+
let containerRef: HTMLDivElement | undefined;
|
|
63
|
+
const [activeTab, setActiveTab] = createSignal(local.defaultValue ?? "");
|
|
64
|
+
|
|
65
|
+
const updateTabs = (value: string) => {
|
|
66
|
+
if (!containerRef) return;
|
|
67
|
+
|
|
68
|
+
const tabs = containerRef.querySelectorAll<HTMLButtonElement>("[data-tab-value]");
|
|
69
|
+
tabs.forEach((tab) => {
|
|
70
|
+
const isActive = tab.dataset.tabValue === value;
|
|
71
|
+
tab.setAttribute("aria-selected", String(isActive));
|
|
72
|
+
tab.setAttribute("tabindex", isActive ? "0" : "-1");
|
|
73
|
+
tab.classList.toggle("active", isActive);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const panels = containerRef.querySelectorAll<HTMLDivElement>("[data-panel-value]");
|
|
77
|
+
panels.forEach((panel) => {
|
|
78
|
+
const isActive = panel.dataset.panelValue === value;
|
|
79
|
+
panel.hidden = !isActive;
|
|
80
|
+
});
|
|
81
|
+
};
|
|
20
82
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
children: JSX.Element;
|
|
24
|
-
}
|
|
83
|
+
const initializeTabs = () => {
|
|
84
|
+
if (!containerRef) return;
|
|
25
85
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
86
|
+
let value = activeTab();
|
|
87
|
+
if (!value) {
|
|
88
|
+
const firstTab = containerRef.querySelector<HTMLButtonElement>("[data-tab-value]");
|
|
89
|
+
value = firstTab?.dataset.tabValue ?? "";
|
|
90
|
+
if (value) setActiveTab(value);
|
|
91
|
+
}
|
|
30
92
|
|
|
31
|
-
|
|
32
|
-
|
|
93
|
+
if (value) updateTabs(value);
|
|
94
|
+
};
|
|
33
95
|
|
|
34
|
-
|
|
96
|
+
onMount(() => {
|
|
97
|
+
if (!containerRef) return;
|
|
35
98
|
|
|
36
|
-
|
|
37
|
-
|
|
99
|
+
// Use event delegation for clicks - handles dynamically added children
|
|
100
|
+
containerRef.addEventListener("click", (e) => {
|
|
101
|
+
const tab = (e.target as HTMLElement).closest<HTMLButtonElement>("[data-tab-value]");
|
|
102
|
+
if (tab?.dataset.tabValue) {
|
|
103
|
+
setActiveTab(tab.dataset.tabValue);
|
|
104
|
+
updateTabs(tab.dataset.tabValue);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
38
107
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
108
|
+
// Initialize with any children already present
|
|
109
|
+
initializeTabs();
|
|
110
|
+
|
|
111
|
+
// Watch for new children being added (e.g., Astro islands hydrating)
|
|
112
|
+
const observer = new MutationObserver(() => {
|
|
113
|
+
initializeTabs();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
observer.observe(containerRef, {
|
|
117
|
+
childList: true,
|
|
118
|
+
subtree: true,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
onCleanup(() => observer.disconnect());
|
|
122
|
+
});
|
|
45
123
|
|
|
46
124
|
const classes = () => `tabs ${local.class ?? ""}`.trim();
|
|
47
125
|
|
|
48
126
|
return (
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</div>
|
|
53
|
-
</TabsContext.Provider>
|
|
127
|
+
<div ref={containerRef} class={classes()} data-default-value={local.defaultValue} {...rest}>
|
|
128
|
+
{local.children}
|
|
129
|
+
</div>
|
|
54
130
|
);
|
|
55
131
|
}
|
|
56
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Container for Tab buttons. Provides the tablist role for accessibility.
|
|
135
|
+
*
|
|
136
|
+
* In Astro, requires `client:load` directive.
|
|
137
|
+
*/
|
|
138
|
+
export interface TabListProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
139
|
+
children: JSX.Element;
|
|
140
|
+
}
|
|
141
|
+
|
|
57
142
|
export function TabList(props: TabListProps) {
|
|
58
143
|
const [local, rest] = splitProps(props, ["children", "class"]);
|
|
59
|
-
|
|
60
144
|
const classes = () => `tab-list ${local.class ?? ""}`.trim();
|
|
61
145
|
|
|
62
146
|
return (
|
|
@@ -66,35 +150,30 @@ export function TabList(props: TabListProps) {
|
|
|
66
150
|
);
|
|
67
151
|
}
|
|
68
152
|
|
|
153
|
+
/**
|
|
154
|
+
* A tab button that switches the active panel when clicked.
|
|
155
|
+
*
|
|
156
|
+
* The `value` prop must match a corresponding `TabPanel` value.
|
|
157
|
+
* In Astro, requires `client:load` directive.
|
|
158
|
+
*/
|
|
159
|
+
export interface TabProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
160
|
+
/** Unique identifier that links this tab to its panel */
|
|
161
|
+
value: string;
|
|
162
|
+
children: JSX.Element;
|
|
163
|
+
}
|
|
164
|
+
|
|
69
165
|
export function Tab(props: TabProps) {
|
|
70
166
|
const [local, rest] = splitProps(props, ["value", "children", "class"]);
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const isActive = () => ctx?.activeTab() === local.value;
|
|
74
|
-
|
|
75
|
-
const handleClick = () => {
|
|
76
|
-
ctx?.setActiveTab(local.value);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const classes = () => {
|
|
80
|
-
const parts = ["tab"];
|
|
81
|
-
if (isActive()) {
|
|
82
|
-
parts.push("active");
|
|
83
|
-
}
|
|
84
|
-
if (local.class) {
|
|
85
|
-
parts.push(local.class);
|
|
86
|
-
}
|
|
87
|
-
return parts.join(" ");
|
|
88
|
-
};
|
|
167
|
+
const classes = () => `tab ${local.class ?? ""}`.trim();
|
|
89
168
|
|
|
90
169
|
return (
|
|
91
170
|
<button
|
|
92
171
|
type="button"
|
|
93
172
|
role="tab"
|
|
94
|
-
aria-selected=
|
|
95
|
-
tabIndex={
|
|
173
|
+
aria-selected="false"
|
|
174
|
+
tabIndex={-1}
|
|
96
175
|
class={classes()}
|
|
97
|
-
|
|
176
|
+
data-tab-value={local.value}
|
|
98
177
|
{...rest}
|
|
99
178
|
>
|
|
100
179
|
{local.children}
|
|
@@ -102,19 +181,26 @@ export function Tab(props: TabProps) {
|
|
|
102
181
|
);
|
|
103
182
|
}
|
|
104
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Content panel that is shown when its corresponding Tab is active.
|
|
186
|
+
*
|
|
187
|
+
* The `value` prop must match a corresponding `Tab` value.
|
|
188
|
+
* Panels are hidden by default and shown when their tab is selected.
|
|
189
|
+
* In Astro, requires `client:load` directive.
|
|
190
|
+
*/
|
|
191
|
+
export interface TabPanelProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
192
|
+
/** Unique identifier that links this panel to its tab */
|
|
193
|
+
value: string;
|
|
194
|
+
children: JSX.Element;
|
|
195
|
+
}
|
|
196
|
+
|
|
105
197
|
export function TabPanel(props: TabPanelProps) {
|
|
106
198
|
const [local, rest] = splitProps(props, ["value", "children", "class"]);
|
|
107
|
-
const ctx = useContext(TabsContext);
|
|
108
|
-
|
|
109
|
-
const isActive = () => ctx?.activeTab() === local.value;
|
|
110
|
-
|
|
111
199
|
const classes = () => `tab-panel ${local.class ?? ""}`.trim();
|
|
112
200
|
|
|
113
201
|
return (
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
</div>
|
|
118
|
-
</Show>
|
|
202
|
+
<div class={classes()} role="tabpanel" data-panel-value={local.value} hidden {...rest}>
|
|
203
|
+
{local.children}
|
|
204
|
+
</div>
|
|
119
205
|
);
|
|
120
206
|
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/* Starlight Theme Overrides */
|
|
2
|
+
|
|
3
|
+
/* Map library tokens to Starlight variables */
|
|
4
|
+
:root {
|
|
5
|
+
--code-background: oklch(from var(--bg) calc(l - 0.03) c h);
|
|
6
|
+
--ec-codeBg: var(--code-background);
|
|
7
|
+
--sl-color-bg: var(--bg);
|
|
8
|
+
--sl-color-bg-nav: var(--bg);
|
|
9
|
+
--sl-color-bg-sidebar: var(--bg);
|
|
10
|
+
--sl-color-text: var(--fg-muted);
|
|
11
|
+
--sl-color-text-accent: var(--accent);
|
|
12
|
+
--sl-color-hairline: var(--border);
|
|
13
|
+
--sl-color-hairline-light: var(--border);
|
|
14
|
+
--sl-font: var(--font);
|
|
15
|
+
--sl-font-system: var(--font);
|
|
16
|
+
--sl-font-mono: var(--font-mono);
|
|
17
|
+
--sl-color-accent-low: var(--bg);
|
|
18
|
+
--sl-color-accent: var(--accent);
|
|
19
|
+
--sl-color-accent-high: var(--fg);
|
|
20
|
+
--sl-color-white: var(--fg);
|
|
21
|
+
--sl-color-gray-1: var(--fg-muted);
|
|
22
|
+
--sl-color-gray-2: var(--fg-subtle);
|
|
23
|
+
--sl-color-gray-3: var(--fg-faint);
|
|
24
|
+
--sl-color-gray-4: var(--border);
|
|
25
|
+
--sl-color-gray-5: var(--bg-alt);
|
|
26
|
+
--sl-color-gray-6: var(--bg);
|
|
27
|
+
--sl-color-black: var(--bg);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Header - smaller height and border */
|
|
31
|
+
:root {
|
|
32
|
+
--sl-nav-height: 2.75rem;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media (min-width: 50rem) {
|
|
36
|
+
:root {
|
|
37
|
+
--sl-nav-height: 3rem;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
header.header {
|
|
42
|
+
border-bottom: 1px solid var(--border);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Apply stack spacing to markdown content */
|
|
46
|
+
.sl-markdown-content {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
gap: var(--space-md);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Override Starlight's aggressive content margin rules */
|
|
53
|
+
.sl-markdown-content :not(h1, h2, h3, h4, h5, h6) + :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content, .sl-steps *)) {
|
|
54
|
+
margin-top: 0 !important;
|
|
55
|
+
}
|
|
56
|
+
.sl-markdown-content :not(a, strong, em, del, span, input, code, br) + :not(a, strong, em, del, span, input, code, br, :where(.not-content, .sl-steps *)) {
|
|
57
|
+
margin-top: 0 !important;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Hide Starlight's default splash hero/tagline/banner/title on landing page */
|
|
61
|
+
.sl-hero,
|
|
62
|
+
.tagline,
|
|
63
|
+
.sl-banner {
|
|
64
|
+
display: none;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Hide the content panel containing the splash page title placeholder */
|
|
68
|
+
.content-panel:has(.splash-title-placeholder) {
|
|
69
|
+
display: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.splash-title-placeholder {
|
|
73
|
+
display: none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Hide the empty content panel with just an h1#_top on splash pages */
|
|
77
|
+
.content-panel:has(> .sl-container > h1#_top:only-child:empty) {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Sidebar border */
|
|
82
|
+
nav .sidebar-pane {
|
|
83
|
+
border-right: 1px solid var(--border);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Remove content max-width constraint */
|
|
87
|
+
.main-pane .sl-container {
|
|
88
|
+
max-width: unset;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
main {
|
|
92
|
+
padding-bottom: unset !important;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Cap "On this page" TOC width and adjust main pane on large screens */
|
|
96
|
+
@media (min-width: 72rem) {
|
|
97
|
+
.right-sidebar-panel .sl-container {
|
|
98
|
+
max-width: 25rem;
|
|
99
|
+
width: 25rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[data-has-sidebar][data-has-toc] .main-pane {
|
|
103
|
+
width: 100%;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.right-sidebar-container {
|
|
107
|
+
width: 25rem;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Selected sidebar item - override Starlight defaults */
|
|
112
|
+
[aria-current="page"],
|
|
113
|
+
[aria-current="page"]:hover,
|
|
114
|
+
[aria-current="page"]:focus {
|
|
115
|
+
background-color: transparent;
|
|
116
|
+
color: var(--fg);
|
|
117
|
+
text-decoration: underline;
|
|
118
|
+
font-weight: 500;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Reduce title sizes */
|
|
122
|
+
h1#_top,
|
|
123
|
+
.content-panel h1 {
|
|
124
|
+
font-size: 1.5rem;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.sl-markdown-content h1 {
|
|
128
|
+
font-size: 1.5rem;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.sl-markdown-content h2 {
|
|
132
|
+
font-size: 1.25rem;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.sl-markdown-content h3 {
|
|
136
|
+
font-size: 1.125rem;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.sl-markdown-content h4,
|
|
140
|
+
.sl-markdown-content h5,
|
|
141
|
+
.sl-markdown-content h6 {
|
|
142
|
+
font-size: 1rem;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Apply library .list styles to markdown content lists */
|
|
146
|
+
.sl-markdown-content ul,
|
|
147
|
+
.sl-markdown-content ol {
|
|
148
|
+
padding-left: var(--space-lg);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.sl-markdown-content ul {
|
|
152
|
+
list-style-type: disc;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.sl-markdown-content ol {
|
|
156
|
+
list-style-type: decimal;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.sl-markdown-content li {
|
|
160
|
+
position: relative;
|
|
161
|
+
padding-left: var(--space-sm);
|
|
162
|
+
margin-bottom: var(--space-xs);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.sl-markdown-content li::marker {
|
|
166
|
+
color: var(--fg-faint);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.sl-markdown-content li:last-child {
|
|
170
|
+
margin-bottom: 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.sl-markdown-content :is(ul, ol) :is(ul, ol) {
|
|
174
|
+
margin-top: var(--space-xs);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.sl-markdown-content ul ul {
|
|
178
|
+
list-style-type: circle;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.sl-markdown-content ul ul ul {
|
|
182
|
+
list-style-type: square;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.sl-markdown-content ol ol {
|
|
186
|
+
list-style-type: lower-alpha;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.sl-markdown-content ol ol ol {
|
|
190
|
+
list-style-type: lower-roman;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Override expressive-code backgrounds */
|
|
194
|
+
.expressive-code,
|
|
195
|
+
.expressive-code pre,
|
|
196
|
+
.expressive-code code,
|
|
197
|
+
.expressive-code .frame {
|
|
198
|
+
background: var(--code-background);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Pagination - compact button style */
|
|
202
|
+
.pagination-links {
|
|
203
|
+
gap: var(--space-sm);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.pagination-links a {
|
|
207
|
+
padding: var(--space-sm) var(--space-md);
|
|
208
|
+
border: 1px solid var(--border);
|
|
209
|
+
border-radius: var(--radius);
|
|
210
|
+
box-shadow: none;
|
|
211
|
+
color: var(--fg-muted);
|
|
212
|
+
background: transparent;
|
|
213
|
+
transition: border-color var(--transition), color var(--transition);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.pagination-links a:hover {
|
|
217
|
+
border-color: var(--fg-faint);
|
|
218
|
+
color: var(--fg);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Hide "Previous"/"Next" text, keep only the page title */
|
|
222
|
+
.pagination-links a > span {
|
|
223
|
+
font-size: 0;
|
|
224
|
+
line-height: 0;
|
|
225
|
+
height: 1.35rem;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.pagination-links a > span br {
|
|
229
|
+
display: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.pagination-links .link-title {
|
|
233
|
+
font-size: var(--text-sm);
|
|
234
|
+
color: var(--fg);
|
|
235
|
+
font-weight: 500;
|
|
236
|
+
line-height: var(--sl-line-height);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.pagination-links svg {
|
|
240
|
+
width: 1rem;
|
|
241
|
+
height: 1rem;
|
|
242
|
+
color: var(--fg-faint);
|
|
243
|
+
}
|