@graphlearning/shell 0.2.0 → 0.4.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/README.md +26 -0
- package/dist/ConceptApp.d.ts +4 -1
- package/dist/CourseIndex.d.ts +4 -1
- package/dist/SectionView.d.ts +4 -1
- package/dist/SiteHeader.d.ts +10 -0
- package/dist/ThemeToggle.d.ts +5 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +321 -206
- package/dist/index.js.map +1 -1
- package/dist/styles.css +254 -42
- package/dist/useTheme.d.ts +17 -0
- package/package.json +7 -7
package/dist/styles.css
CHANGED
|
@@ -13,6 +13,21 @@
|
|
|
13
13
|
:root {
|
|
14
14
|
--bg: #1a1d23; /* Zed-slate (matches graphl-studio/aws) */
|
|
15
15
|
--fg: #eceef2;
|
|
16
|
+
/* The foreground's RGB CHANNELS, so every `rgba(var(--ink-rgb), a)` in this file flips with the
|
|
17
|
+
theme. There were ~20 literal rgba(255,255,255,a) values here — all of them "ink at some alpha"
|
|
18
|
+
— and one token converts every one by substitution rather than needing eleven alpha tokens.
|
|
19
|
+
Dark keeps the exact values it always had. */
|
|
20
|
+
--ink-rgb: 255, 255, 255;
|
|
21
|
+
/* Surfaces lifted OFF --bg: the slide panel, and a catalog card's hover. Literals before 0.4.0. */
|
|
22
|
+
--surface-lift: #2f333d;
|
|
23
|
+
--surface-lift-2: #22262e;
|
|
24
|
+
--slide-ink: #c8cdd6; /* the slide body's text colour */
|
|
25
|
+
--foot-dim: #5b616f; /* the capture footer's section counter */
|
|
26
|
+
--foot-dimmer: #4b515e; /* the catalog chevron at rest */
|
|
27
|
+
/* A SCRIM, not ink: the page colour at 90%, sat behind the floating controls so they stay legible
|
|
28
|
+
over whatever the scene puts under them. It has to follow --bg, so it cannot be alpha over
|
|
29
|
+
--ink-rgb like the rest — on light, ink-at-90% would be a near-black button. */
|
|
30
|
+
--scrim: rgba(26, 29, 35, 0.9);
|
|
16
31
|
/* --- the three tokens a CONTENT REPO owns -------------------------------------------------
|
|
17
32
|
These are NEUTRAL DEFAULTS. Each repo overrides them in its own theme.css, imported AFTER
|
|
18
33
|
this stylesheet so the cascade lets the repo win. They are the entire per-repo design
|
|
@@ -26,6 +41,65 @@
|
|
|
26
41
|
--brand-hover: #a8adb8;
|
|
27
42
|
--accent-2: #c8cdd6;
|
|
28
43
|
--font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
44
|
+
|
|
45
|
+
/* --- site-chrome tokens (SiteHeader) ------------------------------------------------------
|
|
46
|
+
The same names and the same values ui-graphl declares, so the bar is identical on the catalog
|
|
47
|
+
and inside every app. Do NOT rename them: when light mode arrives it is purely additive here
|
|
48
|
+
— a light :root plus prefers-color-scheme and [data-theme] blocks, same names — and the
|
|
49
|
+
header's own CSS never has to change again. --text aliases the shell's existing --fg (same
|
|
50
|
+
value) rather than duplicating the colour. These are PLATFORM tokens: the bar stays
|
|
51
|
+
platform-blue on every site, deliberately NOT --brand, because looking the same everywhere is
|
|
52
|
+
the whole reason it exists. */
|
|
53
|
+
--text: var(--fg);
|
|
54
|
+
--muted: #8b909c;
|
|
55
|
+
--accent: #5b8cff;
|
|
56
|
+
--accent-soft: rgba(91, 140, 255, 0.14); /* the current nav item */
|
|
57
|
+
--line: rgba(var(--ink-rgb), 0.09);
|
|
58
|
+
--header-bg: rgba(var(--ink-rgb), 0.014);
|
|
59
|
+
--hover: rgba(var(--ink-rgb), 0.05);
|
|
60
|
+
--site-h: 60px; /* the bar's row height (its 1px rule is extra) — .idx subtracts it from 100vh */
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* LIGHT — same token NAMES, different values, exactly as the note above anticipated. Set by
|
|
64
|
+
ConceptApp on <html> from its `theme` prop; nothing selects on it but this block.
|
|
65
|
+
*
|
|
66
|
+
* WHY AN EXPLICIT ATTRIBUTE AND NOT prefers-color-scheme: a deck is a VIDEO. The recorded frame
|
|
67
|
+
* cannot depend on the machine that renders it, or the same slug captures differently on two
|
|
68
|
+
* laptops. The repo chooses the theme at build time and the recorder inherits it.
|
|
69
|
+
*
|
|
70
|
+
* --line / --header-bg / --hover / --idx-line are NOT redeclared: they are alpha over --ink-rgb,
|
|
71
|
+
* so flipping the channels flips them too. That is the whole reason --ink-rgb exists.
|
|
72
|
+
*
|
|
73
|
+
* NOT TOUCHED HERE: --brand, --brand-hover and --accent-2. Those belong to the content repo
|
|
74
|
+
* (theme.css, loaded after this file), and a brand tuned for glow on slate can be unreadable on
|
|
75
|
+
* off-white — python's is a mid-blue that drops to ~2.5:1 on #f7f8fa. A repo going light must
|
|
76
|
+
* re-pick its three tokens; the shell cannot do it for them without taking ownership of the one
|
|
77
|
+
* surface it deliberately leaves alone. */
|
|
78
|
+
:root[data-theme='light'] {
|
|
79
|
+
--bg: #f7f8fa; /* identical to ui-flow's light surface, so the scene pane and the page agree */
|
|
80
|
+
--fg: #1a1d23;
|
|
81
|
+
--ink-rgb: 26, 29, 35;
|
|
82
|
+
/* On dark the slide panel is LIFTED off the canvas; on light the same relationship means going
|
|
83
|
+
brighter than the page, not darker — so white, not a grey. */
|
|
84
|
+
--surface-lift: #ffffff;
|
|
85
|
+
--surface-lift-2: #eef1f5;
|
|
86
|
+
--slide-ink: #333a45;
|
|
87
|
+
--foot-dim: #7a8493;
|
|
88
|
+
--foot-dimmer: #98a2b1;
|
|
89
|
+
--muted: #5a6675;
|
|
90
|
+
/* Platform blue darkened until it carries text contrast on off-white; #5b8cff is tuned to glow
|
|
91
|
+
on slate and reads washed out here. Still platform blue, still not --brand. */
|
|
92
|
+
--accent: #2f6fe0;
|
|
93
|
+
--accent-soft: rgba(47, 111, 224, 0.12);
|
|
94
|
+
--scrim: rgba(247, 248, 250, 0.9);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Set for the single frame a theme swap lands in, so the whole page changes at once instead of the
|
|
98
|
+
ground flipping while everything with a hover transition cross-fades after it. Ported from
|
|
99
|
+
ui-graphl's styles.css — the site bar exists three times and they only stay one design if every
|
|
100
|
+
change lands in all of them. */
|
|
101
|
+
.theme-swap * {
|
|
102
|
+
transition: none !important;
|
|
29
103
|
}
|
|
30
104
|
|
|
31
105
|
html,
|
|
@@ -84,8 +158,8 @@ body {
|
|
|
84
158
|
justify-content: center;
|
|
85
159
|
overflow: auto;
|
|
86
160
|
box-sizing: border-box;
|
|
87
|
-
background:
|
|
88
|
-
border-left: 1px solid rgba(
|
|
161
|
+
background: var(--surface-lift); /* lifted slide surface, off the #1a1d23 scene canvas */
|
|
162
|
+
border-left: 1px solid rgba(var(--ink-rgb), 0.09);
|
|
89
163
|
transform: translateX(100%);
|
|
90
164
|
transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
|
|
91
165
|
z-index: 10;
|
|
@@ -101,9 +175,9 @@ body {
|
|
|
101
175
|
width: 36px;
|
|
102
176
|
height: 36px;
|
|
103
177
|
border-radius: 10px;
|
|
104
|
-
border: 1px solid rgba(
|
|
105
|
-
background:
|
|
106
|
-
color: rgba(
|
|
178
|
+
border: 1px solid rgba(var(--ink-rgb), 0.12);
|
|
179
|
+
background: var(--scrim);
|
|
180
|
+
color: rgba(var(--ink-rgb), 0.82);
|
|
107
181
|
font-size: 19px;
|
|
108
182
|
line-height: 1;
|
|
109
183
|
cursor: pointer;
|
|
@@ -145,9 +219,9 @@ body {
|
|
|
145
219
|
width: 36px;
|
|
146
220
|
height: 36px;
|
|
147
221
|
border-radius: 10px;
|
|
148
|
-
border: 1px solid rgba(
|
|
149
|
-
background:
|
|
150
|
-
color: rgba(
|
|
222
|
+
border: 1px solid rgba(var(--ink-rgb), 0.12);
|
|
223
|
+
background: var(--scrim);
|
|
224
|
+
color: rgba(var(--ink-rgb), 0.82);
|
|
151
225
|
cursor: pointer;
|
|
152
226
|
pointer-events: auto; /* the footer container is pointer-events:none; re-enable the buttons */
|
|
153
227
|
transition: color 0.15s, border-color 0.15s;
|
|
@@ -186,7 +260,7 @@ body {
|
|
|
186
260
|
padding: 4px 6px;
|
|
187
261
|
border: none;
|
|
188
262
|
background: transparent;
|
|
189
|
-
color: rgba(
|
|
263
|
+
color: rgba(var(--ink-rgb), 0.82);
|
|
190
264
|
font-family: var(--font-sans);
|
|
191
265
|
font-size: 17px;
|
|
192
266
|
font-weight: 700;
|
|
@@ -258,11 +332,11 @@ body {
|
|
|
258
332
|
font-size: 1.55vh;
|
|
259
333
|
font-weight: 700;
|
|
260
334
|
letter-spacing: -0.01em;
|
|
261
|
-
color: rgba(
|
|
335
|
+
color: rgba(var(--ink-rgb), 0.9);
|
|
262
336
|
}
|
|
263
337
|
.reel-foot__count {
|
|
264
338
|
font-size: 1.4vh;
|
|
265
|
-
color: rgba(
|
|
339
|
+
color: rgba(var(--ink-rgb), 0.5);
|
|
266
340
|
font-variant-numeric: tabular-nums;
|
|
267
341
|
letter-spacing: 0.04em;
|
|
268
342
|
}
|
|
@@ -338,7 +412,7 @@ body {
|
|
|
338
412
|
.slide-panel__scaler {
|
|
339
413
|
box-sizing: border-box;
|
|
340
414
|
padding: 0 40px; /* portrait: trimmed side padding to fill the phone width (landscape restores 60) */
|
|
341
|
-
color:
|
|
415
|
+
color: var(--slide-ink);
|
|
342
416
|
--fs-body: 31px; /* portrait/reels: larger so the slide fills the phone (landscape restores 26) */
|
|
343
417
|
--fs-title: calc(var(--fs-body) * 1.73);
|
|
344
418
|
--fs-heading: calc(var(--fs-body) * 1.2);
|
|
@@ -391,56 +465,185 @@ body {
|
|
|
391
465
|
content: '—';
|
|
392
466
|
position: absolute;
|
|
393
467
|
left: 0;
|
|
394
|
-
color: rgba(
|
|
468
|
+
color: rgba(var(--ink-rgb), 0.28);
|
|
395
469
|
}
|
|
396
470
|
.slide-panel__scaler strong {
|
|
397
|
-
color: rgba(
|
|
471
|
+
color: rgba(var(--ink-rgb), 0.95);
|
|
398
472
|
font-weight: 700;
|
|
399
473
|
}
|
|
400
474
|
.slide-panel__scaler em {
|
|
401
475
|
font-style: italic;
|
|
402
|
-
color: rgba(
|
|
476
|
+
color: rgba(var(--ink-rgb), 0.85);
|
|
403
477
|
}
|
|
404
478
|
.slide-panel__scaler code {
|
|
405
479
|
font-family: 'IBM Plex Mono', ui-monospace, monospace;
|
|
406
480
|
font-size: var(--fs-code);
|
|
407
481
|
padding: 2px 7px;
|
|
408
482
|
border-radius: 5px;
|
|
409
|
-
background: rgba(
|
|
410
|
-
color: rgba(
|
|
483
|
+
background: rgba(var(--ink-rgb), 0.08);
|
|
484
|
+
color: rgba(var(--ink-rgb), 0.9);
|
|
411
485
|
}
|
|
412
486
|
|
|
413
487
|
/* --- Course catalog / landing page (CourseIndex) ---------------------------- */
|
|
414
488
|
/* The index is a scrollable page (unlike the fixed stage): the catalog of courses, each course a
|
|
415
489
|
card listing its ordered sections. Python-blue (--idx-accent) is the one accent. */
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
490
|
+
/* ── Site chrome (SiteHeader) ──────────────────────────────────────────────────────────────────
|
|
491
|
+
Ported from ui-graphl/styles.css, class for class. ui-graphl is buildless vanilla and this is a
|
|
492
|
+
React package, so one shared component is impossible — but one shared DESIGN is not, and it only
|
|
493
|
+
survives if neither copy drifts. Change this, change that. The single deliberate deviation is
|
|
494
|
+
--site-h: over there the 60px row height is a literal, here .idx has to subtract it.
|
|
495
|
+
|
|
496
|
+
Traditional chrome: brand on the left, section nav on the right, full-bleed rule underneath. The
|
|
497
|
+
inner row matches the catalog's own max-width so the brand lines up with the cards below it. */
|
|
498
|
+
.site {
|
|
499
|
+
border-bottom: 1px solid var(--line);
|
|
500
|
+
background: var(--header-bg);
|
|
501
|
+
}
|
|
502
|
+
.site__inner {
|
|
421
503
|
max-width: 940px;
|
|
422
504
|
margin: 0 auto;
|
|
423
|
-
|
|
505
|
+
/* Explicit, and NOT optional: ui-graphl has a global `* { box-sizing: border-box }` and this
|
|
506
|
+
stylesheet deliberately does not (a global reset here would move the section view, which is
|
|
507
|
+
burned into video). Without it the bar's column is 940 + 64 of padding while .idx is 940
|
|
508
|
+
inclusive, and the brand sits 32px left of the cards it is supposed to line up with. */
|
|
424
509
|
box-sizing: border-box;
|
|
510
|
+
padding: 0 32px;
|
|
511
|
+
min-height: var(--site-h);
|
|
512
|
+
display: flex;
|
|
513
|
+
align-items: center;
|
|
514
|
+
gap: 20px;
|
|
425
515
|
}
|
|
426
|
-
.
|
|
427
|
-
margin-
|
|
516
|
+
.site__brand {
|
|
517
|
+
margin-right: auto; /* holds the brand left; nav + actions collect on the right */
|
|
518
|
+
display: inline-flex;
|
|
519
|
+
align-items: center;
|
|
520
|
+
gap: 10px;
|
|
521
|
+
text-decoration: none;
|
|
522
|
+
color: inherit;
|
|
428
523
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
524
|
+
.site__logo {
|
|
525
|
+
display: block;
|
|
526
|
+
border-radius: 6px;
|
|
527
|
+
}
|
|
528
|
+
.site__name {
|
|
529
|
+
font-size: 17px;
|
|
433
530
|
font-weight: 700;
|
|
434
|
-
letter-spacing: 0.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
531
|
+
letter-spacing: -0.01em;
|
|
532
|
+
}
|
|
533
|
+
.site__nav {
|
|
534
|
+
display: flex;
|
|
535
|
+
gap: 4px;
|
|
536
|
+
}
|
|
537
|
+
.site__link {
|
|
538
|
+
flex: none;
|
|
539
|
+
white-space: nowrap;
|
|
540
|
+
padding: 8px 12px;
|
|
541
|
+
border-radius: 8px;
|
|
542
|
+
font-size: 15px;
|
|
543
|
+
font-weight: 600;
|
|
544
|
+
color: var(--muted);
|
|
438
545
|
text-decoration: none;
|
|
439
|
-
transition:
|
|
546
|
+
transition: color 0.15s ease, background 0.15s ease;
|
|
440
547
|
}
|
|
441
|
-
.
|
|
442
|
-
|
|
548
|
+
.site__link:hover {
|
|
549
|
+
color: var(--text);
|
|
550
|
+
background: var(--hover);
|
|
443
551
|
}
|
|
552
|
+
.site__link[aria-current='page'] {
|
|
553
|
+
color: var(--text);
|
|
554
|
+
background: var(--accent-soft);
|
|
555
|
+
}
|
|
556
|
+
.site__link:focus-visible {
|
|
557
|
+
outline: 2px solid var(--accent);
|
|
558
|
+
outline-offset: 2px;
|
|
559
|
+
}
|
|
560
|
+
/* The icon buttons — the theme toggle and the account control when they exist. Kept separate from
|
|
561
|
+
the nav so that on a phone the nav can drop to its own row while these stay up on the brand row.
|
|
562
|
+
Empty until something is passed to SiteHeader's `actions` slot. */
|
|
563
|
+
.site__actions {
|
|
564
|
+
display: flex;
|
|
565
|
+
align-items: center;
|
|
566
|
+
gap: 6px;
|
|
567
|
+
}
|
|
568
|
+
/* Square icon button, styled here so the slot's occupant does not have to carry its own CSS. Sized
|
|
569
|
+
36px so it is a comfortable target without towering over the 60px header row. */
|
|
570
|
+
.site__icon {
|
|
571
|
+
flex: none;
|
|
572
|
+
width: 36px;
|
|
573
|
+
height: 36px;
|
|
574
|
+
display: inline-flex;
|
|
575
|
+
align-items: center;
|
|
576
|
+
justify-content: center;
|
|
577
|
+
font-size: 16px;
|
|
578
|
+
line-height: 1;
|
|
579
|
+
font-family: inherit;
|
|
580
|
+
color: var(--muted);
|
|
581
|
+
background: transparent;
|
|
582
|
+
border: 1px solid transparent;
|
|
583
|
+
border-radius: 9px;
|
|
584
|
+
cursor: pointer;
|
|
585
|
+
transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
|
|
586
|
+
}
|
|
587
|
+
.site__icon:hover {
|
|
588
|
+
color: var(--text);
|
|
589
|
+
background: var(--hover);
|
|
590
|
+
}
|
|
591
|
+
.site__icon:focus-visible {
|
|
592
|
+
outline: 2px solid var(--accent);
|
|
593
|
+
outline-offset: 2px;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* Phone: the nav row scrolls horizontally rather than collapsing into a hamburger. There are three
|
|
597
|
+
sections today and there will be more, but they are the primary navigation and the whole point of
|
|
598
|
+
the current one is being visible; a menu would hide "you are here" behind a tap. */
|
|
599
|
+
@media (max-width: 560px) {
|
|
600
|
+
.site__inner {
|
|
601
|
+
padding: 8px 20px;
|
|
602
|
+
flex-wrap: wrap;
|
|
603
|
+
gap: 0 12px;
|
|
604
|
+
}
|
|
605
|
+
.site__nav {
|
|
606
|
+
order: 3; /* after brand and actions ⇒ its own row */
|
|
607
|
+
flex: 1 0 100%;
|
|
608
|
+
margin: 2px -20px 0; /* bleed to the screen edges so the row can scroll fully */
|
|
609
|
+
padding: 0 20px 4px;
|
|
610
|
+
overflow-x: auto;
|
|
611
|
+
scrollbar-width: none;
|
|
612
|
+
}
|
|
613
|
+
.site__nav::-webkit-scrollbar {
|
|
614
|
+
display: none;
|
|
615
|
+
}
|
|
616
|
+
.site__link {
|
|
617
|
+
display: flex;
|
|
618
|
+
align-items: center;
|
|
619
|
+
min-height: 40px; /* comfortable touch target */
|
|
620
|
+
padding: 8px 10px;
|
|
621
|
+
font-size: 14px;
|
|
622
|
+
}
|
|
623
|
+
.site__icon {
|
|
624
|
+
width: 40px;
|
|
625
|
+
height: 40px;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.idx {
|
|
630
|
+
--idx-accent: var(--brand);
|
|
631
|
+
--idx-muted: var(--muted); /* was the literal #8b909c — the same value --muted already holds, so
|
|
632
|
+
aliasing is byte-identical on dark and picks up the light block */
|
|
633
|
+
--idx-line: rgba(var(--ink-rgb), 0.09);
|
|
634
|
+
/* The bar is a sibling above this column, so the viewport it can fill is what the bar leaves.
|
|
635
|
+
On a phone the bar wraps to two rows and this under-subtracts — harmless, it is a min. */
|
|
636
|
+
min-height: calc(100vh - var(--site-h) - 1px); /* the bar's row, plus its 1px rule */
|
|
637
|
+
max-width: 940px;
|
|
638
|
+
margin: 0 auto;
|
|
639
|
+
padding: 28px 32px 64px;
|
|
640
|
+
box-sizing: border-box;
|
|
641
|
+
}
|
|
642
|
+
.idx__head {
|
|
643
|
+
margin-bottom: 36px;
|
|
644
|
+
}
|
|
645
|
+
/* Catalog header: just the subject title now — the GraphL wordmark it used to sit under is in
|
|
646
|
+
the site bar above (SiteHeader), which is the same bar graphl.in carries. */
|
|
444
647
|
.idx__subject {
|
|
445
648
|
margin: 0;
|
|
446
649
|
font-size: 40px;
|
|
@@ -470,7 +673,7 @@ body {
|
|
|
470
673
|
.idx-card {
|
|
471
674
|
border: 1px solid var(--idx-line);
|
|
472
675
|
border-radius: 14px;
|
|
473
|
-
background: rgba(
|
|
676
|
+
background: rgba(var(--ink-rgb), 0.018);
|
|
474
677
|
overflow: hidden;
|
|
475
678
|
}
|
|
476
679
|
.idx-card__head {
|
|
@@ -489,7 +692,7 @@ body {
|
|
|
489
692
|
transition: background 0.15s ease;
|
|
490
693
|
}
|
|
491
694
|
.idx-card__head:hover {
|
|
492
|
-
background:
|
|
695
|
+
background: var(--surface-lift-2);
|
|
493
696
|
}
|
|
494
697
|
/* Divider only when open — a collapsed row is a clean single bar. */
|
|
495
698
|
.idx-card--open .idx-card__head {
|
|
@@ -556,7 +759,7 @@ body {
|
|
|
556
759
|
font-size: 13px;
|
|
557
760
|
font-weight: 600;
|
|
558
761
|
font-variant-numeric: tabular-nums;
|
|
559
|
-
color:
|
|
762
|
+
color: var(--foot-dim);
|
|
560
763
|
}
|
|
561
764
|
.idx-sec__row a:hover .idx-sec__num {
|
|
562
765
|
color: var(--idx-accent);
|
|
@@ -569,7 +772,7 @@ body {
|
|
|
569
772
|
.idx-sec__chev {
|
|
570
773
|
flex: none;
|
|
571
774
|
font-size: 19px;
|
|
572
|
-
color:
|
|
775
|
+
color: var(--foot-dimmer);
|
|
573
776
|
transition: color 0.12s ease, transform 0.12s ease;
|
|
574
777
|
}
|
|
575
778
|
.idx-sec__row a:hover .idx-sec__chev {
|
|
@@ -587,7 +790,16 @@ body {
|
|
|
587
790
|
font-size: 12px;
|
|
588
791
|
padding: 2px 7px;
|
|
589
792
|
border-radius: 5px;
|
|
590
|
-
background: rgba(
|
|
591
|
-
color: rgba(
|
|
793
|
+
background: rgba(var(--ink-rgb), 0.08);
|
|
794
|
+
color: rgba(var(--ink-rgb), 0.9);
|
|
592
795
|
}
|
|
593
796
|
|
|
797
|
+
/* The bar's phone gutter is 20px, so the catalog column's has to be too — otherwise the brand and
|
|
798
|
+
the subject title below it sit on two different left edges. Matches ui-graphl's own phone rule.
|
|
799
|
+
It lives down HERE, not up with the rest of the .site phone rules, because .idx's own padding is
|
|
800
|
+
declared after them and would otherwise win at equal specificity. */
|
|
801
|
+
@media (max-width: 560px) {
|
|
802
|
+
.idx {
|
|
803
|
+
padding: 20px 20px 48px;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ThemeKey } from '@graphlearning/flow';
|
|
2
|
+
export declare function readStored(): ThemeKey | null;
|
|
3
|
+
/**
|
|
4
|
+
* The theme actually painted. CAPTURE PINS IT TO THE DECK, ignoring the stored choice and the reader
|
|
5
|
+
* entirely — and that is not a nicety. A recorder runs a fresh headless profile with empty storage,
|
|
6
|
+
* so without this pin what a capture painted would depend on the machine that rendered it. The
|
|
7
|
+
* declared prop is the only thing burned into a video.
|
|
8
|
+
*/
|
|
9
|
+
export declare const resolveTheme: (deck: ThemeKey, stored: ThemeKey | null, capture: boolean) => ThemeKey;
|
|
10
|
+
/**
|
|
11
|
+
* Owns the reader's choice and keeps <html data-theme> in step with it. Returns the theme to hand
|
|
12
|
+
* SceneView, plus a two-state flip for the toggle.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useTheme(deck: ThemeKey, capture: boolean): {
|
|
15
|
+
theme: ThemeKey;
|
|
16
|
+
toggle: () => void;
|
|
17
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphlearning/shell",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The GraphL concept-app shell
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "The GraphL concept-app shell — hash router, section view (scene left / slide right), slide panel, course catalog and narration channel, plus the capture/record/publish toolchain that drives them. A content repo supplies courses + scenes.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -28,17 +28,17 @@
|
|
|
28
28
|
"watch": "vite build --watch"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@graphlearning/flow": ">=0.
|
|
31
|
+
"@graphlearning/flow": ">=0.8.0 <1.0.0",
|
|
32
|
+
"esbuild": ">=0.20",
|
|
32
33
|
"lucide-react": "^1.28.0",
|
|
34
|
+
"puppeteer": ">=24",
|
|
33
35
|
"react": "^19.2.0",
|
|
34
36
|
"react-dom": "^19.2.0",
|
|
35
37
|
"react-markdown": "^10.1.0",
|
|
36
|
-
"remark-gfm": "^4.0.1"
|
|
37
|
-
"puppeteer": ">=24",
|
|
38
|
-
"esbuild": ">=0.20"
|
|
38
|
+
"remark-gfm": "^4.0.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@graphlearning/flow": "^0.
|
|
41
|
+
"@graphlearning/flow": "^0.8.0",
|
|
42
42
|
"@types/react": "^19.2.0",
|
|
43
43
|
"@types/react-dom": "^19.2.0",
|
|
44
44
|
"@vitejs/plugin-react": "^4.3.4",
|