@conduction/docusaurus-preset 1.2.1 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/docusaurus-preset",
3
- "version": "1.2.1",
3
+ "version": "1.3.1",
4
4
  "scripts": {
5
5
  "prepack": "node scripts/prepack-bundle-css.js"
6
6
  },
@@ -2,11 +2,9 @@
2
2
  * <ContactCta />
3
3
  *
4
4
  * Soft tinted call-to-action panel for the bottom of academy
5
- * tutorials, docs pages, or any long-form content. Two-column
6
- * layout: title + body on the left, single primary action on the
7
- * right. A subtle pointy-top hex motif decorates the right edge
8
- * so the panel reads as a Conduction surface, not a generic blue
9
- * box.
5
+ * tutorials, docs pages, or any long-form content. Composed from
6
+ * <HexCard decoration> with a mail icon in the top-left badge and
7
+ * the primary CTA in the right-side actions slot.
10
8
  *
11
9
  * Tone: cobalt-50 ground, cobalt-900 type, cobalt-700 body, KNVB
12
10
  * orange reserved for the trailing arrow inside the CTA. One
@@ -34,41 +32,39 @@
34
32
  */
35
33
 
36
34
  import React from 'react';
35
+ import HexCard from '../HexCard/HexCard';
37
36
  import styles from './ContactCta.module.css';
38
37
 
39
- export default function ContactCta({
40
- title,
41
- body,
42
- cta,
43
- className,
44
- }) {
45
- const composed = [styles.panel, className].filter(Boolean).join(' ');
38
+ function CtaButton({cta}) {
39
+ if (!cta) return null;
40
+ const isExternal = cta.href && /^https?:/.test(cta.href);
46
41
  return (
47
- <aside className={composed}>
48
- <span className={styles.hex1} aria-hidden="true" />
49
- <span className={styles.hex2} aria-hidden="true" />
50
- <span className={styles.hex3} aria-hidden="true" />
51
-
52
- <div className={styles.copy}>
53
- {title && <h3 className={styles.title}>{title}</h3>}
54
- {body && <p className={styles.body}>{body}</p>}
55
- </div>
42
+ <a
43
+ className={styles.button}
44
+ href={cta.href || '#'}
45
+ target={isExternal ? '_blank' : undefined}
46
+ rel={isExternal ? 'noreferrer noopener' : undefined}
47
+ >
48
+ <span>{cta.label}</span>
49
+ <svg viewBox="0 0 24 24" aria-hidden="true" width="14" height="14"
50
+ fill="none" stroke="currentColor" strokeWidth="2"
51
+ strokeLinecap="round" strokeLinejoin="round">
52
+ <path d="M5 12h14M12 5l7 7-7 7"/>
53
+ </svg>
54
+ </a>
55
+ );
56
+ }
56
57
 
57
- {cta && (
58
- <a
59
- className={styles.button}
60
- href={cta.href || '#'}
61
- target={cta.href && /^https?:/.test(cta.href) ? '_blank' : undefined}
62
- rel={cta.href && /^https?:/.test(cta.href) ? 'noreferrer noopener' : undefined}
63
- >
64
- <span>{cta.label}</span>
65
- <svg viewBox="0 0 24 24" aria-hidden="true" width="14" height="14"
66
- fill="none" stroke="currentColor" strokeWidth="2"
67
- strokeLinecap="round" strokeLinejoin="round">
68
- <path d="M5 12h14M12 5l7 7-7 7"/>
69
- </svg>
70
- </a>
71
- )}
72
- </aside>
58
+ export default function ContactCta({title, body, cta, className}) {
59
+ return (
60
+ <HexCard
61
+ title={title}
62
+ icon="mail"
63
+ decoration
64
+ actions={<CtaButton cta={cta} />}
65
+ className={className}
66
+ >
67
+ {body && <p>{body}</p>}
68
+ </HexCard>
73
69
  );
74
70
  }
@@ -1,97 +1,9 @@
1
- .panel {
2
- position: relative;
3
- display: grid;
4
- grid-template-columns: 1fr auto;
5
- gap: var(--space-6);
6
- align-items: center;
7
- background: var(--c-cobalt-50);
8
- border: 1px solid var(--c-cobalt-100);
9
- border-radius: var(--radius-lg);
10
- padding: var(--space-7) var(--space-8);
11
- margin: var(--space-12) 0 var(--space-8);
12
- overflow: hidden;
13
- font-family: var(--conduction-typography-font-family-body);
14
- }
15
-
16
- @media (max-width: 720px) {
17
- .panel {
18
- grid-template-columns: 1fr;
19
- padding: var(--space-6);
20
- gap: var(--space-4);
21
- }
22
- }
23
-
24
- /* Decorative pointy-top hexes layered on the right edge. They sit
25
- behind the copy via z-index 0 with the copy and button on z-index 1.
26
- Pointy-top point-up only — never rotated, never flat-top. */
27
- .hex1,
28
- .hex2,
29
- .hex3 {
30
- position: absolute;
31
- background: var(--c-cobalt-100);
32
- clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
33
- pointer-events: none;
34
- z-index: 0;
35
- }
36
-
37
- .hex1 {
38
- width: 220px;
39
- height: 254px;
40
- right: -60px;
41
- top: -40px;
42
- opacity: 0.7;
43
- }
44
-
45
- .hex2 {
46
- width: 110px;
47
- height: 127px;
48
- right: 80px;
49
- bottom: -30px;
50
- opacity: 0.5;
51
- background: var(--c-cobalt-200);
52
- }
53
-
54
- .hex3 {
55
- width: 56px;
56
- height: 65px;
57
- right: 220px;
58
- top: 18px;
59
- opacity: 0.5;
60
- background: var(--c-cobalt-200);
61
- }
62
-
63
- @media (max-width: 720px) {
64
- .hex1 { right: -100px; top: -80px; }
65
- .hex2, .hex3 { display: none; }
66
- }
67
-
68
- .copy {
69
- position: relative;
70
- z-index: 1;
71
- display: grid;
72
- gap: var(--space-2);
73
- max-width: 60ch;
74
- }
75
-
76
- .title {
77
- font-size: 24px;
78
- font-weight: 700;
79
- letter-spacing: -0.01em;
80
- color: var(--c-cobalt-900);
81
- margin: 0;
82
- line-height: 1.25;
83
- }
84
-
85
- .body {
86
- font-size: 16px;
87
- color: var(--c-cobalt-700);
88
- line-height: 1.6;
89
- margin: 0;
90
- }
1
+ /**
2
+ * <ContactCta /> styles. The shell is now <HexCard decoration>; only
3
+ * the action button keeps its own rules here.
4
+ */
91
5
 
92
6
  .button {
93
- position: relative;
94
- z-index: 1;
95
7
  display: inline-flex;
96
8
  align-items: center;
97
9
  gap: 8px;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * <HexCard />
3
+ *
4
+ * Tinted academy/tutorial panel with a top-left hex badge holding an
5
+ * icon. The shared shell behind <ContactCta />, <Outcomes />, and
6
+ * <Prerequisites /> — all three were rebuilt in isolation around the
7
+ * same cobalt-50 surface, so this primitive consolidates the surface,
8
+ * border, padding, and badge slot in one place.
9
+ *
10
+ * Anatomy:
11
+ * ┌──────────────────────────────────────────┐
12
+ * │ ⬢ Title [actions] │
13
+ * │ body / list / paragraph │
14
+ * └──────────────────────────────────────────┘
15
+ *
16
+ * The badge is a <HexThumbnail size="sm" tone="cobalt-deep"> with a
17
+ * white glyph. Cobalt + white keeps the orange accent budget free for
18
+ * the orange checkmark/bullet glyphs inside <Outcomes /> and
19
+ * <Prerequisites />, and for the orange CTA arrow in <ContactCta />.
20
+ *
21
+ * Usage:
22
+ *
23
+ * <HexCard title="Wat je leert" icon="lightbulb">
24
+ * <ul>...</ul>
25
+ * </HexCard>
26
+ *
27
+ * <HexCard
28
+ * title="Wil je meer weten?"
29
+ * icon="mail"
30
+ * decoration
31
+ * actions={<a className="…">Mail ons →</a>}
32
+ * >
33
+ * <p>Mail ons. We helpen je…</p>
34
+ * </HexCard>
35
+ *
36
+ * Mirrors preview/components/hex-card.html.
37
+ */
38
+
39
+ import React from 'react';
40
+ import HexThumbnail from '../primitives/HexThumbnail';
41
+ import styles from './HexCard.module.css';
42
+
43
+ const ICONS = {
44
+ mail: (
45
+ <svg viewBox="0 0 24 24" aria-hidden="true">
46
+ <rect x="3" y="5" width="18" height="14" rx="2"/>
47
+ <path d="M3 7l9 7 9-7"/>
48
+ </svg>
49
+ ),
50
+ lightbulb: (
51
+ <svg viewBox="0 0 24 24" aria-hidden="true">
52
+ <path d="M9 18h6"/>
53
+ <path d="M10 21h4"/>
54
+ <path d="M12 3a6 6 0 0 0-4 10.5c.7.8 1 1.6 1 2.5v1h6v-1c0-.9.3-1.7 1-2.5A6 6 0 0 0 12 3z"/>
55
+ </svg>
56
+ ),
57
+ clipboard: (
58
+ <svg viewBox="0 0 24 24" aria-hidden="true">
59
+ <rect x="6" y="4" width="12" height="17" rx="2"/>
60
+ <rect x="9" y="2" width="6" height="4" rx="1"/>
61
+ <path d="M9 11h6"/>
62
+ <path d="M9 15h6"/>
63
+ </svg>
64
+ ),
65
+ };
66
+
67
+ export default function HexCard({
68
+ title,
69
+ icon,
70
+ iconNode,
71
+ decoration = false,
72
+ actions,
73
+ children,
74
+ className,
75
+ as: Tag = 'aside',
76
+ }) {
77
+ const composed = [
78
+ styles.card,
79
+ decoration && styles.hasDecoration,
80
+ actions && styles.hasActions,
81
+ className,
82
+ ].filter(Boolean).join(' ');
83
+
84
+ const glyph = iconNode || (icon && ICONS[icon]) || null;
85
+
86
+ return (
87
+ <Tag className={composed}>
88
+ {decoration && (
89
+ <span className={styles.decoFrame} aria-hidden="true">
90
+ <span className={`${styles.deco} ${styles.deco1}`} />
91
+ <span className={`${styles.deco} ${styles.deco2}`} />
92
+ <span className={`${styles.deco} ${styles.deco3}`} />
93
+ </span>
94
+ )}
95
+
96
+ {glyph && (
97
+ <span className={styles.badge} aria-hidden="true">
98
+ <HexThumbnail size="sm" tone="cobalt-deep">{glyph}</HexThumbnail>
99
+ </span>
100
+ )}
101
+
102
+ <div className={styles.head}>
103
+ {title && <h3 className={styles.title}>{title}</h3>}
104
+ </div>
105
+
106
+ <div className={styles.body}>
107
+ {children}
108
+ </div>
109
+
110
+ {actions && (
111
+ <div className={styles.actions}>{actions}</div>
112
+ )}
113
+ </Tag>
114
+ );
115
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * <HexCard /> styles. Tinted academy panel with a corner hex badge
3
+ * and an optional right-edge decorative hex motif.
4
+ *
5
+ * Layout: the badge is absolute-positioned and centered on the
6
+ * top-left corner of the card (half outside, half inside). Card
7
+ * padding has extra inset on the left and bottom so the title and
8
+ * the last list item never collide with the protruding hex.
9
+ *
10
+ * ┌── ⬢ ────────────────────────┐
11
+ * │ ⬢ Title [actions]│
12
+ * │ body / list / paragraph │
13
+ * │ │
14
+ * └─────────────────────────────┘
15
+ */
16
+
17
+ .card {
18
+ position: relative;
19
+ display: grid;
20
+ grid-template-columns: 1fr;
21
+ grid-template-areas:
22
+ "head"
23
+ "body";
24
+ row-gap: var(--space-3);
25
+ align-items: start;
26
+ background: var(--c-cobalt-50);
27
+ border: 1px solid var(--c-cobalt-100);
28
+ border-radius: var(--radius-lg);
29
+ padding: var(--space-6) var(--space-8) var(--space-10) var(--space-12);
30
+ margin: var(--space-8) 0;
31
+ overflow: visible;
32
+ font-family: var(--conduction-typography-font-family-body);
33
+ }
34
+
35
+ .hasActions {
36
+ grid-template-columns: 1fr auto;
37
+ grid-template-areas:
38
+ "head actions"
39
+ "body actions";
40
+ align-items: center;
41
+ column-gap: var(--space-5);
42
+ }
43
+
44
+ @media (max-width: 720px) {
45
+ .hasActions {
46
+ grid-template-columns: 1fr;
47
+ grid-template-areas:
48
+ "head"
49
+ "body"
50
+ "actions";
51
+ }
52
+ .card { padding: var(--space-6) var(--space-5) var(--space-8) var(--space-10); }
53
+ }
54
+
55
+ /* Hex badge centered on the top-left corner. Half overflows the
56
+ card to the upper-left so the badge reads as a corner mark, not
57
+ an inset glyph. z-index lifts it above the deco frame. */
58
+ .badge {
59
+ position: absolute;
60
+ top: 0;
61
+ left: 0;
62
+ transform: translate(-50%, -50%);
63
+ z-index: 2;
64
+ display: inline-flex;
65
+ pointer-events: none;
66
+ }
67
+
68
+ .head {
69
+ grid-area: head;
70
+ position: relative;
71
+ z-index: 1;
72
+ align-self: center;
73
+ }
74
+
75
+ .title {
76
+ font-size: 22px;
77
+ font-weight: 700;
78
+ letter-spacing: -0.01em;
79
+ color: var(--c-cobalt-900);
80
+ margin: 0;
81
+ line-height: 1.25;
82
+ }
83
+
84
+ .body {
85
+ grid-area: body;
86
+ position: relative;
87
+ z-index: 1;
88
+ font-size: 16px;
89
+ line-height: 1.6;
90
+ color: var(--c-cobalt-700);
91
+ }
92
+
93
+ .body > p { margin: 0; }
94
+ .body > p + p { margin-top: var(--space-3); }
95
+ .body > ul {
96
+ list-style: none;
97
+ padding: 0;
98
+ margin: 0;
99
+ display: grid;
100
+ gap: var(--space-3);
101
+ }
102
+
103
+ .actions {
104
+ grid-area: actions;
105
+ position: relative;
106
+ z-index: 1;
107
+ display: inline-flex;
108
+ align-items: center;
109
+ white-space: nowrap;
110
+ }
111
+
112
+ /* Wraps the decorative right-edge hexes. Absolute, fills the card,
113
+ inherits border-radius, and clips its own children so the deco
114
+ stays inside the rounded panel even though the card itself has
115
+ overflow: visible (needed for the corner badge). */
116
+ .decoFrame {
117
+ position: absolute;
118
+ inset: 0;
119
+ border-radius: inherit;
120
+ overflow: hidden;
121
+ pointer-events: none;
122
+ z-index: 0;
123
+ }
124
+
125
+ .deco {
126
+ position: absolute;
127
+ background: var(--c-cobalt-100);
128
+ clip-path: var(--hex-pointy-top);
129
+ }
130
+
131
+ .deco1 {
132
+ width: 220px;
133
+ height: 254px;
134
+ right: -60px;
135
+ top: -40px;
136
+ opacity: 0.7;
137
+ }
138
+
139
+ .deco2 {
140
+ width: 110px;
141
+ height: 127px;
142
+ right: 80px;
143
+ bottom: -30px;
144
+ opacity: 0.5;
145
+ background: var(--c-cobalt-200);
146
+ }
147
+
148
+ .deco3 {
149
+ width: 56px;
150
+ height: 65px;
151
+ right: 220px;
152
+ top: 18px;
153
+ opacity: 0.5;
154
+ background: var(--c-cobalt-200);
155
+ }
156
+
157
+ @media (max-width: 720px) {
158
+ .deco1 { right: -100px; top: -80px; }
159
+ .deco2, .deco3 { display: none; }
160
+ }
@@ -10,9 +10,10 @@
10
10
  * - "What do I need before I start?" (Prerequisites)
11
11
  * - the actual tutorial body
12
12
  *
13
- * Visual: tinted card identical to <Prerequisites />, but each item
14
- * uses an orange checkmark glyph (achievement) instead of the orange
15
- * hex bullet (need). Same tone, distinct semantics.
13
+ * Visual: shared <HexCard> shell with a lightbulb icon in the
14
+ * top-left badge. Each item uses an orange checkmark glyph
15
+ * (achievement) instead of the orange hex bullet (need) used in
16
+ * <Prerequisites />. Same surface, distinct semantics.
16
17
  *
17
18
  * Usage:
18
19
  *
@@ -27,6 +28,7 @@
27
28
  */
28
29
 
29
30
  import React from 'react';
31
+ import HexCard from '../HexCard/HexCard';
30
32
  import styles from './Outcomes.module.css';
31
33
 
32
34
  export function Outcome({children, className}) {
@@ -44,11 +46,9 @@ export function Outcome({children, className}) {
44
46
  }
45
47
 
46
48
  export default function Outcomes({title = "What you'll learn", children, className}) {
47
- const composed = [styles.card, className].filter(Boolean).join(' ');
48
49
  return (
49
- <aside className={composed}>
50
- {title && <h2 className={styles.title}>{title}</h2>}
51
- <ul className={styles.list}>{children}</ul>
52
- </aside>
50
+ <HexCard title={title} icon="lightbulb" className={className}>
51
+ <ul>{children}</ul>
52
+ </HexCard>
53
53
  );
54
54
  }
@@ -1,29 +1,7 @@
1
- .card {
2
- background: var(--c-cobalt-50);
3
- border: 1px solid var(--c-cobalt-100);
4
- border-radius: var(--radius-lg);
5
- padding: var(--space-6) var(--space-7);
6
- margin: var(--space-8) 0;
7
- font-family: var(--conduction-typography-font-family-body);
8
- }
9
-
10
- .title {
11
- font-size: 22px;
12
- font-weight: 700;
13
- letter-spacing: -0.01em;
14
- color: var(--c-cobalt-900);
15
- margin: 0 0 var(--space-4);
16
- line-height: 1.25;
17
- }
18
-
19
- .list {
20
- list-style: none;
21
- padding: 0;
22
- margin: 0;
23
- display: grid;
24
- gap: var(--space-3);
25
- max-width: none;
26
- }
1
+ /**
2
+ * <Outcomes /> styles. The shell is provided by <HexCard>; only the
3
+ * orange-checkmark item layout lives here.
4
+ */
27
5
 
28
6
  .item {
29
7
  display: grid;
@@ -5,10 +5,10 @@
5
5
  * starting an academy tutorial. Replaces the ad-hoc "What you need"
6
6
  * h2 + bullet list pattern that was duplicated across tutorials.
7
7
  *
8
- * Each item renders with a small hex-bullet on the left so the list
9
- * reads as a checklist instead of generic prose. The card uses the
10
- * cobalt-50 tinted-surface tone, matching <FeatureList/> and
11
- * <FAQ/> on academy pages.
8
+ * Visual: shared <HexCard> shell with a clipboard icon in the
9
+ * top-left badge. Each item renders with a small orange hex-bullet
10
+ * on the left so the list reads as a checklist instead of generic
11
+ * prose.
12
12
  *
13
13
  * Usage:
14
14
  *
@@ -25,6 +25,7 @@
25
25
  */
26
26
 
27
27
  import React from 'react';
28
+ import HexCard from '../HexCard/HexCard';
28
29
  import styles from './Prerequisites.module.css';
29
30
 
30
31
  export function PrerequisiteItem({children, className}) {
@@ -38,11 +39,9 @@ export function PrerequisiteItem({children, className}) {
38
39
  }
39
40
 
40
41
  export default function Prerequisites({title = 'What you need', children, className}) {
41
- const composed = [styles.card, className].filter(Boolean).join(' ');
42
42
  return (
43
- <aside className={composed}>
44
- {title && <h2 className={styles.title}>{title}</h2>}
45
- <ul className={styles.list}>{children}</ul>
46
- </aside>
43
+ <HexCard title={title} icon="clipboard" className={className}>
44
+ <ul>{children}</ul>
45
+ </HexCard>
47
46
  );
48
47
  }
@@ -1,28 +1,7 @@
1
- .card {
2
- background: var(--c-cobalt-50);
3
- border: 1px solid var(--c-cobalt-100);
4
- border-radius: var(--radius-lg);
5
- padding: var(--space-6) var(--space-7);
6
- margin: var(--space-10) 0;
7
- font-family: var(--conduction-typography-font-family-body);
8
- }
9
-
10
- .title {
11
- font-size: 18px;
12
- font-weight: 700;
13
- letter-spacing: -0.01em;
14
- color: var(--c-cobalt-900);
15
- margin: 0 0 var(--space-3);
16
- line-height: 1.3;
17
- }
18
-
19
- .list {
20
- list-style: none;
21
- padding: 0;
22
- margin: 0;
23
- display: grid;
24
- gap: var(--space-2);
25
- }
1
+ /**
2
+ * <Prerequisites /> styles. The shell is provided by <HexCard>; only
3
+ * the orange hex-bullet item layout lives here.
4
+ */
26
5
 
27
6
  .item {
28
7
  display: grid;
@@ -39,7 +18,7 @@
39
18
  height: 14px;
40
19
  margin-top: 6px;
41
20
  background: var(--c-orange-knvb);
42
- clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
21
+ clip-path: var(--hex-pointy-top);
43
22
  flex-shrink: 0;
44
23
  }
45
24
 
@@ -99,6 +99,7 @@ export {default as ContentDetailHero} from './ContentDetailHero/ContentDetailHer
99
99
  "What you need", "Troubleshooting", and "Next steps" h2 + bullet
100
100
  patterns that academy tutorials kept duplicating. Designed for use
101
101
  inside an MDX academy post body. */
102
+ export {default as HexCard} from './HexCard/HexCard.jsx';
102
103
  export {default as Outcomes, Outcome} from './Outcomes/Outcomes.jsx';
103
104
  export {default as Prerequisites, PrerequisiteItem} from './Prerequisites/Prerequisites.jsx';
104
105
  export {default as Troubleshooting, TroubleshootingItem} from './Troubleshooting/Troubleshooting.jsx';