@conduction/docusaurus-preset 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.
- package/MISSING_COMPONENTS.md +109 -0
- package/README.md +171 -0
- package/package.json +59 -0
- package/src/components/AgentTrace/AgentTrace.jsx +128 -0
- package/src/components/AgentTrace/AgentTrace.module.css +115 -0
- package/src/components/AppMock/AppMock.jsx +86 -0
- package/src/components/AppMock/AppMock.module.css +629 -0
- package/src/components/AppMock/variants/DeciDeskMock.jsx +71 -0
- package/src/components/AppMock/variants/DocuDeskMock.jsx +69 -0
- package/src/components/AppMock/variants/LarpingAppMock.jsx +59 -0
- package/src/components/AppMock/variants/MyDashBiMock.jsx +135 -0
- package/src/components/AppMock/variants/MyDashMock.jsx +96 -0
- package/src/components/AppMock/variants/MyDashTilesMock.jsx +103 -0
- package/src/components/AppMock/variants/MyDashWidgetsMock.jsx +123 -0
- package/src/components/AppMock/variants/NLDesignMock.jsx +70 -0
- package/src/components/AppMock/variants/OpenCatalogiMock.jsx +61 -0
- package/src/components/AppMock/variants/OpenConnectorMock.jsx +83 -0
- package/src/components/AppMock/variants/OpenRegisterMock.jsx +100 -0
- package/src/components/AppMock/variants/OpenWooMock.jsx +61 -0
- package/src/components/AppMock/variants/PipelinQMock.jsx +88 -0
- package/src/components/AppMock/variants/ProcestMock.jsx +87 -0
- package/src/components/AppMock/variants/SoftwareCatalogMock.jsx +71 -0
- package/src/components/AppMock/variants/ZaakAfhandelAppMock.jsx +71 -0
- package/src/components/AppsGrid/AppsGrid.jsx +84 -0
- package/src/components/AppsGrid/AppsGrid.module.css +46 -0
- package/src/components/AppsPreview/AppsPreview.jsx +85 -0
- package/src/components/AppsPreview/AppsPreview.module.css +128 -0
- package/src/components/Clients/Clients.jsx +205 -0
- package/src/components/Clients/Clients.module.css +166 -0
- package/src/components/ComposeBlock/ComposeBlock.jsx +70 -0
- package/src/components/ComposeBlock/ComposeBlock.module.css +74 -0
- package/src/components/ConductionBg/ConductionBg.jsx +150 -0
- package/src/components/ConductionBg/ConductionBg.module.css +41 -0
- package/src/components/ContentCard/ContentCard.jsx +126 -0
- package/src/components/ContentCard/ContentCard.module.css +84 -0
- package/src/components/ContentDetailHero/ContentDetailHero.jsx +136 -0
- package/src/components/ContentDetailHero/ContentDetailHero.module.css +96 -0
- package/src/components/ContentTypeFilter/ContentTypeFilter.jsx +103 -0
- package/src/components/ContentTypeFilter/ContentTypeFilter.module.css +60 -0
- package/src/components/ContentTypeFilter/contentTypes.js +58 -0
- package/src/components/CookieCli/CookieCli.jsx +223 -0
- package/src/components/CookieCli/CookieCli.module.css +166 -0
- package/src/components/CtaBanner/CtaBanner.jsx +61 -0
- package/src/components/CtaBanner/CtaBanner.module.css +65 -0
- package/src/components/DetailHero/DetailHero.jsx +143 -0
- package/src/components/DetailHero/DetailHero.module.css +154 -0
- package/src/components/Diagrams/Diagrams.jsx +148 -0
- package/src/components/EmployeeCard/EmployeeCard.jsx +127 -0
- package/src/components/EmployeeCard/EmployeeCard.module.css +144 -0
- package/src/components/ExternalAppShelf/ExternalAppShelf.jsx +61 -0
- package/src/components/ExternalAppShelf/ExternalAppShelf.module.css +90 -0
- package/src/components/FAQ/FAQ.jsx +42 -0
- package/src/components/FAQ/FAQ.module.css +74 -0
- package/src/components/FacetedFilters/FacetedFilters.jsx +125 -0
- package/src/components/FacetedFilters/FacetedFilters.module.css +133 -0
- package/src/components/FeatureGrid/FeatureGrid.jsx +94 -0
- package/src/components/FeatureGrid/FeatureGrid.module.css +114 -0
- package/src/components/FeatureList/FeatureList.jsx +54 -0
- package/src/components/FeatureList/FeatureList.module.css +52 -0
- package/src/components/FeaturedCard/FeaturedCard.jsx +101 -0
- package/src/components/FeaturedCard/FeaturedCard.module.css +98 -0
- package/src/components/GameModal/GameModal.jsx +197 -0
- package/src/components/GameModal/GameModal.module.css +184 -0
- package/src/components/Hero/Hero.jsx +101 -0
- package/src/components/Hero/Hero.module.css +95 -0
- package/src/components/HexBackground/HexBackground.jsx +56 -0
- package/src/components/HexBackground/HexBackground.module.css +73 -0
- package/src/components/HexNetwork/HexNetwork.jsx +141 -0
- package/src/components/HexNetwork/HexNetwork.module.css +187 -0
- package/src/components/HexRain/HexRain.jsx +81 -0
- package/src/components/HowSteps/HowSteps.jsx +57 -0
- package/src/components/HowSteps/HowSteps.module.css +52 -0
- package/src/components/ManagedCommonGround/ManagedCommonGround.jsx +78 -0
- package/src/components/ManagedCommonGround/ManagedCommonGround.module.css +16 -0
- package/src/components/NewsletterCta/NewsletterCta.jsx +83 -0
- package/src/components/NewsletterCta/NewsletterCta.module.css +103 -0
- package/src/components/PairCard/PairCard.jsx +58 -0
- package/src/components/PairCard/PairCard.module.css +54 -0
- package/src/components/PartnerCard/PartnerCard.jsx +130 -0
- package/src/components/PartnerCard/PartnerCard.module.css +198 -0
- package/src/components/PartnerDirectory/PartnerDirectory.jsx +122 -0
- package/src/components/PartnerDirectory/PartnerDirectory.module.css +25 -0
- package/src/components/PartnerSidecard/PartnerSidecard.jsx +116 -0
- package/src/components/PartnerSidecard/PartnerSidecard.module.css +185 -0
- package/src/components/Pipeline/Pipeline.jsx +198 -0
- package/src/components/Pipeline/Pipeline.module.css +206 -0
- package/src/components/PlatformDiagram/PlatformDiagram.jsx +110 -0
- package/src/components/PlatformOverview/PlatformOverview.jsx +68 -0
- package/src/components/PlatformOverview/PlatformOverview.module.css +71 -0
- package/src/components/ReferenceCard/ReferenceCard.jsx +44 -0
- package/src/components/ReferenceCard/ReferenceCard.module.css +57 -0
- package/src/components/RelatedPosts/RelatedPosts.jsx +58 -0
- package/src/components/RelatedPosts/RelatedPosts.module.css +51 -0
- package/src/components/RotatingCards/RotatingCards.jsx +98 -0
- package/src/components/RotatingCards/RotatingCards.module.css +153 -0
- package/src/components/Showcase/Showcase.jsx +129 -0
- package/src/components/Showcase/Showcase.module.css +168 -0
- package/src/components/SolutionCard/SolutionCard.jsx +83 -0
- package/src/components/SolutionCard/SolutionCard.module.css +99 -0
- package/src/components/StatsStrip/StatsStrip.jsx +38 -0
- package/src/components/StatsStrip/StatsStrip.module.css +53 -0
- package/src/components/WidgetShelf/WidgetShelf.jsx +67 -0
- package/src/components/WidgetShelf/WidgetShelf.module.css +73 -0
- package/src/components/index.js +96 -0
- package/src/components/primitives/AuthorByline.jsx +85 -0
- package/src/components/primitives/AuthorByline.module.css +57 -0
- package/src/components/primitives/BrandCitation.jsx +71 -0
- package/src/components/primitives/Button.jsx +46 -0
- package/src/components/primitives/Button.module.css +88 -0
- package/src/components/primitives/Card.jsx +42 -0
- package/src/components/primitives/Card.module.css +42 -0
- package/src/components/primitives/Eyebrow.jsx +37 -0
- package/src/components/primitives/Eyebrow.module.css +19 -0
- package/src/components/primitives/HexBullet.jsx +37 -0
- package/src/components/primitives/HexBullet.module.css +16 -0
- package/src/components/primitives/HexThumbnail.jsx +70 -0
- package/src/components/primitives/HexThumbnail.module.css +45 -0
- package/src/components/primitives/Pill.jsx +42 -0
- package/src/components/primitives/Pill.module.css +30 -0
- package/src/components/primitives/Section.jsx +51 -0
- package/src/components/primitives/Section.module.css +31 -0
- package/src/components/primitives/SectionHead.jsx +36 -0
- package/src/components/primitives/SectionHead.module.css +43 -0
- package/src/components/primitives/index.js +22 -0
- package/src/css/brand.css +158 -0
- package/src/css/tokens.css +12 -0
- package/src/data/app-downloads.js +42 -0
- package/src/diagrams/README.md +74 -0
- package/src/diagrams/cn-domain-tree.js +105 -0
- package/src/diagrams/cn-hex-prism.js +163 -0
- package/src/diagrams/cn-hex.js +181 -0
- package/src/diagrams/cn-honeycomb-bg.js +135 -0
- package/src/diagrams/cn-pipeline.js +150 -0
- package/src/diagrams/cn-platform.js +156 -0
- package/src/diagrams/cn-side-box.js +104 -0
- package/src/diagrams/index.js +28 -0
- package/src/index.js +183 -0
- package/src/theme/Footer/index.jsx +516 -0
- package/src/theme/MDXPage/index.jsx +134 -0
- package/src/theme/Navbar/index.jsx +120 -0
- package/src/theme/Navbar/styles.module.css +114 -0
- package/src/theme/brand.jsx +63 -0
- package/src/theme.js +45 -0
- package/src/utils/lazyScript.js +37 -0
- package/static/img/favicon.svg +14 -0
- package/static/img/honeycomb-scatter.svg +23 -0
- package/static/img/honeycomb-watermark.svg +108 -0
- package/static/img/logo-dark.svg +11 -0
- package/static/img/logo.svg +14 -0
- package/static/img/nextcloud-logo.svg +5 -0
- package/static/lib/canal-footer.css +418 -0
- package/static/lib/canal-footer.js +499 -0
- package/static/lib/clients-flow.js +317 -0
- package/static/lib/conduction-bg.css +50 -0
- package/static/lib/conduction-bg.js +122 -0
- package/static/lib/hex-rain.css +128 -0
- package/static/lib/hex-rain.js +284 -0
- package/static/lib/kade-cyclist.css +264 -0
- package/static/lib/kade-cyclist.js +420 -0
- package/static/lib/logo-memory.css +219 -0
- package/static/lib/logo-memory.js +540 -0
- package/static/lib/platform-diagram.css +458 -0
- package/static/lib/platform-diagram.js +414 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <Clients /> styles.
|
|
3
|
+
*
|
|
4
|
+
* Grid variant: 5/4/3/2-column flat wall, grayscale at rest. Used for
|
|
5
|
+
* the Partners row and any small logo set.
|
|
6
|
+
*
|
|
7
|
+
* Marquee variant: three pointy-top hex rows, honeycomb stagger. The
|
|
8
|
+
* scroll itself is JS-driven (lib/clients-flow.js): each row is a
|
|
9
|
+
* static relative container, the runtime spawns absolutely-positioned
|
|
10
|
+
* hex anchors on the right, drifts them left via translateX, and culls
|
|
11
|
+
* once they leave the viewport. CSS-animated tracks were dropped after
|
|
12
|
+
* the duplicate-and-wrap approach started showing a seam at the loop
|
|
13
|
+
* boundary. Soft mask fade at left/right edges, prefers-reduced-motion
|
|
14
|
+
* freezes the runtime to a static snapshot, < 720px collapses to one
|
|
15
|
+
* lane.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
.section {
|
|
19
|
+
background: white;
|
|
20
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
21
|
+
padding: 96px 64px;
|
|
22
|
+
}
|
|
23
|
+
@media (max-width: 700px) {
|
|
24
|
+
.section { padding: 64px 16px; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.inner {
|
|
28
|
+
max-width: 1280px;
|
|
29
|
+
margin: 0 auto;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* ============ Grid variant ============ */
|
|
33
|
+
|
|
34
|
+
.grid {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: repeat(5, 1fr);
|
|
37
|
+
gap: 32px;
|
|
38
|
+
margin-top: var(--space-6);
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-items: center;
|
|
41
|
+
}
|
|
42
|
+
@media (max-width: 1100px) {
|
|
43
|
+
.grid { grid-template-columns: repeat(4, 1fr); gap: 24px; }
|
|
44
|
+
}
|
|
45
|
+
@media (max-width: 800px) {
|
|
46
|
+
.grid { grid-template-columns: repeat(3, 1fr); gap: 20px; }
|
|
47
|
+
}
|
|
48
|
+
@media (max-width: 500px) {
|
|
49
|
+
.grid { grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.cell {
|
|
53
|
+
width: 100%;
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
justify-content: center;
|
|
57
|
+
padding: 16px 8px;
|
|
58
|
+
min-height: 72px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.logo {
|
|
62
|
+
max-width: 100%;
|
|
63
|
+
max-height: 56px;
|
|
64
|
+
width: auto;
|
|
65
|
+
height: auto;
|
|
66
|
+
object-fit: contain;
|
|
67
|
+
filter: grayscale(1);
|
|
68
|
+
opacity: 0.6;
|
|
69
|
+
transition: filter 200ms ease, opacity 200ms ease;
|
|
70
|
+
}
|
|
71
|
+
.cell:hover .logo {
|
|
72
|
+
filter: grayscale(0);
|
|
73
|
+
opacity: 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ============ Marquee variant ============ */
|
|
77
|
+
|
|
78
|
+
.marquee {
|
|
79
|
+
--hex-w: 120px;
|
|
80
|
+
--hex-h: 138.56px; /* hex-w * 1.1547 (pointy-top ratio) */
|
|
81
|
+
--gap: 8px; /* breathing room between hexes */
|
|
82
|
+
/* Isotropic gap: vertical row pitch = (hex-w + gap) * √3/2 so the
|
|
83
|
+
visible gap perpendicular to the slanted hex edges matches the
|
|
84
|
+
horizontal gap between flat sides. Overlap = hex-h - that pitch. */
|
|
85
|
+
--hex-overlap: calc(var(--hex-h) - (var(--hex-w) + var(--gap)) * 0.866);
|
|
86
|
+
--offset: calc((var(--hex-w) + var(--gap)) / 2); /* row 2 lateral stagger */
|
|
87
|
+
position: relative;
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
margin-top: var(--space-8);
|
|
90
|
+
padding: var(--space-4) 0;
|
|
91
|
+
-webkit-mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent);
|
|
92
|
+
mask-image: linear-gradient(to right, transparent, black 8%, black 92%, transparent);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Each row is a static relative band the runtime spawns hexes into.
|
|
96
|
+
The honeycomb pack-up (negative margin-top) and row-2 stagger are
|
|
97
|
+
handled here in CSS; the runtime only drives x-axis drift. */
|
|
98
|
+
.row {
|
|
99
|
+
position: relative;
|
|
100
|
+
overflow: hidden;
|
|
101
|
+
height: var(--hex-h);
|
|
102
|
+
margin-top: calc(var(--hex-overlap) * -1);
|
|
103
|
+
}
|
|
104
|
+
.row:first-child { margin-top: 0; }
|
|
105
|
+
/* Row 2 stagger is applied by the runtime via translateX on the
|
|
106
|
+
spawned hexes (data-row-stagger or the default idx === 1 case),
|
|
107
|
+
so no CSS offset is needed here. Padding/margin would only
|
|
108
|
+
inflate the row's bounding rect, which the runtime measures for
|
|
109
|
+
spawn arithmetic — keeping the row plain ensures all three rows
|
|
110
|
+
have identical widths and stay in lockstep. */
|
|
111
|
+
|
|
112
|
+
.hex {
|
|
113
|
+
/* The flow runtime sets position:absolute + top:0 + left:0 + an
|
|
114
|
+
animated translateX in inline style. Absent the runtime (preview
|
|
115
|
+
fallback or reduced-motion before hydrate completes) the hex
|
|
116
|
+
would otherwise pile at the row's natural baseline; absolute
|
|
117
|
+
positioning here keeps it tucked at top:0,left:0 until the
|
|
118
|
+
runtime sets a transform. */
|
|
119
|
+
position: absolute;
|
|
120
|
+
top: 0;
|
|
121
|
+
left: 0;
|
|
122
|
+
width: var(--hex-w);
|
|
123
|
+
height: var(--hex-h);
|
|
124
|
+
background: white;
|
|
125
|
+
clip-path: var(--hex-pointy-top);
|
|
126
|
+
display: grid;
|
|
127
|
+
place-items: center;
|
|
128
|
+
text-decoration: none;
|
|
129
|
+
}
|
|
130
|
+
.hex::before {
|
|
131
|
+
content: "";
|
|
132
|
+
position: absolute; inset: 0;
|
|
133
|
+
background: var(--c-cobalt-50);
|
|
134
|
+
clip-path: var(--hex-pointy-top);
|
|
135
|
+
z-index: 0;
|
|
136
|
+
}
|
|
137
|
+
.hex::after {
|
|
138
|
+
content: "";
|
|
139
|
+
position: absolute; inset: 1.5px;
|
|
140
|
+
background: white;
|
|
141
|
+
clip-path: var(--hex-pointy-top);
|
|
142
|
+
z-index: 1;
|
|
143
|
+
}
|
|
144
|
+
.hexLogo {
|
|
145
|
+
position: relative;
|
|
146
|
+
z-index: 2;
|
|
147
|
+
max-width: 60%;
|
|
148
|
+
max-height: 50%;
|
|
149
|
+
object-fit: contain;
|
|
150
|
+
filter: grayscale(1) opacity(0.55);
|
|
151
|
+
transition: filter 200ms ease;
|
|
152
|
+
}
|
|
153
|
+
.hex:hover .hexLogo,
|
|
154
|
+
.hex:focus-visible .hexLogo {
|
|
155
|
+
filter: grayscale(0) opacity(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@media (max-width: 720px) {
|
|
159
|
+
.marquee {
|
|
160
|
+
--hex-w: 88px;
|
|
161
|
+
--hex-h: 101.6px;
|
|
162
|
+
--gap: 6px;
|
|
163
|
+
/* --hex-overlap recomputes from the calc in .marquee */
|
|
164
|
+
}
|
|
165
|
+
.row2, .row3 { display: none; }
|
|
166
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ComposeBlock />
|
|
3
|
+
*
|
|
4
|
+
* Code block styled like a terminal-adjacent compose-yaml panel.
|
|
5
|
+
* Mirrors the .compose treatment from preview/pages/demo.html: dark
|
|
6
|
+
* cobalt-900 background, IBM Plex Mono, an optional file-name pill
|
|
7
|
+
* at the top, and a copy-to-clipboard button on hover.
|
|
8
|
+
*
|
|
9
|
+
* Used wherever a page needs to drop in a chunk of YAML, JSON, bash,
|
|
10
|
+
* or any other plain text the reader might want to copy: /demo
|
|
11
|
+
* (docker-compose), /apps/<slug> (sample register schema), /docs
|
|
12
|
+
* configuration recipes.
|
|
13
|
+
*
|
|
14
|
+
* Usage in MDX:
|
|
15
|
+
*
|
|
16
|
+
* <ComposeBlock filename="docker-compose.yml" lang="yaml">
|
|
17
|
+
* {`services:
|
|
18
|
+
* nextcloud:
|
|
19
|
+
* image: nextcloud:latest
|
|
20
|
+
* ports: ["8080:80"]
|
|
21
|
+
* `}
|
|
22
|
+
* </ComposeBlock>
|
|
23
|
+
*
|
|
24
|
+
* <ComposeBlock>
|
|
25
|
+
* {`docker compose up -d
|
|
26
|
+
* docker compose logs -f nextcloud`}
|
|
27
|
+
* </ComposeBlock>
|
|
28
|
+
*
|
|
29
|
+
* Note: this component is for verbatim copy-paste blocks. For
|
|
30
|
+
* syntax-highlighted code, use Docusaurus's built-in fenced code
|
|
31
|
+
* blocks (```yaml ...) which Docusaurus colorises via Prism.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import React, {useState, useCallback, useRef} from 'react';
|
|
35
|
+
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
36
|
+
import styles from './ComposeBlock.module.css';
|
|
37
|
+
|
|
38
|
+
export default function ComposeBlock({filename, lang, children, className}) {
|
|
39
|
+
const isBrowser = useIsBrowser();
|
|
40
|
+
const [copied, setCopied] = useState(false);
|
|
41
|
+
const preRef = useRef(null);
|
|
42
|
+
|
|
43
|
+
const onCopy = useCallback(() => {
|
|
44
|
+
if (!isBrowser) return;
|
|
45
|
+
const text = preRef.current?.textContent || '';
|
|
46
|
+
if (!text || !navigator.clipboard) return;
|
|
47
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
48
|
+
setCopied(true);
|
|
49
|
+
setTimeout(() => setCopied(false), 1600);
|
|
50
|
+
}).catch(() => {/* clipboard API can fail in iframes; just no-op */});
|
|
51
|
+
}, [isBrowser]);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className={[styles.block, className].filter(Boolean).join(' ')} role="region" aria-label={filename || 'Code block'}>
|
|
55
|
+
{filename && <span className={styles.filename}>{filename}</span>}
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
className={styles.copy}
|
|
59
|
+
onClick={onCopy}
|
|
60
|
+
aria-label={copied ? 'Copied to clipboard' : 'Copy to clipboard'}
|
|
61
|
+
title={copied ? 'Copied' : 'Copy'}
|
|
62
|
+
>
|
|
63
|
+
{copied ? '✓ copied' : 'copy'}
|
|
64
|
+
</button>
|
|
65
|
+
<pre ref={preRef} className={styles.pre}>
|
|
66
|
+
<code className={lang ? `language-${lang}` : undefined}>{children}</code>
|
|
67
|
+
</pre>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ComposeBlock /> styles. Mirrors the .compose treatment from
|
|
3
|
+
* preview/pages/demo.html so the React component renders identically
|
|
4
|
+
* to the kit page version.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.block {
|
|
8
|
+
position: relative;
|
|
9
|
+
background: var(--c-cobalt-900);
|
|
10
|
+
color: rgba(255, 247, 210, 0.92);
|
|
11
|
+
padding: 28px 32px;
|
|
12
|
+
border-radius: var(--radius-lg);
|
|
13
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
14
|
+
font-size: 13px;
|
|
15
|
+
line-height: 1.55;
|
|
16
|
+
margin: 32px 0;
|
|
17
|
+
overflow-x: auto;
|
|
18
|
+
box-shadow: 0 12px 32px -8px rgba(10, 23, 47, 0.45);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.filename {
|
|
22
|
+
display: inline-block;
|
|
23
|
+
background: rgba(255, 247, 210, 0.08);
|
|
24
|
+
color: var(--c-orange-knvb);
|
|
25
|
+
padding: 4px 10px;
|
|
26
|
+
border-radius: 3px;
|
|
27
|
+
font-size: 11px;
|
|
28
|
+
letter-spacing: 0.06em;
|
|
29
|
+
text-transform: uppercase;
|
|
30
|
+
margin-bottom: 12px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.copy {
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: 14px;
|
|
36
|
+
right: 14px;
|
|
37
|
+
background: rgba(255, 247, 210, 0.06);
|
|
38
|
+
color: rgba(255, 247, 210, 0.7);
|
|
39
|
+
border: 1px solid rgba(255, 247, 210, 0.12);
|
|
40
|
+
padding: 4px 10px;
|
|
41
|
+
font-family: inherit;
|
|
42
|
+
font-size: 11px;
|
|
43
|
+
letter-spacing: 0.06em;
|
|
44
|
+
text-transform: uppercase;
|
|
45
|
+
border-radius: 3px;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
opacity: 0;
|
|
48
|
+
transition: opacity 140ms ease, background 140ms ease, color 140ms ease;
|
|
49
|
+
}
|
|
50
|
+
.block:hover .copy,
|
|
51
|
+
.copy:focus-visible { opacity: 1; }
|
|
52
|
+
.copy:hover {
|
|
53
|
+
background: rgba(255, 247, 210, 0.14);
|
|
54
|
+
color: rgba(255, 247, 210, 0.92);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.pre {
|
|
58
|
+
margin: 0;
|
|
59
|
+
padding: 0;
|
|
60
|
+
background: transparent;
|
|
61
|
+
color: inherit;
|
|
62
|
+
white-space: pre;
|
|
63
|
+
font-family: inherit;
|
|
64
|
+
font-size: inherit;
|
|
65
|
+
line-height: inherit;
|
|
66
|
+
}
|
|
67
|
+
.pre code {
|
|
68
|
+
background: transparent;
|
|
69
|
+
color: inherit;
|
|
70
|
+
padding: 0;
|
|
71
|
+
border: 0;
|
|
72
|
+
font-family: inherit;
|
|
73
|
+
white-space: pre;
|
|
74
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ConductionBg />
|
|
3
|
+
*
|
|
4
|
+
* Parallax honeycomb background. Drops depth-tiered hex spans inside a
|
|
5
|
+
* position:absolute layer that fills its closest position:relative
|
|
6
|
+
* ancestor. Hexes drift on scroll (smaller hexes = smaller depth = read
|
|
7
|
+
* as further away) and breathe in opacity.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors preview/conduction-bg.html in the kit. The runtime-script
|
|
10
|
+
* version still ships under sites/www/static/lib/ for plain-HTML
|
|
11
|
+
* surfaces (the preview pages); this React component is the canonical
|
|
12
|
+
* way to use the pattern from MDX or any Docusaurus swizzle.
|
|
13
|
+
*
|
|
14
|
+
* Per huisstijl: pointy-top only, never rotated.
|
|
15
|
+
*
|
|
16
|
+
* Usage in MDX:
|
|
17
|
+
*
|
|
18
|
+
* <Section background="inverse" spacing="default">
|
|
19
|
+
* <ConductionBg /> {/* white hexes for cobalt surfaces *\/}
|
|
20
|
+
* <h2 style={{position: 'relative', zIndex: 1}}>Pick an app.</h2>
|
|
21
|
+
* </Section>
|
|
22
|
+
*
|
|
23
|
+
* <ConductionBg theme="on-light" count={12} /> {/* cobalt hexes for white surfaces *\/}
|
|
24
|
+
*
|
|
25
|
+
* Note: the parent must be position:relative + overflow:hidden for the
|
|
26
|
+
* hexes to clip and the scroll-tied parallax to feel right.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import React, {useEffect, useRef} from 'react';
|
|
30
|
+
import useIsBrowser from '@docusaurus/useIsBrowser';
|
|
31
|
+
import styles from './ConductionBg.module.css';
|
|
32
|
+
|
|
33
|
+
/* Depth tiers picked to match the kit runtime. Smaller hexes get smaller
|
|
34
|
+
depth so they translate less per scroll-pixel — that's what reads
|
|
35
|
+
as "further away". Keep the weights in sync with conduction-bg.js. */
|
|
36
|
+
const TIERS = [
|
|
37
|
+
{d: 0.10, sMin: 32, sMax: 56, oMin: 0.03, oMax: 0.10, w: 5},
|
|
38
|
+
{d: 0.25, sMin: 56, sMax: 96, oMin: 0.04, oMax: 0.13, w: 4},
|
|
39
|
+
{d: 0.45, sMin: 96, sMax: 150, oMin: 0.05, oMax: 0.16, w: 3},
|
|
40
|
+
{d: 0.65, sMin: 150, sMax: 220, oMin: 0.07, oMax: 0.18, w: 2},
|
|
41
|
+
{d: 0.85, sMin: 220, sMax: 300, oMin: 0.08, oMax: 0.20, w: 1},
|
|
42
|
+
];
|
|
43
|
+
const PARALLAX_FACTOR = 0.4;
|
|
44
|
+
const TIER_TOTAL = TIERS.reduce((s, t) => s + t.w, 0);
|
|
45
|
+
|
|
46
|
+
const rand = (a, b) => Math.random() * (b - a) + a;
|
|
47
|
+
function pickTier() {
|
|
48
|
+
let r = Math.random() * TIER_TOTAL;
|
|
49
|
+
for (const t of TIERS) { if ((r -= t.w) <= 0) return t; }
|
|
50
|
+
return TIERS[0];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default function ConductionBg({theme = 'default', count = 18, className}) {
|
|
54
|
+
const isBrowser = useIsBrowser();
|
|
55
|
+
const ref = useRef(null);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!isBrowser || !ref.current) return;
|
|
59
|
+
const bg = ref.current;
|
|
60
|
+
const host = bg.parentElement;
|
|
61
|
+
if (!host) return;
|
|
62
|
+
|
|
63
|
+
/* Idempotency guard: re-mounting the same wrapper (e.g. SPA route
|
|
64
|
+
change that re-runs the effect) shouldn't double up the hexes. */
|
|
65
|
+
if (bg.dataset.cbgHydrated === '1') return;
|
|
66
|
+
bg.dataset.cbgHydrated = '1';
|
|
67
|
+
|
|
68
|
+
const onLight = theme === 'on-light';
|
|
69
|
+
/* Cobalt-on-white reads stronger per opacity-unit than white-on-cobalt;
|
|
70
|
+
scale back the on-light range so the field stays a soft veil. */
|
|
71
|
+
const opaScale = onLight ? 0.55 : 1.0;
|
|
72
|
+
const reduceMotion = typeof window.matchMedia === 'function'
|
|
73
|
+
&& window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
74
|
+
|
|
75
|
+
const accentIndex = Math.floor(count / 2);
|
|
76
|
+
const hexes = [];
|
|
77
|
+
for (let i = 0; i < count; i++) {
|
|
78
|
+
const tier = pickTier();
|
|
79
|
+
const size = rand(tier.sMin, tier.sMax);
|
|
80
|
+
const isAccent = (i === accentIndex);
|
|
81
|
+
const hex = document.createElement('span');
|
|
82
|
+
hex.className = isAccent ? styles.hex + ' ' + styles.accent : styles.hex;
|
|
83
|
+
hex.style.width = size + 'px';
|
|
84
|
+
hex.style.height = (size * 1.1547) + 'px';
|
|
85
|
+
hex.style.left = rand(-5, 105) + '%';
|
|
86
|
+
hex.style.top = rand(-25, 125) + '%';
|
|
87
|
+
hex.style.setProperty('--cbg-opa-min', (tier.oMin * opaScale).toFixed(3));
|
|
88
|
+
hex.style.setProperty(
|
|
89
|
+
'--cbg-opa-max',
|
|
90
|
+
((isAccent ? Math.min(tier.oMax + 0.05, 0.28) : tier.oMax) * opaScale).toFixed(3)
|
|
91
|
+
);
|
|
92
|
+
hex.style.setProperty('--cbg-dur', rand(11, 18).toFixed(1) + 's');
|
|
93
|
+
hex.style.setProperty('--cbg-delay', rand(-18, 0).toFixed(1) + 's');
|
|
94
|
+
hex.dataset.depth = tier.d;
|
|
95
|
+
bg.appendChild(hex);
|
|
96
|
+
hexes.push(hex);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (reduceMotion) return undefined;
|
|
100
|
+
|
|
101
|
+
/* Scroll-tied parallax. Only run while the host is in viewport
|
|
102
|
+
(intersection observer flips `visible`). */
|
|
103
|
+
let visible = false;
|
|
104
|
+
let ticking = false;
|
|
105
|
+
|
|
106
|
+
function update() {
|
|
107
|
+
const rect = host.getBoundingClientRect();
|
|
108
|
+
const offset = (rect.top + rect.height / 2) - window.innerHeight / 2;
|
|
109
|
+
for (const h of hexes) {
|
|
110
|
+
const depth = parseFloat(h.dataset.depth);
|
|
111
|
+
const ty = -offset * depth * PARALLAX_FACTOR;
|
|
112
|
+
h.style.transform = `translate(-50%, -50%) translateY(${ty.toFixed(1)}px)`;
|
|
113
|
+
}
|
|
114
|
+
ticking = false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function onScroll() {
|
|
118
|
+
if (!visible || ticking) return;
|
|
119
|
+
ticking = true;
|
|
120
|
+
requestAnimationFrame(update);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const io = new IntersectionObserver(([entry]) => {
|
|
124
|
+
visible = entry.isIntersecting;
|
|
125
|
+
if (visible) update();
|
|
126
|
+
}, {rootMargin: '300px 0px'});
|
|
127
|
+
io.observe(host);
|
|
128
|
+
|
|
129
|
+
window.addEventListener('scroll', onScroll, {passive: true});
|
|
130
|
+
window.addEventListener('resize', onScroll, {passive: true});
|
|
131
|
+
update();
|
|
132
|
+
|
|
133
|
+
return () => {
|
|
134
|
+
io.disconnect();
|
|
135
|
+
window.removeEventListener('scroll', onScroll);
|
|
136
|
+
window.removeEventListener('resize', onScroll);
|
|
137
|
+
/* Clean up the injected nodes so a remount starts fresh. */
|
|
138
|
+
for (const h of hexes) h.remove();
|
|
139
|
+
delete bg.dataset.cbgHydrated;
|
|
140
|
+
};
|
|
141
|
+
}, [isBrowser, theme, count]);
|
|
142
|
+
|
|
143
|
+
const composed = [
|
|
144
|
+
styles.bg,
|
|
145
|
+
theme === 'on-light' ? styles.onLight : null,
|
|
146
|
+
className,
|
|
147
|
+
].filter(Boolean).join(' ');
|
|
148
|
+
|
|
149
|
+
return <div ref={ref} className={composed} aria-hidden="true" />;
|
|
150
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ConductionBg /> styles. Mirrors preview/conduction-bg.css scoped
|
|
3
|
+
* under CSS modules so the React component carries its own styling.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.bg {
|
|
7
|
+
position: absolute;
|
|
8
|
+
inset: 0;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
--cbg-hex: #ffffff;
|
|
12
|
+
--cbg-accent: var(--c-orange-knvb);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.onLight {
|
|
16
|
+
--cbg-hex: var(--c-blue-cobalt);
|
|
17
|
+
--cbg-accent: var(--c-orange-knvb);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.hex {
|
|
21
|
+
position: absolute;
|
|
22
|
+
clip-path: var(--hex-pointy-top);
|
|
23
|
+
background: var(--cbg-hex);
|
|
24
|
+
will-change: transform, opacity;
|
|
25
|
+
transform: translate(-50%, -50%);
|
|
26
|
+
animation: conductionBgBreathe var(--cbg-dur, 14s) ease-in-out var(--cbg-delay, 0s) infinite;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.accent { background: var(--cbg-accent); }
|
|
30
|
+
|
|
31
|
+
@keyframes conductionBgBreathe {
|
|
32
|
+
0%, 100% { opacity: var(--cbg-opa-min, 0.04); }
|
|
33
|
+
50% { opacity: var(--cbg-opa-max, 0.18); }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@media (prefers-reduced-motion: reduce) {
|
|
37
|
+
.hex {
|
|
38
|
+
animation: none;
|
|
39
|
+
opacity: var(--cbg-opa-max, 0.12);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ContentCard /> + <ContentCardGrid />
|
|
3
|
+
*
|
|
4
|
+
* Card for an academy entry: blog post, guide, case study, webinar,
|
|
5
|
+
* tutorial. Two-column card with a hex thumbnail panel on the left
|
|
6
|
+
* and meta + title + tags on the right.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the "Keep learning…" cards in preview/components/academy.html
|
|
9
|
+
* and the academy.conduction.nl landing.
|
|
10
|
+
*
|
|
11
|
+
* Visual structure:
|
|
12
|
+
*
|
|
13
|
+
* ┌────────────────────────────────────────────┐
|
|
14
|
+
* │ ┌────────┐ │
|
|
15
|
+
* │ │ hex │ Author · Date │
|
|
16
|
+
* │ │ thumb │ Title goes here. │
|
|
17
|
+
* │ │ │ Optional summary line. │
|
|
18
|
+
* │ └────────┘ ⬢ Type ⬢ Tag ⬢ Tag │
|
|
19
|
+
* └────────────────────────────────────────────┘
|
|
20
|
+
*
|
|
21
|
+
* Card becomes an <a> when href is provided.
|
|
22
|
+
*
|
|
23
|
+
* Usage in MDX:
|
|
24
|
+
*
|
|
25
|
+
* <ContentCardGrid columns={2}>
|
|
26
|
+
* <ContentCard
|
|
27
|
+
* href="/posts/install-opencatalogi"
|
|
28
|
+
* contentType="guide"
|
|
29
|
+
* title="Install OpenCatalogi in two minutes."
|
|
30
|
+
* summary="From app store to first register, no terminal required."
|
|
31
|
+
* author={{ name: "Ruben van der Linde" }}
|
|
32
|
+
* date="2026-05-05"
|
|
33
|
+
* tags={["OpenCatalogi", "Install"]}
|
|
34
|
+
* thumbnail={{ icon: <svg>...</svg>, tone: 'cobalt-deep' }}
|
|
35
|
+
* />
|
|
36
|
+
* </ContentCardGrid>
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import React from 'react';
|
|
40
|
+
import HexThumbnail from '../primitives/HexThumbnail';
|
|
41
|
+
import AuthorByline from '../primitives/AuthorByline';
|
|
42
|
+
import Pill from '../primitives/Pill';
|
|
43
|
+
import {
|
|
44
|
+
CONTENT_TYPE_LABELS,
|
|
45
|
+
CONTENT_TYPE_BULLET_COLOR,
|
|
46
|
+
} from '../ContentTypeFilter/contentTypes';
|
|
47
|
+
import styles from './ContentCard.module.css';
|
|
48
|
+
|
|
49
|
+
export function ContentCardGrid({columns = 2, children, className}) {
|
|
50
|
+
const composed = [
|
|
51
|
+
styles.grid,
|
|
52
|
+
styles['cols-' + columns],
|
|
53
|
+
className,
|
|
54
|
+
].filter(Boolean).join(' ');
|
|
55
|
+
return <div className={composed}>{children}</div>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default function ContentCard({
|
|
59
|
+
href,
|
|
60
|
+
contentType,
|
|
61
|
+
contentTypeLabel,
|
|
62
|
+
title,
|
|
63
|
+
summary,
|
|
64
|
+
author,
|
|
65
|
+
date,
|
|
66
|
+
dateLabel,
|
|
67
|
+
locale,
|
|
68
|
+
tags = [],
|
|
69
|
+
thumbnail,
|
|
70
|
+
className,
|
|
71
|
+
...rest
|
|
72
|
+
}) {
|
|
73
|
+
const Tag = href ? 'a' : 'div';
|
|
74
|
+
const composed = [styles.card, className].filter(Boolean).join(' ');
|
|
75
|
+
|
|
76
|
+
const thumbProps = thumbnail || {};
|
|
77
|
+
const panelToneClass = thumbProps.panelTone
|
|
78
|
+
? styles['panel-' + thumbProps.panelTone]
|
|
79
|
+
: styles['panel-cobalt-deep'];
|
|
80
|
+
|
|
81
|
+
const typeLabel = contentTypeLabel
|
|
82
|
+
|| (contentType && CONTENT_TYPE_LABELS[contentType])
|
|
83
|
+
|| null;
|
|
84
|
+
const typeBullet = contentType
|
|
85
|
+
? CONTENT_TYPE_BULLET_COLOR[contentType]
|
|
86
|
+
: undefined;
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Tag href={href} className={composed} {...rest}>
|
|
90
|
+
<div className={[styles.thumbPanel, panelToneClass].join(' ')}>
|
|
91
|
+
{thumbProps.src
|
|
92
|
+
? <HexThumbnail size={thumbProps.size || 'md'} tone={thumbProps.tone || 'cobalt'} src={thumbProps.src} alt={thumbProps.alt} />
|
|
93
|
+
: <HexThumbnail size={thumbProps.size || 'md'} tone={thumbProps.tone || 'cobalt'}>{thumbProps.icon}</HexThumbnail>}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className={styles.body}>
|
|
97
|
+
{(author || date) && (
|
|
98
|
+
<AuthorByline
|
|
99
|
+
name={author && author.name}
|
|
100
|
+
avatarSrc={author && author.avatarSrc}
|
|
101
|
+
initials={author && author.initials}
|
|
102
|
+
date={date}
|
|
103
|
+
dateLabel={dateLabel}
|
|
104
|
+
locale={locale}
|
|
105
|
+
/>
|
|
106
|
+
)}
|
|
107
|
+
|
|
108
|
+
{title && <h3 className={styles.title}>{title}</h3>}
|
|
109
|
+
{summary && <p className={styles.summary}>{summary}</p>}
|
|
110
|
+
|
|
111
|
+
{(typeLabel || tags.length > 0) && (
|
|
112
|
+
<div className={styles.tags}>
|
|
113
|
+
{typeLabel && (
|
|
114
|
+
<Pill bullet bulletColor={typeBullet}>
|
|
115
|
+
{typeLabel}
|
|
116
|
+
</Pill>
|
|
117
|
+
)}
|
|
118
|
+
{tags.map((tag, i) => (
|
|
119
|
+
<Pill key={i} bullet bulletColor="var(--c-cobalt-300)">{tag}</Pill>
|
|
120
|
+
))}
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
</Tag>
|
|
125
|
+
);
|
|
126
|
+
}
|