docyard 0.4.0 → 0.5.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.
@@ -0,0 +1,114 @@
1
+ /* Previous/Next Navigation Footer */
2
+
3
+ .doc-footer {
4
+ margin-top: 48px;
5
+ padding-top: 24px;
6
+ border-top: 1px solid var(--color-border);
7
+ }
8
+
9
+ .pager {
10
+ display: grid;
11
+ grid-template-columns: repeat(2, 1fr);
12
+ gap: 8px;
13
+ }
14
+
15
+ .pager-link {
16
+ display: block;
17
+ padding: 16px;
18
+ border: 1px solid var(--color-border);
19
+ border-radius: 8px;
20
+ text-decoration: none !important;
21
+ transition: border-color 0.25s ease, background-color 0.25s ease;
22
+ outline: none;
23
+ background-color: transparent;
24
+ }
25
+
26
+ .pager-link:hover {
27
+ border-color: var(--color-primary);
28
+ background-color: var(--color-background-soft);
29
+ }
30
+
31
+ .pager-link:focus-visible {
32
+ outline: 2px solid var(--color-primary);
33
+ outline-offset: 2px;
34
+ border-radius: 8px;
35
+ }
36
+
37
+ .pager-link.prev {
38
+ text-align: left;
39
+ }
40
+
41
+ .pager-link.next {
42
+ text-align: right;
43
+ }
44
+
45
+ .pager-label {
46
+ display: block;
47
+ font-size: 13px;
48
+ font-weight: 500;
49
+ color: var(--color-text-secondary);
50
+ margin-bottom: 0px;
51
+ text-decoration: none !important;
52
+ }
53
+
54
+ .pager-title {
55
+ display: block;
56
+ font-size: 14px;
57
+ font-weight: 500;
58
+ color: var(--color-text-primary);
59
+ overflow: hidden;
60
+ text-overflow: ellipsis;
61
+ white-space: nowrap;
62
+ text-decoration: none !important;
63
+ }
64
+
65
+ /* Mobile responsive */
66
+ @media (max-width: 640px) {
67
+ .doc-footer {
68
+ margin-top: 48px;
69
+ }
70
+
71
+ .pager {
72
+ grid-template-columns: 1fr;
73
+ }
74
+
75
+ .pager-link.next {
76
+ margin-left: 0;
77
+ }
78
+
79
+ .pager-link {
80
+ width: 100%;
81
+ }
82
+
83
+ .pager-title {
84
+ white-space: normal;
85
+ word-break: break-word;
86
+ }
87
+ }
88
+
89
+ /* Reduced motion accessibility */
90
+ @media (prefers-reduced-motion: reduce) {
91
+ .pager-link {
92
+ transition: none;
93
+ }
94
+ }
95
+
96
+ /* Print: hide navigation */
97
+ @media print {
98
+ .doc-footer {
99
+ display: none;
100
+ }
101
+ }
102
+
103
+ /* Visually hidden utility (for screen readers) */
104
+ .visually-hidden {
105
+ position: absolute;
106
+ width: 1px;
107
+ height: 1px;
108
+ padding: 0;
109
+ margin: -1px;
110
+ overflow: hidden;
111
+ clip: rect(0, 0, 0, 0);
112
+ white-space: nowrap;
113
+ border-width: 0;
114
+ }
@@ -0,0 +1,269 @@
1
+ /* Table of Contents - Desktop & Mobile */
2
+
3
+ .docyard-toc {
4
+ width: var(--toc-width);
5
+ position: fixed;
6
+ top: var(--header-height);
7
+ right: 0;
8
+ height: calc(100vh - var(--header-height));
9
+ padding: var(--space-3) var(--space-6) var(--space-3) var(--space-8);
10
+ overflow-y: auto;
11
+ overflow-x: hidden;
12
+ border-left: 1px solid var(--color-border);
13
+ background-color: var(--color-bg);
14
+ }
15
+
16
+ /* TOC Header */
17
+ .docyard-toc__header {
18
+ margin-bottom: var(--space-4);
19
+ }
20
+
21
+ .docyard-toc__title {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 0.375rem;
25
+ font-size: 0.6875rem;
26
+ font-weight: var(--font-weight-semibold);
27
+ color: var(--color-text-tertiary);
28
+ text-transform: uppercase;
29
+ letter-spacing: 0.1em;
30
+ margin-bottom: 0;
31
+ }
32
+
33
+ .docyard-toc__title svg {
34
+ flex-shrink: 0;
35
+ }
36
+
37
+ /* Secondary Header TOC Toggle (tablet & mobile) */
38
+ .secondary-header-toc-toggle {
39
+ display: none;
40
+ }
41
+
42
+ /* Tablet & Mobile TOC Toggle */
43
+ @media (max-width: 1280px) {
44
+ .secondary-header-toc-toggle {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: var(--space-2);
48
+ padding: var(--space-2) var(--space-3);
49
+ background: transparent;
50
+ border: none;
51
+ color: var(--color-text);
52
+ font-size: var(--font-size-sm);
53
+ font-weight: var(--font-weight-medium);
54
+ font-family: var(--font-sans);
55
+ cursor: pointer;
56
+ transition: color var(--transition-fast);
57
+ outline: none;
58
+ }
59
+
60
+ .secondary-header-toc-toggle:hover {
61
+ color: var(--color-text-secondary);
62
+ }
63
+
64
+ .secondary-header-toc-toggle:focus-visible {
65
+ outline: 2px solid var(--color-primary);
66
+ outline-offset: 2px;
67
+ border-radius: var(--radius-sm);
68
+ }
69
+
70
+ .secondary-header-toc-toggle svg {
71
+ transform: rotate(-90deg);
72
+ transition: transform var(--transition-base);
73
+ }
74
+
75
+ .secondary-header-toc-toggle[aria-expanded="true"] svg {
76
+ transform: rotate(0deg);
77
+ }
78
+ }
79
+
80
+ /* TOC Navigation */
81
+ .docyard-toc__nav {
82
+ display: block;
83
+ }
84
+
85
+ /* TOC List */
86
+ .docyard-toc__list {
87
+ list-style: none;
88
+ padding: 0;
89
+ margin: 0;
90
+ }
91
+
92
+ .docyard-toc__list .docyard-toc__list {
93
+ margin-top: var(--space-1);
94
+ margin-left: var(--space-4);
95
+ border-left: 1px solid var(--color-border);
96
+ padding-left: var(--space-3);
97
+ }
98
+
99
+ /* TOC Items */
100
+ .docyard-toc__item {
101
+ margin-bottom: 0.375rem;
102
+ }
103
+
104
+ /* TOC Links */
105
+ .docyard-toc__link {
106
+ display: block;
107
+ padding: 0.25rem var(--space-2);
108
+ color: var(--color-text-secondary);
109
+ text-decoration: none;
110
+ font-size: var(--font-size-sm);
111
+ line-height: var(--line-height-tight);
112
+ border-radius: var(--radius-sm);
113
+ transition: color var(--transition-fast);
114
+ font-weight: var(--font-weight-medium);
115
+ overflow: hidden;
116
+ text-overflow: ellipsis;
117
+ white-space: nowrap;
118
+ }
119
+
120
+ .docyard-toc__link:hover {
121
+ color: var(--color-text);
122
+ text-decoration: none;
123
+ }
124
+
125
+ .docyard-toc__link:focus-visible {
126
+ outline: 2px solid var(--color-primary);
127
+ outline-offset: 2px;
128
+ color: var(--color-text);
129
+ text-decoration: none;
130
+ }
131
+
132
+ /* Active link */
133
+ .docyard-toc__link.is-active {
134
+ color: var(--color-primary);
135
+ font-weight: var(--font-weight-semibold);
136
+ }
137
+
138
+ /* Level-specific styling */
139
+ .docyard-toc__item--level-2>.docyard-toc__link {
140
+ font-weight: var(--font-weight-medium);
141
+ }
142
+
143
+ .docyard-toc__item--level-3>.docyard-toc__link {
144
+ font-size: 0.8125rem;
145
+ }
146
+
147
+ .docyard-toc__item--level-4>.docyard-toc__link {
148
+ font-size: 0.8125rem;
149
+ color: var(--color-text-tertiary);
150
+ }
151
+
152
+ /* TOC Scrollbar */
153
+ .docyard-toc::-webkit-scrollbar {
154
+ width: 0.5rem;
155
+ }
156
+
157
+ .docyard-toc::-webkit-scrollbar-track {
158
+ background: transparent;
159
+ }
160
+
161
+ .docyard-toc::-webkit-scrollbar-thumb {
162
+ background: rgba(208, 215, 222, 0.5);
163
+ border-radius: 0.25rem;
164
+ }
165
+
166
+ .docyard-toc::-webkit-scrollbar-thumb:hover {
167
+ background: rgba(208, 215, 222, 0.7);
168
+ }
169
+
170
+ .dark .docyard-toc::-webkit-scrollbar-thumb {
171
+ background: rgba(110, 118, 129, 0.4);
172
+ }
173
+
174
+ .dark .docyard-toc::-webkit-scrollbar-thumb:hover {
175
+ background: rgba(110, 118, 129, 0.6);
176
+ }
177
+
178
+ /* Tablet & Mobile Layout - TOC in secondary header */
179
+ @media (max-width: 1280px) {
180
+ .docyard-toc {
181
+ display: block;
182
+ position: static;
183
+ width: 0;
184
+ height: 0;
185
+ padding: 0;
186
+ margin: 0;
187
+ border: none;
188
+ overflow: visible;
189
+ }
190
+
191
+ .docyard-toc__header {
192
+ display: none;
193
+ }
194
+
195
+ .docyard-toc__nav {
196
+ display: none;
197
+ position: fixed;
198
+ overflow-y: auto;
199
+ overscroll-behavior: contain;
200
+ padding: var(--space-5);
201
+ background: var(--color-bg);
202
+ border: 1px solid var(--color-border);
203
+ border-radius: var(--radius-lg);
204
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
205
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06),
206
+ 0 0 0 1px rgba(0, 0, 0, 0.05);
207
+ z-index: 9999;
208
+ transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1),
209
+ opacity 0.2s ease,
210
+ transform 0.2s ease;
211
+ transform: translateY(-8px);
212
+ opacity: 0;
213
+ pointer-events: none;
214
+ }
215
+
216
+ .dark .docyard-toc__nav {
217
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3),
218
+ 0 2px 4px -1px rgba(0, 0, 0, 0.2),
219
+ 0 0 0 1px rgba(255, 255, 255, 0.1);
220
+ }
221
+
222
+ .docyard-toc__nav.is-expanded {
223
+ display: block;
224
+ opacity: 1;
225
+ transform: translateY(0);
226
+ pointer-events: auto;
227
+ }
228
+ }
229
+
230
+ /* Tablet-specific: floating dropdown on right */
231
+ @media (max-width: 1280px) and (min-width: 1025px) {
232
+ .docyard-toc__nav {
233
+ top: calc(var(--header-height) + 3rem + var(--space-2));
234
+ right: var(--space-6);
235
+ width: 280px;
236
+ max-height: calc(100vh - var(--header-height) - 3rem - var(--space-8));
237
+ }
238
+ }
239
+
240
+ /* Mobile Layout (< 1024px) */
241
+ @media (max-width: 1024px) {
242
+ .docyard-toc__nav {
243
+ top: calc(var(--header-height) + 3rem - 2px);
244
+ left: var(--space-4);
245
+ right: var(--space-4);
246
+ max-height: calc(100vh - var(--header-height) - 3rem - var(--space-2));
247
+ }
248
+
249
+ .secondary-header.shift-up~.layout .docyard-toc__nav {
250
+ top: calc(3rem - 2px);
251
+ max-height: calc(100vh - 3rem - var(--space-2));
252
+ }
253
+
254
+ .docyard-toc__list .docyard-toc__list {
255
+ margin-left: var(--space-3);
256
+ padding-left: var(--space-2);
257
+ }
258
+ }
259
+
260
+ /* Reduced Motion */
261
+ @media (prefers-reduced-motion: reduce) {
262
+ .docyard-toc__link {
263
+ transition: none;
264
+ }
265
+
266
+ .secondary-header-toc-toggle svg {
267
+ transition: none;
268
+ }
269
+ }
@@ -65,11 +65,45 @@
65
65
  border-radius: var(--radius-sm);
66
66
  }
67
67
 
68
- /* Secondary Header (Mobile Navigation Bar) */
68
+ /* Secondary Header (Tablet & Mobile Navigation Bar) */
69
69
  .secondary-header {
70
70
  display: none;
71
71
  }
72
72
 
73
+ /* Tablet: Show secondary header with TOC toggle only */
74
+ @media (max-width: 1280px) and (min-width: 1025px) {
75
+ .secondary-header {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: flex-end;
79
+ position: fixed;
80
+ top: var(--header-height);
81
+ left: var(--sidebar-width);
82
+ right: 0;
83
+ height: 3rem;
84
+ min-height: 3rem;
85
+ background: var(--color-bg);
86
+ border-bottom: 1px solid var(--color-border);
87
+ padding: 0 var(--space-6);
88
+ z-index: var(--z-secondary-header);
89
+ transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1), left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
90
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.02);
91
+ }
92
+
93
+ .secondary-header.shift-up {
94
+ top: 0;
95
+ }
96
+
97
+ .secondary-header-menu {
98
+ display: none;
99
+ }
100
+
101
+ .layout {
102
+ padding-top: calc(var(--header-height) + 3rem);
103
+ }
104
+ }
105
+
106
+ /* Mobile: Show menu button + TOC toggle */
73
107
  @media (max-width: 1024px) {
74
108
  .secondary-header {
75
109
  display: flex;
@@ -80,6 +114,7 @@
80
114
  left: 0;
81
115
  right: 0;
82
116
  height: 3rem;
117
+ min-height: 3rem;
83
118
  background: var(--color-bg);
84
119
  border-bottom: 1px solid var(--color-border);
85
120
  padding: 0 var(--space-6);
@@ -107,6 +142,7 @@
107
142
  transition: background-color 0.2s ease;
108
143
  font-family: var(--font-sans);
109
144
  margin-left: calc(-1 * var(--space-2));
145
+ outline: none;
110
146
  }
111
147
 
112
148
  .secondary-header-menu:hover {
@@ -117,6 +153,12 @@
117
153
  background-color: var(--color-border);
118
154
  }
119
155
 
156
+ .secondary-header-menu:focus-visible {
157
+ outline: 2px solid var(--color-primary);
158
+ outline-offset: 2px;
159
+ border-radius: var(--radius-sm);
160
+ }
161
+
120
162
  .secondary-header-menu .icon {
121
163
  flex-shrink: 0;
122
164
  }
@@ -176,11 +218,19 @@
176
218
  .layout-main {
177
219
  flex: 1;
178
220
  margin-left: var(--sidebar-width);
221
+ margin-right: var(--toc-width, 0);
179
222
  display: flex;
180
223
  flex-direction: column;
181
224
  min-width: 0;
182
225
  }
183
226
 
227
+ /* Tablet Layout (1024px - 1280px) */
228
+ @media (max-width: 1280px) and (min-width: 1025px) {
229
+ .layout-main {
230
+ margin-right: 0;
231
+ }
232
+ }
233
+
184
234
  /* Content wrapper */
185
235
  main.content {
186
236
  flex: 1;
@@ -200,6 +250,7 @@ main.content {
200
250
 
201
251
  .layout {
202
252
  padding-top: calc(var(--header-height) + 3rem);
253
+ flex-direction: column;
203
254
  }
204
255
 
205
256
  .sidebar {
@@ -223,8 +274,14 @@ main.content {
223
274
  box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
224
275
  }
225
276
 
277
+ .docyard-toc {
278
+ order: 1;
279
+ }
280
+
226
281
  .layout-main {
227
282
  margin-left: 0;
283
+ margin-right: 0;
284
+ order: 2;
228
285
  }
229
286
 
230
287
  main.content {
@@ -98,6 +98,7 @@
98
98
 
99
99
  /* Layout */
100
100
  --sidebar-width: 288px;
101
+ --toc-width: 280px;
101
102
  --header-height: 64px;
102
103
  --content-max-width: 800px;
103
104
  --layout-max-width: 1440px;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * HeadingAnchorManager handles anchor link interactions
3
+ * Provides copy-to-clipboard functionality with visual feedback
4
+ */
5
+ class HeadingAnchorManager {
6
+ constructor() {
7
+ this.anchors = document.querySelectorAll('.heading-anchor');
8
+ this.init();
9
+ }
10
+
11
+ init() {
12
+ this.anchors.forEach(anchor => {
13
+ anchor.addEventListener('click', (e) => this.handleClick(e, anchor));
14
+ });
15
+ }
16
+
17
+ /**
18
+ * Handle anchor link click
19
+ * @param {Event} e - Click event
20
+ * @param {HTMLElement} anchor - Anchor element
21
+ */
22
+ handleClick(e, anchor) {
23
+ e.preventDefault();
24
+
25
+ const headingId = anchor.dataset.headingId;
26
+ const url = `${window.location.origin}${window.location.pathname}#${headingId}`;
27
+
28
+ this.copyToClipboard(url, anchor);
29
+
30
+ history.pushState(null, null, `#${headingId}`);
31
+
32
+ const heading = document.getElementById(headingId);
33
+ if (heading) {
34
+ heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Copy text to clipboard with visual feedback
40
+ * @param {string} text - Text to copy
41
+ * @param {HTMLElement} anchor - Anchor element for feedback
42
+ */
43
+ async copyToClipboard(text, anchor) {
44
+ try {
45
+ await navigator.clipboard.writeText(text);
46
+ this.showFeedback(anchor, true);
47
+ } catch (err) {
48
+ this.fallbackCopyToClipboard(text);
49
+ this.showFeedback(anchor, true);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Fallback copy method for older browsers
55
+ * @param {string} text - Text to copy
56
+ */
57
+ fallbackCopyToClipboard(text) {
58
+ const textarea = document.createElement('textarea');
59
+ textarea.value = text;
60
+ textarea.style.position = 'fixed';
61
+ textarea.style.opacity = '0';
62
+ document.body.appendChild(textarea);
63
+ textarea.select();
64
+ document.execCommand('copy');
65
+ document.body.removeChild(textarea);
66
+ }
67
+
68
+ /**
69
+ * Show visual feedback on copy
70
+ * @param {HTMLElement} anchor - Anchor element
71
+ * @param {boolean} success - Whether copy succeeded
72
+ */
73
+ showFeedback(anchor, success) {
74
+ const originalTitle = anchor.getAttribute('aria-label');
75
+ anchor.setAttribute('aria-label', success ? 'Link copied!' : 'Failed to copy');
76
+
77
+ anchor.style.color = success ? 'var(--color-success, #10b981)' : 'var(--color-danger, #ef4444)';
78
+
79
+ setTimeout(() => {
80
+ anchor.setAttribute('aria-label', originalTitle);
81
+ anchor.style.color = '';
82
+ }, 2000);
83
+ }
84
+ }
85
+
86
+ if (typeof window !== 'undefined') {
87
+ document.addEventListener('DOMContentLoaded', () => {
88
+ new HeadingAnchorManager();
89
+ });
90
+ }
@@ -9,7 +9,9 @@
9
9
  const sidebar = document.querySelector('.sidebar');
10
10
  const overlay = document.querySelector('.mobile-overlay');
11
11
 
12
- if (!toggle || !sidebar || !overlay) return;
12
+ if (!toggle || !sidebar || !overlay) {
13
+ return;
14
+ }
13
15
 
14
16
  function openMenu() {
15
17
  sidebar.classList.add('is-open');
@@ -33,7 +35,9 @@
33
35
  }
34
36
  }
35
37
 
36
- toggle.addEventListener('click', toggleMenu);
38
+ toggle.addEventListener('click', function(e) {
39
+ toggleMenu();
40
+ });
37
41
  overlay.addEventListener('click', closeMenu);
38
42
 
39
43
  document.addEventListener('keydown', function(e) {