@writedocs/generator 0.1.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 (95) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +17 -0
  3. package/astro.config.mjs +419 -0
  4. package/bin/writedocs.js +73 -0
  5. package/package.json +79 -0
  6. package/src/assets/wd_watermark.png +0 -0
  7. package/src/assets/wd_watermark_dark.png +0 -0
  8. package/src/cli/build-auth.js +53 -0
  9. package/src/cli/build.js +40 -0
  10. package/src/cli/dev.js +12 -0
  11. package/src/cli/generate-api-pages.js +359 -0
  12. package/src/cli/init.js +81 -0
  13. package/src/cli/preflight.js +40 -0
  14. package/src/cli/run-astro.js +57 -0
  15. package/src/cli/run-pagefind.js +66 -0
  16. package/src/cli/write-redirects-file.js +80 -0
  17. package/src/components/Accordion.astro +164 -0
  18. package/src/components/AccordionGroup.astro +40 -0
  19. package/src/components/ApiLangSelect.astro +168 -0
  20. package/src/components/ApiPlayground.astro +281 -0
  21. package/src/components/ApiReferencePanel.astro +1754 -0
  22. package/src/components/ApiSchemaField.astro +54 -0
  23. package/src/components/AppIcon.astro +32 -0
  24. package/src/components/Badge.astro +128 -0
  25. package/src/components/Callout.astro +168 -0
  26. package/src/components/Card.astro +136 -0
  27. package/src/components/CardGroup.astro +20 -0
  28. package/src/components/CodeGroup.astro +184 -0
  29. package/src/components/CopyPageMenu.astro +246 -0
  30. package/src/components/Danger.astro +12 -0
  31. package/src/components/Expandable.astro +126 -0
  32. package/src/components/Frame.astro +102 -0
  33. package/src/components/Hint.astro +99 -0
  34. package/src/components/Icon.astro +70 -0
  35. package/src/components/Image.astro +147 -0
  36. package/src/components/Info.astro +12 -0
  37. package/src/components/Note.astro +12 -0
  38. package/src/components/Parameter.astro +119 -0
  39. package/src/components/RequestExample.astro +33 -0
  40. package/src/components/ResponseExample.astro +19 -0
  41. package/src/components/Searchbar.astro +117 -0
  42. package/src/components/Step.astro +10 -0
  43. package/src/components/Steps.astro +32 -0
  44. package/src/components/Tab.astro +9 -0
  45. package/src/components/Tabs.astro +52 -0
  46. package/src/components/Tip.astro +12 -0
  47. package/src/components/Video.astro +135 -0
  48. package/src/components/Warning.astro +12 -0
  49. package/src/components/index.ts +48 -0
  50. package/src/content.config.ts +223 -0
  51. package/src/layout/BaseLayout.astro +750 -0
  52. package/src/layout/components/AnalyticsScripts.astro +77 -0
  53. package/src/layout/components/AskAiWidget.astro +37 -0
  54. package/src/layout/components/Breadcrumbs.astro +97 -0
  55. package/src/layout/components/ImageZoom.astro +19 -0
  56. package/src/layout/components/MobileMenu.astro +200 -0
  57. package/src/layout/components/NavTree.astro +351 -0
  58. package/src/layout/components/SearchModal.astro +42 -0
  59. package/src/layout/components/Sidebar.astro +122 -0
  60. package/src/layout/components/SiteFooter.astro +85 -0
  61. package/src/layout/components/TableOfContents.astro +117 -0
  62. package/src/layout/components/TopBar.astro +311 -0
  63. package/src/layout/styles/banner.css +44 -0
  64. package/src/layout/styles/base.css +234 -0
  65. package/src/layout/styles/dropdown.css +133 -0
  66. package/src/layout/styles/footer.css +108 -0
  67. package/src/layout/styles/image-zoom.css +50 -0
  68. package/src/layout/styles/mobile-menu.css +258 -0
  69. package/src/layout/styles/search-modal.css +122 -0
  70. package/src/layout/styles/topbar.css +437 -0
  71. package/src/lib/config.ts +2131 -0
  72. package/src/lib/mdx-auto-hydrate.js +70 -0
  73. package/src/lib/mdx-inject-builtins.js +87 -0
  74. package/src/lib/mdx-substitute-variables.js +66 -0
  75. package/src/lib/mdx-title-anchor-ids.js +84 -0
  76. package/src/lib/mermaid-rehype.js +72 -0
  77. package/src/lib/openapi-render.ts +479 -0
  78. package/src/lib/shiki-code-block.js +102 -0
  79. package/src/lib/shiki-copy-button.js +45 -0
  80. package/src/lib/styles-asset-integration.js +210 -0
  81. package/src/lib/writedocs-temp-dir.js +93 -0
  82. package/src/pages/404.astro +62 -0
  83. package/src/pages/[...slug].astro +1270 -0
  84. package/src/pages/[...slug].md.ts +78 -0
  85. package/src/pages/llms-full.txt.ts +71 -0
  86. package/src/pages/llms.txt.ts +141 -0
  87. package/src/scripts/banner.ts +20 -0
  88. package/src/scripts/dropdowns.ts +61 -0
  89. package/src/scripts/image-zoom.ts +66 -0
  90. package/src/scripts/mobile-menu.ts +55 -0
  91. package/src/scripts/search.ts +155 -0
  92. package/src/scripts/sidebar-scroll.ts +65 -0
  93. package/src/scripts/theme-toggle.ts +35 -0
  94. package/src/scripts/topbar-offset.ts +141 -0
  95. package/src/styles/global.css +18 -0
@@ -0,0 +1,234 @@
1
+ /* Base element styles + the .wd-shell three-column layout (sidebar / main
2
+ / toc) - imported once by BaseLayout.astro. Everything here consumes the
3
+ semantic --wd-* custom properties (--wd-primary, --wd-background, etc.)
4
+ set on :root by BaseLayout's own inline <style define:vars> block, not
5
+ the raw wdPrimaryLight/wdPrimaryDark values themselves - so, unlike that
6
+ block, none of this needs to live inside an .astro file's own scoped
7
+ <style> to work. A plain imported .css file is never scoped by Astro in
8
+ the first place, so every selector below is already effectively global -
9
+ no :global() wrapper needed (and, if left in by mistake, :global(...) is
10
+ not valid CSS outside Astro's own scoped-<style> processing, so a rule
11
+ written that way here would just silently never match anything). */
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ }
16
+ body {
17
+ margin: 0;
18
+ /* Sticky-footer layout: body is the flex column, .wd-page (everything
19
+ above the footer - banner/topbar/.wd-shell, see BaseLayout.astro) is
20
+ its flex: 1 0 auto item below, and <footer class="wd-footer"> is a
21
+ plain sibling after it - on a page short enough not to need
22
+ scrolling, .wd-page's flex-grow fills the leftover space down to the
23
+ viewport's bottom edge, pushing the footer flush against it (rather
24
+ than the footer trailing right after a half-empty page with its own
25
+ background peeking through beneath it); on a page tall enough to
26
+ already fill/exceed the viewport, .wd-page just settles at its own
27
+ natural (larger) height and the footer trails normally at the real
28
+ end of the document, exactly as before this rule existed. Doesn't
29
+ apply in `mode: "blank"` (no .wd-page wrapper, no footer at all in
30
+ that branch - see BaseLayout.astro) - a blank page already controls
31
+ 100% of its own layout.
32
+ */
33
+ display: flex;
34
+ flex-direction: column;
35
+ min-height: 100vh;
36
+ /* theme.background (writedocs.json) - --wd-page-bg-color/--wd-page-bg-image
37
+ default to the same value as --wd-background/'none' (see
38
+ BaseLayout.astro's lightPageBgColor/lightPageBgImage resolution), so
39
+ an unconfigured site renders identically to a plain background: var(
40
+ --wd-background) rule. background-attachment: fixed keeps a
41
+ configured image sized to the viewport rather than the full
42
+ (possibly very tall) document - the more expected look for a
43
+ decorative page background, not something that stretches/tiles
44
+ across a long scrolling doc. The topbar and footer each get their
45
+ own explicit opaque background (topbar.css, footer.css)
46
+ specifically so this doesn't show through them - the sidebar, main
47
+ content, and toc columns all have no background of their own, so a
48
+ configured page background/image shows through all three. */
49
+ background-color: var(--wd-page-bg-color);
50
+ background-image: var(--wd-page-bg-image);
51
+ background-size: cover;
52
+ background-position: center;
53
+ background-repeat: no-repeat;
54
+ background-attachment: fixed;
55
+ color: var(--wd-text);
56
+ /* --wd-font-family-body/--wd-font-family (BaseLayout.astro's own
57
+ define:vars <style> block, driven by theme.fonts - see
58
+ resolveFonts()/fontsSchema in lib/config.ts) are always both set to a
59
+ real, already-resolved font stack, never left for this rule's own
60
+ fallback to cover - an unconfigured site still resolves to Inter here
61
+ (genuinely loaded via a Google Fonts <link>, not just named as a
62
+ fallback the way this line used to work before theme.fonts existed).
63
+ The literal fallback stack below only matters in the narrow window
64
+ before that <style> block itself has been parsed. */
65
+ font-family: var(--wd-font-family-body, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
66
+ line-height: 1.6;
67
+ }
68
+ a {
69
+ color: var(--wd-primary);
70
+ }
71
+ /* theme.fonts.heading (or the site-wide theme.fonts.family/Inter default,
72
+ when no heading-specific override is configured - see
73
+ --wd-font-family-heading's own resolution in BaseLayout.astro) - applies
74
+ everywhere a heading renders (article prose, the auto-generated
75
+ .wd-article-header h1, etc.), not just inside .wd-article specifically,
76
+ since nothing site-wide should render headings in a different font by
77
+ accident just because it happens to sit outside that one container. */
78
+ h1,
79
+ h2,
80
+ h3,
81
+ h4,
82
+ h5,
83
+ h6 {
84
+ font-family: var(--wd-font-family-heading, inherit);
85
+ }
86
+ code {
87
+ background: var(--wd-surface);
88
+ border: 1px solid var(--wd-border);
89
+ padding: 0.15em 0.45em;
90
+ border-radius: 0.35em;
91
+ font-size: 1em;
92
+ font-weight: 500;
93
+ }
94
+ pre code {
95
+ background: none;
96
+ border: none;
97
+ padding: 0;
98
+ font-weight: normal;
99
+ }
100
+ pre {
101
+ padding: 1rem;
102
+ border-radius: 0.5rem;
103
+ overflow-x: auto;
104
+ }
105
+
106
+ /* flex: 1 0 auto (not the shorthand `flex: 1` main.wd-shell below also
107
+ uses) so this never shrinks below its own natural content height even
108
+ inside a flex *column* ancestor shorter than that - see body's own
109
+ comment above for the sticky-footer layout this is the growing half
110
+ of. */
111
+ .wd-page {
112
+ display: flex;
113
+ flex-direction: column;
114
+ flex: 1 0 auto;
115
+ }
116
+ .wd-shell {
117
+ display: flex;
118
+ max-width: 1440px;
119
+ margin: 0 auto;
120
+ width: 100%;
121
+ /* Grows to fill .wd-page's remaining height (after the banner/topbar
122
+ above it take their own natural height) on a short page - see body's
123
+ comment above. .wd-sidebar-col/.wd-toc-col below then stretch to
124
+ match *this* element's own height for free, via flexbox's own
125
+ default `align-items: stretch` on a row - no min-height of their own
126
+ needed (this replaces one on .wd-sidebar-col that used to hardcode
127
+ 100vh instead, which overshot on a page with a banner/tall topbar
128
+ above it, or undershot the real page height on a page taller than
129
+ one viewport). */
130
+ flex: 1;
131
+ }
132
+ .wd-sidebar-col {
133
+ width: 260px;
134
+ flex-shrink: 0;
135
+ border-right: 1px solid var(--wd-border);
136
+ }
137
+ .wd-main-col {
138
+ flex: 1;
139
+ min-width: 0;
140
+ }
141
+ .wd-toc-col {
142
+ width: 230px;
143
+ flex-shrink: 0;
144
+ border-left: 1px solid var(--wd-border);
145
+ }
146
+ @media (max-width: 1150px) {
147
+ .wd-toc-col {
148
+ display: none;
149
+ }
150
+ }
151
+ @media (max-width: 860px) {
152
+ .wd-shell {
153
+ flex-direction: column;
154
+ }
155
+ /* The sidebar itself no longer stacks full-width above the page content
156
+ at this width - it moved into MobileMenu.astro's own panel instead
157
+ (see its own <slot name="sidebar" /> copy), which renders its own
158
+ second copy of the same sidebar content. This copy (inside
159
+ .wd-sidebar-col) just disappears rather than being repositioned. */
160
+ .wd-sidebar-col {
161
+ display: none;
162
+ }
163
+ /* Sidebar.astro's .wd-sidebar is sticky-with-its-own-scrollbar on wide
164
+ screens (see its own style block for why) - not appropriate for the
165
+ copy now living inside MobileMenu.astro's own scroll area, which
166
+ already has its own scroll area; this makes it flow as a plain block
167
+ there instead of trying to run a second, nested one. This selector
168
+ reaches into Sidebar.astro's own scoped class from a plain external
169
+ stylesheet - works with no special syntax needed (unlike the
170
+ :global() wrapper this rule needed back when it lived in BaseLayout's
171
+ own scoped <style> block), since a plain .css file was never scoped
172
+ to begin with. */
173
+ .wd-sidebar {
174
+ position: static;
175
+ max-height: none;
176
+ overflow-y: visible;
177
+ }
178
+ }
179
+
180
+ /* Dual light/dark logo: both <img> tags are always in the markup (see
181
+ TopBar.astro/MobileMenu.astro/SiteFooter.astro, all three of which
182
+ render this same pair), CSS shows only the one matching the active
183
+ [data-theme] - set by the inline anti-FOUC script in BaseLayout's
184
+ <head> + the theme-toggle click handler (src/scripts/theme-toggle.ts).
185
+ A site that only gave one logo path gets the same <img> rendered twice
186
+ with both classes worth of visibility rules, i.e. it's just always
187
+ shown. Kept here (not co-located with any one of the three components
188
+ above) since all three need it identically. */
189
+ .wd-logo-dark {
190
+ display: none;
191
+ }
192
+ [data-theme="dark"] .wd-logo-light {
193
+ display: none;
194
+ }
195
+ [data-theme="dark"] .wd-logo-dark {
196
+ display: block;
197
+ }
198
+
199
+ /* Same dual-image/CSS-toggle mechanism as the logo pair above, for the
200
+ "Powered by writedocs" sidebar watermark (Sidebar.astro) - kept as
201
+ plain global rules here rather than a `:global()`-wrapped selector
202
+ inside that component's own scoped <style> block, matching this
203
+ proven pattern exactly instead of a second, more exotic variant of
204
+ it. */
205
+ .wd-watermark-dark {
206
+ display: none;
207
+ }
208
+ [data-theme="dark"] .wd-watermark-light {
209
+ display: none;
210
+ }
211
+ [data-theme="dark"] .wd-watermark-dark {
212
+ display: block;
213
+ }
214
+
215
+ /* Astro's Shiki output (both the built-in MDX code-fence pipeline,
216
+ configured with dual themes in astro.config.mjs, and the <Code />
217
+ component used for the API playground's request/response snippets)
218
+ bakes the "light" theme as each element's base color/background and
219
+ stashes the dark equivalents in --shiki-dark/--shiki-dark-bg custom
220
+ properties. Swapping to those on [data-theme='dark'] is the officially
221
+ documented way to make Shiki-highlighted code follow a manual
222
+ (non-media-query) theme toggle - see
223
+ https://docs.astro.build/en/guides/syntax-highlighting/#setting-light-and-dark-mode-themes.
224
+ Site-wide/article-content concern, unrelated to any one layout
225
+ component, which is why it lives here rather than in any of the
226
+ feature-specific stylesheets. */
227
+ [data-theme="dark"] .astro-code,
228
+ [data-theme="dark"] .astro-code span {
229
+ color: var(--shiki-dark) !important;
230
+ background-color: var(--shiki-dark-bg) !important;
231
+ font-style: var(--shiki-dark-font-style) !important;
232
+ font-weight: var(--shiki-dark-font-weight) !important;
233
+ text-decoration: var(--shiki-dark-text-decoration) !important;
234
+ }
@@ -0,0 +1,133 @@
1
+ /* The .wd-dropdown/.wd-dropdown-trigger/.wd-dropdown-menu/
2
+ .wd-dropdown-menu-panel pattern below (plus initDropdowns() in
3
+ src/scripts/dropdowns.ts) is written to be reused by any component that
4
+ wants a click-to-toggle dropdown, not just the topbar selectors/tab
5
+ dropdowns it was first built for - CopyPageMenu.astro is another
6
+ consumer. Imported by TopBar.astro (which owns most of this markup:
7
+ switchers, tabs, the small-screen topbar.links ellipsis) and also by
8
+ MobileMenu.astro, for the .wd-tab-icon/.wd-selector-tag primitives its
9
+ own accordion rows reuse (MobileMenu doesn't render any .wd-dropdown
10
+ markup itself today, but importing this costs nothing extra - Vite
11
+ dedupes a CSS file imported from more than one component). */
12
+ .wd-dropdown {
13
+ position: relative;
14
+ }
15
+ .wd-dropdown-trigger {
16
+ display: flex;
17
+ align-items: center;
18
+ gap: 0.35rem;
19
+ padding: 0.4rem 0.75rem;
20
+ border-radius: 0.4rem;
21
+ border: none;
22
+ background: none;
23
+ cursor: pointer;
24
+ color: var(--wd-text-muted);
25
+ font-size: 0.9rem;
26
+ font-weight: 500;
27
+ font-family: inherit;
28
+ }
29
+ .wd-dropdown-trigger:hover {
30
+ color: var(--wd-text);
31
+ }
32
+ .wd-dropdown-caret {
33
+ transition: transform 0.12s ease;
34
+ }
35
+ .wd-topbar-tabs .wd-dropdown-caret {
36
+ margin-top: 4px;
37
+ }
38
+ .wd-dropdown.open .wd-dropdown-caret {
39
+ transform: rotate(180deg);
40
+ }
41
+ /* .wd-dropdown-menu itself sits flush against the trigger (top: 100%, no
42
+ gap) and is what actually receives :hover/.open - the visible white box
43
+ (background/border/shadow/padding) lives on the .wd-dropdown-menu-panel
44
+ child instead, offset down via padding-top on the parent. This is
45
+ deliberate: an earlier version put that 0.4rem breathing room directly
46
+ on .wd-dropdown-menu's own `top` offset, which left a dead strip between
47
+ the trigger and the menu that belonged to neither - moving the mouse
48
+ from one to the other crossed unhovered space and closed the menu
49
+ before you could reach it. Padding (unlike a `top`/`margin` offset) is
50
+ still part of the element's own hoverable box, so this keeps the same
51
+ visual gap without the dead zone. */
52
+ .wd-dropdown-menu {
53
+ display: none;
54
+ position: absolute;
55
+ top: 100%;
56
+ left: 0;
57
+ padding-top: 0.4rem;
58
+ z-index: 20;
59
+ }
60
+ .wd-dropdown-menu-panel {
61
+ min-width: 180px;
62
+ background: var(--wd-background);
63
+ border: 1px solid var(--wd-border);
64
+ border-radius: 0.5rem;
65
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
66
+ padding: 0.4rem;
67
+ }
68
+ .wd-dropdown.open .wd-dropdown-menu {
69
+ display: block;
70
+ }
71
+ /* Hovering a trigger reveals its menu too, not just clicking it - a plain
72
+ CSS :hover rule alongside the JS-driven .open class (initDropdowns()),
73
+ which still owns click-to-toggle/outside-click-to-close/Escape-to-close.
74
+ Both rules just set the same display:block, so they layer cleanly:
75
+ moving the mouse away closes a hover-only reveal, while a click-opened
76
+ menu (the .open class) stays open regardless of where the mouse is
77
+ until it's explicitly closed. */
78
+ .wd-dropdown:hover .wd-dropdown-caret {
79
+ transform: rotate(180deg);
80
+ }
81
+ .wd-dropdown:hover .wd-dropdown-menu {
82
+ display: block;
83
+ }
84
+ .wd-dropdown-menu a {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: 0.5rem;
88
+ padding: 0.45rem 0.6rem;
89
+ border-radius: 0.35rem;
90
+ text-decoration: none;
91
+ color: var(--wd-text);
92
+ font-size: 0.88rem;
93
+ }
94
+ .wd-dropdown-menu a:hover {
95
+ background: var(--wd-surface);
96
+ }
97
+ .wd-dropdown-menu a.active {
98
+ color: var(--wd-primary);
99
+ background: color-mix(in srgb, var(--wd-primary) 10%, transparent);
100
+ }
101
+
102
+ /* .wd-tab-icon is shared by AppIcon's two render modes: a real <svg>
103
+ (astro-icon, sized to the surrounding text) or a <span> (literal
104
+ emoji/text, sized by inherited font-size alone). Used by every dropdown
105
+ option/trigger above, TopBar.astro's tabs row, MobileMenu.astro's own
106
+ accordion rows, topbar.links (both the desktop row and the collapsed
107
+ mobile dropdown - TopBar.astro), and footer column links
108
+ (SiteFooter.astro) - a generic "icon sized to sit next to text" primitive
109
+ despite the tab-specific name, not worth a same-rules second class. */
110
+ svg.wd-tab-icon {
111
+ width: 1em;
112
+ height: 1em;
113
+ flex-shrink: 0;
114
+ }
115
+ span.wd-tab-icon {
116
+ display: inline-flex;
117
+ align-items: center;
118
+ flex-shrink: 0;
119
+ }
120
+ .wd-selector-tag {
121
+ font-size: 0.65rem;
122
+ font-weight: 600;
123
+ text-transform: uppercase;
124
+ letter-spacing: 0.03em;
125
+ padding: 0.1rem 0.4rem;
126
+ border-radius: 999px;
127
+ background: var(--wd-surface);
128
+ color: var(--wd-text-muted);
129
+ }
130
+ .wd-dropdown-menu a.active .wd-selector-tag {
131
+ background: color-mix(in srgb, var(--wd-primary) 15%, transparent);
132
+ color: var(--wd-primary);
133
+ }
@@ -0,0 +1,108 @@
1
+ /* writedocs.json's `footer.columns`/`socials` - see SiteFooter.astro (only
2
+ rendered at all when one of those has content) and footerSchema/
3
+ `socials` in lib/config.ts. Sits below .wd-shell, same max-width/
4
+ centering convention as .wd-topbar-inner/.wd-shell so its content lines
5
+ up with the rest of the page. */
6
+ .wd-footer {
7
+ border-top: 1px solid var(--wd-border);
8
+ /* Explicit opaque background - previously unset (transparent, just
9
+ showing <body>'s own flat color through it, which looked identical
10
+ to having one). Now load-bearing: theme.background's own color/image
11
+ paints on <body> itself, and this is what keeps that from showing
12
+ through the footer. */
13
+ background: var(--wd-background);
14
+ /* body (base.css) is now the sticky-footer flex column this sits in as
15
+ a plain sibling after .wd-page - flex-shrink: 0 keeps this pinned at
16
+ its own natural height rather than the flex column trying to
17
+ compress it on a page taller than one viewport (harmless in
18
+ practice, since body has no fixed height of its own to overflow
19
+ against, but explicit here to match how every other fixed-size flex
20
+ item in this codebase - .wd-footer-brand, .wd-footer-socials below -
21
+ already spells this out rather than relying on it never coming up). */
22
+ flex-shrink: 0;
23
+ }
24
+ .wd-footer-inner {
25
+ max-width: 1440px;
26
+ margin: 0 auto;
27
+ padding: 2.5rem 1.5rem;
28
+ display: flex;
29
+ flex-wrap: wrap;
30
+ align-items: flex-start;
31
+ justify-content: space-between;
32
+ gap: 2rem;
33
+ }
34
+ /* Only rendered when theme.logo is set (hasLogoImage) - reuses the exact
35
+ same wd-logo-light/wd-logo-dark <img> pair + CSS toggle (base.css) the
36
+ topbar brand link and mobile menu header also use, just in a third spot.
37
+ flex-shrink: 0 so it keeps its own size rather than getting squeezed as
38
+ .wd-footer-columns (flex: 1 below) claims the row's free space. */
39
+ .wd-footer-brand {
40
+ display: flex;
41
+ align-items: center;
42
+ flex-shrink: 0;
43
+ }
44
+ .wd-footer-columns {
45
+ display: flex;
46
+ flex-wrap: wrap;
47
+ gap: 2.5rem;
48
+ flex: 1;
49
+ }
50
+ .wd-footer-column-title {
51
+ font-size: 0.78rem;
52
+ font-weight: 600;
53
+ text-transform: uppercase;
54
+ letter-spacing: 0.03em;
55
+ color: var(--wd-text-muted);
56
+ margin-bottom: 0.75rem;
57
+ }
58
+ .wd-footer-column-links {
59
+ list-style: none;
60
+ margin: 0;
61
+ padding: 0;
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: 0.55rem;
65
+ }
66
+ .wd-footer-column-links a {
67
+ display: inline-flex;
68
+ align-items: center;
69
+ gap: 0.4rem;
70
+ color: var(--wd-text-muted);
71
+ text-decoration: none;
72
+ font-size: 0.88rem;
73
+ }
74
+ .wd-footer-column-links a:hover {
75
+ color: var(--wd-text);
76
+ }
77
+ /* flex-shrink: 0 so a wide set of columns wraps/shrinks before this row
78
+ does - social icons staying a fixed size reads better than them
79
+ shrinking or wrapping mid-row. */
80
+ .wd-footer-socials {
81
+ display: flex;
82
+ align-items: flex-start;
83
+ gap: 0.6rem;
84
+ flex-shrink: 0;
85
+ }
86
+ .wd-footer-social-link {
87
+ display: inline-flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ width: 2rem;
91
+ height: 2rem;
92
+ border-radius: 0.4rem;
93
+ color: var(--wd-text-muted);
94
+ }
95
+ .wd-footer-social-link:hover {
96
+ color: var(--wd-text);
97
+ background: var(--wd-surface);
98
+ }
99
+ /* AppIcon.astro renders either a real <svg> (astro-icon, for an Iconify
100
+ id) or a <span> (literal text/emoji). */
101
+ svg.wd-footer-social-icon {
102
+ width: 1.1rem;
103
+ height: 1.1rem;
104
+ }
105
+ span.wd-footer-social-icon {
106
+ font-size: 1.1rem;
107
+ line-height: 1;
108
+ }
@@ -0,0 +1,50 @@
1
+ /* ImageZoom.astro's own styling - the fullscreen lightbox overlay, plus
2
+ the cursor affordance on the content images that open it (see
3
+ src/scripts/image-zoom.ts, which is what actually adds the
4
+ .wd-zoomable class - only images that end up zoomable get the cursor,
5
+ never Card's own image or anything marked nozoom). */
6
+
7
+ .wd-zoomable {
8
+ cursor: zoom-in;
9
+ }
10
+ .wd-zoom-overlay {
11
+ position: fixed;
12
+ inset: 0;
13
+ z-index: 200;
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ padding: 2.5rem;
18
+ background: rgba(15, 23, 42, 0.85);
19
+ cursor: zoom-out;
20
+ }
21
+ .wd-zoom-overlay[hidden] {
22
+ display: none;
23
+ }
24
+ .wd-zoom-image {
25
+ max-width: 100%;
26
+ max-height: 100%;
27
+ border-radius: 0.5rem;
28
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
29
+ cursor: zoom-out;
30
+ }
31
+ .wd-zoom-close {
32
+ position: fixed;
33
+ top: 1.25rem;
34
+ right: 1.25rem;
35
+ width: 2.25rem;
36
+ height: 2.25rem;
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ border: none;
41
+ border-radius: 50%;
42
+ background: rgba(255, 255, 255, 0.12);
43
+ color: #fff;
44
+ font-size: 1.5rem;
45
+ line-height: 1;
46
+ cursor: pointer;
47
+ }
48
+ .wd-zoom-close:hover {
49
+ background: rgba(255, 255, 255, 0.22);
50
+ }