@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,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ContentCard /> styles. Mirrors .content-card and .content-grid in
|
|
3
|
+
* preview/components/academy.css.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.grid { display: grid; gap: var(--space-6); }
|
|
7
|
+
.cols-1 { grid-template-columns: 1fr; }
|
|
8
|
+
.cols-2 { grid-template-columns: repeat(2, 1fr); }
|
|
9
|
+
.cols-3 { grid-template-columns: repeat(3, 1fr); }
|
|
10
|
+
@media (max-width: 900px) {
|
|
11
|
+
.cols-2, .cols-3 { grid-template-columns: 1fr; }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.card {
|
|
15
|
+
display: grid;
|
|
16
|
+
grid-template-columns: 200px 1fr;
|
|
17
|
+
gap: var(--space-6);
|
|
18
|
+
padding: var(--space-5);
|
|
19
|
+
background: var(--c-cobalt-50);
|
|
20
|
+
border-radius: var(--radius-lg);
|
|
21
|
+
text-decoration: none;
|
|
22
|
+
color: inherit;
|
|
23
|
+
transition: transform 160ms ease, box-shadow 160ms ease, background 160ms ease;
|
|
24
|
+
align-items: center;
|
|
25
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
26
|
+
}
|
|
27
|
+
.card:hover {
|
|
28
|
+
background: white;
|
|
29
|
+
box-shadow: var(--shadow-2);
|
|
30
|
+
transform: translateY(-2px);
|
|
31
|
+
text-decoration: none;
|
|
32
|
+
color: inherit;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media (max-width: 700px) {
|
|
36
|
+
.card { grid-template-columns: 1fr; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.thumbPanel {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
width: 100%;
|
|
44
|
+
height: 184px;
|
|
45
|
+
border-radius: var(--radius-md);
|
|
46
|
+
position: relative;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
}
|
|
49
|
+
.panel-cobalt { background: var(--c-blue-cobalt); }
|
|
50
|
+
.panel-cobalt-dark { background: var(--c-cobalt-700); }
|
|
51
|
+
.panel-cobalt-deep { background: var(--c-cobalt-900); }
|
|
52
|
+
.panel-mint { background: var(--c-mint-500); }
|
|
53
|
+
.panel-orange { background: var(--c-orange-knvb); }
|
|
54
|
+
.panel-cobalt-50 { background: var(--c-cobalt-50); }
|
|
55
|
+
|
|
56
|
+
.body {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
gap: var(--space-3);
|
|
60
|
+
min-width: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.title {
|
|
64
|
+
font-size: 18px;
|
|
65
|
+
font-weight: 700;
|
|
66
|
+
letter-spacing: -0.01em;
|
|
67
|
+
color: var(--c-cobalt-900);
|
|
68
|
+
margin: 0;
|
|
69
|
+
line-height: 1.3;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.summary {
|
|
73
|
+
font-size: 14px;
|
|
74
|
+
color: var(--c-cobalt-700);
|
|
75
|
+
margin: 0;
|
|
76
|
+
line-height: 1.55;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.tags {
|
|
80
|
+
display: flex;
|
|
81
|
+
gap: 6px;
|
|
82
|
+
flex-wrap: wrap;
|
|
83
|
+
margin-top: 4px;
|
|
84
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ContentDetailHero />
|
|
3
|
+
*
|
|
4
|
+
* Header for individual academy posts. Crumb, content-type chip plus
|
|
5
|
+
* topical tags, h1 title, optional summary, author byline, optional
|
|
6
|
+
* duration label, then a 16:9 cover region with the honeycomb
|
|
7
|
+
* watermark and either a hex-thumb icon or a hero image inside.
|
|
8
|
+
*
|
|
9
|
+
* Distinct from <DetailHero /> (which is for app/solution/partner
|
|
10
|
+
* pages and has the 360px cobalt hex on the right). Academy detail
|
|
11
|
+
* pages want a wider headline and a full-width cover image; this
|
|
12
|
+
* component is built for that.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors the .content-detail-hero section in
|
|
15
|
+
* preview/components/academy.html.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
*
|
|
19
|
+
* <ContentDetailHero
|
|
20
|
+
* crumb={[{label: 'Academy', href: '/'}, {label: 'Guides', href: '/?type=guide'}, 'Install OpenCatalogi']}
|
|
21
|
+
* contentType="guide"
|
|
22
|
+
* tags={['OpenCatalogi', 'Install']}
|
|
23
|
+
* title="Install OpenCatalogi in two minutes."
|
|
24
|
+
* summary="From app store to first register, no terminal required."
|
|
25
|
+
* author={{ name: 'Ruben van der Linde' }}
|
|
26
|
+
* date="2026-05-05"
|
|
27
|
+
* duration="12 min read"
|
|
28
|
+
* cover={{ src: '/img/posts/install-opencatalogi.jpg', alt: 'Install screen' }}
|
|
29
|
+
* />
|
|
30
|
+
*
|
|
31
|
+
* If `cover` is omitted, the cover region renders a single large
|
|
32
|
+
* pointy-top hex on the cobalt-900 ground. Pass `cover.icon` for a
|
|
33
|
+
* custom SVG instead of the default placeholder.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import React from 'react';
|
|
37
|
+
import HexThumbnail from '../primitives/HexThumbnail';
|
|
38
|
+
import AuthorByline from '../primitives/AuthorByline';
|
|
39
|
+
import Pill from '../primitives/Pill';
|
|
40
|
+
import {
|
|
41
|
+
CONTENT_TYPE_LABELS,
|
|
42
|
+
CONTENT_TYPE_BULLET_COLOR,
|
|
43
|
+
} from '../ContentTypeFilter/contentTypes';
|
|
44
|
+
import styles from './ContentDetailHero.module.css';
|
|
45
|
+
|
|
46
|
+
export default function ContentDetailHero({
|
|
47
|
+
crumb,
|
|
48
|
+
contentType,
|
|
49
|
+
contentTypeLabel,
|
|
50
|
+
tags = [],
|
|
51
|
+
title,
|
|
52
|
+
summary,
|
|
53
|
+
author,
|
|
54
|
+
date,
|
|
55
|
+
dateLabel,
|
|
56
|
+
duration,
|
|
57
|
+
locale,
|
|
58
|
+
cover,
|
|
59
|
+
className,
|
|
60
|
+
}) {
|
|
61
|
+
const composed = [styles.hero, className].filter(Boolean).join(' ');
|
|
62
|
+
|
|
63
|
+
const typeLabel = contentTypeLabel
|
|
64
|
+
|| (contentType && CONTENT_TYPE_LABELS[contentType])
|
|
65
|
+
|| null;
|
|
66
|
+
const typeBullet = contentType
|
|
67
|
+
? CONTENT_TYPE_BULLET_COLOR[contentType]
|
|
68
|
+
: undefined;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<section className={composed}>
|
|
72
|
+
{Array.isArray(crumb) && crumb.length > 0 && (
|
|
73
|
+
<div className={styles.crumb}>
|
|
74
|
+
{crumb.map((c, i) => {
|
|
75
|
+
const last = i === crumb.length - 1;
|
|
76
|
+
const sep = !last
|
|
77
|
+
? <span className={styles.sep} aria-hidden="true">/</span>
|
|
78
|
+
: null;
|
|
79
|
+
if (typeof c === 'string') {
|
|
80
|
+
return <React.Fragment key={i}>{c}{sep}</React.Fragment>;
|
|
81
|
+
}
|
|
82
|
+
return (
|
|
83
|
+
<React.Fragment key={i}>
|
|
84
|
+
{c.href
|
|
85
|
+
? <a href={c.href}>{c.label}</a>
|
|
86
|
+
: <span>{c.label}</span>}
|
|
87
|
+
{sep}
|
|
88
|
+
</React.Fragment>
|
|
89
|
+
);
|
|
90
|
+
})}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
{(typeLabel || tags.length > 0) && (
|
|
95
|
+
<div className={styles.tags}>
|
|
96
|
+
{typeLabel && (
|
|
97
|
+
<Pill bullet bulletColor={typeBullet}>{typeLabel}</Pill>
|
|
98
|
+
)}
|
|
99
|
+
{tags.map((t, i) => (
|
|
100
|
+
<Pill key={i} bullet bulletColor="var(--c-cobalt-300)">{t}</Pill>
|
|
101
|
+
))}
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
{title && <h1 className={styles.title}>{title}</h1>}
|
|
106
|
+
{summary && <p className={styles.summary}>{summary}</p>}
|
|
107
|
+
|
|
108
|
+
{(author || date || duration) && (
|
|
109
|
+
<div className={styles.meta}>
|
|
110
|
+
{(author || date) && (
|
|
111
|
+
<AuthorByline
|
|
112
|
+
name={author && author.name}
|
|
113
|
+
avatarSrc={author && author.avatarSrc}
|
|
114
|
+
initials={author && author.initials}
|
|
115
|
+
date={date}
|
|
116
|
+
dateLabel={dateLabel}
|
|
117
|
+
locale={locale}
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
{duration && <span className={styles.duration}>{duration}</span>}
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
|
|
124
|
+
<div className={styles.cover}>
|
|
125
|
+
<span className={styles.watermark} aria-hidden="true" />
|
|
126
|
+
{cover && cover.src
|
|
127
|
+
? <img src={cover.src} alt={cover.alt || ''} className={styles.coverImg} />
|
|
128
|
+
: (
|
|
129
|
+
<HexThumbnail size="xl" tone={cover && cover.tone || 'cobalt'}>
|
|
130
|
+
{cover && cover.icon}
|
|
131
|
+
</HexThumbnail>
|
|
132
|
+
)}
|
|
133
|
+
</div>
|
|
134
|
+
</section>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ContentDetailHero /> styles. Mirrors .content-detail-hero in
|
|
3
|
+
* preview/components/academy.css.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.hero {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
gap: var(--space-6);
|
|
10
|
+
padding: var(--space-12) 0 var(--space-8);
|
|
11
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.crumb {
|
|
15
|
+
display: flex;
|
|
16
|
+
gap: 6px;
|
|
17
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
18
|
+
font-size: 12px;
|
|
19
|
+
letter-spacing: 0.04em;
|
|
20
|
+
color: var(--c-cobalt-400);
|
|
21
|
+
flex-wrap: wrap;
|
|
22
|
+
}
|
|
23
|
+
.crumb a {
|
|
24
|
+
color: var(--c-cobalt-700);
|
|
25
|
+
text-decoration: none;
|
|
26
|
+
}
|
|
27
|
+
.crumb a:hover { color: var(--c-blue-cobalt); }
|
|
28
|
+
.crumb .sep { color: var(--c-cobalt-300); }
|
|
29
|
+
|
|
30
|
+
.tags {
|
|
31
|
+
display: flex;
|
|
32
|
+
gap: 6px;
|
|
33
|
+
flex-wrap: wrap;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.title {
|
|
37
|
+
font-size: 44px;
|
|
38
|
+
font-weight: 700;
|
|
39
|
+
letter-spacing: -0.02em;
|
|
40
|
+
color: var(--c-cobalt-900);
|
|
41
|
+
margin: 0;
|
|
42
|
+
line-height: 1.1;
|
|
43
|
+
max-width: 24ch;
|
|
44
|
+
}
|
|
45
|
+
@media (max-width: 700px) { .title { font-size: 32px; } }
|
|
46
|
+
|
|
47
|
+
.summary {
|
|
48
|
+
font-size: 18px;
|
|
49
|
+
color: var(--c-cobalt-700);
|
|
50
|
+
line-height: 1.55;
|
|
51
|
+
margin: 0;
|
|
52
|
+
max-width: 60ch;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.meta {
|
|
56
|
+
display: flex;
|
|
57
|
+
gap: var(--space-4);
|
|
58
|
+
align-items: center;
|
|
59
|
+
flex-wrap: wrap;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.duration {
|
|
63
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
letter-spacing: 0.04em;
|
|
66
|
+
color: var(--c-cobalt-400);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.cover {
|
|
70
|
+
width: 100%;
|
|
71
|
+
aspect-ratio: 16 / 9;
|
|
72
|
+
background: var(--c-cobalt-900);
|
|
73
|
+
border-radius: var(--radius-lg);
|
|
74
|
+
margin-top: var(--space-4);
|
|
75
|
+
position: relative;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.coverImg {
|
|
83
|
+
width: 100%;
|
|
84
|
+
height: 100%;
|
|
85
|
+
object-fit: cover;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.watermark {
|
|
89
|
+
position: absolute;
|
|
90
|
+
inset: 0;
|
|
91
|
+
pointer-events: none;
|
|
92
|
+
background-image: url('/img/honeycomb-scatter.svg');
|
|
93
|
+
background-size: 320px;
|
|
94
|
+
background-repeat: repeat;
|
|
95
|
+
opacity: 0.5;
|
|
96
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ContentTypeFilter />
|
|
3
|
+
*
|
|
4
|
+
* Top-of-page chip row that filters academy content by type. "All" is
|
|
5
|
+
* the default chip and is always present. Driven by a `?type=` query
|
|
6
|
+
* parameter so the filter survives reload and copy-paste.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the chip row in preview/components/academy.html.
|
|
9
|
+
*
|
|
10
|
+
* Modes:
|
|
11
|
+
* - controlled: pass `value` (string | null) and `onChange` (fn)
|
|
12
|
+
* - uncontrolled link mode: pass `hrefForType` (fn) to render <a>
|
|
13
|
+
* chips with hrefs and let the surrounding page re-render on
|
|
14
|
+
* navigation. Useful for static-site filtering via query string.
|
|
15
|
+
*
|
|
16
|
+
* Counts: pass `counts` keyed by type ({ blog: 18, guide: 9, ... })
|
|
17
|
+
* to render a small monospaced count next to each label. Pass a number
|
|
18
|
+
* to `allCount` for the "Everything" chip total. Counts are optional.
|
|
19
|
+
*
|
|
20
|
+
* Usage in MDX (uncontrolled, query-string driven):
|
|
21
|
+
*
|
|
22
|
+
* <ContentTypeFilter
|
|
23
|
+
* value={type}
|
|
24
|
+
* hrefForType={(t) => t ? `?type=${t}` : '?'}
|
|
25
|
+
* counts={{ blog: 18, guide: 9, 'case-study': 6, webinar: 5, tutorial: 4 }}
|
|
26
|
+
* />
|
|
27
|
+
*
|
|
28
|
+
* Usage in JSX (controlled):
|
|
29
|
+
*
|
|
30
|
+
* const [type, setType] = useState(null);
|
|
31
|
+
* <ContentTypeFilter value={type} onChange={setType} />
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import React from 'react';
|
|
35
|
+
import {
|
|
36
|
+
CONTENT_TYPES,
|
|
37
|
+
CONTENT_TYPE_PLURAL_LABELS,
|
|
38
|
+
} from './contentTypes';
|
|
39
|
+
import styles from './ContentTypeFilter.module.css';
|
|
40
|
+
|
|
41
|
+
const ALL = '__all__';
|
|
42
|
+
|
|
43
|
+
export default function ContentTypeFilter({
|
|
44
|
+
value = null,
|
|
45
|
+
onChange,
|
|
46
|
+
hrefForType,
|
|
47
|
+
counts,
|
|
48
|
+
allLabel = 'Everything',
|
|
49
|
+
allCount,
|
|
50
|
+
types = CONTENT_TYPES,
|
|
51
|
+
className,
|
|
52
|
+
}) {
|
|
53
|
+
const isLinkMode = typeof hrefForType === 'function';
|
|
54
|
+
|
|
55
|
+
const renderChip = (key, label, count) => {
|
|
56
|
+
const active = (key === ALL && value == null) || key === value;
|
|
57
|
+
const composed = [
|
|
58
|
+
styles.chip,
|
|
59
|
+
active ? styles.active : null,
|
|
60
|
+
].filter(Boolean).join(' ');
|
|
61
|
+
|
|
62
|
+
if (isLinkMode) {
|
|
63
|
+
return (
|
|
64
|
+
<a
|
|
65
|
+
key={key}
|
|
66
|
+
href={hrefForType(key === ALL ? null : key)}
|
|
67
|
+
className={composed}
|
|
68
|
+
>
|
|
69
|
+
<span className={styles.label}>{label}</span>
|
|
70
|
+
{typeof count === 'number' && (
|
|
71
|
+
<span className={styles.count}>{count}</span>
|
|
72
|
+
)}
|
|
73
|
+
</a>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<button
|
|
79
|
+
key={key}
|
|
80
|
+
type="button"
|
|
81
|
+
className={composed}
|
|
82
|
+
onClick={() => onChange && onChange(key === ALL ? null : key)}
|
|
83
|
+
aria-pressed={active}
|
|
84
|
+
>
|
|
85
|
+
<span className={styles.label}>{label}</span>
|
|
86
|
+
{typeof count === 'number' && (
|
|
87
|
+
<span className={styles.count}>{count}</span>
|
|
88
|
+
)}
|
|
89
|
+
</button>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div className={[styles.row, className].filter(Boolean).join(' ')}>
|
|
95
|
+
{renderChip(ALL, allLabel, allCount)}
|
|
96
|
+
{types.map((t) =>
|
|
97
|
+
renderChip(t, CONTENT_TYPE_PLURAL_LABELS[t] || t, counts && counts[t])
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {CONTENT_TYPES, CONTENT_TYPE_PLURAL_LABELS} from './contentTypes';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <ContentTypeFilter /> styles. Mirrors .type-filter in
|
|
3
|
+
* preview/components/academy.css.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.row {
|
|
7
|
+
display: flex;
|
|
8
|
+
gap: 8px;
|
|
9
|
+
flex-wrap: wrap;
|
|
10
|
+
align-items: center;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.chip {
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: 6px;
|
|
17
|
+
padding: 6px 14px;
|
|
18
|
+
border-radius: var(--radius-pill);
|
|
19
|
+
background: white;
|
|
20
|
+
border: 1px solid var(--c-cobalt-100);
|
|
21
|
+
color: var(--c-cobalt-700);
|
|
22
|
+
font-family: var(--conduction-typography-font-family-body);
|
|
23
|
+
font-size: 13px;
|
|
24
|
+
font-weight: 500;
|
|
25
|
+
line-height: 1.4;
|
|
26
|
+
cursor: pointer;
|
|
27
|
+
transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
|
|
28
|
+
text-decoration: none;
|
|
29
|
+
}
|
|
30
|
+
.chip:hover {
|
|
31
|
+
background: var(--c-cobalt-50);
|
|
32
|
+
border-color: var(--c-cobalt-200);
|
|
33
|
+
text-decoration: none;
|
|
34
|
+
color: var(--c-cobalt-700);
|
|
35
|
+
}
|
|
36
|
+
.chip:focus-visible {
|
|
37
|
+
outline: 2px solid var(--c-blue-cobalt);
|
|
38
|
+
outline-offset: 2px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.chip.active {
|
|
42
|
+
background: var(--c-blue-cobalt);
|
|
43
|
+
border-color: var(--c-blue-cobalt);
|
|
44
|
+
color: white;
|
|
45
|
+
}
|
|
46
|
+
.chip.active:hover {
|
|
47
|
+
background: var(--c-cobalt-700);
|
|
48
|
+
border-color: var(--c-cobalt-700);
|
|
49
|
+
color: white;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.label { display: inline-block; }
|
|
53
|
+
|
|
54
|
+
.count {
|
|
55
|
+
font-family: var(--conduction-typography-font-family-code);
|
|
56
|
+
font-size: 11px;
|
|
57
|
+
color: var(--c-cobalt-400);
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
}
|
|
60
|
+
.chip.active .count { color: var(--c-cobalt-200); }
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the academy content-type taxonomy.
|
|
3
|
+
*
|
|
4
|
+
* Used by:
|
|
5
|
+
* - <ContentTypeFilter /> for the chip row
|
|
6
|
+
* - <ContentCard /> to render the type chip and pick a bullet colour
|
|
7
|
+
* - <ContentDetailHero /> to render the type chip in the header
|
|
8
|
+
* - schemas/academy/content.schema.json (mirrored in the JSON Schema)
|
|
9
|
+
*
|
|
10
|
+
* Content types correspond 1:1 with frontmatter `contentType:` values
|
|
11
|
+
* in academy MDX files. Extending this list here is enough to surface
|
|
12
|
+
* a new chip across every site that consumes the preset; the JSON
|
|
13
|
+
* Schema in /schemas/academy/content.schema.json must be updated in
|
|
14
|
+
* lockstep.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export const CONTENT_TYPES = [
|
|
18
|
+
'blog',
|
|
19
|
+
'guide',
|
|
20
|
+
'case-study',
|
|
21
|
+
'webinar',
|
|
22
|
+
'tutorial',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export const CONTENT_TYPE_LABELS = {
|
|
26
|
+
'blog': 'Blog',
|
|
27
|
+
'guide': 'Guide',
|
|
28
|
+
'case-study': 'Case study',
|
|
29
|
+
'webinar': 'Webinar',
|
|
30
|
+
'tutorial': 'Tutorial',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Plural, sentence-case labels for the chip row. We use plural for
|
|
35
|
+
* the filter ("Blogs", "Case studies") because each chip filters a
|
|
36
|
+
* collection.
|
|
37
|
+
*/
|
|
38
|
+
export const CONTENT_TYPE_PLURAL_LABELS = {
|
|
39
|
+
'blog': 'Blogs',
|
|
40
|
+
'guide': 'Guides',
|
|
41
|
+
'case-study': 'Case studies',
|
|
42
|
+
'webinar': 'Webinars',
|
|
43
|
+
'tutorial': 'Tutorials',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Bullet (HexBullet) colour for the type chip. One token per type so
|
|
48
|
+
* a card's content-type pill is identifiable at a glance. Only one
|
|
49
|
+
* type uses KNVB orange (webinar), so the kit's "one orange accent
|
|
50
|
+
* per screen" rule still holds at landing scale.
|
|
51
|
+
*/
|
|
52
|
+
export const CONTENT_TYPE_BULLET_COLOR = {
|
|
53
|
+
'blog': 'var(--c-blue-cobalt)',
|
|
54
|
+
'guide': 'var(--c-mint-500)',
|
|
55
|
+
'case-study': 'var(--c-cobalt-700)',
|
|
56
|
+
'webinar': 'var(--c-orange-knvb)',
|
|
57
|
+
'tutorial': 'var(--c-cobalt-400)',
|
|
58
|
+
};
|