@kolkrabbi/kol-workshop 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -47,4 +47,12 @@ const inventory = buildInventory(modules)
47
47
 
48
48
  So the package ships the **engine + chrome**, never the content — bring your own docs.
49
49
 
50
+ ## Tailwind v4 consumers
51
+
52
+ Tailwind skips `node_modules` when scanning, so point it at this package's source — otherwise the shell's grid/sidebar/toc layout utilities never generate and the docs shell collapses to a single column:
53
+
54
+ ```css
55
+ @source "../node_modules/@kolkrabbi/kol-workshop/src";
56
+ ```
57
+
50
58
  Self-check: `node src/engine/__check.mjs`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-workshop",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "private": false,
5
5
  "description": "KOL workshop/docs system — a handrolled markdown engine, search, tag system (incl. a d3 tag graph), docs viewer, and docs shell. Sits above @kolkrabbi/kol-{theme,icons,component,framework}; content is consumer-injected (the package never globs docs).",
6
6
  "license": "MIT",
@@ -14,10 +14,10 @@
14
14
  "dependencies": {
15
15
  "d3": "^7.9.0",
16
16
  "@kolkrabbi/kol-brand": "^0.1.2",
17
- "@kolkrabbi/kol-component": "0.7.0",
18
- "@kolkrabbi/kol-theme": "0.7.1",
19
- "@kolkrabbi/kol-icons": "0.5.0",
20
- "@kolkrabbi/kol-framework": "0.3.2"
17
+ "@kolkrabbi/kol-framework": "0.3.3",
18
+ "@kolkrabbi/kol-component": "0.8.0",
19
+ "@kolkrabbi/kol-theme": "0.7.3",
20
+ "@kolkrabbi/kol-icons": "0.5.0"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "react": "^18.3.0 || ^19.0.0",
@@ -27,14 +27,14 @@ const WorkshopDefaultSidebar = ({ routes = [], basePath = '/workshop' }) => {
27
27
  <div className="space-y-4">
28
28
  {siblings.length > 1 && (
29
29
  <div>
30
- <div className="shell-sidebar-label">{parentRoute.label}</div>
30
+ <div className="shell-sidebar-label kol-helper-10 text-meta">{parentRoute.label}</div>
31
31
  <nav className="space-y-0">
32
32
  {siblings.map(child => (
33
33
  <Link
34
34
  key={child.id}
35
35
  to={`${basePath}/${child.path}`}
36
- className={`shell-sidebar-link block ${
37
- location.pathname === `${basePath}/${child.path}` ? 'active' : ''
36
+ className={`shell-sidebar-link block kol-mono-14 ${
37
+ location.pathname === `${basePath}/${child.path}` ? 'text-emphasis' : 'text-body'
38
38
  }`}
39
39
  >
40
40
  {child.label}
@@ -46,16 +46,16 @@ const WorkshopDefaultSidebar = ({ routes = [], basePath = '/workshop' }) => {
46
46
 
47
47
  {links && (
48
48
  <div>
49
- <div className="shell-sidebar-label">Repository</div>
49
+ <div className="shell-sidebar-label kol-helper-10 text-meta">Repository</div>
50
50
  <div className="space-y-1">
51
51
  {links.live && (
52
- <a href={links.live} target="_blank" rel="noopener noreferrer" className="shell-sidebar-action">
52
+ <a href={links.live} target="_blank" rel="noopener noreferrer" className="shell-sidebar-action kol-mono-14 text-body">
53
53
  <Icon name="external-link" size={14} />
54
54
  Live site
55
55
  </a>
56
56
  )}
57
57
  {links.repo && (
58
- <a href={links.repo} target="_blank" rel="noopener noreferrer" className="shell-sidebar-action">
58
+ <a href={links.repo} target="_blank" rel="noopener noreferrer" className="shell-sidebar-action kol-mono-14 text-body">
59
59
  <Icon name="external-link" size={14} />
60
60
  GitHub
61
61
  </a>
@@ -65,15 +65,15 @@ const WorkshopDefaultSidebar = ({ routes = [], basePath = '/workshop' }) => {
65
65
  )}
66
66
 
67
67
  <div>
68
- <div className="shell-sidebar-label">Quick actions</div>
68
+ <div className="shell-sidebar-label kol-helper-10 text-meta">Quick actions</div>
69
69
  <div className="space-y-1">
70
- <Link to={basePath} className="shell-sidebar-action">
70
+ <Link to={basePath} className="shell-sidebar-action kol-mono-14 text-body">
71
71
  <Icon name="layout" size={14} />
72
72
  Workshop home
73
73
  </Link>
74
74
  <button
75
75
  type="button"
76
- className="shell-sidebar-action"
76
+ className="shell-sidebar-action kol-mono-14 text-body"
77
77
  onClick={() => navigator.clipboard.writeText(window.location.pathname)}
78
78
  title="Copy page path to clipboard"
79
79
  >
@@ -62,7 +62,7 @@ const DocsSidebar = ({ inventory = [], docHref, basePath, onNavigate, collapsed,
62
62
  return (
63
63
  <div className="space-y-4">
64
64
  <div className="shell-sidebar-toggle shell-sidebar-label" style={{ justifyContent: 'space-between', paddingRight: '4px' }}>
65
- <Link to={docHref()} className="shell-sidebar-label" onClick={(e) => {
65
+ <Link to={docHref()} className="shell-sidebar-label kol-helper-10 text-meta" onClick={(e) => {
66
66
  if (collapsed && onToggle) onToggle()
67
67
  if (onNavigate) onNavigate(e)
68
68
  }}>
@@ -92,7 +92,7 @@ const DocsSidebar = ({ inventory = [], docHref, basePath, onNavigate, collapsed,
92
92
  <div key={major} className="shell-nav-group">
93
93
  <button
94
94
  type="button"
95
- className="shell-nav-group-header w-full text-left"
95
+ className="shell-nav-group-header w-full text-left kol-helper-14 text-body"
96
96
  onClick={() => toggleGroup(major)}
97
97
  >
98
98
  <span className="flex items-center gap-2">
@@ -103,7 +103,7 @@ const DocsSidebar = ({ inventory = [], docHref, basePath, onNavigate, collapsed,
103
103
  />
104
104
  {categoryLabels[major] || 'Other'}
105
105
  </span>
106
- <span className="shell-nav-group-count">({docs.length})</span>
106
+ <span className="kol-helper-12 text-subtle">({docs.length})</span>
107
107
  </button>
108
108
  {!isCollapsed && (
109
109
  <div className="shell-nav-items">
@@ -113,11 +113,11 @@ const DocsSidebar = ({ inventory = [], docHref, basePath, onNavigate, collapsed,
113
113
  <Link
114
114
  key={d.id}
115
115
  to={docHref(d.id)}
116
- className={`shell-nav-item shell-nav-item-doc ${isActive ? 'active' : ''}`}
116
+ className={`shell-nav-item shell-nav-item-doc kol-mono-14 ${isActive ? 'text-emphasis' : 'text-body'}`}
117
117
  onClick={onNavigate}
118
118
  >
119
119
  <span className="shell-nav-item-title">{cleanTitle(d.title, d.id)}</span>
120
- <span className="shell-nav-item-id">{extractDocNumber(d.id)}</span>
120
+ <span className="shell-nav-item-id kol-helper-10 text-subtle">{extractDocNumber(d.id)}</span>
121
121
  </Link>
122
122
  )
123
123
  })}
@@ -1,7 +1,7 @@
1
1
  const join = (...classes) => classes.filter(Boolean).join(' ')
2
2
 
3
3
  const DocsArticle = ({ children, className }) => (
4
- <article className={join('docs-article', className)}>{children}</article>
4
+ <article className={join('docs-article kol-prose', className)}>{children}</article>
5
5
  )
6
6
 
7
7
  export default DocsArticle
@@ -33,18 +33,18 @@ const DocsFrontmatter = ({ metadata, docId }) => {
33
33
 
34
34
  return (
35
35
  <div className="docs-frontmatter">
36
- <p className="shell-sidebar-label">Frontmatter</p>
36
+ <p className="shell-sidebar-label kol-helper-10 text-meta">Frontmatter</p>
37
37
  {fields.map((key) => {
38
38
  const value = metadata[key]
39
39
  const icon = FIELD_ICONS[key]
40
40
 
41
41
  return (
42
42
  <div key={key} className="docs-frontmatter-row">
43
- <span className="docs-frontmatter-key">
43
+ <span className="docs-frontmatter-key kol-helper-12 text-meta">
44
44
  {icon && <Icon name={icon} size={14} />}
45
45
  {key}
46
46
  </span>
47
- <span className="docs-frontmatter-value">
47
+ <span className="kol-mono-12 text-strong">
48
48
  {key === 'tags' && Array.isArray(value) ? (
49
49
  <span className="flex flex-wrap gap-1.5">
50
50
  {value.map((tag) => (
@@ -2,10 +2,10 @@ const join = (...classes) => classes.filter(Boolean).join(' ')
2
2
 
3
3
  const DocsHeader = ({ title, subtitle, meta = [], className }) => (
4
4
  <header className={join('docs-header', className)}>
5
- {title ? <h1 className="docs-title">{title}</h1> : null}
6
- {subtitle ? <p className="docs-subtitle">{subtitle}</p> : null}
5
+ {title ? <h1 className="kol-sans-heading-02 text-strong">{title}</h1> : null}
6
+ {subtitle ? <p className="kol-mono-14 text-meta">{subtitle}</p> : null}
7
7
  {Array.isArray(meta) && meta.length > 0 ? (
8
- <div className="docs-meta">
8
+ <div className="docs-meta kol-helper-10 text-body">
9
9
  {meta.map(({ label, value }) => (
10
10
  <span key={label}>
11
11
  <strong>{label}</strong> {value}
@@ -13,7 +13,7 @@ const SidebarSection = ({ sectionKey, label, collapsedSections, toggleSection, c
13
13
  <div>
14
14
  <button
15
15
  type="button"
16
- className="shell-sidebar-toggle shell-sidebar-label"
16
+ className="shell-sidebar-toggle shell-sidebar-label kol-helper-10 text-meta"
17
17
  onClick={() => toggleSection(sectionKey)}
18
18
  >
19
19
  {label}
@@ -48,23 +48,23 @@ const DocReaderSidebar = ({ toc, allTags, docId, docsIndexHref, componentsHref,
48
48
  <div className="space-y-1">
49
49
  <button
50
50
  type="button"
51
- className="shell-sidebar-action"
51
+ className="shell-sidebar-action kol-mono-14 text-body"
52
52
  onClick={() => navigate(-1)}
53
53
  >
54
54
  <Icon name="arrow-left" size={14} />
55
55
  Back
56
56
  </button>
57
- <Link to={docsIndexHref} className="shell-sidebar-action">
57
+ <Link to={docsIndexHref} className="shell-sidebar-action kol-mono-14 text-body">
58
58
  <Icon name="book-open" size={14} />
59
59
  All documentation
60
60
  </Link>
61
- <Link to={componentsHref} className="shell-sidebar-action">
61
+ <Link to={componentsHref} className="shell-sidebar-action kol-mono-14 text-body">
62
62
  <Icon name="grid" size={14} />
63
63
  View components
64
64
  </Link>
65
65
  <button
66
66
  type="button"
67
- className="shell-sidebar-action"
67
+ className="shell-sidebar-action kol-mono-14 text-body"
68
68
  onClick={() => navigator.clipboard.writeText(docFilePath(docId))}
69
69
  title="Copy file path to clipboard"
70
70
  >
@@ -243,7 +243,7 @@ const DocumentationReader = ({
243
243
  <DocsArticle>
244
244
  <DocsFrontmatter metadata={doc.metadata} docId={docId} />
245
245
  {docTitle && (
246
- <h1 className="docs-title">{docTitle}</h1>
246
+ <h1>{docTitle}</h1>
247
247
  )}
248
248
  {/* Render intro blocks (excluding H1 which is docTitle) */}
249
249
  {introBlocks.filter(b => b.type !== 'heading1').map((block, index) => {
@@ -83,7 +83,7 @@ export const renderInlineTokens = (
83
83
  <Link
84
84
  key={tokenKey}
85
85
  to={tagHref(token.tag)}
86
- className="inline-tag-pill"
86
+ className="inline-tag-pill kol-helper-12 bg-fg-08 text-strong"
87
87
  >
88
88
  #{token.tag}
89
89
  </Link>
@@ -100,18 +100,19 @@ const ShellLayout = ({ routes = [], basePath = '/', brandLogoSrc, brandLogoAlt =
100
100
  navigate(item.href)
101
101
  }
102
102
  }
103
- const brand = (
104
- <Link to={basePath} className="shell-header-logo flex items-center gap-2 text-emphasis">
105
- {brandLogoSrc ? (
106
- <img src={brandLogoSrc} alt={brandLogoAlt} className="h-6 w-auto" />
107
- ) : (
108
- <>
109
- <Asset name="kol-wordmark" title="Kolkrabbi" className="inline-flex [&>svg]:h-5 [&>svg]:w-auto" />
110
- <span className="text-fg-24" aria-hidden="true">/</span>
111
- <Asset name="wordmark-workshop" title="Workshop" className="inline-flex [&>svg]:h-4 [&>svg]:w-auto" />
112
- </>
113
- )}
103
+ const brand = brandLogoSrc ? (
104
+ <Link to={basePath} className="shell-header-logo flex items-center text-emphasis">
105
+ <img src={brandLogoSrc} alt={brandLogoAlt} className="h-6 w-auto" />
114
106
  </Link>
107
+ ) : (
108
+ // Two separate wordmarks: KOLKRABBI holds the logo slot (reserves the 256px
109
+ // nav column at lg+), WORKSHOP falls at the content-column edge. Both h-6.
110
+ <>
111
+ <Link to={basePath} className="shell-header-logo hidden md:flex shrink-0 items-center text-emphasis lg:w-64">
112
+ <Asset name="kol-wordmark" title="Kolkrabbi" className="inline-flex [&>svg]:h-6 [&>svg]:w-auto" />
113
+ </Link>
114
+ <Asset name="wordmark-workshop" title="Workshop" className="inline-flex text-emphasis [&>svg]:h-6 [&>svg]:w-auto" />
115
+ </>
115
116
  )
116
117
  const searchTrigger = (
117
118
  <button
@@ -67,14 +67,14 @@ const ShellSidebar = ({ routes = [], basePath = '/', onNavigate, label = 'Naviga
67
67
  <div className="space-y-4">
68
68
  <div className="shell-sidebar-toggle shell-sidebar-label" style={{ justifyContent: 'space-between', paddingRight: '4px' }}>
69
69
  {labelTo ? (
70
- <Link to={labelTo} className="shell-sidebar-label kol-helper-14" onClick={(e) => {
70
+ <Link to={labelTo} className="shell-sidebar-label kol-helper-10 text-meta" onClick={(e) => {
71
71
  if (navCollapsed && handleToggle) handleToggle()
72
72
  if (onNavigate) onNavigate(e)
73
73
  }}>
74
74
  {label}
75
75
  </Link>
76
76
  ) : (
77
- <button type="button" className="kol-helper-14" onClick={handleToggle}>{label}</button>
77
+ <button type="button" className="kol-helper-10 text-meta" onClick={handleToggle}>{label}</button>
78
78
  )}
79
79
  <button
80
80
  type="button"
@@ -99,7 +99,7 @@ const ShellSidebar = ({ routes = [], basePath = '/', onNavigate, label = 'Naviga
99
99
  <div key={route.id} className="shell-nav-group">
100
100
  <button
101
101
  type="button"
102
- className="shell-nav-group-header w-full text-left kol-helper-14"
102
+ className="shell-nav-group-header w-full text-left kol-helper-14 text-body"
103
103
  onClick={() => handleSectionClick(route)}
104
104
  >
105
105
  <span className="flex items-center gap-2">
@@ -111,7 +111,7 @@ const ShellSidebar = ({ routes = [], basePath = '/', onNavigate, label = 'Naviga
111
111
  {route.label}
112
112
  </span>
113
113
  {route.children?.length > 0 && (
114
- <span className="shell-nav-group-count kol-helper-14">({route.children.length})</span>
114
+ <span className="kol-helper-12 text-subtle">({route.children.length})</span>
115
115
  )}
116
116
  </button>
117
117
 
@@ -125,11 +125,11 @@ const ShellSidebar = ({ routes = [], basePath = '/', onNavigate, label = 'Naviga
125
125
  to={childPath}
126
126
  end
127
127
  className={({ isActive }) =>
128
- `shell-nav-item${isActive ? ' active' : ''}`
128
+ `shell-nav-item kol-mono-14 ${isActive ? 'text-emphasis' : 'text-body'}`
129
129
  }
130
130
  onClick={onNavigate}
131
131
  >
132
- <span className="shell-nav-item-title kol-helper-14">{child.label}</span>
132
+ <span className="shell-nav-item-title">{child.label}</span>
133
133
  </NavLink>
134
134
  )
135
135
  })}
@@ -217,7 +217,7 @@ const TagGraph = ({ docs, activeTag, onTagClick, allDocs, tagHref = defaultTagHr
217
217
 
218
218
  if (graphData.nodes.length === 0) {
219
219
  return (
220
- <div className="text-fg-48 kol-mono-xs py-4 px-2">
220
+ <div className="text-fg-48 kol-mono-12 py-4 px-2">
221
221
  No tags found.
222
222
  </div>
223
223
  )
@@ -232,7 +232,7 @@ const TagGraph = ({ docs, activeTag, onTagClick, allDocs, tagHref = defaultTagHr
232
232
  className="tag-graph-svg"
233
233
  />
234
234
  {hoveredNode && (
235
- <div className="tag-graph-tooltip">
235
+ <div className="tag-graph-tooltip kol-helper-10 text-emphasis">
236
236
  #{hoveredNode.id} ({hoveredNode.count} {hoveredNode.count === 1 ? 'doc' : 'docs'})
237
237
  </div>
238
238
  )}
@@ -1,7 +1,11 @@
1
1
  import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
2
2
  import { useLocation } from 'react-router-dom'
3
3
 
4
- const TagModeContext = createContext(null)
4
+ // Singleton across duplicate module instances: Vite dev can serve this file
5
+ // under two URLs (barrel `?v=` vs direct import), which would otherwise create
6
+ // two distinct contexts and break useTagMode. Stash on globalThis so every
7
+ // instance shares one context object. Harmless in prod (single instance).
8
+ const TagModeContext = (globalThis.__KOL_TAGMODE_CONTEXT__ ||= createContext(null))
5
9
 
6
10
  /**
7
11
  * Default route helpers. These are the ONLY place the old hardcoded workshop
@@ -141,7 +141,7 @@ const TagModeOverlay = () => {
141
141
  </button>
142
142
  ))}
143
143
  {visibleTags.length === 0 && (
144
- <p className="text-fg-48 kol-mono-xs py-4">No tags matching "{search}"</p>
144
+ <p className="text-fg-48 kol-mono-12 py-4">No tags matching "{search}"</p>
145
145
  )}
146
146
  </div>
147
147