docyard 0.2.0 → 0.3.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -1
  3. data/LICENSE.vscode-icons +42 -0
  4. data/README.md +46 -5
  5. data/lib/docyard/asset_handler.rb +33 -0
  6. data/lib/docyard/components/base_processor.rb +24 -0
  7. data/lib/docyard/components/callout_processor.rb +121 -0
  8. data/lib/docyard/components/code_block_processor.rb +55 -0
  9. data/lib/docyard/components/code_detector.rb +59 -0
  10. data/lib/docyard/components/icon_detector.rb +57 -0
  11. data/lib/docyard/components/icon_processor.rb +51 -0
  12. data/lib/docyard/components/registry.rb +34 -0
  13. data/lib/docyard/components/tabs_parser.rb +60 -0
  14. data/lib/docyard/components/tabs_processor.rb +44 -0
  15. data/lib/docyard/config/validator.rb +171 -0
  16. data/lib/docyard/config.rb +133 -0
  17. data/lib/docyard/constants.rb +5 -0
  18. data/lib/docyard/icons/LICENSE.phosphor +21 -0
  19. data/lib/docyard/icons/file_types.rb +92 -0
  20. data/lib/docyard/icons/phosphor.rb +63 -0
  21. data/lib/docyard/icons.rb +40 -0
  22. data/lib/docyard/initializer.rb +20 -2
  23. data/lib/docyard/language_mapping.rb +52 -0
  24. data/lib/docyard/markdown.rb +14 -3
  25. data/lib/docyard/rack_application.rb +76 -7
  26. data/lib/docyard/renderer.rb +40 -7
  27. data/lib/docyard/server.rb +5 -2
  28. data/lib/docyard/sidebar_builder.rb +10 -2
  29. data/lib/docyard/templates/assets/css/code.css +150 -2
  30. data/lib/docyard/templates/assets/css/components/callout.css +169 -0
  31. data/lib/docyard/templates/assets/css/components/code-block.css +196 -0
  32. data/lib/docyard/templates/assets/css/components/icon.css +16 -0
  33. data/lib/docyard/templates/assets/css/components/logo.css +44 -0
  34. data/lib/docyard/templates/assets/css/{components.css → components/navigation.css} +47 -47
  35. data/lib/docyard/templates/assets/css/components/tabs.css +298 -0
  36. data/lib/docyard/templates/assets/css/components/theme-toggle.css +61 -0
  37. data/lib/docyard/templates/assets/css/layout.css +14 -4
  38. data/lib/docyard/templates/assets/css/markdown.css +9 -8
  39. data/lib/docyard/templates/assets/css/reset.css +4 -0
  40. data/lib/docyard/templates/assets/css/variables.css +94 -3
  41. data/lib/docyard/templates/assets/favicon.svg +16 -0
  42. data/lib/docyard/templates/assets/js/components/code-block.js +162 -0
  43. data/lib/docyard/templates/assets/js/components/tabs.js +338 -0
  44. data/lib/docyard/templates/assets/js/theme.js +16 -0
  45. data/lib/docyard/templates/assets/logo-dark.svg +4 -0
  46. data/lib/docyard/templates/assets/logo.svg +12 -0
  47. data/lib/docyard/templates/config/docyard.yml.erb +20 -0
  48. data/lib/docyard/templates/layouts/default.html.erb +31 -3
  49. data/lib/docyard/templates/markdown/components/callouts.md.erb +204 -0
  50. data/lib/docyard/templates/markdown/components/icons.md.erb +125 -0
  51. data/lib/docyard/templates/markdown/components/tabs.md.erb +686 -0
  52. data/lib/docyard/templates/markdown/configuration.md.erb +202 -0
  53. data/lib/docyard/templates/partials/_callout.html.erb +11 -0
  54. data/lib/docyard/templates/partials/_code_block.html.erb +6 -0
  55. data/lib/docyard/templates/partials/_icon.html.erb +1 -0
  56. data/lib/docyard/templates/partials/_icon_file_extension.html.erb +1 -0
  57. data/lib/docyard/templates/partials/_tabs.html.erb +40 -0
  58. data/lib/docyard/templates/partials/_theme_toggle.html.erb +13 -0
  59. data/lib/docyard/version.rb +1 -1
  60. metadata +41 -2
@@ -35,7 +35,7 @@
35
35
  }
36
36
 
37
37
  .highlight table td {
38
- padding: 5px;
38
+ padding: 0.3125rem;
39
39
  }
40
40
 
41
41
  .highlight table pre {
@@ -196,7 +196,7 @@
196
196
 
197
197
  /* Code block scrollbar */
198
198
  .content pre::-webkit-scrollbar {
199
- height: 8px;
199
+ height: 0.5rem;
200
200
  }
201
201
 
202
202
  .content pre::-webkit-scrollbar-track {
@@ -212,3 +212,151 @@
212
212
  .content pre::-webkit-scrollbar-thumb:hover {
213
213
  background: var(--color-text-tertiary);
214
214
  }
215
+
216
+ /* Dark Mode Syntax Highlighting - GitHub Dark */
217
+ .dark .highlight,
218
+ .dark .highlight .w {
219
+ color: #e6edf3;
220
+ background-color: #161b22;
221
+ }
222
+
223
+ /* Keywords */
224
+ .dark .highlight .k,
225
+ .dark .highlight .kd,
226
+ .dark .highlight .kn,
227
+ .dark .highlight .kp,
228
+ .dark .highlight .kr,
229
+ .dark .highlight .kt,
230
+ .dark .highlight .kv {
231
+ color: #ff7b72;
232
+ }
233
+
234
+ .dark .highlight .gr {
235
+ color: #161b22;
236
+ }
237
+
238
+ .dark .highlight .gd {
239
+ color: #ffa198;
240
+ background-color: #490202;
241
+ }
242
+
243
+ /* Built-in names */
244
+ .dark .highlight .nb,
245
+ .dark .highlight .nc,
246
+ .dark .highlight .no,
247
+ .dark .highlight .nn {
248
+ color: #ffa657;
249
+ }
250
+
251
+ /* Strings and regex */
252
+ .dark .highlight .sr,
253
+ .dark .highlight .na,
254
+ .dark .highlight .nt {
255
+ color: #7ee787;
256
+ }
257
+
258
+ .dark .highlight .gi {
259
+ color: #7ee787;
260
+ background-color: #0f3b17;
261
+ }
262
+
263
+ /* Constants and literals */
264
+ .dark .highlight .kc,
265
+ .dark .highlight .l,
266
+ .dark .highlight .ld,
267
+ .dark .highlight .m,
268
+ .dark .highlight .mb,
269
+ .dark .highlight .mf,
270
+ .dark .highlight .mh,
271
+ .dark .highlight .mi,
272
+ .dark .highlight .il,
273
+ .dark .highlight .mo,
274
+ .dark .highlight .mx,
275
+ .dark .highlight .sb,
276
+ .dark .highlight .bp,
277
+ .dark .highlight .ne,
278
+ .dark .highlight .nl,
279
+ .dark .highlight .py {
280
+ color: #79c0ff;
281
+ }
282
+
283
+ /* Variables */
284
+ .dark .highlight .nv,
285
+ .dark .highlight .vc,
286
+ .dark .highlight .vg,
287
+ .dark .highlight .vi,
288
+ .dark .highlight .vm {
289
+ color: #79c0ff;
290
+ }
291
+
292
+ /* Operators */
293
+ .dark .highlight .o,
294
+ .dark .highlight .ow {
295
+ color: #79c0ff;
296
+ }
297
+
298
+ /* Headers and subheaders in diffs */
299
+ .dark .highlight .gh,
300
+ .dark .highlight .gu {
301
+ color: #79c0ff;
302
+ font-weight: bold;
303
+ }
304
+
305
+ /* Strings */
306
+ .dark .highlight .s,
307
+ .dark .highlight .sa,
308
+ .dark .highlight .sc,
309
+ .dark .highlight .dl,
310
+ .dark .highlight .sd,
311
+ .dark .highlight .s2,
312
+ .dark .highlight .se,
313
+ .dark .highlight .sh,
314
+ .dark .highlight .sx,
315
+ .dark .highlight .s1,
316
+ .dark .highlight .ss {
317
+ color: #a5d6ff;
318
+ }
319
+
320
+ /* Decorators and functions */
321
+ .dark .highlight .nd,
322
+ .dark .highlight .nf,
323
+ .dark .highlight .fm {
324
+ color: #d2a8ff;
325
+ }
326
+
327
+ /* Errors */
328
+ .dark .highlight .err {
329
+ color: #161b22;
330
+ background-color: #ffa198;
331
+ }
332
+
333
+ /* Comments */
334
+ .dark .highlight .c,
335
+ .dark .highlight .ch,
336
+ .dark .highlight .cd,
337
+ .dark .highlight .cm,
338
+ .dark .highlight .cp,
339
+ .dark .highlight .cpf,
340
+ .dark .highlight .c1,
341
+ .dark .highlight .cs,
342
+ .dark .highlight .gl,
343
+ .dark .highlight .gt {
344
+ color: #8b949e;
345
+ }
346
+
347
+ /* Name indicators */
348
+ .dark .highlight .ni,
349
+ .dark .highlight .si {
350
+ color: #e6edf3;
351
+ }
352
+
353
+ /* Generic emphasis */
354
+ .dark .highlight .ge {
355
+ color: #e6edf3;
356
+ font-style: italic;
357
+ }
358
+
359
+ .dark .highlight .gs {
360
+ color: #e6edf3;
361
+ font-weight: bold;
362
+ }
@@ -0,0 +1,169 @@
1
+ /* Callouts/Admonitions */
2
+ .docyard-callout {
3
+ display: flex;
4
+ gap: var(--space-3);
5
+ padding: var(--space-4);
6
+ margin: var(--space-6) 0;
7
+ border: 1px solid;
8
+ border-radius: var(--radius-lg);
9
+ transition: all var(--transition-base);
10
+ }
11
+
12
+ .docyard-callout__icon {
13
+ flex-shrink: 0;
14
+ width: 22px;
15
+ height: 22px;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ }
20
+
21
+ .docyard-callout__icon .docyard-icon {
22
+ width: 100%;
23
+ height: 100%;
24
+ }
25
+
26
+ .docyard-callout__icon svg {
27
+ width: 100%;
28
+ height: 100%;
29
+ }
30
+
31
+ .docyard-callout__content {
32
+ flex: 1;
33
+ min-width: 0;
34
+ }
35
+
36
+ .docyard-callout__title {
37
+ font-weight: var(--font-weight-semibold);
38
+ margin: 0 0 var(--space-2) 0;
39
+ font-size: var(--font-size-base);
40
+ line-height: var(--line-height-tight);
41
+ }
42
+
43
+ .docyard-callout__body {
44
+ line-height: var(--line-height-relaxed);
45
+ }
46
+
47
+ .docyard-callout__body>*:first-child {
48
+ margin-top: 0;
49
+ }
50
+
51
+ .docyard-callout__body>*:last-child {
52
+ margin-bottom: 0;
53
+ }
54
+
55
+ /* Code blocks inside callouts */
56
+ .docyard-callout__body .highlight {
57
+ margin: var(--space-4) 0;
58
+ border-radius: var(--radius-md);
59
+ }
60
+
61
+ .docyard-callout__body pre.highlight {
62
+ margin: 0;
63
+ }
64
+
65
+ .docyard-callout__body pre {
66
+ margin: 0;
67
+ border-radius: var(--radius-md);
68
+ }
69
+
70
+ .docyard-callout__body pre code {
71
+ display: block;
72
+ padding: var(--space-4);
73
+ overflow-x: auto;
74
+ background-color: var(--color-bg-secondary) !important;
75
+ }
76
+
77
+ .docyard-callout__body :not(pre)>code {
78
+ background-color: rgba(0, 0, 0, 0.05);
79
+ border: 1px solid rgba(0, 0, 0, 0.1);
80
+ padding: 0.125rem 0.375rem;
81
+ border-radius: var(--radius-sm);
82
+ }
83
+
84
+ .dark .docyard-callout__body :not(pre)>code {
85
+ background-color: rgba(255, 255, 255, 0.1);
86
+ border: 1px solid rgba(255, 255, 255, 0.15);
87
+ }
88
+
89
+ /* Callout type: note (blue) */
90
+ .docyard-callout--note {
91
+ border-color: var(--callout-note-border);
92
+ background-color: var(--callout-note-bg);
93
+ }
94
+
95
+ .docyard-callout--note .docyard-callout__icon {
96
+ color: var(--callout-note);
97
+ }
98
+
99
+ .docyard-callout--note .docyard-callout__title {
100
+ color: var(--callout-note-text);
101
+ }
102
+
103
+ /* Callout type: tip (green) */
104
+ .docyard-callout--tip {
105
+ border-color: var(--callout-tip-border);
106
+ background-color: var(--callout-tip-bg);
107
+ }
108
+
109
+ .docyard-callout--tip .docyard-callout__icon {
110
+ color: var(--callout-tip);
111
+ }
112
+
113
+ .docyard-callout--tip .docyard-callout__title {
114
+ color: var(--callout-tip-text);
115
+ }
116
+
117
+ /* Callout type: important (purple) */
118
+ .docyard-callout--important {
119
+ border-color: var(--callout-important-border);
120
+ background-color: var(--callout-important-bg);
121
+ }
122
+
123
+ .docyard-callout--important .docyard-callout__icon {
124
+ color: var(--callout-important);
125
+ }
126
+
127
+ .docyard-callout--important .docyard-callout__title {
128
+ color: var(--callout-important-text);
129
+ }
130
+
131
+ /* Callout type: warning (orange/yellow) */
132
+ .docyard-callout--warning {
133
+ border-color: var(--callout-warning-border);
134
+ background-color: var(--callout-warning-bg);
135
+ }
136
+
137
+ .docyard-callout--warning .docyard-callout__icon {
138
+ color: var(--callout-warning);
139
+ }
140
+
141
+ .docyard-callout--warning .docyard-callout__title {
142
+ color: var(--callout-warning-text);
143
+ }
144
+
145
+ /* Callout type: danger (red) */
146
+ .docyard-callout--danger {
147
+ border-color: var(--callout-danger-border);
148
+ background-color: var(--callout-danger-bg);
149
+ }
150
+
151
+ .docyard-callout--danger .docyard-callout__icon {
152
+ color: var(--callout-danger);
153
+ }
154
+
155
+ .docyard-callout--danger .docyard-callout__title {
156
+ color: var(--callout-danger-text);
157
+ }
158
+
159
+ /* Nested callouts */
160
+ .docyard-callout .docyard-callout {
161
+ margin: var(--space-4) 0;
162
+ }
163
+
164
+ /* Reduced motion support */
165
+ @media (prefers-reduced-motion: reduce) {
166
+ .docyard-callout {
167
+ transition: none;
168
+ }
169
+ }
@@ -0,0 +1,196 @@
1
+ /* Code Block Component with Copy Button */
2
+
3
+ .docyard-code-block {
4
+ position: relative;
5
+ margin: var(--space-6) 0;
6
+ }
7
+
8
+ .docyard-code-block .highlight {
9
+ margin: 0;
10
+ }
11
+
12
+ /* Inside tabs - remove wrapper margin when code block is only child */
13
+ .docyard-tabs__panel:has(> [class*="language-"]:only-child) .docyard-code-block {
14
+ margin: 0;
15
+ }
16
+
17
+ /* Inside tabs with mixed content - use normal spacing */
18
+ .docyard-tabs__panel:not(:has(> [class*="language-"]:only-child)) .docyard-code-block {
19
+ margin: var(--space-4) 0;
20
+ }
21
+
22
+ /* Inside callouts - use callout's spacing */
23
+ .docyard-callout__body .docyard-code-block {
24
+ margin: var(--space-4) 0;
25
+ }
26
+
27
+ .docyard-callout__body .docyard-code-block:first-child {
28
+ margin-top: 0;
29
+ }
30
+
31
+ .docyard-callout__body .docyard-code-block:last-child {
32
+ margin-bottom: 0;
33
+ }
34
+
35
+ .docyard-code-block__copy {
36
+ position: absolute;
37
+ top: var(--space-3);
38
+ right: var(--space-3);
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ width: 32px;
43
+ height: 32px;
44
+ padding: 0;
45
+ border: 1px solid var(--color-border);
46
+ border-radius: var(--radius-md);
47
+ background-color: var(--color-bg);
48
+ color: var(--color-text-secondary);
49
+ cursor: pointer;
50
+ transition: all var(--transition-base);
51
+ opacity: 0;
52
+ visibility: hidden;
53
+ }
54
+
55
+ .docyard-code-block:hover .docyard-code-block__copy {
56
+ opacity: 1;
57
+ visibility: visible;
58
+ }
59
+
60
+ .docyard-code-block__copy:hover {
61
+ background-color: var(--color-bg-secondary);
62
+ border-color: var(--color-text-tertiary);
63
+ color: var(--color-text);
64
+ }
65
+
66
+ .docyard-code-block__copy:active {
67
+ transform: scale(0.95);
68
+ }
69
+
70
+ .docyard-code-block__copy:focus-visible {
71
+ opacity: 1;
72
+ visibility: visible;
73
+ outline: 2px solid var(--color-primary);
74
+ outline-offset: 2px;
75
+ }
76
+
77
+ /* Icon sizing */
78
+ .docyard-code-block__copy svg {
79
+ width: 16px;
80
+ height: 16px;
81
+ transition: all 0.2s ease;
82
+ }
83
+
84
+ /* Success state */
85
+ .docyard-code-block__copy.is-success {
86
+ opacity: 1;
87
+ visibility: visible;
88
+ background-color: var(--color-primary);
89
+ border-color: var(--color-primary);
90
+ color: white;
91
+ animation: success-pulse 0.4s ease;
92
+ }
93
+
94
+ .docyard-code-block__copy.is-success:hover {
95
+ background-color: var(--color-primary-hover);
96
+ border-color: var(--color-primary-hover);
97
+ color: white;
98
+ }
99
+
100
+ .docyard-code-block__copy.is-success svg {
101
+ animation: check-pop 0.3s ease;
102
+ }
103
+
104
+ /* Success animation */
105
+ @keyframes success-pulse {
106
+ 0% {
107
+ transform: scale(1);
108
+ }
109
+
110
+ 50% {
111
+ transform: scale(1.1);
112
+ }
113
+
114
+ 100% {
115
+ transform: scale(1);
116
+ }
117
+ }
118
+
119
+ @keyframes check-pop {
120
+ 0% {
121
+ transform: scale(0) rotate(-45deg);
122
+ opacity: 0;
123
+ }
124
+
125
+ 50% {
126
+ transform: scale(1.2) rotate(10deg);
127
+ }
128
+
129
+ 100% {
130
+ transform: scale(1) rotate(0deg);
131
+ opacity: 1;
132
+ }
133
+ }
134
+
135
+ /* Error state */
136
+ .docyard-code-block__copy.is-error {
137
+ opacity: 1;
138
+ visibility: visible;
139
+ background-color: #ef4444;
140
+ border-color: #ef4444;
141
+ color: white;
142
+ }
143
+
144
+ .docyard-code-block__copy.is-error:hover {
145
+ background-color: #dc2626;
146
+ border-color: #dc2626;
147
+ color: white;
148
+ }
149
+
150
+ /* Mobile: Always show copy button */
151
+ @media (max-width: 1024px) {
152
+ .docyard-code-block__copy {
153
+ opacity: 1;
154
+ visibility: visible;
155
+ }
156
+ }
157
+
158
+ /* Dark mode adjustments */
159
+ .dark .docyard-code-block__copy {
160
+ background-color: var(--color-bg-secondary);
161
+ border-color: var(--color-border);
162
+ }
163
+
164
+ .dark .docyard-code-block__copy:hover {
165
+ background-color: var(--color-bg-tertiary);
166
+ border-color: var(--color-text-tertiary);
167
+ }
168
+
169
+ /* Keep success state using primary colors in dark mode */
170
+ .dark .docyard-code-block__copy.is-success {
171
+ background-color: var(--color-primary);
172
+ border-color: var(--color-primary);
173
+ }
174
+
175
+ .dark .docyard-code-block__copy.is-success:hover {
176
+ background-color: var(--color-primary-hover);
177
+ border-color: var(--color-primary-hover);
178
+ }
179
+
180
+ .dark .docyard-code-block__copy.is-error {
181
+ background-color: #ef4444;
182
+ border-color: #ef4444;
183
+ }
184
+
185
+ /* Reduced motion support - WCAG accessibility requirement */
186
+ @media (prefers-reduced-motion: reduce) {
187
+ .docyard-code-block__copy,
188
+ .docyard-code-block__copy svg {
189
+ transition: none;
190
+ animation: none;
191
+ }
192
+
193
+ .docyard-code-block__copy:active {
194
+ transform: none;
195
+ }
196
+ }
@@ -0,0 +1,16 @@
1
+ /* Icon System */
2
+ .docyard-icon {
3
+ display: inline-flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ vertical-align: middle;
7
+ width: 1em;
8
+ height: 1em;
9
+ line-height: 1;
10
+ }
11
+
12
+ .docyard-icon svg {
13
+ width: 100%;
14
+ height: 100%;
15
+ display: block;
16
+ }
@@ -0,0 +1,44 @@
1
+ /* Site Logo */
2
+ .site-logo-container {
3
+ display: flex;
4
+ align-items: center;
5
+ flex-shrink: 0;
6
+ position: relative;
7
+ margin-right: 0.1rem;
8
+ }
9
+
10
+ .site-logo {
11
+ height: 2rem;
12
+ width: auto;
13
+ display: block;
14
+ object-fit: contain;
15
+ }
16
+
17
+ .site-logo-light {
18
+ display: block;
19
+ }
20
+
21
+ .site-logo-dark {
22
+ display: none;
23
+ }
24
+
25
+ .dark .site-logo-light {
26
+ display: none;
27
+ }
28
+
29
+ .dark .site-logo-dark {
30
+ display: block;
31
+ }
32
+
33
+
34
+ @media (max-width: 1024px) {
35
+ .site-logo {
36
+ height: 1.75rem;
37
+ }
38
+ }
39
+
40
+ @media (max-width: 640px) {
41
+ .site-logo {
42
+ height: 1.5rem;
43
+ }
44
+ }
@@ -1,50 +1,5 @@
1
1
  /* Navigation Components */
2
2
 
3
- /* Sidebar footer */
4
- .sidebar-footer {
5
- margin-top: auto;
6
- padding: var(--space-3) var(--space-5) var(--space-5);
7
- }
8
-
9
- .sidebar-footer-link {
10
- display: flex;
11
- align-items: center;
12
- gap: var(--space-2);
13
- text-decoration: none;
14
- padding: var(--space-2);
15
- border-radius: var(--radius-md);
16
- transition: all var(--transition-fast);
17
- }
18
-
19
- .sidebar-footer-link:hover {
20
- text-decoration: none;
21
- }
22
-
23
- .sidebar-footer-link:hover .external-icon {
24
- opacity: 0.5;
25
- }
26
-
27
- .sidebar-footer-text {
28
- flex: 1;
29
- }
30
-
31
- .sidebar-footer-title {
32
- margin: 0;
33
- font-size: 0.6875rem;
34
- color: var(--color-text-tertiary);
35
- font-weight: var(--font-weight-medium);
36
- line-height: 1.4;
37
- }
38
-
39
- .external-icon {
40
- width: 0.75rem;
41
- height: 0.75rem;
42
- color: var(--color-text-secondary);
43
- opacity: 0;
44
- transition: opacity var(--transition-fast);
45
- flex-shrink: 0;
46
- }
47
-
48
3
  /* Sidebar navigation */
49
4
  .sidebar nav {
50
5
  flex: 1;
@@ -239,8 +194,8 @@
239
194
  border: none;
240
195
  padding: var(--space-2);
241
196
  cursor: pointer;
242
- width: 36px;
243
- height: 36px;
197
+ width: 2.25rem;
198
+ height: 2.25rem;
244
199
  align-items: center;
245
200
  justify-content: center;
246
201
  border-radius: var(--radius-md);
@@ -256,3 +211,48 @@
256
211
  display: flex;
257
212
  }
258
213
  }
214
+
215
+ /* Sidebar footer */
216
+ .sidebar-footer {
217
+ margin-top: auto;
218
+ padding: var(--space-3) var(--space-5) var(--space-5);
219
+ }
220
+
221
+ .sidebar-footer-link {
222
+ display: flex;
223
+ align-items: center;
224
+ gap: var(--space-2);
225
+ text-decoration: none;
226
+ padding: var(--space-2);
227
+ border-radius: var(--radius-md);
228
+ transition: all var(--transition-fast);
229
+ }
230
+
231
+ .sidebar-footer-link:hover {
232
+ text-decoration: none;
233
+ }
234
+
235
+ .sidebar-footer-link:hover .external-icon {
236
+ opacity: 0.5;
237
+ }
238
+
239
+ .sidebar-footer-text {
240
+ flex: 1;
241
+ }
242
+
243
+ .sidebar-footer-title {
244
+ margin: 0;
245
+ font-size: 0.6875rem;
246
+ color: var(--color-text-tertiary);
247
+ font-weight: var(--font-weight-medium);
248
+ line-height: 1.4;
249
+ }
250
+
251
+ .external-icon {
252
+ width: 0.75rem;
253
+ height: 0.75rem;
254
+ color: var(--color-text-secondary);
255
+ opacity: 0;
256
+ transition: opacity var(--transition-fast);
257
+ flex-shrink: 0;
258
+ }