@refrakt-md/lumina 0.5.0 → 0.6.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.
Files changed (41) hide show
  1. package/base.css +2 -0
  2. package/contracts/structures.json +43 -0
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +2 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/icons.d.ts +3 -0
  7. package/dist/icons.d.ts.map +1 -0
  8. package/dist/icons.js +86 -0
  9. package/dist/icons.js.map +1 -0
  10. package/index.css +2 -0
  11. package/manifest.json +12 -0
  12. package/package.json +5 -5
  13. package/styles/elements/code.css +40 -0
  14. package/styles/layouts/blog.css +15 -3
  15. package/styles/layouts/default.css +15 -3
  16. package/styles/layouts/docs.css +64 -6
  17. package/styles/layouts/mobile.css +6 -4
  18. package/styles/layouts/on-this-page.css +48 -0
  19. package/styles/runes/feature.css +10 -0
  20. package/styles/runes/palette.css +5 -0
  21. package/styles/runes/preview.css +2 -1
  22. package/styles/runes/spacing.css +5 -0
  23. package/styles/runes/steps.css +14 -2
  24. package/styles/runes/symbol.css +9 -11
  25. package/styles/runes/typography.css +13 -0
  26. package/svelte/components/OnThisPage.svelte +76 -0
  27. package/svelte/index.ts +24 -7
  28. package/svelte/layouts/DocsLayout.svelte +17 -3
  29. package/svelte/manifest.json +3 -20
  30. package/dist/lib/engine.d.ts +0 -13
  31. package/dist/lib/engine.d.ts.map +0 -1
  32. package/dist/lib/engine.js +0 -218
  33. package/dist/lib/engine.js.map +0 -1
  34. package/dist/lib/helpers.d.ts +0 -14
  35. package/dist/lib/helpers.d.ts.map +0 -1
  36. package/dist/lib/helpers.js +0 -26
  37. package/dist/lib/helpers.js.map +0 -1
  38. package/dist/lib/types.d.ts +0 -74
  39. package/dist/lib/types.d.ts.map +0 -1
  40. package/dist/lib/types.js +0 -2
  41. package/dist/lib/types.js.map +0 -1
@@ -14,7 +14,7 @@
14
14
  display: flex;
15
15
  align-items: center;
16
16
  justify-content: space-between;
17
- padding: 0.875rem 1.5rem;
17
+ padding: 1.125rem 1.5rem;
18
18
  }
19
19
  .rf-docs-header p {
20
20
  margin: 0;
@@ -28,9 +28,16 @@
28
28
  .rf-docs-header a:hover {
29
29
  text-decoration: none;
30
30
  }
31
+ .rf-docs-header p:first-child a {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: 0.5rem;
35
+ font-weight: 600;
36
+ font-size: 1.2rem;
37
+ letter-spacing: -0.02em;
38
+ }
31
39
  .rf-docs-header img {
32
- display: block;
33
- height: 1.5rem;
40
+ height: 2rem;
34
41
  width: auto;
35
42
  }
36
43
  .rf-docs-header__inner p ~ p {
@@ -43,12 +50,17 @@
43
50
  .rf-docs-header__inner p ~ p a:hover {
44
51
  color: var(--rf-color-text);
45
52
  }
53
+ .rf-docs-header__inner p ~ p a svg {
54
+ width: 1.1rem;
55
+ height: 1.1rem;
56
+ vertical-align: -0.15em;
57
+ }
46
58
 
47
59
  /* ---- Fixed sidebar ---- */
48
60
  .rf-docs-sidebar {
49
61
  position: fixed;
50
62
  left: 0;
51
- top: 3.375rem;
63
+ top: 4.125rem;
52
64
  bottom: 0;
53
65
  width: 240px;
54
66
  overflow-y: auto;
@@ -63,7 +75,7 @@
63
75
 
64
76
  /* ---- Content area ---- */
65
77
  .rf-docs-content {
66
- padding-top: 5rem;
78
+ padding-top: 5.75rem;
67
79
  padding-bottom: 4rem;
68
80
  container-type: inline-size;
69
81
  }
@@ -76,6 +88,41 @@
76
88
  padding: 0 2.5rem;
77
89
  --rf-content-padding: 2.5rem;
78
90
  }
91
+ .rf-docs-content__inner--has-toc {
92
+ display: flex;
93
+ gap: 0;
94
+ max-width: calc(60rem + 220px);
95
+ }
96
+ .rf-docs-content__inner--has-toc .rf-docs-content__body {
97
+ flex: 1;
98
+ min-width: 0;
99
+ }
100
+
101
+ /* ---- "On this page" sidebar ---- */
102
+ .rf-docs-toc {
103
+ width: 220px;
104
+ flex-shrink: 0;
105
+ position: sticky;
106
+ top: 5.75rem;
107
+ align-self: flex-start;
108
+ max-height: calc(100vh - 5.75rem);
109
+ overflow-y: auto;
110
+ padding: 0 1.25rem;
111
+ }
112
+ .rf-docs-toc::-webkit-scrollbar {
113
+ width: 0;
114
+ }
115
+
116
+ /* ---- Smooth scroll with fixed-header offset ---- */
117
+ html:has(.rf-docs-header) {
118
+ scroll-behavior: smooth;
119
+ scroll-padding-top: 5rem;
120
+ }
121
+ @media (prefers-reduced-motion: reduce) {
122
+ html:has(.rf-docs-header) {
123
+ scroll-behavior: auto;
124
+ }
125
+ }
79
126
 
80
127
  /* ---- Mobile toolbar (hidden on desktop) ---- */
81
128
  .rf-docs-toolbar {
@@ -118,6 +165,17 @@
118
165
  font-weight: 500;
119
166
  }
120
167
 
168
+ /* ---- Hide TOC on medium screens ---- */
169
+ @media (max-width: 1100px) {
170
+ .rf-docs-toc {
171
+ display: none;
172
+ }
173
+ .rf-docs-content__inner--has-toc {
174
+ display: block;
175
+ max-width: 60rem;
176
+ }
177
+ }
178
+
121
179
  /* ---- Mobile overrides ---- */
122
180
  @media (max-width: 768px) {
123
181
  .rf-docs-header {
@@ -130,7 +188,7 @@
130
188
  display: flex;
131
189
  }
132
190
  .rf-mobile-panel--nav {
133
- top: 5.5rem;
191
+ top: 6.25rem;
134
192
  }
135
193
  .rf-docs-sidebar {
136
194
  display: none;
@@ -28,7 +28,7 @@
28
28
  display: flex;
29
29
  align-items: center;
30
30
  justify-content: space-between;
31
- padding: 0.875rem 1.5rem;
31
+ padding: 1.125rem 1.5rem;
32
32
  border-bottom: 1px solid var(--rf-color-border);
33
33
  }
34
34
  .rf-mobile-panel__title {
@@ -74,11 +74,13 @@
74
74
  padding: 1rem 1.5rem;
75
75
  }
76
76
 
77
+ /* Panel opens via data-open attribute (set by mobile-menu behavior) */
78
+ .rf-mobile-panel[data-open] {
79
+ display: block;
80
+ }
81
+
77
82
  @media (max-width: 768px) {
78
83
  .rf-mobile-menu-btn {
79
84
  display: block;
80
85
  }
81
- .rf-mobile-panel {
82
- display: block;
83
- }
84
86
  }
@@ -0,0 +1,48 @@
1
+ /* On This Page — TOC sidebar navigation */
2
+
3
+ .rf-on-this-page {
4
+ font-size: 0.8rem;
5
+ }
6
+
7
+ .rf-on-this-page__title {
8
+ font-size: 0.7rem;
9
+ font-weight: 600;
10
+ text-transform: uppercase;
11
+ letter-spacing: 0.05em;
12
+ color: var(--rf-color-muted, #64748b);
13
+ margin: 0 0 0.75rem;
14
+ }
15
+
16
+ .rf-on-this-page__list {
17
+ list-style: none;
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+
22
+ .rf-on-this-page__item {
23
+ border-left: 2px solid transparent;
24
+ }
25
+
26
+ .rf-on-this-page__item[data-level="3"] {
27
+ padding-left: 0.75rem;
28
+ }
29
+
30
+ .rf-on-this-page__item a {
31
+ display: block;
32
+ padding: 0.25rem 0.75rem;
33
+ color: var(--rf-color-muted, #64748b);
34
+ text-decoration: none;
35
+ line-height: 1.4;
36
+ }
37
+
38
+ .rf-on-this-page__item a:hover {
39
+ color: var(--rf-color-text, #1a1a2e);
40
+ }
41
+
42
+ .rf-on-this-page__item[data-active] {
43
+ border-left-color: var(--rf-color-primary, #0ea5e9);
44
+ }
45
+
46
+ .rf-on-this-page__item[data-active] a {
47
+ color: var(--rf-color-primary, #0ea5e9);
48
+ }
@@ -51,6 +51,16 @@
51
51
  letter-spacing: -0.01em;
52
52
  color: var(--rf-color-text);
53
53
  }
54
+ .rf-feature-definition dt:has(> .rf-icon) {
55
+ display: flex;
56
+ flex-direction: column;
57
+ }
58
+ .rf-feature-definition dt > .rf-icon {
59
+ width: 1.5rem;
60
+ height: 1.5rem;
61
+ margin-bottom: 0.5rem;
62
+ color: var(--rf-color-accent);
63
+ }
54
64
  .rf-feature-definition dd {
55
65
  margin: 0;
56
66
  color: var(--rf-color-muted);
@@ -84,3 +84,8 @@
84
84
  margin-top: 0.25rem;
85
85
  font-size: 0.8rem;
86
86
  }
87
+
88
+ /* Context-aware: no outer margin when inside design-context */
89
+ .rf-palette--in-design-context {
90
+ margin: 0;
91
+ }
@@ -139,7 +139,8 @@
139
139
  margin: 0;
140
140
  border-radius: 0;
141
141
  }
142
- .rf-preview__source .rf-codeblock {
142
+ .rf-preview__source .rf-codeblock,
143
+ .rf-preview__source .rf-code-wrapper {
143
144
  border-radius: 0;
144
145
  }
145
146
 
@@ -103,3 +103,8 @@
103
103
  font-weight: 500;
104
104
  font-size: 0.8rem;
105
105
  }
106
+
107
+ /* Context-aware: no outer margin when inside design-context */
108
+ .rf-spacing--in-design-context {
109
+ margin: 0;
110
+ }
@@ -30,22 +30,34 @@
30
30
  top: 0;
31
31
  width: 1.875rem;
32
32
  height: 1.875rem;
33
- background: var(--rf-color-primary);
34
- color: white;
33
+ background: var(--rf-color-border);
34
+ color: var(--rf-color-muted);
35
35
  border-radius: var(--rf-radius-full);
36
36
  display: flex;
37
37
  align-items: center;
38
38
  justify-content: center;
39
39
  font-weight: 650;
40
40
  font-size: 0.8rem;
41
+ font-variant-numeric: tabular-nums;
41
42
  box-shadow: 0 0 0 4px var(--rf-color-bg);
42
43
  }
44
+ .rf-step h1,
45
+ .rf-step h2,
43
46
  .rf-step h3,
47
+ .rf-step h4,
48
+ .rf-step h5,
49
+ .rf-step h6,
44
50
  .rf-step strong {
45
51
  display: block;
46
52
  margin-top: 0;
47
53
  margin-bottom: 0.375rem;
48
54
  }
55
+ .rf-step pre {
56
+ margin-top: 0;
57
+ }
58
+ .rf-step > p:first-of-type {
59
+ margin-top: 0;
60
+ }
49
61
  .rf-step p {
50
62
  color: var(--rf-color-muted);
51
63
  font-size: 0.925rem;
@@ -1,17 +1,12 @@
1
1
  /* Symbol */
2
2
  .rf-symbol {
3
- border: 1px solid var(--rf-color-border);
4
- border-radius: var(--rf-radius-lg);
5
3
  margin: 1.5rem 0;
6
- overflow: hidden;
7
4
  }
8
5
  .rf-symbol__header {
9
6
  display: flex;
10
7
  align-items: center;
11
8
  gap: 0.5rem;
12
- padding: 0.75rem 1.25rem;
13
- background: var(--rf-color-surface-hover);
14
- border-bottom: 1px solid var(--rf-color-border);
9
+ padding: 0.5rem 0;
15
10
  flex-wrap: wrap;
16
11
  }
17
12
  .rf-symbol__kind-badge {
@@ -56,7 +51,7 @@
56
51
  color: var(--rf-color-primary);
57
52
  }
58
53
  .rf-symbol__body {
59
- padding: 1.25rem;
54
+ padding: 0;
60
55
  }
61
56
  .rf-symbol__body > header {
62
57
  margin-bottom: 1rem;
@@ -100,6 +95,9 @@
100
95
  border-radius: 0 var(--rf-radius-sm) var(--rf-radius-sm) 0;
101
96
  font-size: 0.9375rem;
102
97
  }
98
+ .rf-symbol__body blockquote::before {
99
+ content: none;
100
+ }
103
101
 
104
102
  /* Deprecated symbol */
105
103
  [data-deprecated] .rf-symbol__body > header h2,
@@ -132,10 +130,7 @@
132
130
  /* SymbolMember */
133
131
  .rf-symbol-member {
134
132
  margin: 1.25rem 0;
135
- padding: 1rem;
136
- border: 1px solid var(--rf-color-border);
137
- border-radius: var(--rf-radius-md);
138
- background: var(--rf-color-surface);
133
+ padding: 0;
139
134
  }
140
135
  .rf-symbol-member h4 {
141
136
  margin: 0 0 0.5rem;
@@ -162,3 +157,6 @@
162
157
  border-radius: 0 var(--rf-radius-sm) var(--rf-radius-sm) 0;
163
158
  font-size: 0.875rem;
164
159
  }
160
+ .rf-symbol-member blockquote::before {
161
+ content: none;
162
+ }
@@ -89,3 +89,16 @@
89
89
  word-break: break-all;
90
90
  color: var(--rf-color-text-muted, #6b7280);
91
91
  }
92
+
93
+ /* Context-aware: no outer border when inside design-context (parent already has one) */
94
+ .rf-typography--in-design-context {
95
+ margin: 0;
96
+ }
97
+ .rf-typography--in-design-context .rf-typography__specimen {
98
+ border: none;
99
+ border-radius: 0;
100
+ padding: 0;
101
+ }
102
+ .rf-typography--in-design-context .rf-typography__specimen + .rf-typography__specimen {
103
+ padding-top: 1.5rem;
104
+ }
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ interface Heading {
3
+ level: number;
4
+ text: string;
5
+ id: string;
6
+ }
7
+
8
+ interface Props {
9
+ headings: Heading[];
10
+ }
11
+
12
+ let { headings }: Props = $props();
13
+
14
+ // Filter to h2 and h3 only
15
+ const filtered = $derived(headings.filter(h => h.level >= 2 && h.level <= 3));
16
+ </script>
17
+
18
+ <nav class="rf-on-this-page" data-scrollspy>
19
+ <p class="rf-on-this-page__title">On this page</p>
20
+ <ul class="rf-on-this-page__list">
21
+ {#each filtered as heading}
22
+ <li class="rf-on-this-page__item" data-level={heading.level}>
23
+ <a href="#{heading.id}">{heading.text}</a>
24
+ </li>
25
+ {/each}
26
+ </ul>
27
+ </nav>
28
+
29
+ <style>
30
+ .rf-on-this-page {
31
+ font-size: 0.8rem;
32
+ }
33
+
34
+ .rf-on-this-page__title {
35
+ font-size: 0.7rem;
36
+ font-weight: 600;
37
+ text-transform: uppercase;
38
+ letter-spacing: 0.05em;
39
+ color: var(--rf-color-muted, #64748b);
40
+ margin: 0 0 0.75rem;
41
+ }
42
+
43
+ .rf-on-this-page__list {
44
+ list-style: none;
45
+ margin: 0;
46
+ padding: 0;
47
+ }
48
+
49
+ .rf-on-this-page__item {
50
+ border-left: 2px solid transparent;
51
+ }
52
+
53
+ .rf-on-this-page__item[data-level="3"] {
54
+ padding-left: 0.75rem;
55
+ }
56
+
57
+ .rf-on-this-page__item a {
58
+ display: block;
59
+ padding: 0.25rem 0.75rem;
60
+ color: var(--rf-color-muted, #64748b);
61
+ text-decoration: none;
62
+ line-height: 1.4;
63
+ }
64
+
65
+ .rf-on-this-page__item a:hover {
66
+ color: var(--rf-color-text, #1a1a2e);
67
+ }
68
+
69
+ .rf-on-this-page__item[data-active] {
70
+ border-left-color: var(--rf-color-primary, #0ea5e9);
71
+ }
72
+
73
+ .rf-on-this-page__item[data-active] a {
74
+ color: var(--rf-color-primary, #0ea5e9);
75
+ }
76
+ </style>
package/svelte/index.ts CHANGED
@@ -1,18 +1,35 @@
1
1
  import type { SvelteTheme } from '@refrakt-md/svelte';
2
- import manifest from './manifest.json';
2
+ import adapterManifest from './manifest.json';
3
3
  import { registry } from './registry.js';
4
4
  import { elements } from './elements.js';
5
- import DocsLayout from './layouts/DocsLayout.svelte';
6
- import DefaultLayout from './layouts/DefaultLayout.svelte';
7
- import BlogLayout from './layouts/BlogLayout.svelte';
5
+ import { defaultLayout, docsLayout, blogArticleLayout } from '@refrakt-md/theme-base';
8
6
 
9
- /** Re-export the raw manifest for server-side use (no Svelte imports) */
10
- export { default as manifest } from './manifest.json';
7
+ // Layout region metadata from the base theme manifest (packages/lumina/manifest.json).
8
+ // Merged with adapter manifest component paths to produce full LayoutDefinitions.
9
+ const layoutRegions: Record<string, { regions: string[]; requiredRegions?: string[] }> = {
10
+ default: { regions: ['header', 'footer'] },
11
+ docs: { regions: ['header', 'nav', 'sidebar', 'footer'], requiredRegions: ['nav'] },
12
+ 'blog-article': { regions: ['header', 'sidebar', 'footer'] },
13
+ };
14
+
15
+ const layouts: Record<string, any> = {};
16
+ for (const [name, adapter] of Object.entries(adapterManifest.layouts)) {
17
+ layouts[name] = { ...layoutRegions[name], ...adapter };
18
+ }
19
+
20
+ const manifest = { ...adapterManifest, layouts };
21
+
22
+ /** Re-export the merged manifest for server-side use (no Svelte imports) */
23
+ export { manifest };
11
24
 
12
25
  /** The structured theme object consumed by ThemeShell */
13
26
  export const theme: SvelteTheme = {
14
27
  manifest: manifest as any,
15
- layouts: { default: DefaultLayout, docs: DocsLayout, blog: BlogLayout },
28
+ layouts: {
29
+ default: defaultLayout,
30
+ docs: docsLayout,
31
+ 'blog-article': blogArticleLayout,
32
+ },
16
33
  components: registry,
17
34
  elements,
18
35
  };
@@ -1,17 +1,24 @@
1
1
  <script lang="ts">
2
2
  import { Renderer } from '@refrakt-md/svelte';
3
+ import OnThisPage from '../components/OnThisPage.svelte';
3
4
 
4
- let { title, regions, renderable, url, pages }: {
5
+ let { title, regions, renderable, url, pages, headings, frontmatter }: {
5
6
  title: string;
6
7
  description: string;
7
8
  regions: Record<string, { name: string; mode: string; content: any[] }>;
8
9
  renderable: any;
9
10
  url: string;
10
11
  pages: Array<{ url: string; title: string; draft: boolean }>;
12
+ headings?: Array<{ level: number; text: string; id: string }>;
13
+ frontmatter?: Record<string, unknown>;
11
14
  } = $props();
12
15
 
13
16
  const hasNav = $derived(!!regions.nav);
14
17
 
18
+ // Show TOC when 2+ qualifying headings (h2/h3) and not opted out via frontmatter
19
+ const tocHeadings = $derived((headings ?? []).filter(h => h.level >= 2 && h.level <= 3));
20
+ const showToc = $derived(tocHeadings.length >= 2 && frontmatter?.toc !== false);
21
+
15
22
  // Mobile panel state
16
23
  let menuOpen = $state(false);
17
24
  let navOpen = $state(false);
@@ -149,7 +156,14 @@
149
156
  {/if}
150
157
 
151
158
  <main class="rf-docs-content" class:rf-docs-content--has-nav={hasNav}>
152
- <div class="rf-docs-content__inner">
153
- <Renderer node={renderable} />
159
+ <div class="rf-docs-content__inner" class:rf-docs-content__inner--has-toc={showToc}>
160
+ <div class="rf-docs-content__body">
161
+ <Renderer node={renderable} />
162
+ </div>
163
+ {#if showToc}
164
+ <aside class="rf-docs-toc">
165
+ <OnThisPage headings={tocHeadings} />
166
+ </aside>
167
+ {/if}
154
168
  </div>
155
169
  </main>
@@ -6,28 +6,11 @@
6
6
  "designTokens": "tokens.css",
7
7
 
8
8
  "layouts": {
9
- "default": {
10
- "component": "layouts/DefaultLayout.svelte",
11
- "regions": ["header", "footer"]
12
- },
13
- "docs": {
14
- "component": "layouts/DocsLayout.svelte",
15
- "regions": ["header", "nav", "sidebar", "footer"],
16
- "requiredRegions": ["nav"]
17
- },
18
- "blog": {
19
- "component": "layouts/BlogLayout.svelte",
20
- "regions": ["header", "sidebar", "footer"]
21
- }
9
+ "default": { "component": "layouts/DefaultLayout.svelte" },
10
+ "docs": { "component": "layouts/DocsLayout.svelte" },
11
+ "blog": { "component": "layouts/BlogLayout.svelte" }
22
12
  },
23
13
 
24
- "routeRules": [
25
- { "pattern": "docs/**", "layout": "docs" },
26
- { "pattern": "blog", "layout": "blog" },
27
- { "pattern": "blog/**", "layout": "blog" },
28
- { "pattern": "**", "layout": "default" }
29
- ],
30
-
31
14
  "components": {
32
15
  "Hint": { "component": "components/Hint.svelte" },
33
16
  "CallToAction": { "component": "components/CallToAction.svelte" },
@@ -1,13 +0,0 @@
1
- import type { ThemeConfig, RendererNode } from './types.js';
2
- /**
3
- * Create an identity transform function from a theme configuration.
4
- *
5
- * The returned function walks the serialized tag tree and enhances it:
6
- * - Adds BEM classes based on the rune config
7
- * - Reads and consumes meta tags for variant info
8
- * - Auto-labels children by tag name (e.g., summary → data-name="header")
9
- * - Injects structural elements (headers, icons, titles) from config
10
- * - Recurses into children for nested runes
11
- */
12
- export declare function createTransform(config: ThemeConfig): (tree: RendererNode) => RendererNode;
13
- //# sourceMappingURL=engine.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/lib/engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAA6C,YAAY,EAAE,MAAM,YAAY,CAAC;AAkBvG;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,UAGjB,YAAY,KAAG,YAAY,CAgB5D"}