@conduction/docusaurus-preset 3.0.0 → 3.1.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": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "scripts": {
5
5
  "prepack": "node scripts/prepack-bundle-css.js"
6
6
  },
@@ -0,0 +1,157 @@
1
+ /**
2
+ * <LeafCard /> + <LeafGrid />
3
+ *
4
+ * Metadata header for an Open Register leaf integration. Mirrors the
5
+ * registry descriptor (id, label, icon, group, requiredApp, storage)
6
+ * so every per-leaf docs page leads with the same compact summary —
7
+ * one card, six fields, no prose to skim. Used at the top of every
8
+ * docs/Integrations/{leaf}.md page in the openregister docs site.
9
+ *
10
+ * Used on:
11
+ * - /docs/Integrations/{leaf} (one <LeafCard />)
12
+ * - /docs/Integrations/leaf-system (a <LeafGrid /> of all 18+1)
13
+ *
14
+ * Brand:
15
+ * - Pointy-top hex carries the leaf's MDI icon (cobalt by default,
16
+ * orange for the one accent-leaf when grouped). Falls back to the
17
+ * `id` initial when no icon is given.
18
+ * - Status pill colours: green = backend-ready, amber = stub,
19
+ * cobalt = external (OpenConnector-routed), grey = built-in.
20
+ *
21
+ * Usage in MDX:
22
+ *
23
+ * import {LeafCard} from '@conduction/docusaurus-preset/components';
24
+ *
25
+ * <LeafCard
26
+ * id="calendar"
27
+ * label="Meetings"
28
+ * icon="Calendar"
29
+ * group="comms"
30
+ * requiredApp="calendar"
31
+ * storage="link-table"
32
+ * status="backend-ready" />
33
+ *
34
+ * <LeafGrid /> renders a responsive grid of LeafCard children for
35
+ * the overview page:
36
+ *
37
+ * <LeafGrid>
38
+ * <LeafCard id="calendar" ... />
39
+ * <LeafCard id="contacts" ... />
40
+ * </LeafGrid>
41
+ */
42
+
43
+ import React from 'react';
44
+ import styles from './LeafCard.module.css';
45
+
46
+ const STATUS_LABELS = {
47
+ 'backend-ready': 'Backend ready',
48
+ 'stub': 'Provider stub',
49
+ 'external': 'External (OpenConnector)',
50
+ 'built-in': 'Built-in',
51
+ };
52
+
53
+ const GROUP_LABELS = {
54
+ 'core': 'Core',
55
+ 'comms': 'Communication',
56
+ 'docs': 'Documents',
57
+ 'workflow': 'Workflow',
58
+ 'external': 'External',
59
+ };
60
+
61
+ const STORAGE_LABELS = {
62
+ 'magic-column': 'Magic column',
63
+ 'link-table': 'Link table',
64
+ 'external': 'External (no local store)',
65
+ 'query-time': 'Query-time (live)',
66
+ };
67
+
68
+ /**
69
+ * Render a single MDI icon glyph by name, with a graceful fallback
70
+ * to the first letter of the leaf id when the icon name is unknown.
71
+ * The docs site doesn't ship the full MDI set, so unknown icons fall
72
+ * back to a text initial rather than 404'ing on a missing asset.
73
+ */
74
+ function LeafGlyph({icon, id}) {
75
+ if (icon) {
76
+ // Defer the actual icon rendering to the consuming docs site
77
+ // (which can swizzle this if it wires up vue-material-design-icons
78
+ // or any equivalent React icon pack). Default: render the icon
79
+ // name as a small uppercase tag so the page still reads.
80
+ return <span className={styles.iconText} aria-hidden="true">{icon.charAt(0)}</span>;
81
+ }
82
+ return <span className={styles.iconText} aria-hidden="true">{(id || '?').charAt(0).toUpperCase()}</span>;
83
+ }
84
+
85
+ export function LeafCard({
86
+ id,
87
+ label,
88
+ icon,
89
+ group,
90
+ requiredApp,
91
+ storage,
92
+ status,
93
+ href,
94
+ description,
95
+ className,
96
+ }) {
97
+ const statusLabel = STATUS_LABELS[status] || status;
98
+ const groupLabel = GROUP_LABELS[group] || group;
99
+ const storageLabel = STORAGE_LABELS[storage] || storage;
100
+ const composed = [styles.card, styles['status-' + (status || 'unknown')], className].filter(Boolean).join(' ');
101
+
102
+ const inner = (
103
+ <>
104
+ <div className={styles.head}>
105
+ <div className={styles.hex} aria-hidden="true">
106
+ <LeafGlyph icon={icon} id={id} />
107
+ </div>
108
+ <div className={styles.heading}>
109
+ <div className={styles.label}>{label}</div>
110
+ <code className={styles.id}>{id}</code>
111
+ </div>
112
+ {status && <span className={styles.statusPill}>{statusLabel}</span>}
113
+ </div>
114
+ {description && <p className={styles.description}>{description}</p>}
115
+ <dl className={styles.meta}>
116
+ {group && (
117
+ <>
118
+ <dt>Group</dt>
119
+ <dd>{groupLabel}</dd>
120
+ </>
121
+ )}
122
+ {requiredApp !== undefined && (
123
+ <>
124
+ <dt>Required app</dt>
125
+ <dd>{requiredApp ? <code>{requiredApp}</code> : <span className={styles.muted}>None (always available)</span>}</dd>
126
+ </>
127
+ )}
128
+ {storage && (
129
+ <>
130
+ <dt>Storage</dt>
131
+ <dd>{storageLabel}</dd>
132
+ </>
133
+ )}
134
+ {icon && (
135
+ <>
136
+ <dt>Icon</dt>
137
+ <dd><code>{icon}</code></dd>
138
+ </>
139
+ )}
140
+ </dl>
141
+ </>
142
+ );
143
+
144
+ // Render as link when href is set so leaves on the overview grid
145
+ // navigate to their dedicated page on click.
146
+ if (href) {
147
+ return <a href={href} className={composed}>{inner}</a>;
148
+ }
149
+ return <div className={composed}>{inner}</div>;
150
+ }
151
+
152
+ export function LeafGrid({columns = 3, children, className}) {
153
+ const composed = [styles.grid, styles['grid-' + columns], className].filter(Boolean).join(' ');
154
+ return <div className={composed}>{children}</div>;
155
+ }
156
+
157
+ export default LeafCard;
@@ -0,0 +1,153 @@
1
+ /* LeafCard — per-leaf metadata header.
2
+ Pointy-top hex + label + status pill + meta grid. Sized for both
3
+ single-card placement at the top of a docs page and grid placement
4
+ on the overview. */
5
+
6
+ .card {
7
+ display: block;
8
+ background: white;
9
+ border: 1px solid var(--c-cobalt-100);
10
+ border-radius: var(--radius-md);
11
+ padding: var(--space-5);
12
+ text-decoration: none;
13
+ color: inherit;
14
+ transition: border-color 120ms ease, transform 120ms ease;
15
+ }
16
+
17
+ a.card:hover {
18
+ border-color: var(--c-cobalt-400);
19
+ transform: translateY(-1px);
20
+ }
21
+
22
+ .head {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: var(--space-4);
26
+ margin-bottom: var(--space-3);
27
+ }
28
+
29
+ .hex {
30
+ width: 40px;
31
+ height: 46px;
32
+ clip-path: var(--hex-pointy-top);
33
+ background: var(--c-cobalt-50);
34
+ color: var(--c-cobalt-700);
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ flex-shrink: 0;
39
+ font-weight: 700;
40
+ font-size: 16px;
41
+ }
42
+
43
+ .iconText {
44
+ display: inline-block;
45
+ font-weight: 700;
46
+ font-size: 18px;
47
+ }
48
+
49
+ .heading {
50
+ flex: 1;
51
+ min-width: 0;
52
+ }
53
+
54
+ .label {
55
+ font-size: 18px;
56
+ font-weight: 700;
57
+ color: var(--c-cobalt-900);
58
+ margin: 0;
59
+ line-height: 1.2;
60
+ }
61
+
62
+ .id {
63
+ display: inline-block;
64
+ font-size: 12px;
65
+ color: var(--c-cobalt-400);
66
+ font-family: var(--conduction-typography-font-family-code);
67
+ margin-top: 2px;
68
+ }
69
+
70
+ .statusPill {
71
+ display: inline-block;
72
+ font-size: 11px;
73
+ font-weight: 600;
74
+ text-transform: uppercase;
75
+ letter-spacing: 0.06em;
76
+ padding: 4px 10px;
77
+ border-radius: 999px;
78
+ flex-shrink: 0;
79
+ white-space: nowrap;
80
+ }
81
+
82
+ .status-backend-ready .statusPill {
83
+ background: var(--c-cobalt-50);
84
+ color: var(--c-cobalt-700);
85
+ }
86
+
87
+ .status-stub .statusPill {
88
+ background: var(--c-orange-50, #fff4e5);
89
+ color: var(--c-orange-knvb);
90
+ }
91
+
92
+ .status-external .statusPill {
93
+ background: var(--c-cobalt-100);
94
+ color: var(--c-cobalt-900);
95
+ }
96
+
97
+ .status-built-in .statusPill {
98
+ background: var(--c-cobalt-50);
99
+ color: var(--c-cobalt-700);
100
+ }
101
+
102
+ .description {
103
+ font-size: 14px;
104
+ color: var(--c-cobalt-700);
105
+ margin: 0 0 var(--space-4);
106
+ line-height: 1.55;
107
+ }
108
+
109
+ .meta {
110
+ display: grid;
111
+ grid-template-columns: max-content 1fr;
112
+ gap: 6px var(--space-4);
113
+ margin: 0;
114
+ font-size: 13px;
115
+ }
116
+
117
+ .meta dt {
118
+ color: var(--c-cobalt-400);
119
+ font-weight: 500;
120
+ text-transform: uppercase;
121
+ letter-spacing: 0.06em;
122
+ font-size: 11px;
123
+ align-self: center;
124
+ }
125
+
126
+ .meta dd {
127
+ margin: 0;
128
+ color: var(--c-cobalt-900);
129
+ align-self: center;
130
+ }
131
+
132
+ .muted {
133
+ color: var(--c-cobalt-400);
134
+ font-style: italic;
135
+ }
136
+
137
+ /* Grid for the overview page. */
138
+ .grid {
139
+ display: grid;
140
+ gap: var(--space-5);
141
+ margin: var(--space-5) 0;
142
+ }
143
+
144
+ .grid-1 { grid-template-columns: 1fr; }
145
+ .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
146
+ .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
147
+ .grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
148
+
149
+ @media (max-width: 768px) {
150
+ .grid-2, .grid-3, .grid-4 {
151
+ grid-template-columns: 1fr;
152
+ }
153
+ }
@@ -63,6 +63,12 @@ export {default as GameModal} from './GameModal/GameModal.jsx';
63
63
  framework-agnostic diagram set in @conduction/diagrams. Brand is
64
64
  flat-hex only; the 3D prism wrapper was removed in v3.0.0. */
65
65
  export {Hex, Platform, DomainTree, DiagramPipeline, SideBox, HoneycombBg, Pair, ArchFlow} from './Diagrams/Diagrams.jsx';
66
+
67
+ /* LeafCard — metadata header for an Open Register leaf integration.
68
+ <LeafGrid> renders a responsive grid of LeafCards for the overview
69
+ page; <LeafCard> on its own anchors the top of a per-leaf docs
70
+ page. See docs/Integrations/leaf-system.md for the worked use. */
71
+ export {default as LeafCard, LeafGrid} from './LeafCard/LeafCard.jsx';
66
72
  export {default as ComposeBlock} from './ComposeBlock/ComposeBlock.jsx';
67
73
  export {default as AppsGrid} from './AppsGrid/AppsGrid.jsx';
68
74
  export {default as AppMock} from './AppMock/AppMock.jsx';
package/src/css/brand.css CHANGED
@@ -310,3 +310,23 @@ a:not(.navbar__link):not(.footer__link-item):not(.button):hover {
310
310
  color: var(--c-cobalt-700);
311
311
  background: transparent;
312
312
  }
313
+
314
+ /* ============================================================
315
+ Body grows with content.
316
+
317
+ Infima ships `html, body { height: 100% }`. With long pages the
318
+ body stops at one viewport while content overflows beneath it.
319
+ Anything past that height paints over the browser canvas, not
320
+ over body. On screens where the canvas is dark (Chrome on macOS
321
+ dark mode, Windows high-contrast), the canal-footer pattern is
322
+ preceded by a `#1B1B1D` band.
323
+
324
+ Swapping `height: 100%` for `min-height: 100vh` keeps short
325
+ pages looking the same and lets body cover the full document
326
+ on long pages. Site-level customCss can still override.
327
+ ============================================================ */
328
+ html,
329
+ body {
330
+ height: auto;
331
+ min-height: 100vh;
332
+ }